stable-harness 0.0.104 → 0.0.106
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 +14 -0
- package/docs/guides/integration-guide.md +24 -2
- package/docs/protocols/agent-protocols.md +11 -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/dist/runtime/direct-tool-call.js +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/recovery/adapter-result.js +1 -1
- 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.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/args.d.ts +2 -0
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/console/session.js +1 -1
- package/packages/cli/dist/src/daemon/client.d.ts +3 -1
- package/packages/cli/dist/src/daemon/client.js +1 -1
- package/packages/cli/package.json +8 -8
- package/packages/core/dist/runtime/direct-tool-call.js +1 -1
- package/packages/core/dist/runtime/recovery/adapter-result.js +1 -1
- 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.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
|
@@ -114,6 +114,20 @@ and `local` always runs an in-process runtime. The CLI prints the selected path
|
|
|
114
114
|
to stderr. Console mode keeps slash commands such as `/session`, `/sessions`,
|
|
115
115
|
`/memory`, `/health`, `/debug`, and `/clear` in the active session.
|
|
116
116
|
|
|
117
|
+
When a protocol facade is enabled, the same CLI can also choose the client
|
|
118
|
+
protocol:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
stable-harness -w ./examples/minimal-deepagents --protocol stable-runtime --tool echo_tool --tool-args-json '{"value":"native"}'
|
|
122
|
+
stable-harness -w ./examples/minimal-deepagents --protocol a2a --protocol-url http://127.0.0.1:8650 --tool echo_tool --tool-args-json '{"value":"a2a"}'
|
|
123
|
+
stable-harness -w ./examples/minimal-deepagents --protocol agui --protocol-url http://127.0.0.1:8650 --tool echo_tool --tool-args-json '{"value":"agui"}'
|
|
124
|
+
stable-harness -w ./examples/minimal-deepagents --protocol acp --protocol-url http://127.0.0.1:8650 --tool echo_tool --tool-args-json '{"value":"acp"}'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`stable-runtime` exposes the full operator control plane. A2A, AG-UI, and ACP
|
|
128
|
+
map request turns through their protocol facades; use Stable Runtime HTTP + SSE
|
|
129
|
+
for traces, approvals, memory administration, and full session inspection.
|
|
130
|
+
|
|
117
131
|
Build a portable Docker runtime artifact for the workspace:
|
|
118
132
|
|
|
119
133
|
```bash
|
|
@@ -43,6 +43,28 @@ stable-harness console -w ./workspace --runtime daemon
|
|
|
43
43
|
| `daemon` | Require a matching Stable Runtime daemon and fail clearly if unavailable. |
|
|
44
44
|
| `local` | Always create an in-process runtime for this CLI process. |
|
|
45
45
|
|
|
46
|
+
Clients can also choose the wire protocol used for daemon/server requests:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
stable-harness -w ./workspace --protocol stable-runtime "Review the release evidence."
|
|
50
|
+
stable-harness -w ./workspace --protocol a2a --protocol-url http://127.0.0.1:8650 "Review the release evidence."
|
|
51
|
+
stable-harness -w ./workspace --protocol agui --protocol-url http://127.0.0.1:8650 "Review the release evidence."
|
|
52
|
+
stable-harness -w ./workspace --protocol acp --protocol-url http://127.0.0.1:8650 "Review the release evidence."
|
|
53
|
+
stable-harness console -w ./workspace --protocol a2a --protocol-url http://127.0.0.1:8650
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Protocol | Behavior |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `stable-runtime` | Native Stable Runtime HTTP + SSE with request/session/trace/operator features. |
|
|
59
|
+
| `a2a` | Sends request turns through the A2A facade. |
|
|
60
|
+
| `agui` | Sends request turns through the AG-UI HTTP+SSE facade. |
|
|
61
|
+
| `acp` | Sends request turns through the ACP HTTP JSON-RPC custom transport. |
|
|
62
|
+
|
|
63
|
+
`--protocol-url` overrides the configured agent protocol server. A2A, AG-UI,
|
|
64
|
+
and ACP carry explicit Stable Runtime request extensions for CLI parity, but
|
|
65
|
+
they do not replace the Stable Runtime operator API. Use `stable-runtime` for
|
|
66
|
+
traces, approvals, memory administration, and full session inspection.
|
|
67
|
+
|
|
46
68
|
The CLI prints the selected runtime path to stderr:
|
|
47
69
|
|
|
48
70
|
- `connected to daemon` means the request is handled by the workspace daemon.
|
|
@@ -158,8 +180,8 @@ See [Stable Runtime HTTP + SSE](../protocols/http-runtime.md).
|
|
|
158
180
|
|
|
159
181
|
| Client kind | Recommended connection | When to use it |
|
|
160
182
|
| --- | --- | --- |
|
|
161
|
-
| CLI one-shot | `--runtime
|
|
162
|
-
| CLI console | Same
|
|
183
|
+
| CLI one-shot | `--runtime ...` plus `--protocol ...` | Use runtime mode for process selection and protocol mode for wire compatibility. |
|
|
184
|
+
| CLI console | Same runtime and protocol options as one-shot CLI | Use `stable-runtime` for full slash-command operator features; use A2A/AG-UI/ACP for protocol turn testing. |
|
|
163
185
|
| Embedded app | SDK / in-process runtime | Use when the application owns the process lifecycle. |
|
|
164
186
|
| First-party UI | Stable Runtime HTTP + SSE | Use for sessions, request inspection, approvals, artifacts, memory, traces, and cross-client realtime events. |
|
|
165
187
|
| OpenAI clients | OpenAI-compatible `/v1` | Use for existing chat-completion clients and evaluation harnesses. |
|
|
@@ -40,6 +40,11 @@ A2A is exposed as an HTTP+JSON and SSE facade:
|
|
|
40
40
|
Streaming responses subscribe to native runtime events and project them as A2A
|
|
41
41
|
status updates.
|
|
42
42
|
|
|
43
|
+
Stable Harness clients may include a `runtimeRequest` extension field when they
|
|
44
|
+
need explicit runtime fields such as `toolCall`, `workflow`, `agentId`, or
|
|
45
|
+
`sessionId`. External A2A clients can ignore that extension and use normal A2A
|
|
46
|
+
message parts.
|
|
47
|
+
|
|
43
48
|
## AG-UI
|
|
44
49
|
|
|
45
50
|
AG-UI is exposed as an HTTP+SSE event stream:
|
|
@@ -51,6 +56,9 @@ AG-UI is exposed as an HTTP+SSE event stream:
|
|
|
51
56
|
Runtime events, `TextMessageChunk` for final assistant output, and `RunFinished`
|
|
52
57
|
or `RunError`.
|
|
53
58
|
|
|
59
|
+
Stable Harness clients may include `runtimeRequest` in the run body to preserve
|
|
60
|
+
explicit runtime fields while still using the AG-UI event stream.
|
|
61
|
+
|
|
54
62
|
## ACP
|
|
55
63
|
|
|
56
64
|
ACP's standard transport is JSON-RPC over stdio. Stable Harness currently
|
|
@@ -71,3 +79,6 @@ Supported methods:
|
|
|
71
79
|
`session/prompt` maps to a native `RuntimeRequest` and returns the ACP stop
|
|
72
80
|
reason plus Stable Harness request metadata. A future stdio entrypoint can reuse
|
|
73
81
|
the same JSON-RPC method handler without changing runtime semantics.
|
|
82
|
+
|
|
83
|
+
Stable Harness clients may include `params.runtimeRequest` to carry explicit
|
|
84
|
+
runtime fields through the ACP-shaped transport.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/adapter-deepagents",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106",
|
|
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.106",
|
|
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.106"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{evaluateToolGuardrails as t}from"./policy/tool-invocation.js";import{toolCircuitOpenEvent as o,toolFailureEvent as e}from"./tool-failure.js";export async function runDirectToolCall(o){const e=o.request.toolCall;if(!e)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const r=await async function resolveDirectToolCall(t){if(t.agent.tools.includes(t.toolId)&&t.gateway.get(t.toolId))return{toolId:t.toolId,args:t.args};const o=await(t.gateway.repairToolCall?.({toolId:t.toolId,args:t.args,allowedToolIds:t.agent.tools,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}}));if(o&&t.agent.tools.includes(o.toolId)&&t.gateway.get(o.toolId))return emitToolRepair(t,"repaired",o.toolId),o;if(!t.agent.tools.includes(t.toolId))throw emitToolRepair(t,"blocked",void 0,`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`),new Error(`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`);throw emitToolRepair(t,"blocked",void 0,`Tool is not registered: ${t.toolId}`),new Error(`Tool is not registered: ${t.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:e.toolId,args:e.args});o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId});const s=t({agent:o.agent,args:r.args,events:o.events??[],toolId:r.toolId},o.toolGuardrails);if(s)return o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:s.eventOutput}),{text:s.modelOutput,metadata:{toolCall:{toolId:r.toolId},controlStatus:s.status}};if(o.toolFailureTracker?.isCircuitOpen(r.toolId)){const t=new Error(`Tool circuit is open: ${r.toolId}`);throw emitToolFailure(o,r.toolId,t),t}const a=await async function invokeToolWithFailureEvents(t,o){try{return await t.gateway.invoke({toolId:o.toolId,args:o.args,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}})}catch(e){throw emitToolFailure(t,o.toolId,e),e}}(o,r);return o.toolFailureTracker?.recordSuccess(a.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:a.toolId,output:a.output}),{text:(
|
|
1
|
+
import{evaluateToolGuardrails as t}from"./policy/tool-invocation.js";import{toolCircuitOpenEvent as o,toolFailureEvent as e}from"./tool-failure.js";export async function runDirectToolCall(o){const e=o.request.toolCall;if(!e)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const r=await async function resolveDirectToolCall(t){if(t.agent.tools.includes(t.toolId)&&t.gateway.get(t.toolId))return{toolId:t.toolId,args:t.args};const o=await(t.gateway.repairToolCall?.({toolId:t.toolId,args:t.args,allowedToolIds:t.agent.tools,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}}));if(o&&t.agent.tools.includes(o.toolId)&&t.gateway.get(o.toolId))return emitToolRepair(t,"repaired",o.toolId),o;if(!t.agent.tools.includes(t.toolId))throw emitToolRepair(t,"blocked",void 0,`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`),new Error(`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`);throw emitToolRepair(t,"blocked",void 0,`Tool is not registered: ${t.toolId}`),new Error(`Tool is not registered: ${t.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:e.toolId,args:e.args});o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId});const s=t({agent:o.agent,args:r.args,events:o.events??[],toolId:r.toolId},o.toolGuardrails);if(s)return o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:s.eventOutput}),{text:s.modelOutput,metadata:{toolCall:{toolId:r.toolId},controlStatus:s.status}};if(o.toolFailureTracker?.isCircuitOpen(r.toolId)){const t=new Error(`Tool circuit is open: ${r.toolId}`);throw emitToolFailure(o,r.toolId,t),t}const a=await async function invokeToolWithFailureEvents(t,o){try{return await t.gateway.invoke({toolId:o.toolId,args:o.args,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}})}catch(e){throw emitToolFailure(t,o.toolId,e),e}}(o,r);return o.toolFailureTracker?.recordSuccess(a.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:a.toolId,output:a.output}),{text:stringifyToolOutput(a.output),metadata:{toolCall:{toolId:a.toolId}}}}function emitToolFailure(t,r,s){const a=e({requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:r,error:s});t.emit(a),t.toolFailureTracker?.recordFailure(r)&&t.emit(o({requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:r,reason:"runtime.tool.failure"===a.type?a.failure.reason:"unknown"}))}function emitToolRepair(t,o,e,r){t.emit({type:"runtime.inventory.repair",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,status:o,diagnostic:{layer:"tool",owner:"stable_runtime_policy",originalId:t.toolId,repairedId:e,candidateIds:t.agent.tools,reason:r}})}function stringifyToolOutput(t){if("string"==typeof t)return t;const o=JSON.stringify(t);return"string"==typeof o?o:""}function readApprovalIds(t){const o=t?.approvalIds??t?.approvalId;return"string"==typeof o&&o.trim()?[o.trim()]:Array.isArray(o)?o.filter(t=>"string"==typeof t&&t.trim().length>0):void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{buildRawArgsRecoveryRequest as t,buildRawArgsToolEvidenceRecoveryRequest as o,hasUniqueRawArgsTool as r,matchUniqueRawArgsTool as a}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as s,assertNoRawToolCallOutput as l,assertNoRawToolResultOutput as
|
|
1
|
+
import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{buildRawArgsRecoveryRequest as t,buildRawArgsToolEvidenceRecoveryRequest as o,hasUniqueRawArgsTool as r,matchUniqueRawArgsTool as a}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as s,assertNoRawToolCallOutput as l,assertNoRawToolResultOutput as u,assertNoToolExecutionErrorOutput as i,buildEvidenceSynthesisOutput as n,buildResultRecoveryRequest as c,containsRawToolCallOutput as p,rawToolCallFailureMessage as d,rawToolCallOutputPreview as y,toolCallRecoveryEnabled as v}from"../../recovery/tool-call.js";import{controlGaps as g}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(o){let g=o.result,m=o.request;const w=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(o.recoveryPolicy),R=new Set,_=new Set;for(let e=0;;e+=1){const r=o.store.getRun(o.requestId)?.events??[],s=a({output:g.text,agent:o.agent,workspace:o.workspace,toolGateway:o.toolGateway,events:r,policy:o.recoveryPolicy}),l=s?rawArgsToolKey(s):void 0,u=l&&!R.has(l)?t({request:m,output:g.text,agent:o.agent,workspace:o.workspace,toolGateway:o.toolGateway,events:r,policy:o.recoveryPolicy}):void 0,i=e<w?c({request:m,output:g.text,events:r,availableToolIds:o.agent.tools,policy:o.recoveryPolicy}):void 0,n=u??i;if(!n)break;if(n===u&&R.add(l),m=n,emitRepair(o,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(g.text,o.agent.tools)),g=await o.runAdapter(n),emitRepair(o,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(g.text,o.agent.tools)),n===u){const t=await recoverRepeatedRawArgsByToolGateway(o,m,g,R,_,e+1);t&&(m=t.request,g=t.result)}}return function finalizeRecoveredOutput(t,o){if(!v(t.recoveryPolicy))return o;let a=!1;if(p(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:d(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const c=n({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return c&&(a=!0,o={...o,text:c,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),a||(p(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),l(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(r({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${y(t)}`)}(t,o.text),s(o.text,t.agent.tools,t.recoveryPolicy),u(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),i(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(o,g)}async function recoverRepeatedRawArgsByToolGateway(e,t,r,s,l,u){const i=a({output:r.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],policy:e.recoveryPolicy});if(!i||!e.runRecoveredToolCall)return;const n=rawArgsToolKey(i);if(!s.has(n)||l.has(n))return;l.add(n),emitRepair(e,"runtime.repair.started","result_output",u,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(r.text,e.agent.tools));const c=function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}(await e.runRecoveredToolCall(i.toolId,i.args)),p=o({request:t,match:i,toolOutput:c}),d=await e.runAdapter(p),y=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolBlocker({match:i,toolOutput:c,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",u,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(y.text,e.agent.tools)),{request:p,result:y}}function emitRepair(e,t,o,r,a,s,l){const u={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...l?{diagnostics:l}:{}};e.emit("runtime.repair.started"===t?{type:t,...u}:{type:t,...u,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:y(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function buildEmptyRecoveredToolBlocker(e){const t=g(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106",
|
|
15
|
+
"@stable-harness/memory": "0.0.106"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,
|
|
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(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 async function handleAcpMessage(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")}(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)})}})}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){return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:e.type},metadata:e}}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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106"
|
|
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.106",
|
|
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.106"
|
|
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.106",
|
|
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.106",
|
|
86
|
+
"@stable-harness/adapter-langgraph": "0.0.106",
|
|
87
|
+
"@stable-harness/core": "0.0.106",
|
|
88
|
+
"@stable-harness/governance": "0.0.106",
|
|
89
|
+
"@stable-harness/memory": "0.0.106",
|
|
90
|
+
"@stable-harness/protocols": "0.0.106",
|
|
91
|
+
"@stable-harness/tool-gateway": "0.0.106",
|
|
92
|
+
"@stable-harness/workspace-yaml": "0.0.106",
|
|
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.106",
|
|
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.106",
|
|
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.106",
|
|
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.106"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function parseArgs(e){const r=function createDefaultArgs(){return{workspaceRoot:process.cwd(),command:"request",toolArgs:void 0,trace:!1,traceJson:!1,progress:!1,serveOpenAi:!1,host:process.env.STABLE_HARNESS_OPENAI_HOST,port:process.env.STABLE_HARNESS_OPENAI_PORT?Number(process.env.STABLE_HARNESS_OPENAI_PORT):void 0,apiKey:process.env.STABLE_HARNESS_OPENAI_API_KEY,runtimeMode:readRuntimeMode(process.env.STABLE_HARNESS_CLIENT_RUNTIME)??"auto",daemonUrl:process.env.STABLE_HARNESS_DAEMON_URL,timeoutMs:Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),help:!1,prompt:"",shouldRunRequest:!1}}(),t=[];for(let o=0;o<e.length;o+=1)o=parseOneArg(e,o,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,o){const n=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===o.length&&function parseTopLevelCommand(e,r,t){return"start"===e[r]?(t.command="start",t.serveOpenAi=!0,!0):"stop"===e[r]?(t.command="stop",!0):"init"===e[r]?(t.command="init",!0):"build"===e[r]?(t.command="build",!0):"console"===e[r]||"session"===e[r]?(t.command="console",!0):"workflow"!==e[r]||"render"!==e[r+1]&&"inspect"!==e[r+1]?"agent"===e[r]&&"render"===e[r+1]&&(Object.assign(t,function parseAgentCommand(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}(e,r)),!0):(Object.assign(t,function parseWorkflowCommand(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}(e,r)),!0)}(e,r,t))return function stateIndex(e,r){return"workflow"===e[r]||"agent"===e[r]?r+2:r}(e,r);if("-h"===e[r]||"--help"===e[r])t.help=!0;else if("start"===t.command&&function isProtocolName(e){return"openai"===e||"openai-compatible"===e}(e[r]))t.serveOpenAi=!0;else{if("-w"===e[r]||"--workspace"===e[r])return setString(n,t,"workspaceRoot");if("--agent"===e[r])return setString(n,t,"agentId");if("--workflow"===e[r])return setString(n,t,"workflowRunId");if("--session-id"===e[r])return setString(n,t,"sessionId");if("--tool"===e[r])return setString(n,t,"toolId");if("--tool-args-json"===e[r])return t.toolArgs=function parseJsonArg(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}(n.value??"{}"),n.index;if("--trace"===e[r])t.trace=!0;else if("--trace-json"===e[r])t.traceJson=!0;else if("--progress"===e[r])t.progress=!0;else if("--serve-openai"===e[r])t.serveOpenAi=!0;else{if("--host"===e[r])return setString(n,t,"host");if("--port"===e[r])return t.port=Number(n.value??t.port),n.index;if("--api-key"===e[r])return setString(n,t,"apiKey");if("--runtime"===e[r])return function setRuntimeMode(e,r){const t=readRuntimeMode(e.value);if(!t)throw new Error("Unsupported --runtime value. Supported values: auto, daemon, local");return r.runtimeMode=t,e.index}(n,t);if("--daemon-url"===e[r])return setString(n,t,"daemonUrl");if("--target"===e[r])return function setBuildTarget(e,r){if("docker"===e.value)return r.buildTarget="docker",e.index;throw new Error("Unsupported build target. Supported targets: docker")}(n,t);if("--output"===e[r]||"-o"===e[r])return setString(n,t,"buildOutput");if("--timeout-ms"===e[r])return t.timeoutMs=Number(n.value??t.timeoutMs),n.index;o.push(e[r])}}return r}function setString(e,r,t){return"string"==typeof e.value&&Object.assign(r,{[t]:e.value}),e.index}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness console -w <workspace>"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness init [workspace]"," stable-harness build --target docker -w <workspace> --output <dir>"," stable-harness start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --runtime <mode> Client runtime mode: auto, daemon, or local."," --daemon-url <url> Stable Runtime daemon URL for daemon/auto modes."," --target docker Build target for workspace artifacts."," -o, --output <dir> Build output directory."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}function readRuntimeMode(e){return"auto"===e||"daemon"===e||"local"===e?e:void 0}
|
|
1
|
+
export function parseArgs(e){const r=function createDefaultArgs(){return{workspaceRoot:process.cwd(),command:"request",toolArgs:void 0,trace:!1,traceJson:!1,progress:!1,serveOpenAi:!1,host:process.env.STABLE_HARNESS_OPENAI_HOST,port:process.env.STABLE_HARNESS_OPENAI_PORT?Number(process.env.STABLE_HARNESS_OPENAI_PORT):void 0,apiKey:process.env.STABLE_HARNESS_OPENAI_API_KEY,runtimeMode:readRuntimeMode(process.env.STABLE_HARNESS_CLIENT_RUNTIME)??"auto",daemonUrl:process.env.STABLE_HARNESS_DAEMON_URL,clientProtocol:readClientProtocol(process.env.STABLE_HARNESS_CLIENT_PROTOCOL)??"stable-runtime",protocolUrl:process.env.STABLE_HARNESS_PROTOCOL_URL,timeoutMs:Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),help:!1,prompt:"",shouldRunRequest:!1}}(),t=[];for(let o=0;o<e.length;o+=1)o=parseOneArg(e,o,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,o){const n=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===o.length&&function parseTopLevelCommand(e,r,t){return"start"===e[r]?(t.command="start",t.serveOpenAi=!0,!0):"stop"===e[r]?(t.command="stop",!0):"init"===e[r]?(t.command="init",!0):"build"===e[r]?(t.command="build",!0):"console"===e[r]||"session"===e[r]?(t.command="console",!0):"workflow"!==e[r]||"render"!==e[r+1]&&"inspect"!==e[r+1]?"agent"===e[r]&&"render"===e[r+1]&&(Object.assign(t,function parseAgentCommand(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}(e,r)),!0):(Object.assign(t,function parseWorkflowCommand(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}(e,r)),!0)}(e,r,t))return function stateIndex(e,r){return"workflow"===e[r]||"agent"===e[r]?r+2:r}(e,r);if("-h"===e[r]||"--help"===e[r])t.help=!0;else if("start"===t.command&&function isProtocolName(e){return"openai"===e||"openai-compatible"===e}(e[r]))t.serveOpenAi=!0;else{if("-w"===e[r]||"--workspace"===e[r])return setString(n,t,"workspaceRoot");if("--agent"===e[r])return setString(n,t,"agentId");if("--workflow"===e[r])return setString(n,t,"workflowRunId");if("--session-id"===e[r])return setString(n,t,"sessionId");if("--tool"===e[r])return setString(n,t,"toolId");if("--tool-args-json"===e[r])return t.toolArgs=function parseJsonArg(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}(n.value??"{}"),n.index;if("--trace"===e[r])t.trace=!0;else if("--trace-json"===e[r])t.traceJson=!0;else if("--progress"===e[r])t.progress=!0;else if("--serve-openai"===e[r])t.serveOpenAi=!0;else{if("--host"===e[r])return setString(n,t,"host");if("--port"===e[r])return t.port=Number(n.value??t.port),n.index;if("--api-key"===e[r])return setString(n,t,"apiKey");if("--runtime"===e[r])return function setRuntimeMode(e,r){const t=readRuntimeMode(e.value);if(!t)throw new Error("Unsupported --runtime value. Supported values: auto, daemon, local");return r.runtimeMode=t,e.index}(n,t);if("--daemon-url"===e[r])return setString(n,t,"daemonUrl");if("--protocol"===e[r])return function setClientProtocol(e,r){const t=readClientProtocol(e.value);if(!t)throw new Error("Unsupported --protocol value. Supported values: stable-runtime, a2a, agui, acp");return r.clientProtocol=t,e.index}(n,t);if("--protocol-url"===e[r])return setString(n,t,"protocolUrl");if("--target"===e[r])return function setBuildTarget(e,r){if("docker"===e.value)return r.buildTarget="docker",e.index;throw new Error("Unsupported build target. Supported targets: docker")}(n,t);if("--output"===e[r]||"-o"===e[r])return setString(n,t,"buildOutput");if("--timeout-ms"===e[r])return t.timeoutMs=Number(n.value??t.timeoutMs),n.index;o.push(e[r])}}return r}function setString(e,r,t){return"string"==typeof e.value&&Object.assign(r,{[t]:e.value}),e.index}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness console -w <workspace>"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness init [workspace]"," stable-harness build --target docker -w <workspace> --output <dir>"," stable-harness start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --runtime <mode> Client runtime mode: auto, daemon, or local."," --daemon-url <url> Stable Runtime daemon URL for daemon/auto modes."," --protocol <name> Client protocol: stable-runtime, a2a, agui, or acp."," --protocol-url <url> Protocol server URL for a2a/agui/acp."," --target docker Build target for workspace artifacts."," -o, --output <dir> Build output directory."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}function readRuntimeMode(e){return"auto"===e||"daemon"===e||"local"===e?e:void 0}function readClientProtocol(e){return"stable"===e||"stable-runtime"===e||"http"===e?"stable-runtime":"a2a"===e||"agui"===e||"acp"===e?e:"ag-ui"===e?"agui":void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{createInterface as s}from"node:readline/promises";import{stdin as n,stdout as t}from"node:process";import{daemonBaseUrl as o,isDaemonAvailable as i}from"../daemon/client.js";import{formatCliRuntimeEvent as r}from"../event-view.js";export async function runConsole(a){const c=await async function createConsoleClient(e){if("local"===e.args.runtimeMode)return createLocalConsoleClient(await e.createRuntime(),e.eventView);const s=o(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 n=await fetch(e,{method:"POST",body:JSON.stringify(s)});if(!n.ok)throw new Error(`HTTP ${n.status}`);return await n.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 n=await fetch(`${e}/requests/${encodeURIComponent(s)}`);return n.ok?await n.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:(n,t)=>function streamDaemonEvents(e,s,n,t){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:i}=await s.read();if(i)return;o+=Buffer.from(e).toString("utf8");const a=o.split("\n\n");o=a.pop()??"";for(const e of a){const s=e.split("\n").find(e=>e.startsWith("data: "))?.slice(6);if(!s)continue;const o=r(JSON.parse(s),n);o&&t(o)}}}).catch(e=>{o.signal.aborted||process.stderr.write(`stable-harness console event stream failed: ${String(e)}\n`)}),{close:()=>o.abort()}}(e,n,s,t)}}(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)}(a);let d=a.args.sessionId??e(),u=a.args.agentId;process.stderr.write(`stable-harness console: ${function consoleRuntimeMessage(e,s){return"daemon"===e.mode?`connected to daemon at ${e.baseUrl}`:"local"===s?"local mode selected; running in-process":"no matching daemon found; running in-process"}(c,a.args.runtimeMode)}\n`),process.stdout.write(`session ${d}\n`);const l=s({input:n,output:t,terminal:process.stdout.isTTY});try{const e=process.stdout.isTTY?await async function runInteractiveLoop(e){let{sessionId:s,agentId:n}=e;for(;;){const t=await e.rl.question("stable> "),o=await handleConsoleLineSafely({client:e.client,line:t.trim(),sessionId:s,agentId:n,eventView:e.eventView});if(o.done)return o;s=o.sessionId,n=o.agentId}}({rl:l,client:c,sessionId:d,agentId:u,eventView:a.eventView}):await async function runPipedLoop(e){let{sessionId:s,agentId:n}=e;for await(const t of e.rl){const o=await handleConsoleLineSafely({client:e.client,line:t.trim(),sessionId:s,agentId:n,eventView:e.eventView});if(o.done)return o;s=o.sessionId,n=o.agentId}return{done:!0,sessionId:s,agentId:n}}({rl:l,client:c,sessionId:d,agentId:u,eventView:a.eventView});d=e.sessionId,u=e.agentId}catch(e){if(!function isEndOfInput(e){return e instanceof Error&&e.message.includes("closed")}(e))throw e}finally{l.close()}}async function handleConsoleLineSafely(s){try{return await async function handleConsoleLine(s){const n=function parseConsoleCommand(e){if(!e.startsWith("/"))return;const[s="",...n]=e.slice(1).trim().split(/\s+/u);return{name:s,args:n.join(" ").trim()}}(s.line);return s.line?n?await async function runConsoleCommand(s){const{client:n,command:t}=s;if("exit"===t.name||"quit"===t.name)return{...s,done:!0};if("help"===t.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.","/debug Print runtime inspection JSON.","/exit Leave console.",""].join("\n"))}();else if("health"===t.name)process.stdout.write(`${await n.health()}\n`);else if("debug"===t.name)process.stdout.write(`${JSON.stringify(await n.debug())}\n`);else{if("session"===t.name)return switchSession(s,t.args);if("new"===t.name)return switchSession(s,e());if("agent"===t.name)return function switchAgent(e,s){const n=s.trim()||void 0;return process.stdout.write(`agent ${n??"default"}\n`),{done:!1,sessionId:e.sessionId,agentId:n}}(s,t.args);"sessions"===t.name?printJsonLines(await n.sessions(),"sessionId"):"requests"===t.name?printJsonLines(await n.requests("all"===t.args?void 0:s.sessionId),"requestId"):"memory"===t.name||"memories"===t.name?printJsonLines(await n.memories(t.args||void 0),"id"):"clear"===t.name?process.stdout.write(`cleared ${await n.clearSession(s.sessionId)} requests from ${s.sessionId}\n`):"tool"===t.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: /${t.name}\n`)}return{done:!1,sessionId:s.sessionId,agentId:s.agentId}}({...s,command:n}):(await runConsoleRequest(s.client,await async function withSessionHistory(e,s){if(!s.sessionId||s.toolCall||s.workflow)return s;const n=await async function buildSessionHistory(e,s){const n=(await e.requests(s)).filter(e=>"completed"===e.state).slice(-6),t=[];for(const s of n){const n=await e.inspectRequest(s.requestId),o=readHistoryInput(n),i=n?.output?.trim();o&&i&&t.push({role:"user",content:o},{role:"assistant",content:i})}return t.slice(-12)}(e,s.sessionId);return{...s,metadata:{...s.metadata,consoleSession:!0,openaiMessages:[...n,{role:"user",content:s.input}],openaiSessionHistory:n.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,n){const t=n.requestId??e(),o=s.stream(t,e=>process.stdout.write(`${e}\n`));try{const e=await s.request({...n,requestId:t});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:(n,t)=>({close:e.subscribe(e=>{if(e.requestId===n){const n=r(e,s);n&&t(n)}})})}}function readHistoryInput(e){const s=Array.isArray(e?.metadata?.openaiMessages)?e.metadata.openaiMessages:[],n=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 n?n:e?.input.trim()||void 0}function switchSession(e,s){const n=s.trim()||e.sessionId;return process.stdout.write(`session ${n}\n`),{done:!1,sessionId:n,agentId:e.agentId}}function printJsonLines(e,s){if(0!==e.length)for(const n of e){const e="object"==typeof n&&n?n:{};process.stdout.write(`${String(e[s]??"")} ${JSON.stringify(n)}\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: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}`:"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.","/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("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,4 +1,4 @@
|
|
|
1
|
-
import { type WorkspaceRuntimePolicy } from "@stable-harness/core";
|
|
1
|
+
import { type RuntimeRequest, type RuntimeResponse, type WorkspaceRuntimePolicy } from "@stable-harness/core";
|
|
2
2
|
import type { CliArgs } from "../args.js";
|
|
3
3
|
import { type CliEventViewConfig } from "../event-view.js";
|
|
4
4
|
export declare function runRequestThroughDaemon(input: {
|
|
@@ -7,4 +7,6 @@ export declare function runRequestThroughDaemon(input: {
|
|
|
7
7
|
eventView: CliEventViewConfig;
|
|
8
8
|
}): Promise<boolean>;
|
|
9
9
|
export declare function daemonBaseUrl(policy: WorkspaceRuntimePolicy, overrideUrl?: string): string;
|
|
10
|
+
export declare function agentProtocolBaseUrl(policy: WorkspaceRuntimePolicy, overrideUrl?: string): string;
|
|
10
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>;
|
|
@@ -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("local"===e.args.runtimeMode)return!1;const n=daemonBaseUrl(e.runtimePolicy,e.args.daemonUrl);if(!await isDaemonAvailable(
|
|
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 r=await fetch(`${t}/health`,{signal:AbortSignal.timeout(300)});if(!r.ok)return!1;const o=await r.json();return Array.isArray(o.protocols)&&o.protocols.includes("agui"===e?"agui":e)}catch{return!1}}(o,r))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()));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){const n="a2a"===r?await async function sendA2aRequest(t,e){const r=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}]}});return r.task?.status?.message?.parts?.map(t=>t.text).filter(Boolean).join("\n")??""}(e,o):"agui"===r?await async function sendAguiRequest(t,e){const r=await fetch(`${t}/ag-ui/runs`,{method:"POST",body:JSON.stringify({input:e.input,runId:e.requestId,threadId:e.sessionId,agentId:e.agentId,runtimeRequest:e})});if(!r.ok||!r.body)throw new Error(`AG-UI request failed: HTTP ${r.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))}}}(r.body.getReader())).map(readAguiText).filter(Boolean).join("")}(e,o):await async function sendAcpRequest(e,r){const o=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}});if(o.error)throw new Error(o.error.message??"ACP request failed");return o.result?._meta?.output??""}(e,o);return{requestId:o.requestId??t(),sessionId:o.sessionId??"",agentId:o.agentId??"",state:"completed",output:n}}async function postJson(t,e){const r=await fetch(t,{method:"POST",body:JSON.stringify(e)});if(!r.ok)throw new Error(`protocol request failed: HTTP ${r.status}`);return await r.json()}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"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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106",
|
|
18
|
+
"@stable-harness/adapter-langgraph": "0.0.106",
|
|
19
|
+
"@stable-harness/core": "0.0.106",
|
|
20
|
+
"@stable-harness/memory": "0.0.106",
|
|
21
|
+
"@stable-harness/protocols": "0.0.106",
|
|
22
|
+
"@stable-harness/tool-gateway": "0.0.106",
|
|
23
|
+
"@stable-harness/workspace-yaml": "0.0.106"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{evaluateToolGuardrails as t}from"./policy/tool-invocation.js";import{toolCircuitOpenEvent as o,toolFailureEvent as e}from"./tool-failure.js";export async function runDirectToolCall(o){const e=o.request.toolCall;if(!e)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const r=await async function resolveDirectToolCall(t){if(t.agent.tools.includes(t.toolId)&&t.gateway.get(t.toolId))return{toolId:t.toolId,args:t.args};const o=await(t.gateway.repairToolCall?.({toolId:t.toolId,args:t.args,allowedToolIds:t.agent.tools,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}}));if(o&&t.agent.tools.includes(o.toolId)&&t.gateway.get(o.toolId))return emitToolRepair(t,"repaired",o.toolId),o;if(!t.agent.tools.includes(t.toolId))throw emitToolRepair(t,"blocked",void 0,`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`),new Error(`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`);throw emitToolRepair(t,"blocked",void 0,`Tool is not registered: ${t.toolId}`),new Error(`Tool is not registered: ${t.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:e.toolId,args:e.args});o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId});const s=t({agent:o.agent,args:r.args,events:o.events??[],toolId:r.toolId},o.toolGuardrails);if(s)return o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:s.eventOutput}),{text:s.modelOutput,metadata:{toolCall:{toolId:r.toolId},controlStatus:s.status}};if(o.toolFailureTracker?.isCircuitOpen(r.toolId)){const t=new Error(`Tool circuit is open: ${r.toolId}`);throw emitToolFailure(o,r.toolId,t),t}const a=await async function invokeToolWithFailureEvents(t,o){try{return await t.gateway.invoke({toolId:o.toolId,args:o.args,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}})}catch(e){throw emitToolFailure(t,o.toolId,e),e}}(o,r);return o.toolFailureTracker?.recordSuccess(a.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:a.toolId,output:a.output}),{text:(
|
|
1
|
+
import{evaluateToolGuardrails as t}from"./policy/tool-invocation.js";import{toolCircuitOpenEvent as o,toolFailureEvent as e}from"./tool-failure.js";export async function runDirectToolCall(o){const e=o.request.toolCall;if(!e)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const r=await async function resolveDirectToolCall(t){if(t.agent.tools.includes(t.toolId)&&t.gateway.get(t.toolId))return{toolId:t.toolId,args:t.args};const o=await(t.gateway.repairToolCall?.({toolId:t.toolId,args:t.args,allowedToolIds:t.agent.tools,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}}));if(o&&t.agent.tools.includes(o.toolId)&&t.gateway.get(o.toolId))return emitToolRepair(t,"repaired",o.toolId),o;if(!t.agent.tools.includes(t.toolId))throw emitToolRepair(t,"blocked",void 0,`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`),new Error(`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`);throw emitToolRepair(t,"blocked",void 0,`Tool is not registered: ${t.toolId}`),new Error(`Tool is not registered: ${t.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:e.toolId,args:e.args});o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId});const s=t({agent:o.agent,args:r.args,events:o.events??[],toolId:r.toolId},o.toolGuardrails);if(s)return o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:s.eventOutput}),{text:s.modelOutput,metadata:{toolCall:{toolId:r.toolId},controlStatus:s.status}};if(o.toolFailureTracker?.isCircuitOpen(r.toolId)){const t=new Error(`Tool circuit is open: ${r.toolId}`);throw emitToolFailure(o,r.toolId,t),t}const a=await async function invokeToolWithFailureEvents(t,o){try{return await t.gateway.invoke({toolId:o.toolId,args:o.args,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input,approvalIds:readApprovalIds(t.request.metadata)}})}catch(e){throw emitToolFailure(t,o.toolId,e),e}}(o,r);return o.toolFailureTracker?.recordSuccess(a.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:a.toolId,output:a.output}),{text:stringifyToolOutput(a.output),metadata:{toolCall:{toolId:a.toolId}}}}function emitToolFailure(t,r,s){const a=e({requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:r,error:s});t.emit(a),t.toolFailureTracker?.recordFailure(r)&&t.emit(o({requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:r,reason:"runtime.tool.failure"===a.type?a.failure.reason:"unknown"}))}function emitToolRepair(t,o,e,r){t.emit({type:"runtime.inventory.repair",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,status:o,diagnostic:{layer:"tool",owner:"stable_runtime_policy",originalId:t.toolId,repairedId:e,candidateIds:t.agent.tools,reason:r}})}function stringifyToolOutput(t){if("string"==typeof t)return t;const o=JSON.stringify(t);return"string"==typeof o?o:""}function readApprovalIds(t){const o=t?.approvalIds??t?.approvalId;return"string"==typeof o&&o.trim()?[o.trim()]:Array.isArray(o)?o.filter(t=>"string"==typeof t&&t.trim().length>0):void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{buildRawArgsRecoveryRequest as t,buildRawArgsToolEvidenceRecoveryRequest as o,hasUniqueRawArgsTool as r,matchUniqueRawArgsTool as a}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as s,assertNoRawToolCallOutput as l,assertNoRawToolResultOutput as
|
|
1
|
+
import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{buildRawArgsRecoveryRequest as t,buildRawArgsToolEvidenceRecoveryRequest as o,hasUniqueRawArgsTool as r,matchUniqueRawArgsTool as a}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as s,assertNoRawToolCallOutput as l,assertNoRawToolResultOutput as u,assertNoToolExecutionErrorOutput as i,buildEvidenceSynthesisOutput as n,buildResultRecoveryRequest as c,containsRawToolCallOutput as p,rawToolCallFailureMessage as d,rawToolCallOutputPreview as y,toolCallRecoveryEnabled as v}from"../../recovery/tool-call.js";import{controlGaps as g}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(o){let g=o.result,m=o.request;const w=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(o.recoveryPolicy),R=new Set,_=new Set;for(let e=0;;e+=1){const r=o.store.getRun(o.requestId)?.events??[],s=a({output:g.text,agent:o.agent,workspace:o.workspace,toolGateway:o.toolGateway,events:r,policy:o.recoveryPolicy}),l=s?rawArgsToolKey(s):void 0,u=l&&!R.has(l)?t({request:m,output:g.text,agent:o.agent,workspace:o.workspace,toolGateway:o.toolGateway,events:r,policy:o.recoveryPolicy}):void 0,i=e<w?c({request:m,output:g.text,events:r,availableToolIds:o.agent.tools,policy:o.recoveryPolicy}):void 0,n=u??i;if(!n)break;if(n===u&&R.add(l),m=n,emitRepair(o,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(g.text,o.agent.tools)),g=await o.runAdapter(n),emitRepair(o,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(g.text,o.agent.tools)),n===u){const t=await recoverRepeatedRawArgsByToolGateway(o,m,g,R,_,e+1);t&&(m=t.request,g=t.result)}}return function finalizeRecoveredOutput(t,o){if(!v(t.recoveryPolicy))return o;let a=!1;if(p(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:d(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const c=n({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return c&&(a=!0,o={...o,text:c,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),a||(p(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),l(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(r({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${y(t)}`)}(t,o.text),s(o.text,t.agent.tools,t.recoveryPolicy),u(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),i(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(o,g)}async function recoverRepeatedRawArgsByToolGateway(e,t,r,s,l,u){const i=a({output:r.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],policy:e.recoveryPolicy});if(!i||!e.runRecoveredToolCall)return;const n=rawArgsToolKey(i);if(!s.has(n)||l.has(n))return;l.add(n),emitRepair(e,"runtime.repair.started","result_output",u,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(r.text,e.agent.tools));const c=function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}(await e.runRecoveredToolCall(i.toolId,i.args)),p=o({request:t,match:i,toolOutput:c}),d=await e.runAdapter(p),y=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolBlocker({match:i,toolOutput:c,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",u,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(y.text,e.agent.tools)),{request:p,result:y}}function emitRepair(e,t,o,r,a,s,l){const u={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...l?{diagnostics:l}:{}};e.emit("runtime.repair.started"===t?{type:t,...u}:{type:t,...u,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:y(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function buildEmptyRecoveredToolBlocker(e){const t=g(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106",
|
|
15
|
+
"@stable-harness/memory": "0.0.106"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/evaluation",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,
|
|
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(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 async function handleAcpMessage(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")}(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)})}})}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){return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:e.type},metadata:e}}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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stable-harness/protocols",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
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.106"
|
|
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.106",
|
|
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.106"
|
|
15
15
|
}
|
|
16
16
|
}
|