stable-harness 0.0.86 → 0.0.88

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 (38) hide show
  1. package/README.md +1 -1
  2. package/docs/guides/integration-guide.md +11 -5
  3. package/docs/protocols/http-runtime.md +51 -3
  4. package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
  5. package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
  6. package/node_modules/@stable-harness/core/dist/recovery/progress-intent.d.ts +2 -0
  7. package/node_modules/@stable-harness/core/dist/recovery/progress-intent.js +1 -0
  8. package/node_modules/@stable-harness/core/dist/recovery/tool-call.d.ts +1 -0
  9. package/node_modules/@stable-harness/core/dist/recovery/tool-call.js +1 -1
  10. package/node_modules/@stable-harness/core/dist/runtime/recovery/adapter-result.js +1 -1
  11. package/node_modules/@stable-harness/core/package.json +3 -3
  12. package/node_modules/@stable-harness/governance/package.json +1 -1
  13. package/node_modules/@stable-harness/memory/package.json +1 -1
  14. package/node_modules/@stable-harness/protocols/dist/src/http-events.d.ts +16 -0
  15. package/node_modules/@stable-harness/protocols/dist/src/http-events.js +1 -0
  16. package/node_modules/@stable-harness/protocols/dist/src/http-server.js +1 -1
  17. package/node_modules/@stable-harness/protocols/package.json +2 -2
  18. package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
  19. package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
  20. package/package.json +10 -10
  21. package/packages/adapter-deepagents/package.json +2 -2
  22. package/packages/adapter-langgraph/package.json +2 -2
  23. package/packages/cli/package.json +8 -8
  24. package/packages/core/dist/recovery/progress-intent.d.ts +2 -0
  25. package/packages/core/dist/recovery/progress-intent.js +1 -0
  26. package/packages/core/dist/recovery/tool-call.d.ts +1 -0
  27. package/packages/core/dist/recovery/tool-call.js +1 -1
  28. package/packages/core/dist/runtime/recovery/adapter-result.js +1 -1
  29. package/packages/core/package.json +3 -3
  30. package/packages/evaluation/package.json +2 -2
  31. package/packages/governance/package.json +1 -1
  32. package/packages/memory/package.json +1 -1
  33. package/packages/protocols/dist/src/http-events.d.ts +16 -0
  34. package/packages/protocols/dist/src/http-events.js +1 -0
  35. package/packages/protocols/dist/src/http-server.js +1 -1
  36. package/packages/protocols/package.json +2 -2
  37. package/packages/tool-gateway/package.json +1 -1
  38. package/packages/workspace-yaml/package.json +2 -2
package/README.md CHANGED
@@ -219,9 +219,9 @@ This is constrained repair, not silent magic:
219
219
 
220
220
  ## Protocols
221
221
 
222
+ - Stable Runtime HTTP + SSE: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
222
223
  - OpenAI-compatible facade: [docs/protocols/openai-compatible.md](docs/protocols/openai-compatible.md)
223
224
  - LangGraph-compatible facade: [docs/protocols/langgraph-compatible.md](docs/protocols/langgraph-compatible.md)
224
- - HTTP runtime protocol: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
225
225
 
226
226
  ## Documentation
227
227
 
@@ -88,13 +88,19 @@ Use this path when an existing product or evaluation harness already speaks
88
88
  OpenAI-compatible chat completions. Stable Harness still owns workspace loading,
89
89
  runtime events, tool-gateway policy, memory lifecycle, and adapter selection.
90
90
 
91
- ## HTTP Runtime Protocol
91
+ ## Stable Runtime HTTP + SSE
92
92
 
93
- Use the HTTP runtime protocol when the caller needs Stable Harness concepts
94
- directly: request IDs, sessions, traces, approvals, cancellation, and runtime
95
- inspection.
93
+ Use Stable Runtime HTTP + SSE when the caller needs Stable Harness concepts
94
+ directly: request IDs, sessions, traces, spans, approvals, cancellation,
95
+ artifacts, memory lifecycle, runtime inspection, and real-time control-plane
96
+ updates.
96
97
 
97
- See [HTTP runtime protocol](protocols/http-runtime.md).
98
+ This is the first-party protocol for Studio and product shells. OpenAI
99
+ compatible `/v1` and LangGraph-compatible servers are ecosystem facades; future
100
+ ACP, A2A, and AG-UI support should map to this protocol instead of adding new
101
+ runtime semantics.
102
+
103
+ See [Stable Runtime HTTP + SSE](../protocols/http-runtime.md).
98
104
 
99
105
  ## Backend Adapters
100
106
 
@@ -1,6 +1,14 @@
1
- # HTTP Runtime Protocol
1
+ # Stable Runtime HTTP + SSE Protocol
2
2
 
3
- The native HTTP server exposes stable runtime state and request submission.
3
+ The native Stable Runtime protocol is the product control-plane contract. It is
4
+ the source of truth for first-party clients such as Studio, operator dashboards,
5
+ and embedded product shells.
6
+
7
+ OpenAI-compatible `/v1` and LangGraph-compatible servers are ecosystem
8
+ facades. ACP, A2A, and AG-UI should be added later as adapters over this
9
+ contract, not as new runtime semantics.
10
+
11
+ ## HTTP Commands And Queries
4
12
 
5
13
  `POST /requests` is a protocol adapter over `RuntimeRequest`. It may pass these
6
14
  stable fields through to the runtime:
@@ -22,8 +30,9 @@ runtime requests.
22
30
  Inspection endpoints expose normalized runtime state:
23
31
 
24
32
  - `GET /inspect`
25
- - `GET /requests`
33
+ - `GET /requests?sessionId=...&agentId=...&state=...`
26
34
  - `GET /requests/:id`
35
+ - `POST /requests/:id/cancel`
27
36
  - `DELETE /requests/:id`
28
37
  - `GET /runs/:id/trace`
29
38
  - `GET /runs/:id/spans`
@@ -33,6 +42,12 @@ Inspection endpoints expose normalized runtime state:
33
42
  - `GET /workflows/:id/mermaid`
34
43
  - `GET /workflows/:id/plan`
35
44
 
45
+ Approval endpoints expose operator decisions:
46
+
47
+ - `GET /approvals?status=pending`
48
+ - `POST /approvals/:id/approve`
49
+ - `POST /approvals/:id/reject`
50
+
36
51
  Memory endpoints expose the native runtime memory store when one is configured:
37
52
 
38
53
  - `POST /memories`
@@ -53,3 +68,36 @@ Each span includes `spanId`, optional `parentSpanId`, `kind`, `name`, `status`,
53
68
  event references, and timing when the stored events include `emittedAt`.
54
69
  Clients should render hierarchy from `parentSpanId` instead of inferring it from
55
70
  display labels.
71
+
72
+ ## SSE Runtime Events
73
+
74
+ `GET /events` streams normalized `RuntimeEvent` records as Server-Sent Events.
75
+ It replays matching stored events first, then streams new events from
76
+ `runtime.subscribe`.
77
+
78
+ Supported filters:
79
+
80
+ - `requestId`
81
+ - `sessionId`
82
+ - `agentId`
83
+ - `types`, as comma-separated event names or prefix globs such as
84
+ `runtime.request.*,runtime.approval.*`
85
+ - `since`, as an emitted-at timestamp cursor
86
+
87
+ Example:
88
+
89
+ ```text
90
+ GET /events?sessionId=customer-123&types=runtime.request.*,runtime.approval.*
91
+ ```
92
+
93
+ Event format:
94
+
95
+ ```text
96
+ event: runtime.request.started
97
+ id: <eventId>
98
+ data: {"type":"runtime.request.started","eventId":"...","requestId":"...","sessionId":"...","agentId":"...","emittedAt":"..."}
99
+ ```
100
+
101
+ This stream is for runtime/control-plane updates. OpenAI chat streaming remains
102
+ scoped to a single `/v1/chat/completions` response and must not become the
103
+ first-party control-plane event source.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
18
+ "@stable-harness/core": "0.0.88",
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.86",
3
+ "version": "0.0.88",
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.86"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
@@ -0,0 +1,2 @@
1
+ export declare function containsProgressOnlyToolIntent(text: string, toolIds: string[] | undefined): boolean;
2
+ export declare function progressOnlyToolIntentMessage(output: string): string;
@@ -0,0 +1 @@
1
+ export function containsProgressOnlyToolIntent(e,t){const n=e.trim();return!!function looksLikeProgressOnlyCandidate(e){return e.length>0&&e.length<=1200&&!/^#{1,6}\s+\S/mu.test(e)&&!/\n-{3,}\n/u.test(e)}(n)&&(t??[]).some(e=>function futureToolIntentPattern(e){const t=function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}(e);return new RegExp(String.raw`\b(?:I(?:'ll| will| am going to|'m going to)|Now I(?:'ll| will| am going to|'m going to)|Next I(?:'ll| will| am going to|'m going to)|Let me|I need to)\s+(?:call|use|invoke|run|execute)\s+(?:the\s+)?${t}\b`,"iu")}(e).test(n))}export function progressOnlyToolIntentMessage(e){return`Adapter returned progress-only future tool intent as the final answer after recovery. The backend must execute the named tool or fail closed. Output preview: ${function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}(e)}`}
@@ -19,6 +19,7 @@ export declare function containsRawToolCallOutput(output: string, policy: unknow
19
19
  export declare function containsRecoverableResultOutput(output: string, policy: unknown): boolean;
20
20
  export declare function assertNoRawToolResultOutput(output: string, events: RuntimeEvent[], policy: unknown): void;
21
21
  export declare function assertNoToolExecutionErrorOutput(output: string, policy: unknown): void;
22
+ export declare function assertNoProgressOnlyToolIntentOutput(output: string, toolIds: string[] | undefined, policy: unknown): void;
22
23
  export declare function rawToolCallFailureMessage(): string;
23
24
  export declare function buildEvidenceSynthesisOutput(input: {
24
25
  request: RuntimeRequest;
@@ -1 +1 @@
1
- import{controlGaps as e}from"../quality/event-evidence.js";import{containsRawToolResultText as t}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 o=readToolCallRecovery(e.policy);if(!0!==o.enabled)return;if(containsRawToolCallText(e.output,o)){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(t(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,o)){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 n=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,o);return n?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",n.output,n.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,o,n){if(!0===readToolCallRecovery(n).enabled&&t(e,o))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 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",i=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,i):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,i)}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{containsProgressOnlyToolIntent as t,progressOnlyToolIntentMessage as o}from"./progress-intent.js";import{containsRawToolResultText as n}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 o=readToolCallRecovery(e.policy);if(!0!==o.enabled)return;if(containsRawToolCallText(e.output,o)){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(t(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(n(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,o)){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 r=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,o);return r?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",r.output,r.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&&n(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,n,r){if(!0===readToolCallRecovery(r).enabled&&t(e,n))throw new Error(o(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{assertNoRawToolCallOutput as e,assertNoRawToolResultOutput as t,assertNoToolExecutionErrorOutput as o,buildEvidenceSynthesisOutput as r,buildResultRecoveryRequest as a,containsRawToolCallOutput as l,rawToolCallFailureMessage as s,rawToolCallOutputPreview as i,toolCallRecoveryEnabled as u}from"../../recovery/tool-call.js";export async function recoverAdapterResultOutput(i){let n=i.result,c=i.request;const p=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(i.recoveryPolicy);let d=0;for(let e=0;e<p;e+=1){const t=i.store.getRun(i.requestId)?.events??[],o=a({request:c,output:n.text,events:t.slice(d),availableToolIds:i.agent.tools,policy:i.recoveryPolicy});if(!o)break;c=o,d=i.store.getRun(i.requestId)?.events.length??0,emitRepair(i,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(n.text,i.agent.tools)),n=await i.runAdapter(o),emitRepair(i,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(n.text,i.agent.tools))}return function finalizeRecoveredOutput(a,i){if(!u(a.recoveryPolicy))return i;let n=!1;if(l(i.text,a.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(a.request.metadata)){const e=i.text;i={...i,text:s(),metadata:{...i.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(a,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,a.agent.tools))}const c=r({request:a.request,output:i.text,events:a.store.getRun(a.requestId)?.events??[],policy:a.recoveryPolicy});return c&&(n=!0,i={...i,text:c,metadata:{...i.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(a,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),n||(l(i.text,a.recoveryPolicy)&&emitRepair(a,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(i.text,a.agent.tools)),e(i.text,a.recoveryPolicy),t(i.text,a.store.getRun(a.requestId)?.events??[],a.recoveryPolicy),o(i.text,a.recoveryPolicy)),i}(i,n)}function emitRepair(e,t,o,r,a,l,s){const i={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...s?{diagnostics:s}:{}};e.emit("runtime.repair.started"===t?{type:t,...i}:{type:t,...i,outcome:l??"retried"})}function repairDiagnostics(e,t){return{outputPreview:i(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}
1
+ import{assertNoProgressOnlyToolIntentOutput as e,assertNoRawToolCallOutput as t,assertNoRawToolResultOutput as o,assertNoToolExecutionErrorOutput as r,buildEvidenceSynthesisOutput as a,buildResultRecoveryRequest as l,containsRawToolCallOutput as s,rawToolCallFailureMessage as i,rawToolCallOutputPreview as u,toolCallRecoveryEnabled as n}from"../../recovery/tool-call.js";export async function recoverAdapterResultOutput(u){let c=u.result,p=u.request;const d=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(u.recoveryPolicy);let y=0;for(let e=0;e<d;e+=1){const t=u.store.getRun(u.requestId)?.events??[],o=l({request:p,output:c.text,events:t.slice(y),availableToolIds:u.agent.tools,policy:u.recoveryPolicy});if(!o)break;p=o,y=u.store.getRun(u.requestId)?.events.length??0,emitRepair(u,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(c.text,u.agent.tools)),c=await u.runAdapter(o),emitRepair(u,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(c.text,u.agent.tools))}return function finalizeRecoveredOutput(l,u){if(!n(l.recoveryPolicy))return u;let c=!1;if(s(u.text,l.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(l.request.metadata)){const e=u.text;u={...u,text:i(),metadata:{...u.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(l,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,l.agent.tools))}const p=a({request:l.request,output:u.text,events:l.store.getRun(l.requestId)?.events??[],policy:l.recoveryPolicy});return p&&(c=!0,u={...u,text:p,metadata:{...u.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(l,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),c||(s(u.text,l.recoveryPolicy)&&emitRepair(l,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(u.text,l.agent.tools)),t(u.text,l.recoveryPolicy),e(u.text,l.agent.tools,l.recoveryPolicy),o(u.text,l.store.getRun(l.requestId)?.events??[],l.recoveryPolicy),r(u.text,l.recoveryPolicy)),u}(u,c)}function emitRepair(e,t,o,r,a,l,s){const i={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...s?{diagnostics:s}:{}};e.emit("runtime.repair.started"===t?{type:t,...i}:{type:t,...i,outcome:l??"retried"})}function repairDiagnostics(e,t){return{outputPreview:u(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
15
- "@stable-harness/memory": "0.0.86"
14
+ "@stable-harness/governance": "0.0.88",
15
+ "@stable-harness/memory": "0.0.88"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -0,0 +1,16 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { StableHarnessRuntime } from "@stable-harness/core";
3
+ export type RuntimeEventFilter = {
4
+ requestId?: string;
5
+ sessionId?: string;
6
+ agentId?: string;
7
+ types?: string[];
8
+ since?: string;
9
+ };
10
+ export declare function readRuntimeEventFilter(url: string | undefined): RuntimeEventFilter | undefined;
11
+ export declare function streamRuntimeEvents(input: {
12
+ runtime: StableHarnessRuntime;
13
+ request: IncomingMessage;
14
+ response: ServerResponse;
15
+ filter: RuntimeEventFilter;
16
+ }): void;
@@ -0,0 +1 @@
1
+ export function readRuntimeEventFilter(e){if(!e?.startsWith("/events"))return;const t=new URL(e,"http://stable-harness.local");if("/events"!==t.pathname)return;const n=t.searchParams.get("types")?.split(",").map(e=>e.trim()).filter(Boolean);return{...readParam(t,"requestId")?{requestId:readParam(t,"requestId")}:{},...readParam(t,"sessionId")?{sessionId:readParam(t,"sessionId")}:{},...readParam(t,"agentId")?{agentId:readParam(t,"agentId")}:{},...n&&n.length>0?{types:n}:{},...readParam(t,"since")?{since:readParam(t,"since")}:{}}}export function streamRuntimeEvents(e){e.response.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"}),e.response.write(": stable-harness runtime event stream\n\n"),e.response.flushHeaders();for(const t of function replayEvents(e,t){return e.inspect().runs.flatMap(e=>e.events).filter(e=>matchesEventFilter(e,t))}(e.runtime,e.filter))writeRuntimeEvent(e.response,t);const t=e.runtime.subscribe(t=>{matchesEventFilter(t,e.filter)&&writeRuntimeEvent(e.response,t)});e.request.on("close",()=>{t(),e.response.end()})}function matchesEventFilter(e,t){return matchesValue(t.requestId,e.requestId)&&matchesValue(t.sessionId,e.sessionId)&&matchesValue(t.agentId,e.agentId)&&function matchesTypeFilter(e,t){return!t||0===t.length||t.some(t=>t.endsWith("*")?e.startsWith(t.slice(0,-1)):e===t)}(e.type,t.types)&&function matchesSince(e,t){return!t||e.eventId!==t&&Boolean(e.emittedAt&&e.emittedAt>t)}(e,t.since)}function matchesValue(e,t){return!e||e===t}function writeRuntimeEvent(e,t){e.write(`event: ${t.type}\n`),t.eventId&&e.write(`id: ${t.eventId}\n`),e.write(`data: ${JSON.stringify(t)}\n\n`)}function readParam(e,t){const n=e.searchParams.get(t);return n&&n.trim()?n:void 0}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as n,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";export function createHttpServer(a){return e((e,d)=>{!async function handleHttpRequest(e,a,d){try{await async function routeHttpRequest(e,a,d){if("GET"===a.method&&"/health"===a.url)return void sendJson(d,200,{ok:!0});if("GET"===a.method&&"/metrics"===a.url)return void function sendText(e,t,r,n="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":n}),e.end(r)}(d,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8");if("GET"===a.method&&"/inspect"===a.url)return void sendJson(d,200,e.inspect());if("GET"===a.method&&"/requests"===a.url)return void sendJson(d,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:n}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(n,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&o)return sendJson(n,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(n,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(n,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(n,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(n,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(n,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(n,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:a,response:d}))return;const i=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(a.url);if("GET"===a.method&&i)return void sendJson(d,200,e.listArtifacts(i));const u=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&u){const t=e.getArtifact(u),r=e.readArtifact(u);return void sendJson(d,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const c=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&c){const t=e.getArtifact(c);return void sendJson(d,t?200:404,t??{error:"artifact_not_found"})}const f=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(a.url);if("GET"===a.method&&f)return void sendJson(d,200,await e.listApprovals(f.status));const m=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(a.url);if("POST"===a.method&&m){const t=await e.resolveApproval(m.id,m.status);return void sendJson(d,t?200:404,t??{error:"approval_not_found"})}if("GET"===a.method&&"/workflows"===a.url)return void sendJson(d,200,e.inspect().workflows);const l=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&l){const t=e.getWorkflow(l);return void sendJson(d,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"})}const p=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&p){const r=e.getWorkflow(p);return void sendJson(d,r?200:404,r?t(r):{error:"workflow_not_found"})}const h=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&h){const t=e.inspectRequest(h);return void sendJson(d,t?200:404,t??{error:"request_not_found"})}const v=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&v){const t=e.getRun(v);return void sendJson(d,t?200:404,t?r(t):{error:"run_not_found"})}const I=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&I){const t=e.getRun(I);return void sendJson(d,t?200:404,t?n(t):{error:"run_not_found"})}const w=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&w){const t=e.exportReplayBundle(w);return void sendJson(d,t?200:404,t??{error:"run_not_found"})}if("POST"===a.method&&"/requests"===a.url){const t=await readJson(a);return void sendJson(d,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),n=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=readRecord(e.metadata);return{input:"string"==typeof e.input?e.input:"",..."string"==typeof e.agentId?{agentId:e.agentId}:{},..."string"==typeof e.sessionId?{sessionId:e.sessionId}:{},..."string"==typeof e.requestId?{requestId:e.requestId}:{},..."string"==typeof e.parentRunId?{parentRunId:e.parentRunId}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...n?{memory:n}:{},...o?{metadata:o}:{}}}(t)))}sendJson(d,404,{error:"not_found"})}(e,a,d)}catch(e){sendJson(d,500,{error:e instanceof Error?e.message:String(e)})}}(a,e,d)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
1
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as n,projectRuntimeTraceSpans as r,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";import{readRuntimeEventFilter as a,streamRuntimeEvents as d}from"./http-events.js";export function createHttpServer(u){return e((e,i)=>{!async function handleHttpRequest(e,u,i){try{await async function routeHttpRequest(e,u,i){(function handleSystemRoutes(e,t,n){return"GET"===t.method&&"/health"===t.url?(sendJson(n,200,{ok:!0}),!0):"GET"===t.method&&"/metrics"===t.url?(function sendText(e,t,n,r="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":r}),e.end(n)}(n,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8"),!0):"GET"===t.method&&"/inspect"===t.url&&(sendJson(n,200,e.inspect()),!0)})(e,u,i)||function handleEventRoutes(e,t,n){const r=a(t.url);return!("GET"!==t.method||!r||(d({runtime:e,request:t,response:n,filter:r}),0))}(e,u,i)||await async function handleAdministrationRoutes(e){const{runtime:t,request:n,response:r}=e;if("GET"===n.method&&"/sessions"===n.url)return sendJson(r,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&o)return sendJson(r,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&s)return sendJson(r,200,t.deleteRequest(s)),!0;if("GET"===n.method&&n.url?.startsWith("/memories"))return sendJson(r,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),n=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...n.length>0?{statuses:n}:{}}}(n.url))),!0;if("POST"===n.method&&"/memories"===n.url){const e=await readJson(n);return sendJson(r,200,await t.memorize(e)),!0}if("POST"===n.method&&"/memories/recall"===n.url){const e=await readJson(n);return sendJson(r,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("POST"===n.method&&a){const e=await readJson(n);return sendJson(r,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);return!("PATCH"!==n.method||!d||(sendJson(r,200,await t.updateMemory({id:d,...await readJson(n)})),0))}({runtime:e,request:u,response:i})||function handleArtifactRoutes(e,t,n){const r=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listArtifacts(r)),!0;const o=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&o){const t=e.getArtifact(o),r=e.readArtifact(o);return sendJson(n,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"}),!0}const s=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.getArtifact(s);return sendJson(n,t?200:404,t??{error:"artifact_not_found"}),!0}return!1}(e,u,i)||await async function handleApprovalRoutes(e,t,n){const r=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const n=t.searchParams.get("status");return{status:"pending"===n||"approved"===n||"rejected"===n?n:void 0}}(t.url);if("GET"===t.method&&r)return sendJson(n,200,await e.listApprovals(r.status)),!0;const o=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(t.url);if("POST"===t.method&&o){const t=await e.resolveApproval(o.id,o.status);return sendJson(n,t?200:404,t??{error:"approval_not_found"}),!0}return!1}(e,u,i)||await async function handleRequestRoutes(e,t,n){if("GET"===t.method&&"/requests"===t.url)return sendJson(n,200,e.listRequests()),!0;const r=function readRequestList(e){if(!e?.startsWith("/requests"))return;const t=new URL(e,"http://stable-harness.local");return"/requests"===t.pathname?{...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...readRunState(t.searchParams.get("state"))?{state:readRunState(t.searchParams.get("state"))}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listRequests(r)),!0;const o=function readRequestCancelId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/cancel$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("POST"===t.method&&o){const r=await readJson(t);return e.cancel(o,"string"==typeof r.reason?r.reason:void 0),sendJson(n,200,e.inspectRequest(o)??{error:"request_not_found"}),!0}if("POST"===t.method&&"/requests"===t.url){const r=await readJson(t);return sendJson(n,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),n=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),r=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=readRecord(e.metadata);return{input:"string"==typeof e.input?e.input:"",..."string"==typeof e.agentId?{agentId:e.agentId}:{},..."string"==typeof e.sessionId?{sessionId:e.sessionId}:{},..."string"==typeof e.requestId?{requestId:e.requestId}:{},..."string"==typeof e.parentRunId?{parentRunId:e.parentRunId}:{},...t?{toolCall:t}:{},...n?{workflow:n}:{},...r?{memory:r}:{},...o?{metadata:o}:{}}}(r))),!0}return!1}(e,u,i)||function handleWorkflowRoutes(e,n,r){if("GET"===n.method&&"/workflows"===n.url)return sendJson(r,200,e.inspect().workflows),!0;const o=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&o){const t=e.getWorkflow(o);return sendJson(r,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"}),!0}const a=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&a){const n=e.getWorkflow(a);return sendJson(r,n?200:404,n?t(n):{error:"workflow_not_found"}),!0}return!1}(e,u,i)||function handleRunInspectionRoutes(e,t,o){const s=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(o,t?200:404,t??{error:"request_not_found"}),!0}const a=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&a){const t=e.getRun(a);return sendJson(o,t?200:404,t?n(t):{error:"run_not_found"}),!0}const d=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&d){const t=e.getRun(d);return sendJson(o,t?200:404,t?r(t):{error:"run_not_found"}),!0}const u=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&u){const t=e.exportReplayBundle(u);return sendJson(o,t?200:404,t??{error:"run_not_found"}),!0}return!1}(e,u,i)||sendJson(i,404,{error:"not_found"})}(e,u,i)}catch(e){sendJson(i,500,{error:e instanceof Error?e.message:String(e)})}}(u,e,i)})}function readRunState(e){return"queued"===e||"running"===e||"completed"===e||"failed"===e||"cancelled"===e?e:void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,n){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}async function readJson(e){const t=[];for await(const n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
3
+ "version": "0.0.88",
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.86"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -75,21 +75,21 @@
75
75
  "packages/*"
76
76
  ],
77
77
  "dependencies": {
78
- "@easynet/better-call": "^0.1.69",
78
+ "@easynet/better-call": "^0.1.70",
79
79
  "@langchain/core": "^1.1.46",
80
80
  "@langchain/langgraph": "^1.3.0",
81
81
  "@langchain/langgraph-api": "^1.2.1",
82
82
  "@langchain/node-vfs": "^0.1.4",
83
83
  "@langchain/ollama": "^1.2.7",
84
84
  "@langchain/openai": "^1.4.5",
85
- "@stable-harness/adapter-deepagents": "0.0.86",
86
- "@stable-harness/adapter-langgraph": "0.0.86",
87
- "@stable-harness/core": "0.0.86",
88
- "@stable-harness/governance": "0.0.86",
89
- "@stable-harness/memory": "0.0.86",
90
- "@stable-harness/protocols": "0.0.86",
91
- "@stable-harness/tool-gateway": "0.0.86",
92
- "@stable-harness/workspace-yaml": "0.0.86",
85
+ "@stable-harness/adapter-deepagents": "0.0.88",
86
+ "@stable-harness/adapter-langgraph": "0.0.88",
87
+ "@stable-harness/core": "0.0.88",
88
+ "@stable-harness/governance": "0.0.88",
89
+ "@stable-harness/memory": "0.0.88",
90
+ "@stable-harness/protocols": "0.0.88",
91
+ "@stable-harness/tool-gateway": "0.0.88",
92
+ "@stable-harness/workspace-yaml": "0.0.88",
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.86",
3
+ "version": "0.0.88",
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.86",
18
+ "@stable-harness/core": "0.0.88",
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.86",
3
+ "version": "0.0.88",
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.86"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/cli",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
18
- "@stable-harness/adapter-langgraph": "0.0.86",
19
- "@stable-harness/core": "0.0.86",
20
- "@stable-harness/memory": "0.0.86",
21
- "@stable-harness/protocols": "0.0.86",
22
- "@stable-harness/tool-gateway": "0.0.86",
23
- "@stable-harness/workspace-yaml": "0.0.86"
17
+ "@stable-harness/adapter-deepagents": "0.0.88",
18
+ "@stable-harness/adapter-langgraph": "0.0.88",
19
+ "@stable-harness/core": "0.0.88",
20
+ "@stable-harness/memory": "0.0.88",
21
+ "@stable-harness/protocols": "0.0.88",
22
+ "@stable-harness/tool-gateway": "0.0.88",
23
+ "@stable-harness/workspace-yaml": "0.0.88"
24
24
  }
25
25
  }
@@ -0,0 +1,2 @@
1
+ export declare function containsProgressOnlyToolIntent(text: string, toolIds: string[] | undefined): boolean;
2
+ export declare function progressOnlyToolIntentMessage(output: string): string;
@@ -0,0 +1 @@
1
+ export function containsProgressOnlyToolIntent(e,t){const n=e.trim();return!!function looksLikeProgressOnlyCandidate(e){return e.length>0&&e.length<=1200&&!/^#{1,6}\s+\S/mu.test(e)&&!/\n-{3,}\n/u.test(e)}(n)&&(t??[]).some(e=>function futureToolIntentPattern(e){const t=function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}(e);return new RegExp(String.raw`\b(?:I(?:'ll| will| am going to|'m going to)|Now I(?:'ll| will| am going to|'m going to)|Next I(?:'ll| will| am going to|'m going to)|Let me|I need to)\s+(?:call|use|invoke|run|execute)\s+(?:the\s+)?${t}\b`,"iu")}(e).test(n))}export function progressOnlyToolIntentMessage(e){return`Adapter returned progress-only future tool intent as the final answer after recovery. The backend must execute the named tool or fail closed. Output preview: ${function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}(e)}`}
@@ -19,6 +19,7 @@ export declare function containsRawToolCallOutput(output: string, policy: unknow
19
19
  export declare function containsRecoverableResultOutput(output: string, policy: unknown): boolean;
20
20
  export declare function assertNoRawToolResultOutput(output: string, events: RuntimeEvent[], policy: unknown): void;
21
21
  export declare function assertNoToolExecutionErrorOutput(output: string, policy: unknown): void;
22
+ export declare function assertNoProgressOnlyToolIntentOutput(output: string, toolIds: string[] | undefined, policy: unknown): void;
22
23
  export declare function rawToolCallFailureMessage(): string;
23
24
  export declare function buildEvidenceSynthesisOutput(input: {
24
25
  request: RuntimeRequest;
@@ -1 +1 @@
1
- import{controlGaps as e}from"../quality/event-evidence.js";import{containsRawToolResultText as t}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 o=readToolCallRecovery(e.policy);if(!0!==o.enabled)return;if(containsRawToolCallText(e.output,o)){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(t(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,o)){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 n=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,o);return n?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",n.output,n.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,o,n){if(!0===readToolCallRecovery(n).enabled&&t(e,o))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 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",i=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,i):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,i)}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{containsProgressOnlyToolIntent as t,progressOnlyToolIntentMessage as o}from"./progress-intent.js";import{containsRawToolResultText as n}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 o=readToolCallRecovery(e.policy);if(!0!==o.enabled)return;if(containsRawToolCallText(e.output,o)){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(t(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(n(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,o)){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 r=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,o);return r?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",r.output,r.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&&n(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,n,r){if(!0===readToolCallRecovery(r).enabled&&t(e,n))throw new Error(o(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{assertNoRawToolCallOutput as e,assertNoRawToolResultOutput as t,assertNoToolExecutionErrorOutput as o,buildEvidenceSynthesisOutput as r,buildResultRecoveryRequest as a,containsRawToolCallOutput as l,rawToolCallFailureMessage as s,rawToolCallOutputPreview as i,toolCallRecoveryEnabled as u}from"../../recovery/tool-call.js";export async function recoverAdapterResultOutput(i){let n=i.result,c=i.request;const p=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(i.recoveryPolicy);let d=0;for(let e=0;e<p;e+=1){const t=i.store.getRun(i.requestId)?.events??[],o=a({request:c,output:n.text,events:t.slice(d),availableToolIds:i.agent.tools,policy:i.recoveryPolicy});if(!o)break;c=o,d=i.store.getRun(i.requestId)?.events.length??0,emitRepair(i,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(n.text,i.agent.tools)),n=await i.runAdapter(o),emitRepair(i,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(n.text,i.agent.tools))}return function finalizeRecoveredOutput(a,i){if(!u(a.recoveryPolicy))return i;let n=!1;if(l(i.text,a.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(a.request.metadata)){const e=i.text;i={...i,text:s(),metadata:{...i.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(a,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,a.agent.tools))}const c=r({request:a.request,output:i.text,events:a.store.getRun(a.requestId)?.events??[],policy:a.recoveryPolicy});return c&&(n=!0,i={...i,text:c,metadata:{...i.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(a,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),n||(l(i.text,a.recoveryPolicy)&&emitRepair(a,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(i.text,a.agent.tools)),e(i.text,a.recoveryPolicy),t(i.text,a.store.getRun(a.requestId)?.events??[],a.recoveryPolicy),o(i.text,a.recoveryPolicy)),i}(i,n)}function emitRepair(e,t,o,r,a,l,s){const i={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...s?{diagnostics:s}:{}};e.emit("runtime.repair.started"===t?{type:t,...i}:{type:t,...i,outcome:l??"retried"})}function repairDiagnostics(e,t){return{outputPreview:i(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}
1
+ import{assertNoProgressOnlyToolIntentOutput as e,assertNoRawToolCallOutput as t,assertNoRawToolResultOutput as o,assertNoToolExecutionErrorOutput as r,buildEvidenceSynthesisOutput as a,buildResultRecoveryRequest as l,containsRawToolCallOutput as s,rawToolCallFailureMessage as i,rawToolCallOutputPreview as u,toolCallRecoveryEnabled as n}from"../../recovery/tool-call.js";export async function recoverAdapterResultOutput(u){let c=u.result,p=u.request;const d=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(u.recoveryPolicy);let y=0;for(let e=0;e<d;e+=1){const t=u.store.getRun(u.requestId)?.events??[],o=l({request:p,output:c.text,events:t.slice(y),availableToolIds:u.agent.tools,policy:u.recoveryPolicy});if(!o)break;p=o,y=u.store.getRun(u.requestId)?.events.length??0,emitRepair(u,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(c.text,u.agent.tools)),c=await u.runAdapter(o),emitRepair(u,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(c.text,u.agent.tools))}return function finalizeRecoveredOutput(l,u){if(!n(l.recoveryPolicy))return u;let c=!1;if(s(u.text,l.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(l.request.metadata)){const e=u.text;u={...u,text:i(),metadata:{...u.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(l,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,l.agent.tools))}const p=a({request:l.request,output:u.text,events:l.store.getRun(l.requestId)?.events??[],policy:l.recoveryPolicy});return p&&(c=!0,u={...u,text:p,metadata:{...u.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(l,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),c||(s(u.text,l.recoveryPolicy)&&emitRepair(l,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(u.text,l.agent.tools)),t(u.text,l.recoveryPolicy),e(u.text,l.agent.tools,l.recoveryPolicy),o(u.text,l.store.getRun(l.requestId)?.events??[],l.recoveryPolicy),r(u.text,l.recoveryPolicy)),u}(u,c)}function emitRepair(e,t,o,r,a,l,s){const i={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...s?{diagnostics:s}:{}};e.emit("runtime.repair.started"===t?{type:t,...i}:{type:t,...i,outcome:l??"retried"})}function repairDiagnostics(e,t){return{outputPreview:u(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])new RegExp(`(?:^|[^A-Za-z0-9_-])${escapeRegexp(r)}(?:$|[^A-Za-z0-9_-])`,"u").test(e)&&o.add(r);return[...o]}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
15
- "@stable-harness/memory": "0.0.86"
14
+ "@stable-harness/governance": "0.0.88",
15
+ "@stable-harness/memory": "0.0.88"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/evaluation",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -0,0 +1,16 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { StableHarnessRuntime } from "@stable-harness/core";
3
+ export type RuntimeEventFilter = {
4
+ requestId?: string;
5
+ sessionId?: string;
6
+ agentId?: string;
7
+ types?: string[];
8
+ since?: string;
9
+ };
10
+ export declare function readRuntimeEventFilter(url: string | undefined): RuntimeEventFilter | undefined;
11
+ export declare function streamRuntimeEvents(input: {
12
+ runtime: StableHarnessRuntime;
13
+ request: IncomingMessage;
14
+ response: ServerResponse;
15
+ filter: RuntimeEventFilter;
16
+ }): void;
@@ -0,0 +1 @@
1
+ export function readRuntimeEventFilter(e){if(!e?.startsWith("/events"))return;const t=new URL(e,"http://stable-harness.local");if("/events"!==t.pathname)return;const n=t.searchParams.get("types")?.split(",").map(e=>e.trim()).filter(Boolean);return{...readParam(t,"requestId")?{requestId:readParam(t,"requestId")}:{},...readParam(t,"sessionId")?{sessionId:readParam(t,"sessionId")}:{},...readParam(t,"agentId")?{agentId:readParam(t,"agentId")}:{},...n&&n.length>0?{types:n}:{},...readParam(t,"since")?{since:readParam(t,"since")}:{}}}export function streamRuntimeEvents(e){e.response.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"}),e.response.write(": stable-harness runtime event stream\n\n"),e.response.flushHeaders();for(const t of function replayEvents(e,t){return e.inspect().runs.flatMap(e=>e.events).filter(e=>matchesEventFilter(e,t))}(e.runtime,e.filter))writeRuntimeEvent(e.response,t);const t=e.runtime.subscribe(t=>{matchesEventFilter(t,e.filter)&&writeRuntimeEvent(e.response,t)});e.request.on("close",()=>{t(),e.response.end()})}function matchesEventFilter(e,t){return matchesValue(t.requestId,e.requestId)&&matchesValue(t.sessionId,e.sessionId)&&matchesValue(t.agentId,e.agentId)&&function matchesTypeFilter(e,t){return!t||0===t.length||t.some(t=>t.endsWith("*")?e.startsWith(t.slice(0,-1)):e===t)}(e.type,t.types)&&function matchesSince(e,t){return!t||e.eventId!==t&&Boolean(e.emittedAt&&e.emittedAt>t)}(e,t.since)}function matchesValue(e,t){return!e||e===t}function writeRuntimeEvent(e,t){e.write(`event: ${t.type}\n`),t.eventId&&e.write(`id: ${t.eventId}\n`),e.write(`data: ${JSON.stringify(t)}\n\n`)}function readParam(e,t){const n=e.searchParams.get(t);return n&&n.trim()?n:void 0}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as n,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";export function createHttpServer(a){return e((e,d)=>{!async function handleHttpRequest(e,a,d){try{await async function routeHttpRequest(e,a,d){if("GET"===a.method&&"/health"===a.url)return void sendJson(d,200,{ok:!0});if("GET"===a.method&&"/metrics"===a.url)return void function sendText(e,t,r,n="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":n}),e.end(r)}(d,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8");if("GET"===a.method&&"/inspect"===a.url)return void sendJson(d,200,e.inspect());if("GET"===a.method&&"/requests"===a.url)return void sendJson(d,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:n}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(n,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&o)return sendJson(n,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(n,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(n,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(n,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(n,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(n,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(n,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:a,response:d}))return;const i=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(a.url);if("GET"===a.method&&i)return void sendJson(d,200,e.listArtifacts(i));const u=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&u){const t=e.getArtifact(u),r=e.readArtifact(u);return void sendJson(d,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const c=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&c){const t=e.getArtifact(c);return void sendJson(d,t?200:404,t??{error:"artifact_not_found"})}const f=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(a.url);if("GET"===a.method&&f)return void sendJson(d,200,await e.listApprovals(f.status));const m=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(a.url);if("POST"===a.method&&m){const t=await e.resolveApproval(m.id,m.status);return void sendJson(d,t?200:404,t??{error:"approval_not_found"})}if("GET"===a.method&&"/workflows"===a.url)return void sendJson(d,200,e.inspect().workflows);const l=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&l){const t=e.getWorkflow(l);return void sendJson(d,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"})}const p=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&p){const r=e.getWorkflow(p);return void sendJson(d,r?200:404,r?t(r):{error:"workflow_not_found"})}const h=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&h){const t=e.inspectRequest(h);return void sendJson(d,t?200:404,t??{error:"request_not_found"})}const v=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&v){const t=e.getRun(v);return void sendJson(d,t?200:404,t?r(t):{error:"run_not_found"})}const I=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&I){const t=e.getRun(I);return void sendJson(d,t?200:404,t?n(t):{error:"run_not_found"})}const w=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&w){const t=e.exportReplayBundle(w);return void sendJson(d,t?200:404,t??{error:"run_not_found"})}if("POST"===a.method&&"/requests"===a.url){const t=await readJson(a);return void sendJson(d,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),n=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=readRecord(e.metadata);return{input:"string"==typeof e.input?e.input:"",..."string"==typeof e.agentId?{agentId:e.agentId}:{},..."string"==typeof e.sessionId?{sessionId:e.sessionId}:{},..."string"==typeof e.requestId?{requestId:e.requestId}:{},..."string"==typeof e.parentRunId?{parentRunId:e.parentRunId}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...n?{memory:n}:{},...o?{metadata:o}:{}}}(t)))}sendJson(d,404,{error:"not_found"})}(e,a,d)}catch(e){sendJson(d,500,{error:e instanceof Error?e.message:String(e)})}}(a,e,d)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
1
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as n,projectRuntimeTraceSpans as r,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";import{readRuntimeEventFilter as a,streamRuntimeEvents as d}from"./http-events.js";export function createHttpServer(u){return e((e,i)=>{!async function handleHttpRequest(e,u,i){try{await async function routeHttpRequest(e,u,i){(function handleSystemRoutes(e,t,n){return"GET"===t.method&&"/health"===t.url?(sendJson(n,200,{ok:!0}),!0):"GET"===t.method&&"/metrics"===t.url?(function sendText(e,t,n,r="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":r}),e.end(n)}(n,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8"),!0):"GET"===t.method&&"/inspect"===t.url&&(sendJson(n,200,e.inspect()),!0)})(e,u,i)||function handleEventRoutes(e,t,n){const r=a(t.url);return!("GET"!==t.method||!r||(d({runtime:e,request:t,response:n,filter:r}),0))}(e,u,i)||await async function handleAdministrationRoutes(e){const{runtime:t,request:n,response:r}=e;if("GET"===n.method&&"/sessions"===n.url)return sendJson(r,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&o)return sendJson(r,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&s)return sendJson(r,200,t.deleteRequest(s)),!0;if("GET"===n.method&&n.url?.startsWith("/memories"))return sendJson(r,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),n=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...n.length>0?{statuses:n}:{}}}(n.url))),!0;if("POST"===n.method&&"/memories"===n.url){const e=await readJson(n);return sendJson(r,200,await t.memorize(e)),!0}if("POST"===n.method&&"/memories/recall"===n.url){const e=await readJson(n);return sendJson(r,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("POST"===n.method&&a){const e=await readJson(n);return sendJson(r,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);return!("PATCH"!==n.method||!d||(sendJson(r,200,await t.updateMemory({id:d,...await readJson(n)})),0))}({runtime:e,request:u,response:i})||function handleArtifactRoutes(e,t,n){const r=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listArtifacts(r)),!0;const o=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&o){const t=e.getArtifact(o),r=e.readArtifact(o);return sendJson(n,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"}),!0}const s=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.getArtifact(s);return sendJson(n,t?200:404,t??{error:"artifact_not_found"}),!0}return!1}(e,u,i)||await async function handleApprovalRoutes(e,t,n){const r=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const n=t.searchParams.get("status");return{status:"pending"===n||"approved"===n||"rejected"===n?n:void 0}}(t.url);if("GET"===t.method&&r)return sendJson(n,200,await e.listApprovals(r.status)),!0;const o=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(t.url);if("POST"===t.method&&o){const t=await e.resolveApproval(o.id,o.status);return sendJson(n,t?200:404,t??{error:"approval_not_found"}),!0}return!1}(e,u,i)||await async function handleRequestRoutes(e,t,n){if("GET"===t.method&&"/requests"===t.url)return sendJson(n,200,e.listRequests()),!0;const r=function readRequestList(e){if(!e?.startsWith("/requests"))return;const t=new URL(e,"http://stable-harness.local");return"/requests"===t.pathname?{...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...readRunState(t.searchParams.get("state"))?{state:readRunState(t.searchParams.get("state"))}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listRequests(r)),!0;const o=function readRequestCancelId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/cancel$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("POST"===t.method&&o){const r=await readJson(t);return e.cancel(o,"string"==typeof r.reason?r.reason:void 0),sendJson(n,200,e.inspectRequest(o)??{error:"request_not_found"}),!0}if("POST"===t.method&&"/requests"===t.url){const r=await readJson(t);return sendJson(n,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),n=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),r=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=readRecord(e.metadata);return{input:"string"==typeof e.input?e.input:"",..."string"==typeof e.agentId?{agentId:e.agentId}:{},..."string"==typeof e.sessionId?{sessionId:e.sessionId}:{},..."string"==typeof e.requestId?{requestId:e.requestId}:{},..."string"==typeof e.parentRunId?{parentRunId:e.parentRunId}:{},...t?{toolCall:t}:{},...n?{workflow:n}:{},...r?{memory:r}:{},...o?{metadata:o}:{}}}(r))),!0}return!1}(e,u,i)||function handleWorkflowRoutes(e,n,r){if("GET"===n.method&&"/workflows"===n.url)return sendJson(r,200,e.inspect().workflows),!0;const o=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&o){const t=e.getWorkflow(o);return sendJson(r,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"}),!0}const a=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&a){const n=e.getWorkflow(a);return sendJson(r,n?200:404,n?t(n):{error:"workflow_not_found"}),!0}return!1}(e,u,i)||function handleRunInspectionRoutes(e,t,o){const s=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(o,t?200:404,t??{error:"request_not_found"}),!0}const a=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&a){const t=e.getRun(a);return sendJson(o,t?200:404,t?n(t):{error:"run_not_found"}),!0}const d=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&d){const t=e.getRun(d);return sendJson(o,t?200:404,t?r(t):{error:"run_not_found"}),!0}const u=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&u){const t=e.exportReplayBundle(u);return sendJson(o,t?200:404,t??{error:"run_not_found"}),!0}return!1}(e,u,i)||sendJson(i,404,{error:"not_found"})}(e,u,i)}catch(e){sendJson(i,500,{error:e instanceof Error?e.message:String(e)})}}(u,e,i)})}function readRunState(e){return"queued"===e||"running"===e||"completed"===e||"failed"===e||"cancelled"===e?e:void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,n){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}async function readJson(e){const t=[];for await(const n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.86",
3
+ "version": "0.0.88",
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.86",
3
+ "version": "0.0.88",
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.86"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }