stable-harness 0.0.102 → 0.0.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/docs/guides/index.md +1 -0
- package/docs/guides/integration-guide.md +27 -5
- package/docs/protocols/agent-protocols.md +73 -0
- package/docs/protocols/http-runtime.md +3 -3
- 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 +7 -0
- package/node_modules/@stable-harness/protocols/dist/src/agent-protocols.js +1 -0
- package/node_modules/@stable-harness/protocols/dist/src/index.d.ts +2 -0
- package/node_modules/@stable-harness/protocols/dist/src/index.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 +9 -9
- package/packages/adapter-deepagents/package.json +2 -2
- package/packages/adapter-langgraph/package.json +2 -2
- 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 +7 -0
- package/packages/protocols/dist/src/agent-protocols.js +1 -0
- package/packages/protocols/dist/src/index.d.ts +2 -0
- package/packages/protocols/dist/src/index.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
package/README.md
CHANGED
|
@@ -97,6 +97,9 @@ http://127.0.0.1:8641
|
|
|
97
97
|
http://127.0.0.1:8642/v1
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
ACP, A2A, and AG-UI facades are available when enabled in runtime YAML through
|
|
101
|
+
the combined `agentProtocols` server.
|
|
102
|
+
|
|
100
103
|
For request commands, the CLI is daemon-first. It prints whether it connected to
|
|
101
104
|
the workspace daemon or fell back to an in-process local runtime.
|
|
102
105
|
Console mode uses the same daemon-first behavior and keeps slash commands such
|
|
@@ -236,6 +239,7 @@ This is constrained repair, not silent magic:
|
|
|
236
239
|
- Stable Runtime HTTP + SSE: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
|
|
237
240
|
- OpenAI-compatible facade: [docs/protocols/openai-compatible.md](docs/protocols/openai-compatible.md)
|
|
238
241
|
- LangGraph-compatible facade: [docs/protocols/langgraph-compatible.md](docs/protocols/langgraph-compatible.md)
|
|
242
|
+
- ACP, A2A, and AG-UI facades: [docs/protocols/agent-protocols.md](docs/protocols/agent-protocols.md)
|
|
239
243
|
|
|
240
244
|
## Documentation
|
|
241
245
|
|
package/docs/guides/index.md
CHANGED
|
@@ -40,6 +40,7 @@ embed it, operate it, or explain why it exists.
|
|
|
40
40
|
- [OpenAI-compatible protocol](../protocols/openai-compatible.md)
|
|
41
41
|
- [LangGraph-compatible protocol](../protocols/langgraph-compatible.md)
|
|
42
42
|
- [HTTP runtime protocol](../protocols/http-runtime.md)
|
|
43
|
+
- [ACP, A2A, and AG-UI protocol facades](../protocols/agent-protocols.md)
|
|
43
44
|
|
|
44
45
|
The short version: keep execution semantics upstream-native, define product
|
|
45
46
|
inventory in YAML, and use Stable Harness for lifecycle, governance,
|
|
@@ -105,8 +105,9 @@ stable-harness start -w ./workspace --host 127.0.0.1 --port 8642
|
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
`stable-harness start` exposes the first-party Stable Runtime control plane,
|
|
108
|
-
the OpenAI-compatible facade,
|
|
109
|
-
by runtime config. Point first-party clients
|
|
108
|
+
the OpenAI-compatible facade, the LangGraph-compatible server, and optional
|
|
109
|
+
ACP/A2A/AG-UI facades when enabled by runtime config. Point first-party clients
|
|
110
|
+
at:
|
|
110
111
|
|
|
111
112
|
```text
|
|
112
113
|
http://127.0.0.1:8641
|
|
@@ -130,12 +131,33 @@ artifacts, memory lifecycle, runtime inspection, and real-time control-plane
|
|
|
130
131
|
updates.
|
|
131
132
|
|
|
132
133
|
This is the first-party protocol for Studio and product shells. OpenAI
|
|
133
|
-
compatible `/v1
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
compatible `/v1`, LangGraph-compatible, ACP, A2A, and AG-UI servers are
|
|
135
|
+
ecosystem facades that map to this protocol instead of adding new runtime
|
|
136
|
+
semantics.
|
|
136
137
|
|
|
137
138
|
See [Stable Runtime HTTP + SSE](../protocols/http-runtime.md).
|
|
138
139
|
|
|
140
|
+
## ACP, A2A, and AG-UI Facades
|
|
141
|
+
|
|
142
|
+
Enable the combined agent protocol server when an external client already
|
|
143
|
+
speaks one of these protocols:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
apiVersion: stable-harness.dev/v1
|
|
147
|
+
kind: Runtime
|
|
148
|
+
spec:
|
|
149
|
+
protocols:
|
|
150
|
+
agentProtocols:
|
|
151
|
+
enabled: true
|
|
152
|
+
host: 127.0.0.1
|
|
153
|
+
port: 8650
|
|
154
|
+
protocols: [acp, a2a, agui]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The server exposes A2A over HTTP+JSON/SSE, AG-UI over HTTP+SSE, and ACP over a
|
|
158
|
+
documented HTTP JSON-RPC custom transport. See
|
|
159
|
+
[ACP, A2A, and AG-UI Protocol Facades](../protocols/agent-protocols.md).
|
|
160
|
+
|
|
139
161
|
## Backend Adapters
|
|
140
162
|
|
|
141
163
|
Adapters translate Stable Harness runtime requests into upstream backend calls.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ACP, A2A, and AG-UI Protocol Facades
|
|
2
|
+
|
|
3
|
+
Stable Harness exposes ACP, A2A, and AG-UI as protocol adapters over the native
|
|
4
|
+
Stable Runtime request, event, and inspection surfaces. These adapters must not
|
|
5
|
+
add routing heuristics, backend execution semantics, or workspace-specific
|
|
6
|
+
behavior.
|
|
7
|
+
|
|
8
|
+
Enable the combined protocol server from runtime YAML:
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
apiVersion: stable-harness.dev/v1
|
|
12
|
+
kind: Runtime
|
|
13
|
+
spec:
|
|
14
|
+
protocols:
|
|
15
|
+
agentProtocols:
|
|
16
|
+
enabled: true
|
|
17
|
+
host: 127.0.0.1
|
|
18
|
+
port: 8650
|
|
19
|
+
protocols: [acp, a2a, agui]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You can also enable a single protocol by configuring one of `acp`, `a2a`, or
|
|
23
|
+
`agui` with `enabled: true`. The CLI starts one combined HTTP server for the
|
|
24
|
+
enabled adapter set.
|
|
25
|
+
|
|
26
|
+
## A2A
|
|
27
|
+
|
|
28
|
+
A2A is exposed as an HTTP+JSON and SSE facade:
|
|
29
|
+
|
|
30
|
+
- `GET /.well-known/agent-card.json`
|
|
31
|
+
- `GET /a2a/agent-card.json`
|
|
32
|
+
- `POST /a2a/message:send`
|
|
33
|
+
- `POST /a2a/message:stream`
|
|
34
|
+
- `POST /a2a/rpc`
|
|
35
|
+
- `GET /a2a/tasks`
|
|
36
|
+
- `GET /a2a/tasks/:id`
|
|
37
|
+
- `POST /a2a/tasks/:id:cancel`
|
|
38
|
+
|
|
39
|
+
`message:send` and JSON-RPC `SendMessage` map to a native `RuntimeRequest`.
|
|
40
|
+
Streaming responses subscribe to native runtime events and project them as A2A
|
|
41
|
+
status updates.
|
|
42
|
+
|
|
43
|
+
## AG-UI
|
|
44
|
+
|
|
45
|
+
AG-UI is exposed as an HTTP+SSE event stream:
|
|
46
|
+
|
|
47
|
+
- `GET /ag-ui/capabilities`
|
|
48
|
+
- `POST /ag-ui/runs`
|
|
49
|
+
|
|
50
|
+
`POST /ag-ui/runs` emits `RunStarted`, protocol `Custom` events for Stable
|
|
51
|
+
Runtime events, `TextMessageChunk` for final assistant output, and `RunFinished`
|
|
52
|
+
or `RunError`.
|
|
53
|
+
|
|
54
|
+
## ACP
|
|
55
|
+
|
|
56
|
+
ACP's standard transport is JSON-RPC over stdio. Stable Harness currently
|
|
57
|
+
exposes ACP over an HTTP JSON-RPC endpoint as a documented custom transport:
|
|
58
|
+
|
|
59
|
+
- `GET /acp/capabilities`
|
|
60
|
+
- `POST /acp`
|
|
61
|
+
|
|
62
|
+
Supported methods:
|
|
63
|
+
|
|
64
|
+
- `initialize`
|
|
65
|
+
- `session/new`
|
|
66
|
+
- `session/load`
|
|
67
|
+
- `session/list`
|
|
68
|
+
- `session/prompt`
|
|
69
|
+
- `session/cancel`
|
|
70
|
+
|
|
71
|
+
`session/prompt` maps to a native `RuntimeRequest` and returns the ACP stop
|
|
72
|
+
reason plus Stable Harness request metadata. A future stdio entrypoint can reuse
|
|
73
|
+
the same JSON-RPC method handler without changing runtime semantics.
|
|
@@ -4,9 +4,9 @@ The native Stable Runtime protocol is the product control-plane contract. It is
|
|
|
4
4
|
the source of truth for first-party clients such as Studio, operator dashboards,
|
|
5
5
|
and embedded product shells.
|
|
6
6
|
|
|
7
|
-
OpenAI-compatible `/v1
|
|
8
|
-
facades.
|
|
9
|
-
|
|
7
|
+
OpenAI-compatible `/v1`, LangGraph-compatible, ACP, A2A, and AG-UI servers are
|
|
8
|
+
ecosystem facades. They adapt external protocols to this contract rather than
|
|
9
|
+
adding new runtime semantics.
|
|
10
10
|
|
|
11
11
|
## HTTP Commands And Queries
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103",
|
|
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.103",
|
|
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.103"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103",
|
|
15
|
+
"@stable-harness/memory": "0.0.103"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
|
+
import type { StableHarnessRuntime } from "@stable-harness/core";
|
|
3
|
+
export type AgentProtocolServerOptions = {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
enabledProtocols?: Array<"acp" | "a2a" | "agui">;
|
|
6
|
+
};
|
|
7
|
+
export declare function createAgentProtocolHttpServer(runtime: StableHarnessRuntime, options?: AgentProtocolServerOptions): import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,s={}){const a=new Set(s.enabledProtocols??["acp","a2a","agui"]);return e(async(e,n)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(n,200,{ok:!0,protocols:[...a]});if(a.has("a2a")&&await async function handleA2a(e,t,s,a){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(s,200,function createAgentCard(e,t){const s=e.inspect();return{protocolVersion:"1.0",name:s.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:s.agents.map(e=>({id:e,name:e,description:e}))}}(e,a)),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&n){const t=e.inspectRequest(n);return sendJson(s,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(s,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const r=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&r?(e.cancel(r,"a2a_cancel"),sendJson(s,200,{task:e.inspectRequest(r)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(s,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),s),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,s){if("SendMessage"===t.method){const a=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(s,200,jsonRpcResult(t.id,{task:toA2aTask(a)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const a=readString(readRecord(t.params)?.id),n=a?e.inspectRequest(a):void 0;return void sendJson(s,n?200:404,n?jsonRpcResult(t.id,toA2aTask(n)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(s,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(s,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},s,t.id)}(e,await readJson(t),s),!0)}(t,e,n,s))return;if(a.has("agui")&&await async function handleAgui(e,t,s){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(s,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,s){const a=readString(t.runId)??crypto.randomUUID(),n=readString(t.threadId)??readString(t.sessionId)??`thread-${a}`,r=readPromptText(t);writeSseHeaders(s),writeSse(s,{type:"RunStarted",threadId:n,runId:a,input:{messages:t.messages,input:r}});const o=e.subscribe(e=>{e.requestId===a&&writeSse(s,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 o=await e.request({input:r,requestId:a,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${a}-message`;o.output&&writeSse(s,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:o.output}),writeSse(s,{type:"RunFinished",threadId:n,runId:a,outcome:{type:"completed"===o.state?"success":"interrupt"},result:o.output})}catch(e){writeSse(s,{type:"RunError",threadId:n,runId:a,message:errorMessage(e)})}finally{o(),s.end()}}(e,await readJson(t),s),!0)}(t,e,n))return;if(a.has("acp")&&await async function handleAcp(e,t,s){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(s,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 a=await readJson(t),n=await async function handleAcpMessage(e,t){if(!t.id&&"session/cancel"===t.method){const s=readString(readRecord(t.params)?.sessionId);return void(s&&e.listRequests({sessionId:s,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 s=readRecord(t.params)??{},a=await e.request({input:readPromptText(s),sessionId:readString(s.sessionId),agentId:readString(s.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===a.state?"end_turn":"refusal",_meta:{requestId:a.requestId,output:a.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}(e,a);return n?sendJson(s,200,n):s.writeHead(204).end(),!0}return!1}(t,e,n))return;sendJson(n,404,{error:"not_found"})}catch(e){sendJson(n,400,{error:errorMessage(e)})}})}async function streamA2a(e,t,s,a){const n=toA2aRuntimeRequest(t);writeSseHeaders(s);const r=e.subscribe(e=>{e.requestId===n.requestId&&writeSse(s,{jsonrpc:"2.0",id:a,result:{event:toA2aEvent(e)}})});try{const t=await e.request(n);writeSse(s,{jsonrpc:"2.0",id:a,result:{task:toA2aTask(t),final:!0}})}finally{r(),s.end()}}function toA2aRuntimeRequest(e){const t=readRecord(e.message)??e;return{input:readPromptText(t),requestId:readString(e.requestId)??readString(t.messageId),sessionId:readString(e.taskId)??readString(t.taskId)??readString(t.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,s="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:s?{role:"agent",messageId:`${a}-response`,parts:[{kind:"text",text:s}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:e.type},metadata:e}}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 s=(e??"").match(t);return s?.[1]?decodeURIComponent(s[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,s){return{jsonrpc:"2.0",id:e,error:{code:t,message:s}}}function sendJson(e,t,s){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(s))}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 s of e)t.push(Buffer.isBuffer(s)?s:Buffer.from(s));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createInProcessClient } from "./in-process-client.js";
|
|
2
|
+
export { createAgentProtocolHttpServer } from "./agent-protocols.js";
|
|
3
|
+
export type { AgentProtocolServerOptions } from "./agent-protocols.js";
|
|
2
4
|
export { createHttpServer } from "./http-server.js";
|
|
3
5
|
export { createOpenAiCompatibleHttpServer } from "./openai-compatible.js";
|
|
4
6
|
export type { OpenAiCompatibleServerOptions } from "./openai-compatible.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createInProcessClient}from"./in-process-client.js";export{createHttpServer}from"./http-server.js";export{createOpenAiCompatibleHttpServer}from"./openai-compatible.js";
|
|
1
|
+
export{createInProcessClient}from"./in-process-client.js";export{createAgentProtocolHttpServer}from"./agent-protocols.js";export{createHttpServer}from"./http-server.js";export{createOpenAiCompatibleHttpServer}from"./openai-compatible.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103"
|
|
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.103",
|
|
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.103"
|
|
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.103",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -82,14 +82,14 @@
|
|
|
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.103",
|
|
86
|
+
"@stable-harness/adapter-langgraph": "0.0.103",
|
|
87
|
+
"@stable-harness/core": "0.0.103",
|
|
88
|
+
"@stable-harness/governance": "0.0.103",
|
|
89
|
+
"@stable-harness/memory": "0.0.103",
|
|
90
|
+
"@stable-harness/protocols": "0.0.103",
|
|
91
|
+
"@stable-harness/tool-gateway": "0.0.103",
|
|
92
|
+
"@stable-harness/workspace-yaml": "0.0.103",
|
|
93
93
|
"deepagents": "^1.10.1",
|
|
94
94
|
"langchain": "^1.4.0",
|
|
95
95
|
"yaml": "^2.8.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103",
|
|
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.103",
|
|
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.103"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createHttpServer as e,createOpenAiCompatibleHttpServer as n}from"@stable-harness/protocols";import{startOfficialLangGraphServer as
|
|
1
|
+
import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createAgentProtocolHttpServer as o,createHttpServer as e,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 o=createConfiguredServers(t,r),e=[];let n=0;for(const r of o)if("http"===r.kind){if(!await listen(r)){process.stdout.write(`stable-harness ${r.protocol} API already running on ${serverUrl(r)}\n`);continue}e.push(()=>closeHttpServer(r.server)),n+=1;const t=r.server.address(),o="object"==typeof t&&t?t.port:r.port;process.stdout.write(`stable-harness ${r.protocol} API listening on ${serverUrl({...r,port:o})}\n`)}else{const o=await startLangGraphServer(t,r);if(!o){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.config.host}:${r.config.port}\n`);continue}e.push(o.cleanup),n+=1,process.stdout.write(`stable-harness ${r.protocol} API listening on ${o.url}\n`)}0!==n&&await async function waitForShutdown(t){const r=setInterval(()=>{},864e5);await new Promise(o=>{const shutdown=()=>{clearInterval(r),Promise.allSettled(t.map(t=>t())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(e)}export async function stopProtocol(t,r){const o=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}),e=await Promise.all(o.map(async t=>({target:t,pids:await stableHarnessListenerPids(t.port)}))),n=[...new Set(e.flatMap(t=>t.pids))];for(const t of n)process.kill(t,"SIGTERM");for(const{target:t,pids:r}of e)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 o=readRecord(t.getRuntimePolicy().protocols)??{},e=protocolConfig(o,"stableRuntime","stable-runtime","http")??{},n=protocolConfig(o,"openaiCompatible","openai-compatible","openai")??{},s=function agentProtocolConfig(t){const r=protocolConfig(t,"agentProtocols","agent-protocols");if(r)return enabled(r)?r:void 0;const o=["acp","a2a","agui"].filter(r=>enabled(protocolConfig(t,r)??{enabled:!1}));return o.length>0?{protocols:o}:void 0}(o),i=protocolConfig(o,"langgraph")??{};return[...enabled(e)?[stableRuntimeServer(t,e,r)]:[],...enabled(n)?[openAiServer(t,n,r)]:[],...s?[agentProtocolServer(t,s,r)]:[],...enabled(i)?[langGraphServer(i)]:[]]}function stableRuntimeServer(t,r,o){return{kind:"http",protocol:"stable-runtime",server:e(t),host:o.host??configString(r.host)??i,port:configNumber(r.port)??configNumber(process.env.STABLE_HARNESS_RUNTIME_PORT)??8641}}function openAiServer(t,r,o){const e=configString(r.host)??i,s=o.port??configNumber(r.port)??8642,a=o.host??e,c=configString(r.bearerToken)??configString(r.apiKey)??o.apiKey;return{kind:"http",protocol:"openai-compatible",server:n(t,{bearerToken:c}),host:a,port:s,...c?{bearerToken:c}:{}}}function agentProtocolServer(t,r,e){const n=configString(r.host)??i,s=configNumber(r.port)??8650,a=e.host??n;return{kind:"http",protocol:"agent-protocols",server:o(t,{baseUrl:`http://${a}:${s}`,enabledProtocols:configProtocolIds(r.protocols)}),host:a,port:s}}function langGraphServer(t){const r=configString(t.host)??i,o=configNumber(t.port)??2024,e=configStringArray(t.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:r,port:o,nWorkers:configNumber(t.nWorkers)??10,...e?{exposeAgents:e}:{},...void 0!==t.env?{env:t.env}:{},...void 0!==t.envFile?{envFile:t.envFile}:{}}}}function protocolConfig(t,...r){for(const o of r){const r=readRecord(t[o]);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,o)=>{t.server.once("error",o),t.server.listen(t.port,t.host,()=>{t.server.off("error",o),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 o=await fetch(t,{headers:r});if(!o.ok)return;return await o.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,o,e){return isAddressInUse(t)?new Error([`stable-harness ${r} port is already in use: ${o}:${e}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${o}:${e}.`].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 o,e="";for(const n of t)'"'!==n&&"'"!==n||void 0!==o?n!==o?/\s/u.test(n)&&void 0===o?e&&(r.push(e),e=""):e+=n:o=void 0:o=n;return e&&r.push(e),r}(t),o=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 o>=0&&r.slice(o+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,o)=>{t.close(t=>{t?o(t):r()})})}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103",
|
|
18
|
+
"@stable-harness/adapter-langgraph": "0.0.103",
|
|
19
|
+
"@stable-harness/core": "0.0.103",
|
|
20
|
+
"@stable-harness/memory": "0.0.103",
|
|
21
|
+
"@stable-harness/protocols": "0.0.103",
|
|
22
|
+
"@stable-harness/tool-gateway": "0.0.103",
|
|
23
|
+
"@stable-harness/workspace-yaml": "0.0.103"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103",
|
|
15
|
+
"@stable-harness/memory": "0.0.103"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/evaluation",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
|
+
import type { StableHarnessRuntime } from "@stable-harness/core";
|
|
3
|
+
export type AgentProtocolServerOptions = {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
enabledProtocols?: Array<"acp" | "a2a" | "agui">;
|
|
6
|
+
};
|
|
7
|
+
export declare function createAgentProtocolHttpServer(runtime: StableHarnessRuntime, options?: AgentProtocolServerOptions): import("node:http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,s={}){const a=new Set(s.enabledProtocols??["acp","a2a","agui"]);return e(async(e,n)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(n,200,{ok:!0,protocols:[...a]});if(a.has("a2a")&&await async function handleA2a(e,t,s,a){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(s,200,function createAgentCard(e,t){const s=e.inspect();return{protocolVersion:"1.0",name:s.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:s.agents.map(e=>({id:e,name:e,description:e}))}}(e,a)),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&n){const t=e.inspectRequest(n);return sendJson(s,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(s,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const r=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&r?(e.cancel(r,"a2a_cancel"),sendJson(s,200,{task:e.inspectRequest(r)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(s,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),s),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,s){if("SendMessage"===t.method){const a=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(s,200,jsonRpcResult(t.id,{task:toA2aTask(a)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const a=readString(readRecord(t.params)?.id),n=a?e.inspectRequest(a):void 0;return void sendJson(s,n?200:404,n?jsonRpcResult(t.id,toA2aTask(n)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(s,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(s,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},s,t.id)}(e,await readJson(t),s),!0)}(t,e,n,s))return;if(a.has("agui")&&await async function handleAgui(e,t,s){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(s,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,s){const a=readString(t.runId)??crypto.randomUUID(),n=readString(t.threadId)??readString(t.sessionId)??`thread-${a}`,r=readPromptText(t);writeSseHeaders(s),writeSse(s,{type:"RunStarted",threadId:n,runId:a,input:{messages:t.messages,input:r}});const o=e.subscribe(e=>{e.requestId===a&&writeSse(s,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 o=await e.request({input:r,requestId:a,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${a}-message`;o.output&&writeSse(s,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:o.output}),writeSse(s,{type:"RunFinished",threadId:n,runId:a,outcome:{type:"completed"===o.state?"success":"interrupt"},result:o.output})}catch(e){writeSse(s,{type:"RunError",threadId:n,runId:a,message:errorMessage(e)})}finally{o(),s.end()}}(e,await readJson(t),s),!0)}(t,e,n))return;if(a.has("acp")&&await async function handleAcp(e,t,s){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(s,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 a=await readJson(t),n=await async function handleAcpMessage(e,t){if(!t.id&&"session/cancel"===t.method){const s=readString(readRecord(t.params)?.sessionId);return void(s&&e.listRequests({sessionId:s,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 s=readRecord(t.params)??{},a=await e.request({input:readPromptText(s),sessionId:readString(s.sessionId),agentId:readString(s.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===a.state?"end_turn":"refusal",_meta:{requestId:a.requestId,output:a.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}(e,a);return n?sendJson(s,200,n):s.writeHead(204).end(),!0}return!1}(t,e,n))return;sendJson(n,404,{error:"not_found"})}catch(e){sendJson(n,400,{error:errorMessage(e)})}})}async function streamA2a(e,t,s,a){const n=toA2aRuntimeRequest(t);writeSseHeaders(s);const r=e.subscribe(e=>{e.requestId===n.requestId&&writeSse(s,{jsonrpc:"2.0",id:a,result:{event:toA2aEvent(e)}})});try{const t=await e.request(n);writeSse(s,{jsonrpc:"2.0",id:a,result:{task:toA2aTask(t),final:!0}})}finally{r(),s.end()}}function toA2aRuntimeRequest(e){const t=readRecord(e.message)??e;return{input:readPromptText(t),requestId:readString(e.requestId)??readString(t.messageId),sessionId:readString(e.taskId)??readString(t.taskId)??readString(t.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,s="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:s?{role:"agent",messageId:`${a}-response`,parts:[{kind:"text",text:s}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:e.type},metadata:e}}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 s=(e??"").match(t);return s?.[1]?decodeURIComponent(s[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,s){return{jsonrpc:"2.0",id:e,error:{code:t,message:s}}}function sendJson(e,t,s){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(s))}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 s of e)t.push(Buffer.isBuffer(s)?s:Buffer.from(s));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createInProcessClient } from "./in-process-client.js";
|
|
2
|
+
export { createAgentProtocolHttpServer } from "./agent-protocols.js";
|
|
3
|
+
export type { AgentProtocolServerOptions } from "./agent-protocols.js";
|
|
2
4
|
export { createHttpServer } from "./http-server.js";
|
|
3
5
|
export { createOpenAiCompatibleHttpServer } from "./openai-compatible.js";
|
|
4
6
|
export type { OpenAiCompatibleServerOptions } from "./openai-compatible.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createInProcessClient}from"./in-process-client.js";export{createHttpServer}from"./http-server.js";export{createOpenAiCompatibleHttpServer}from"./openai-compatible.js";
|
|
1
|
+
export{createInProcessClient}from"./in-process-client.js";export{createAgentProtocolHttpServer}from"./agent-protocols.js";export{createHttpServer}from"./http-server.js";export{createOpenAiCompatibleHttpServer}from"./openai-compatible.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
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.103"
|
|
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.103",
|
|
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.103"
|
|
15
15
|
}
|
|
16
16
|
}
|