feat(agent): chat panel with streaming + tool dispatch#217
Open
appergb wants to merge 1 commit into
Open
Conversation
- opentake-agent/src/chat/: session + loop + llm (streaming SSE) - src-tauri: chat_send/history/cancel + chat_delta/tool_call/done events - web: AgentPanel message list + streaming + tool call cards - Context Signal injected into system prompt - BYOK via opentake-gen provider/keys - tool calls reuse 44-tool dispatch (single edit entry) Co-authored-by: opencode <opencode@anomaly.co>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the in-app Agent chat panel (HANDOFF §3.3, P1). The UI, external MCP server, and in-app chat are now peer clients of the same
Dispatcher— a chat turn's tool calls land on the exact same 44-toolEditCommandpipeline an external agent would use, preserving the single-edit-entry invariant.Changes
Backend —
crates/opentake-agent/src/chat/(new)session.rs—ChatSession/ChatMessage/Role/ToolCall(serde camelCase for the front-end mirror).llm.rs— BYOK streaming chat client overopentake_gen::KeyringStore. Two providers: OpenAI (/v1/chat/completionsSSE) + Anthropic (/v1/messagesSSE). Picks the first with a stored key; returns a guided message when neither is configured.loop.rs— the turn loop: assembles the system prompt (base + active plugin viaprompt::assemble) + a live Context Signal block (signal::engine::build_signal) so the model can act without aget_timelineround-trip; streams the model; dispatches tool calls through the sharedDispatcherviaspawn_blocking; re-feeds results until a final text turn. Cancel is a sharedArc<AtomicBool>(no tokio dep, so the--no-default-featuresbuild stays clean).Tauri —
src-tauri/src/chat.rs(new) +lib.rschat_send(session_id, text)— spawns a turn; streamschat_delta/chat_tool_call/chat_doneevents.chat_history(session_id)— returns the persisted message log.chat_cancel(session_id)— sets the cancel flag at the next boundary.ChatStatemanaged insetup: builds its ownDispatcherover the liveAppCore+ the workflow registry from the same<app_data_dir>/workflowsthe MCP server scans. No media bridge (chat doesn't render/import).Frontend
web/src/lib/types.ts—ChatRole/ChatMessage/ChatToolCall(mirror Rust serde camelCase).web/src/lib/api.ts—chatSend/chatHistory/chatCancel+onChatDelta/onChatToolCall/onChatDonelisteners.web/src/store/chatStore.ts(new) — Zustand store: message list + streaming state + tool-call card upsert by id.web/src/components/agent/AgentPanel.tsx— rewritten: message list (user right / assistant left), streaming delta accumulation, collapsible tool-call cards (args + result + error badge), input (Enter send / Shift+Enter newline), cancel button, no-key guided message with an Open-Settings affordance.web/src/i18n/dict.ts—agent.*keys (zh + en).Verification (all 6 green)
New unit tests: 18 in
opentake-agent::chat(session serde, OpenAI/Anthropic body shape, tool-call round-trip, no-key path, system-prompt signal injection) + 2 inopentake-tauri::chat(registry + session take/put).Acceptance
tighten_silences→ripple_delete_ranges(sameDispatcheras MCP).chat_deltaevents.Notes
Dispatcheris built without aMediaBridge(no GPU/import from chat);inspect_timeline/import_mediareturn "not available" — the model routes around them. A follow-up can share the MCP server's bridge if chat-side rendering becomes important.opentake-genis a generation-job client (no chat surface), so chat ships its own streaming client over the sameKeyringStore.