Conversation
… delegation Adds migration m20260522_000001 with two nullable text columns on conversation; mirrors them into ConversationSummary / DbConversationSummary (plus surfaces the existing parent_id on the public summary); fills None at all 8 parser sites and the 3 existing ActiveModel construction sites. Adds round-trip tests covering both the populated and default-null cases. Phase 1 of multi-agent delegation; see docs/superpowers/specs/2026-05-22-multi-agent-delegation-design.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…versationLinked
- Extend ConversationLinked payload with optional parent_conversation_id (i32) and
parent_tool_use_id (String). Both serialize-skip when None so existing wire
consumers see no change.
- New variants DelegationStarted / DelegationCompleted with a DelegationResultSummary
tagged enum (ok { duration_ms } / err { error_code }).
- Patch all 7 ConversationLinked construction/pattern sites + extend the
apply_event match in SessionState to ignore the new delegation events
(broker handles them out of band).
- Document the meta["codeg.delegation"] convention on ToolCallState.
Phase 2 of multi-agent delegation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New module acp/delegation/ with the spawner trait + DelegationLink carrier. The trait is the surface DelegationBroker depends on; production wires up via a new ConnectionManagerSpawner wrapper (manager + DB), tests use MockSpawner. - Extend send_prompt_linked with optional delegation arg; on the new-row branch it flows into create_with_delegation (new conversation_service entry point) and into the ConversationLinked event's parent_* fields. - Return type widened to Result<Option<i32>, AcpError> so the spawner trait can hand the bound conversation id back to the broker; non-broker callers ignore the value. - Trait mock + 3 unit tests cover queued spawn/send results and unqueued-fail. Phase 3 of multi-agent delegation; broker arrives in Phase 4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cascade Drives a single delegate_to_agent call end-to-end against the Phase 3 ConnectionSpawner trait: - broker.rs: handle_request → depth pre-check → spawn → send_prompt_linked → park oneshot → race timeout vs complete_call. complete_call disconnects the child (v1 one-shot). cancel_by_parent fans out cancel+disconnect to every pending child of a given parent_connection_id. - types.rs: DelegationRequest / DelegationOutcome / DelegationError with stable wire-format error codes for the MCP tool_result payload. - depth.rs: generic async parent-chain walker, saturates at cap so a corrupted chain can't unbounded-walk the DB. - DbDepthLookup implements ConversationDepthLookup over AppDatabase; tests use an in-memory MockDepth + MockSpawner with no DB or runtime ACP. 14 new tests cover config round-trip, disabled fast-path, happy path with in-flight complete_call, spawn/send failure mapping, timeout cleanup, parent-cancel cascade, and depth limit rejection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nd-trip Companion side of the multi-agent delegation flow. Caller agents launch codeg-mcp from their MCP config; the LLM sees a single `delegate_to_agent` tool whose tools/call gets forwarded to the main-process broker. - src/bin/codeg_mcp.rs: thin stdio loop, plain argv parsing (no clap), exits cleanly on EOF. - acp/delegation/companion.rs: JSON-RPC 2.0 dispatch (initialize, tools/list, tools/call), notification filtering, MCP tool_result rendering. Pulled into the lib so handle_line() is unit-testable without process spawning. - acp/delegation/transport.rs: length-prefixed JSON frame writer/reader plus cross-platform client_round_trip (UDS on unix, named pipe on windows). 16 MiB frame cap, in-memory + real-UDS tests. - acp/delegation/tool_schema.json: embedded delegate_to_agent schema with all 6 agent_type variants. Packaged as a third binary in the existing crate (matching codeg-server) rather than a workspace member, to avoid duplicating broker types and to share target/. New tests: 12 (3 transport + 9 companion). Full lib suite passes 419 tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gs end-to-end This is the integration phase: the Phase 5 codeg-mcp companion now reaches a running broker via UDS, the LLM-issued delegate_to_agent ToolUse spawns a child session, and its TurnComplete resolves the parent's tool_use_id. New modules: - acp/delegation/listener.rs: UDS / named-pipe accept loop, token registry (register/revoke/lookup/revoke_by_parent), ParentSessionLookup trait, process() with token + parent-conn + agent_type + task validation. 7 unit tests cover the rejection paths and a duplex-stream happy path. - commands/delegation.rs: persistence layer for the 3 settings keys (enabled / depth_limit / default_timeout_seconds) backed by app_metadata, Tauri commands get/set_delegation_settings, clamp helper, broker re-application on save. 4 unit tests round-trip storage + clamp behavior. - tests/delegation_e2e_uds.rs: full UDS round-trip with a mock spawner — happy path + invalid-token rejection. Wiring: - AppState gains delegation_broker / delegation_tokens / delegation_socket_path; app_state::build_delegation_stack() constructs the full stack (broker, depth lookup, token registry, PID-scoped socket) and installs the injection onto ConnectionManager via a OnceLock-backed install_delegation so all 5 spawn_agent call sites pick it up without parameter threading. - Both codeg-server bootstrap and Tauri lib.rs setup() build the stack, apply_persisted_config() before listener bind, and spawn run(). Tauri also .manage()s broker/tokens/socket-path for HTTP/Tauri command lookup. - connection.rs: run_connection + spawn_agent_connection gained an Option<DelegationInjection> arg. After init handshake, inject_codeg_delegate_mcp registers a per-launch token, appends a stdio McpServer entry pointing at the codeg-mcp binary colocated next to current_exe, and stashes the token on SessionState.delegation_token. On run_connection exit the token is revoked and broker.cancel_by_parent fans out cascade-cancel. - ConnectionManagerParentLookup impl in manager.rs reads the live session state to answer "what conversation is parent X currently in?" - send_prompt_linked emits AcpEvent::DelegationStarted on the new-row branch when a DelegationLink is supplied (Task 6.7). - lifecycle.rs: handle_event / handle_event_with_retry / connection_worker_loop / lifecycle_subscriber_task all take Option<Arc<DelegationBroker>>. On TurnComplete for a delegation child, forward_turn_complete_to_broker maps stop_reason → DelegationOutcome, calls broker.complete_call (which disconnects the child per v1 one-shot semantics), and emits AcpEvent::DelegationCompleted. On Disconnected / Error, forward_disconnect_to_broker calls cancel_by_child_connection so the parent's tool_use_id doesn't dangle. - SessionState gains last_assistant_text — captured at TurnComplete just before live_message is cleared so the broker outcome carries real text instead of an empty stub. - broker: new cancel_by_child_connection method, symmetric to cancel_by_parent. Tests: 430 lib passing (+11 vs Phase 5), 2 e2e UDS, clippy clean across desktop + server features. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delegation sub-sessions live in the DB as conversations with parent_id set. Without a filter they'd appear as top-level rows in the workspace list, duplicating what their parent's ToolCallBlock will render inline (Phase 8). * `list_all` gains include_children: bool — defaults to false so the workspace list automatically hides delegation children. * New `list_children(parent_id)` service + `list_child_conversations` Tauri command / HTTP handler / route, oldest-first, soft-delete aware. * DbConversationSummary mirror adds parent_id / parent_tool_use_id / delegation_call_id so the frontend can render the parent ↔ child link. * api.ts + tauri.ts: listAllConversations gains includeChildren param, new listChildConversations(parentId). * 4 service tests + 2 command tests cover default-exclude, opt-in include, child-scoping, and soft-delete invisibility. Frontend UI toggle deferred to Phase 8 where the inline child rendering under the parent's ToolCallBlock lands — separate top-level toggle would just be developer scaffolding until that view exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the parent ↔ child delegation rendering for `delegate_to_agent` ToolCallBlocks. The wire events fire on the child's connection stream, so the parent's UI needs a global subscription to resolve a binding by parent_tool_use_id — that's the new DelegationContext. * AcpEvent mirror gains `delegation_started` / `delegation_completed` (+ DelegationResultSummary discriminated by `kind`). * DelegationProvider — one global `acp://event` subscription, builds Map<parent_tool_use_id, DelegationBinding>, status transitions running → ok/err. Mounted between AcpConnectionsProvider and the ConversationRuntimeProvider so the binding outlives a single chat tab. * useDelegatedSubSession — resolves the binding + fetches the child conversation detail lazily (only on expand). Internal useReducer state machine to keep `react-hooks/set-state-in-effect` happy. * DelegatedSubThread — collapsed header (agent label + status badge + last assistant text snippet) and expand-to-preview body listing the child's turns. Status badge uses static error-code keys; the Rust DelegationError taxonomy maps 1:1 to the next-intl keys. * content-parts-renderer routes `delegate_to_agent` to the new component when the tool-call has a toolCallId; otherwise falls through. * i18n: en + zh-CN + zh-TW fully translated; ar/de/es/fr/ja/ko/pt get English placeholders for parity (Phase 9 localizes properly). * Test: vitest covers the four user-visible states (no binding, running, error code, expand-on-click). Deferred from plan Task 8.3 / 8.7: * Permission inline routing — the existing per-connection permission store needs broader plumbing to surface child-permission requests on the parent ToolCallBlock. Tackle in Phase 9. * `ToolCallState.meta["codeg.delegation"]` live state — the backend doesn't yet stamp delegation markers on tool-call meta, so the pre-binding "Delegating to X…" status has no source field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the multi-agent delegation feature: a settings panel that lets
users toggle the kill switch / depth / timeout, a chat-channel summary
so Slack/Telegram/etc. relays render delegation calls as one line each
instead of dumping the MCP tool I/O, and the i18n coverage that was
missing for the AcpAgentSettings.multiAgent block in all 10 locales.
* `web/handlers/delegation.rs` (new) + router routes — the HTTP mirror
of `get_delegation_settings` / `set_delegation_settings` Tauri
commands. Server mode needs them too, otherwise CodeG-server users
have no way to tune the broker.
* `lib/api.ts` — `getDelegationSettings` / `setDelegationSettings`
wrappers + `DelegationSettings` type mirror. No `tauri.ts` entry
needed; `getTransport().call()` already routes both modes.
* `components/settings/delegation-settings.tsx` (new) — a self-contained
section mounted under `/settings/agents` as a sibling of the
per-agent settings. Lives in its own file because delegation is
a global feature and the existing acp-agent-settings is 7.8k lines.
Server-side clamp values are mirrored back into the inputs after
save so the UI always reflects what was actually persisted.
* `chat_channel/session_event_subscriber.rs` — delegation-aware relay:
fires "🤖 Delegating to {agent}…" on ToolCall and "✅/❌ {agent}:
{preview}" on ToolCallUpdate completed. Skips the generic
`>> {detail}` line for these calls so users see one delegation
status, not three. +7 unit tests cover the matchers and outcome
formatters (ok / err code / empty / long-text truncation).
* i18n — en/zh-CN/zh-TW carry the full localized copy for the
settings panel; the other 7 locales get the English fallback to
keep parity tests green (next translator pass can fill them in).
Verification:
cargo test --features test-utils --lib → 443 passed
cargo clippy --all-targets --features test-utils → clean
cargo check --no-default-features --bin codeg-server --bin codeg-mcp
pnpm test → 177 passed
pnpm eslint . → clean
pnpm build → static export ok
Manual acceptance (per spec §15.3) — pending user-driven runs:
* Claude Code → Codex flow
* Codex → Claude Code reverse flow
* Cancel cascade (parent cancel → child stops < 1s)
* Timeout (30s setting + slow task → error_code timeout)
* Depth limit (3-deep chain at depth=2 → error_code depth_limit;
bump depth to 3 → succeeds)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* settings/agents page: wrap in a scrolling container so the global delegation section is reachable below AcpAgentSettings (which still claims h-full for its grid layout). * broker: ACP clients (Codex/Claude Code) don't populate `_meta.tool_use_id`, so the companion no longer rejects calls without it; instead, the lifecycle subscribes to parent ToolCall events and pushes their tool_call_ids into a per-connection FIFO that the broker claims when an MCP round-trip arrives (UUID fallback when empty). * tool-call renderer: render `delegate_to_agent` invocations as a dedicated AgentIcon-driven card with the task text pulled directly from the LLM input — even before the DelegationStarted event lands. Tool-name normalization picks up the `mcp__<server>__delegate_to_agent` prefix. * i18n: three new strings (`delegatedLabel`, `unknownAgent`, `waitingForChild`) localized for zh-CN / zh-TW / ja / ko / es / de / fr / pt / ar; English-only fallback dropped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…kdown
* tool-kind-classifier: `isAgentLikeToolName` now also matches
`delegate_to_agent` and `mcp__<server>__delegate_to_agent`, so the
delegation card breaks the tool-call run instead of folding into a
consecutive-tool-group capsule.
* DelegatedSubThread: drop the shadow, remove the in-header outcome
summary line (collapsed card now shows only agent + task), and parse
the broker's `{kind, text, code, message}` outcome so the expanded
body renders the actual `text` (or `message`) as markdown via
MessageResponse instead of a raw JSON string. Sub-thread turn bodies
also render markdown now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop the static "· delegated" label — it was the only decoration on the header row and there's no other type/status to disambiguate against, so it added visual noise without information. * Remove the inner bordered/tinted box around the parsed outcome. Markdown body now sits directly under the divider; error variants use the destructive text color instead of a frame. * Rework the expanded-body state machine so the rendered content prioritizes what's already in hand: child detail turns → parsed outcome from the parent's tool_result → fetch in flight → "still running and no output yet". Previously the body keyed off the live delegation binding, so when the DelegationStarted event raced the MCP round-trip the body stayed stuck on "Waiting for the child agent to start…" even though the broker had returned text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sing The lifecycle's `is_delegation_tool_title` and the chat-channel relay's `is_delegation_title` only matched the exact MCP method name. ACP `title` is free-form text the host agent composes; in practice Codex and Claude Code prefix it (`mcp__codeg-delegate__delegate_to_agent`, `Run mcp__…`) and other hosts rephrase it entirely (`Delegate to codex`). When neither literal matched, the lifecycle never registered the parent tool_call_id into the broker's pending queue → the broker fell back to a UUID placeholder for `parent_tool_use_id` → the frontend binding indexed by the real `part.toolCallId` never resolved → the expanded card sat on "Waiting for the child agent to start…" indefinitely. * lifecycle.rs: rename to `is_delegation_invocation` and match by substring on the normalized title OR by raw_input shape (presence of both `agent_type` and `task` string fields). raw_input is a near-zero false-positive signal because that pair only co-occurs on the delegate_to_agent schema. * chat-channel relay: same substring widening on its local `is_delegation_title` (it already had a raw_input fallback for the completion side). * card status: drop the `&& output` guard on the output-available branch so the card flips to "ok" the moment the tool reports completed — even if `output` is empty / not yet joined — instead of staying on "running" and showing the waiting line. * i18n: drop the now-unused `delegatedLabel` key from all 10 locales. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline DelegatedSubThread now stitches every child text segment append-only under a persistent "sub-agent running…" indicator, filtering out tool calls and thinking blocks so the parent UI only ever shows the visible result — later segments never overwrite earlier ones. Parent ToolUse snapshots carry meta.codeg.delegation (new meta_writer module + meta field threaded through models, parsers, ACP types), so a post-refresh remount rebinds to the child connection without depending on the live event surviving. Broker matches each MCP round-trip to the parent's real ACP tool_call_id via a pending queue with a brief race-wait, and the lifecycle dispatcher forwards ToolCall events so that queue actually fills — the card always binds to the real tool_use_id rather than falling back to a synthetic one. i18n consolidates the delegation strings around subAgentRunning across all 10 locales.
…nnels
Every shipping path now lands codeg-mcp next to codeg-server / codeg so
locate_codeg_mcp_binary()'s exe-sibling lookup finds it. Without this,
the delegate_to_agent MCP tool was silently disabled in production
builds because the binary was only produced for local cargo runs.
* Tauri desktop: declare codeg-mcp as bundle.externalBin in
tauri.conf.json. beforeBuildCommand / beforeDevCommand run
src-tauri/scripts/prepare-sidecars.mjs to cargo-build the companion
for the active target triple and stage it as
src-tauri/binaries/codeg-mcp-<triple>{.exe}. build.rs writes a
zero-byte placeholder when the staged file is missing so plain
cargo check / clippy succeed without going through the prepare step,
with a cargo:warning if production paths skip the wrapper.
* CI release matrix: build-tauri runs prepare-sidecars per matrix
target before tauri-action, then asserts the file is staged.
build-server builds and tarballs both binaries and smoke-tests
codeg-mcp --help on native targets.
* Docker: Dockerfile builds both binaries; Dockerfile.ci copies both
per-arch into /usr/local/bin/ with a FATAL guard if either is
missing from the build context.
* install.sh / install.ps1: install both binaries from the release
archive, scan PATH for either as shadowing conflicts, and verify
codeg-mcp lands executable next to codeg-server.
Runtime locator (acp/connection.rs) falls back across CODEG_MCP_BIN
-> current_exe sibling -> which::which and returns Option, gated by an
is_executable_file check (regular file + Unix +x bit). On None,
inject_codeg_delegate_mcp skips MCP injection with a stderr warning
rather than registering a phantom McpServerStdio that would break
stricter agents at session start.
DB: parent_tool_use_id and delegation_call_id, queried by the
include_children filter and list_child_conversations, get
idx_conversation_parent_tool_use_id and
idx_conversation_delegation_call_id in the m20260522 migration to
keep lookups O(log N) as session history grows.
- Recognize delegate_to_agent regardless of the host's server prefix or separator (mcp__codeg-delegate__, codeg-delegate/, dot/colon variants) so the parent always lands on the DelegatedSubThread card. - Surface the task line by walking common JSON-RPC wrappers (arguments, input, params, payload, _meta) plus double-encoded payloads; warn with a truncated sample when no known wrapper matches. - Render the broker outcome as markdown by unwrapping the MCP CallToolResult envelope (structuredContent) and the Codex "Wall time:" prefix; inherit the outer isError flag. - Accumulate the child's live assistant text across turns so prior segments stay visible after STATUS_CHANGED resets liveMessage.
Remove the decorative welcome backdrop and use a shared attached input frame for folder and branch selectors. Align chat input spacing and reduce the default composer height.
…ancel to children Broker now emits DelegationCompleted at all terminal exits (complete_call, timeout, channel-drop, cancel-by-child, cancel-by-parent) via a dedicated event emitter, so frontend bindings flip out of "running" on failure paths. Parent connection cancels and non-end_turn turn completions cascade an ACP cancel to in-flight child delegations, preventing leaked child state.
…arent cancel cascade Child turn-failure stop reasons (refusal / max_tokens / max_turn_requests / empty / unknown) now surface as distinct DelegationError variants with dedicated wire codes and localized labels in all ten supported locales, replacing the generic "subagent_error" fallback. The parent connection's four cascade-cancel sites dispatch cancel_by_parent via tokio::spawn so a slow child teardown can't stall the parent's message loop.
…g and delegation New /settings/general nav entry (slot #2, after Appearance) hosts the default-terminal-shell picker, the Windows-only hardware-acceleration toggle, and the multi-agent delegation panel. /settings/system now carries only version updates, network proxy, and language, and /settings/agents is back to AcpAgentSettings on its own. Delegation section header swapped to the Bubbles icon. i18n: 23 terminal/rendering/restart keys moved from SystemSettings to GeneralSettings across all ten locales, with loadFailed copied into both namespaces. Backend label_key docstrings updated to resolve under GeneralSettings.*.
…itter fanout The delegation status badge now renders translated labels for ar / de / es / fr / ja / ko / pt, replacing the English placeholders for every running / ok / error key including the granular child-failure codes. Broker test suite gains two integration tests that exercise the production ConnectionManagerEventEmitter against a real ConnectionManager: one asserts DelegationCompleted fans out to both the per-connection stream and the InternalEventBus, the other pins the silent no-op behavior when the parent connection is gone.
Move the focus-within ring from the outer wrapper onto the composer with ring-inset so focusing the textarea no longer lights up the folder/branch picker row beneath it. Drop the outer wrapper's border, deepen its background to bg-muted/60 for a clearer tray under the input, and give the composer its own full border. Trim the picker row to py-1 and align its leading padding with the composer's button row via pl-2.
… and center chevron
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.
No description provided.