- Add checkpoint guard in all 4 tool-executors ensuring every
tool_call_id gets a ToolMessage response
- Add cleanPollutedToolCalls utility to heal corrupted message history
- Add error banner with "重置此对话" button in frontend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SearchResultProps does not declare a card_id field, so passing it caused
TS2769 type errors. search-result cards do not need loading→done dedup
(each google_search/web_search_deep call produces a complete result in one
push), so the card_id and associated local variables are unnecessary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each search result card now carries a unique card_id containing the tool call id
(e.g. google-search-<tc.id>), enabling the ToolCallStatus exact-match logic to
correctly bind cards to their originating tool call without index-based offset errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add parallel_tool_calls: false to searcher agentNode bindTools call so the
LLM is forced to execute search tools one at a time (google_search first,
then web_read in a separate turn); prompt-level "strict禁止" alone cannot
override model-side parallel tool calling behaviour
- Remove card_id field from google_search and web_search_deep ui.push props;
SearchResultProps does not declare card_id so the field caused TS2769
overload errors; these cards do not need deduplication (each search is a
distinct query), while the card_id variables were introduced by a previous
commit and are now unused
- Clean up unused googleCardId and deepCardId local variables
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add web_read -> web-read-progress mapping to standaloneUiItems filter in main.tsx
so web-read-progress cards are no longer mistakenly treated as standalone
- Add web-read-progress to CARD_TYPE_PRIORITY (550) to ensure stable sort order
and prevent search-result cards (500) from being reordered relative to web-read cards
- Add unique card_id (containing tc.id) to google_search and web_search_deep ui.push
calls so each search result card can be precisely identified
- Improve uiItem matching in ToolCallStatus to first try exact match via card_id
containing tc.id, falling back to index-based matching only when necessary;
this prevents index offset bugs when multiple same-type tools run concurrently
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When LLM sends multiple parallel tool_calls but one throws unexpectedly,
Promise.all loses all results causing 400 error from OpenAI.
Promise.allSettled ensures every tool_call_id gets a response.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Azure Container App auto-deploy workflow replaces our manual
deploy-langgraph.yml. CD is now managed via Azure Portal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8s timeout caused Jina Reader failures on slower sites like news.cn.
Increased to 30s with 2 retries and 1s backoff.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wraps all LoadExternalComponent usages with CardErrorBoundary so
card rendering errors show an inline error message instead of
crashing the entire app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tool call status cards and execution log panel now expand by default
so users can see CoT process and results without clicking.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace soc-langgraph Web App with Azure Container App (soc-cae environment)
- New URL: soc-langgraph.victorioussand-69befc84.southeastasia.azurecontainerapps.io
- Update deploy-langgraph.yml: Web App restart -> az containerapp update --image
- Update deploy-langgraph-ui.yml: VITE_LANGGRAPH_URL to Container App URL
- Container App: min 1 replica, max 3, CPU 1.0, Memory 2Gi, port 8080
- Update CLAUDE.md architecture and deployment docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
server.ts production mode crashes on startup (exit code 1).
Revert to langgraphjs dev until startServer() compatibility is resolved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
server.ts uses createRequire to resolve @langchain/langgraph-api at startup.
With pnpm, indirect dependencies are not hoisted to node_modules/@langchain/,
causing "Cannot find module" error and container exit code 1 on Azure.
Adding it as an explicit dependency ensures pnpm creates the direct symlink.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add web-read-progress Gen-UI card for real-time web reading feedback
- Searcher tool-executor pushes loading→complete cards during web_read
- ToolCallStatus supports expanding during loading state
- Switch from langgraphjs dev to production mode (server.ts)
- Add Hono middleware to strip sensitive request headers
- Bundle splitting via Vite manualChunks (1.85MB → 8 chunks)
- SPA fallback via staticwebapp.config.json
- Agent model configs updated (sonnet 1M → standard sonnet)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously web_search_deep tool calls were missing from the UI_NAME_MAP,
causing their search-result cards to render as standalone cards instead of
being embedded within the ToolCallStatus component row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When tool rounds hit MAX_ITERATIONS, the router was jumping directly to
END without giving the LLM a chance to produce a final text reply.
Added a `force-summary` node that invokes the LLM without tools,
ensuring a coherent Chinese summary is always generated after all
search results are collected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The LangGraph API server injects all x-* request headers into
thread.config.configurable, which gets persisted and exposed via
POST /threads/search. This leaked Azure-injected headers (x-forwarded-for,
x-client-ip, x-arr-ssl, etc.) to any user.
Adds a Hono middleware (http.app) that strips sensitive infrastructure
headers at the HTTP layer before they reach the LangGraph API routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace single pendingSubmitRef with pendingQueueRef array supporting multiple queued messages
- Add SubmitPayload interface to snapshot text/file/tools/modelMode at submission time
- Rewrite handleSubmit/doSubmit to use payload snapshots instead of closures over mutable state
- Rewrite handleConcurrentQueue with polling loop that drains the queue sequentially
- Rewrite handleConcurrentInterrupt and handleConcurrentCancel for queue awareness
- StreamStatusBar queued state now shows count (N 条消息已排队) and inline cancel button
- Add key={concurrentStatus} to StreamStatusBar div for fade-in animation on status change
- Config panel summary button shows numeric badge instead of dot when tools are active
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- filterTools now actually filters by modelMode (flash excludes search tools)
- Hard limit of 3 tool calls per turn in preValidateToolCall
- Temperature default changed from 0.5 to 0.2 to match MODEL_PRESETS.auto
- ticket_list blocked when message contains specific ticket ID (TK-xxxx)
- google_search/web_search_deep blocked for internal knowledge queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace flat tool toggles + model selector bar with a default-collapsed
summary chip (e.g. "Auto · 已开启 3 个工具 ▾") that expands upward into
a compact panel. Panel auto-closes on outside mousedown click. Options
changes keep the panel open.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add showConcurrentDialog modal when submitting while AI is loading
- Offer interrupt (stop + submit) or queue (poll until done) options
- Remove disabled={thread.isLoading} from textarea and FileUploadButton
- Fix TS2352: cast BaseMessage to unknown first before Record
- Fix TS6133: rename unused toolName param to _toolName
- Fix TS2769: chart_type typed as union literal, not string
- Fix TS2322: separate cmdMap lookup from const declaration in soc-client
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Add CornerDownRight, AlertCircle icons for fallback/partial states
- New isPartial/isFallback props on ToolCallRow with hint banners
- Read execution_log from thread.values in main.tsx, pass to ToolCallStatus
- Match by tc.name === entry.tool to determine partial/fallback per row
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
GOOGLE_API_KEY is not used at startup (no code calls config.google.apiKey).
DATABASE_URL is only needed for production checkpointer. Both are now
warned-but-not-fatal in dev (NODE_ENV != production) to allow local
development without these credentials.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- coder/tool-executor: push sandbox-result card with friendly message on
failure instead of raw error, guide user to local execution or retry
- enterprise/tool-executor: skip ticket-summary card when ticket_list
returns empty, only return hint in tool message to avoid blank UI
- searcher/tool-executor: auto-supplement web_search_deep with
google_search when results < 3, matching existing google_search behavior
- ticket_detail UUID: already correctly uses find() match (no change needed)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Fix ToolCallStatus label duplication: label span was shown twice when
failed/done; consolidate into a single span with correct copy
- Add animate-in fade-in duration-300 wrapper to standalone Gen-UI cards
and streaming orphan cards in main.tsx (already present in ToolCallStatus
expanded view)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Cards with the same card_id in props are deduplicated to show only the
last (complete) version, replacing any earlier loading skeleton cards.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Fix ticket_detail UUID resolution: use find() to match ticketNumber instead of tickets[0]
- Add sandbox fallback hint in coder tool-executor catch block
- Add ticket_list empty result friendly hint with suggestions
- Add google_search auto-supplement with Jina when results < 3
- Enhance enterprise tool-executor general catch with fallback_hint
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Fix Daytona code execution endpoint from process/code-run (404) to process/execute
- Fix request body format to {command} with language-aware shell wrapping
- Add missing sourceType/confidence props to writer doc_edit and doc_translate ui.push
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Router prompt now clearly distinguishes "写代码" (execute code → coder)
from "写文档" (create document → writer)
- Writer prompt strengthened to always use doc_create, never plain text
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add CanvasPanel.tsx: right-side 480px drawer, renders markdown via
MessageBubble or code via SyntaxHighlighter, with Copy/Download/Close
header; listens to window 'open-canvas' CustomEvent dispatched by Gen-UI cards
- Update ThreadSidebar.tsx: add search input below new-chat button with
live filtering; group filtered threads by date (今天/昨天/本周/更早)
with section headings; internal state only, Props interface unchanged
- Update main.tsx: import CanvasPanel, add canvasDoc state, register
open-canvas event listener on mount, render CanvasPanel alongside
the main chat column inside a flex-row wrapper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Oryx runs `npm install` which fails on peer dependency conflicts between
@assistant-ui/react@^0.8.0 and @assistant-ui/react-syntax-highlighter@^0.7.2.
Adding .npmrc with legacy-peer-deps=true and CUSTOM_BUILD_COMMAND for pnpm.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create 5 enterprise UI card components under src/agent-uis/enterprise/:
knowledge-result, ticket-summary, ticket-detail, search-result, sandbox-result
- Update ComponentMap in src/agent-uis/index.tsx to replace () => null placeholders
with real component imports
- Implement src/main.tsx with useStream + LoadExternalComponent for full Chat UI,
connecting to LangGraph agent with VITE_LANGGRAPH_URL env var
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
workspace/ files existed on disk but were not included in previous
incremental commit, causing git to record them as deleted. Re-adding
all workspace card components, AgentWorkspace, ActivityTimeline, and
WorkspaceCardRenderer to properly track them.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
## Fixes
- [#3] Status events now add/update a status ActivityNode in timeline (upsertWsStatusNode)
- [#4] Done callback adds done node; error callback adds error node + sets session.status="error"
- [#5] handleRegenerate fully synced with workspace: status/tool/card/done/error all handled
- [#1][#2] localStorage persistence: completed/error sessions auto-saved, lazy-loaded on demand
- [#1] handleSelectConversation preloads workspace sessions from localStorage for history messages
- Refactored completeWsSession to include done ActivityNode in timeline
- Added errorWsSession, upsertWsStatusNode, saveWsToStorage, loadWsFromStorage helpers
## Known limitation
- [#7] workspace_card merge:true not yet used by backend (all cards are append-only for now)
- History workspace recovery depends on localStorage (browser-local, not cross-device)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Two bugs caused the same AI response to appear twice:
1. handleSelectConversation would fetch conversation history even while
a stream was in progress (isLoading). If the fetch resolved mid-stream,
fetchConversation overwrote the conversation messages unconditionally,
writing the server-persisted AI message (with a real UUID) alongside the
still-streaming client-side message (with a temp ID), producing a duplicate.
Fix: guard the fetch with !isLoading, and add a second check inside the
.then callback so setConversations only writes history if the messages
array is still empty (protects against slow-resolving network requests).
2. onDone in streamChat could theoretically be called a second time if the
ReadableStream completed without a "done" SSE event. Fix: call
reader.cancel() immediately after onDone so the while loop exits cleanly.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>