Skip to content

feat(harness): shared ACP transport — hermes/kimi/kiro/qoder/trae (5 agents, one client) #255

Description

@oxoxDev

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

  1. 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.
  2. 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).
  3. Standard ACP lifecycleinitializesession/newsession/set_model (consumes the per-task model from the model-discovery slice) → session/prompt; handle session/update notifications.
  4. 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.
  5. 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.
  6. 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

  • One AcpTransport drives the full ACP lifecycle over stdio; unit-tested against a mock ACP peer.
  • All five providers (hermes/kimi/kiro/qoder/trae) registered and launchable; each emits the standard SessionEnvelopeV2 kinds (agent_message/thinking/tool_call/tool_result/approval_request/status/error).
  • session/request_permission surfaces as an approval_request through the existing gate — no blanket bypass.
  • Concurrency safe w.r.t. the shared-workspace gap (isolation landed, or concurrency bounded with a logged cap).
  • Tests + typecheck + lint green; ready for openhuman vendor/tinyplace submodule bump.

Related

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions