stable-harness 0.0.108 → 0.0.110
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/docs/protocols/agent-protocols.md +54 -3
- package/docs/protocols/external-sdk-interop.md +78 -0
- package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
- package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
- package/node_modules/@stable-harness/core/package.json +3 -3
- package/node_modules/@stable-harness/governance/package.json +1 -1
- package/node_modules/@stable-harness/memory/package.json +1 -1
- package/node_modules/@stable-harness/protocols/dist/src/agent-protocols.d.ts +1 -0
- package/node_modules/@stable-harness/protocols/dist/src/agent-protocols.js +1 -1
- package/node_modules/@stable-harness/protocols/package.json +2 -2
- package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
- package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
- package/package.json +10 -9
- package/packages/adapter-deepagents/package.json +2 -2
- package/packages/adapter-langgraph/package.json +2 -2
- package/packages/cli/dist/src/console/session.js +1 -1
- package/packages/cli/dist/src/daemon/client.d.ts +1 -1
- package/packages/cli/dist/src/daemon/client.js +1 -1
- package/packages/cli/dist/src/server.js +1 -1
- package/packages/cli/package.json +8 -8
- package/packages/core/package.json +3 -3
- package/packages/evaluation/package.json +2 -2
- package/packages/governance/package.json +1 -1
- package/packages/memory/package.json +1 -1
- package/packages/protocols/dist/src/agent-protocols.d.ts +1 -0
- package/packages/protocols/dist/src/agent-protocols.js +1 -1
- package/packages/protocols/package.json +2 -2
- package/packages/tool-gateway/package.json +1 -1
- package/packages/workspace-yaml/package.json +2 -2
|
@@ -38,6 +38,19 @@ Stable Runtime operator API. Stable Runtime HTTP/SSE and the SDK remain the
|
|
|
38
38
|
first-party surfaces for listing sessions, deleting session history, inspecting
|
|
39
39
|
requests, managing memory, approvals, artifacts, and native event filters.
|
|
40
40
|
|
|
41
|
+
Reconnect and replay behavior is intentionally layered:
|
|
42
|
+
|
|
43
|
+
| Surface | Reconnect behavior |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| Stable Runtime SSE | Replays stored runtime events when clients reconnect with request/session/type filters. |
|
|
46
|
+
| A2A streaming | Emits live task status updates for a request stream. |
|
|
47
|
+
| A2A task subscribe | Replays the current task snapshot and streams future updates when the task is still running. |
|
|
48
|
+
| AG-UI stream | Emits one run stream; use Stable Runtime SSE for first-party replay/resume. |
|
|
49
|
+
| ACP stdio/HTTP | Session IDs resume prompt continuity; progress replay is not provided through ACP. |
|
|
50
|
+
|
|
51
|
+
For external SDK compatibility status, see
|
|
52
|
+
[External SDK Interop Spike](./external-sdk-interop.md).
|
|
53
|
+
|
|
41
54
|
## A2A
|
|
42
55
|
|
|
43
56
|
A2A is exposed as an HTTP+JSON and SSE facade:
|
|
@@ -50,6 +63,7 @@ A2A is exposed as an HTTP+JSON and SSE facade:
|
|
|
50
63
|
- `GET /a2a/tasks`
|
|
51
64
|
- `GET /a2a/tasks/:id`
|
|
52
65
|
- `POST /a2a/tasks/:id:cancel`
|
|
66
|
+
- `GET /a2a/tasks/:id:subscribe`
|
|
53
67
|
|
|
54
68
|
`message:send` and JSON-RPC `SendMessage` map to a native `RuntimeRequest`.
|
|
55
69
|
Streaming responses subscribe to native runtime events and project them as A2A
|
|
@@ -63,6 +77,19 @@ message parts.
|
|
|
63
77
|
Session mapping: `taskId` or message `contextId` becomes the Stable Runtime
|
|
64
78
|
`sessionId`.
|
|
65
79
|
|
|
80
|
+
Compliance notes:
|
|
81
|
+
|
|
82
|
+
| Capability | Status | Notes |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| Agent card | Supported | Exposed at `/.well-known/agent-card.json` and `/a2a/agent-card.json`. |
|
|
85
|
+
| Version header | Supported | Empty, `0.3`, and `1.0` are accepted; unsupported versions fail clearly. |
|
|
86
|
+
| Send message | Supported | Returns a Task backed by a Stable Runtime request. |
|
|
87
|
+
| Streaming message | Supported | Emits an initial Task snapshot, then task status update events, then closes on completion. |
|
|
88
|
+
| Task artifacts | Supported | Final text output is exposed as an output artifact while status messages remain available. |
|
|
89
|
+
| Task subscription | Partial | `GET /a2a/tasks/:id:subscribe` replays the current task and streams future runtime updates for running tasks. |
|
|
90
|
+
| Push notifications | Not implemented | Capability advertises `pushNotifications: false`; use Stable Runtime SSE or A2A streaming. |
|
|
91
|
+
| Full task history | Partial | Stable Runtime stores request records; A2A history projection is intentionally limited. |
|
|
92
|
+
|
|
66
93
|
## AG-UI
|
|
67
94
|
|
|
68
95
|
AG-UI is exposed as an HTTP+SSE event stream:
|
|
@@ -70,9 +97,9 @@ AG-UI is exposed as an HTTP+SSE event stream:
|
|
|
70
97
|
- `GET /ag-ui/capabilities`
|
|
71
98
|
- `POST /ag-ui/runs`
|
|
72
99
|
|
|
73
|
-
`POST /ag-ui/runs` emits
|
|
74
|
-
|
|
75
|
-
or `
|
|
100
|
+
`POST /ag-ui/runs` emits canonical AG-UI event enum values: `RUN_STARTED`,
|
|
101
|
+
`CUSTOM` events for Stable Runtime events, `TEXT_MESSAGE_START`,
|
|
102
|
+
`TEXT_MESSAGE_CONTENT`, `TEXT_MESSAGE_END`, and `RUN_FINISHED` or `RUN_ERROR`.
|
|
76
103
|
|
|
77
104
|
Stable Harness clients may include `runtimeRequest` in the run body to preserve
|
|
78
105
|
explicit runtime fields while still using the AG-UI event stream.
|
|
@@ -80,6 +107,16 @@ explicit runtime fields while still using the AG-UI event stream.
|
|
|
80
107
|
Session mapping: `threadId` or `sessionId` becomes the Stable Runtime
|
|
81
108
|
`sessionId`.
|
|
82
109
|
|
|
110
|
+
Compliance notes:
|
|
111
|
+
|
|
112
|
+
| Capability | Status | Notes |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| Run lifecycle | Supported | Emits `RUN_STARTED`, `RUN_FINISHED`, and `RUN_ERROR`. |
|
|
115
|
+
| Text message lifecycle | Supported | Emits start/content/end events for final assistant text. |
|
|
116
|
+
| Tool events | Partial | Direct runtime tool start/result events are projected to AG-UI tool events. |
|
|
117
|
+
| Runtime events | Supported | Non-native events remain available through `CUSTOM` payloads. |
|
|
118
|
+
| Reconnect/resume | Not implemented | Use Stable Runtime event replay when first-party resume behavior is required. |
|
|
119
|
+
|
|
83
120
|
## ACP
|
|
84
121
|
|
|
85
122
|
ACP's standard transport is JSON-RPC over stdio. Stable Harness exposes a local
|
|
@@ -112,3 +149,17 @@ Stable Harness clients may include `params.runtimeRequest` to carry explicit
|
|
|
112
149
|
runtime fields through the ACP-shaped transport.
|
|
113
150
|
|
|
114
151
|
Session mapping: ACP `sessionId` becomes the Stable Runtime `sessionId`.
|
|
152
|
+
|
|
153
|
+
Compliance notes:
|
|
154
|
+
|
|
155
|
+
| Capability | Status | Notes |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| stdio JSON-RPC | Supported | `stable-harness acp-stdio -w ./workspace` accepts one JSON-RPC message per line. |
|
|
158
|
+
| HTTP JSON-RPC | Supported | `/acp` reuses the same method handler as stdio. |
|
|
159
|
+
| initialize | Supported | Negotiates protocol major version `1`, returns `agentCapabilities`, `agentInfo`, and `authMethods`. |
|
|
160
|
+
| session/new | Supported | Creates a client-usable Stable Runtime session ID. |
|
|
161
|
+
| session/load | Supported | Returns the requested session ID when load is requested. |
|
|
162
|
+
| session/list | Supported | Projects Stable Runtime session summaries. |
|
|
163
|
+
| session/prompt | Supported | Maps prompt turns to Stable Runtime requests. |
|
|
164
|
+
| session/cancel | Supported | Cancels currently running requests in the session. |
|
|
165
|
+
| client file/terminal requests | Not implemented | Stable Harness does not request client-side filesystem or terminal operations through ACP. |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# External SDK Interop Spike
|
|
2
|
+
|
|
3
|
+
Stable Harness protocol facades remain adapters over the native Stable Runtime
|
|
4
|
+
contract. External SDK interop tests must validate protocol payloads and
|
|
5
|
+
transport behavior without moving SDK-specific execution semantics into the
|
|
6
|
+
runtime.
|
|
7
|
+
|
|
8
|
+
## Current CI-Safe Interop
|
|
9
|
+
|
|
10
|
+
| Protocol | External package | CI status | Notes |
|
|
11
|
+
| --- | --- | --- | --- |
|
|
12
|
+
| AG-UI | `@ag-ui/core` | Enabled | `test/protocol/protocol-conformance.test.ts` parses emitted events with official `EventSchemas`. |
|
|
13
|
+
| A2A | `@a2a-js/sdk` | Fixture-ready | A2A stream fixtures assert task snapshot, status updates, artifacts, and version handling. A full SDK client test should be added only after the client API is stable enough for CI. |
|
|
14
|
+
| ACP | `@agentclientprotocol/sdk` | Fixture-ready | ACP JSON-RPC transcript fixtures exercise the same handler used by HTTP and stdio. A full `ClientSideConnection` test needs a small stdio transport harness. |
|
|
15
|
+
|
|
16
|
+
## Compatibility Findings
|
|
17
|
+
|
|
18
|
+
### A2A
|
|
19
|
+
|
|
20
|
+
Stable Harness now exposes the core HTTP/SSE facade expected by A2A clients:
|
|
21
|
+
|
|
22
|
+
- agent card discovery
|
|
23
|
+
- `message:send`
|
|
24
|
+
- `message:stream`
|
|
25
|
+
- task lookup
|
|
26
|
+
- task subscribe
|
|
27
|
+
- cancel
|
|
28
|
+
- task artifact projection
|
|
29
|
+
- `A2A-Version` rejection for unsupported versions
|
|
30
|
+
|
|
31
|
+
Remaining SDK interop work is client-specific. `@a2a-js/sdk` is appropriate for
|
|
32
|
+
a future real client canary, but the CI test should be isolated from optional
|
|
33
|
+
Express/server helpers and should exercise only client-side HTTP/SSE calls.
|
|
34
|
+
|
|
35
|
+
### AG-UI
|
|
36
|
+
|
|
37
|
+
AG-UI is the strongest current external SDK validation path. The facade emits
|
|
38
|
+
canonical event enum values and validates against `@ag-ui/core` runtime schemas:
|
|
39
|
+
|
|
40
|
+
- `RUN_STARTED`
|
|
41
|
+
- `CUSTOM`
|
|
42
|
+
- `TEXT_MESSAGE_START`
|
|
43
|
+
- `TEXT_MESSAGE_CONTENT`
|
|
44
|
+
- `TEXT_MESSAGE_END`
|
|
45
|
+
- `RUN_FINISHED`
|
|
46
|
+
- `RUN_ERROR`
|
|
47
|
+
|
|
48
|
+
This should stay in CI because it is schema-only, deterministic, and does not
|
|
49
|
+
require a browser or frontend framework.
|
|
50
|
+
|
|
51
|
+
### ACP
|
|
52
|
+
|
|
53
|
+
Stable Harness supports both:
|
|
54
|
+
|
|
55
|
+
- `stable-harness acp-stdio -w ./workspace`
|
|
56
|
+
- `/acp` HTTP JSON-RPC custom transport
|
|
57
|
+
|
|
58
|
+
Both transports share `handleAcpJsonRpcMessage`. The current CI fixture uses the
|
|
59
|
+
shared handler directly, which proves protocol response shape without needing a
|
|
60
|
+
subprocess. A future `@agentclientprotocol/sdk` client test should create a
|
|
61
|
+
small stdio bridge around the CLI entrypoint and verify:
|
|
62
|
+
|
|
63
|
+
- initialize
|
|
64
|
+
- session/new
|
|
65
|
+
- session/prompt
|
|
66
|
+
- session/cancel notification
|
|
67
|
+
|
|
68
|
+
## Follow-Up Gate
|
|
69
|
+
|
|
70
|
+
Do not add a heavyweight SDK dependency or browser test to the default full
|
|
71
|
+
suite unless it satisfies all of these:
|
|
72
|
+
|
|
73
|
+
- deterministic under local and GitHub Actions execution
|
|
74
|
+
- no external service dependency
|
|
75
|
+
- no long-lived subprocess leak
|
|
76
|
+
- no protocol-specific runtime semantics in core
|
|
77
|
+
- failure message points to the protocol facade boundary
|
|
78
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@langchain/node-vfs": "^0.1.4",
|
|
16
16
|
"@langchain/ollama": "^1.2.7",
|
|
17
17
|
"@langchain/openai": "^1.4.5",
|
|
18
|
-
"@stable-harness/core": "0.0.
|
|
18
|
+
"@stable-harness/core": "0.0.110",
|
|
19
19
|
"deepagents": "^1.10.1",
|
|
20
20
|
"langchain": "^1.4.0"
|
|
21
21
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-langgraph",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@langchain/langgraph": "^1.3.0",
|
|
14
|
-
"@stable-harness/core": "0.0.
|
|
14
|
+
"@stable-harness/core": "0.0.110"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
".": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@stable-harness/governance": "0.0.
|
|
15
|
-
"@stable-harness/memory": "0.0.
|
|
14
|
+
"@stable-harness/governance": "0.0.110",
|
|
15
|
+
"@stable-harness/memory": "0.0.110"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const r=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...r]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(r));if(r.has("a2a")&&await async function handleA2a(e,t,a,r){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(a,200,function createAgentCard(e,t){const a=e.inspect();return{protocolVersion:"1.0",name:a.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,r)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(a,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(a,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&n?(e.cancel(n,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(n)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(a,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),a),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,a){if("SendMessage"===t.method){const r=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(r)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const r=readString(readRecord(t.params)?.id),s=r?e.inspectRequest(r):void 0;return void sendJson(a,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(a,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},a,t.id)}(e,await readJson(t),a),!0)}(t,e,s,a))return;if(r.has("agui")&&await async function handleAgui(e,t,a){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(a,200,{protocol:"ag-ui",transports:["http+sse"],events:["RunStarted","TextMessageChunk","Custom","RunFinished","RunError"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const r=readRuntimeRequestExtension(t),s=r?.requestId??readString(t.runId)??crypto.randomUUID(),n=r?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RunStarted",threadId:n,runId:s,input:{messages:t.messages,input:o}});const d=e.subscribe(e=>{e.requestId===s&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"Custom",name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"ToolCallStart",toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"ToolCallResult",messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:e.output,role:"tool"}:{type:"Custom",name:`stable-harness.${e.type}`,value:e}}(e))});try{const d=await e.request(r??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${s}-message`;d.output&&writeSse(a,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:d.output}),writeSse(a,{type:"RunFinished",threadId:n,runId:s,outcome:{type:"completed"===d.state?"success":"interrupt"},result:d.output})}catch(e){writeSse(a,{type:"RunError",threadId:n,runId:s,message:errorMessage(e)})}finally{d(),a.end()}}(e,await readJson(t),a),!0)}(t,e,s))return;if(r.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}}),!0;if("POST"===t.method&&"/acp"===t.url){const r=await readJson(t),s=await handleAcpJsonRpcMessage(e,r);return s?sendJson(a,200,s):a.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}});if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},r=readRuntimeRequestExtension(a),s=await e.request(r??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,r){const s=toA2aRuntimeRequest(t);writeSseHeaders(a);const n=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(a,{jsonrpc:"2.0",id:r,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(a,{jsonrpc:"2.0",id:r,result:{task:toA2aTask(t),final:!0}})}finally{n(),a.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const a=readRecord(e.message)??e;return{input:readPromptText(a),requestId:readString(e.requestId)??readString(a.messageId),sessionId:readString(e.taskId)??readString(a.taskId)??readString(a.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,a="output"in e?e.output:e.output??"",r="requestId"in e?e.requestId:e.summary.requestId;return{id:r,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${r}-response`,parts:[{kind:"text",text:a}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const a=(e??"").match(t);return a?.[1]?decodeURIComponent(a[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,a){return{jsonrpc:"2.0",id:e,error:{code:t,message:a}}}function sendJson(e,t,a){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(a))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const a of e)t.push(Buffer.isBuffer(a)?a:Buffer.from(a));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
1
|
+
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,r={}){const a=new Set(r.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...a]});if(!function isAuthorized(e,t){return!t.bearerToken||e.headers.authorization===`Bearer ${t.bearerToken}`}(e,r))return void sendJson(s,401,{error:"unauthorized"});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(a));if(a.has("a2a")&&await async function handleA2a(e,t,r,a){if(!function validateA2aVersion(e,t){const r=e.headers["a2a-version"],a=Array.isArray(r)?r[0]:r;return!a||"0.3"===a||"1.0"===a||(sendJson(t,400,jsonRpcError(null,-32006,"version_not_supported")),!1)}(t,r))return!0;if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(r,200,function createAgentCard(e,t){const r=e.inspect();return{protocolVersion:"1.0",name:r.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0,pushNotifications:!1},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:r.agents.map(e=>({id:e,name:e,description:e}))}}(e,a)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):subscribe$/u);if("GET"===t.method&&s)return function streamA2aTask(e,t,r){const a=e.inspectRequest(t);if(!a)return void sendJson(r,404,{error:"task_not_found"});if(writeSseHeaders(r),writeSse(r,{jsonrpc:"2.0",result:{task:toA2aTask(a)}}),function isTerminalState(e){return"completed"===e||"failed"===e||"cancelled"===e||"canceled"===e||"rejected"===e}(a.summary.state))return void r.end();const s=e.subscribe(e=>{e.requestId===t&&writeSse(r,{jsonrpc:"2.0",result:{event:toA2aEvent(e)}})});r.on("close",s)}(e,s,r),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);if("POST"===t.method&&n)return e.cancel(n,"a2a_cancel"),sendJson(r,200,{task:e.inspectRequest(n)}),!0;const o=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&o){const t=e.inspectRequest(o);return sendJson(r,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}return"GET"===t.method&&"/a2a/tasks"===t.url?(sendJson(r,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(r,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),r),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,r){if("SendMessage"===t.method){const a=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(r,200,jsonRpcResult(t.id,{task:toA2aTask(a)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const a=readString(readRecord(t.params)?.id),s=a?e.inspectRequest(a):void 0;return void sendJson(r,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}if("ListTasks"!==t.method){if("CancelTask"===t.method){const a=readString(readRecord(t.params)?.id);a&&e.cancel(a,"a2a_cancel");const s=a?e.inspectRequest(a):void 0;return void sendJson(r,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}sendJson(r,404,jsonRpcError(t.id,-32601,"method_not_found"))}else sendJson(r,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},r,t.id)}(e,await readJson(t),r),!0)}(t,e,s,r))return;if(a.has("agui")&&await async function handleAgui(e,t,r){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(r,200,{protocol:"ag-ui",transports:["http+sse"],events:["RUN_STARTED","TEXT_MESSAGE_START","TEXT_MESSAGE_CONTENT","TEXT_MESSAGE_END","CUSTOM","RUN_FINISHED","RUN_ERROR"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,r){const a=readRuntimeRequestExtension(t),s=a?.requestId??readString(t.runId)??crypto.randomUUID(),n=a?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);var i;writeSseHeaders(r),writeSse(r,{type:"RUN_STARTED",timestamp:Date.now(),threadId:n,runId:s,input:(i={body:t,input:o,threadId:n,runId:s},{threadId:i.threadId,runId:i.runId,messages:Array.isArray(i.body.messages)?i.body.messages:[{id:`${i.runId}-user`,role:"user",content:i.input}],tools:Array.isArray(i.body.tools)?i.body.tools:[],context:Array.isArray(i.body.context)?i.body.context:[]})});const d=e.subscribe(e=>{e.requestId===s&&writeSse(r,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"CUSTOM",timestamp:Date.now(),name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"TOOL_CALL_START",timestamp:Date.now(),toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"TOOL_CALL_RESULT",timestamp:Date.now(),messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:JSON.stringify(e.output),role:"tool"}:{type:"CUSTOM",timestamp:Date.now(),name:`stable-harness.${e.type}`,value:e}}(e))});try{const i=await e.request(a??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),d=`${s}-message`;i.output&&function writeAguiText(e,t,r){writeSse(e,{type:"TEXT_MESSAGE_START",timestamp:Date.now(),messageId:t,role:"assistant"}),writeSse(e,{type:"TEXT_MESSAGE_CONTENT",timestamp:Date.now(),messageId:t,delta:r}),writeSse(e,{type:"TEXT_MESSAGE_END",timestamp:Date.now(),messageId:t})}(r,d,i.output),writeSse(r,{type:"RUN_FINISHED",timestamp:Date.now(),threadId:n,runId:s,result:i.output})}catch(e){writeSse(r,{type:"RUN_ERROR",timestamp:Date.now(),threadId:n,runId:s,message:errorMessage(e),code:"runtime_error"})}finally{d(),r.end()}}(e,await readJson(t),r),!0)}(t,e,s))return;if(a.has("acp")&&await async function handleAcp(e,t,r){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(r,200,acpInitializeResult()),!0;if("POST"===t.method&&"/acp"===t.url){const a=await readJson(t),s=await handleAcpJsonRpcMessage(e,a);return s?sendJson(r,200,s):r.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const r=readString(readRecord(t.params)?.sessionId);return void(r&&e.listRequests({sessionId:r,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,acpInitializeResult(0,readRecord(t.params)));if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const r=readRecord(t.params)??{},a=readRuntimeRequestExtension(r),s=await e.request(a??{input:readPromptText(r),sessionId:readString(r.sessionId),agentId:readString(r.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,r,a){const s=toA2aRuntimeRequest(t);var n;writeSseHeaders(r),writeSse(r,{jsonrpc:"2.0",id:a,result:{task:(n=s,{id:n.requestId??crypto.randomUUID(),contextId:n.sessionId,status:{state:"submitted",timestamp:(new Date).toISOString()},metadata:{agentId:n.agentId}})}});const o=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(r,{jsonrpc:"2.0",id:a,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(r,{jsonrpc:"2.0",id:a,result:{task:toA2aTask(t),final:!0}})}finally{o(),r.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const r=readRecord(e.message)??e;return{input:readPromptText(r),requestId:readString(e.requestId)??readString(r.messageId),sessionId:readString(e.taskId)??readString(r.taskId)??readString(r.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,r="output"in e?e.output:e.output??"",a="requestId"in e?e.requestId:e.summary.requestId;return{id:a,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:r?{role:"agent",messageId:`${a}-response`,parts:[{kind:"text",text:r}]}:void 0,timestamp:(new Date).toISOString()},artifacts:r?[{artifactId:`${a}-output`,name:"result",parts:[{kind:"text",text:r}]}]:void 0}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(r=e.type,"runtime.request.started"===r?"working":"runtime.request.completed"===r?"completed":"runtime.request.failed"===r?"failed":"runtime.request.cancelled"===r?"canceled":"working"),message:t,timestamp:(new Date).toISOString()},metadata:{stableHarnessEvent:e}};var r}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),r=readString(t?.toolId);return r?{toolId:r,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function acpInitializeResult(e,t){return"number"==typeof t?.protocolVersion&&t.protocolVersion,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},mcpCapabilities:{http:!1,sse:!1},session:{update:!0,cancel:!0,list:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"},authMethods:[]}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const r=(e??"").match(t);return r?.[1]?decodeURIComponent(r[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,r){return{jsonrpc:"2.0",id:e,error:{code:t,message:r}}}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@stable-harness/core": "0.0.
|
|
13
|
+
"@stable-harness/core": "0.0.110"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/workspace-yaml",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
".": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@stable-harness/core": "0.0.
|
|
14
|
+
"@stable-harness/core": "0.0.110"
|
|
15
15
|
}
|
|
16
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stable-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -82,20 +82,21 @@
|
|
|
82
82
|
"@langchain/node-vfs": "^0.1.4",
|
|
83
83
|
"@langchain/ollama": "^1.2.7",
|
|
84
84
|
"@langchain/openai": "^1.4.5",
|
|
85
|
-
"@stable-harness/adapter-deepagents": "0.0.
|
|
86
|
-
"@stable-harness/adapter-langgraph": "0.0.
|
|
87
|
-
"@stable-harness/core": "0.0.
|
|
88
|
-
"@stable-harness/governance": "0.0.
|
|
89
|
-
"@stable-harness/memory": "0.0.
|
|
90
|
-
"@stable-harness/protocols": "0.0.
|
|
91
|
-
"@stable-harness/tool-gateway": "0.0.
|
|
92
|
-
"@stable-harness/workspace-yaml": "0.0.
|
|
85
|
+
"@stable-harness/adapter-deepagents": "0.0.110",
|
|
86
|
+
"@stable-harness/adapter-langgraph": "0.0.110",
|
|
87
|
+
"@stable-harness/core": "0.0.110",
|
|
88
|
+
"@stable-harness/governance": "0.0.110",
|
|
89
|
+
"@stable-harness/memory": "0.0.110",
|
|
90
|
+
"@stable-harness/protocols": "0.0.110",
|
|
91
|
+
"@stable-harness/tool-gateway": "0.0.110",
|
|
92
|
+
"@stable-harness/workspace-yaml": "0.0.110",
|
|
93
93
|
"deepagents": "^1.10.1",
|
|
94
94
|
"langchain": "^1.4.0",
|
|
95
95
|
"yaml": "^2.8.2",
|
|
96
96
|
"zod": "^4.1.13"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
|
+
"@ag-ui/core": "^0.0.53",
|
|
99
100
|
"@types/node": "^25.6.2",
|
|
100
101
|
"terser": "^5.47.1",
|
|
101
102
|
"typescript": "^5.9.3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@langchain/node-vfs": "^0.1.4",
|
|
16
16
|
"@langchain/ollama": "^1.2.7",
|
|
17
17
|
"@langchain/openai": "^1.4.5",
|
|
18
|
-
"@stable-harness/core": "0.0.
|
|
18
|
+
"@stable-harness/core": "0.0.110",
|
|
19
19
|
"deepagents": "^1.10.1",
|
|
20
20
|
"langchain": "^1.4.0"
|
|
21
21
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-langgraph",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"@langchain/langgraph": "^1.3.0",
|
|
14
|
-
"@stable-harness/core": "0.0.
|
|
14
|
+
"@stable-harness/core": "0.0.110"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{createInterface as s}from"node:readline/promises";import{stdin as t,stdout as n}from"node:process";import{agentProtocolBaseUrl as o,daemonBaseUrl as r,isDaemonAvailable as i,sendAgentProtocolRuntimeRequest as a}from"../daemon/client.js";import{formatCliRuntimeEvent as c}from"../event-view.js";export async function runConsole(l){const u=await async function createConsoleClient(e){if("stable-runtime"!==e.args.clientProtocol){if("local"===e.args.runtimeMode)throw new Error(`stable-harness console: protocol ${e.args.clientProtocol} requires a protocol server; use --protocol stable-runtime for local mode`);return await async function createAgentProtocolConsoleClient(e,s){const t=e.clientProtocol,n=o(s,e.protocolUrl);if("stable-runtime"===t)throw new Error("stable-runtime is handled by the native console client");if(!(await fetch(`${n}/health`,{signal:AbortSignal.timeout(300)})).ok)throw new Error(`stable-harness console: protocol ${t} unavailable at ${n}`);return{mode:"protocol",protocol:t,baseUrl:n,request:e=>a(n,t,e),health:async()=>"ok",sessions:unsupportedList("sessions",t),requests:unsupportedList("requests",t),inspectRequest:async()=>{},memories:unsupportedList("memory",t),clearSession:unsupportedNumber("clear",t),debug:async()=>({protocol:t,baseUrl:n,note:"Protocol console exposes prompt turns only. Use --protocol stable-runtime for operator inspection."}),stream:()=>({close:()=>{}})}}(e.args,e.runtimePolicy)}if("local"===e.args.runtimeMode)return createLocalConsoleClient(await e.createRuntime(),e.eventView);const s=r(e.runtimePolicy,e.args.daemonUrl);if(await i(s,e.args.workspaceRoot))return function createDaemonConsoleClient(e,s){return{mode:"daemon",baseUrl:e,request:s=>async function postJson(e,s){const t=await fetch(e,{method:"POST",body:JSON.stringify(s)});if(!t.ok)throw new Error(`HTTP ${t.status}`);return await t.json()}(`${e}/requests`,s),health:async()=>(await getJson(`${e}/health`)).ok?"ok":"unhealthy",sessions:()=>getJson(`${e}/sessions`),requests:s=>getJson(`${e}/requests${s?`?sessionId=${encodeURIComponent(s)}`:""}`),inspectRequest:async s=>{const t=await fetch(`${e}/requests/${encodeURIComponent(s)}`);return t.ok?await t.json():void 0},memories:s=>getJson(`${e}/memories${s?`?namespace=${encodeURIComponent(s)}`:""}`),clearSession:async s=>(await async function deleteJson(e){const s=await fetch(e,{method:"DELETE"});if(!s.ok)throw new Error(`HTTP ${s.status}`);return await s.json()}(`${e}/sessions/${encodeURIComponent(s)}`)).deletedCount,debug:()=>getJson(`${e}/inspect`),stream:(t,n)=>function streamDaemonEvents(e,s,t,n){const o=new AbortController;return fetch(`${e}/events?requestId=${encodeURIComponent(s)}`,{signal:o.signal}).then(async e=>{const s=e.body?.getReader();if(!s)return;let o="";for(;;){const{value:e,done:r}=await s.read();if(r)return;o+=Buffer.from(e).toString("utf8");const i=o.split("\n\n");o=i.pop()??"";for(const e of i){const s=e.split("\n").find(e=>e.startsWith("data: "))?.slice(6);if(!s)continue;const o=c(JSON.parse(s),t);o&&n(o)}}}).catch(e=>{o.signal.aborted||process.stderr.write(`stable-harness console event stream failed: ${String(e)}\n`)}),{close:()=>o.abort()}}(e,t,s,n)}}(s,e.eventView);if("daemon"===e.args.runtimeMode)throw new Error(`stable-harness console: daemon required but unavailable at ${s}`);return createLocalConsoleClient(await e.createRuntime(),e.eventView)}(l);let d=l.args.sessionId??e(),m=l.args.agentId;process.stderr.write(`stable-harness console: ${function consoleRuntimeMessage(e,s){return"daemon"===e.mode?`connected to daemon at ${e.baseUrl}`:"protocol"===e.mode?`connected through ${e.protocol} at ${e.baseUrl}; prompt turns only, use --protocol stable-runtime for operator session APIs`:"local"===s?"local mode selected; running in-process":"no matching daemon found; running in-process"}(u,l.args.runtimeMode)}\n`),process.stdout.write(`session ${d}\n`);const p=s({input:t,output:n,terminal:process.stdout.isTTY});try{const e=process.stdout.isTTY?await async function runInteractiveLoop(e){let{sessionId:s,agentId:t}=e;for(;;){const n=await e.rl.question("stable> "),o=await handleConsoleLineSafely({client:e.client,line:n.trim(),sessionId:s,agentId:t,eventView:e.eventView});if(o.done)return o;s=o.sessionId,t=o.agentId}}({rl:p,client:u,sessionId:d,agentId:m,eventView:l.eventView}):await async function runPipedLoop(e){let{sessionId:s,agentId:t}=e;for await(const n of e.rl){const o=await handleConsoleLineSafely({client:e.client,line:n.trim(),sessionId:s,agentId:t,eventView:e.eventView});if(o.done)return o;s=o.sessionId,t=o.agentId}return{done:!0,sessionId:s,agentId:t}}({rl:p,client:u,sessionId:d,agentId:m,eventView:l.eventView});d=e.sessionId,m=e.agentId}catch(e){if(!function isEndOfInput(e){return e instanceof Error&&e.message.includes("closed")}(e))throw e}finally{p.close()}}async function handleConsoleLineSafely(s){try{return await async function handleConsoleLine(s){const t=function parseConsoleCommand(e){if(!e.startsWith("/"))return;const[s="",...t]=e.slice(1).trim().split(/\s+/u);return{name:s,args:t.join(" ").trim()}}(s.line);return s.line?t?await async function runConsoleCommand(s){const{client:t,command:n}=s;if("exit"===n.name||"quit"===n.name)return{...s,done:!0};if("help"===n.name)!function printHelp(){process.stdout.write(["/help Show console commands.","/session [id] Show or switch session.","/new Create and switch to a new session.","/sessions List known sessions.","/agent [id] Show or switch selected agent.","/tool <id> [json] Invoke a tool in the current session.","/requests [all] List current-session or all requests.","/memory [namespace] List memory records.","/clear Delete current session requests.","/health Check runtime health.","/protocol Show connection protocol and capability level.","/capabilities Show connection protocol and capability level.","/debug Print runtime inspection JSON.","/exit Leave console.",""].join("\n"))}();else if("health"===n.name)process.stdout.write(`${await t.health()}\n`);else if("debug"===n.name)process.stdout.write(`${JSON.stringify(await t.debug())}\n`);else if("protocol"===n.name||"capabilities"===n.name)!function printProtocolCapabilities(e){const s="protocol"===e.mode?e.protocol:"stable-runtime",t="protocol"===e.mode?"prompt-turns-only":"full-operator-session-api";process.stdout.write(`${JSON.stringify({mode:e.mode,protocol:s,baseUrl:e.baseUrl,session:t})}\n`)}(t);else{if("session"===n.name)return switchSession(s,n.args);if("new"===n.name)return switchSession(s,e());if("agent"===n.name)return function switchAgent(e,s){const t=s.trim()||void 0;return process.stdout.write(`agent ${t??"default"}\n`),{done:!1,sessionId:e.sessionId,agentId:t}}(s,n.args);"sessions"===n.name?printJsonLines(await t.sessions(),"sessionId"):"requests"===n.name?printJsonLines(await t.requests("all"===n.args?void 0:s.sessionId),"requestId"):"memory"===n.name||"memories"===n.name?printJsonLines(await t.memories(n.args||void 0),"id"):"clear"===n.name?process.stdout.write(`cleared ${await t.clearSession(s.sessionId)} requests from ${s.sessionId}\n`):"tool"===n.name?await async function runConsoleTool(e){const s=e.command.args.match(/^(\S+)(?:\s+([\s\S]+))?$/u);s?.[1]?await runConsoleRequest(e.client,{input:"",sessionId:e.sessionId,...e.agentId?{agentId:e.agentId}:{},toolCall:{toolId:s[1],args:s[2]?JSON.parse(s[2]):{}}}):process.stdout.write("usage: /tool <tool-id> [json-args]\n")}(s):process.stdout.write(`unknown command: /${n.name}\n`)}return{done:!1,sessionId:s.sessionId,agentId:s.agentId}}({...s,command:t}):(await runConsoleRequest(s.client,await async function withSessionHistory(e,s){if(!s.sessionId||s.toolCall||s.workflow)return s;if("protocol"===e.mode)return{...s,metadata:{...s.metadata,consoleSession:!0,protocol:e.protocol}};const t=await async function buildSessionHistory(e,s){const t=(await e.requests(s)).filter(e=>"completed"===e.state).slice(-6),n=[];for(const s of t){const t=await e.inspectRequest(s.requestId),o=readHistoryInput(t),r=t?.output?.trim();o&&r&&n.push({role:"user",content:o},{role:"assistant",content:r})}return n.slice(-12)}(e,s.sessionId);return{...s,metadata:{...s.metadata,consoleSession:!0,openaiMessages:[...t,{role:"user",content:s.input}],openaiSessionHistory:t.length>0}}}(s.client,{input:s.line,sessionId:s.sessionId,...s.agentId?{agentId:s.agentId}:{}})),{done:!1,sessionId:s.sessionId,agentId:s.agentId}):{done:!1,sessionId:s.sessionId,agentId:s.agentId}}(s)}catch(e){return process.stdout.write(`error: ${function errorMessage(e){return e instanceof Error?e.message:String(e)}(e)}\n`),{done:!1,sessionId:s.sessionId,agentId:s.agentId}}}async function runConsoleRequest(s,t){const n=t.requestId??e(),o=s.stream(n,e=>process.stdout.write(`${e}\n`));try{const e=await s.request({...t,requestId:n});process.stdout.write(`${e.output}\n`)}finally{o.close()}}function createLocalConsoleClient(e,s){return{mode:"local",request:s=>e.request(s),health:async()=>"ok",sessions:async()=>e.listSessions(),requests:async s=>e.listRequests(s?{sessionId:s}:void 0),inspectRequest:async s=>e.inspectRequest(s),memories:async s=>e.listMemories(s?{namespace:s}:{}),clearSession:async s=>e.deleteSession(s).deletedCount,debug:async()=>e.inspect(),stream:(t,n)=>({close:e.subscribe(e=>{if(e.requestId===t){const t=c(e,s);t&&n(t)}})})}}function unsupportedList(e,s){return async()=>(process.stdout.write(`/${e} is not available through ${s}; use --protocol stable-runtime for operator features.\n`),[])}function unsupportedNumber(e,s){return async()=>(process.stdout.write(`/${e} is not available through ${s}; use --protocol stable-runtime for operator features.\n`),0)}function readHistoryInput(e){const s=Array.isArray(e?.metadata?.openaiMessages)?e.metadata.openaiMessages:[],t=s.map(e=>"object"==typeof e&&e?e:{}).filter(e=>"user"===e.role&&"string"==typeof e.content&&e.content.trim()).at(-1)?.content;return"string"==typeof t?t:e?.input.trim()||void 0}function switchSession(e,s){const t=s.trim()||e.sessionId;return process.stdout.write(`session ${t}\n`),{done:!1,sessionId:t,agentId:e.agentId}}function printJsonLines(e,s){if(0!==e.length)for(const t of e){const e="object"==typeof t&&t?t:{};process.stdout.write(`${String(e[s]??"")} ${JSON.stringify(t)}\n`)}else process.stdout.write("none\n")}async function getJson(e){const s=await fetch(e);if(!s.ok)throw new Error(`HTTP ${s.status}`);return await s.json()}
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{createInterface as s}from"node:readline/promises";import{stdin as t,stdout as n}from"node:process";import{agentProtocolBaseUrl as o,daemonBaseUrl as r,isDaemonAvailable as i,sendAgentProtocolRuntimeRequest as a}from"../daemon/client.js";import{formatCliRuntimeEvent as c}from"../event-view.js";export async function runConsole(l){const u=await async function createConsoleClient(e){if("stable-runtime"!==e.args.clientProtocol){if("local"===e.args.runtimeMode)throw new Error(`stable-harness console: protocol ${e.args.clientProtocol} requires a protocol server; use --protocol stable-runtime for local mode`);return await async function createAgentProtocolConsoleClient(e,s){const t=e.clientProtocol,n=o(s,e.protocolUrl);if("stable-runtime"===t)throw new Error("stable-runtime is handled by the native console client");if(!(await fetch(`${n}/health`,{signal:AbortSignal.timeout(300)})).ok)throw new Error(`stable-harness console: protocol ${t} unavailable at ${n}`);return{mode:"protocol",protocol:t,baseUrl:n,request:s=>a(n,t,s,e.apiKey),health:async()=>"ok",sessions:unsupportedList("sessions",t),requests:unsupportedList("requests",t),inspectRequest:async()=>{},memories:unsupportedList("memory",t),clearSession:unsupportedNumber("clear",t),debug:async()=>({protocol:t,baseUrl:n,note:"Protocol console exposes prompt turns only. Use --protocol stable-runtime for operator inspection."}),stream:()=>({close:()=>{}})}}(e.args,e.runtimePolicy)}if("local"===e.args.runtimeMode)return createLocalConsoleClient(await e.createRuntime(),e.eventView);const s=r(e.runtimePolicy,e.args.daemonUrl);if(await i(s,e.args.workspaceRoot))return function createDaemonConsoleClient(e,s){return{mode:"daemon",baseUrl:e,request:s=>async function postJson(e,s){const t=await fetch(e,{method:"POST",body:JSON.stringify(s)});if(!t.ok)throw new Error(`HTTP ${t.status}`);return await t.json()}(`${e}/requests`,s),health:async()=>(await getJson(`${e}/health`)).ok?"ok":"unhealthy",sessions:()=>getJson(`${e}/sessions`),requests:s=>getJson(`${e}/requests${s?`?sessionId=${encodeURIComponent(s)}`:""}`),inspectRequest:async s=>{const t=await fetch(`${e}/requests/${encodeURIComponent(s)}`);return t.ok?await t.json():void 0},memories:s=>getJson(`${e}/memories${s?`?namespace=${encodeURIComponent(s)}`:""}`),clearSession:async s=>(await async function deleteJson(e){const s=await fetch(e,{method:"DELETE"});if(!s.ok)throw new Error(`HTTP ${s.status}`);return await s.json()}(`${e}/sessions/${encodeURIComponent(s)}`)).deletedCount,debug:()=>getJson(`${e}/inspect`),stream:(t,n)=>function streamDaemonEvents(e,s,t,n){const o=new AbortController;return fetch(`${e}/events?requestId=${encodeURIComponent(s)}`,{signal:o.signal}).then(async e=>{const s=e.body?.getReader();if(!s)return;let o="";for(;;){const{value:e,done:r}=await s.read();if(r)return;o+=Buffer.from(e).toString("utf8");const i=o.split("\n\n");o=i.pop()??"";for(const e of i){const s=e.split("\n").find(e=>e.startsWith("data: "))?.slice(6);if(!s)continue;const o=c(JSON.parse(s),t);o&&n(o)}}}).catch(e=>{o.signal.aborted||process.stderr.write(`stable-harness console event stream failed: ${String(e)}\n`)}),{close:()=>o.abort()}}(e,t,s,n)}}(s,e.eventView);if("daemon"===e.args.runtimeMode)throw new Error(`stable-harness console: daemon required but unavailable at ${s}`);return createLocalConsoleClient(await e.createRuntime(),e.eventView)}(l);let d=l.args.sessionId??e(),m=l.args.agentId;process.stderr.write(`stable-harness console: ${function consoleRuntimeMessage(e,s){return"daemon"===e.mode?`connected to daemon at ${e.baseUrl}`:"protocol"===e.mode?`connected through ${e.protocol} at ${e.baseUrl}; prompt turns only, use --protocol stable-runtime for operator session APIs`:"local"===s?"local mode selected; running in-process":"no matching daemon found; running in-process"}(u,l.args.runtimeMode)}\n`),process.stdout.write(`session ${d}\n`);const p=s({input:t,output:n,terminal:process.stdout.isTTY});try{const e=process.stdout.isTTY?await async function runInteractiveLoop(e){let{sessionId:s,agentId:t}=e;for(;;){const n=await e.rl.question("stable> "),o=await handleConsoleLineSafely({client:e.client,line:n.trim(),sessionId:s,agentId:t,eventView:e.eventView});if(o.done)return o;s=o.sessionId,t=o.agentId}}({rl:p,client:u,sessionId:d,agentId:m,eventView:l.eventView}):await async function runPipedLoop(e){let{sessionId:s,agentId:t}=e;for await(const n of e.rl){const o=await handleConsoleLineSafely({client:e.client,line:n.trim(),sessionId:s,agentId:t,eventView:e.eventView});if(o.done)return o;s=o.sessionId,t=o.agentId}return{done:!0,sessionId:s,agentId:t}}({rl:p,client:u,sessionId:d,agentId:m,eventView:l.eventView});d=e.sessionId,m=e.agentId}catch(e){if(!function isEndOfInput(e){return e instanceof Error&&e.message.includes("closed")}(e))throw e}finally{p.close()}}async function handleConsoleLineSafely(s){try{return await async function handleConsoleLine(s){const t=function parseConsoleCommand(e){if(!e.startsWith("/"))return;const[s="",...t]=e.slice(1).trim().split(/\s+/u);return{name:s,args:t.join(" ").trim()}}(s.line);return s.line?t?await async function runConsoleCommand(s){const{client:t,command:n}=s;if("exit"===n.name||"quit"===n.name)return{...s,done:!0};if("help"===n.name)!function printHelp(){process.stdout.write(["/help Show console commands.","/session [id] Show or switch session.","/new Create and switch to a new session.","/sessions List known sessions.","/agent [id] Show or switch selected agent.","/tool <id> [json] Invoke a tool in the current session.","/requests [all] List current-session or all requests.","/memory [namespace] List memory records.","/clear Delete current session requests.","/health Check runtime health.","/protocol Show connection protocol and capability level.","/capabilities Show connection protocol and capability level.","/debug Print runtime inspection JSON.","/exit Leave console.",""].join("\n"))}();else if("health"===n.name)process.stdout.write(`${await t.health()}\n`);else if("debug"===n.name)process.stdout.write(`${JSON.stringify(await t.debug())}\n`);else if("protocol"===n.name||"capabilities"===n.name)!function printProtocolCapabilities(e){const s="protocol"===e.mode?e.protocol:"stable-runtime",t="protocol"===e.mode?"prompt-turns-only":"full-operator-session-api";process.stdout.write(`${JSON.stringify({mode:e.mode,protocol:s,baseUrl:e.baseUrl,session:t})}\n`)}(t);else{if("session"===n.name)return switchSession(s,n.args);if("new"===n.name)return switchSession(s,e());if("agent"===n.name)return function switchAgent(e,s){const t=s.trim()||void 0;return process.stdout.write(`agent ${t??"default"}\n`),{done:!1,sessionId:e.sessionId,agentId:t}}(s,n.args);"sessions"===n.name?printJsonLines(await t.sessions(),"sessionId"):"requests"===n.name?printJsonLines(await t.requests("all"===n.args?void 0:s.sessionId),"requestId"):"memory"===n.name||"memories"===n.name?printJsonLines(await t.memories(n.args||void 0),"id"):"clear"===n.name?process.stdout.write(`cleared ${await t.clearSession(s.sessionId)} requests from ${s.sessionId}\n`):"tool"===n.name?await async function runConsoleTool(e){const s=e.command.args.match(/^(\S+)(?:\s+([\s\S]+))?$/u);s?.[1]?await runConsoleRequest(e.client,{input:"",sessionId:e.sessionId,...e.agentId?{agentId:e.agentId}:{},toolCall:{toolId:s[1],args:s[2]?JSON.parse(s[2]):{}}}):process.stdout.write("usage: /tool <tool-id> [json-args]\n")}(s):process.stdout.write(`unknown command: /${n.name}\n`)}return{done:!1,sessionId:s.sessionId,agentId:s.agentId}}({...s,command:t}):(await runConsoleRequest(s.client,await async function withSessionHistory(e,s){if(!s.sessionId||s.toolCall||s.workflow)return s;if("protocol"===e.mode)return{...s,metadata:{...s.metadata,consoleSession:!0,protocol:e.protocol}};const t=await async function buildSessionHistory(e,s){const t=(await e.requests(s)).filter(e=>"completed"===e.state).slice(-6),n=[];for(const s of t){const t=await e.inspectRequest(s.requestId),o=readHistoryInput(t),r=t?.output?.trim();o&&r&&n.push({role:"user",content:o},{role:"assistant",content:r})}return n.slice(-12)}(e,s.sessionId);return{...s,metadata:{...s.metadata,consoleSession:!0,openaiMessages:[...t,{role:"user",content:s.input}],openaiSessionHistory:t.length>0}}}(s.client,{input:s.line,sessionId:s.sessionId,...s.agentId?{agentId:s.agentId}:{}})),{done:!1,sessionId:s.sessionId,agentId:s.agentId}):{done:!1,sessionId:s.sessionId,agentId:s.agentId}}(s)}catch(e){return process.stdout.write(`error: ${function errorMessage(e){return e instanceof Error?e.message:String(e)}(e)}\n`),{done:!1,sessionId:s.sessionId,agentId:s.agentId}}}async function runConsoleRequest(s,t){const n=t.requestId??e(),o=s.stream(n,e=>process.stdout.write(`${e}\n`));try{const e=await s.request({...t,requestId:n});process.stdout.write(`${e.output}\n`)}finally{o.close()}}function createLocalConsoleClient(e,s){return{mode:"local",request:s=>e.request(s),health:async()=>"ok",sessions:async()=>e.listSessions(),requests:async s=>e.listRequests(s?{sessionId:s}:void 0),inspectRequest:async s=>e.inspectRequest(s),memories:async s=>e.listMemories(s?{namespace:s}:{}),clearSession:async s=>e.deleteSession(s).deletedCount,debug:async()=>e.inspect(),stream:(t,n)=>({close:e.subscribe(e=>{if(e.requestId===t){const t=c(e,s);t&&n(t)}})})}}function unsupportedList(e,s){return async()=>(process.stdout.write(`/${e} is not available through ${s}; use --protocol stable-runtime for operator features.\n`),[])}function unsupportedNumber(e,s){return async()=>(process.stdout.write(`/${e} is not available through ${s}; use --protocol stable-runtime for operator features.\n`),0)}function readHistoryInput(e){const s=Array.isArray(e?.metadata?.openaiMessages)?e.metadata.openaiMessages:[],t=s.map(e=>"object"==typeof e&&e?e:{}).filter(e=>"user"===e.role&&"string"==typeof e.content&&e.content.trim()).at(-1)?.content;return"string"==typeof t?t:e?.input.trim()||void 0}function switchSession(e,s){const t=s.trim()||e.sessionId;return process.stdout.write(`session ${t}\n`),{done:!1,sessionId:t,agentId:e.agentId}}function printJsonLines(e,s){if(0!==e.length)for(const t of e){const e="object"==typeof t&&t?t:{};process.stdout.write(`${String(e[s]??"")} ${JSON.stringify(t)}\n`)}else process.stdout.write("none\n")}async function getJson(e){const s=await fetch(e);if(!s.ok)throw new Error(`HTTP ${s.status}`);return await s.json()}
|
|
@@ -9,4 +9,4 @@ export declare function runRequestThroughDaemon(input: {
|
|
|
9
9
|
export declare function daemonBaseUrl(policy: WorkspaceRuntimePolicy, overrideUrl?: string): string;
|
|
10
10
|
export declare function agentProtocolBaseUrl(policy: WorkspaceRuntimePolicy, overrideUrl?: string): string;
|
|
11
11
|
export declare function isDaemonAvailable(baseUrl: string, workspaceRoot: string): Promise<boolean>;
|
|
12
|
-
export declare function sendAgentProtocolRuntimeRequest(baseUrl: string, protocol: Exclude<CliArgs["clientProtocol"], "stable-runtime">, request: RuntimeRequest): Promise<RuntimeResponse>;
|
|
12
|
+
export declare function sendAgentProtocolRuntimeRequest(baseUrl: string, protocol: Exclude<CliArgs["clientProtocol"], "stable-runtime">, request: RuntimeRequest, bearerToken?: string): Promise<RuntimeResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as t}from"node:crypto";import e from"node:path";import{formatCliRuntimeEvent as r}from"../event-view.js";export async function runRequestThroughDaemon(e){if("stable-runtime"!==e.args.clientProtocol)return await async function runRequestThroughAgentProtocol(e){if("local"===e.args.runtimeMode)throw new Error(`stable-harness protocol ${e.args.clientProtocol} requires a protocol server; use --protocol stable-runtime for local mode`);const r=function readAgentProtocol(t){if("stable-runtime"!==t)return t;throw new Error("stable-runtime is handled by the native daemon client")}(e.args.clientProtocol);if(e.args.trace||e.args.traceJson)throw new Error(`stable-harness protocol ${r} does not expose Stable Runtime traces; use --protocol stable-runtime for traces`);const o=agentProtocolBaseUrl(e.runtimePolicy,e.args.protocolUrl);if(!await async function isAgentProtocolAvailable(t,e){try{const
|
|
1
|
+
import{randomUUID as t}from"node:crypto";import e from"node:path";import{formatCliRuntimeEvent as r}from"../event-view.js";export async function runRequestThroughDaemon(e){if("stable-runtime"!==e.args.clientProtocol)return await async function runRequestThroughAgentProtocol(e){if("local"===e.args.runtimeMode)throw new Error(`stable-harness protocol ${e.args.clientProtocol} requires a protocol server; use --protocol stable-runtime for local mode`);const r=function readAgentProtocol(t){if("stable-runtime"!==t)return t;throw new Error("stable-runtime is handled by the native daemon client")}(e.args.clientProtocol);if(e.args.trace||e.args.traceJson)throw new Error(`stable-harness protocol ${r} does not expose Stable Runtime traces; use --protocol stable-runtime for traces`);const o=agentProtocolBaseUrl(e.runtimePolicy,e.args.protocolUrl);if(!await async function isAgentProtocolAvailable(t,e,r){try{const o=await fetch(`${t}/health`,{headers:authHeaders(r),signal:AbortSignal.timeout(300)});if(!o.ok)return!1;const n=await o.json();return Array.isArray(n.protocols)&&n.protocols.includes("agui"===e?"agui":e)}catch{return!1}}(o,r,e.args.apiKey))throw new Error(`stable-harness protocol ${r} required but unavailable at ${o}`);process.stderr.write(`stable-harness runtime: connected through ${r} at ${o}\n`);const n=await sendAgentProtocolRuntimeRequest(o,r,toRuntimeRequest(e.args,t()),e.args.apiKey);return process.stdout.write(`${n.output}\n`),!0}(e);if("local"===e.args.runtimeMode)return!1;const o=daemonBaseUrl(e.runtimePolicy,e.args.daemonUrl);if(!await isDaemonAvailable(o,e.args.workspaceRoot)){if("daemon"===e.args.runtimeMode)throw new Error(`stable-harness runtime: daemon required but unavailable at ${o}`);return!1}process.stderr.write(`stable-harness runtime: connected to daemon at ${o}\n`);const n=t(),s=function streamDaemonEvents(t,e,o){const n=new AbortController;return async function readDaemonEventStream(t,e,o,n){try{const s=await fetch(`${t}/events?requestId=${encodeURIComponent(e)}`,{signal:n}),a=s.body?.getReader();if(!a)return;await async function readSseMessages(t,e){let r="";for(;;){const{value:o,done:n}=await t.read();if(n)return;r+=Buffer.from(o).toString("utf8");const s=r.split("\n\n");r=s.pop()??"";for(const t of s){const r=parseSseEvent(t);r&&e(r)}}}(a,t=>{const e=r(t,o);e&&process.stdout.write(`${e}\n`)})}catch(t){n.aborted||process.stderr.write(`stable-harness daemon event stream failed: ${function errorMessage(t){return t instanceof Error?t.message:String(t)}(t)}\n`)}}(t,e,o,n.signal),{close:()=>n.abort()}}(o,n,e.eventView);try{const t=await async function postRuntimeRequest(t,e,r){const o=await fetch(`${t}/requests`,{method:"POST",body:JSON.stringify(toRuntimeRequest(e,r))});if(!o.ok)throw new Error(`stable-harness daemon request failed: HTTP ${o.status}`);return await o.json()}(o,e.args,n);return(e.args.trace||e.args.traceJson)&&await async function printDaemonTrace(t,e,r){const o=await fetch(`${t}/runs/${encodeURIComponent(e)}/trace`);if(!o.ok)return;const n=await o.json();if(r)process.stdout.write(`${JSON.stringify({trace:n})}\n`);else for(const t of n)process.stdout.write(`trace:${t.agentId}:${t.label}${t.detail?` ${JSON.stringify(t.detail)}`:""}\n`)}(o,t.requestId,e.args.traceJson),process.stdout.write(`${t.output}\n`),!0}finally{s.close()}}export function daemonBaseUrl(t,e){if(e?.trim())return e.replace(/\/$/u,"");const r=protocolConfig(readRecord(t.protocols)??{},"stableRuntime","stable-runtime","http")??{};return`http://${configString(r.host)??process.env.STABLE_HARNESS_RUNTIME_HOST??"127.0.0.1"}:${configNumber(r.port)??configNumber(process.env.STABLE_HARNESS_RUNTIME_PORT)??8641}`}export function agentProtocolBaseUrl(t,e){if(e?.trim())return e.replace(/\/$/u,"");const r=readRecord(t.protocols)??{},o=protocolConfig(r,"agentProtocols","agent-protocols")??function agentProtocolConfigFromIndividual(t){for(const e of["acp","a2a","agui"]){const r=protocolConfig(t,e);if(r)return r}}(r)??{};return`http://${configString(o.host)??process.env.STABLE_HARNESS_AGENT_PROTOCOL_HOST??"127.0.0.1"}:${configNumber(o.port)??configNumber(process.env.STABLE_HARNESS_AGENT_PROTOCOL_PORT)??8650}`}export async function isDaemonAvailable(t,r){try{const o=await fetch(`${t}/inspect`,{signal:AbortSignal.timeout(300)});if(!o.ok)return!1;const n=await o.json();return Array.isArray(n.agents)&&Array.isArray(n.runs)&&n.workspaceRoot===e.resolve(r)}catch{return!1}}export async function sendAgentProtocolRuntimeRequest(e,r,o,n){const s="a2a"===r?await async function sendA2aRequest(t,e,r){const o=await postJson(`${t}/a2a/message:send`,{requestId:e.requestId,agentId:e.agentId,runtimeRequest:e,message:{role:"user",contextId:e.sessionId,parts:[{kind:"text",text:e.input}]}},r);return o.task?.status?.message?.parts?.map(t=>t.text).filter(Boolean).join("\n")??""}(e,o,n):"agui"===r?await async function sendAguiRequest(t,e,r){const o=await fetch(`${t}/ag-ui/runs`,{method:"POST",headers:authHeaders(r),body:JSON.stringify({input:e.input,runId:e.requestId,threadId:e.sessionId,agentId:e.agentId,runtimeRequest:e})});if(!o.ok||!o.body)throw new Error(`AG-UI request failed: HTTP ${o.status}`);return(await async function readProtocolSseMessages(t){const e=[];let r="";for(;;){const{value:o,done:n}=await t.read();if(n)return e;r+=Buffer.from(o).toString("utf8");const s=r.split("\n\n");r=s.pop()??"";for(const t of s){const r=t.split("\n").find(t=>t.startsWith("data: "))?.slice(6);r&&e.push(JSON.parse(r))}}}(o.body.getReader())).map(readAguiText).filter(Boolean).join("")}(e,o,n):await async function sendAcpRequest(e,r,o){const n=await postJson(`${e}/acp`,{jsonrpc:"2.0",id:r.requestId??t(),method:"session/prompt",params:{prompt:r.input,sessionId:r.sessionId,agentId:r.agentId,runtimeRequest:r}},o);if(n.error)throw new Error(n.error.message??"ACP request failed");return n.result?._meta?.output??""}(e,o,n);return{requestId:o.requestId??t(),sessionId:o.sessionId??"",agentId:o.agentId??"",state:"completed",output:s}}async function postJson(t,e,r){const o=await fetch(t,{method:"POST",headers:authHeaders(r),body:JSON.stringify(e)});if(!o.ok)throw new Error(`protocol request failed: HTTP ${o.status}`);return await o.json()}function authHeaders(t){return t?{authorization:`Bearer ${t}`}:{}}function toRuntimeRequest(t,e){return{input:t.prompt,requestId:e,...t.agentId?{agentId:t.agentId}:{},...t.sessionId?{sessionId:t.sessionId}:{},...t.toolId?{toolCall:{toolId:t.toolId,args:t.toolArgs}}:{},...t.workflowRunId?{workflow:{workflowId:t.workflowRunId,input:t.prompt}}:{}}}function readAguiText(t){const e=readRecord(t);return"TEXT_MESSAGE_CONTENT"!==e?.type&&"TextMessageChunk"!==e?.type||"string"!=typeof e.delta?"":e.delta}function parseSseEvent(t){const e=t.split("\n").find(t=>t.startsWith("data: "))?.slice(6);return e?JSON.parse(e):void 0}function protocolConfig(t,...e){for(const r of e){const e=readRecord(t[r]);if(e)return e}}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function configString(t){if("string"!=typeof t||!t.trim())return;const e=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return e?process.env[e[1]]??e[2]:t}function configNumber(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createAgentProtocolHttpServer as
|
|
1
|
+
import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createAgentProtocolHttpServer as e,createHttpServer as o,createOpenAiCompatibleHttpServer as n}from"@stable-harness/protocols";import{startOfficialLangGraphServer as s}from"./langgraph-official.js";const i="127.0.0.1",a=r(t);export async function serveProtocol(t,r){const e=createConfiguredServers(t,r),o=[];let n=0;for(const r of e)if("http"===r.kind){if(!await listen(r)){process.stdout.write(`stable-harness ${r.protocol} API already running on ${serverUrl(r)}\n`);continue}o.push(()=>closeHttpServer(r.server)),n+=1;const t=r.server.address(),e="object"==typeof t&&t?t.port:r.port;process.stdout.write(`stable-harness ${r.protocol} API listening on ${serverUrl({...r,port:e})}\n`)}else{const e=await startLangGraphServer(t,r);if(!e){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.config.host}:${r.config.port}\n`);continue}o.push(e.cleanup),n+=1,process.stdout.write(`stable-harness ${r.protocol} API listening on ${e.url}\n`)}0!==n&&await async function waitForShutdown(t){const r=setInterval(()=>{},864e5);await new Promise(e=>{const shutdown=()=>{clearInterval(r),Promise.allSettled(t.map(t=>t())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(o)}export async function stopProtocol(t,r){const e=createConfiguredServers({getRuntimePolicy:()=>t.runtime},r).map(t=>"http"===t.kind?{protocol:t.protocol,host:t.host,port:t.port}:{protocol:t.protocol,host:t.config.host,port:t.config.port}),o=await Promise.all(e.map(async t=>({target:t,pids:await stableHarnessListenerPids(t.port)}))),n=[...new Set(o.flatMap(t=>t.pids))];for(const t of n)process.kill(t,"SIGTERM");for(const{target:t,pids:r}of o)0!==r.length?process.stdout.write(`stable-harness ${t.protocol} API stopped on ${t.host}:${t.port} pid=${r.join(",")}\n`):process.stdout.write(`stable-harness ${t.protocol} API not running on ${t.host}:${t.port}\n`)}function createConfiguredServers(t,r){const e=readRecord(t.getRuntimePolicy().protocols)??{},o=protocolConfig(e,"stableRuntime","stable-runtime","http")??{},n=protocolConfig(e,"openaiCompatible","openai-compatible","openai")??{},s=function agentProtocolConfig(t){const r=protocolConfig(t,"agentProtocols","agent-protocols");if(r)return enabled(r)?r:void 0;const e=["acp","a2a","agui"].filter(r=>enabled(protocolConfig(t,r)??{enabled:!1}));return e.length>0?{protocols:e}:void 0}(e),i=protocolConfig(e,"langgraph")??{};return[...enabled(o)?[stableRuntimeServer(t,o,r)]:[],...enabled(n)?[openAiServer(t,n,r)]:[],...s?[agentProtocolServer(t,s,r)]:[],...enabled(i)?[langGraphServer(i)]:[]]}function stableRuntimeServer(t,r,e){return{kind:"http",protocol:"stable-runtime",server:o(t),host:e.host??configString(r.host)??i,port:configNumber(r.port)??configNumber(process.env.STABLE_HARNESS_RUNTIME_PORT)??8641}}function openAiServer(t,r,e){const o=configString(r.host)??i,s=e.port??configNumber(r.port)??8642,a=e.host??o,c=configString(r.bearerToken)??configString(r.apiKey)??e.apiKey;return{kind:"http",protocol:"openai-compatible",server:n(t,{bearerToken:c}),host:a,port:s,...c?{bearerToken:c}:{}}}function agentProtocolServer(t,r,o){const n=configString(r.host)??i,s=configNumber(r.port)??8650,a=o.host??n,c=configString(r.bearerToken)??configString(r.apiKey)??o.apiKey;return{kind:"http",protocol:"agent-protocols",server:e(t,{baseUrl:`http://${a}:${s}`,enabledProtocols:configProtocolIds(r.protocols),bearerToken:c}),host:a,port:s,...c?{bearerToken:c}:{}}}function langGraphServer(t){const r=configString(t.host)??i,e=configNumber(t.port)??2024,o=configStringArray(t.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:r,port:e,nWorkers:configNumber(t.nWorkers)??10,...o?{exposeAgents:o}:{},...void 0!==t.env?{env:t.env}:{},...void 0!==t.envFile?{envFile:t.envFile}:{}}}}function protocolConfig(t,...r){for(const e of r){const r=readRecord(t[e]);if(r)return r}}function enabled(t){return!1!==t.enabled}function configString(t){if("string"!=typeof t||!t.trim())return;const r=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:t}function configNumber(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}function configStringArray(t){if(Array.isArray(t)&&t.every(t=>"string"==typeof t))return t.filter(t=>t.trim()).map(t=>t.trim())}function configProtocolIds(t){const r=configStringArray(t)?.filter(t=>"acp"===t||"a2a"===t||"agui"===t);return r&&r.length>0?r:void 0}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}async function listen(t){try{return await new Promise((r,e)=>{t.server.once("error",e),t.server.listen(t.port,t.host,()=>{t.server.off("error",e),r()})}),!0}catch(r){if(isAddressInUse(r)&&await async function isHttpServerAlreadyRunning(t){return"stable-runtime"===t.protocol?await async function isStableRuntimeServerAlreadyRunning(t){const r=await fetchJson(`http://${t.host}:${t.port}/inspect`);return Array.isArray(r?.agents)&&Array.isArray(r?.runs)}(t):"openai-compatible"===t.protocol?await async function isOpenAiServerAlreadyRunning(t){const r=await fetchJson(`http://${t.host}:${t.port}/v1/capabilities`,{...t.bearerToken?{authorization:`Bearer ${t.bearerToken}`}:{}});return"stable_harness.capabilities"===r?.object}(t):await async function isAgentProtocolServerAlreadyRunning(t){const r=await fetchJson(`http://${t.host}:${t.port}/health`);return!0===r?.ok&&Array.isArray(r.protocols)}(t)}(t))return!1;throw portConflictError(r,t.protocol,t.host,t.port)}}async function startLangGraphServer(t,r){if(!await isLangGraphServerAlreadyRunning(r))try{return await s(t,r.config)}catch(t){if(isAddressInUse(t)&&await isLangGraphServerAlreadyRunning(r))return;throw portConflictError(t,r.protocol,r.config.host,r.config.port)}}function serverUrl(t){const r=`http://${t.host}:${t.port}`;return"openai-compatible"===t.protocol?`${r}/v1`:"agent-protocols"===t.protocol?`${r}/acp / ${r}/a2a / ${r}/ag-ui`:r}async function isLangGraphServerAlreadyRunning(t){const r=await fetchJson(`http://${t.config.host}:${t.config.port}/ok`);return!0===r?.ok}async function fetchJson(t,r={}){try{const e=await fetch(t,{headers:r});if(!e.ok)return;return await e.json()}catch{return}}function isAddressInUse(t){return"EADDRINUSE"===function readErrorCode(t){return"object"==typeof t&&null!==t&&"code"in t?t.code:void 0}(t)||String(t).includes("EADDRINUSE")}function portConflictError(t,r,e,o){return isAddressInUse(t)?new Error([`stable-harness ${r} port is already in use: ${e}:${o}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${e}:${o}.`].join("\n")):t}async function stableHarnessListenerPids(t){const r=await async function listenerPids(t){try{const{stdout:r}=await a("lsof",[`-tiTCP:${t}`,"-sTCP:LISTEN"]);return r.split(/\s+/u).map(t=>Number(t)).filter(t=>Number.isInteger(t)&&t>0)}catch{return[]}}(t);return(await Promise.all(r.map(async t=>{const r=await async function processCommand(t){try{const{stdout:r}=await a("ps",["-p",String(t),"-o","command="]);return r.trim()}catch{return""}}(t);return isStableHarnessStartCommand(r)?t:void 0}))).filter(t=>"number"==typeof t)}export function isStableHarnessStartCommand(t){if(function hasUnsafeCommandCharacters(t){return/[\u0000-\u001F\u007F;|`&<>]/u.test(t)}(t))return!1;const r=function splitCommandLine(t){const r=[];let e,o="";for(const n of t)'"'!==n&&"'"!==n||void 0!==e?n!==e?/\s/u.test(n)&&void 0===e?o&&(r.push(o),o=""):o+=n:e=void 0:e=n;return o&&r.push(o),r}(t),e=function stableHarnessCommandIndex(t){return isStableHarnessExecutableToken(t[0]??"")?0:function isNodeExecutableToken(t){const r=t.split(/[\\/]/u).at(-1);return"node"===r||"nodejs"===r}(t[0]??"")&&(isStableHarnessExecutableToken(t[1]??"")||function isStableHarnessScriptToken(t){if(hasTraversalSegment(t))return!1;const r=t.replaceAll("\\","/");return r.includes("/stable-harness/")&&r.endsWith("/packages/cli/dist/src/cli.js")}(t[1]??""))?1:-1}(r);return e>=0&&r.slice(e+1).includes("start")}function isStableHarnessExecutableToken(t){if(hasTraversalSegment(t))return!1;const r=t.split(/[\\/]/u).at(-1);return"stable-harness"===r||"botbotgo"===r}function hasTraversalSegment(t){return t.split(/[\\/]/u).some(t=>"."===t||".."===t)}async function closeHttpServer(t){await new Promise((r,e)=>{t.close(t=>{t?e(t):r()})})}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"types": "dist/src/index.d.ts",
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"@langchain/langgraph-api": "^1.2.1",
|
|
17
|
-
"@stable-harness/adapter-deepagents": "0.0.
|
|
18
|
-
"@stable-harness/adapter-langgraph": "0.0.
|
|
19
|
-
"@stable-harness/core": "0.0.
|
|
20
|
-
"@stable-harness/memory": "0.0.
|
|
21
|
-
"@stable-harness/protocols": "0.0.
|
|
22
|
-
"@stable-harness/tool-gateway": "0.0.
|
|
23
|
-
"@stable-harness/workspace-yaml": "0.0.
|
|
17
|
+
"@stable-harness/adapter-deepagents": "0.0.110",
|
|
18
|
+
"@stable-harness/adapter-langgraph": "0.0.110",
|
|
19
|
+
"@stable-harness/core": "0.0.110",
|
|
20
|
+
"@stable-harness/memory": "0.0.110",
|
|
21
|
+
"@stable-harness/protocols": "0.0.110",
|
|
22
|
+
"@stable-harness/tool-gateway": "0.0.110",
|
|
23
|
+
"@stable-harness/workspace-yaml": "0.0.110"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
".": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@stable-harness/governance": "0.0.
|
|
15
|
-
"@stable-harness/memory": "0.0.
|
|
14
|
+
"@stable-harness/governance": "0.0.110",
|
|
15
|
+
"@stable-harness/memory": "0.0.110"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/evaluation",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@stable-harness/core": "0.0.
|
|
13
|
+
"@stable-harness/core": "0.0.110"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const r=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...r]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(r));if(r.has("a2a")&&await async function handleA2a(e,t,a,r){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(a,200,function createAgentCard(e,t){const a=e.inspect();return{protocolVersion:"1.0",name:a.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,r)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(a,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(a,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&n?(e.cancel(n,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(n)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(a,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),a),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,a){if("SendMessage"===t.method){const r=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(r)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const r=readString(readRecord(t.params)?.id),s=r?e.inspectRequest(r):void 0;return void sendJson(a,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(a,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},a,t.id)}(e,await readJson(t),a),!0)}(t,e,s,a))return;if(r.has("agui")&&await async function handleAgui(e,t,a){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(a,200,{protocol:"ag-ui",transports:["http+sse"],events:["RunStarted","TextMessageChunk","Custom","RunFinished","RunError"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const r=readRuntimeRequestExtension(t),s=r?.requestId??readString(t.runId)??crypto.randomUUID(),n=r?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RunStarted",threadId:n,runId:s,input:{messages:t.messages,input:o}});const d=e.subscribe(e=>{e.requestId===s&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"Custom",name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"ToolCallStart",toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"ToolCallResult",messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:e.output,role:"tool"}:{type:"Custom",name:`stable-harness.${e.type}`,value:e}}(e))});try{const d=await e.request(r??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${s}-message`;d.output&&writeSse(a,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:d.output}),writeSse(a,{type:"RunFinished",threadId:n,runId:s,outcome:{type:"completed"===d.state?"success":"interrupt"},result:d.output})}catch(e){writeSse(a,{type:"RunError",threadId:n,runId:s,message:errorMessage(e)})}finally{d(),a.end()}}(e,await readJson(t),a),!0)}(t,e,s))return;if(r.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}}),!0;if("POST"===t.method&&"/acp"===t.url){const r=await readJson(t),s=await handleAcpJsonRpcMessage(e,r);return s?sendJson(a,200,s):a.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}});if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},r=readRuntimeRequestExtension(a),s=await e.request(r??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,r){const s=toA2aRuntimeRequest(t);writeSseHeaders(a);const n=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(a,{jsonrpc:"2.0",id:r,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(a,{jsonrpc:"2.0",id:r,result:{task:toA2aTask(t),final:!0}})}finally{n(),a.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const a=readRecord(e.message)??e;return{input:readPromptText(a),requestId:readString(e.requestId)??readString(a.messageId),sessionId:readString(e.taskId)??readString(a.taskId)??readString(a.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,a="output"in e?e.output:e.output??"",r="requestId"in e?e.requestId:e.summary.requestId;return{id:r,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${r}-response`,parts:[{kind:"text",text:a}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const a=(e??"").match(t);return a?.[1]?decodeURIComponent(a[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,a){return{jsonrpc:"2.0",id:e,error:{code:t,message:a}}}function sendJson(e,t,a){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(a))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const a of e)t.push(Buffer.isBuffer(a)?a:Buffer.from(a));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
1
|
+
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,r={}){const a=new Set(r.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...a]});if(!function isAuthorized(e,t){return!t.bearerToken||e.headers.authorization===`Bearer ${t.bearerToken}`}(e,r))return void sendJson(s,401,{error:"unauthorized"});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(a));if(a.has("a2a")&&await async function handleA2a(e,t,r,a){if(!function validateA2aVersion(e,t){const r=e.headers["a2a-version"],a=Array.isArray(r)?r[0]:r;return!a||"0.3"===a||"1.0"===a||(sendJson(t,400,jsonRpcError(null,-32006,"version_not_supported")),!1)}(t,r))return!0;if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(r,200,function createAgentCard(e,t){const r=e.inspect();return{protocolVersion:"1.0",name:r.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0,pushNotifications:!1},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:r.agents.map(e=>({id:e,name:e,description:e}))}}(e,a)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):subscribe$/u);if("GET"===t.method&&s)return function streamA2aTask(e,t,r){const a=e.inspectRequest(t);if(!a)return void sendJson(r,404,{error:"task_not_found"});if(writeSseHeaders(r),writeSse(r,{jsonrpc:"2.0",result:{task:toA2aTask(a)}}),function isTerminalState(e){return"completed"===e||"failed"===e||"cancelled"===e||"canceled"===e||"rejected"===e}(a.summary.state))return void r.end();const s=e.subscribe(e=>{e.requestId===t&&writeSse(r,{jsonrpc:"2.0",result:{event:toA2aEvent(e)}})});r.on("close",s)}(e,s,r),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);if("POST"===t.method&&n)return e.cancel(n,"a2a_cancel"),sendJson(r,200,{task:e.inspectRequest(n)}),!0;const o=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&o){const t=e.inspectRequest(o);return sendJson(r,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}return"GET"===t.method&&"/a2a/tasks"===t.url?(sendJson(r,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(r,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),r),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,r){if("SendMessage"===t.method){const a=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(r,200,jsonRpcResult(t.id,{task:toA2aTask(a)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const a=readString(readRecord(t.params)?.id),s=a?e.inspectRequest(a):void 0;return void sendJson(r,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}if("ListTasks"!==t.method){if("CancelTask"===t.method){const a=readString(readRecord(t.params)?.id);a&&e.cancel(a,"a2a_cancel");const s=a?e.inspectRequest(a):void 0;return void sendJson(r,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}sendJson(r,404,jsonRpcError(t.id,-32601,"method_not_found"))}else sendJson(r,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},r,t.id)}(e,await readJson(t),r),!0)}(t,e,s,r))return;if(a.has("agui")&&await async function handleAgui(e,t,r){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(r,200,{protocol:"ag-ui",transports:["http+sse"],events:["RUN_STARTED","TEXT_MESSAGE_START","TEXT_MESSAGE_CONTENT","TEXT_MESSAGE_END","CUSTOM","RUN_FINISHED","RUN_ERROR"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,r){const a=readRuntimeRequestExtension(t),s=a?.requestId??readString(t.runId)??crypto.randomUUID(),n=a?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);var i;writeSseHeaders(r),writeSse(r,{type:"RUN_STARTED",timestamp:Date.now(),threadId:n,runId:s,input:(i={body:t,input:o,threadId:n,runId:s},{threadId:i.threadId,runId:i.runId,messages:Array.isArray(i.body.messages)?i.body.messages:[{id:`${i.runId}-user`,role:"user",content:i.input}],tools:Array.isArray(i.body.tools)?i.body.tools:[],context:Array.isArray(i.body.context)?i.body.context:[]})});const d=e.subscribe(e=>{e.requestId===s&&writeSse(r,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"CUSTOM",timestamp:Date.now(),name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"TOOL_CALL_START",timestamp:Date.now(),toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"TOOL_CALL_RESULT",timestamp:Date.now(),messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:JSON.stringify(e.output),role:"tool"}:{type:"CUSTOM",timestamp:Date.now(),name:`stable-harness.${e.type}`,value:e}}(e))});try{const i=await e.request(a??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),d=`${s}-message`;i.output&&function writeAguiText(e,t,r){writeSse(e,{type:"TEXT_MESSAGE_START",timestamp:Date.now(),messageId:t,role:"assistant"}),writeSse(e,{type:"TEXT_MESSAGE_CONTENT",timestamp:Date.now(),messageId:t,delta:r}),writeSse(e,{type:"TEXT_MESSAGE_END",timestamp:Date.now(),messageId:t})}(r,d,i.output),writeSse(r,{type:"RUN_FINISHED",timestamp:Date.now(),threadId:n,runId:s,result:i.output})}catch(e){writeSse(r,{type:"RUN_ERROR",timestamp:Date.now(),threadId:n,runId:s,message:errorMessage(e),code:"runtime_error"})}finally{d(),r.end()}}(e,await readJson(t),r),!0)}(t,e,s))return;if(a.has("acp")&&await async function handleAcp(e,t,r){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(r,200,acpInitializeResult()),!0;if("POST"===t.method&&"/acp"===t.url){const a=await readJson(t),s=await handleAcpJsonRpcMessage(e,a);return s?sendJson(r,200,s):r.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const r=readString(readRecord(t.params)?.sessionId);return void(r&&e.listRequests({sessionId:r,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,acpInitializeResult(0,readRecord(t.params)));if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const r=readRecord(t.params)??{},a=readRuntimeRequestExtension(r),s=await e.request(a??{input:readPromptText(r),sessionId:readString(r.sessionId),agentId:readString(r.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,r,a){const s=toA2aRuntimeRequest(t);var n;writeSseHeaders(r),writeSse(r,{jsonrpc:"2.0",id:a,result:{task:(n=s,{id:n.requestId??crypto.randomUUID(),contextId:n.sessionId,status:{state:"submitted",timestamp:(new Date).toISOString()},metadata:{agentId:n.agentId}})}});const o=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(r,{jsonrpc:"2.0",id:a,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(r,{jsonrpc:"2.0",id:a,result:{task:toA2aTask(t),final:!0}})}finally{o(),r.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const r=readRecord(e.message)??e;return{input:readPromptText(r),requestId:readString(e.requestId)??readString(r.messageId),sessionId:readString(e.taskId)??readString(r.taskId)??readString(r.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,r="output"in e?e.output:e.output??"",a="requestId"in e?e.requestId:e.summary.requestId;return{id:a,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:r?{role:"agent",messageId:`${a}-response`,parts:[{kind:"text",text:r}]}:void 0,timestamp:(new Date).toISOString()},artifacts:r?[{artifactId:`${a}-output`,name:"result",parts:[{kind:"text",text:r}]}]:void 0}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(r=e.type,"runtime.request.started"===r?"working":"runtime.request.completed"===r?"completed":"runtime.request.failed"===r?"failed":"runtime.request.cancelled"===r?"canceled":"working"),message:t,timestamp:(new Date).toISOString()},metadata:{stableHarnessEvent:e}};var r}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),r=readString(t?.toolId);return r?{toolId:r,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function acpInitializeResult(e,t){return"number"==typeof t?.protocolVersion&&t.protocolVersion,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},mcpCapabilities:{http:!1,sse:!1},session:{update:!0,cancel:!0,list:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"},authMethods:[]}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const r=(e??"").match(t);return r?.[1]?decodeURIComponent(r[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,r){return{jsonrpc:"2.0",id:e,error:{code:t,message:r}}}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@stable-harness/core": "0.0.
|
|
13
|
+
"@stable-harness/core": "0.0.110"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/workspace-yaml",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.110",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist/**/*.js",
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
".": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"@stable-harness/core": "0.0.
|
|
14
|
+
"@stable-harness/core": "0.0.110"
|
|
15
15
|
}
|
|
16
16
|
}
|