Skip to content

feat(chat): add experimental ACP runtime - #5376

Closed
werkamsus wants to merge 3 commits into
ParadevAI:mainfrom
werkamsus:nick/acp-chat-integration
Closed

werkamsus wants to merge 3 commits into
ParadevAI:mainfrom
werkamsus:nick/acp-chat-integration

Conversation

@werkamsus

@werkamsus werkamsus commented Jun 27, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds an experimental ACP-backed chat runtime for Superset workspace chat while keeping the existing Mastra chat runtime as the default.

This PR is intentionally additive:

  • SUPERSET_EXPERIMENTAL_ACP_CHAT remains the host-service runtime flag.
  • The Desktop Experimental settings tab now exposes the flag as Use ACP chat runtime and restarts active host services after changes.
  • The default ACP command is omp acp; advanced users can still override with SUPERSET_ACP_COMMAND and SUPERSET_ACP_ARGS.
  • The existing renderer chat/tRPC contract is preserved.
  • ACP lifecycle support covers initialize, session/new, session/prompt, streaming session/update handling, cancellation, process cleanup, timeouts, and ACP permission requests.

Research notes:

  • Zed ACP reference uses stdio JSON-RPC with initialize, session/new, session/prompt, session/cancel, and session/update notifications.
  • Pi ACP and Oh My Pi expose the same lifecycle; Oh My Pi documents omp acp as the stdio entrypoint.
  • ACP plan updates are informational protocol updates, not approval requests. This PR keeps plan text rendering honest and does not fake plan approval support where ACP has no response path.

Related Issues

Related to #3350
Related to #2019
Related to #4628
Related to #5326

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

Ran:

bun test packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts packages/host-service/src/runtime/chat/acp-protocol.test.ts packages/host-service/test/integration/chat.integration.test.ts
bun run --cwd packages/host-service typecheck
bun run lint

Also ran:

bun run --cwd packages/local-db generate

Screenshots (if applicable)

Not included yet. The PR is draft while the ACP setting and review feedback fixes are being finalized.

Additional Notes

This PR is draft for now.

Follow-up commit addresses review feedback from CodeRabbit/Greptile/Cubic:

  • serializes ACP prompts per chat session
  • settles pending prompts on stop()
  • avoids creating ACP runtimes for stop/approval/control-only actions
  • disposes both runtime maps instead of relying on the current experimental flag value
  • clears pending permission state on terminal failures
  • prevents overlapping permission waiters
  • preserves always_allow_category semantics
  • preserves tool input during progress updates
  • supports numeric ACP tool IDs consistently
  • adds ACP request timeouts
  • tracks child process exit state for SIGKILL fallback and stale reference cleanup
  • guarantees ACP runtime disposal in tests

Adds an ACP stdio client and chat runtime adapter behind SUPERSET_EXPERIMENTAL_ACP_CHAT so existing Mastra chat remains the default. The adapter launches a configurable ACP command, initializes protocol v1, creates an ACP session, streams session/update message and tool state into the existing chat snapshot shape, handles cancellation, and bridges ACP permission requests to the existing approval UI.\n\nDefault ACP command is 'omp acp'; override with SUPERSET_ACP_COMMAND and SUPERSET_ACP_ARGS.\n\nVerification:\n- bun test packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts packages/host-service/test/integration/chat.integration.test.ts\n- bun run --cwd packages/host-service typecheck\n- bun run lint
@coderabbitai

coderabbitai Bot commented Jun 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73b7ab23-5011-4c9d-aebc-b38a5a14ee65

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new ACP (agent control plane) chat backend. A StdioAcpAgentClient spawns an external agent process and communicates via stdio JSON-RPC. AcpChatRuntime manages session lifecycle, message history, display state, and permission request/response flows. ChatRuntimeManager is refactored to branch between the new ACP backend and the existing Mastra backend based on a feature flag.

Changes

ACP Chat Runtime

Layer / File(s) Summary
ACP type definitions
packages/host-service/src/runtime/chat/acp-types.ts
Adds all exported TypeScript types: message payloads, content part interfaces, interactive workflow payloads (ChatApprovalPayload, ChatPendingQuestion), ChatDisplayState, and ChatSnapshot.
StdioAcpAgentClient JSON-RPC transport
packages/host-service/src/runtime/chat/stdio-acp-client.ts
Implements the stdio JSON-RPC client that spawns the agent process, routes inbound responses/notifications/requests, exposes initialize/newSession/prompt/cancel/dispose, and introduces AcpJsonRpcError.
ACP protocol conversion and display-state mutation
packages/host-service/src/runtime/chat/acp-protocol.ts
Provides payloadToAcpPrompt, payloadToChatParts, appendAcpUpdateToDisplayState (incremental state mutation for all session update event types), finishCurrentAssistantMessage, and internal helpers.
AcpChatRuntime lifecycle and permission handling
packages/host-service/src/runtime/chat/acp-chat-runtime.ts
Implements AcpChatRuntime: construction, initialize, sendMessage (with auth-required/error finalization), stop/dispose, handlePermissionRequest/respondToApproval with selectPermissionOption decision mapping.
ChatRuntimeManager ACP/Mastra branching
packages/host-service/src/runtime/chat/chat.ts
Refactors ChatRuntimeManager to maintain separate Mastra/ACP runtime maps and in-flight creation tracking, adds shouldUseAcpChat() branching across all public methods, rewrites display-state shaping, and adds Mastra helpers for optimistic question response and restart.
AcpChatRuntime integration tests
packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts
Adds Bun integration tests for streaming message flow and permission request/response flow using generated in-process agent scripts as fakes.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Caller
  participant ChatRuntimeManager
  participant AcpChatRuntime
  participant StdioAcpAgentClient
  participant AgentProcess

  Client->>ChatRuntimeManager: sendMessage(payload)
  ChatRuntimeManager->>AcpChatRuntime: sendMessage(payload)
  AcpChatRuntime->>StdioAcpAgentClient: prompt(sessionId, acpBlocks)
  StdioAcpAgentClient->>AgentProcess: session/prompt (stdin)
  AgentProcess-->>StdioAcpAgentClient: session/update notifications (stdout)
  StdioAcpAgentClient->>AcpChatRuntime: onUpdate → appendAcpUpdateToDisplayState
  AgentProcess->>StdioAcpAgentClient: session/request_permission (stdout)
  StdioAcpAgentClient->>AcpChatRuntime: onPermissionRequest → pendingApproval set
  Client->>ChatRuntimeManager: respondToApproval(decision)
  ChatRuntimeManager->>AcpChatRuntime: respondToApproval(decision)
  AcpChatRuntime->>AcpChatRuntime: selectPermissionOption → resolve waiter
  StdioAcpAgentClient-->>AgentProcess: permission result (stdin)
  AgentProcess-->>StdioAcpAgentClient: stopReason response (stdout)
  StdioAcpAgentClient-->>AcpChatRuntime: prompt resolves
  AcpChatRuntime->>AcpChatRuntime: finishCurrentAssistantMessage
  AcpChatRuntime-->>ChatRuntimeManager: stopReason
  ChatRuntimeManager-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 Hop hop, a new agent hops in,
JSON lines flow through stdin!
Permissions asked, decisions made,
Tool calls tracked, states displayed.
The rabbit wires two backends tight—
ACP or Mastra, both feel right! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately highlights the main change: adding an experimental ACP runtime.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description matches the template and includes the required sections with relevant details on changes, issues, type, testing, screenshots, and notes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ghost

ghost commented Jun 27, 2026 •

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 7 individual chapters for you:

Title
1 Define ACP chat types and protocol
2 Implement Stdio ACP client and runtime
3 Integrate ACP into ChatRuntimeManager
4 Add database schema for ACP setting
5 Wire ACP setting through Desktop app
6 Expose ACP setting in Experimental UI
7 Ensure host services start for active org
Open in Stage

Chapters generated by Stage for commit 92e5a38 on Jun 28, 2026 12:36am UTC.

@greptile-apps

greptile-apps Bot commented Jun 27, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an experimental ACP chat runtime behind an environment flag. The main changes are:

  • A new ACP runtime and stdio JSON-RPC client.
  • ACP prompt, update, permission, and message-shape adapters.
  • ChatRuntimeManager routing between Mastra and ACP paths.
  • Tests for basic ACP streaming and permission approval.

Confidence Score: 4/5

The ACP runtime path has lifecycle bugs that can corrupt streamed chat state, hang stopped prompts, and leak runtimes.

  • Concurrent sends for one ACP session share one assistant message state.
  • Stop marks the UI cancelled without settling the pending prompt request.
  • Runtime cleanup can target the wrong map if the experimental flag changes.
  • ACP plan updates do not surface the existing approval UI.

packages/host-service/src/runtime/chat/acp-chat-runtime.ts, packages/host-service/src/runtime/chat/chat.ts, packages/host-service/src/runtime/chat/acp-protocol.ts

Important Files Changed

Filename Overview
packages/host-service/src/runtime/chat/acp-chat-runtime.ts Adds the ACP session runtime, including prompt sending, cancellation, approval handling, and chat state storage.
packages/host-service/src/runtime/chat/stdio-acp-client.ts Adds the stdio JSON-RPC client used to start the ACP process and route requests, responses, notifications, and permission requests.
packages/host-service/src/runtime/chat/acp-protocol.ts Adds conversion between Superset chat payloads/state and ACP prompt/update shapes.
packages/host-service/src/runtime/chat/chat.ts Updates ChatRuntimeManager to keep Mastra as the default path and switch to ACP when the experimental flag is enabled.
packages/host-service/src/runtime/chat/acp-types.ts Adds shared ACP chat runtime types for messages, display state, approvals, questions, and plans.
packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts Adds tests for sequential ACP streaming and permission approval behavior.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant UI as Chat UI
  participant M as ChatRuntimeManager
  participant R as AcpChatRuntime
  participant C as StdioAcpAgentClient
  participant A as ACP agent

  UI->>M: sendMessage(sessionId)
  M->>R: sendMessage(payload)
  R->>C: session/prompt
  C->>A: JSON-RPC prompt
  A-->>C: session/update
  C-->>R: onUpdate(update)
  R->>R: append to displayState
  UI->>M: stop(sessionId)
  M->>R: stop()
  R->>C: session/cancel
  R->>R: finish as cancelled
  A-->>C: prompt response may be late or absent
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant UI as Chat UI
  participant M as ChatRuntimeManager
  participant R as AcpChatRuntime
  participant C as StdioAcpAgentClient
  participant A as ACP agent

  UI->>M: sendMessage(sessionId)
  M->>R: sendMessage(payload)
  R->>C: session/prompt
  C->>A: JSON-RPC prompt
  A-->>C: session/update
  C-->>R: onUpdate(update)
  R->>R: append to displayState
  UI->>M: stop(sessionId)
  M->>R: stop()
  R->>C: session/cancel
  R->>R: finish as cancelled
  A-->>C: prompt response may be late or absent
Loading

Comments Outside Diff (1)

  1. packages/host-service/src/runtime/chat/acp-protocol.ts, line 641-647 (link)

    P2 Numeric Tool IDs Disconnect Results

    tool_call_update ignores any toolCallId that is not already a string. If an ACP agent sends numeric tool IDs, the earlier tool_call path creates a random ID, but the update path drops the numeric ID entirely, so the tool result is never attached and the tool can stay visible as running.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: packages/host-service/src/runtime/chat/acp-protocol.ts
    Line: 641-647
    
    Comment:
    **Numeric Tool IDs Disconnect Results**
    
    `tool_call_update` ignores any `toolCallId` that is not already a string. If an ACP agent sends numeric tool IDs, the earlier `tool_call` path creates a random ID, but the update path drops the numeric ID entirely, so the tool result is never attached and the tool can stay visible as running.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 5 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 5
packages/host-service/src/runtime/chat/acp-chat-runtime.ts:92-112
**Concurrent Prompts Share State**

When two `sendMessage` mutations reach the same ACP session before the first prompt resolves, both prompts append updates into the same `displayState.currentMessage`. The UI can show one assistant message with mixed chunks or tool calls from two user turns, and whichever prompt finishes first can finalize the shared message for the wrong turn.

### Issue 2 of 5
packages/host-service/src/runtime/chat/acp-chat-runtime.ts:134-143
**Cancelled Prompt Still Pending**

`stop()` sends `session/cancel` and marks the local turn cancelled, but it does not settle the pending `session/prompt` request. If the ACP agent stops streaming without replying to the original prompt request, the `sendMessage` mutation can hang indefinitely; if it replies later, the old prompt completion can run after the UI already recorded a cancelled turn.

### Issue 3 of 5
packages/host-service/src/runtime/chat/chat.ts:375-381
**Disposal Uses Current Flag**

`disposeRuntime` re-reads `SUPERSET_EXPERIMENTAL_ACP_CHAT` instead of disposing the runtime that actually owns the session. If the flag changes during the process lifetime, a Mastra session can be looked up in the ACP map, or an ACP session in the Mastra map, causing the method to return without aborting the real runtime or killing the ACP child process.

### Issue 4 of 5
packages/host-service/src/runtime/chat/acp-protocol.ts:225-233
**Plans Never Become Approvals**

ACP `plan` updates are converted into plain assistant text, while `pendingPlanApproval` remains `null` for the whole ACP display state. An agent that sends a plan requiring approval will show the plan text but no approve or reject controls, so the plan workflow cannot be completed from the existing chat UI.

### Issue 5 of 5
packages/host-service/src/runtime/chat/acp-protocol.ts:641-647
**Numeric Tool IDs Disconnect Results**

`tool_call_update` ignores any `toolCallId` that is not already a string. If an ACP agent sends numeric tool IDs, the earlier `tool_call` path creates a random ID, but the update path drops the numeric ID entirely, so the tool result is never attached and the tool can stay visible as running.

Reviews (1): Last reviewed commit: "feat(chat): add experimental ACP runtime" | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts`:
- Around line 16-52: Ensure each AcpChatRuntime instance is always cleaned up by
moving runtime.dispose() into a finally block in the affected tests. In the
AcpChatRuntime test cases that call initialize(), sendMessage(), listMessages(),
and getDisplayState(), wrap the assertions in try/finally so dispose() runs even
if an assertion fails, preventing leaked stdio child processes and test
interference.
- Around line 98-109: The waitFor helper currently fails after a fixed number of
event-loop turns instead of a real elapsed-time limit, which can make the
acp-chat-runtime permission test flaky on slower machines. Update waitFor to
measure wall-clock time with an explicit timeout budget and keep polling until
either predicate() passes or that duration elapses; use the existing
nextEventLoopTurn helper for yielding, and keep the timeout error in waitFor
descriptive so it still identifies the condition that timed out.

In `@packages/host-service/src/runtime/chat/acp-chat-runtime.ts`:
- Around line 57-66: The ACP error path in ACPChatRuntime’s onError currently
finalizes the turn but leaves pendingPermission alive, so clear or cancel that
JSON-RPC permission waiter before calling finishCurrentAssistantMessage. Update
the error handling in acp-chat-runtime’s onError flow (and the related
terminal-failure path around the same ACP prompt handling) to explicitly
resolve/reject and null out pendingPermission whenever the prompt fails,
especially when displayState.isRunning is true.
- Around line 233-246: Update the approval resolution logic in
acp-chat-runtime’s option-matching flow so always_allow_category does not fall
back to allow_once or generic “allow” labels. In the preferredKinds and
labelNeedles handling used to select a chat approval option, keep
always_allow_category restricted to explicit permanent/always choices only, and
if no such option exists, return an error or failure instead of silently
treating it as a one-time approval. Use the decision-based matching around the
existing option selection helpers to preserve the semantics of
always_allow_category.
- Around line 201-206: The permission waiter in ACPChatRuntime is being
overwritten when a new request arrives, which leaves the previous Promise
unresolved. Update the pending-permission flow in ACPChatRuntime so it checks
for an existing this.pendingPermission before assigning a new waiter; if one is
already active, reject or return the new request, or enqueue it, but do not
replace the current resolver. Use the existing pendingPermission, resolve, and
toolCallId handling to keep only one active permission request at a time.

In `@packages/host-service/src/runtime/chat/acp-protocol.ts`:
- Around line 199-221: The tool_call_update handling in acp-protocol should
preserve the original tool input instead of overwriting it with an empty object
when progress updates omit content. Update the logic in the tool update branch
to reuse the existing entry from state.activeTools or the original args/input
for the same toolCallId, and only replace input when update.content is actually
present. Keep the behavior in ensureCurrentAssistantMessage, findToolName, and
state.activeTools consistent so non-terminal updates do not erase the renderer’s
call context.

In `@packages/host-service/src/runtime/chat/chat.ts`:
- Around line 562-568: The ACP control-only handlers in chat.ts are still
creating runtimes via getOrCreateAcpRuntime, which can spawn a new acp process
for stale stop/response actions. Update restartFromMessage, stop,
respondToApproval, respondToQuestion, and respondToPlan to use an
existing-runtime lookup path instead of creating one, and keep
restartFromMessage throwing the unsupported restart error without initializing
ACP. Use the chat runtime methods around shouldUseAcpChat(),
getOrCreateAcpRuntime, and the ACP approval/question/plan handlers as the places
to refactor.

In `@packages/host-service/src/runtime/chat/stdio-acp-client.ts`:
- Around line 173-183: The request flow in stdio-acp-client’s request method can
leave entries stuck in pending forever when the agent stops responding. Add a
per-request timeout in request() that rejects the promise and removes the
pending entry if no response arrives in time, and make sure the timeout is
cleared when a reply or write failure resolves the request. Apply the fix
through the request/pending handling used by initialize(), newSession(), and
prompt().
- Around line 124-132: The shutdown logic in the stdio ACP client uses
child.killed to decide whether to escalate, but that flag is set as soon as
SIGTERM is sent and does not confirm the process exited. Update the kill path in
the client shutdown flow to track actual exit state separately via the existing
child.once("exit") handling (or an explicit exited flag), and use that state to
decide whether to send the SIGKILL fallback. Keep the timeout/promise flow
intact, but only suppress escalation after the child has really exited.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b365ab39-41e0-46e1-9359-7d7308e05a27

📥 Commits

Reviewing files that changed from the base of the PR and between 5eec659 and 9215d3c.

📒 Files selected for processing (6)
  • packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts
  • packages/host-service/src/runtime/chat/acp-chat-runtime.ts
  • packages/host-service/src/runtime/chat/acp-protocol.ts
  • packages/host-service/src/runtime/chat/acp-types.ts
  • packages/host-service/src/runtime/chat/chat.ts
  • packages/host-service/src/runtime/chat/stdio-acp-client.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12 issues found across 6 files

Architecture diagram
sequenceDiagram
    participant UI as Chat UI (Renderer)
    participant TRPC as tRPC Router
    participant Manager as ChatRuntimeManager
    participant Mastra as MastraRuntime (unchanged)
    participant ACP as AcpChatRuntime (NEW)
    participant Client as StdioAcpAgentClient (NEW)
    participant Agent as ACP Agent Process (NEW)
    participant Protocol as acp-protocol (NEW)

    Note over UI,Agent: Experimental ACP Chat Flow (only when flag enabled)

    UI->>TRPC: sendMessage(sessionId, payload)
    TRPC->>Manager: sendMessage(sessionId, payload)

    alt SUPERSET_EXPERIMENTAL_ACP_CHAT=1
        Manager->>ACP: route to AcpChatRuntime
        Note over ACP,Agent: NEW: Session creation & initialization
        ACP->>Client: initialize()
        Client->>Agent: spawn(child_process, stdio)
        Agent-->>Client: stderr/sidestream
        Client->>Agent: JSON-RPC: initialize(protocolVersion, clientCapabilities)
        Agent-->>Client: {result: protocolVersion, agentCapabilities}
        Client-->>ACP: initialized

        ACP->>Client: newSession(cwd)
        Client->>Agent: JSON-RPC: session/new(cwd, mcpServers)
        Agent-->>Client: {sessionId: "acp-session-1"}
        Client-->>ACP: sessionId

        Note over ACP,Protocol: NEW: sendMessage with streaming
        ACP->>Client: prompt(sessionId, promptBlocks)
        Client->>Agent: JSON-RPC: session/prompt(sessionId, prompt)
        Agent-->>Client: session/update (agent_message_chunk) "Hello "
        Agent-->>Client: session/update (agent_thought_chunk) "...thinking..."
        Agent-->>Client: session/update (tool_call) id="tool-1", title="read", input={path:"README.md"}
        Agent-->>Client: session/update (tool_call_update) id="tool-1", status="completed"
        Client-->>ACP: update notification
        Note over ACP,Protocol: appendAcpUpdateToDisplayState()
        Protocol-->>ACP: returns updated ChatDisplayState

        opt ACP requests permission
            Agent-->>Client: session/request_permission (toolCall, options)
            Client-->>ACP: onPermissionRequest
            ACP-->>UI: pendingApproval set in display state
            UI->>ACP: respondToApproval({decision: "approve"})
            ACP-->>Agent: resolve permission (selected option)
        end

        Agent-->>Client: session/prompt result {stopReason: "end_turn"}
        Client-->>ACP: finished
        ACP->>ACP: finishCurrentAssistantMessage()
        ACP-->>TRPC: {stopReason: "end_turn"}

        opt User stops
            UI->>TRPC: stop(sessionId)
            TRPC->>ACP: stop()
            ACP->>Client: cancel(sessionId)
            Client->>Agent: JSON-RPC: session/cancel
            ACP->>ACP: finishCurrentAssistantMessage(stopReason: "cancelled")
        end

        opt ACP process error or exit
            Agent--xClient: child process exits unexpectedly
            Client-->>ACP: onError(error)
            ACP->>ACP: finishCurrentAssistantMessage(stopReason: "error", errorMessage)
        end

    else default (Mastra)
        Manager->>Mastra: route to existing MastraRuntime
        Note over Mastra: Unchanged flow (not detailed)
    end

    TRPC-->>UI: result (shape unchanged)
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@werkamsus
werkamsus marked this pull request as draft June 27, 2026 23:52
Adds the ACP chat runtime toggle to Desktop Settings > Experimental, persists it in local SQLite settings, and restarts active host services so the host-service env picks it up.\n\nAddresses PR review feedback by serializing ACP prompts, settling prompts on stop, avoiding runtime creation for control-only actions, disposing the runtime owner independent of current flags, clearing pending permission waiters on terminal failures, preserving tool inputs, supporting numeric tool ids, adding request timeouts, and hardening child process cleanup.\n\nUpdates the PR body to the repo template and keeps PR ParadevAI#5376 in draft.\n\nVerification:\n- bun test packages/host-service/src/runtime/chat/acp-chat-runtime.test.ts packages/host-service/src/runtime/chat/acp-protocol.test.ts packages/host-service/test/integration/chat.integration.test.ts\n- bun run --cwd packages/host-service typecheck\n- bun run --cwd packages/local-db typecheck\n- bun run lint
@werkamsus

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 5574c90 and PR is now draft. I moved the ACP runtime flag into Settings → Experimental, updated the PR body to the repo template, and addressed the valid review findings: prompt serialization/cancel settling, control-only runtime lookup, flag-independent disposal, permission cleanup/guarding, always-allow matching, numeric tool IDs, tool input preservation, request timeouts, child exit cleanup, and test disposal. I did not turn ACP plan updates into pending plan approvals because ACP plan updates are informational protocol updates with no approval response path; faking that would leave the agent uninformed.

Ensure LocalHostServiceProvider starts a host-service child for the session active organization even when the synced organization collection has not populated yet. This keeps local workspace creation unblocked after app startup.
@Kitenite

Copy link
Copy Markdown
Member

Closing: main already ships this. ACP stack was added then deleted in f2f000b (#6434). Thanks for the PR; if you think the shipped version misses a case, open an issue and link this.

@Kitenite Kitenite closed this Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants