Summary
Add a shared ACP (Agent Client Protocol) transport to the coding-agent daemon so five agents that all speak ACP — hermes, kimi, kiro, qoder, trae — become first-class harness providers through one clean-room JSON-RPC-over-stdio client. Each agent then differs only by its launch command.
Implementation home for the [OpenHuman] agent-runtime abstraction epic (tinyhumansai/openhuman#4731). Reference pattern only — clean-room, no third-party source copied.
Problem (current state, sdk/typescript/src/cli/)
- The harness has exactly two capture engines: interactive file-tail (
HarnessSessionTailer) and daemon stdout-line (runProviderAttempt, daemon/providers.ts). ACP is a request/response JSON-RPC protocol — neither engine fits.
- No stdio JSON-RPC client exists in the SDK. The only
jsonrpc: "2.0" code (api/a2a.ts, api/mcp.ts) is HTTP-based and spawns no child. So the transport is greenfield.
- Result: five otherwise-supportable agents can't be wrapped at all.
Approach
- New
AcpTransport (e.g. daemon/acp.ts) sitting beside runProviderAttempt as a third executor. It satisfies RunTaskOptions / RunTaskResult so DaemonRuntime.runTask dispatches to it unchanged.
- Clean-room JSON-RPC 2.0 over stdio — line-framed request/response + notification handling, built fresh (borrow only the request/response type shape from
api/a2a.ts).
- Standard ACP lifecycle —
initialize → session/new → session/set_model (consumes the per-task model from the model-discovery slice) → session/prompt; handle session/update notifications.
- Approvals through the existing gate — answer
session/request_permission by emitting an approval_request event routed through OpenHuman's approval flow. No blanket auto-approve / yolo bypass.
- Translate to the shared feed — emit
HarnessSemanticEvent[] directly from ACP notifications (bypassing the NDJSON LINE_MAPPERS), into the same onEvent → reply / buildEventEnvelopeV2 → publish seam everything else uses. One acpEventsFromLine-style translator backs all five; provider identity only drives bin/argv.
- Register 5 providers — extend
HarnessProvider union (types/harness.ts), DAEMON_PROVIDERS / PROVIDER_BINS (daemon/providers.ts), and the protocol PROVIDERS set (daemon/protocol.ts).
Risk / dependency
- Shared-workspace gap. The daemon currently runs every task in one shared
workspace (no per-task isolation, daemon/runtime.ts). Running multiple ACP agents concurrently likely needs per-task workdir isolation — either land isolation first or bound ACP concurrency to 1 until it exists.
- Depends on the model-discovery slice for
session/set_model.
Acceptance criteria
Related
Summary
Add a shared ACP (Agent Client Protocol) transport to the coding-agent daemon so five agents that all speak ACP — hermes, kimi, kiro, qoder, trae — become first-class harness providers through one clean-room JSON-RPC-over-stdio client. Each agent then differs only by its launch command.
Implementation home for the [OpenHuman] agent-runtime abstraction epic (tinyhumansai/openhuman#4731). Reference pattern only — clean-room, no third-party source copied.
Problem (current state,
sdk/typescript/src/cli/)HarnessSessionTailer) and daemon stdout-line (runProviderAttempt,daemon/providers.ts). ACP is a request/response JSON-RPC protocol — neither engine fits.jsonrpc: "2.0"code (api/a2a.ts,api/mcp.ts) is HTTP-based and spawns no child. So the transport is greenfield.Approach
AcpTransport(e.g.daemon/acp.ts) sitting besiderunProviderAttemptas a third executor. It satisfiesRunTaskOptions/RunTaskResultsoDaemonRuntime.runTaskdispatches to it unchanged.api/a2a.ts).initialize→session/new→session/set_model(consumes the per-task model from the model-discovery slice) →session/prompt; handlesession/updatenotifications.session/request_permissionby emitting anapproval_requestevent routed through OpenHuman's approval flow. No blanket auto-approve / yolo bypass.HarnessSemanticEvent[]directly from ACP notifications (bypassing the NDJSONLINE_MAPPERS), into the sameonEvent→ reply /buildEventEnvelopeV2→ publish seam everything else uses. OneacpEventsFromLine-style translator backs all five; provider identity only drives bin/argv.HarnessProviderunion (types/harness.ts),DAEMON_PROVIDERS/PROVIDER_BINS(daemon/providers.ts), and the protocolPROVIDERSset (daemon/protocol.ts).Risk / dependency
workspace(no per-task isolation,daemon/runtime.ts). Running multiple ACP agents concurrently likely needs per-task workdir isolation — either land isolation first or bound ACP concurrency to 1 until it exists.session/set_model.Acceptance criteria
AcpTransportdrives the full ACP lifecycle over stdio; unit-tested against a mock ACP peer.SessionEnvelopeV2kinds (agent_message/thinking/tool_call/tool_result/approval_request/status/error).session/request_permissionsurfaces as anapproval_requestthrough the existing gate — no blanket bypass.vendor/tinyplacesubmodule bump.Related