@botbotgo/agent-harness 0.0.213 → 0.0.215

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -126,10 +126,11 @@ The public API spans a full product runtime—persistent records, memory and evi
126
126
 
127
127
  - **Core runtime API:** `createAgentHarness`, `request`, `subscribe`, `resolveApproval`, inspection helpers, and stable persisted runtime records for `requests`, `sessions`, `approvals`, `events`, and artifacts.
128
128
  - **Runtime memory and evidence:** `memorize`, `recall`, `listMemories`, memory policy hooks, `listArtifacts`, `getArtifact`, `exportEvaluationBundle`, `replayEvaluationBundle`, and request/session evidence export helpers.
129
- - **Protocol and transport surfaces:** `createAcpServer`, `serveAcpStdio`, `serveAcpHttp`, `serveA2aHttp`, `serveAgUiHttp`, and `createRuntimeMcpServer` / `serveRuntimeMcpOverStdio`.
129
+ - **Protocol and transport surfaces:** `createAcpServer`, `createAcpStdioClient`, `serveAcpStdio`, `serveAcpHttp`, `serveA2aHttp`, `serveAgUiHttp`, and `createRuntimeMcpServer` / `serveRuntimeMcpOverStdio`.
130
130
  - **Governed workspace runtime:** YAML-owned routing, concurrency, maintenance, MCP policy, runtime governance bundles, and approval defaults for sensitive memory or write-like MCP side effects.
131
131
 
132
132
  If you integrate external clients, treat `deepagents-acp` as the primary protocol direction: clients connect through that surface while `agent-harness` keeps persistence, recovery, approvals, and operator control on the runtime side.
133
+ Keep the standard stack split explicit: MCP connects agents to resources and tools, A2A bridges task exchange between agent platforms, ACP is the client-to-runtime boundary, AG-UI is the UI event surface, and runtime MCP exposes the operator control plane.
133
134
 
134
135
  ## The problem this solves
135
136
 
@@ -267,6 +268,7 @@ These scenarios map most directly to what the runtime is built for:
267
268
 
268
269
  - Enterprise internal agent runtime: approvals, restart-safe recovery, operator evidence, and policy-owned MCP access.
269
270
  - Code modernization runtime: long-running coding flows, approval checkpoints, resumable runs, and exported evidence packages.
271
+ - Agent-ops and remediation workflow runtime: parallel agent attempts, human review gates, durable evidence, and operator visibility for security or maintenance work.
270
272
  - Protocol bridge runtime: ACP, A2A, AG-UI, and runtime MCP on one stable control plane instead of bespoke per-surface glue.
271
273
 
272
274
  Typical runtime governance defaults now look like:
@@ -992,6 +994,7 @@ Primary exports:
992
994
  - `exportEvaluationBundle`
993
995
  - `replayEvaluationBundle`
994
996
  - `createAcpServer`
997
+ - `createAcpStdioClient`
995
998
  - `serveAcpHttp`
996
999
  - `serveAcpStdio`
997
1000
  - `serveA2aHttp`
@@ -1020,8 +1023,8 @@ ACP transport notes:
1020
1023
  - `serveAcpStdio(runtime)` exposes newline-delimited JSON-RPC over stdio for local IDE, CLI, or subprocess clients.
1021
1024
  - `serveAcpHttp(runtime)` exposes JSON-RPC over HTTP plus SSE runtime events so remote operator surfaces can connect without importing the runtime in-process.
1022
1025
  - ACP transport validation now covers the reference-client core flow: capability discovery, request submit, session lookup, request lookup, invalid-JSON handling, notification calls without response ids, stdio JSON-RPC, and HTTP plus SSE runtime notifications.
1023
- - For the thinnest editor or CLI starter, begin with `agent-harness acp serve --workspace . --transport stdio` and mirror the `examples/protocol-hello-world/app/acp-stdio-hello-world.mjs` wire shape.
1024
- - `serveA2aHttp(runtime)` exposes an A2A-compatible HTTP JSON-RPC bridge plus agent card discovery, mapping both existing methods such as `message/send` and newer aliases such as `SendMessage`, `GetTask`, `ListTasks`, `CancelTask`, and `SubscribeToTask` onto the existing session/request runtime surface.
1026
+ - For the thinnest editor or CLI starter, begin with `agent-harness acp serve --workspace . --transport stdio` and mirror the `examples/protocol-hello-world/app/acp-stdio-hello-world.mjs` wire shape. Applications that want an in-process reference client can use `createAcpStdioClient(...)` to issue JSON-RPC requests and route runtime notifications without hand-rolling line parsing.
1027
+ - `serveA2aHttp(runtime)` exposes an A2A-compatible HTTP JSON-RPC bridge plus agent card discovery, mapping both existing methods such as `message/send` and A2A v1.0 PascalCase methods such as `SendMessage`, `GetTask`, `ListTasks`, `CancelTask`, and `SubscribeToTask` onto the existing session/request runtime surface. The v1.0 path publishes `supportedInterfaces`, `TASK_STATE_*` statuses, the `{ task }` `SendMessage` wrapper, and explicit unsupported errors for streaming or push notifications.
1025
1028
  - `serveAgUiHttp(runtime)` exposes an AG-UI-compatible HTTP SSE bridge that projects runtime lifecycle, text output, upstream thinking, step progress, and tool calls onto `RUN_*`, `TEXT_MESSAGE_*`, `THINKING_TEXT_MESSAGE_*`, `STEP_*`, and `TOOL_CALL_*` events for UI clients.
1026
1029
  - `createRuntimeMcpServer(runtime)` and `serveRuntimeMcpOverStdio(runtime)` expose the persisted runtime control surface itself as MCP tools, including sessions, requests, approvals, artifacts, events, and package export helpers.
1027
1030
  - `listRequestEvents(...)` and `exportRequestPackage(...)` are the request-first inspection helpers.
package/README.zh.md CHANGED
@@ -122,10 +122,11 @@ try {
122
122
 
123
123
  - **核心 runtime API:** `createAgentHarness`、`request`、`subscribe`、`resolveApproval`、各类查询与检查辅助 API,以及稳定持久化的 `requests`、`sessions`、`approvals`、`events` 与 artifacts 记录。
124
124
  - **运行时 memory 与证据能力:** `memorize`、`recall`、`listMemories`、memory policy hooks、`listArtifacts`、`getArtifact`、`exportEvaluationBundle`、`replayEvaluationBundle`,以及 request / session 级证据导出辅助函数。
125
- - **协议与传输层:** `createAcpServer`、`serveAcpStdio`、`serveAcpHttp`、`serveA2aHttp`、`serveAgUiHttp`,以及 `createRuntimeMcpServer` / `serveRuntimeMcpOverStdio`。
125
+ - **协议与传输层:** `createAcpServer`、`createAcpStdioClient`、`serveAcpStdio`、`serveAcpHttp`、`serveA2aHttp`、`serveAgUiHttp`,以及 `createRuntimeMcpServer` / `serveRuntimeMcpOverStdio`。
126
126
  - **受治理的工作区运行时:** 由 YAML 持有的路由、并发、维护、MCP 策略、runtime governance bundles,以及针对敏感 memory 或写类 MCP 副作用的默认审批门槛。
127
127
 
128
128
  若你的产品需要对接外部客户端,可从本节理解边界:`deepagents-acp` 是主要的外部协议接入方向;持久化、恢复、审批与运行控制仍由 `agent-harness` 在运行时侧承担。
129
+ 协议栈分工也要保持明确:MCP 连接资源和工具,A2A 承接 agent 平台之间的 task exchange,ACP 是 client-to-runtime 边界,AG-UI 是 UI 事件面,runtime MCP 则把 operator control plane 以 MCP tools 暴露出去。
129
130
 
130
131
  ## 这能解决什么问题
131
132
 
@@ -263,6 +264,7 @@ AI 让 agent 逻辑、工具调用和工作流代码更容易生成,真正更
263
264
 
264
265
  - 企业内部智能体运行时:审批、重启恢复、运维侧证据链,以及由策略约束的 MCP 访问控制。
265
266
  - 代码现代化运行时:长链路 coding flow、审批检查点、可恢复 runs,以及可导出的运行证据包。
267
+ - Agent ops 与 remediation workflow runtime:并行 agent 尝试、人工 review gate、持久证据包,以及面向安全或维护任务的 operator visibility。
266
268
  - 协议桥接运行时:ACP、A2A、AG-UI 与 runtime MCP 共用一套稳定控制面,而不是为每个对外入口各写一层集成胶水。
267
269
 
268
270
  一套常见的 runtime 治理默认值大致如下:
@@ -950,6 +952,7 @@ spec:
950
952
  - `exportEvaluationBundle`
951
953
  - `replayEvaluationBundle`
952
954
  - `createAcpServer`
955
+ - `createAcpStdioClient`
953
956
  - `serveAcpHttp`
954
957
  - `serveAcpStdio`
955
958
  - `serveA2aHttp`
@@ -978,8 +981,8 @@ ACP transport 说明:
978
981
  - `serveAcpStdio(runtime)` 提供基于 stdio 的 newline-delimited JSON-RPC,适合本地 IDE、CLI 或子进程客户端。
979
982
  - `serveAcpHttp(runtime)` 提供基于 HTTP 的 JSON-RPC 与 SSE runtime events,适合远程界面或独立控制面接入。
980
983
  - ACP transport 现已覆盖核心参考客户端流程验证:capability discovery、request submit、session lookup、request lookup、invalid JSON 处理、无 id notification 不返回响应,以及 stdio JSON-RPC 与 HTTP + SSE runtime notifications。
981
- - 如果要从最薄的一层 editor / CLI starter 开始,优先用 `agent-harness acp serve --workspace . --transport stdio`,并直接参考 `examples/protocol-hello-world/app/acp-stdio-hello-world.mjs` 的 wire shape。
982
- - `serveA2aHttp(runtime)` 提供 A2A HTTP JSON-RPC bridge 与 agent card discovery,同时兼容 `message/send` 这类旧方法,以及 `SendMessage`、`GetTask`、`ListTasks`、`CancelTask`、`SubscribeToTask` 这类更新的方法别名,并统一映射到现有 session/request 运行记录。
984
+ - 如果要从最薄的一层 editor / CLI starter 开始,优先用 `agent-harness acp serve --workspace . --transport stdio`,并直接参考 `examples/protocol-hello-world/app/acp-stdio-hello-world.mjs` 的 wire shape。需要在应用内使用 reference client 时,可直接用 `createAcpStdioClient(...)` 发起 JSON-RPC 请求并分流 runtime notifications,避免每个 sidecar 自己重写 line parsing
985
+ - `serveA2aHttp(runtime)` 提供 A2A HTTP JSON-RPC bridge 与 agent card discovery,同时兼容 `message/send` 这类旧方法,以及 `SendMessage`、`GetTask`、`ListTasks`、`CancelTask`、`SubscribeToTask` 这类 A2A v1.0 PascalCase 方法,并统一映射到现有 session/request 运行记录。v1.0 路径会发布 `supportedInterfaces`、`TASK_STATE_*` 状态、`SendMessage` 的 `{ task }` wrapper,并对 streaming 或 push notification 返回明确 unsupported error。
983
986
  - `serveAgUiHttp(runtime)` 提供 AG-UI HTTP SSE bridge,把 runtime 生命周期、文本输出、upstream thinking、step 进度与 tool call 投影成 `RUN_*`、`TEXT_MESSAGE_*`、`THINKING_TEXT_MESSAGE_*`、`STEP_*` 与 `TOOL_CALL_*` 事件,便于 UI 客户端直接接入。
984
987
  - `createRuntimeMcpServer(runtime)` 与 `serveRuntimeMcpOverStdio(runtime)` 会把持久化 runtime 控制面本身暴露成 MCP tools,包括 sessions、requests、approvals、artifacts、events 与 package export helpers。
985
988
  - `listRequestEvents(...)` 与 `exportRequestPackage(...)` 是 request-first 的检查 helper。
package/dist/acp.d.ts CHANGED
@@ -84,4 +84,5 @@ export declare class AgentHarnessAcpServer {
84
84
  private dispatch;
85
85
  }
86
86
  export declare function createAcpServer(runtime: AgentHarnessRuntime): AgentHarnessAcpServer;
87
- export {};
87
+ export { createAcpStdioClient } from "./protocol/acp/client.js";
88
+ export type { AcpStdioClient, AcpStdioClientOptions } from "./protocol/acp/client.js";
package/dist/acp.js CHANGED
@@ -206,3 +206,4 @@ export class AgentHarnessAcpServer {
206
206
  export function createAcpServer(runtime) {
207
207
  return new AgentHarnessAcpServer(runtime);
208
208
  }
209
+ export { createAcpStdioClient } from "./protocol/acp/client.js";
package/dist/api.d.ts CHANGED
@@ -4,12 +4,14 @@ import type { InventoryAgentRecord, InventorySkillRecord } from "./runtime/harne
4
4
  import type { RequirementAssessmentOptions } from "./runtime/harness/system/skill-requirements.js";
5
5
  import type { RuntimeMcpServerOptions, ToolMcpServerOptions } from "./mcp.js";
6
6
  export { AgentHarnessAcpServer, createAcpServer } from "./acp.js";
7
+ export { createAcpStdioClient } from "./protocol/acp/client.js";
7
8
  export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, } from "./acp.js";
8
9
  export { AgentHarnessRuntime } from "./runtime/harness.js";
9
10
  export { buildFlowGraph, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid } from "./flow/index.js";
10
11
  export { createUpstreamTimelineReducer } from "./upstream-events.js";
11
12
  export type { ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeSessionPackageInput, RuntimeSessionPackage, UpdateMemoryInput, } from "./contracts/types.js";
12
13
  export type { AcpHttpServer, AcpHttpServerOptions } from "./protocol/acp/http.js";
14
+ export type { AcpStdioClient, AcpStdioClientOptions } from "./protocol/acp/client.js";
13
15
  export type { A2aAgentCard, A2aHttpServer, A2aHttpServerOptions, A2aTask, A2aTaskState } from "./protocol/a2a/http.js";
14
16
  export type { AcpStdioServer, AcpStdioServerOptions } from "./protocol/acp/stdio.js";
15
17
  export type { AgUiEvent, AgUiHttpServer, AgUiHttpServerOptions, AgUiRunAgentInput } from "./protocol/ag-ui/http.js";
package/dist/api.js CHANGED
@@ -6,6 +6,7 @@ import { serveAcpOverStdio } from "./protocol/acp/stdio.js";
6
6
  import { normalizeMessageContent } from "./utils/message-content.js";
7
7
  import { loadWorkspace } from "./workspace/compile.js";
8
8
  export { AgentHarnessAcpServer, createAcpServer } from "./acp.js";
9
+ export { createAcpStdioClient } from "./protocol/acp/client.js";
9
10
  export { AgentHarnessRuntime } from "./runtime/harness.js";
10
11
  export { buildFlowGraph, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid } from "./flow/index.js";
11
12
  export { createUpstreamTimelineReducer } from "./upstream-events.js";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
2
- export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, } from "./acp.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
2
+ export type { AcpApproval, AcpArtifact, AcpEventNotification, AcpJsonRpcError, AcpJsonRpcRequest, AcpJsonRpcResponse, AcpJsonRpcSuccess, AcpRequestRecord, AcpRunRequestParams, AcpServerCapabilities, AcpSessionRecord, AcpStdioClient, AcpStdioClientOptions, } from "./acp.js";
3
3
  export type { Approval, ListMemoriesInput, ListMemoriesResult, MemoryDecision, MemoryKind, MemoryRecord, MemoryScope, MemorizeInput, MemorizeResult, NormalizeUserChatInputOptions, OperatorOverview, PublicRunListeners, RequestArtifactListing, RequestEvent, RequestEventType, RequestPackage, RequestPackageInput, RequestResult, RequestUpstreamEventItem, RecallInput, RecallResult, RemoveMemoryInput, RuntimeEvaluationExport, RuntimeEvaluationExportInput, RuntimeEvaluationReplayInput, RuntimeEvaluationReplayResult, RuntimeSessionPackage, RuntimeSessionPackageInput, UpdateMemoryInput, UserChatInput, UserChatMessage, } from "./api.js";
4
4
  export type { BuildFlowGraphInput, FlowEdge, FlowEdgeKind, FlowGraph, FlowGraphMermaidOptions, FlowGraphSequenceMermaidOptions, FlowGroup, FlowGroupKind, FlowNode, FlowNodeKind, FlowNodeLayer, FlowNodeStatus, } from "./flow/index.js";
5
5
  export type { A2aAgentCard, A2aHttpServer, A2aHttpServerOptions, A2aTask, A2aTaskState, AcpHttpServer, AcpHttpServerOptions, AcpStdioServer, AcpStdioServerOptions, AgUiEvent, AgUiHttpServer, AgUiHttpServerOptions, AgUiRunAgentInput, } from "./api.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
1
+ export { AgentHarnessAcpServer, AgentHarnessRuntime, buildFlowGraph, cancelRun, createAgentHarness, createAcpServer, createAcpStdioClient, createRuntimeMcpServer, createUpstreamTimelineReducer, createToolMcpServer, deleteSession, describeInventory, exportEvaluationBundle, exportRequestPackage, exportSessionPackage, replayEvaluationBundle, getArtifact, getAgent, getApproval, getOperatorOverview, getRequest, getHealth, listMemories, getSession, listAgentSkills, listArtifacts, listApprovals, listRequests, listRequestEvents, listSessions, memorize, normalizeUserChatInput, request, recall, removeMemory, resolveApproval, serveA2aHttp, serveAcpHttp, serveAcpStdio, serveAgUiHttp, serveRuntimeMcpOverStdio, serveToolsOverStdio, subscribe, stop, updateMemory, exportFlowGraphToMermaid, exportFlowGraphToSequenceMermaid, } from "./api.js";
2
2
  export { tool } from "./tools.js";
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.212";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.214";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.212";
1
+ export const AGENT_HARNESS_VERSION = "0.0.214";
@@ -18,20 +18,23 @@ export type A2aAgentCard = {
18
18
  description: string;
19
19
  version: string;
20
20
  protocolVersion?: string;
21
- url: string;
22
- preferredTransport: "JSONRPC";
21
+ url?: string;
22
+ preferredTransport?: "JSONRPC";
23
23
  provider?: {
24
24
  organization?: string;
25
25
  url?: string;
26
26
  };
27
27
  documentationUrl?: string;
28
28
  defaultAgentId?: string;
29
- supportsAuthenticatedExtendedCard?: boolean;
30
- supportedInterfaces?: string[];
29
+ supportedInterfaces: Array<{
30
+ url: string;
31
+ protocolBinding: "JSONRPC";
32
+ protocolVersion: "1.0";
33
+ }>;
31
34
  capabilities: {
32
35
  streaming: boolean;
33
36
  pushNotifications: boolean;
34
- stateTransitionHistory: boolean;
37
+ extendedAgentCard: boolean;
35
38
  };
36
39
  defaultInputModes: string[];
37
40
  defaultOutputModes: string[];
@@ -43,16 +46,17 @@ export type A2aAgentCard = {
43
46
  examples: string[];
44
47
  }>;
45
48
  };
46
- export type A2aTaskState = "submitted" | "working" | "input-required" | "completed" | "failed" | "canceled";
49
+ export type A2aTaskState = "TASK_STATE_SUBMITTED" | "TASK_STATE_WORKING" | "TASK_STATE_INPUT_REQUIRED" | "TASK_STATE_COMPLETED" | "TASK_STATE_FAILED" | "TASK_STATE_CANCELED";
47
50
  type A2aTaskStatus = {
48
51
  state: A2aTaskState;
49
52
  timestamp: string;
50
53
  messageId?: string;
51
54
  message?: {
52
- role: "agent";
55
+ role: "ROLE_AGENT";
56
+ messageId: string;
53
57
  parts: Array<{
54
- type: "text";
55
58
  text: string;
59
+ mediaType: "text/plain";
56
60
  }>;
57
61
  };
58
62
  };
@@ -62,17 +66,19 @@ export type A2aTask = {
62
66
  kind: "task";
63
67
  status: A2aTaskStatus;
64
68
  history: Array<{
65
- role: "user" | "agent";
69
+ role: "ROLE_USER" | "ROLE_AGENT";
70
+ messageId: string;
66
71
  parts: Array<{
67
- type: "text";
68
72
  text: string;
73
+ mediaType: "text/plain";
69
74
  }>;
70
75
  }>;
71
76
  artifacts: Array<{
77
+ artifactId: string;
72
78
  name: string;
73
79
  parts: Array<{
74
- type: "text";
75
80
  text: string;
81
+ mediaType: "text/plain";
76
82
  }>;
77
83
  }>;
78
84
  metadata: Record<string, unknown>;
@@ -41,11 +41,16 @@ function parseTextPart(part) {
41
41
  return null;
42
42
  }
43
43
  const typed = part;
44
- if (typed.type !== "text" || typeof typed.text !== "string" || typed.text.trim().length === 0) {
44
+ if (typeof typed.text !== "string"
45
+ || typed.text.trim().length === 0
46
+ || (typed.type !== undefined && typed.type !== "text")) {
45
47
  return null;
46
48
  }
47
49
  return typed.text;
48
50
  }
51
+ function normalizeUserRole(role) {
52
+ return typeof role === "string" ? role.toLowerCase().replace(/^role_/, "") : "";
53
+ }
49
54
  function parseMessageSendParams(params) {
50
55
  if (!params || typeof params !== "object" || Array.isArray(params)) {
51
56
  throw new Error("A2A message/send requires params object.");
@@ -56,9 +61,9 @@ function parseMessageSendParams(params) {
56
61
  throw new Error("A2A message/send requires `message`.");
57
62
  }
58
63
  const typedMessage = message;
59
- const role = typeof typedMessage.role === "string" ? typedMessage.role : "";
64
+ const role = normalizeUserRole(typedMessage.role);
60
65
  if (role !== "user") {
61
- throw new Error("A2A message/send currently supports only `user` role messages.");
66
+ throw new Error("A2A message/send currently supports only `user` / `ROLE_USER` role messages.");
62
67
  }
63
68
  const parts = Array.isArray(typedMessage.parts) ? typedMessage.parts : [];
64
69
  const textParts = parts.map(parseTextPart).filter((item) => Boolean(item));
@@ -67,7 +72,9 @@ function parseMessageSendParams(params) {
67
72
  }
68
73
  const sessionId = typeof typed.contextId === "string" && typed.contextId.trim().length > 0
69
74
  ? typed.contextId.trim()
70
- : undefined;
75
+ : typeof typedMessage.contextId === "string" && typedMessage.contextId.trim().length > 0
76
+ ? typedMessage.contextId.trim()
77
+ : undefined;
71
78
  const agentId = typeof typed.agentId === "string" && typed.agentId.trim().length > 0
72
79
  ? typed.agentId.trim()
73
80
  : undefined;
@@ -102,39 +109,71 @@ function parseTaskListParams(params) {
102
109
  return { limit: 20 };
103
110
  }
104
111
  const typed = params;
105
- const limitValue = typeof typed.limit === "number" && Number.isFinite(typed.limit)
106
- ? Math.max(1, Math.min(100, Math.trunc(typed.limit)))
112
+ const rawLimit = typeof typed.pageSize === "number" && Number.isFinite(typed.pageSize)
113
+ ? typed.pageSize
114
+ : typeof typed.limit === "number" && Number.isFinite(typed.limit)
115
+ ? typed.limit
116
+ : 20;
117
+ const limitValue = typeof rawLimit === "number" && Number.isFinite(rawLimit)
118
+ ? Math.max(1, Math.min(100, Math.trunc(rawLimit)))
107
119
  : 20;
108
120
  return {
109
121
  ...(typeof typed.agentId === "string" && typed.agentId.trim().length > 0 ? { agentId: typed.agentId.trim() } : {}),
110
122
  ...(typeof typed.contextId === "string" && typed.contextId.trim().length > 0 ? { contextId: typed.contextId.trim() } : {}),
111
- ...(typeof typed.state === "string" && typed.state.trim().length > 0 ? { state: typed.state.trim() } : {}),
112
- ...(typeof typed.cursor === "string" && typed.cursor.trim().length > 0 ? { cursor: typed.cursor.trim() } : {}),
123
+ ...(typeof typed.status === "string" && typed.status.trim().length > 0
124
+ ? { state: typed.status.trim() }
125
+ : typeof typed.state === "string" && typed.state.trim().length > 0
126
+ ? { state: typed.state.trim() }
127
+ : {}),
128
+ ...(typeof typed.pageToken === "string" && typed.pageToken.trim().length > 0
129
+ ? { cursor: typed.pageToken.trim() }
130
+ : typeof typed.cursor === "string" && typed.cursor.trim().length > 0
131
+ ? { cursor: typed.cursor.trim() }
132
+ : {}),
113
133
  limit: limitValue,
114
134
  };
115
135
  }
116
136
  function mapRunState(state) {
117
137
  switch (state) {
118
138
  case "queued":
119
- return "submitted";
139
+ return "TASK_STATE_SUBMITTED";
120
140
  case "running":
121
- return "working";
141
+ return "TASK_STATE_WORKING";
122
142
  case "waiting_for_approval":
123
- return "input-required";
143
+ return "TASK_STATE_INPUT_REQUIRED";
124
144
  case "completed":
125
- return "completed";
145
+ return "TASK_STATE_COMPLETED";
126
146
  case "cancelled":
127
- return "canceled";
147
+ return "TASK_STATE_CANCELED";
128
148
  case "failed":
129
149
  default:
150
+ return "TASK_STATE_FAILED";
151
+ }
152
+ }
153
+ function mapTaskStatusToRunState(state) {
154
+ switch (state?.toLowerCase().replace(/^task_state_/, "").replace(/_/g, "-")) {
155
+ case "submitted":
156
+ return "queued";
157
+ case "working":
158
+ return "running";
159
+ case "input-required":
160
+ return "waiting_for_approval";
161
+ case "completed":
162
+ return "completed";
163
+ case "failed":
130
164
  return "failed";
165
+ case "canceled":
166
+ case "cancelled":
167
+ return "cancelled";
168
+ default:
169
+ return undefined;
131
170
  }
132
171
  }
133
172
  function toTextParts(text) {
134
173
  if (typeof text !== "string" || text.length === 0) {
135
174
  return [];
136
175
  }
137
- return [{ type: "text", text }];
176
+ return [{ text, mediaType: "text/plain" }];
138
177
  }
139
178
  function contentToText(content) {
140
179
  if (typeof content === "string") {
@@ -230,7 +269,8 @@ function buildTaskFromSessionAndRequest(session, request, approvals, output, fai
230
269
  timestamp: request.updatedAt,
231
270
  ...(statusText ? {
232
271
  message: {
233
- role: "agent",
272
+ role: "ROLE_AGENT",
273
+ messageId: `${request.requestId}:status`,
234
274
  parts: toTextParts(statusText),
235
275
  },
236
276
  } : {}),
@@ -238,13 +278,15 @@ function buildTaskFromSessionAndRequest(session, request, approvals, output, fai
238
278
  history: [
239
279
  ...(latestUserText
240
280
  ? [{
241
- role: "user",
281
+ role: "ROLE_USER",
282
+ messageId: `${request.requestId}:user`,
242
283
  parts: toTextParts(latestUserText),
243
284
  }]
244
285
  : []),
245
286
  ...(latestAgentText
246
287
  ? [{
247
- role: "agent",
288
+ role: "ROLE_AGENT",
289
+ messageId: `${request.requestId}:agent`,
248
290
  parts: toTextParts(latestAgentText),
249
291
  }]
250
292
  : []),
@@ -269,18 +311,23 @@ async function buildTaskFromRuntime(runtime, requestId) {
269
311
  return buildTaskFromSessionAndRequest(toSessionRecord(session), toRequestRecord(request), approvals);
270
312
  }
271
313
  async function listTasksFromRuntime(runtime, params) {
314
+ const runState = mapTaskStatusToRunState(params.state);
272
315
  const runs = await runtime.listRuns({
273
316
  ...(params.agentId ? { agentId: params.agentId } : {}),
274
317
  ...(params.contextId ? { threadId: params.contextId } : {}),
275
- ...(params.state ? { state: params.state } : {}),
318
+ ...(runState ? { state: runState } : {}),
276
319
  });
277
320
  const startIndex = params.cursor ? Number.parseInt(Buffer.from(params.cursor, "base64url").toString("utf8"), 10) || 0 : 0;
278
321
  const page = runs.slice(startIndex, startIndex + params.limit);
279
322
  const tasks = (await Promise.all(page.map((run) => buildTaskFromRuntime(runtime, run.runId)))).filter((task) => Boolean(task));
280
323
  const nextIndex = startIndex + page.length;
324
+ const nextPageToken = nextIndex < runs.length ? Buffer.from(String(nextIndex), "utf8").toString("base64url") : "";
281
325
  return {
282
326
  tasks,
283
- ...(nextIndex < runs.length ? { nextCursor: Buffer.from(String(nextIndex), "utf8").toString("base64url") } : {}),
327
+ pageSize: params.limit,
328
+ totalSize: runs.length,
329
+ nextPageToken,
330
+ ...(nextPageToken ? { nextCursor: nextPageToken } : {}),
284
331
  };
285
332
  }
286
333
  function buildAgentCard(runtime, options) {
@@ -296,33 +343,36 @@ function buildAgentCard(runtime, options) {
296
343
  name: options.agentName,
297
344
  description: options.agentDescription,
298
345
  version: "0.1.0",
346
+ // Older JSON-RPC clients may still read these top-level fields; A2A v1.0 discovery uses `supportedInterfaces`.
299
347
  protocolVersion: "1.0",
300
348
  url: options.rpcUrl,
301
349
  preferredTransport: "JSONRPC",
302
- supportsAuthenticatedExtendedCard: false,
303
350
  supportedInterfaces: [
304
- "message/send",
305
- "tasks/send",
306
- "tasks/get",
307
- "tasks/list",
308
- "tasks/cancel",
309
- "tasks/subscribe",
310
- "SendMessage",
311
- "GetTask",
312
- "ListTasks",
313
- "CancelTask",
314
- "SubscribeToTask",
351
+ {
352
+ url: options.rpcUrl,
353
+ protocolBinding: "JSONRPC",
354
+ protocolVersion: "1.0",
355
+ },
315
356
  ],
316
357
  capabilities: {
317
358
  streaming: false,
318
359
  pushNotifications: false,
319
- stateTransitionHistory: true,
360
+ extendedAgentCard: false,
320
361
  },
321
- defaultInputModes: ["text"],
322
- defaultOutputModes: ["text"],
362
+ defaultInputModes: ["text/plain"],
363
+ defaultOutputModes: ["text/plain"],
323
364
  skills,
324
365
  };
325
366
  }
367
+ function isV1Method(method) {
368
+ return /^[A-Z]/.test(method);
369
+ }
370
+ function toSendMessageResult(method, task) {
371
+ if (!task || !isV1Method(method)) {
372
+ return task;
373
+ }
374
+ return { task };
375
+ }
326
376
  export async function serveA2aOverHttp(runtime, options = {}) {
327
377
  const hostname = options.hostname?.trim() || "127.0.0.1";
328
378
  const port = typeof options.port === "number" && Number.isFinite(options.port) ? options.port : 0;
@@ -372,14 +422,18 @@ export async function serveA2aOverHttp(runtime, options = {}) {
372
422
  const requestRecord = await runtime.getRun(result.runId);
373
423
  const approvals = await runtime.listApprovals({ threadId: result.threadId, runId: result.runId });
374
424
  const task = buildTaskFromSessionAndRequest(toSessionRecord(session), toRequestRecord(requestRecord), approvals, result.output);
375
- writeJson(response, 200, toSuccess(payload.id ?? null, task));
425
+ writeJson(response, 200, toSuccess(payload.id ?? null, toSendMessageResult(payload.method, task)));
426
+ return;
427
+ }
428
+ if (payload.method === "SendStreamingMessage") {
429
+ writeJson(response, 200, toError(payload.id ?? null, -32004, "A2A streaming is not supported by this bridge."));
376
430
  return;
377
431
  }
378
432
  if (payload.method === "tasks/get" || payload.method === "GetTask") {
379
433
  const { taskId } = parseTaskLocatorParams(payload.params);
380
434
  const task = await buildTaskFromRuntime(runtime, taskId);
381
435
  if (!task) {
382
- writeJson(response, 200, toError(payload.id ?? null, -32004, "Task not found."));
436
+ writeJson(response, 200, toError(payload.id ?? null, -32001, "Task not found."));
383
437
  return;
384
438
  }
385
439
  writeJson(response, 200, toSuccess(payload.id ?? null, task));
@@ -401,19 +455,34 @@ export async function serveA2aOverHttp(runtime, options = {}) {
401
455
  const { taskId } = parseTaskLocatorParams(payload.params);
402
456
  const task = await buildTaskFromRuntime(runtime, taskId);
403
457
  if (!task) {
404
- writeJson(response, 200, toError(payload.id ?? null, -32004, "Task not found."));
458
+ writeJson(response, 200, toError(payload.id ?? null, -32001, "Task not found."));
459
+ return;
460
+ }
461
+ if (payload.method === "SubscribeToTask") {
462
+ writeJson(response, 200, toError(payload.id ?? null, -32004, "A2A streaming subscriptions are not supported by this bridge."));
405
463
  return;
406
464
  }
407
465
  writeJson(response, 200, toSuccess(payload.id ?? null, {
408
466
  task,
409
467
  streamable: false,
410
468
  pushNotifications: false,
411
- nextPollAfterMs: task.status.state === "completed" || task.status.state === "failed" || task.status.state === "canceled"
469
+ nextPollAfterMs: task.status.state === "TASK_STATE_COMPLETED" || task.status.state === "TASK_STATE_FAILED" || task.status.state === "TASK_STATE_CANCELED"
412
470
  ? 0
413
471
  : 1_000,
414
472
  }));
415
473
  return;
416
474
  }
475
+ if (payload.method === "CreateTaskPushNotificationConfig"
476
+ || payload.method === "GetTaskPushNotificationConfig"
477
+ || payload.method === "ListTaskPushNotificationConfigs"
478
+ || payload.method === "DeleteTaskPushNotificationConfig") {
479
+ writeJson(response, 200, toError(payload.id ?? null, -32003, "A2A push notifications are not supported by this bridge."));
480
+ return;
481
+ }
482
+ if (payload.method === "GetExtendedAgentCard") {
483
+ writeJson(response, 200, toError(payload.id ?? null, -32004, "A2A extended agent cards are not supported by this bridge."));
484
+ return;
485
+ }
417
486
  writeJson(response, 200, toError(payload.id ?? null, -32601, `Unknown A2A method: ${payload.method}`));
418
487
  }
419
488
  catch (error) {
@@ -0,0 +1,14 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ import type { AcpEventNotification, AcpJsonRpcSuccess } from "../../acp.js";
3
+ export type AcpStdioClientOptions = {
4
+ input: Writable;
5
+ output: Readable;
6
+ idPrefix?: string;
7
+ };
8
+ export type AcpStdioClient = {
9
+ request: (method: string, params?: unknown) => Promise<AcpJsonRpcSuccess["result"]>;
10
+ notify: (method: string, params?: unknown) => Promise<void>;
11
+ subscribe: (listener: (notification: AcpEventNotification) => void) => () => void;
12
+ close: () => Promise<void>;
13
+ };
14
+ export declare function createAcpStdioClient(options: AcpStdioClientOptions): AcpStdioClient;
@@ -0,0 +1,140 @@
1
+ import { createInterface } from "node:readline";
2
+ class AcpClientError extends Error {
3
+ error;
4
+ constructor(error) {
5
+ super(error.message);
6
+ this.error = error;
7
+ this.name = "AcpClientError";
8
+ }
9
+ }
10
+ function writeJsonLine(output, payload) {
11
+ return new Promise((resolve, reject) => {
12
+ output.write(`${JSON.stringify(payload)}\n`, (error) => {
13
+ if (error) {
14
+ reject(error);
15
+ return;
16
+ }
17
+ resolve();
18
+ });
19
+ });
20
+ }
21
+ function isResponse(value) {
22
+ return typeof value === "object"
23
+ && value !== null
24
+ && !Array.isArray(value)
25
+ && value.jsonrpc === "2.0"
26
+ && Object.hasOwn(value, "id");
27
+ }
28
+ function isNotification(value) {
29
+ return typeof value === "object"
30
+ && value !== null
31
+ && !Array.isArray(value)
32
+ && value.jsonrpc === "2.0"
33
+ && typeof value.method === "string"
34
+ && !Object.hasOwn(value, "id");
35
+ }
36
+ export function createAcpStdioClient(options) {
37
+ const idPrefix = options.idPrefix?.trim() || "acp";
38
+ let sequence = 0;
39
+ let closed = false;
40
+ const pending = new Map();
41
+ const listeners = new Set();
42
+ const reader = createInterface({
43
+ input: options.output,
44
+ crlfDelay: Infinity,
45
+ });
46
+ const completed = (async () => {
47
+ try {
48
+ for await (const line of reader) {
49
+ const trimmed = line.trim();
50
+ if (!trimmed) {
51
+ continue;
52
+ }
53
+ const parsed = JSON.parse(trimmed);
54
+ if (isNotification(parsed)) {
55
+ for (const listener of Array.from(listeners)) {
56
+ listener(parsed);
57
+ }
58
+ continue;
59
+ }
60
+ if (!isResponse(parsed)) {
61
+ continue;
62
+ }
63
+ const request = pending.get(parsed.id);
64
+ if (!request) {
65
+ continue;
66
+ }
67
+ pending.delete(parsed.id);
68
+ if ("error" in parsed) {
69
+ request.reject(new AcpClientError(parsed.error));
70
+ }
71
+ else {
72
+ request.resolve(parsed.result);
73
+ }
74
+ }
75
+ }
76
+ catch (error) {
77
+ closed = true;
78
+ for (const request of pending.values()) {
79
+ request.reject(error);
80
+ }
81
+ pending.clear();
82
+ }
83
+ })();
84
+ return {
85
+ async request(method, params) {
86
+ if (closed) {
87
+ throw new Error("ACP stdio client is closed.");
88
+ }
89
+ const id = `${idPrefix}-${++sequence}`;
90
+ const payload = {
91
+ jsonrpc: "2.0",
92
+ id,
93
+ method,
94
+ ...(params === undefined ? {} : { params }),
95
+ };
96
+ const result = new Promise((resolve, reject) => {
97
+ pending.set(id, { resolve, reject });
98
+ });
99
+ try {
100
+ await writeJsonLine(options.input, payload);
101
+ }
102
+ catch (error) {
103
+ pending.delete(id);
104
+ throw error;
105
+ }
106
+ return result;
107
+ },
108
+ async notify(method, params) {
109
+ if (closed) {
110
+ throw new Error("ACP stdio client is closed.");
111
+ }
112
+ await writeJsonLine(options.input, {
113
+ jsonrpc: "2.0",
114
+ method,
115
+ ...(params === undefined ? {} : { params }),
116
+ });
117
+ },
118
+ subscribe(listener) {
119
+ listeners.add(listener);
120
+ return () => {
121
+ listeners.delete(listener);
122
+ };
123
+ },
124
+ async close() {
125
+ closed = true;
126
+ reader.close();
127
+ if (typeof options.output.destroy === "function") {
128
+ options.output.destroy();
129
+ }
130
+ if (typeof options.input.end === "function") {
131
+ options.input.end();
132
+ }
133
+ await completed.catch(() => undefined);
134
+ for (const request of pending.values()) {
135
+ request.reject(new Error("ACP stdio client closed before response."));
136
+ }
137
+ pending.clear();
138
+ },
139
+ };
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.213",
3
+ "version": "0.0.215",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",