stable-harness 0.0.107 → 0.0.109

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.
Files changed (35) hide show
  1. package/docs/protocols/agent-protocols.md +41 -3
  2. package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
  3. package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
  4. package/node_modules/@stable-harness/core/dist/execution-contract.d.ts +1 -0
  5. package/node_modules/@stable-harness/core/dist/execution-contract.js +1 -1
  6. package/node_modules/@stable-harness/core/dist/quality/event-evidence.d.ts +1 -0
  7. package/node_modules/@stable-harness/core/dist/quality/event-evidence.js +1 -1
  8. package/node_modules/@stable-harness/core/dist/recovery/tool-call.js +1 -1
  9. package/node_modules/@stable-harness/core/dist/runtime.js +1 -1
  10. package/node_modules/@stable-harness/core/package.json +3 -3
  11. package/node_modules/@stable-harness/governance/package.json +1 -1
  12. package/node_modules/@stable-harness/memory/package.json +1 -1
  13. package/node_modules/@stable-harness/protocols/dist/src/agent-protocols.js +1 -1
  14. package/node_modules/@stable-harness/protocols/package.json +2 -2
  15. package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
  16. package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
  17. package/package.json +9 -9
  18. package/packages/adapter-deepagents/package.json +2 -2
  19. package/packages/adapter-langgraph/package.json +2 -2
  20. package/packages/cli/dist/src/daemon/client.js +1 -1
  21. package/packages/cli/package.json +8 -8
  22. package/packages/core/dist/execution-contract.d.ts +1 -0
  23. package/packages/core/dist/execution-contract.js +1 -1
  24. package/packages/core/dist/quality/event-evidence.d.ts +1 -0
  25. package/packages/core/dist/quality/event-evidence.js +1 -1
  26. package/packages/core/dist/recovery/tool-call.js +1 -1
  27. package/packages/core/dist/runtime.js +1 -1
  28. package/packages/core/package.json +3 -3
  29. package/packages/evaluation/package.json +2 -2
  30. package/packages/governance/package.json +1 -1
  31. package/packages/memory/package.json +1 -1
  32. package/packages/protocols/dist/src/agent-protocols.js +1 -1
  33. package/packages/protocols/package.json +2 -2
  34. package/packages/tool-gateway/package.json +1 -1
  35. package/packages/workspace-yaml/package.json +2 -2
@@ -50,6 +50,7 @@ A2A is exposed as an HTTP+JSON and SSE facade:
50
50
  - `GET /a2a/tasks`
51
51
  - `GET /a2a/tasks/:id`
52
52
  - `POST /a2a/tasks/:id:cancel`
53
+ - `GET /a2a/tasks/:id:subscribe`
53
54
 
54
55
  `message:send` and JSON-RPC `SendMessage` map to a native `RuntimeRequest`.
55
56
  Streaming responses subscribe to native runtime events and project them as A2A
@@ -63,6 +64,19 @@ message parts.
63
64
  Session mapping: `taskId` or message `contextId` becomes the Stable Runtime
64
65
  `sessionId`.
65
66
 
67
+ Compliance notes:
68
+
69
+ | Capability | Status | Notes |
70
+ | --- | --- | --- |
71
+ | Agent card | Supported | Exposed at `/.well-known/agent-card.json` and `/a2a/agent-card.json`. |
72
+ | Version header | Supported | Empty, `0.3`, and `1.0` are accepted; unsupported versions fail clearly. |
73
+ | Send message | Supported | Returns a Task backed by a Stable Runtime request. |
74
+ | Streaming message | Supported | Emits an initial Task snapshot, then task status update events, then closes on completion. |
75
+ | Task artifacts | Supported | Final text output is exposed as an output artifact while status messages remain available. |
76
+ | Task subscription | Partial | `GET /a2a/tasks/:id:subscribe` replays the current task and streams future runtime updates for running tasks. |
77
+ | Push notifications | Not implemented | Capability advertises `pushNotifications: false`; use Stable Runtime SSE or A2A streaming. |
78
+ | Full task history | Partial | Stable Runtime stores request records; A2A history projection is intentionally limited. |
79
+
66
80
  ## AG-UI
67
81
 
68
82
  AG-UI is exposed as an HTTP+SSE event stream:
@@ -70,9 +84,9 @@ AG-UI is exposed as an HTTP+SSE event stream:
70
84
  - `GET /ag-ui/capabilities`
71
85
  - `POST /ag-ui/runs`
72
86
 
73
- `POST /ag-ui/runs` emits `RunStarted`, protocol `Custom` events for Stable
74
- Runtime events, `TextMessageChunk` for final assistant output, and `RunFinished`
75
- or `RunError`.
87
+ `POST /ag-ui/runs` emits canonical AG-UI event enum values: `RUN_STARTED`,
88
+ `CUSTOM` events for Stable Runtime events, `TEXT_MESSAGE_START`,
89
+ `TEXT_MESSAGE_CONTENT`, `TEXT_MESSAGE_END`, and `RUN_FINISHED` or `RUN_ERROR`.
76
90
 
77
91
  Stable Harness clients may include `runtimeRequest` in the run body to preserve
78
92
  explicit runtime fields while still using the AG-UI event stream.
@@ -80,6 +94,16 @@ explicit runtime fields while still using the AG-UI event stream.
80
94
  Session mapping: `threadId` or `sessionId` becomes the Stable Runtime
81
95
  `sessionId`.
82
96
 
97
+ Compliance notes:
98
+
99
+ | Capability | Status | Notes |
100
+ | --- | --- | --- |
101
+ | Run lifecycle | Supported | Emits `RUN_STARTED`, `RUN_FINISHED`, and `RUN_ERROR`. |
102
+ | Text message lifecycle | Supported | Emits start/content/end events for final assistant text. |
103
+ | Tool events | Partial | Direct runtime tool start/result events are projected to AG-UI tool events. |
104
+ | Runtime events | Supported | Non-native events remain available through `CUSTOM` payloads. |
105
+ | Reconnect/resume | Not implemented | Use Stable Runtime event replay when first-party resume behavior is required. |
106
+
83
107
  ## ACP
84
108
 
85
109
  ACP's standard transport is JSON-RPC over stdio. Stable Harness exposes a local
@@ -112,3 +136,17 @@ Stable Harness clients may include `params.runtimeRequest` to carry explicit
112
136
  runtime fields through the ACP-shaped transport.
113
137
 
114
138
  Session mapping: ACP `sessionId` becomes the Stable Runtime `sessionId`.
139
+
140
+ Compliance notes:
141
+
142
+ | Capability | Status | Notes |
143
+ | --- | --- | --- |
144
+ | stdio JSON-RPC | Supported | `stable-harness acp-stdio -w ./workspace` accepts one JSON-RPC message per line. |
145
+ | HTTP JSON-RPC | Supported | `/acp` reuses the same method handler as stdio. |
146
+ | initialize | Supported | Negotiates protocol major version `1`, returns `agentCapabilities`, `agentInfo`, and `authMethods`. |
147
+ | session/new | Supported | Creates a client-usable Stable Runtime session ID. |
148
+ | session/load | Supported | Returns the requested session ID when load is requested. |
149
+ | session/list | Supported | Projects Stable Runtime session summaries. |
150
+ | session/prompt | Supported | Maps prompt turns to Stable Runtime requests. |
151
+ | session/cancel | Supported | Cancels currently running requests in the session. |
152
+ | client file/terminal requests | Not implemented | Stable Harness does not request client-side filesystem or terminal operations through ACP. |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107",
18
+ "@stable-harness/core": "0.0.109",
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.107",
3
+ "version": "0.0.109",
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.107"
14
+ "@stable-harness/core": "0.0.109"
15
15
  }
16
16
  }
@@ -5,5 +5,6 @@ export declare function assertExecutionContract(input: {
5
5
  requestId: string;
6
6
  sessionId: string;
7
7
  agent: WorkspaceAgent;
8
+ output?: string;
8
9
  metadata?: Record<string, unknown>;
9
10
  }): void;
@@ -1 +1 @@
1
- export function assertExecutionContract(e){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(e.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(e),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(e),function assertToolDependencies(e){const t=readExecutionContract(e.agent),n=isRecord(t.toolDependencies)?t.toolDependencies:{},r=Object.entries(n).map(([e,t])=>[e,readStringArray(t)]).filter(e=>e[1].length>0);if(0===r.length)return;const o=e.store.getRun(e.requestId),i=new Set((o?.events??[]).flatMap(readEvidenceToolId)),s=r.filter(([e])=>i.has(e)).flatMap(([e,t])=>t.filter(e=>!i.has(e)).map(t=>`${e} requires ${t}`));if(0!==s.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_tool_dependency",missingEvidenceTools:s}),new Error(`Execution contract missing required tool dependencies: ${s.join(", ")}`)}(e))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&function isToolResultEvent(e){return"deepagents.tool_execution.result"===e.eventType||"agent.tool.result"===e.phase}(e.event)&&"string"==typeof e.event.toolId&&function isSuccessfulEvidenceEvent(e){const t=function readString(e){return"string"==typeof e&&e.length>0?e:void 0}(e.controlStatus)??function readOutputStatus(e){if("string"!=typeof e)return;const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:e.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e.output);return!t||/^(?:completed|success|ok|recorded)$/iu.test(t)}(e.event)?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
1
+ import{outputUsesPriorEvidence as e,successfulEvidenceItems as t}from"./quality/event-evidence.js";export function assertExecutionContract(n){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(n.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(n),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(n),function assertRequiredEvidenceUsage(n){const r=function readRequiredEvidenceUsageTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceUsageTools)}(n.agent);if(0===r.length)return;const o=n.store.getRun(n.requestId),i=t(o?.events??[]),s=new Set(i.map(e=>e.source)),c=n.output??o?.output??"",a=r.filter(t=>!s.has(t)||!e(c,t,i));if(0!==a.length)throw n.emit({type:"runtime.execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_required_evidence_usage",missingEvidenceTools:a}),new Error(`Execution contract final output must use required evidence from: ${a.join(", ")}`)}(n),function assertToolDependencies(e){const t=readExecutionContract(e.agent),n=isRecord(t.toolDependencies)?t.toolDependencies:{},r=Object.entries(n).map(([e,t])=>[e,readStringArray(t)]).filter(e=>e[1].length>0);if(0===r.length)return;const o=e.store.getRun(e.requestId),i=new Set((o?.events??[]).flatMap(readEvidenceToolId)),s=r.filter(([e])=>i.has(e)).flatMap(([e,t])=>t.filter(e=>!i.has(e)).map(t=>`${e} requires ${t}`));if(0!==s.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_tool_dependency",missingEvidenceTools:s}),new Error(`Execution contract missing required tool dependencies: ${s.join(", ")}`)}(n))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&function isToolResultEvent(e){return"deepagents.tool_execution.result"===e.eventType||"agent.tool.result"===e.phase}(e.event)&&"string"==typeof e.event.toolId&&function isSuccessfulEvidenceEvent(e){const t=function readString(e){return"string"==typeof e&&e.length>0?e:void 0}(e.controlStatus)??function readOutputStatus(e){if("string"!=typeof e)return;const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:e.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e.output);return!t||/^(?:completed|success|ok|recorded)$/iu.test(t)}(e.event)?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
@@ -10,3 +10,4 @@ export declare function successfulEvidenceItems(events: RuntimeEvent[]): Success
10
10
  export declare function controlBlockers(events: RuntimeEvent[]): string[];
11
11
  export declare function controlGaps(events: RuntimeEvent[]): string[];
12
12
  export declare function omittedControlGaps(events: RuntimeEvent[], output: string): string[];
13
+ export declare function outputUsesPriorEvidence(output: string, source: string, items: SuccessfulEvidenceItem[]): boolean;
@@ -1 +1 @@
1
- export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return isToolResultEvent(e)&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(r)?[]:stringifyEvidence(e.evidenceOutput??e.output).filter(t=>function isUsableEvidenceOutput(t,e){return"task"!==t||!function looksLikeUnexecutedToolIntent(t){const e=t.trim();return!(e.length>4e3)&&[/\b(?:I need to|I will|I'll|I am going to|I'm going to|Let me)\s+(?:call|use|run|invoke|get|fetch|check)\b[\s\S]{0,1200}\b(?:tool|function|system_time|web_search|url_text_fetch|task)\b/iu,/(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|获取|查看)\b[\s\S]{0,1200}\b(?:工具|函数|system_time|web_search|url_text_fetch|task)\b/iu,/```(?:json)?\s*(?:system_time|web_search|url_text_fetch|task)\s*```/iu].some(t=>t.test(e))}(e)&&!function looksLikeDelegatedCommentary(t){const e=t.trim();return!(e.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(t=>t.test(e))}(e)}(r,t)).map(t=>({source:r,output:t})):[]})}export function controlBlockers(t){const e=successfulEventIndexesBySource(t);return t.flatMap((t,r)=>{const n=readAdapterEvent(t),o=readControlStatus(n),s=readControlSource(n)??"tool";return o&&isBlockerStatus(o)?isResolvedByLaterCompletion(o)&&completedAfter(e,s,r)||isResolvedByLaterAnyCompletion(o)&&completedAfterAny(e,r)?[]:[`${s}:${o}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const r=readAdapterEvent(t),n=readControlStatus(r),o=readControlSource(r)??"tool";return n&&isGapStatus(n)?e.has(o)&&isResolvedByLaterCompletion(n)?[]:[`${o}:${n}`]:[]})}export function omittedControlGaps(t,e){const r=successfulEventIndexesBySource(t),n=successfulEvidenceItems(t),o=function outputHasUnsupportedEvidenceClaims(t,e,r){const n=function evidenceCorpus(t,e){return[...e.map(t=>t.output),...t.flatMap(t=>"runtime.request.started"===t.type?[t.input??""]:[]),...t.flatMap(t=>"runtime.memory.recall.completed"===t.type?[t.context]:[])].join("\n")}(e,r).toLowerCase();return unsupportedTokens(t,n,/\b[A-Z][A-Z0-9]{1,12}-\d+\b/gu).length>0||unsupportedTokens(t,n,/\b[A-Za-z_$][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*\b/gu).filter(isCodeLikeIdentifier).length>=2}(e,t,n);return t.flatMap((t,s)=>{const u=readAdapterEvent(t),i=readControlStatus(u),a=readControlSource(u)??"tool";return i&&function isOmittedControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}(i)?isResolvedByLaterCompletion(i)&&completedAfter(r,a,s)||isResolvedByLaterAnyCompletion(i)&&completedAfterAny(r,s)||!o&&function outputUsesPriorEvidence(t,e,r){const n=t.toLowerCase();return r.filter(t=>t.source===e).some(t=>{const e=t.output.trim();return!!e&&(!!n.includes(e.slice(0,500).toLowerCase())||function matchingEvidenceTokens(t,e){const r=new Set(["status","completed","success","recorded"]);return[...new Set(e.toLowerCase().match(/[a-z0-9][a-z0-9_.-]{1,}/gu)??[])].filter(t=>!r.has(t)).filter(e=>t.includes(e)).length}(n,e)>=2)})}(e,a,n)?[]:[`${a}:${i}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){if("runtime.adapter.event"===t.type&&isRecord(t.event))return t.event;const e=t;return isRecord(e)&&isToolResultEvent(e)?e:void 0}function isSuccessfulEvidenceEvent(t){const e=readEventStatus(t);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function isResolvedByLaterAnyCompletion(t){return"task_inventory_blocked"===t}function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,r)=>{const n=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r&&isSuccessfulEvidenceEvent(e)?r:void 0}(t);n&&e.set(n,[...e.get(n)??[],r])}),e}function isToolResultEvent(t){return"deepagents.tool_execution.result"===t?.eventType||"agent.tool.result"===t?.phase||"agent.tool.result"===t?.type||"deepagents.tool_execution.result"===t?.type}function completedAfter(t,e,r){return(t.get(e)??[]).some(t=>t>r)}function completedAfterAny(t,e){return[...t.values()].some(t=>t.some(t=>t>e))}function unsupportedTokens(t,e,r){return[...new Set(t.match(r)??[])].filter(t=>!e.includes(t.toLowerCase()))}function isCodeLikeIdentifier(t){return!(t.length<8)&&(/(?:Service|Controller|Repository|Manager|Client|Resolver|Agent|Tool|Async)$/u.test(t)||/[a-z][A-Z]/u.test(t))}function readOutputStatus(t){if(isRecord(t)&&"string"==typeof t.status)return t.status;if("string"==typeof t){const e=parseJsonRecord(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}}function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}function readEventStatus(t){return readString(t?.controlStatus)??readString(t?.status)??readOutputStatus(t?.output)}function readToolSource(t){return readString(t?.toolId)??readString(t?.toolName)??readString(t?.name)}function readControlStatus(t){const e=function readInventoryRepairControlStatus(t){if("inventory.repair"===t?.phase&&"blocked"===t.status)return"task"===readInventoryRepairSource(t)?"task_inventory_blocked":"blocked"}(t);if(e)return e;const r=readEventStatus(t);return r&&isControlStatus(r)?r:findNestedControlStatus(t)}function readControlSource(t){return readToolSource(t)??readInventoryRepairSource(t)??findNestedToolSource(t)}function isControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}function findNestedControlStatus(t,e=0){if(e>5||null==t)return;if("string"==typeof t)return function readNestedStringStatus(t,e){const r=parseJsonRecord(t);if(r)return findNestedControlStatus(r,e+1);const n=t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1];return n&&isControlStatus(n)?n:function readControlToken(t){const e=t.match(/\b(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked|dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)\b/iu)?.[0];return e&&isControlStatus(e)?e:void 0}(t)}(t,e);if(Array.isArray(t))return findFirstNested(t,t=>findNestedControlStatus(t,e+1));if(!isRecord(t))return;const r=readString(t.controlStatus)??readString(t.status);if(r&&isControlStatus(r))return r;const n=readOutputStatus(t.output);return n&&isControlStatus(n)?n:findFirstNested(Object.values(t),t=>findNestedControlStatus(t,e+1))}function findNestedToolSource(t,e=0){if(!(e>5||null==t)){if("string"==typeof t)return function readToolSourceFromText(t){const e=t.match(/["'](?:toolId|toolName|name)["']\s*:\s*["']([^"']+)["']/u)?.[1];return readString(e)}(t);if(Array.isArray(t))return findFirstNested(t,t=>findNestedToolSource(t,e+1));if(isRecord(t))return(readString(t.toolId)??readString(t.toolName)??readString(t.name))||findFirstNested(Object.values(t),t=>findNestedToolSource(t,e+1))}}function findFirstNested(t,e){for(const r of t){const t=e(r);if(void 0!==t)return t}}function readInventoryRepairSource(t){const e=isRecord(t?.diagnostic)?t.diagnostic:void 0;return readString(e?.layer)}
1
+ export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return isToolResultEvent(e)&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(r)?[]:stringifyEvidence(e.evidenceOutput??e.output).filter(t=>function isUsableEvidenceOutput(t,e){return"task"!==t||!function looksLikeUnexecutedToolIntent(t){const e=t.trim();return!(e.length>4e3)&&[/\b(?:I need to|I will|I'll|I am going to|I'm going to|Let me)\s+(?:call|use|run|invoke|get|fetch|check)\b[\s\S]{0,1200}\b(?:tool|function|system_time|web_search|url_text_fetch|task)\b/iu,/(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|获取|查看)\b[\s\S]{0,1200}\b(?:工具|函数|system_time|web_search|url_text_fetch|task)\b/iu,/```(?:json)?\s*(?:system_time|web_search|url_text_fetch|task)\s*```/iu].some(t=>t.test(e))}(e)&&!function looksLikeDelegatedCommentary(t){const e=t.trim();return!(e.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(t=>t.test(e))}(e)}(r,t)).map(t=>({source:r,output:t})):[]})}export function controlBlockers(t){const e=successfulEventIndexesBySource(t);return t.flatMap((t,r)=>{const n=readAdapterEvent(t),o=readControlStatus(n),s=readControlSource(n)??"tool";return o&&isBlockerStatus(o)?isResolvedByLaterCompletion(o)&&completedAfter(e,s,r)||isResolvedByLaterAnyCompletion(o)&&completedAfterAny(e,r)?[]:[`${s}:${o}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const r=readAdapterEvent(t),n=readControlStatus(r),o=readControlSource(r)??"tool";return n&&isGapStatus(n)?e.has(o)&&isResolvedByLaterCompletion(n)?[]:[`${o}:${n}`]:[]})}export function omittedControlGaps(t,e){const r=successfulEventIndexesBySource(t),n=successfulEvidenceItems(t),o=function outputHasUnsupportedEvidenceClaims(t,e,r){const n=function evidenceCorpus(t,e){return[...e.map(t=>t.output),...t.flatMap(t=>"runtime.request.started"===t.type?[t.input??""]:[]),...t.flatMap(t=>"runtime.memory.recall.completed"===t.type?[t.context]:[])].join("\n")}(e,r).toLowerCase();return unsupportedTokens(t,n,/\b[A-Z][A-Z0-9]{1,12}-\d+\b/gu).length>0||unsupportedTokens(t,n,/\b[A-Za-z_$][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*\b/gu).filter(isCodeLikeIdentifier).length>=2}(e,t,n);return t.flatMap((t,s)=>{const u=readAdapterEvent(t),i=readControlStatus(u),a=readControlSource(u)??"tool";return i&&function isOmittedControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}(i)?isResolvedByLaterCompletion(i)&&completedAfter(r,a,s)||isResolvedByLaterAnyCompletion(i)&&completedAfterAny(r,s)||!o&&outputUsesPriorEvidence(e,a,n)?[]:[`${a}:${i}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){if("runtime.adapter.event"===t.type&&isRecord(t.event))return t.event;const e=t;return isRecord(e)&&isToolResultEvent(e)?e:void 0}function isSuccessfulEvidenceEvent(t){const e=readEventStatus(t);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function isResolvedByLaterAnyCompletion(t){return"task_inventory_blocked"===t}function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,r)=>{const n=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r&&isSuccessfulEvidenceEvent(e)?r:void 0}(t);n&&e.set(n,[...e.get(n)??[],r])}),e}function isToolResultEvent(t){return"deepagents.tool_execution.result"===t?.eventType||"agent.tool.result"===t?.phase||"agent.tool.result"===t?.type||"deepagents.tool_execution.result"===t?.type}function completedAfter(t,e,r){return(t.get(e)??[]).some(t=>t>r)}function completedAfterAny(t,e){return[...t.values()].some(t=>t.some(t=>t>e))}export function outputUsesPriorEvidence(t,e,r){const n=t.toLowerCase();return r.filter(t=>t.source===e).some(t=>{const e=t.output.trim();return!!e&&(!!n.includes(e.slice(0,500).toLowerCase())||function matchingEvidenceTokens(t,e){const r=new Set(["status","completed","success","recorded"]);return[...new Set(e.toLowerCase().match(/[a-z0-9][a-z0-9_.-]{1,}/gu)??[])].filter(t=>!r.has(t)).filter(e=>t.includes(e)).length}(n,e)>=2)})}function unsupportedTokens(t,e,r){return[...new Set(t.match(r)??[])].filter(t=>!e.includes(t.toLowerCase()))}function isCodeLikeIdentifier(t){return!(t.length<8)&&(/(?:Service|Controller|Repository|Manager|Client|Resolver|Agent|Tool|Async)$/u.test(t)||/[a-z][A-Z]/u.test(t))}function readOutputStatus(t){if(isRecord(t)&&"string"==typeof t.status)return t.status;if("string"==typeof t){const e=parseJsonRecord(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}}function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}function readEventStatus(t){return readString(t?.controlStatus)??readString(t?.status)??readOutputStatus(t?.output)}function readToolSource(t){return readString(t?.toolId)??readString(t?.toolName)??readString(t?.name)}function readControlStatus(t){const e=function readInventoryRepairControlStatus(t){if("inventory.repair"===t?.phase&&"blocked"===t.status)return"task"===readInventoryRepairSource(t)?"task_inventory_blocked":"blocked"}(t);if(e)return e;const r=readEventStatus(t);return r&&isControlStatus(r)?r:findNestedControlStatus(t)}function readControlSource(t){return readToolSource(t)??readInventoryRepairSource(t)??findNestedToolSource(t)}function isControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}function findNestedControlStatus(t,e=0){if(e>5||null==t)return;if("string"==typeof t)return function readNestedStringStatus(t,e){const r=parseJsonRecord(t);if(r)return findNestedControlStatus(r,e+1);const n=t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1];return n&&isControlStatus(n)?n:function readControlToken(t){const e=t.match(/\b(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked|dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)\b/iu)?.[0];return e&&isControlStatus(e)?e:void 0}(t)}(t,e);if(Array.isArray(t))return findFirstNested(t,t=>findNestedControlStatus(t,e+1));if(!isRecord(t))return;const r=readString(t.controlStatus)??readString(t.status);if(r&&isControlStatus(r))return r;const n=readOutputStatus(t.output);return n&&isControlStatus(n)?n:findFirstNested(Object.values(t),t=>findNestedControlStatus(t,e+1))}function findNestedToolSource(t,e=0){if(!(e>5||null==t)){if("string"==typeof t)return function readToolSourceFromText(t){const e=t.match(/["'](?:toolId|toolName|name)["']\s*:\s*["']([^"']+)["']/u)?.[1];return readString(e)}(t);if(Array.isArray(t))return findFirstNested(t,t=>findNestedToolSource(t,e+1));if(isRecord(t))return(readString(t.toolId)??readString(t.toolName)??readString(t.name))||findFirstNested(Object.values(t),t=>findNestedToolSource(t,e+1))}}function findFirstNested(t,e){for(const r of t){const t=e(r);if(void 0!==t)return t}}function readInventoryRepairSource(t){const e=isRecord(t?.diagnostic)?t.diagnostic:void 0;return readString(e?.layer)}
@@ -1 +1 @@
1
- import{controlGaps as e}from"../quality/event-evidence.js";import{omittedControlRecoveryLines as t}from"./control-omission.js";import{containsProgressOnlyToolIntent as o,progressOnlyToolIntentMessage as n}from"./progress-intent.js";import{containsRawToolResultText as r}from"./tool-result.js";export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const n=readToolCallRecovery(e.policy);if(!0!==n.enabled)return;if(containsRawToolCallText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(o(e.output,e.availableToolIds)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer was progress-only text that declared a future tool call instead of executing it.","Continue the same user request by calling the named available tool through the backend's normal structured mechanism.","If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not narrate intended future tool calls as a final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(r(e.output,e.events)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer copied an executed tool result JSON as the user-facing answer.","Continue the same user request from the existing tool evidence.","If more evidence is required, use one remaining declared tool or subagent action through the backend's normal structured mechanism.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Otherwise synthesize a human-readable final answer from the executed tool evidence.","Do not return the raw tool result JSON as the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const s=t(e);if(s)return recoverRequest(e.request,s);const a="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,n);return a?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",a.output,a.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastMissingEvidenceTools(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return readStringArray(o.missingEvidenceTools)}return[]}(e.events);return 0!==t.length?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoRawToolResultOutput(e,t,o){if(!0===readToolCallRecovery(o).enabled&&r(e,t))throw new Error(`Adapter returned raw tool result JSON as the final answer after recovery. The backend must synthesize a user-facing answer. Output preview: ${previewOutput(e)}`)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function assertNoProgressOnlyToolIntentOutput(e,t,r){if(!0===readToolCallRecovery(r).enabled&&o(e,t))throw new Error(n(e))}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(t){const o=readToolCallRecovery(t.policy);if(!0!==o.enabled||!1===o.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(t.output,o))return;const n=function latestDelegatedTaskReport(e){return e.flatMap(e=>{const t="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;if(!isToolResultEvent(t)||"task"!==t.toolId)return[];const o="string"==typeof t.output?t.output.trim():"";return function looksLikeFinalReport(e){const t=e.trim();return!(t.length<80)&&(/^#{1,3}\s+\S/mu.test(t)||/\n#{1,3}\s+\S/mu.test(t)||/\n-{3,}\n/u.test(t))}(o)?[o]:[]}).at(-1)||void 0}(t.events);if(n)return n;const r=recentToolEvidence(t.events,6e3);if(0===r.length)return;const s=/\p{Script=Han}/u.test(t.request.input)?"zh":"en",a=e(t.events);return"zh"===s?function buildChineseEvidenceSynthesis(e,t,o){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,...o.length>0?["","未解决的证据缺口:",...o.map(e=>`- ${e}`)]:[],"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a):function buildEnglishEvidenceSynthesis(e,t,o){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,...o.length>0?["","Unresolved evidence gaps:",...o.map(e=>`- ${e}`)]:[],"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a)}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*[A-Za-z_][\w.-]*\s*\([^>]{0,2000}\)\s*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Let me|I'll|I will|I am going to|I'm going to)\s+(?:start\s+by\s+)?(?:read(?:ing)?|access(?:ing)?|gather(?:ing)?|collect(?:ing)?|fetch(?:ing)?|check(?:ing)?|inspect(?:ing)?)\b[\s\S]{0,1200}\b(?:context|instructions?|workflow|pull request|PR|issue|data|evidence|details?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return isToolResultEvent(o)&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function isToolResultEvent(e){return"deepagents.tool_execution.result"===e?.eventType||"agent.tool.result"===e?.phase}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{controlGaps as e}from"../quality/event-evidence.js";import{omittedControlRecoveryLines as t}from"./control-omission.js";import{containsProgressOnlyToolIntent as o,progressOnlyToolIntentMessage as n}from"./progress-intent.js";import{containsRawToolResultText as r}from"./tool-result.js";export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const n=readToolCallRecovery(e.policy);if(!0!==n.enabled)return;if(containsRawToolCallText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(o(e.output,e.availableToolIds)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer was progress-only text that declared a future tool call instead of executing it.","Continue the same user request by calling the named available tool through the backend's normal structured mechanism.","If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not narrate intended future tool calls as a final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(r(e.output,e.events)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer copied an executed tool result JSON as the user-facing answer.","Continue the same user request from the existing tool evidence.","If more evidence is required, use one remaining declared tool or subagent action through the backend's normal structured mechanism.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Otherwise synthesize a human-readable final answer from the executed tool evidence.","Do not return the raw tool result JSON as the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const s=t(e);if(s)return recoverRequest(e.request,s);const a="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,n);return a?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",a.output,a.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastExecutionContractFailure(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return{reason:o.reason,missing:readStringArray(o.missingEvidenceTools)}}return{missing:[]}}(e.events);return 0!==t.missing.length?"missing_required_evidence_usage"===t.reason?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`The final answer did not use required executed evidence from: ${t.missing.join(", ")}`,"Synthesize from those required source(s), or call them through normal structured tool calling if their evidence is unavailable.","Do not replace required evidence with unrelated intermediate evidence, progress text, or summaries from other tools."]):recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.missing.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoRawToolResultOutput(e,t,o){if(!0===readToolCallRecovery(o).enabled&&r(e,t))throw new Error(`Adapter returned raw tool result JSON as the final answer after recovery. The backend must synthesize a user-facing answer. Output preview: ${previewOutput(e)}`)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function assertNoProgressOnlyToolIntentOutput(e,t,r){if(!0===readToolCallRecovery(r).enabled&&o(e,t))throw new Error(n(e))}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(t){const o=readToolCallRecovery(t.policy);if(!0!==o.enabled||!1===o.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(t.output,o))return;const n=function latestDelegatedTaskReport(e){return e.flatMap(e=>{const t="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;if(!isToolResultEvent(t)||"task"!==t.toolId)return[];const o="string"==typeof t.output?t.output.trim():"";return function looksLikeFinalReport(e){const t=e.trim();return!(t.length<80)&&(/^#{1,3}\s+\S/mu.test(t)||/\n#{1,3}\s+\S/mu.test(t)||/\n-{3,}\n/u.test(t))}(o)?[o]:[]}).at(-1)||void 0}(t.events);if(n)return n;const r=recentToolEvidence(t.events,6e3);if(0===r.length)return;const s=/\p{Script=Han}/u.test(t.request.input)?"zh":"en",a=e(t.events);return"zh"===s?function buildChineseEvidenceSynthesis(e,t,o){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,...o.length>0?["","未解决的证据缺口:",...o.map(e=>`- ${e}`)]:[],"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a):function buildEnglishEvidenceSynthesis(e,t,o){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,...o.length>0?["","Unresolved evidence gaps:",...o.map(e=>`- ${e}`)]:[],"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a)}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*[A-Za-z_][\w.-]*\s*\([^>]{0,2000}\)\s*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Let me|I'll|I will|I am going to|I'm going to)\s+(?:start\s+by\s+)?(?:read(?:ing)?|access(?:ing)?|gather(?:ing)?|collect(?:ing)?|fetch(?:ing)?|check(?:ing)?|inspect(?:ing)?)\b[\s\S]{0,1200}\b(?:context|instructions?|workflow|pull request|PR|issue|data|evidence|details?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return isToolResultEvent(o)&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function isToolResultEvent(e){return"deepagents.tool_execution.result"===e?.eventType||"agent.tool.result"===e?.phase}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as o}from"./recovery/tool-call.js";import{recoverQualityReview as s,resolveQualityPolicy as n}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{createRuntimeAdministrationMethods as p}from"./runtime/admin/administration.js";import{runDirectToolCall as d}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as l}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as m}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as w}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as g,normalizeAdapterResult as y}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{resolveToolCallRecoveryPolicy as f}from"./runtime/recovery/tool-call-policy.js";import{createRuntimeMemoryAdministration as q}from"./runtime/admin/memory.js";import{createInMemoryRuntimeStore as R}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as v}from"./runtime/progress-narration.js";import{repairRuntimeSelection as k}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as b}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as A}from"./runtime/tool-failure.js";import{runWorkflowRequest as C}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const y=new Set,x=t.store??R(),j=g([I(t),v({options:t.progressNarration,policy:t.workspace.runtime}),b({policy:t.workspace.runtime,store:x,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);x.appendEvent(r);for(const e of y)e(r)},emit=e=>{emitBase(e),j.emitSideEffects(e,emitBase)},E=m({gateway:l({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),G={...t,toolGateway:E},T=A(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const p=t.request.requestId??e(),l=t.request.sessionId??e(),m=[],{agent:w,adapter:g}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await k({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):e.workspace.runtime.defaultAgentId,o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:p,sessionId:l,emit:e=>m.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,p,l,w)),m.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:p,sessionId:l,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await C({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:p,sessionId:l,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await d({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:p,sessionId:l,agent:w,toolFailureTracker:t.toolFailureTracker,toolGuardrails:t.input.toolGuardrails,events:t.store.getRun(p)?.events??[]});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],l=f({workspace:e.input.workspace,agent:e.agent}),m=n(e.input.workspace.runtime,e.agent),w=new Map;let g;try{g=await runAdapterOnce(e,t,e.request,p,d,w,l)}catch(a){if(!o(a,l))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),g=await runAdapterOnce(e,t,r(e.request,a,l),p,d,w,l),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}g=await i({...e,request:e.request,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),e.request,g,m),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:g});try{assertRequestExecutionContract(e)}catch(r){const o=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:l});if(!o)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),g=await runAdapterOnce(e,t,o,p,d,w,l),g=await i({...e,request:o,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),o,g,m),assertRequestExecutionContract(e),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const y=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:g,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:g,response:y}),y}({...t,adapter:g,requestId:p,sessionId:l,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,error:e})}}({input:G,capabilities:j,store:x,emit:emit,request:t,toolFailureTracker:T}),subscribe:e=>(y.add(e),()=>y.delete(e)),...w({workspace:t.workspace,store:x,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),...p({store:x,emit:emit}),...q({memory:t.memory}),cancel(e,t){const r=x.getRun(e);r&&"running"===r.state&&(x.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await j.stop(),y.clear()}}}function runRecoveredToolCall(e,t,r){return d({gateway:e.input.toolGateway,workspace:e.input.workspace,emit:e.emit,request:{...e.request,toolCall:{toolId:t,args:r}},requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,events:e.store.getRun(e.requestId)?.events??[]})}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a,o){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a,o),reviewModel:e.input.qualityReviewModel,executionEvaluatorRules:e.input.executionEvaluatorRules,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s,n){return y(await t.run({workspace:{...e.input.workspace,runtime:n},agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,executionEvaluatorRules:e.input.executionEvaluatorRules,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,o){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:o}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
1
+ import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as o}from"./recovery/tool-call.js";import{recoverQualityReview as s,resolveQualityPolicy as n}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{createRuntimeAdministrationMethods as p}from"./runtime/admin/administration.js";import{runDirectToolCall as d}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as l}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as m}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as w}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as g,normalizeAdapterResult as y}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{resolveToolCallRecoveryPolicy as f}from"./runtime/recovery/tool-call-policy.js";import{createRuntimeMemoryAdministration as q}from"./runtime/admin/memory.js";import{createInMemoryRuntimeStore as R}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as v}from"./runtime/progress-narration.js";import{repairRuntimeSelection as k}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as b}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as A}from"./runtime/tool-failure.js";import{runWorkflowRequest as C}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const y=new Set,x=t.store??R(),j=g([I(t),v({options:t.progressNarration,policy:t.workspace.runtime}),b({policy:t.workspace.runtime,store:x,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);x.appendEvent(r);for(const e of y)e(r)},emit=e=>{emitBase(e),j.emitSideEffects(e,emitBase)},E=m({gateway:l({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),G={...t,toolGateway:E},T=A(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const p=t.request.requestId??e(),l=t.request.sessionId??e(),m=[],{agent:w,adapter:g}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await k({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):e.workspace.runtime.defaultAgentId,o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:p,sessionId:l,emit:e=>m.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,p,l,w)),m.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:p,sessionId:l,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await C({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:p,sessionId:l,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await d({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:p,sessionId:l,agent:w,toolFailureTracker:t.toolFailureTracker,toolGuardrails:t.input.toolGuardrails,events:t.store.getRun(p)?.events??[]});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],l=f({workspace:e.input.workspace,agent:e.agent}),m=n(e.input.workspace.runtime,e.agent),w=new Map;let g;try{g=await runAdapterOnce(e,t,e.request,p,d,w,l)}catch(a){if(!o(a,l))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),g=await runAdapterOnce(e,t,r(e.request,a,l),p,d,w,l),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}g=await i({...e,request:e.request,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),e.request,g,m),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:g});try{assertRequestExecutionContract(e,g)}catch(r){const o=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:l});if(!o)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),g=await runAdapterOnce(e,t,o,p,d,w,l),g=await i({...e,request:o,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),o,g,m),assertRequestExecutionContract(e,g),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const y=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:g,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:g,response:y}),y}({...t,adapter:g,requestId:p,sessionId:l,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,error:e})}}({input:G,capabilities:j,store:x,emit:emit,request:t,toolFailureTracker:T}),subscribe:e=>(y.add(e),()=>y.delete(e)),...w({workspace:t.workspace,store:x,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),...p({store:x,emit:emit}),...q({memory:t.memory}),cancel(e,t){const r=x.getRun(e);r&&"running"===r.state&&(x.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await j.stop(),y.clear()}}}function runRecoveredToolCall(e,t,r){return d({gateway:e.input.toolGateway,workspace:e.input.workspace,emit:e.emit,request:{...e.request,toolCall:{toolId:t,args:r}},requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,events:e.store.getRun(e.requestId)?.events??[]})}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a,o){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a,o),reviewModel:e.input.qualityReviewModel,executionEvaluatorRules:e.input.executionEvaluatorRules,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e,r){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,output:r.text,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s,n){return y(await t.run({workspace:{...e.input.workspace,runtime:n},agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,executionEvaluatorRules:e.input.executionEvaluatorRules,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,o){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:o}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107",
15
- "@stable-harness/memory": "0.0.107"
14
+ "@stable-harness/governance": "0.0.109",
15
+ "@stable-harness/memory": "0.0.109"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const r=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...r]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(r));if(r.has("a2a")&&await async function handleA2a(e,t,a,r){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(a,200,function createAgentCard(e,t){const a=e.inspect();return{protocolVersion:"1.0",name:a.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,r)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(a,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(a,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&n?(e.cancel(n,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(n)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(a,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),a),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,a){if("SendMessage"===t.method){const r=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(r)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const r=readString(readRecord(t.params)?.id),s=r?e.inspectRequest(r):void 0;return void sendJson(a,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(a,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},a,t.id)}(e,await readJson(t),a),!0)}(t,e,s,a))return;if(r.has("agui")&&await async function handleAgui(e,t,a){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(a,200,{protocol:"ag-ui",transports:["http+sse"],events:["RunStarted","TextMessageChunk","Custom","RunFinished","RunError"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const r=readRuntimeRequestExtension(t),s=r?.requestId??readString(t.runId)??crypto.randomUUID(),n=r?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RunStarted",threadId:n,runId:s,input:{messages:t.messages,input:o}});const d=e.subscribe(e=>{e.requestId===s&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"Custom",name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"ToolCallStart",toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"ToolCallResult",messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:e.output,role:"tool"}:{type:"Custom",name:`stable-harness.${e.type}`,value:e}}(e))});try{const d=await e.request(r??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${s}-message`;d.output&&writeSse(a,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:d.output}),writeSse(a,{type:"RunFinished",threadId:n,runId:s,outcome:{type:"completed"===d.state?"success":"interrupt"},result:d.output})}catch(e){writeSse(a,{type:"RunError",threadId:n,runId:s,message:errorMessage(e)})}finally{d(),a.end()}}(e,await readJson(t),a),!0)}(t,e,s))return;if(r.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}}),!0;if("POST"===t.method&&"/acp"===t.url){const r=await readJson(t),s=await handleAcpJsonRpcMessage(e,r);return s?sendJson(a,200,s):a.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}});if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},r=readRuntimeRequestExtension(a),s=await e.request(r??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,r){const s=toA2aRuntimeRequest(t);writeSseHeaders(a);const n=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(a,{jsonrpc:"2.0",id:r,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(a,{jsonrpc:"2.0",id:r,result:{task:toA2aTask(t),final:!0}})}finally{n(),a.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const a=readRecord(e.message)??e;return{input:readPromptText(a),requestId:readString(e.requestId)??readString(a.messageId),sessionId:readString(e.taskId)??readString(a.taskId)??readString(a.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,a="output"in e?e.output:e.output??"",r="requestId"in e?e.requestId:e.summary.requestId;return{id:r,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${r}-response`,parts:[{kind:"text",text:a}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const a=(e??"").match(t);return a?.[1]?decodeURIComponent(a[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,a){return{jsonrpc:"2.0",id:e,error:{code:t,message:a}}}function sendJson(e,t,a){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(a))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const a of e)t.push(Buffer.isBuffer(a)?a:Buffer.from(a));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
1
+ import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const s=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,r)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(r,200,{ok:!0,protocols:[...s]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(r,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(s));if(s.has("a2a")&&await async function handleA2a(e,t,a,s){if(!function validateA2aVersion(e,t){const a=e.headers["a2a-version"],s=Array.isArray(a)?a[0]:a;return!s||"0.3"===s||"1.0"===s||(sendJson(t,400,jsonRpcError(null,-32006,"version_not_supported")),!1)}(t,a))return!0;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,pushNotifications:!1},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,s)),!0;const r=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&r){const t=e.inspectRequest(r);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\/([^/]+):subscribe$/u);if("GET"===t.method&&n)return function streamA2aTask(e,t,a){const s=e.inspectRequest(t);if(!s)return void sendJson(a,404,{error:"task_not_found"});if(writeSseHeaders(a),writeSse(a,{jsonrpc:"2.0",result:{task:toA2aTask(s)}}),function isTerminalState(e){return"completed"===e||"failed"===e||"cancelled"===e||"canceled"===e||"rejected"===e}(s.summary.state))return void a.end();const r=e.subscribe(e=>{e.requestId===t&&writeSse(a,{jsonrpc:"2.0",result:{event:toA2aEvent(e)}})});a.on("close",r)}(e,n,a),!0;const o=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&o?(e.cancel(o,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(o)}),!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 s=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(s)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const s=readString(readRecord(t.params)?.id),r=s?e.inspectRequest(s):void 0;return void sendJson(a,r?200:404,r?jsonRpcResult(t.id,toA2aTask(r)):jsonRpcError(t.id,-32004,"task_not_found"))}if("ListTasks"!==t.method){if("CancelTask"===t.method){const s=readString(readRecord(t.params)?.id);s&&e.cancel(s,"a2a_cancel");const r=s?e.inspectRequest(s):void 0;return void sendJson(a,r?200:404,r?jsonRpcResult(t.id,toA2aTask(r)):jsonRpcError(t.id,-32004,"task_not_found"))}sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found"))}else 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,r,a))return;if(s.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:["RUN_STARTED","TEXT_MESSAGE_START","TEXT_MESSAGE_CONTENT","TEXT_MESSAGE_END","CUSTOM","RUN_FINISHED","RUN_ERROR"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const s=readRuntimeRequestExtension(t),r=s?.requestId??readString(t.runId)??crypto.randomUUID(),n=s?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${r}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RUN_STARTED",timestamp:Date.now(),threadId:n,runId:r,input:{messages:t.messages,input:o}});const i=e.subscribe(e=>{e.requestId===r&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"CUSTOM",timestamp:Date.now(),name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"TOOL_CALL_START",timestamp:Date.now(),toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"TOOL_CALL_RESULT",timestamp:Date.now(),messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:JSON.stringify(e.output),role:"tool"}:{type:"CUSTOM",timestamp:Date.now(),name:`stable-harness.${e.type}`,value:e}}(e))});try{const i=await e.request(s??{input:o,requestId:r,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),d=`${r}-message`;i.output&&function writeAguiText(e,t,a){writeSse(e,{type:"TEXT_MESSAGE_START",timestamp:Date.now(),messageId:t,role:"assistant"}),writeSse(e,{type:"TEXT_MESSAGE_CONTENT",timestamp:Date.now(),messageId:t,delta:a}),writeSse(e,{type:"TEXT_MESSAGE_END",timestamp:Date.now(),messageId:t})}(a,d,i.output),writeSse(a,{type:"RUN_FINISHED",timestamp:Date.now(),threadId:n,runId:r,result:i.output})}catch(e){writeSse(a,{type:"RUN_ERROR",timestamp:Date.now(),threadId:n,runId:r,message:errorMessage(e),code:"runtime_error"})}finally{i(),a.end()}}(e,await readJson(t),a),!0)}(t,e,r))return;if(s.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,acpInitializeResult()),!0;if("POST"===t.method&&"/acp"===t.url){const s=await readJson(t),r=await handleAcpJsonRpcMessage(e,s);return r?sendJson(a,200,r):a.writeHead(204).end(),!0}return!1}(t,e,r))return;sendJson(r,404,{error:"not_found"})}catch(e){sendJson(r,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,acpInitializeResult(0,readRecord(t.params)));if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},s=readRuntimeRequestExtension(a),r=await e.request(s??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===r.state?"end_turn":"refusal",_meta:{requestId:r.requestId,output:r.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,s){const r=toA2aRuntimeRequest(t);var n;writeSseHeaders(a),writeSse(a,{jsonrpc:"2.0",id:s,result:{task:(n=r,{id:n.requestId??crypto.randomUUID(),contextId:n.sessionId,status:{state:"submitted",timestamp:(new Date).toISOString()},metadata:{agentId:n.agentId}})}});const o=e.subscribe(e=>{e.requestId===r.requestId&&writeSse(a,{jsonrpc:"2.0",id:s,result:{event:toA2aEvent(e)}})});try{const t=await e.request(r);writeSse(a,{jsonrpc:"2.0",id:s,result:{task:toA2aTask(t),final:!0}})}finally{o(),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??"",s="requestId"in e?e.requestId:e.summary.requestId;return{id:s,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${s}-response`,parts:[{kind:"text",text:a}]}:void 0,timestamp:(new Date).toISOString()},artifacts:a?[{artifactId:`${s}-output`,name:"result",parts:[{kind:"text",text:a}]}]:void 0}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t,timestamp:(new Date).toISOString()},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function acpInitializeResult(e,t){return"number"==typeof t?.protocolVersion&&t.protocolVersion,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},mcpCapabilities:{http:!1,sse:!1},session:{update:!0,cancel:!0,list:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"},authMethods:[]}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const 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.107",
3
+ "version": "0.0.109",
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.107"
13
+ "@stable-harness/core": "0.0.109"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107"
14
+ "@stable-harness/core": "0.0.109"
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107",
86
- "@stable-harness/adapter-langgraph": "0.0.107",
87
- "@stable-harness/core": "0.0.107",
88
- "@stable-harness/governance": "0.0.107",
89
- "@stable-harness/memory": "0.0.107",
90
- "@stable-harness/protocols": "0.0.107",
91
- "@stable-harness/tool-gateway": "0.0.107",
92
- "@stable-harness/workspace-yaml": "0.0.107",
85
+ "@stable-harness/adapter-deepagents": "0.0.109",
86
+ "@stable-harness/adapter-langgraph": "0.0.109",
87
+ "@stable-harness/core": "0.0.109",
88
+ "@stable-harness/governance": "0.0.109",
89
+ "@stable-harness/memory": "0.0.109",
90
+ "@stable-harness/protocols": "0.0.109",
91
+ "@stable-harness/tool-gateway": "0.0.109",
92
+ "@stable-harness/workspace-yaml": "0.0.109",
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.107",
3
+ "version": "0.0.109",
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.107",
18
+ "@stable-harness/core": "0.0.109",
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.107",
3
+ "version": "0.0.109",
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.107"
14
+ "@stable-harness/core": "0.0.109"
15
15
  }
16
16
  }
@@ -1 +1 @@
1
- import{randomUUID as t}from"node:crypto";import e from"node:path";import{formatCliRuntimeEvent as r}from"../event-view.js";export async function runRequestThroughDaemon(e){if("stable-runtime"!==e.args.clientProtocol)return await async function runRequestThroughAgentProtocol(e){if("local"===e.args.runtimeMode)throw new Error(`stable-harness protocol ${e.args.clientProtocol} requires a protocol server; use --protocol stable-runtime for local mode`);const r=function readAgentProtocol(t){if("stable-runtime"!==t)return t;throw new Error("stable-runtime is handled by the native daemon client")}(e.args.clientProtocol);if(e.args.trace||e.args.traceJson)throw new Error(`stable-harness protocol ${r} does not expose Stable Runtime traces; use --protocol stable-runtime for traces`);const o=agentProtocolBaseUrl(e.runtimePolicy,e.args.protocolUrl);if(!await async function isAgentProtocolAvailable(t,e){try{const 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
+ 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"TEXT_MESSAGE_CONTENT"!==e?.type&&"TextMessageChunk"!==e?.type||"string"!=typeof e.delta?"":e.delta}function parseSseEvent(t){const e=t.split("\n").find(t=>t.startsWith("data: "))?.slice(6);return e?JSON.parse(e):void 0}function protocolConfig(t,...e){for(const r of e){const e=readRecord(t[r]);if(e)return e}}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function configString(t){if("string"!=typeof t||!t.trim())return;const e=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return e?process.env[e[1]]??e[2]:t}function configNumber(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/cli",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107",
18
- "@stable-harness/adapter-langgraph": "0.0.107",
19
- "@stable-harness/core": "0.0.107",
20
- "@stable-harness/memory": "0.0.107",
21
- "@stable-harness/protocols": "0.0.107",
22
- "@stable-harness/tool-gateway": "0.0.107",
23
- "@stable-harness/workspace-yaml": "0.0.107"
17
+ "@stable-harness/adapter-deepagents": "0.0.109",
18
+ "@stable-harness/adapter-langgraph": "0.0.109",
19
+ "@stable-harness/core": "0.0.109",
20
+ "@stable-harness/memory": "0.0.109",
21
+ "@stable-harness/protocols": "0.0.109",
22
+ "@stable-harness/tool-gateway": "0.0.109",
23
+ "@stable-harness/workspace-yaml": "0.0.109"
24
24
  }
25
25
  }
@@ -5,5 +5,6 @@ export declare function assertExecutionContract(input: {
5
5
  requestId: string;
6
6
  sessionId: string;
7
7
  agent: WorkspaceAgent;
8
+ output?: string;
8
9
  metadata?: Record<string, unknown>;
9
10
  }): void;
@@ -1 +1 @@
1
- export function assertExecutionContract(e){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(e.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(e),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(e),function assertToolDependencies(e){const t=readExecutionContract(e.agent),n=isRecord(t.toolDependencies)?t.toolDependencies:{},r=Object.entries(n).map(([e,t])=>[e,readStringArray(t)]).filter(e=>e[1].length>0);if(0===r.length)return;const o=e.store.getRun(e.requestId),i=new Set((o?.events??[]).flatMap(readEvidenceToolId)),s=r.filter(([e])=>i.has(e)).flatMap(([e,t])=>t.filter(e=>!i.has(e)).map(t=>`${e} requires ${t}`));if(0!==s.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_tool_dependency",missingEvidenceTools:s}),new Error(`Execution contract missing required tool dependencies: ${s.join(", ")}`)}(e))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&function isToolResultEvent(e){return"deepagents.tool_execution.result"===e.eventType||"agent.tool.result"===e.phase}(e.event)&&"string"==typeof e.event.toolId&&function isSuccessfulEvidenceEvent(e){const t=function readString(e){return"string"==typeof e&&e.length>0?e:void 0}(e.controlStatus)??function readOutputStatus(e){if("string"!=typeof e)return;const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:e.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e.output);return!t||/^(?:completed|success|ok|recorded)$/iu.test(t)}(e.event)?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
1
+ import{outputUsesPriorEvidence as e,successfulEvidenceItems as t}from"./quality/event-evidence.js";export function assertExecutionContract(n){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(n.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(n),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(n),function assertRequiredEvidenceUsage(n){const r=function readRequiredEvidenceUsageTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceUsageTools)}(n.agent);if(0===r.length)return;const o=n.store.getRun(n.requestId),i=t(o?.events??[]),s=new Set(i.map(e=>e.source)),c=n.output??o?.output??"",a=r.filter(t=>!s.has(t)||!e(c,t,i));if(0!==a.length)throw n.emit({type:"runtime.execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_required_evidence_usage",missingEvidenceTools:a}),new Error(`Execution contract final output must use required evidence from: ${a.join(", ")}`)}(n),function assertToolDependencies(e){const t=readExecutionContract(e.agent),n=isRecord(t.toolDependencies)?t.toolDependencies:{},r=Object.entries(n).map(([e,t])=>[e,readStringArray(t)]).filter(e=>e[1].length>0);if(0===r.length)return;const o=e.store.getRun(e.requestId),i=new Set((o?.events??[]).flatMap(readEvidenceToolId)),s=r.filter(([e])=>i.has(e)).flatMap(([e,t])=>t.filter(e=>!i.has(e)).map(t=>`${e} requires ${t}`));if(0!==s.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_tool_dependency",missingEvidenceTools:s}),new Error(`Execution contract missing required tool dependencies: ${s.join(", ")}`)}(n))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&function isToolResultEvent(e){return"deepagents.tool_execution.result"===e.eventType||"agent.tool.result"===e.phase}(e.event)&&"string"==typeof e.event.toolId&&function isSuccessfulEvidenceEvent(e){const t=function readString(e){return"string"==typeof e&&e.length>0?e:void 0}(e.controlStatus)??function readOutputStatus(e){if("string"!=typeof e)return;const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:e.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e.output);return!t||/^(?:completed|success|ok|recorded)$/iu.test(t)}(e.event)?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
@@ -10,3 +10,4 @@ export declare function successfulEvidenceItems(events: RuntimeEvent[]): Success
10
10
  export declare function controlBlockers(events: RuntimeEvent[]): string[];
11
11
  export declare function controlGaps(events: RuntimeEvent[]): string[];
12
12
  export declare function omittedControlGaps(events: RuntimeEvent[], output: string): string[];
13
+ export declare function outputUsesPriorEvidence(output: string, source: string, items: SuccessfulEvidenceItem[]): boolean;
@@ -1 +1 @@
1
- export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return isToolResultEvent(e)&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(r)?[]:stringifyEvidence(e.evidenceOutput??e.output).filter(t=>function isUsableEvidenceOutput(t,e){return"task"!==t||!function looksLikeUnexecutedToolIntent(t){const e=t.trim();return!(e.length>4e3)&&[/\b(?:I need to|I will|I'll|I am going to|I'm going to|Let me)\s+(?:call|use|run|invoke|get|fetch|check)\b[\s\S]{0,1200}\b(?:tool|function|system_time|web_search|url_text_fetch|task)\b/iu,/(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|获取|查看)\b[\s\S]{0,1200}\b(?:工具|函数|system_time|web_search|url_text_fetch|task)\b/iu,/```(?:json)?\s*(?:system_time|web_search|url_text_fetch|task)\s*```/iu].some(t=>t.test(e))}(e)&&!function looksLikeDelegatedCommentary(t){const e=t.trim();return!(e.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(t=>t.test(e))}(e)}(r,t)).map(t=>({source:r,output:t})):[]})}export function controlBlockers(t){const e=successfulEventIndexesBySource(t);return t.flatMap((t,r)=>{const n=readAdapterEvent(t),o=readControlStatus(n),s=readControlSource(n)??"tool";return o&&isBlockerStatus(o)?isResolvedByLaterCompletion(o)&&completedAfter(e,s,r)||isResolvedByLaterAnyCompletion(o)&&completedAfterAny(e,r)?[]:[`${s}:${o}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const r=readAdapterEvent(t),n=readControlStatus(r),o=readControlSource(r)??"tool";return n&&isGapStatus(n)?e.has(o)&&isResolvedByLaterCompletion(n)?[]:[`${o}:${n}`]:[]})}export function omittedControlGaps(t,e){const r=successfulEventIndexesBySource(t),n=successfulEvidenceItems(t),o=function outputHasUnsupportedEvidenceClaims(t,e,r){const n=function evidenceCorpus(t,e){return[...e.map(t=>t.output),...t.flatMap(t=>"runtime.request.started"===t.type?[t.input??""]:[]),...t.flatMap(t=>"runtime.memory.recall.completed"===t.type?[t.context]:[])].join("\n")}(e,r).toLowerCase();return unsupportedTokens(t,n,/\b[A-Z][A-Z0-9]{1,12}-\d+\b/gu).length>0||unsupportedTokens(t,n,/\b[A-Za-z_$][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*\b/gu).filter(isCodeLikeIdentifier).length>=2}(e,t,n);return t.flatMap((t,s)=>{const u=readAdapterEvent(t),i=readControlStatus(u),a=readControlSource(u)??"tool";return i&&function isOmittedControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}(i)?isResolvedByLaterCompletion(i)&&completedAfter(r,a,s)||isResolvedByLaterAnyCompletion(i)&&completedAfterAny(r,s)||!o&&function outputUsesPriorEvidence(t,e,r){const n=t.toLowerCase();return r.filter(t=>t.source===e).some(t=>{const e=t.output.trim();return!!e&&(!!n.includes(e.slice(0,500).toLowerCase())||function matchingEvidenceTokens(t,e){const r=new Set(["status","completed","success","recorded"]);return[...new Set(e.toLowerCase().match(/[a-z0-9][a-z0-9_.-]{1,}/gu)??[])].filter(t=>!r.has(t)).filter(e=>t.includes(e)).length}(n,e)>=2)})}(e,a,n)?[]:[`${a}:${i}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){if("runtime.adapter.event"===t.type&&isRecord(t.event))return t.event;const e=t;return isRecord(e)&&isToolResultEvent(e)?e:void 0}function isSuccessfulEvidenceEvent(t){const e=readEventStatus(t);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function isResolvedByLaterAnyCompletion(t){return"task_inventory_blocked"===t}function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,r)=>{const n=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r&&isSuccessfulEvidenceEvent(e)?r:void 0}(t);n&&e.set(n,[...e.get(n)??[],r])}),e}function isToolResultEvent(t){return"deepagents.tool_execution.result"===t?.eventType||"agent.tool.result"===t?.phase||"agent.tool.result"===t?.type||"deepagents.tool_execution.result"===t?.type}function completedAfter(t,e,r){return(t.get(e)??[]).some(t=>t>r)}function completedAfterAny(t,e){return[...t.values()].some(t=>t.some(t=>t>e))}function unsupportedTokens(t,e,r){return[...new Set(t.match(r)??[])].filter(t=>!e.includes(t.toLowerCase()))}function isCodeLikeIdentifier(t){return!(t.length<8)&&(/(?:Service|Controller|Repository|Manager|Client|Resolver|Agent|Tool|Async)$/u.test(t)||/[a-z][A-Z]/u.test(t))}function readOutputStatus(t){if(isRecord(t)&&"string"==typeof t.status)return t.status;if("string"==typeof t){const e=parseJsonRecord(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}}function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}function readEventStatus(t){return readString(t?.controlStatus)??readString(t?.status)??readOutputStatus(t?.output)}function readToolSource(t){return readString(t?.toolId)??readString(t?.toolName)??readString(t?.name)}function readControlStatus(t){const e=function readInventoryRepairControlStatus(t){if("inventory.repair"===t?.phase&&"blocked"===t.status)return"task"===readInventoryRepairSource(t)?"task_inventory_blocked":"blocked"}(t);if(e)return e;const r=readEventStatus(t);return r&&isControlStatus(r)?r:findNestedControlStatus(t)}function readControlSource(t){return readToolSource(t)??readInventoryRepairSource(t)??findNestedToolSource(t)}function isControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}function findNestedControlStatus(t,e=0){if(e>5||null==t)return;if("string"==typeof t)return function readNestedStringStatus(t,e){const r=parseJsonRecord(t);if(r)return findNestedControlStatus(r,e+1);const n=t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1];return n&&isControlStatus(n)?n:function readControlToken(t){const e=t.match(/\b(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked|dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)\b/iu)?.[0];return e&&isControlStatus(e)?e:void 0}(t)}(t,e);if(Array.isArray(t))return findFirstNested(t,t=>findNestedControlStatus(t,e+1));if(!isRecord(t))return;const r=readString(t.controlStatus)??readString(t.status);if(r&&isControlStatus(r))return r;const n=readOutputStatus(t.output);return n&&isControlStatus(n)?n:findFirstNested(Object.values(t),t=>findNestedControlStatus(t,e+1))}function findNestedToolSource(t,e=0){if(!(e>5||null==t)){if("string"==typeof t)return function readToolSourceFromText(t){const e=t.match(/["'](?:toolId|toolName|name)["']\s*:\s*["']([^"']+)["']/u)?.[1];return readString(e)}(t);if(Array.isArray(t))return findFirstNested(t,t=>findNestedToolSource(t,e+1));if(isRecord(t))return(readString(t.toolId)??readString(t.toolName)??readString(t.name))||findFirstNested(Object.values(t),t=>findNestedToolSource(t,e+1))}}function findFirstNested(t,e){for(const r of t){const t=e(r);if(void 0!==t)return t}}function readInventoryRepairSource(t){const e=isRecord(t?.diagnostic)?t.diagnostic:void 0;return readString(e?.layer)}
1
+ export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return isToolResultEvent(e)&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(r)?[]:stringifyEvidence(e.evidenceOutput??e.output).filter(t=>function isUsableEvidenceOutput(t,e){return"task"!==t||!function looksLikeUnexecutedToolIntent(t){const e=t.trim();return!(e.length>4e3)&&[/\b(?:I need to|I will|I'll|I am going to|I'm going to|Let me)\s+(?:call|use|run|invoke|get|fetch|check)\b[\s\S]{0,1200}\b(?:tool|function|system_time|web_search|url_text_fetch|task)\b/iu,/(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|获取|查看)\b[\s\S]{0,1200}\b(?:工具|函数|system_time|web_search|url_text_fetch|task)\b/iu,/```(?:json)?\s*(?:system_time|web_search|url_text_fetch|task)\s*```/iu].some(t=>t.test(e))}(e)&&!function looksLikeDelegatedCommentary(t){const e=t.trim();return!(e.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(t=>t.test(e))}(e)}(r,t)).map(t=>({source:r,output:t})):[]})}export function controlBlockers(t){const e=successfulEventIndexesBySource(t);return t.flatMap((t,r)=>{const n=readAdapterEvent(t),o=readControlStatus(n),s=readControlSource(n)??"tool";return o&&isBlockerStatus(o)?isResolvedByLaterCompletion(o)&&completedAfter(e,s,r)||isResolvedByLaterAnyCompletion(o)&&completedAfterAny(e,r)?[]:[`${s}:${o}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const r=readAdapterEvent(t),n=readControlStatus(r),o=readControlSource(r)??"tool";return n&&isGapStatus(n)?e.has(o)&&isResolvedByLaterCompletion(n)?[]:[`${o}:${n}`]:[]})}export function omittedControlGaps(t,e){const r=successfulEventIndexesBySource(t),n=successfulEvidenceItems(t),o=function outputHasUnsupportedEvidenceClaims(t,e,r){const n=function evidenceCorpus(t,e){return[...e.map(t=>t.output),...t.flatMap(t=>"runtime.request.started"===t.type?[t.input??""]:[]),...t.flatMap(t=>"runtime.memory.recall.completed"===t.type?[t.context]:[])].join("\n")}(e,r).toLowerCase();return unsupportedTokens(t,n,/\b[A-Z][A-Z0-9]{1,12}-\d+\b/gu).length>0||unsupportedTokens(t,n,/\b[A-Za-z_$][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*\b/gu).filter(isCodeLikeIdentifier).length>=2}(e,t,n);return t.flatMap((t,s)=>{const u=readAdapterEvent(t),i=readControlStatus(u),a=readControlSource(u)??"tool";return i&&function isOmittedControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}(i)?isResolvedByLaterCompletion(i)&&completedAfter(r,a,s)||isResolvedByLaterAnyCompletion(i)&&completedAfterAny(r,s)||!o&&outputUsesPriorEvidence(e,a,n)?[]:[`${a}:${i}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){if("runtime.adapter.event"===t.type&&isRecord(t.event))return t.event;const e=t;return isRecord(e)&&isToolResultEvent(e)?e:void 0}function isSuccessfulEvidenceEvent(t){const e=readEventStatus(t);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function isResolvedByLaterAnyCompletion(t){return"task_inventory_blocked"===t}function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,r)=>{const n=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t),r=readToolSource(e);return isToolResultEvent(e)&&r&&isSuccessfulEvidenceEvent(e)?r:void 0}(t);n&&e.set(n,[...e.get(n)??[],r])}),e}function isToolResultEvent(t){return"deepagents.tool_execution.result"===t?.eventType||"agent.tool.result"===t?.phase||"agent.tool.result"===t?.type||"deepagents.tool_execution.result"===t?.type}function completedAfter(t,e,r){return(t.get(e)??[]).some(t=>t>r)}function completedAfterAny(t,e){return[...t.values()].some(t=>t.some(t=>t>e))}export function outputUsesPriorEvidence(t,e,r){const n=t.toLowerCase();return r.filter(t=>t.source===e).some(t=>{const e=t.output.trim();return!!e&&(!!n.includes(e.slice(0,500).toLowerCase())||function matchingEvidenceTokens(t,e){const r=new Set(["status","completed","success","recorded"]);return[...new Set(e.toLowerCase().match(/[a-z0-9][a-z0-9_.-]{1,}/gu)??[])].filter(t=>!r.has(t)).filter(e=>t.includes(e)).length}(n,e)>=2)})}function unsupportedTokens(t,e,r){return[...new Set(t.match(r)??[])].filter(t=>!e.includes(t.toLowerCase()))}function isCodeLikeIdentifier(t){return!(t.length<8)&&(/(?:Service|Controller|Repository|Manager|Client|Resolver|Agent|Tool|Async)$/u.test(t)||/[a-z][A-Z]/u.test(t))}function readOutputStatus(t){if(isRecord(t)&&"string"==typeof t.status)return t.status;if("string"==typeof t){const e=parseJsonRecord(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}}function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}function readEventStatus(t){return readString(t?.controlStatus)??readString(t?.status)??readOutputStatus(t?.output)}function readToolSource(t){return readString(t?.toolId)??readString(t?.toolName)??readString(t?.name)}function readControlStatus(t){const e=function readInventoryRepairControlStatus(t){if("inventory.repair"===t?.phase&&"blocked"===t.status)return"task"===readInventoryRepairSource(t)?"task_inventory_blocked":"blocked"}(t);if(e)return e;const r=readEventStatus(t);return r&&isControlStatus(r)?r:findNestedControlStatus(t)}function readControlSource(t){return readToolSource(t)??readInventoryRepairSource(t)??findNestedToolSource(t)}function isControlStatus(t){return isGapStatus(t)||isBlockerStatus(t)}function findNestedControlStatus(t,e=0){if(e>5||null==t)return;if("string"==typeof t)return function readNestedStringStatus(t,e){const r=parseJsonRecord(t);if(r)return findNestedControlStatus(r,e+1);const n=t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1];return n&&isControlStatus(n)?n:function readControlToken(t){const e=t.match(/\b(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input|task_inventory_blocked|dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)\b/iu)?.[0];return e&&isControlStatus(e)?e:void 0}(t)}(t,e);if(Array.isArray(t))return findFirstNested(t,t=>findNestedControlStatus(t,e+1));if(!isRecord(t))return;const r=readString(t.controlStatus)??readString(t.status);if(r&&isControlStatus(r))return r;const n=readOutputStatus(t.output);return n&&isControlStatus(n)?n:findFirstNested(Object.values(t),t=>findNestedControlStatus(t,e+1))}function findNestedToolSource(t,e=0){if(!(e>5||null==t)){if("string"==typeof t)return function readToolSourceFromText(t){const e=t.match(/["'](?:toolId|toolName|name)["']\s*:\s*["']([^"']+)["']/u)?.[1];return readString(e)}(t);if(Array.isArray(t))return findFirstNested(t,t=>findNestedToolSource(t,e+1));if(isRecord(t))return(readString(t.toolId)??readString(t.toolName)??readString(t.name))||findFirstNested(Object.values(t),t=>findNestedToolSource(t,e+1))}}function findFirstNested(t,e){for(const r of t){const t=e(r);if(void 0!==t)return t}}function readInventoryRepairSource(t){const e=isRecord(t?.diagnostic)?t.diagnostic:void 0;return readString(e?.layer)}
@@ -1 +1 @@
1
- import{controlGaps as e}from"../quality/event-evidence.js";import{omittedControlRecoveryLines as t}from"./control-omission.js";import{containsProgressOnlyToolIntent as o,progressOnlyToolIntentMessage as n}from"./progress-intent.js";import{containsRawToolResultText as r}from"./tool-result.js";export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const n=readToolCallRecovery(e.policy);if(!0!==n.enabled)return;if(containsRawToolCallText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(o(e.output,e.availableToolIds)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer was progress-only text that declared a future tool call instead of executing it.","Continue the same user request by calling the named available tool through the backend's normal structured mechanism.","If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not narrate intended future tool calls as a final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(r(e.output,e.events)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer copied an executed tool result JSON as the user-facing answer.","Continue the same user request from the existing tool evidence.","If more evidence is required, use one remaining declared tool or subagent action through the backend's normal structured mechanism.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Otherwise synthesize a human-readable final answer from the executed tool evidence.","Do not return the raw tool result JSON as the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const s=t(e);if(s)return recoverRequest(e.request,s);const a="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,n);return a?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",a.output,a.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastMissingEvidenceTools(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return readStringArray(o.missingEvidenceTools)}return[]}(e.events);return 0!==t.length?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoRawToolResultOutput(e,t,o){if(!0===readToolCallRecovery(o).enabled&&r(e,t))throw new Error(`Adapter returned raw tool result JSON as the final answer after recovery. The backend must synthesize a user-facing answer. Output preview: ${previewOutput(e)}`)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function assertNoProgressOnlyToolIntentOutput(e,t,r){if(!0===readToolCallRecovery(r).enabled&&o(e,t))throw new Error(n(e))}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(t){const o=readToolCallRecovery(t.policy);if(!0!==o.enabled||!1===o.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(t.output,o))return;const n=function latestDelegatedTaskReport(e){return e.flatMap(e=>{const t="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;if(!isToolResultEvent(t)||"task"!==t.toolId)return[];const o="string"==typeof t.output?t.output.trim():"";return function looksLikeFinalReport(e){const t=e.trim();return!(t.length<80)&&(/^#{1,3}\s+\S/mu.test(t)||/\n#{1,3}\s+\S/mu.test(t)||/\n-{3,}\n/u.test(t))}(o)?[o]:[]}).at(-1)||void 0}(t.events);if(n)return n;const r=recentToolEvidence(t.events,6e3);if(0===r.length)return;const s=/\p{Script=Han}/u.test(t.request.input)?"zh":"en",a=e(t.events);return"zh"===s?function buildChineseEvidenceSynthesis(e,t,o){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,...o.length>0?["","未解决的证据缺口:",...o.map(e=>`- ${e}`)]:[],"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a):function buildEnglishEvidenceSynthesis(e,t,o){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,...o.length>0?["","Unresolved evidence gaps:",...o.map(e=>`- ${e}`)]:[],"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a)}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*[A-Za-z_][\w.-]*\s*\([^>]{0,2000}\)\s*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Let me|I'll|I will|I am going to|I'm going to)\s+(?:start\s+by\s+)?(?:read(?:ing)?|access(?:ing)?|gather(?:ing)?|collect(?:ing)?|fetch(?:ing)?|check(?:ing)?|inspect(?:ing)?)\b[\s\S]{0,1200}\b(?:context|instructions?|workflow|pull request|PR|issue|data|evidence|details?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return isToolResultEvent(o)&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function isToolResultEvent(e){return"deepagents.tool_execution.result"===e?.eventType||"agent.tool.result"===e?.phase}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{controlGaps as e}from"../quality/event-evidence.js";import{omittedControlRecoveryLines as t}from"./control-omission.js";import{containsProgressOnlyToolIntent as o,progressOnlyToolIntentMessage as n}from"./progress-intent.js";import{containsRawToolResultText as r}from"./tool-result.js";export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const n=readToolCallRecovery(e.policy);if(!0!==n.enabled)return;if(containsRawToolCallText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(o(e.output,e.availableToolIds)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer was progress-only text that declared a future tool call instead of executing it.","Continue the same user request by calling the named available tool through the backend's normal structured mechanism.","If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not narrate intended future tool calls as a final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(r(e.output,e.events)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer copied an executed tool result JSON as the user-facing answer.","Continue the same user request from the existing tool evidence.","If more evidence is required, use one remaining declared tool or subagent action through the backend's normal structured mechanism.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Otherwise synthesize a human-readable final answer from the executed tool evidence.","Do not return the raw tool result JSON as the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,n)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const s=t(e);if(s)return recoverRequest(e.request,s);const a="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,n);return a?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",a.output,a.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastExecutionContractFailure(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return{reason:o.reason,missing:readStringArray(o.missingEvidenceTools)}}return{missing:[]}}(e.events);return 0!==t.missing.length?"missing_required_evidence_usage"===t.reason?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`The final answer did not use required executed evidence from: ${t.missing.join(", ")}`,"Synthesize from those required source(s), or call them through normal structured tool calling if their evidence is unavailable.","Do not replace required evidence with unrelated intermediate evidence, progress text, or summaries from other tools."]):recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.missing.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoRawToolResultOutput(e,t,o){if(!0===readToolCallRecovery(o).enabled&&r(e,t))throw new Error(`Adapter returned raw tool result JSON as the final answer after recovery. The backend must synthesize a user-facing answer. Output preview: ${previewOutput(e)}`)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function assertNoProgressOnlyToolIntentOutput(e,t,r){if(!0===readToolCallRecovery(r).enabled&&o(e,t))throw new Error(n(e))}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(t){const o=readToolCallRecovery(t.policy);if(!0!==o.enabled||!1===o.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(t.output,o))return;const n=function latestDelegatedTaskReport(e){return e.flatMap(e=>{const t="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;if(!isToolResultEvent(t)||"task"!==t.toolId)return[];const o="string"==typeof t.output?t.output.trim():"";return function looksLikeFinalReport(e){const t=e.trim();return!(t.length<80)&&(/^#{1,3}\s+\S/mu.test(t)||/\n#{1,3}\s+\S/mu.test(t)||/\n-{3,}\n/u.test(t))}(o)?[o]:[]}).at(-1)||void 0}(t.events);if(n)return n;const r=recentToolEvidence(t.events,6e3);if(0===r.length)return;const s=/\p{Script=Han}/u.test(t.request.input)?"zh":"en",a=e(t.events);return"zh"===s?function buildChineseEvidenceSynthesis(e,t,o){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,...o.length>0?["","未解决的证据缺口:",...o.map(e=>`- ${e}`)]:[],"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a):function buildEnglishEvidenceSynthesis(e,t,o){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,...o.length>0?["","Unresolved evidence gaps:",...o.map(e=>`- ${e}`)]:[],"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(t.output,r,a)}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*[A-Za-z_][\w.-]*\s*\([^>]{0,2000}\)\s*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Let me|I'll|I will|I am going to|I'm going to)\s+(?:start\s+by\s+)?(?:read(?:ing)?|access(?:ing)?|gather(?:ing)?|collect(?:ing)?|fetch(?:ing)?|check(?:ing)?|inspect(?:ing)?)\b[\s\S]{0,1200}\b(?:context|instructions?|workflow|pull request|PR|issue|data|evidence|details?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return isToolResultEvent(o)&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function isToolResultEvent(e){return"deepagents.tool_execution.result"===e?.eventType||"agent.tool.result"===e?.phase}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as o}from"./recovery/tool-call.js";import{recoverQualityReview as s,resolveQualityPolicy as n}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{createRuntimeAdministrationMethods as p}from"./runtime/admin/administration.js";import{runDirectToolCall as d}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as l}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as m}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as w}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as g,normalizeAdapterResult as y}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{resolveToolCallRecoveryPolicy as f}from"./runtime/recovery/tool-call-policy.js";import{createRuntimeMemoryAdministration as q}from"./runtime/admin/memory.js";import{createInMemoryRuntimeStore as R}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as v}from"./runtime/progress-narration.js";import{repairRuntimeSelection as k}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as b}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as A}from"./runtime/tool-failure.js";import{runWorkflowRequest as C}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const y=new Set,x=t.store??R(),j=g([I(t),v({options:t.progressNarration,policy:t.workspace.runtime}),b({policy:t.workspace.runtime,store:x,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);x.appendEvent(r);for(const e of y)e(r)},emit=e=>{emitBase(e),j.emitSideEffects(e,emitBase)},E=m({gateway:l({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),G={...t,toolGateway:E},T=A(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const p=t.request.requestId??e(),l=t.request.sessionId??e(),m=[],{agent:w,adapter:g}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await k({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):e.workspace.runtime.defaultAgentId,o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:p,sessionId:l,emit:e=>m.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,p,l,w)),m.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:p,sessionId:l,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await C({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:p,sessionId:l,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await d({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:p,sessionId:l,agent:w,toolFailureTracker:t.toolFailureTracker,toolGuardrails:t.input.toolGuardrails,events:t.store.getRun(p)?.events??[]});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],l=f({workspace:e.input.workspace,agent:e.agent}),m=n(e.input.workspace.runtime,e.agent),w=new Map;let g;try{g=await runAdapterOnce(e,t,e.request,p,d,w,l)}catch(a){if(!o(a,l))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),g=await runAdapterOnce(e,t,r(e.request,a,l),p,d,w,l),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}g=await i({...e,request:e.request,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),e.request,g,m),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:g});try{assertRequestExecutionContract(e)}catch(r){const o=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:l});if(!o)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),g=await runAdapterOnce(e,t,o,p,d,w,l),g=await i({...e,request:o,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),o,g,m),assertRequestExecutionContract(e),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const y=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:g,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:g,response:y}),y}({...t,adapter:g,requestId:p,sessionId:l,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,error:e})}}({input:G,capabilities:j,store:x,emit:emit,request:t,toolFailureTracker:T}),subscribe:e=>(y.add(e),()=>y.delete(e)),...w({workspace:t.workspace,store:x,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),...p({store:x,emit:emit}),...q({memory:t.memory}),cancel(e,t){const r=x.getRun(e);r&&"running"===r.state&&(x.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await j.stop(),y.clear()}}}function runRecoveredToolCall(e,t,r){return d({gateway:e.input.toolGateway,workspace:e.input.workspace,emit:e.emit,request:{...e.request,toolCall:{toolId:t,args:r}},requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,events:e.store.getRun(e.requestId)?.events??[]})}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a,o){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a,o),reviewModel:e.input.qualityReviewModel,executionEvaluatorRules:e.input.executionEvaluatorRules,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s,n){return y(await t.run({workspace:{...e.input.workspace,runtime:n},agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,executionEvaluatorRules:e.input.executionEvaluatorRules,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,o){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:o}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
1
+ import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as o}from"./recovery/tool-call.js";import{recoverQualityReview as s,resolveQualityPolicy as n}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{createRuntimeAdministrationMethods as p}from"./runtime/admin/administration.js";import{runDirectToolCall as d}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as l}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as m}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as w}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as g,normalizeAdapterResult as y}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{resolveToolCallRecoveryPolicy as f}from"./runtime/recovery/tool-call-policy.js";import{createRuntimeMemoryAdministration as q}from"./runtime/admin/memory.js";import{createInMemoryRuntimeStore as R}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as v}from"./runtime/progress-narration.js";import{repairRuntimeSelection as k}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as b}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as A}from"./runtime/tool-failure.js";import{runWorkflowRequest as C}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const y=new Set,x=t.store??R(),j=g([I(t),v({options:t.progressNarration,policy:t.workspace.runtime}),b({policy:t.workspace.runtime,store:x,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);x.appendEvent(r);for(const e of y)e(r)},emit=e=>{emitBase(e),j.emitSideEffects(e,emitBase)},E=m({gateway:l({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),G={...t,toolGateway:E},T=A(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const p=t.request.requestId??e(),l=t.request.sessionId??e(),m=[],{agent:w,adapter:g}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await k({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):e.workspace.runtime.defaultAgentId,o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:p,sessionId:l,emit:e=>m.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,p,l,w)),m.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:p,sessionId:l,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await C({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:p,sessionId:l,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await d({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:p,sessionId:l,agent:w,toolFailureTracker:t.toolFailureTracker,toolGuardrails:t.input.toolGuardrails,events:t.store.getRun(p)?.events??[]});return u({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],l=f({workspace:e.input.workspace,agent:e.agent}),m=n(e.input.workspace.runtime,e.agent),w=new Map;let g;try{g=await runAdapterOnce(e,t,e.request,p,d,w,l)}catch(a){if(!o(a,l))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),g=await runAdapterOnce(e,t,r(e.request,a,l),p,d,w,l),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}g=await i({...e,request:e.request,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),e.request,g,m),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:g});try{assertRequestExecutionContract(e,g)}catch(r){const o=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:l});if(!o)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),g=await runAdapterOnce(e,t,o,p,d,w,l),g=await i({...e,request:o,result:g,recoveryPolicy:l,workspace:e.input.workspace,toolGateway:e.input.toolGateway,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w,l),runRecoveredToolCall:(t,r)=>runRecoveredToolCall(e,t,r)}),g=await s(createQualityRuntimeInput(e,p,d,w,l),o,g,m),assertRequestExecutionContract(e,g),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const y=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:g,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:g,response:y}),y}({...t,adapter:g,requestId:p,sessionId:l,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:p,sessionId:l,agent:w,error:e})}}({input:G,capabilities:j,store:x,emit:emit,request:t,toolFailureTracker:T}),subscribe:e=>(y.add(e),()=>y.delete(e)),...w({workspace:t.workspace,store:x,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),...p({store:x,emit:emit}),...q({memory:t.memory}),cancel(e,t){const r=x.getRun(e);r&&"running"===r.state&&(x.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await j.stop(),y.clear()}}}function runRecoveredToolCall(e,t,r){return d({gateway:e.input.toolGateway,workspace:e.input.workspace,emit:e.emit,request:{...e.request,toolCall:{toolId:t,args:r}},requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,events:e.store.getRun(e.requestId)?.events??[]})}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a,o){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a,o),reviewModel:e.input.qualityReviewModel,executionEvaluatorRules:e.input.executionEvaluatorRules,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e,r){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,output:r.text,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s,n){return y(await t.run({workspace:{...e.input.workspace,runtime:n},agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,toolGuardrails:e.input.toolGuardrails,executionEvaluatorRules:e.input.executionEvaluatorRules,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,o){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:o}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107",
15
- "@stable-harness/memory": "0.0.107"
14
+ "@stable-harness/governance": "0.0.109",
15
+ "@stable-harness/memory": "0.0.109"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/evaluation",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107"
13
+ "@stable-harness/core": "0.0.109"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const r=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0,protocols:[...r]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(s,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(r));if(r.has("a2a")&&await async function handleA2a(e,t,a,r){if("GET"===t.method&&("/.well-known/agent-card.json"===t.url||"/a2a/agent-card.json"===t.url))return sendJson(a,200,function createAgentCard(e,t){const a=e.inspect();return{protocolVersion:"1.0",name:a.workspaceRoot.split(/[\\/]/u).filter(Boolean).at(-1)??"stable-harness",description:"Stable Harness runtime agent endpoint",version:"0.0.0",url:t.baseUrl?`${t.baseUrl.replace(/\/$/u,"")}/a2a`:"/a2a",preferredTransport:"HTTP+JSON",capabilities:{streaming:!0},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,r)),!0;const s=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(a,t?200:404,t?{task:toA2aTask(t)}:{error:"task_not_found"}),!0}if("GET"===t.method&&"/a2a/tasks"===t.url)return sendJson(a,200,{tasks:e.listRequests().map(toA2aTaskSummary)}),!0;const n=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&n?(e.cancel(n,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(n)}),!0):"POST"===t.method&&"/a2a/message:send"===t.url?(sendJson(a,200,{task:toA2aTask(await e.request(toA2aRuntimeRequest(await readJson(t))))}),!0):"POST"===t.method&&"/a2a/message:stream"===t.url?(await streamA2a(e,await readJson(t),a),!0):"POST"===t.method&&"/a2a/rpc"===t.url&&(await async function handleA2aRpc(e,t,a){if("SendMessage"===t.method){const r=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(r)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const r=readString(readRecord(t.params)?.id),s=r?e.inspectRequest(r):void 0;return void sendJson(a,s?200:404,s?jsonRpcResult(t.id,toA2aTask(s)):jsonRpcError(t.id,-32004,"task_not_found"))}"ListTasks"!==t.method?sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found")):sendJson(a,200,jsonRpcResult(t.id,{tasks:e.listRequests().map(toA2aTaskSummary)}))}else await streamA2a(e,readRecord(t.params)??{},a,t.id)}(e,await readJson(t),a),!0)}(t,e,s,a))return;if(r.has("agui")&&await async function handleAgui(e,t,a){return"GET"===t.method&&"/ag-ui/capabilities"===t.url?(sendJson(a,200,{protocol:"ag-ui",transports:["http+sse"],events:["RunStarted","TextMessageChunk","Custom","RunFinished","RunError"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const r=readRuntimeRequestExtension(t),s=r?.requestId??readString(t.runId)??crypto.randomUUID(),n=r?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${s}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RunStarted",threadId:n,runId:s,input:{messages:t.messages,input:o}});const d=e.subscribe(e=>{e.requestId===s&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"Custom",name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"ToolCallStart",toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"ToolCallResult",messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:e.output,role:"tool"}:{type:"Custom",name:`stable-harness.${e.type}`,value:e}}(e))});try{const d=await e.request(r??{input:o,requestId:s,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),i=`${s}-message`;d.output&&writeSse(a,{type:"TextMessageChunk",messageId:i,role:"assistant",delta:d.output}),writeSse(a,{type:"RunFinished",threadId:n,runId:s,outcome:{type:"completed"===d.state?"success":"interrupt"},result:d.output})}catch(e){writeSse(a,{type:"RunError",threadId:n,runId:s,message:errorMessage(e)})}finally{d(),a.end()}}(e,await readJson(t),a),!0)}(t,e,s))return;if(r.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}}),!0;if("POST"===t.method&&"/acp"===t.url){const r=await readJson(t),s=await handleAcpJsonRpcMessage(e,r);return s?sendJson(a,200,s):a.writeHead(204).end(),!0}return!1}(t,e,s))return;sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"}});if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},r=readRuntimeRequestExtension(a),s=await e.request(r??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===s.state?"end_turn":"refusal",_meta:{requestId:s.requestId,output:s.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,r){const s=toA2aRuntimeRequest(t);writeSseHeaders(a);const n=e.subscribe(e=>{e.requestId===s.requestId&&writeSse(a,{jsonrpc:"2.0",id:r,result:{event:toA2aEvent(e)}})});try{const t=await e.request(s);writeSse(a,{jsonrpc:"2.0",id:r,result:{task:toA2aTask(t),final:!0}})}finally{n(),a.end()}}function toA2aRuntimeRequest(e){const t=readRuntimeRequestExtension(e);if(t)return{...t,metadata:{...t.metadata,protocol:"a2a"}};const a=readRecord(e.message)??e;return{input:readPromptText(a),requestId:readString(e.requestId)??readString(a.messageId),sessionId:readString(e.taskId)??readString(a.taskId)??readString(a.contextId),agentId:readString(e.agentId)??readString(e.metadata&&readRecord(e.metadata)?.agentId),metadata:{protocol:"a2a",configuration:e.configuration,metadata:e.metadata}}}function toA2aTask(e){const t="state"in e?e.state:e.summary.state,a="output"in e?e.output:e.output??"",r="requestId"in e?e.requestId:e.summary.requestId;return{id:r,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${r}-response`,parts:[{kind:"text",text:a}]}:void 0}}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const a=(e??"").match(t);return a?.[1]?decodeURIComponent(a[1]):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function jsonRpcResult(e,t){return{jsonrpc:"2.0",id:e,result:t}}function jsonRpcError(e,t,a){return{jsonrpc:"2.0",id:e,error:{code:t,message:a}}}function sendJson(e,t,a){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(a))}function writeSseHeaders(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function writeSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}async function readJson(e){const t=[];for await(const a of e)t.push(Buffer.isBuffer(a)?a:Buffer.from(a));return t.length>0?JSON.parse(Buffer.concat(t).toString("utf8")):{}}function errorMessage(e){return e instanceof Error?e.message:String(e)}
1
+ import{createServer as e}from"node:http";export function createAgentProtocolHttpServer(t,a={}){const s=new Set(a.enabledProtocols??["acp","a2a","agui"]);return e(async(e,r)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(r,200,{ok:!0,protocols:[...s]});if("GET"===e.method&&"/capabilities"===e.url)return void sendJson(r,200,function createProtocolCapabilityManifest(e){return{protocol:"stable-harness-agent-protocols",protocols:[...e].sort().map(e=>({id:e,transports:"acp"===e?["stdio","http-jsonrpc"]:"a2a"===e?["http-json","sse"]:["http-sse"],session:"acp"===e?{level:"protocol-session",mapsTo:"sessionId",operatorApi:!1}:"a2a"===e?{level:"continuity",mapsTo:"contextId|taskId",operatorApi:!1}:{level:"continuity",mapsTo:"threadId|sessionId",operatorApi:!1}})),stableRuntime:{session:{level:"operator-api",operatorApi:!0}}}}(s));if(s.has("a2a")&&await async function handleA2a(e,t,a,s){if(!function validateA2aVersion(e,t){const a=e.headers["a2a-version"],s=Array.isArray(a)?a[0]:a;return!s||"0.3"===s||"1.0"===s||(sendJson(t,400,jsonRpcError(null,-32006,"version_not_supported")),!1)}(t,a))return!0;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,pushNotifications:!1},defaultInputModes:["text/plain"],defaultOutputModes:["text/plain"],skills:a.agents.map(e=>({id:e,name:e,description:e}))}}(e,s)),!0;const r=matchPath(t.url,/^\/a2a\/tasks\/([^/]+)$/u);if("GET"===t.method&&r){const t=e.inspectRequest(r);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\/([^/]+):subscribe$/u);if("GET"===t.method&&n)return function streamA2aTask(e,t,a){const s=e.inspectRequest(t);if(!s)return void sendJson(a,404,{error:"task_not_found"});if(writeSseHeaders(a),writeSse(a,{jsonrpc:"2.0",result:{task:toA2aTask(s)}}),function isTerminalState(e){return"completed"===e||"failed"===e||"cancelled"===e||"canceled"===e||"rejected"===e}(s.summary.state))return void a.end();const r=e.subscribe(e=>{e.requestId===t&&writeSse(a,{jsonrpc:"2.0",result:{event:toA2aEvent(e)}})});a.on("close",r)}(e,n,a),!0;const o=matchPath(t.url,/^\/a2a\/tasks\/([^/]+):cancel$/u);return"POST"===t.method&&o?(e.cancel(o,"a2a_cancel"),sendJson(a,200,{task:e.inspectRequest(o)}),!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 s=await e.request(toA2aRuntimeRequest(readRecord(t.params)??{}));return void sendJson(a,200,jsonRpcResult(t.id,{task:toA2aTask(s)}))}if("SendStreamingMessage"!==t.method){if("GetTask"===t.method){const s=readString(readRecord(t.params)?.id),r=s?e.inspectRequest(s):void 0;return void sendJson(a,r?200:404,r?jsonRpcResult(t.id,toA2aTask(r)):jsonRpcError(t.id,-32004,"task_not_found"))}if("ListTasks"!==t.method){if("CancelTask"===t.method){const s=readString(readRecord(t.params)?.id);s&&e.cancel(s,"a2a_cancel");const r=s?e.inspectRequest(s):void 0;return void sendJson(a,r?200:404,r?jsonRpcResult(t.id,toA2aTask(r)):jsonRpcError(t.id,-32004,"task_not_found"))}sendJson(a,404,jsonRpcError(t.id,-32601,"method_not_found"))}else 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,r,a))return;if(s.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:["RUN_STARTED","TEXT_MESSAGE_START","TEXT_MESSAGE_CONTENT","TEXT_MESSAGE_END","CUSTOM","RUN_FINISHED","RUN_ERROR"]}),!0):"POST"===t.method&&"/ag-ui/runs"===t.url&&(await async function streamAgui(e,t,a){const s=readRuntimeRequestExtension(t),r=s?.requestId??readString(t.runId)??crypto.randomUUID(),n=s?.sessionId??readString(t.threadId)??readString(t.sessionId)??`thread-${r}`,o=readPromptText(t);writeSseHeaders(a),writeSse(a,{type:"RUN_STARTED",timestamp:Date.now(),threadId:n,runId:r,input:{messages:t.messages,input:o}});const i=e.subscribe(e=>{e.requestId===r&&writeSse(a,function toAguiEvent(e){return"runtime.progress.narration"===e.type?{type:"CUSTOM",timestamp:Date.now(),name:"stable-harness.progress",value:e}:"runtime.tool.direct.started"===e.type?{type:"TOOL_CALL_START",timestamp:Date.now(),toolCallId:`${e.requestId}:${e.toolId}`,toolCallName:e.toolId}:"runtime.tool.direct.completed"===e.type?{type:"TOOL_CALL_RESULT",timestamp:Date.now(),messageId:`${e.requestId}-message`,toolCallId:`${e.requestId}:${e.toolId}`,content:JSON.stringify(e.output),role:"tool"}:{type:"CUSTOM",timestamp:Date.now(),name:`stable-harness.${e.type}`,value:e}}(e))});try{const i=await e.request(s??{input:o,requestId:r,sessionId:n,agentId:readString(t.agentId),metadata:{protocol:"ag-ui"}}),d=`${r}-message`;i.output&&function writeAguiText(e,t,a){writeSse(e,{type:"TEXT_MESSAGE_START",timestamp:Date.now(),messageId:t,role:"assistant"}),writeSse(e,{type:"TEXT_MESSAGE_CONTENT",timestamp:Date.now(),messageId:t,delta:a}),writeSse(e,{type:"TEXT_MESSAGE_END",timestamp:Date.now(),messageId:t})}(a,d,i.output),writeSse(a,{type:"RUN_FINISHED",timestamp:Date.now(),threadId:n,runId:r,result:i.output})}catch(e){writeSse(a,{type:"RUN_ERROR",timestamp:Date.now(),threadId:n,runId:r,message:errorMessage(e),code:"runtime_error"})}finally{i(),a.end()}}(e,await readJson(t),a),!0)}(t,e,r))return;if(s.has("acp")&&await async function handleAcp(e,t,a){if("GET"===t.method&&"/acp/capabilities"===t.url)return sendJson(a,200,acpInitializeResult()),!0;if("POST"===t.method&&"/acp"===t.url){const s=await readJson(t),r=await handleAcpJsonRpcMessage(e,s);return r?sendJson(a,200,r):a.writeHead(204).end(),!0}return!1}(t,e,r))return;sendJson(r,404,{error:"not_found"})}catch(e){sendJson(r,400,{error:errorMessage(e)})}})}export async function handleAcpJsonRpcMessage(e,t){if(!t.id&&"session/cancel"===t.method){const a=readString(readRecord(t.params)?.sessionId);return void(a&&e.listRequests({sessionId:a,state:"running"}).forEach(t=>e.cancel(t.requestId,"acp_cancel")))}if("initialize"===t.method)return jsonRpcResult(t.id,acpInitializeResult(0,readRecord(t.params)));if("session/new"===t.method)return jsonRpcResult(t.id,{sessionId:`acp-${crypto.randomUUID()}`});if("session/load"===t.method)return jsonRpcResult(t.id,{sessionId:readString(readRecord(t.params)?.sessionId)});if("session/list"===t.method)return jsonRpcResult(t.id,{sessions:e.listSessions()});if("session/prompt"===t.method){const a=readRecord(t.params)??{},s=readRuntimeRequestExtension(a),r=await e.request(s??{input:readPromptText(a),sessionId:readString(a.sessionId),agentId:readString(a.agentId),metadata:{protocol:"acp",transport:"http-jsonrpc"}});return jsonRpcResult(t.id,{stopReason:"completed"===r.state?"end_turn":"refusal",_meta:{requestId:r.requestId,output:r.output}})}return jsonRpcError(t.id,-32601,"method_not_found")}async function streamA2a(e,t,a,s){const r=toA2aRuntimeRequest(t);var n;writeSseHeaders(a),writeSse(a,{jsonrpc:"2.0",id:s,result:{task:(n=r,{id:n.requestId??crypto.randomUUID(),contextId:n.sessionId,status:{state:"submitted",timestamp:(new Date).toISOString()},metadata:{agentId:n.agentId}})}});const o=e.subscribe(e=>{e.requestId===r.requestId&&writeSse(a,{jsonrpc:"2.0",id:s,result:{event:toA2aEvent(e)}})});try{const t=await e.request(r);writeSse(a,{jsonrpc:"2.0",id:s,result:{task:toA2aTask(t),final:!0}})}finally{o(),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??"",s="requestId"in e?e.requestId:e.summary.requestId;return{id:s,contextId:"sessionId"in e?e.sessionId:e.summary.sessionId,status:{state:"completed"===t?"completed":t,message:a?{role:"agent",messageId:`${s}-response`,parts:[{kind:"text",text:a}]}:void 0,timestamp:(new Date).toISOString()},artifacts:a?[{artifactId:`${s}-output`,name:"result",parts:[{kind:"text",text:a}]}]:void 0}}function toA2aTaskSummary(e){return{id:e.requestId,contextId:e.sessionId,status:{state:e.state},metadata:{agentId:e.agentId}}}function toA2aEvent(e){const t="runtime.request.completed"===e.type?{role:"agent",messageId:`${e.requestId}-response`,parts:[{kind:"text",text:e.output}]}:void 0;return{kind:"status-update",taskId:e.requestId,contextId:e.sessionId,status:{state:(a=e.type,"runtime.request.started"===a?"working":"runtime.request.completed"===a?"completed":"runtime.request.failed"===a?"failed":"runtime.request.cancelled"===a?"canceled":"working"),message:t,timestamp:(new Date).toISOString()},metadata:{stableHarnessEvent:e}};var a}function readRuntimeRequestExtension(e){const t=readRecord(e.runtimeRequest)??readRecord(readRecord(e.metadata)?.runtimeRequest);if(t)return{input:readString(t.input)??"",...readString(t.requestId)?{requestId:readString(t.requestId)}:{},...readString(t.sessionId)?{sessionId:readString(t.sessionId)}:{},...readString(t.agentId)?{agentId:readString(t.agentId)}:{},...readToolCall(t.toolCall)?{toolCall:readToolCall(t.toolCall)}:{},...readWorkflow(t.workflow)?{workflow:readWorkflow(t.workflow)}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function readToolCall(e){const t=readRecord(e),a=readString(t?.toolId);return a?{toolId:a,args:t?.args}:void 0}function readWorkflow(e){const t=readRecord(e);if(t)return{...readString(t.workflowId)?{workflowId:readString(t.workflowId)}:{},...readString(t.routeId)?{routeId:readString(t.routeId)}:{},...void 0!==t.input?{input:t.input}:{},...readRecord(t.metadata)?{metadata:readRecord(t.metadata)}:{}}}function acpInitializeResult(e,t){return"number"==typeof t?.protocolVersion&&t.protocolVersion,{protocolVersion:1,agentCapabilities:{loadSession:!0,promptCapabilities:{embeddedContext:!0},mcpCapabilities:{http:!1,sse:!1},session:{update:!0,cancel:!0,list:!0},_meta:{transport:"http-jsonrpc"}},agentInfo:{name:"stable-harness",title:"Stable Harness",version:"0.0.0"},authMethods:[]}}function readPromptText(e){return"string"==typeof e.input?e.input:"string"==typeof e.text?e.text:"string"==typeof e.prompt?e.prompt:Array.isArray(e.parts)?e.parts.map(readPartText).filter(Boolean).join("\n"):Array.isArray(e.messages)?readPromptText(readRecord(e.messages.at(-1))??{}):Array.isArray(e.content)?e.content.map(readPartText).filter(Boolean).join("\n"):"string"==typeof e.content?e.content:""}function readPartText(e){const t=readRecord(e);return t?readString(t.text)??readString(t.content)??"":"string"==typeof e?e:""}function matchPath(e,t){const 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.107",
3
+ "version": "0.0.109",
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.107"
13
+ "@stable-harness/core": "0.0.109"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.107",
3
+ "version": "0.0.109",
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.107"
14
+ "@stable-harness/core": "0.0.109"
15
15
  }
16
16
  }