stable-harness 0.0.31 → 0.0.32

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.
@@ -98,7 +98,18 @@ during execution; agent-internal tool calls belong to `agent.tool.*` signals.
98
98
  | `runtime.tool.direct.started` | `runtime.tool.direct.started` | `requestId`, `sessionId`, `agentId`, `toolId` | | Direct tool request started. |
99
99
  | `runtime.tool.direct.completed` | `runtime.tool.direct.completed` | `requestId`, `sessionId`, `agentId`, `toolId`, `output` | | Direct tool request completed. |
100
100
 
101
- #### 2.1.4 Event Group: `runtime.workflow.*`
101
+ #### 2.1.4 Event Group: `runtime.approval.*`
102
+
103
+ These events are owned by the stable-harness governance control plane. They
104
+ record inspectable approval lifecycle facts; they do not ask an adapter to infer
105
+ approval from prompt text.
106
+
107
+ | Event | Event type | Required fields | Optional fields | Meaning |
108
+ | --- | --- | --- | --- | --- |
109
+ | `runtime.approval.requested` | `runtime.approval.requested` | `requestId`, `sessionId`, `agentId`, `approval` | | Governance policy requested operator approval. |
110
+ | `runtime.approval.resolved` | `runtime.approval.resolved` | `requestId`, `sessionId`, `agentId`, `approval` | | Operator approval was approved or rejected. |
111
+
112
+ #### 2.1.5 Event Group: `runtime.workflow.*`
102
113
 
103
114
  These events are owned by the stable-harness workflow runtime. They are emitted
104
115
  as top-level `runtime.workflow.*` facts, not adapter payloads; adapter payloads
@@ -110,13 +121,13 @@ only carry upstream/backend-owned workflow signals such as
110
121
  | `runtime.workflow.started` | `runtime.workflow.started` | `requestId`, `sessionId`, `agentId`, `workflowId`, `adapter` | | stable-harness workflow execution started. |
111
122
  | `runtime.workflow.completed` | `runtime.workflow.completed` | `requestId`, `sessionId`, `agentId`, `workflowId`, `adapter` | | stable-harness workflow execution completed. |
112
123
 
113
- #### 2.1.5 Event Group: `runtime.artifact.*`
124
+ #### 2.1.6 Event Group: `runtime.artifact.*`
114
125
 
115
126
  | Event | Event type | Required fields | Optional fields | Meaning |
116
127
  | --- | --- | --- | --- | --- |
117
128
  | `runtime.artifact.created` | `runtime.artifact.created` | `requestId`, `sessionId`, `agentId`, `artifact` | | Artifact was created. |
118
129
 
119
- #### 2.1.6 Event Group: `runtime.specDriven.*`
130
+ #### 2.1.7 Event Group: `runtime.specDriven.*`
120
131
 
121
132
  These events are owned by the stable-harness spec-driven workflow capability.
122
133
  They record control-plane phase facts and artifacts; they do not replace backend
@@ -129,7 +140,7 @@ agent execution semantics.
129
140
  | `runtime.specDriven.phase.completed` | `runtime.specDriven.phase.completed` | `requestId`, `sessionId`, `agentId`, `phaseId` | `workflowId`, `artifact` | Spec-driven phase completed. |
130
141
  | `runtime.specDriven.phase.verified` | `runtime.specDriven.phase.verified` | `requestId`, `sessionId`, `agentId`, `phaseId` | `workflowId`, `artifact` | Spec-driven phase was verified. |
131
142
 
132
- #### 2.1.7 Event Group: `runtime.skill.*`
143
+ #### 2.1.8 Event Group: `runtime.skill.*`
133
144
 
134
145
  | Event | Event type | Required fields | Optional fields | Meaning |
135
146
  | --- | --- | --- | --- | --- |
@@ -93,7 +93,17 @@ Runtime facts 是稳定、类型化、可审计、会写入 run record 的事实
93
93
  | `runtime.tool.direct.started` | `runtime.tool.direct.started` | `requestId`, `sessionId`, `agentId`, `toolId` | | direct tool request 开始执行。 |
94
94
  | `runtime.tool.direct.completed` | `runtime.tool.direct.completed` | `requestId`, `sessionId`, `agentId`, `toolId`, `output` | | direct tool request 执行完成。 |
95
95
 
96
- #### 2.1.4 Event Group: `runtime.workflow.*`
96
+ #### 2.1.4 Event Group: `runtime.approval.*`
97
+
98
+ 这些事件属于 stable-harness governance control plane。它们记录可检查的
99
+ approval lifecycle fact;adapter 不需要从 prompt 文本推断 approval。
100
+
101
+ | 事件 | event type | 必填字段 | 可选字段 | 含义 |
102
+ | --- | --- | --- | --- | --- |
103
+ | `runtime.approval.requested` | `runtime.approval.requested` | `requestId`, `sessionId`, `agentId`, `approval` | | governance policy 请求操作员审批。 |
104
+ | `runtime.approval.resolved` | `runtime.approval.resolved` | `requestId`, `sessionId`, `agentId`, `approval` | | 操作员审批已通过或拒绝。 |
105
+
106
+ #### 2.1.5 Event Group: `runtime.workflow.*`
97
107
 
98
108
  这些事件的 owner 是 stable-harness workflow runtime。它们作为顶层
99
109
  `runtime.workflow.*` fact 发出,不再通过 adapter payload 表达;adapter payload
@@ -104,13 +114,13 @@ Runtime facts 是稳定、类型化、可审计、会写入 run record 的事实
104
114
  | `runtime.workflow.started` | `runtime.workflow.started` | `requestId`, `sessionId`, `agentId`, `workflowId`, `adapter` | | stable-harness workflow execution 开始。 |
105
115
  | `runtime.workflow.completed` | `runtime.workflow.completed` | `requestId`, `sessionId`, `agentId`, `workflowId`, `adapter` | | stable-harness workflow execution 完成。 |
106
116
 
107
- #### 2.1.5 Event Group: `runtime.artifact.*`
117
+ #### 2.1.6 Event Group: `runtime.artifact.*`
108
118
 
109
119
  | 事件 | event type | 必填字段 | 可选字段 | 含义 |
110
120
  | --- | --- | --- | --- | --- |
111
121
  | `runtime.artifact.created` | `runtime.artifact.created` | `requestId`, `sessionId`, `agentId`, `artifact` | | artifact 被创建。 |
112
122
 
113
- #### 2.1.6 Event Group: `runtime.specDriven.*`
123
+ #### 2.1.7 Event Group: `runtime.specDriven.*`
114
124
 
115
125
  这些事件属于 stable-harness 的 spec-driven workflow runtime capability。它们记录
116
126
  control-plane 阶段事实和产物,不替代 backend agent execution semantics。
@@ -122,7 +132,7 @@ control-plane 阶段事实和产物,不替代 backend agent execution semantic
122
132
  | `runtime.specDriven.phase.completed` | `runtime.specDriven.phase.completed` | `requestId`, `sessionId`, `agentId`, `phaseId` | `workflowId`, `artifact` | spec-driven 阶段完成。 |
123
133
  | `runtime.specDriven.phase.verified` | `runtime.specDriven.phase.verified` | `requestId`, `sessionId`, `agentId`, `phaseId` | `workflowId`, `artifact` | spec-driven 阶段完成验证。 |
124
134
 
125
- #### 2.1.7 Event Group: `runtime.skill.*`
135
+ #### 2.1.8 Event Group: `runtime.skill.*`
126
136
 
127
137
  | 事件 | event type | 必填字段 | 可选字段 | 含义 |
128
138
  | --- | --- | --- | --- | --- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -1 +1 @@
1
- import{ToolMessage as e}from"@langchain/core/messages";import{tool as t}from"@langchain/core/tools";import{isSuccessfulEvidenceOutput as o,observedToolEvidence as r,recordObservedToolEvidence as n}from"./gateway/tool-evidence.js";import{emitStructuredToolFailure as s}from"./gateway/tool-failure-events.js";export function buildGatewayTools(o,n,a,i,l=createToolRepeatState(o.workspace.runtime.toolGateway)){return o.toolGateway?a.flatMap(a=>{const u=o.toolGateway?.get(a);if(!u)return[];const c=o.workspace.tools.get(a),d=c?.schema??u.schema;return[t(async t=>async function invokeGuardedGatewayTool(t){emitToolResult(t.input,t.agentId,t.toolId,void 0);const o=function missingRequiredPlanContent(e,t){const o=readRecord(e.agent.config.executionContract);if(!0!==o?.requiresPlan)return"";const n=readStringArray(o.planEvidenceTools);if(0===n.length||n.includes(t))return"";const s=new Set(r(e).map(e=>e.toolId));return n.some(e=>s.has(e))?"":["Status: plan_required",`Evidence tool: ${t}`,`Blocker: execution contract requires a planning checkpoint from one of: ${n.join(", ")} before evidence tools run.`,"Instruction: call the planning tool first, then retry this atomic evidence tool with repaired arguments."].join("\n")}(t.input,t.toolId);if(o)return emitToolResult(t.input,t.agentId,t.toolId,o),o;const n=function missingToolDependencyContent(e,t){const o=readRecord(e.agent.config.executionContract),n=readStringArray(readRecord(o.toolDependencies)[t]);if(0===n.length)return"";const s=new Set(r(e).map(e=>e.toolId)),a=n.filter(e=>!s.has(e));return 0===a.length?"":["Status: dependency_required",`Evidence tool: ${t}`,`Blocker: this atomic evidence tool requires completed dependency evidence from: ${a.join(", ")}.`,"Instruction: complete the dependency tool first, evaluate it, then retry this atomic evidence tool."].join("\n")}(t.input,t.toolId);if(n)return emitToolResult(t.input,t.agentId,t.toolId,n),n;const a=t.repeatState?beforeToolInvoke(t.toolId,t.args,t.repeatState):void 0;if(a)return emitToolResult(t.input,t.agentId,t.toolId,a.eventOutput),a.modelOutput;const i=await async function invokeGatewayTool(t,o,r,n,a){try{if(t.toolFailureTracker?.isCircuitOpen(r))throw new Error(`Tool circuit is open: ${r}`);const e=await t.toolGateway.invoke({toolId:r,args:n,repairModel:a,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:o,requestInput:t.request.input,observedEvidence:formatObservedEvidenceForToolContext(t)}});return t.toolFailureTracker?.recordSuccess(r),e}catch(n){if(s(t,o,r,n),function isToolArgumentValidationError(e){return e instanceof Error&&"ToolArgumentValidationError"===e.name&&"string"==typeof e.toolId&&Array.isArray(e.issues)}(n))return new e({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:formatToolArgumentError(n)});if(t.workspace.runtime.retry?.tools?.enabled)throw n;return new e({tool_call_id:`stable-harness-${r}-execution-error`,name:r,status:"error",content:JSON.stringify({error:"tool_execution_failed",toolId:r,message:formatError(n),retry:"Use the error as evidence, adjust the tool arguments if possible, or return a final answer with the blocker."})})}}(t.input,t.agentId,t.toolId,t.args,t.repairModel),l=i instanceof e?String(i.content):stringifyDeepAgentResult(i.output),u=t.repeatState?afterToolInvoke(t.toolId,t.args,l,i,t.repeatState):{};return emitToolResult(t.input,t.agentId,t.toolId,u.eventOutput??l),void 0!==u.modelOutput?u.modelOutput:i instanceof e?i:l}({input:o,agentId:n,toolId:a,args:t,repairModel:i,repeatState:l}),{name:a,description:buildToolDescription(c?.description??u.description??a,d,o.workspace.runtime.toolGateway,a),schema:{type:"object",additionalProperties:!0}})]}):[]}export function createToolRepeatState(e){if(function repeatGuardEnabled(e){return!0===repeatGuardConfig(e).enabled}(e))return{successfulCalls:new Map,duplicateCallCounts:new Map,latestSuccessfulOutputByTool:new Map,successfulToolCounts:new Map,toolCallCounts:new Map,maxDuplicateCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxDuplicateCallsPerTool)??3,maxCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxCallsPerTool),maxSuccessfulCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxSuccessfulCallsPerTool),maxCallsByTool:readPositiveIntegerMap(repeatGuardConfig(e).maxCallsByTool),maxSuccessfulCallsByTool:readPositiveIntegerMap(repeatGuardConfig(e).maxSuccessfulCallsByTool),returnPreviousOutputOnRepeatLimit:!0===repeatGuardConfig(e).returnPreviousOutputOnRepeatLimit}}function repeatGuardConfig(e){return isRecord(e)&&isRecord(e.repeatGuard)?e.repeatGuard:{}}function readPositiveInteger(e){return"number"==typeof e&&Number.isInteger(e)&&e>0?e:void 0}function readPositiveIntegerMap(e){return isRecord(e)?new Map(Object.entries(e).map(([e,t])=>[e,readPositiveInteger(t)]).filter(e=>void 0!==e[1])):new Map}export function beforeToolInvoke(e,t,o){const r=o.toolCallCounts.get(e)??0;o.toolCallCounts.set(e,r+1);const n=o.maxCallsByTool.get(e)??o.maxCallsPerTool;if(void 0!==n&&r>=n){const t=o.latestSuccessfulOutputByTool.get(e),r=repeatedToolCallLimitContent(e,t);return{eventOutput:r,modelOutput:repeatLimitModelOutput(r,t,o)}}const s=o.maxSuccessfulCallsByTool.get(e)??o.maxSuccessfulCallsPerTool;if(void 0!==s&&(o.successfulToolCounts.get(e)??0)>=s){const t=o.latestSuccessfulOutputByTool.get(e),r=repeatedToolCallLimitContent(e,t);return{eventOutput:r,modelOutput:repeatLimitModelOutput(r,t,o)}}const a=stableToolCallKey(e,t),i=o.successfulCalls.get(a);if(void 0!==i){const t=o.duplicateCallCounts.get(a)??0;if(o.duplicateCallCounts.set(a,t+1),void 0!==o.maxDuplicateCallsPerTool&&t>=o.maxDuplicateCallsPerTool){const t=repeatedToolCallLimitContent(e);return{eventOutput:t,modelOutput:t}}const r=function duplicateToolCallContent(e,t){return JSON.stringify({status:"duplicate_tool_call",toolId:e,instruction:"This agent already completed an equivalent tool call. Use the prior evidence instead of calling the tool again.",previousOutput:t})}(e,i);return{eventOutput:r,modelOutput:i}}}function repeatLimitModelOutput(e,t,o){return o.returnPreviousOutputOnRepeatLimit&&void 0!==t&&0!==t.trim().length?t:e}export function isToolRepeatLimitReached(e,t){if(!t)return!1;const o=t.maxCallsByTool.get(e)??t.maxCallsPerTool;if(void 0!==o&&(t.toolCallCounts.get(e)??0)>=o)return!0;const r=t.maxSuccessfulCallsByTool.get(e)??t.maxSuccessfulCallsPerTool;return void 0!==r&&(t.successfulToolCounts.get(e)??0)>=r}export function afterToolInvoke(t,r,n,s,a){return s instanceof e&&"error"===s.status?{}:o(n)?(a.successfulCalls.set(stableToolCallKey(t,r),n),a.latestSuccessfulOutputByTool.set(t,n),a.successfulToolCounts.set(t,(a.successfulToolCounts.get(t)??0)+1),{}):{}}function emitToolResult(e,t,o,r){void 0!==r&&n(e,t,o,r),e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:t,event:void 0===r?{adapter:"deepagents",phase:"agent.tool.start",toolId:o}:{adapter:"deepagents",phase:"agent.tool.result",toolId:o,output:previewToolOutput(r),...toolControlProjection(r)}})}function repeatedToolCallLimitContent(e,t){return JSON.stringify({status:"repeated_tool_call_limit",toolId:e,instruction:"This tool reached the configured repeat limit for this request. Do not call this tool or a substitute tool for the same evidence need again. Use previousOutput and the collected evidence to produce the final answer now, or report the remaining gap explicitly.",...void 0!==t?{previousOutput:t}:{}})}export function stringifyDeepAgentResult(t){if(t instanceof e)return function stringifyToolMessageContent(e){return"string"==typeof e?e:JSON.stringify(e)}(t.content);if("string"==typeof t)return t;if(isRecord(t)){const e=t.structuredResponse??t.structured_response;if(void 0!==e)return"string"==typeof e?e:JSON.stringify(e);const o=(Array.isArray(t.messages)?t.messages:[]).at(-1);if(isRecord(o)&&"string"==typeof o.content)return o.content;const r=(isRecord(t.update)&&Array.isArray(t.update.messages)?t.update.messages:[]).at(-1);if(isRecord(r)&&isRecord(r.kwargs)&&"string"==typeof r.kwargs.content)return r.kwargs.content;if(isRecord(r)&&"string"==typeof r.content)return r.content}return JSON.stringify(t)}function buildToolDescription(e,t,o,r){const n=function toolRepeatPolicyDescription(e,t){const o=repeatGuardConfig(e),r=readPositiveIntegerMap(o.maxSuccessfulCallsByTool).get(t)??readPositiveInteger(o.maxSuccessfulCallsPerTool);return void 0===r?"":`Stable runtime repeat policy: call this tool at most ${r} successful time(s) for this request. If more detail is needed, include the dimensions in the first call and synthesize after the result returns.`}(o,r),s=n?`${e}\n\n${n}`:e;return t?`${s}\n\nStable tool input schema:\n${previewToolOutput(JSON.stringify(t))}`:s}function previewToolOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>500?`${t.slice(0,497)}...`:t}export function toolControlProjection(e){const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);if("string"==typeof t?.status)return{controlStatus:t.status};const o=function readTextStatus(e){return String(e).match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e);return o?{controlStatus:o}:"string"==typeof t?.error?{controlStatus:t.error}:e.startsWith("Task delegation target is not in the workspace inventory")?{controlStatus:"task_inventory_blocked"}:{}}function stableToolCallKey(e,t){return`${e}:${stableJson(t)}`}function stableJson(e){return Array.isArray(e)?`[${e.map(stableJson).join(",")}]`:isRecord(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${stableJson(e[t])}`).join(",")}}`:JSON.stringify(e)}function formatObservedEvidenceForToolContext(e){const t=r(e).map(e=>`Tool: ${e.toolId}\n${e.output}`).join("\n\n---\n\n");return t.length>12e3?`${t.slice(0,12e3)}\n[truncated]`:t}function formatToolArgumentError(e){return JSON.stringify({error:"tool_argument_validation_failed",toolId:e.toolId,issues:e.issues,retry:"Call the same tool again with arguments that satisfy the reported schema and semantic issues."})}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readRecord(e){return isRecord(e)?e:{}}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function formatError(e){return e instanceof Error?e.message:String(e)}
1
+ import{ToolMessage as e}from"@langchain/core/messages";import{tool as t}from"@langchain/core/tools";import{isSuccessfulEvidenceOutput as o,observedToolEvidence as r,recordObservedToolEvidence as n}from"./gateway/tool-evidence.js";import{emitStructuredToolFailure as s}from"./gateway/tool-failure-events.js";export function buildGatewayTools(o,n,a,i,l=createToolRepeatState(o.workspace.runtime.toolGateway)){return o.toolGateway?a.flatMap(a=>{const u=o.toolGateway?.get(a);if(!u)return[];const c=o.workspace.tools.get(a),d=c?.schema??u.schema;return[t(async t=>async function invokeGuardedGatewayTool(t){emitToolResult(t.input,t.agentId,t.toolId,void 0);const o=function missingRequiredPlanContent(e,t){const o=readRecord(e.agent.config.executionContract);if(!0!==o?.requiresPlan)return"";const n=readStringArray(o.planEvidenceTools);if(0===n.length||n.includes(t))return"";const s=new Set(r(e).map(e=>e.toolId));return n.some(e=>s.has(e))?"":["Status: plan_required",`Evidence tool: ${t}`,`Blocker: execution contract requires a planning checkpoint from one of: ${n.join(", ")} before evidence tools run.`,"Instruction: call the planning tool first, then retry this atomic evidence tool with repaired arguments."].join("\n")}(t.input,t.toolId);if(o)return emitToolResult(t.input,t.agentId,t.toolId,o),o;const n=function missingToolDependencyContent(e,t){const o=readRecord(e.agent.config.executionContract),n=readStringArray(readRecord(o.toolDependencies)[t]);if(0===n.length)return"";const s=new Set(r(e).map(e=>e.toolId)),a=n.filter(e=>!s.has(e));return 0===a.length?"":["Status: dependency_required",`Evidence tool: ${t}`,`Blocker: this atomic evidence tool requires completed dependency evidence from: ${a.join(", ")}.`,"Instruction: complete the dependency tool first, evaluate it, then retry this atomic evidence tool."].join("\n")}(t.input,t.toolId);if(n)return emitToolResult(t.input,t.agentId,t.toolId,n),n;const a=t.repeatState?beforeToolInvoke(t.toolId,t.args,t.repeatState):void 0;if(a)return emitToolResult(t.input,t.agentId,t.toolId,a.eventOutput),a.modelOutput;const i=await async function invokeGatewayTool(t,o,r,n,a){try{if(t.toolFailureTracker?.isCircuitOpen(r))throw new Error(`Tool circuit is open: ${r}`);const e=await t.toolGateway.invoke({toolId:r,args:n,repairModel:a,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:o,requestInput:t.request.input,observedEvidence:formatObservedEvidenceForToolContext(t),approvalIds:readApprovalIds(t.request.metadata)}});return t.toolFailureTracker?.recordSuccess(r),e}catch(n){if(s(t,o,r,n),function isToolArgumentValidationError(e){return e instanceof Error&&"ToolArgumentValidationError"===e.name&&"string"==typeof e.toolId&&Array.isArray(e.issues)}(n))return new e({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:formatToolArgumentError(n)});if(t.workspace.runtime.retry?.tools?.enabled)throw n;return new e({tool_call_id:`stable-harness-${r}-execution-error`,name:r,status:"error",content:JSON.stringify({error:"tool_execution_failed",toolId:r,message:formatError(n),retry:"Use the error as evidence, adjust the tool arguments if possible, or return a final answer with the blocker."})})}}(t.input,t.agentId,t.toolId,t.args,t.repairModel),l=i instanceof e?String(i.content):stringifyDeepAgentResult(i.output),u=t.repeatState?afterToolInvoke(t.toolId,t.args,l,i,t.repeatState):{};return emitToolResult(t.input,t.agentId,t.toolId,u.eventOutput??l),void 0!==u.modelOutput?u.modelOutput:i instanceof e?i:l}({input:o,agentId:n,toolId:a,args:t,repairModel:i,repeatState:l}),{name:a,description:buildToolDescription(c?.description??u.description??a,d,o.workspace.runtime.toolGateway,a),schema:{type:"object",additionalProperties:!0}})]}):[]}export function createToolRepeatState(e){if(function repeatGuardEnabled(e){return!0===repeatGuardConfig(e).enabled}(e))return{successfulCalls:new Map,duplicateCallCounts:new Map,latestSuccessfulOutputByTool:new Map,successfulToolCounts:new Map,toolCallCounts:new Map,maxDuplicateCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxDuplicateCallsPerTool)??3,maxCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxCallsPerTool),maxSuccessfulCallsPerTool:readPositiveInteger(repeatGuardConfig(e).maxSuccessfulCallsPerTool),maxCallsByTool:readPositiveIntegerMap(repeatGuardConfig(e).maxCallsByTool),maxSuccessfulCallsByTool:readPositiveIntegerMap(repeatGuardConfig(e).maxSuccessfulCallsByTool),returnPreviousOutputOnRepeatLimit:!0===repeatGuardConfig(e).returnPreviousOutputOnRepeatLimit}}function repeatGuardConfig(e){return isRecord(e)&&isRecord(e.repeatGuard)?e.repeatGuard:{}}function readPositiveInteger(e){return"number"==typeof e&&Number.isInteger(e)&&e>0?e:void 0}function readPositiveIntegerMap(e){return isRecord(e)?new Map(Object.entries(e).map(([e,t])=>[e,readPositiveInteger(t)]).filter(e=>void 0!==e[1])):new Map}export function beforeToolInvoke(e,t,o){const r=o.toolCallCounts.get(e)??0;o.toolCallCounts.set(e,r+1);const n=o.maxCallsByTool.get(e)??o.maxCallsPerTool;if(void 0!==n&&r>=n){const t=o.latestSuccessfulOutputByTool.get(e),r=repeatedToolCallLimitContent(e,t);return{eventOutput:r,modelOutput:repeatLimitModelOutput(r,t,o)}}const s=o.maxSuccessfulCallsByTool.get(e)??o.maxSuccessfulCallsPerTool;if(void 0!==s&&(o.successfulToolCounts.get(e)??0)>=s){const t=o.latestSuccessfulOutputByTool.get(e),r=repeatedToolCallLimitContent(e,t);return{eventOutput:r,modelOutput:repeatLimitModelOutput(r,t,o)}}const a=stableToolCallKey(e,t),i=o.successfulCalls.get(a);if(void 0!==i){const t=o.duplicateCallCounts.get(a)??0;if(o.duplicateCallCounts.set(a,t+1),void 0!==o.maxDuplicateCallsPerTool&&t>=o.maxDuplicateCallsPerTool){const t=repeatedToolCallLimitContent(e);return{eventOutput:t,modelOutput:t}}const r=function duplicateToolCallContent(e,t){return JSON.stringify({status:"duplicate_tool_call",toolId:e,instruction:"This agent already completed an equivalent tool call. Use the prior evidence instead of calling the tool again.",previousOutput:t})}(e,i);return{eventOutput:r,modelOutput:i}}}function repeatLimitModelOutput(e,t,o){return o.returnPreviousOutputOnRepeatLimit&&void 0!==t&&0!==t.trim().length?t:e}export function isToolRepeatLimitReached(e,t){if(!t)return!1;const o=t.maxCallsByTool.get(e)??t.maxCallsPerTool;if(void 0!==o&&(t.toolCallCounts.get(e)??0)>=o)return!0;const r=t.maxSuccessfulCallsByTool.get(e)??t.maxSuccessfulCallsPerTool;return void 0!==r&&(t.successfulToolCounts.get(e)??0)>=r}export function afterToolInvoke(t,r,n,s,a){return s instanceof e&&"error"===s.status?{}:o(n)?(a.successfulCalls.set(stableToolCallKey(t,r),n),a.latestSuccessfulOutputByTool.set(t,n),a.successfulToolCounts.set(t,(a.successfulToolCounts.get(t)??0)+1),{}):{}}function emitToolResult(e,t,o,r){void 0!==r&&n(e,t,o,r),e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:t,event:void 0===r?{adapter:"deepagents",phase:"agent.tool.start",toolId:o}:{adapter:"deepagents",phase:"agent.tool.result",toolId:o,output:previewToolOutput(r),...toolControlProjection(r)}})}function repeatedToolCallLimitContent(e,t){return JSON.stringify({status:"repeated_tool_call_limit",toolId:e,instruction:"This tool reached the configured repeat limit for this request. Do not call this tool or a substitute tool for the same evidence need again. Use previousOutput and the collected evidence to produce the final answer now, or report the remaining gap explicitly.",...void 0!==t?{previousOutput:t}:{}})}export function stringifyDeepAgentResult(t){if(t instanceof e)return function stringifyToolMessageContent(e){return"string"==typeof e?e:JSON.stringify(e)}(t.content);if("string"==typeof t)return t;if(isRecord(t)){const e=t.structuredResponse??t.structured_response;if(void 0!==e)return"string"==typeof e?e:JSON.stringify(e);const o=(Array.isArray(t.messages)?t.messages:[]).at(-1);if(isRecord(o)&&"string"==typeof o.content)return o.content;const r=(isRecord(t.update)&&Array.isArray(t.update.messages)?t.update.messages:[]).at(-1);if(isRecord(r)&&isRecord(r.kwargs)&&"string"==typeof r.kwargs.content)return r.kwargs.content;if(isRecord(r)&&"string"==typeof r.content)return r.content}return JSON.stringify(t)}function buildToolDescription(e,t,o,r){const n=function toolRepeatPolicyDescription(e,t){const o=repeatGuardConfig(e),r=readPositiveIntegerMap(o.maxSuccessfulCallsByTool).get(t)??readPositiveInteger(o.maxSuccessfulCallsPerTool);return void 0===r?"":`Stable runtime repeat policy: call this tool at most ${r} successful time(s) for this request. If more detail is needed, include the dimensions in the first call and synthesize after the result returns.`}(o,r),s=n?`${e}\n\n${n}`:e;return t?`${s}\n\nStable tool input schema:\n${previewToolOutput(JSON.stringify(t))}`:s}function previewToolOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>500?`${t.slice(0,497)}...`:t}export function toolControlProjection(e){const t=function parseJsonRecord(e){try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);if("string"==typeof t?.status)return{controlStatus:t.status};const o=function readTextStatus(e){return String(e).match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}(e);return o?{controlStatus:o}:"string"==typeof t?.error?{controlStatus:t.error}:e.startsWith("Task delegation target is not in the workspace inventory")?{controlStatus:"task_inventory_blocked"}:{}}function stableToolCallKey(e,t){return`${e}:${stableJson(t)}`}function stableJson(e){return Array.isArray(e)?`[${e.map(stableJson).join(",")}]`:isRecord(e)?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${stableJson(e[t])}`).join(",")}}`:JSON.stringify(e)}function readApprovalIds(e){const t=e?.approvalIds??e?.approvalId;return"string"==typeof t&&t.trim()?[t.trim()]:Array.isArray(t)?t.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function formatObservedEvidenceForToolContext(e){const t=r(e).map(e=>`Tool: ${e.toolId}\n${e.output}`).join("\n\n---\n\n");return t.length>12e3?`${t.slice(0,12e3)}\n[truncated]`:t}function formatToolArgumentError(e){return JSON.stringify({error:"tool_argument_validation_failed",toolId:e.toolId,issues:e.issues,retry:"Call the same tool again with arguments that satisfy the reported schema and semantic issues."})}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readRecord(e){return isRecord(e)?e:{}}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function formatError(e){return e instanceof Error?e.message:String(e)}
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createBackendModel as o,createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as s}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as i}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as n}from"@stable-harness/core";import{createModuleToolGateway as u}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as l}from"@stable-harness/workspace-yaml";import{helpText as d,parseArgs as c}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as f,shouldEnableCliProgressNarration as m}from"./event-view.js";import{initWorkspace as w}from"./init.js";import{ensureCliMemoryServices as g}from"./memory/lifecycle.js";import{createCliMemoryProviders as v}from"./memory/providers.js";import{formatDetail as y,inspectWorkflow as k,renderAgent as I,renderWorkflow as R,workspaceStatus as h}from"./output.js";import{serveProtocol as q,stopProtocol as b}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=c(e);if(t.help)return void process.stdout.write(d());const o=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),s=t.workspaceRoot;try{if("init"===t.command)return void process.stdout.write(await w(t.prompt||s));const e=await l(s);if(t.workflowRenderId)return void process.stdout.write(R(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(k(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(I(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(o),void await b(e,t);const d=await u({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await g(e);const c=v(e),C=f(e.runtime);let M;if(M=i({workspace:e,toolGateway:d,memoryProviders:c,adapters:[r()],workflowAdapters:[createCliWorkflowAdapter(d,()=>M)],progressNarration:m(C,e.runtime)?{enabled:!0,style:"cli"}:void 0,qualityReviewModel:createQualityReviewModel(e)}),t.serveOpenAi)return clearTimeout(o),void await q(M,t);if(!t.shouldRunRequest)return void process.stdout.write(h(e,s));t.trace&&M.subscribe(e=>{const t=a(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${y(t.detail)}\n`)}),M.subscribe(e=>{const t=p(e,C);t&&process.stdout.write(`${t}\n`)});const j=await M.request({input:t.prompt,agentId:t.agentId,sessionId:t.sessionId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0,workflow:t.workflowRunId?{workflowId:t.workflowRunId,input:t.prompt}:void 0});if(t.trace||t.traceJson){const e=M.getRun(j.requestId),o=e?n(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:o})}\n`)}process.stdout.write(`${j.output}\n`)}finally{clearTimeout(o)}}function createQualityReviewModel(e){const t=function readQualityModelRef(e){const t=isRecord(e)?e:{},o=isRecord(t.reviewer)?t.reviewer:t;return"string"==typeof o.modelRef&&o.modelRef.trim()?o.modelRef.trim():void 0}(e.runtime.quality),r=t?e.models.get(t):void 0,s=r?o(r):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(s)?s:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function createCliWorkflowAdapter(e,t){return s({nodeResolvers:{tools:async({id:t,node:o,request:r,requestId:s,sessionId:i,state:a,workspace:n})=>{return(await e.invoke({toolId:t,args:(u=o.config,l=r.input,d=a.outputs,!0===u?.inputFromState?{...u,requestInput:l,outputs:d}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof l&&null!==l?l:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:i,agentId:`workflow:${o.id}`}})).output;var u,l,d},agents:async({id:e,node:o,request:r,sessionId:s,state:i})=>{var a,n,u,l;return(await t().request({input:(a=e,n=r.input,u=i.outputs,l=o.config,[`Workflow node agents.${a}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...l?[`Workflow node config: ${JSON.stringify(l)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:r.metadata})).output}}})}(function isCliEntrypoint(){const o=process.argv[1];if(!o)return!1;try{return e(t(import.meta.url))===e(o)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
2
+ import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createBackendModel as r,createDeepAgentsAdapter as o}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as s}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as a}from"@stable-harness/core";import{projectEvent as i,projectRuntimeTrace as n}from"@stable-harness/core";import{createInMemoryApprovalQueue as l}from"@stable-harness/governance";import{createModuleToolGateway as d}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as p,parseArgs as c}from"./args.js";import{formatCliRuntimeEvent as f,readCliEventViewConfig as m,shouldEnableCliProgressNarration as w}from"./event-view.js";import{initWorkspace as v}from"./init.js";import{ensureCliMemoryServices as g}from"./memory/lifecycle.js";import{createCliMemoryProviders as y}from"./memory/providers.js";import{formatDetail as I,inspectWorkflow as k,renderAgent as R,renderWorkflow as h,workspaceStatus as b}from"./output.js";import{serveProtocol as q,stopProtocol as A}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=c(e);if(t.help)return void process.stdout.write(p());const r=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),s=t.workspaceRoot;try{if("init"===t.command)return void process.stdout.write(await v(t.prompt||s));const e=await u(s);if(t.workflowRenderId)return void process.stdout.write(h(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(k(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(R(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(r),void await A(e,t);const p=await d({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await g(e);const c=y(e),C=l(),M=m(e.runtime);let j;if(j=a({workspace:e,toolGateway:p,approvals:C,memoryProviders:c,adapters:[o()],workflowAdapters:[createCliWorkflowAdapter(p,()=>j)],progressNarration:w(M,e.runtime)?{enabled:!0,style:"cli"}:void 0,qualityReviewModel:createQualityReviewModel(e)}),t.serveOpenAi)return clearTimeout(r),void await q(j,t);if(!t.shouldRunRequest)return void process.stdout.write(b(e,s));t.trace&&j.subscribe(e=>{const t=i(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${I(t.detail)}\n`)}),j.subscribe(e=>{const t=f(e,M);t&&process.stdout.write(`${t}\n`)});const $=await j.request({input:t.prompt,agentId:t.agentId,sessionId:t.sessionId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0,workflow:t.workflowRunId?{workflowId:t.workflowRunId,input:t.prompt}:void 0});if(t.trace||t.traceJson){const e=j.getRun($.requestId),r=e?n(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${$.output}\n`)}finally{clearTimeout(r)}}function createQualityReviewModel(e){const t=function readQualityModelRef(e){const t=isRecord(e)?e:{},r=isRecord(t.reviewer)?t.reviewer:t;return"string"==typeof r.modelRef&&r.modelRef.trim()?r.modelRef.trim():void 0}(e.runtime.quality),o=t?e.models.get(t):void 0,s=o?r(o):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(s)?s:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function createCliWorkflowAdapter(e,t){return s({nodeResolvers:{tools:async({id:t,node:r,request:o,requestId:s,sessionId:a,state:i,workspace:n})=>{return(await e.invoke({toolId:t,args:(l=r.config,d=o.input,u=i.outputs,!0===l?.inputFromState?{...l,requestInput:d,outputs:u}:l&&"requiredInput"in l?l.requiredInput:l&&("args"in l||"cwd"in l||"timeoutMs"in l)?l:"object"==typeof d&&null!==d?d:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`,approvalIds:readApprovalIds(o.metadata)}})).output;var l,d,u},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,l,d;return(await t().request({input:(i=e,n=o.input,l=a.outputs,d=r.config,[`Workflow node agents.${i}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...d?[`Workflow node config: ${JSON.stringify(d)}`]:[],"Prior workflow outputs:",JSON.stringify(l)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}function readApprovalIds(e){const t=e?.approvalIds??e?.approvalId;return"string"==typeof t&&t.trim()?[t.trim()]:Array.isArray(t)?t.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}(function isCliEntrypoint(){const r=process.argv[1];if(!r)return!1;try{return e(t(import.meta.url))===e(r)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
@@ -1 +1 @@
1
- export function readCliEventViewConfig(e){const t=readRecord(e.cli),r=readRecord(t?.events);return{enabled:readBoolean(r?.enabled)??!0,include:readStringList(r?.include)??["runtime.progress.narration"],exclude:readStringList(r?.exclude)??[]}}export function shouldEnableCliProgressNarration(e,t){if(!e.enabled)return!1;const r=readRecord(readRecord(t.progress)?.narration);return!1!==readBoolean(r?.enabled)&&e.include.some(e=>matchesEventPattern("runtime.progress.narration",e))}export function formatCliRuntimeEvent(e,t){if(!t.enabled||!function isIncluded(e,t){const r=function eventKeys(e){return"runtime.adapter.event"===e.type&&isRecord(e.event)&&"string"==typeof e.event.phase?[e.type,e.event.phase]:[e.type]}(e),n=t.include.some(e=>r.some(t=>matchesEventPattern(t,e))),i=t.exclude.some(e=>r.some(t=>matchesEventPattern(t,e)));return n&&!i}(e,t))return;const r=function projectCliEventView(e){return"runtime.progress.narration"===e.type?{group:"Progress",title:e.message}:e.type.startsWith("runtime.request.")?{group:"Run",title:e.type}:"runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type?{group:"Tool",title:e.type,detail:e.toolId}:"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?{group:"Workflow",title:e.type,detail:e.workflowId}:e.type.startsWith("runtime.memory.")?{group:"Memory",title:e.type,detail:memoryDetail(e)}:"runtime.skill.candidate.created"===e.type?{group:"Skill",title:e.type,detail:e.name}:"runtime.artifact.created"===e.type?{group:"Artifact",title:e.type}:"runtime.execution.contract.failed"===e.type?{group:"Contract",title:e.type,detail:e.reason}:"runtime.adapter.event"===e.type?function adapterEventView(e){if(!isRecord(e))return{group:"Adapter",title:"runtime.adapter.event"};const t="string"==typeof e.phase?e.phase:"runtime.adapter.event";return t.startsWith("agent.tool.")?{group:"Agent Tool",title:t,detail:readString(e.toolId)}:t.startsWith("agent.output.")?{group:"Agent Output",title:t,detail:readString(e.text)}:t.startsWith("agent.")?{group:"Agent",title:t,detail:readString(e.adapter)}:{group:"Adapter",title:t,detail:readString(e.adapter)}}(e.event):{group:"event",title:e.type}}(e);return`[${r.group}] ${e.agentId} | ${r.title}${r.detail?` | ${r.detail}`:""}`}function matchesEventPattern(e,t){return"*"===t||(t.endsWith(".*")?e===t.slice(0,-2)||e.startsWith(t.slice(0,-1)):e===t)}function memoryDetail(e){return"runtime.memory.recall.completed"===e.type?`${e.recordIds.length} records`:"provider"in e?e.provider:"target"in e?e.target:void 0}function readRecord(e){return isRecord(e)?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
1
+ export function readCliEventViewConfig(e){const t=readRecord(e.cli),r=readRecord(t?.events);return{enabled:readBoolean(r?.enabled)??!0,include:readStringList(r?.include)??["runtime.progress.narration"],exclude:readStringList(r?.exclude)??[]}}export function shouldEnableCliProgressNarration(e,t){if(!e.enabled)return!1;const r=readRecord(readRecord(t.progress)?.narration);return!1!==readBoolean(r?.enabled)&&e.include.some(e=>matchesEventPattern("runtime.progress.narration",e))}export function formatCliRuntimeEvent(e,t){if(!t.enabled||!function isIncluded(e,t){const r=function eventKeys(e){return"runtime.adapter.event"===e.type&&isRecord(e.event)&&"string"==typeof e.event.phase?[e.type,e.event.phase]:[e.type]}(e),n=t.include.some(e=>r.some(t=>matchesEventPattern(t,e))),i=t.exclude.some(e=>r.some(t=>matchesEventPattern(t,e)));return n&&!i}(e,t))return;const r=function projectCliEventView(e){return"runtime.progress.narration"===e.type?{group:"Progress",title:e.message}:e.type.startsWith("runtime.request.")?{group:"Run",title:e.type}:"runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type?{group:"Tool",title:e.type,detail:e.toolId}:"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?{group:"Workflow",title:e.type,detail:e.workflowId}:"runtime.approval.requested"===e.type||"runtime.approval.resolved"===e.type?{group:"Approval",title:e.type,detail:`${e.approval.kind}:${e.approval.status}`}:e.type.startsWith("runtime.memory.")?{group:"Memory",title:e.type,detail:memoryDetail(e)}:"runtime.skill.candidate.created"===e.type?{group:"Skill",title:e.type,detail:e.name}:"runtime.artifact.created"===e.type?{group:"Artifact",title:e.type}:"runtime.execution.contract.failed"===e.type?{group:"Contract",title:e.type,detail:e.reason}:"runtime.adapter.event"===e.type?function adapterEventView(e){if(!isRecord(e))return{group:"Adapter",title:"runtime.adapter.event"};const t="string"==typeof e.phase?e.phase:"runtime.adapter.event";return t.startsWith("agent.tool.")?{group:"Agent Tool",title:t,detail:readString(e.toolId)}:t.startsWith("agent.output.")?{group:"Agent Output",title:t,detail:readString(e.text)}:t.startsWith("agent.")?{group:"Agent",title:t,detail:readString(e.adapter)}:{group:"Adapter",title:t,detail:readString(e.adapter)}}(e.event):{group:"event",title:e.type}}(e);return`[${r.group}] ${e.agentId} | ${r.title}${r.detail?` | ${r.detail}`:""}`}function matchesEventPattern(e,t){return"*"===t||(t.endsWith(".*")?e===t.slice(0,-2)||e.startsWith(t.slice(0,-1)):e===t)}function memoryDetail(e){return"runtime.memory.recall.completed"===e.type?`${e.recordIds.length} records`:"provider"in e?e.provider:"target"in e?e.target:void 0}function readRecord(e){return isRecord(e)?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
@@ -1,4 +1,5 @@
1
1
  export * from "./runtime/persistence/artifacts.js";
2
+ export type { ApprovalRequest, ApprovalRequestStatus } from "@stable-harness/governance";
2
3
  export * from "./boundary-scan.js";
3
4
  export * from "./execution-contract.js";
4
5
  export * from "./recovery/tool-call.js";
@@ -1 +1 @@
1
- export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(e.toolId)?[]:stringifyEvidence(e.output).map(t=>({source:e.toolId,output:t})):[]})}export function controlBlockers(t){const e=function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,r)=>{const n=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?e.toolId:void 0}(t);n&&e.set(n,[...e.get(n)??[],r])}),e}(t);return t.flatMap((t,r)=>{const n=readAdapterEvent(t),o=readString(n?.controlStatus)??readOutputStatus(n?.output),u=readString(n?.toolId)??"tool";return o&&isBlockerStatus(o)?isResolvedByLaterCompletion(o)&&function completedAfter(t,e,r){return(t.get(e)??[]).some(t=>t>r)}(e,u,r)?[]:[`${u}:${o}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const r=readAdapterEvent(t),n=readString(r?.controlStatus)??readOutputStatus(r?.output),o=readString(r?.toolId)??"tool";return n&&isGapStatus(n)?e.has(o)&&isResolvedByLaterCompletion(n)?[]:[`${o}:${n}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){return"runtime.adapter.event"===t.type&&isRecord(t.event)?t.event:void 0}function isSuccessfulEvidenceEvent(t){const e=readString(t.controlStatus)??readOutputStatus(t.output);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function readOutputStatus(t){if("string"!=typeof t)return;const e=function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}
1
+ export function hasPlanningEvidence(t){return t.some(t=>{const e=readAdapterEvent(t);return!!e&&("plan"===e.traceType||String(e.traceLabel??"").startsWith("plan.")||"write_todos"===e.toolId&&String(e.phase??"").startsWith("agent.tool."))})}export function successfulEvidenceToolIds(t){const e=t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return[t.toolId];const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?[e.toolId]:[]});return[...new Set(e)]}export function successfulEvidenceOutputs(t){return successfulEvidenceItems(t).map(t=>t.output)}export function successfulEvidenceItems(t){return t.flatMap(t=>{if("runtime.tool.direct.completed"===t.type)return stringifyEvidence(t.output).map(e=>({source:t.toolId,output:e}));const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId?!isSuccessfulEvidenceEvent(e)||function isPlanningTool(t){return/^(?:write_todos|read_todos)$/u.test(t)}(e.toolId)?[]:stringifyEvidence(e.output).filter(t=>function isUsableEvidenceOutput(t,e){return"task"!==t||!function looksLikeUnexecutedToolIntent(t){const e=t.trim();return!(e.length>4e3)&&[/\b(?:I need to|I will|I'll|I am going to|I'm going to|Let me)\s+(?:call|use|run|invoke|get|fetch|check)\b[\s\S]{0,1200}\b(?:tool|function|system_time|web_search|url_text_fetch|task)\b/iu,/(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|获取|查看)\b[\s\S]{0,1200}\b(?:工具|函数|system_time|web_search|url_text_fetch|task)\b/iu,/```(?:json)?\s*(?:system_time|web_search|url_text_fetch|task)\s*```/iu].some(t=>t.test(e))}(e)&&!function looksLikeDelegatedCommentary(t){const e=t.trim();return!(e.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(t=>t.test(e))}(e)}(e.toolId,t)).map(t=>({source:e.toolId,output:t})):[]})}export function controlBlockers(t){const e=function successfulEventIndexesBySource(t){const e=new Map;return t.forEach((t,n)=>{const o=function successfulEventSource(t){if("runtime.tool.direct.completed"===t.type)return t.toolId;const e=readAdapterEvent(t);return e&&"agent.tool.result"===e.phase&&"string"==typeof e.toolId&&isSuccessfulEvidenceEvent(e)?e.toolId:void 0}(t);o&&e.set(o,[...e.get(o)??[],n])}),e}(t);return t.flatMap((t,n)=>{const o=readAdapterEvent(t),r=readString(o?.controlStatus)??readOutputStatus(o?.output),s=readString(o?.toolId)??"tool";return r&&isBlockerStatus(r)?isResolvedByLaterCompletion(r)&&function completedAfter(t,e,n){return(t.get(e)??[]).some(t=>t>n)}(e,s,n)?[]:[`${s}:${r}`]:[]})}export function controlGaps(t){const e=new Set(successfulEvidenceItems(t).map(t=>t.source));return t.flatMap(t=>{const n=readAdapterEvent(t),o=readString(n?.controlStatus)??readOutputStatus(n?.output),r=readString(n?.toolId)??"tool";return o&&isGapStatus(o)?e.has(r)&&isResolvedByLaterCompletion(o)?[]:[`${r}:${o}`]:[]})}function stringifyEvidence(t){return"string"==typeof t?t.trim()?[t]:[]:null==t?[]:[JSON.stringify(t)]}function readAdapterEvent(t){return"runtime.adapter.event"===t.type&&isRecord(t.event)?t.event:void 0}function isSuccessfulEvidenceEvent(t){const e=readString(t.controlStatus)??readOutputStatus(t.output);return!e||/^(?:completed|success|ok|recorded)$/iu.test(e)}function isBlockerStatus(t){return/^(?:blocked|approval_required|schema_repair_failed|tool_argument_error|invalid_input)$/iu.test(t)}function isGapStatus(t){return/^(?:dependency_required|plan_required|repeated_tool_call_limit|duplicate_tool_call)$/iu.test(t)}function isResolvedByLaterCompletion(t){return isGapStatus(t)||isBlockerStatus(t)}function readOutputStatus(t){if("string"!=typeof t)return;const e=function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}(t);return"string"==typeof e?.status?e.status:t.match(/^Status:\s*([A-Za-z0-9_-]+)/imu)?.[1]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}
@@ -1 +1 @@
1
- export function evidenceFields(e){const t=new Set,l=[];for(const r of e)for(const e of parseEvidenceFields(r)){const r=`${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`;t.has(r)||(t.add(r),l.push(e))}return l}export function selectSectionFields(e,t,l){return function withSourceCoverage(e,t){const l=[...e].sort((e,t)=>scoreField(t)-scoreField(e)),r=new Map;for(const e of[...new Set(l.map(e=>e.source))]){const t=l.find(t=>t.source===e);t&&r.set(fieldKey(t),t)}for(const e of l)if(r.set(fieldKey(e),e),r.size>=t)break;return[...r.values()].slice(0,t)}(e.filter(e=>e.kind===t),l).sort((e,t)=>scoreField(t)-scoreField(e)||e.label.localeCompare(t.label))}export function fieldTableLines(e,t){return 0===e.length?[]:[..."zh"===t?["| 项目 | 数值 | 来源 |","|---|---:|---|"]:["| Field | Value | Source |","|---|---:|---|"],...e.map(e=>`| ${escapeTableCell(e.label)} | ${escapeTableCell(e.value)} | ${escapeTableCell(function humanSourceLabel(e){const t=e.replace(/([a-z])([A-Z])/gu,"$1 $2").split(/[_:.\-\s]+/u).map(e=>e.trim()).filter(Boolean);return 0===t.length?e:t.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e.source))} |`)]}export function hasExtractableFields(e){return extractKeyValueFields(e).some(e=>!isBoilerplateField(e.label)&&e.value.length>0)}export function classifyFact(e){return/(?:gap|blocked|missing|unavailable|unsupported|缺口|阻塞|缺失|不支持|无法|未提供)/iu.test(e)?"limit":/(?:news|headline|recent|latest|filing|event|公告|新闻|近期|最新|披露|\b20\d{2}[-/年])/iu.test(e)?"timeBound":/(?:[$€£¥%]|\b\d[\d,]*(?:\.\d+)?\b)/u.test(e)?"data":/(?:name|company|symbol|resolved|overview|query|名称|公司|代码|概览|识别)/iu.test(e)?"context":"other"}function parseEvidenceFields(e){return extractKeyValueFields(e.text).filter(e=>e.value.length>0&&!isBoilerplateField(e.label)).filter(e=>!function isLowValueField(e,t){return!!/(?:snapshot for|source type|loaded sources)/iu.test(e)||scoreField({source:"",label:e,value:t,kind:"other"})<0}(e.label,e.value)).map(t=>{const l=classifyFact(`${t.label}: ${t.value}`);return{source:e.source,label:titleCaseLabel(t.label),value:formatEvidence(t.value),kind:"other"===l?e.kind:l}})}function scoreField(e){const t=normalizeFieldLabel(e.label);let l=e.value.length>0?10:0;return/(?:price|value|amount|total|ratio|rate|range|target|estimate|revenue|income|earnings|margin|volume|date|rating|status|count|metric|价格|收入|利润|目标|评级|数量|日期|范围|比率)/iu.test(t)&&(l+=8),/(?:name|company|symbol|title|headline|summary|名称|公司|代码|标题|摘要)/iu.test(t)&&(l+=6),/(?:unix|epoch|timestamp|raw|html|url|href|source|id|identifier|internal|debug)/iu.test(t)&&(l-=20),(/^https?:\/\//iu.test(e.value)||"/"===e.value)&&(l-=12),e.value.length>240&&(l-=5),l}function extractKeyValueFields(e){const t=[...e.matchAll(/(^|[\s;|])([\p{L}][\p{L}\p{N} _./()%&+\-]{0,48})[::](?!\/\/)\s*/gu)].filter(e=>void 0!==e.index).map((e,t,l)=>{l[t-1];const r=function cleanFieldLabel(e){const t=e.replace(/^(?:[-*]\s*)+/u,"").replace(/\s+/gu," ").trim(),l=t.split(" ").filter(Boolean);return l.length>1&&/^[A-Z]$/u.test(l[0]??"")||l.length>1&&/^(?:inc|llc|ltd|corp|corporation|co)$/iu.test(l[0]??"")?l.slice(1).join(" "):t}(e[2]??""),n=function droppedLabelPrefix(e,t){const l=e.trim();if(l===t)return"";const r=l.toLowerCase().lastIndexOf(t.toLowerCase());return r>0?l.slice(0,r):""}(e[2]??"",r);return{index:(e.index??0)+String(e[1]??"").length+n.length,start:(e.index??0)+e[0].length,label:r}});return t.map((l,r)=>{const n=t[r+1]?.index??e.length;return{label:l.label,value:(a=e.slice(l.start,n),a.replace(/^[\s,;|.-]+/u,"").replace(/[\s,;|.-]+$/u,"").replace(/\s+/gu," ").trim())};var a}).filter(e=>e.label.length>0)}function isBoilerplateField(e){return/^(?:status|control status|evidence tool|tool|source|来源)$/iu.test(e.trim())}export function normalizeFieldLabel(e){return e.toLowerCase().replace(/[^a-z0-9\p{L}\p{N}]+/giu," ").trim()}function titleCaseLabel(e){if(/\p{Script=Han}/u.test(e))return e;const t=e.split(/[^A-Za-z0-9]+/u).filter(Boolean);return normalizeFieldLabel(e).split(" ").filter(Boolean).map((e,l)=>{const r=t[l]??e;return/^[A-Z0-9]{2,}$/u.test(r)?r:e.charAt(0).toUpperCase()+e.slice(1)}).join(" ")}function formatEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1200?`${t.slice(0,1197)}...`:t}function escapeTableCell(e){return e.replace(/\|/gu,"\\|").replace(/\r?\n/gu," ")}function fieldKey(e){return`${e.source}\0${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`}
1
+ export function evidenceFields(e){const t=new Set,l=[];for(const r of e)for(const e of parseEvidenceFields(r)){const r=`${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`;t.has(r)||(t.add(r),l.push(e))}return l}export function selectSectionFields(e,t,l){return function withSourceCoverage(e,t){const l=[...e].sort((e,t)=>scoreField(t)-scoreField(e)),r=new Map;for(const e of[...new Set(l.map(e=>e.source))]){const t=l.find(t=>t.source===e);t&&r.set(fieldKey(t),t)}for(const e of l)if(r.set(fieldKey(e),e),r.size>=t)break;return[...r.values()].slice(0,t)}(e.filter(e=>e.kind===t),l).sort((e,t)=>scoreField(t)-scoreField(e)||e.label.localeCompare(t.label))}export function fieldTableLines(e,t){return 0===e.length?[]:[..."zh"===t?["| 项目 | 数值 | 来源 |","|---|---:|---|"]:["| Field | Value | Source |","|---|---:|---|"],...e.map(e=>`| ${escapeTableCell(e.label)} | ${escapeTableCell(e.value)} | ${escapeTableCell(function humanSourceLabel(e){const t=e.replace(/([a-z])([A-Z])/gu,"$1 $2").split(/[_:.\-\s]+/u).map(e=>e.trim()).filter(Boolean);return 0===t.length?e:t.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}(e.source))} |`)]}export function hasExtractableFields(e){return extractKeyValueFields(e).some(e=>!isBoilerplateField(e.label)&&e.value.length>0)}export function classifyFact(e){return/(?:gap|blocked|missing|unavailable|unsupported|缺口|阻塞|缺失|不支持|无法|未提供)/iu.test(e)?"limit":/(?:news|headline|recent|latest|filing|event|公告|新闻|近期|最新|披露|\b20\d{2}[-/年])/iu.test(e)?"timeBound":/(?:[$€£¥%]|\b\d[\d,]*(?:\.\d+)?\b)/u.test(e)?"data":/(?:name|company|symbol|resolved|overview|query|名称|公司|代码|概览|识别)/iu.test(e)?"context":"other"}function parseEvidenceFields(e){return extractKeyValueFields(e.text).filter(e=>e.value.length>0&&!isBoilerplateField(e.label)).filter(e=>!function isLowValueField(e,t){return!!/(?:snapshot for|source type|loaded sources)/iu.test(e)||scoreField({source:"",label:e,value:t,kind:"other"})<0}(e.label,e.value)).map(t=>{const l=classifyFact(`${t.label}: ${t.value}`);return{source:e.source,label:titleCaseLabel(t.label),value:formatEvidence(t.value),kind:"other"===l?e.kind:l}})}function scoreField(e){const t=normalizeFieldLabel(e.label);let l=e.value.length>0?10:0;return/(?:price|value|amount|total|ratio|rate|range|target|estimate|revenue|income|earnings|margin|volume|date|rating|status|count|metric|价格|收入|利润|目标|评级|数量|日期|范围|比率)/iu.test(t)&&(l+=8),/(?:name|company|symbol|title|headline|summary|名称|公司|代码|标题|摘要)/iu.test(t)&&(l+=6),/(?:unix|epoch|timestamp|raw|html|url|href|source|id|identifier|internal|debug)/iu.test(t)&&(l-=20),(/^https?:\/\//iu.test(e.value)||"/"===e.value)&&(l-=12),e.value.length>240&&(l-=5),l}function extractKeyValueFields(e){const t=[...e.matchAll(/(^|[\s;|])([\p{L}][\p{L}\p{N} _./()%&+\-]{0,48})[::](?!\/\/)\s*/gu)].filter(e=>void 0!==e.index).map((e,t,l)=>{l[t-1];const r=function cleanFieldLabel(e){const t=e.replace(/^(?:[-*]\s*)+/u,"").replace(/\s+/gu," ").trim(),l=t.split(" ").filter(Boolean);return l.length>1&&/^[A-Z]$/u.test(l[0]??"")||l.length>1&&/^(?:inc\.?|llc\.?|ltd\.?|corp\.?|corporation|co\.?)$/iu.test(l[0]??"")||l.length>1&&/^(?:usd|eur|gbp|jpy|cny|cad|aud)$/iu.test(l[0]??"")?l.slice(1).join(" "):function trailingMetricLabel(e){if(!/(?:filed|\b20\d{2}|10-k|10-q|\bfy\b)/iu.test(e))return e;const t=e.match(/\b(?:Revenue|Net Income|Operating Income|Diluted EPS|EPS|Market Cap|Book Value|Target Estimate|Target Mean Price|Target Median Price|Target High Price|Target Low Price|PE Ratio TTM|P\/B|Beta(?: 5Y Monthly)?)$/iu);return t?.[0]??e}(t)}(e[2]??""),n=function droppedLabelPrefix(e,t){const l=e.trim();if(l===t)return"";const r=l.toLowerCase().lastIndexOf(t.toLowerCase());return r>0?l.slice(0,r):""}(e[2]??"",r);return{index:(e.index??0)+String(e[1]??"").length+n.length,start:(e.index??0)+e[0].length,label:r}});return t.map((l,r)=>{const n=t[r+1]?.index??e.length;return{label:l.label,value:cleanFieldValue(e.slice(l.start,n),l.label)}}).filter(e=>e.label.length>0)}function cleanFieldValue(e,t){const l=e.replace(/^[\s,;|.-]+/u,"").replace(/[\s,;|.-]+$/u,"").replace(/\s+/gu," ").trim(),r=normalizeFieldLabel(t);if(/^(?:ticker|symbol|resolved symbol|resolved ticker|股票 resolved ticker|代码)$/iu.test(r)){const e=l.match(/^([A-Z0-9._-]{1,24})(?:\s+\d+[.)]\s+|$)/u);if(e?.[1])return e[1]}return l}function isBoilerplateField(e){return/^(?:status|control status|evidence tool|tool|source|来源)$/iu.test(e.trim())}export function normalizeFieldLabel(e){return e.toLowerCase().replace(/[^a-z0-9\p{L}\p{N}]+/giu," ").trim()}function titleCaseLabel(e){if(/\p{Script=Han}/u.test(e))return e;const t=e.split(/[^A-Za-z0-9]+/u).filter(Boolean);return normalizeFieldLabel(e).split(" ").filter(Boolean).map((e,l)=>{const r=t[l]??e;return/^[A-Z0-9]{2,}$/u.test(r)?r:e.charAt(0).toUpperCase()+e.slice(1)}).join(" ")}function formatEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1200?`${t.slice(0,1197)}...`:t}function escapeTableCell(e){return e.replace(/\|/gu,"\\|").replace(/\r?\n/gu," ")}function fieldKey(e){return`${e.source}\0${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`}
@@ -1 +1 @@
1
- import{controlBlockers as e,controlGaps as t,successfulEvidenceItems as n}from"./event-evidence.js";import{classifyFact as s,evidenceFields as r,fieldTableLines as o,hasExtractableFields as i,normalizeFieldLabel as c,selectSectionFields as u}from"./synthesis/fields.js";import{detectSynthesisLanguage as a}from"./synthesis/language.js";export function synthesizeEvidenceOnlyReport(s,r,o){if(!o.enabled||!o.synthesis.enabled||"evidence_only"!==o.synthesis.mode)return;if("pass"===r.verdict||!function hasRecoverableSynthesisIssue(e){return e.issues.some(e=>"control_blocker"!==e.code)}(r))return;const i=n(s.events).slice(-o.synthesis.maxEvidenceItems),c=e(s.events),u=t(s.events);if(0===i.length&&0===c.length&&0===u.length)return;const l=function latestDelegatedReport(e){const t=e.filter(e=>"task"===e.source&&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))}(e.output)).at(-1)?.output.trim();return t||void 0}(i);return l||("zh"===a(s)?function buildChineseReport(e,t,n){const s=evidenceFacts(e);return["# 调查结果","","## 结论",...resultSummaryLines(e,s,t,n,"zh"),"","## 证据摘要",...summaryLines(e,t,n,"zh"),"",...structuredFactSections(s,"zh"),"","## 证据缺口与阻塞",...gapLines(t,n,"zh"),"","## 使用的证据来源",...sourceLines(sourceSummary(e),"zh")].join("\n")}(i,c,u):function buildEnglishReport(e,t,n){const s=evidenceFacts(e);return["# Result","","## Conclusion",...resultSummaryLines(e,s,t,n,"en"),"","## Evidence summary",...summaryLines(e,t,n,"en"),"",...structuredFactSections(s,"en"),"","## Evidence gaps and blockers",...gapLines(t,n,"en"),"","## Sources used",...sourceLines(sourceSummary(e),"en")].join("\n")}(i,c,u))}function resultSummaryLines(e,t,n,s,o){if(0===e.length)return["zh"===o?"- 没有成功证据可用于形成调查结论。":"- No successful evidence was available for a result."];const i=[...new Set(e.map(e=>humanSourceLabel(e.source)))],u=function observedResultLines(e,t){const value=(...t)=>function fieldValue(e,t){const n=new Set(t.map(c));return e.find(e=>n.has(c(e.label)))?.value}(e,t),n=value("Resolved Ticker","Symbol","Ticker"),s=value("Company Name","Company"),r=value("Close"),o=value("Open"),i=value("High"),u=value("Low"),a=value("Volume"),l=value("Date"),d=value("Inc Revenue","Revenue"),m=value("Filed 2026 03 06 Net Income","Net Income"),h=value("Filed 2026 03 06 Operating Income","Operating Income"),p=value("Filed 2026 03 06 Diluted Eps","Diluted Eps"),f=value("Workday Wday Stock Dips While Market Gains"),$=value("Summary"),g=[];if((n||s)&&g.push("zh"===t?`- 标的已确认:${s??"公司未命名"}${n?`(${n})`:""}。`:`- Confirmed target: ${s??"unnamed company"}${n?` (${n})`:""}.`),r){const e=i&&u?"zh"===t?`,盘中区间 ${u}-${i}`:`, intraday range ${u}-${i}`:"",n=o?"zh"===t?`,开盘 ${o}`:`, open ${o}`:"",s=a?"zh"===t?`,成交量 ${a}`:`, volume ${a}`:"";g.push("zh"===t?`- 报价观察:${l?`${l} `:""}收盘 ${r}${n}${e}${s}。`:`- Quote observed: ${l?`${l} `:""}close ${r}${n}${e}${s}.`)}return(d||m||h||p)&&g.push("zh"===t?`- 财务观察:${compactParts([d&&`营收 ${d}`,m&&`净利润 ${m}`,h&&`经营利润 ${h}`,p&&`摊薄 EPS ${p}`]).join(";")}。`:`- Financials observed: ${compactParts([d&&`revenue ${d}`,m&&`net income ${m}`,h&&`operating income ${h}`,p&&`diluted EPS ${p}`]).join("; ")}.`),(f||$)&&g.push("zh"===t?`- 新闻观察:${compactParts([f,$]).join(";")}。`:`- News observed: ${compactParts([f,$]).join("; ")}.`),g.length>0?g:["zh"===t?"- 只观察到原始证据,无法形成更具体的归纳。":"- Only raw evidence was observed; no more specific synthesis is available."]}(r(t),o),a=0===n.length&&0===s.length?"zh"===o?"未观察到运行时阻塞;未覆盖的业务、估值或风险细节不应视为已确认。":"No runtime blocker was observed; uncovered business, valuation, or risk details should not be treated as confirmed.":"zh"===o?`仍有 ${n.length+s.length} 个证据缺口或阻塞,结论应按部分调查读取。`:`${n.length+s.length} evidence gap(s) or blocker(s) remain, so read this as a partial result.`;return["zh"===o?`- 已基于 ${i.join("、")} 形成可交付结果。`:`- Result formed from ${i.join(", ")}.`,`- ${a}`,...u]}function compactParts(e){return e.filter(e=>"string"==typeof e&&e.trim().length>0)}function sourceSummary(e){const t=new Map;for(const n of e){const e=t.get(n.source)??{source:n.source,count:0,facts:[]};e.count+=1,e.facts.push(...factLines(n.output)),t.set(n.source,e)}return[...t.values()].map(t=>({...t,facts:t.facts.length>0?t.facts.slice(0,8):[fallbackEvidenceText(evidenceTextForSource(e,t.source))]}))}function evidenceFacts(e){return e.flatMap(e=>{const t=factLines(e.output);return(t.length>0?t:[fallbackEvidenceText(e.output)]).map(t=>({source:e.source,text:t,kind:s(t)}))}).slice(0,40)}function summaryLines(e,t,n,s){if(0===e.length)return["zh"===s?"- 未观察到成功的工具或委托任务证据。":"- No successful tool or delegated-task evidence was observed."];const r=new Set(e.map(e=>e.source)).size,o=0===t.length&&0===n.length?"zh"===s?"未观察到未解决的运行时证据缺口或阻塞。":"No unresolved runtime evidence gaps or blockers were observed.":"zh"===s?`仍有 ${t.length+n.length} 个运行时证据缺口或阻塞。`:`${t.length+n.length} runtime evidence gaps or blockers remain.`;return["zh"===s?`- 已使用 ${e.length} 条完成证据,来自 ${r} 个来源。`:`- Used ${e.length} completed evidence item(s) from ${r} source(s).`,`- ${o}`]}function structuredFactSections(e,t){if(0===e.length)return["zh"===t?"- 证据缺口:没有可用于生成事实性结论的成功证据。":"- Evidence gap: no successful tool or delegated-task evidence was available."];const n=r(e);return[{kind:"context",title:"zh"===t?"## 已确认背景":"## Confirmed context"},{kind:"data",title:"zh"===t?"## 观察到的数据":"## Observed data points"},{kind:"timeBound",title:"zh"===t?"## 近期或时间相关证据":"## Recent or time-bound evidence"},{kind:"limit",title:"zh"===t?"## 证据限制":"## Evidence limits"},{kind:"other",title:"zh"===t?"## 其他观察":"## Additional observations"}].flatMap(s=>{const r=u(n,s.kind,12),c=e.filter(e=>e.kind===s.kind&&!i(e.text)).slice(0,5);return 0===r.length&&0===c.length?[]:[s.title,...o(r,t),...c.map(e=>function factLine(e,t){const n=humanSourceLabel(e.source);return"zh"===t?`- ${e.text}(来源:${n})`:`- ${e.text} (source: ${n})`}(e,t)),""]}).filter((e,t,n)=>""!==e||t<n.length-1)}function sourceLines(e,t){return 0===e.length?["zh"===t?"- 未使用成功证据来源。":"- No successful evidence source was used."]:e.map(e=>`- ${humanSourceLabel(e.source)}${e.count>1?` (${e.count})`:""}: ${e.source}`)}function gapLines(e,t,n){return 0===e.length&&0===t.length?["zh"===n?"- 未观察到未解决的运行时证据缺口或阻塞。":"- No unresolved runtime evidence gaps or blockers were observed."]:"zh"===n?[...e.map(e=>`- 阻塞:${e}`),...t.map(e=>`- 证据缺口:${e}`)]:[...e.map(e=>`- Blocked: ${e}`),...t.map(e=>`- Evidence gap: ${e}`)]}function formatEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1200?`${t.slice(0,1197)}...`:t}function factLines(e){const t=function factLinesFromJson(e){try{const t=JSON.parse(e);return function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(t)?Object.entries(t).filter(([e])=>!/^(?:status|controlStatus)$/iu.test(e)).slice(0,8).map(([e,t])=>`${e}: ${formatEvidence(function stringifyJsonValue(e){return"string"==typeof e?e:JSON.stringify(e)}(t))}`):[]}catch{return[]}}(e);return t.length>0?t:function splitPlainTextFacts(e){const t=function stripControlPreamble(e){return e.replace(/^(?:Status:\s*(?:completed|success|ok|recorded)\b\.?\s*)+/iu,"").replace(/^(?:Evidence tool:\s*[A-Za-z0-9_.-]+\b\.?\s*)+/iu,"").trim()}(e);return t.split(/\r?\n|;\s*/u).map(e=>e.trim()).map(stripControlFragments).filter(e=>e&&!function isControlFact(e){return/^Status:\s*(?:completed|success|ok|recorded)$/iu.test(e)||/^Evidence tool:\s*[A-Za-z0-9_.-]+$/iu.test(e)}(e))}(e).slice(0,8).map(formatEvidence)}function stripControlFragments(e){return e.replace(/\bStatus:\s*(?:completed|success|ok|recorded)\b\.?\s*/giu,"").replace(/\bEvidence tool:\s*[A-Za-z0-9_.-]+\b\.?\s*/giu,"").trim()}function evidenceTextForSource(e,t){return e.find(e=>e.source===t)?.output??""}function fallbackEvidenceText(e){return formatEvidence(e)}function humanSourceLabel(e){const t=e.replace(/([a-z])([A-Z])/gu,"$1 $2").split(/[_:.\-\s]+/u).map(e=>e.trim()).filter(Boolean);return 0===t.length?e:t.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}
1
+ import{controlBlockers as e,controlGaps as t,successfulEvidenceItems as n}from"./event-evidence.js";import{classifyFact as s,evidenceFields as r,fieldTableLines as o,hasExtractableFields as i,normalizeFieldLabel as c,selectSectionFields as u}from"./synthesis/fields.js";import{detectSynthesisLanguage as a}from"./synthesis/language.js";export function synthesizeEvidenceOnlyReport(s,r,o){if(!o.enabled||!o.synthesis.enabled||"evidence_only"!==o.synthesis.mode)return;if("pass"===r.verdict||!function hasRecoverableSynthesisIssue(e){return e.issues.some(e=>"control_blocker"!==e.code)}(r))return;const i=n(s.events).slice(-o.synthesis.maxEvidenceItems),c=e(s.events),u=t(s.events);if(0===i.length&&0===c.length&&0===u.length)return;const l=function latestDelegatedReport(e){const t=e.filter(e=>"task"===e.source&&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))}(e.output)&&!function looksLikeCommentaryInsteadOfEvidence(e){const t=e.trim();return!(t.length>8e3)&&[/(?:这篇文章|this article)[\s\S]{0,1200}(?:我认为|我认同|打动我|值得深入探讨|特别认同|what I find|I think|I agree)/iu,/(?:你还有什么|还想深入探讨|would you like|do you want).{0,120}[??]\s*$/iu].some(e=>e.test(t))}(e.output)).at(-1)?.output.trim();return t||void 0}(i);return l||("zh"===a(s)?function buildChineseReport(e,t,n){const s=evidenceFacts(e);return["# 调查结果","","## 结论",...resultSummaryLines(e,s,t,n,"zh"),"","## 证据摘要",...summaryLines(e,t,n,"zh"),"",...structuredFactSections(s,"zh"),"","## 证据缺口与阻塞",...gapLines(t,n,"zh"),"","## 使用的证据来源",...sourceLines(sourceSummary(e),"zh")].join("\n")}(i,c,u):function buildEnglishReport(e,t,n){const s=evidenceFacts(e);return["# Result","","## Conclusion",...resultSummaryLines(e,s,t,n,"en"),"","## Evidence summary",...summaryLines(e,t,n,"en"),"",...structuredFactSections(s,"en"),"","## Evidence gaps and blockers",...gapLines(t,n,"en"),"","## Sources used",...sourceLines(sourceSummary(e),"en")].join("\n")}(i,c,u))}function resultSummaryLines(e,t,n,s,o){if(0===e.length)return["zh"===o?"- 没有成功证据可用于形成调查结论。":"- No successful evidence was available for a result."];const i=[...new Set(e.map(e=>humanSourceLabel(e.source)))],u=function observedResultLines(e,t){const value=(...t)=>function fieldValue(e,t){const n=new Set(t.map(c));return e.find(e=>n.has(c(e.label)))?.value}(e,t),n=value("Resolved Ticker","Symbol","Ticker"),s=value("Company Name","Company"),r=value("Close"),o=value("Open"),i=value("High"),u=value("Low"),a=value("Volume"),l=value("Date"),d=value("Inc Revenue","Revenue"),m=value("Filed 2026 03 06 Net Income","Net Income"),h=value("Filed 2026 03 06 Operating Income","Operating Income"),f=value("Filed 2026 03 06 Diluted Eps","Diluted Eps"),p=value("Workday Wday Stock Dips While Market Gains"),g=value("Summary"),$=[];if((n||s)&&$.push("zh"===t?`- 标的已确认:${s??"公司未命名"}${n?`(${n})`:""}。`:`- Confirmed target: ${s??"unnamed company"}${n?` (${n})`:""}.`),r){const e=i&&u?"zh"===t?`,盘中区间 ${u}-${i}`:`, intraday range ${u}-${i}`:"",n=o?"zh"===t?`,开盘 ${o}`:`, open ${o}`:"",s=a?"zh"===t?`,成交量 ${a}`:`, volume ${a}`:"";$.push("zh"===t?`- 报价观察:${l?`${l} `:""}收盘 ${r}${n}${e}${s}。`:`- Quote observed: ${l?`${l} `:""}close ${r}${n}${e}${s}.`)}return(d||m||h||f)&&$.push("zh"===t?`- 财务观察:${compactParts([d&&`营收 ${d}`,m&&`净利润 ${m}`,h&&`经营利润 ${h}`,f&&`摊薄 EPS ${f}`]).join(";")}。`:`- Financials observed: ${compactParts([d&&`revenue ${d}`,m&&`net income ${m}`,h&&`operating income ${h}`,f&&`diluted EPS ${f}`]).join("; ")}.`),(p||g)&&$.push("zh"===t?`- 新闻观察:${compactParts([p,g]).join(";")}。`:`- News observed: ${compactParts([p,g]).join("; ")}.`),$.length>0?$:["zh"===t?"- 只观察到原始证据,无法形成更具体的归纳。":"- Only raw evidence was observed; no more specific synthesis is available."]}(r(t),o),a=0===n.length&&0===s.length?"zh"===o?"未观察到运行时阻塞;未覆盖的业务、估值或风险细节不应视为已确认。":"No runtime blocker was observed; uncovered business, valuation, or risk details should not be treated as confirmed.":"zh"===o?`仍有 ${n.length+s.length} 个证据缺口或阻塞,结论应按部分调查读取。`:`${n.length+s.length} evidence gap(s) or blocker(s) remain, so read this as a partial result.`;return["zh"===o?`- 已基于 ${i.join("、")} 形成可交付结果。`:`- Result formed from ${i.join(", ")}.`,`- ${a}`,...u]}function compactParts(e){return e.filter(e=>"string"==typeof e&&e.trim().length>0)}function sourceSummary(e){const t=new Map;for(const n of e){const e=t.get(n.source)??{source:n.source,count:0,facts:[]};e.count+=1,e.facts.push(...factLines(n.output)),t.set(n.source,e)}return[...t.values()].map(t=>({...t,facts:t.facts.length>0?t.facts.slice(0,8):[fallbackEvidenceText(evidenceTextForSource(e,t.source))]}))}function evidenceFacts(e){return e.flatMap(e=>{const t=factLines(e.output);return(t.length>0?t:[fallbackEvidenceText(e.output)]).map(t=>({source:e.source,text:t,kind:s(t)}))}).slice(0,40)}function summaryLines(e,t,n,s){if(0===e.length)return["zh"===s?"- 未观察到成功的工具或委托任务证据。":"- No successful tool or delegated-task evidence was observed."];const r=new Set(e.map(e=>e.source)).size,o=0===t.length&&0===n.length?"zh"===s?"未观察到未解决的运行时证据缺口或阻塞。":"No unresolved runtime evidence gaps or blockers were observed.":"zh"===s?`仍有 ${t.length+n.length} 个运行时证据缺口或阻塞。`:`${t.length+n.length} runtime evidence gaps or blockers remain.`;return["zh"===s?`- 已使用 ${e.length} 条完成证据,来自 ${r} 个来源。`:`- Used ${e.length} completed evidence item(s) from ${r} source(s).`,`- ${o}`]}function structuredFactSections(e,t){if(0===e.length)return["zh"===t?"- 证据缺口:没有可用于生成事实性结论的成功证据。":"- Evidence gap: no successful tool or delegated-task evidence was available."];const n=r(e);return[{kind:"context",title:"zh"===t?"## 已确认背景":"## Confirmed context"},{kind:"data",title:"zh"===t?"## 观察到的数据":"## Observed data points"},{kind:"timeBound",title:"zh"===t?"## 近期或时间相关证据":"## Recent or time-bound evidence"},{kind:"limit",title:"zh"===t?"## 证据限制":"## Evidence limits"},{kind:"other",title:"zh"===t?"## 其他观察":"## Additional observations"}].flatMap(s=>{const r=u(n,s.kind,12),c=e.filter(e=>e.kind===s.kind&&!i(e.text)).slice(0,5);return 0===r.length&&0===c.length?[]:[s.title,...o(r,t),...c.map(e=>function factLine(e,t){const n=humanSourceLabel(e.source);return"zh"===t?`- ${e.text}(来源:${n})`:`- ${e.text} (source: ${n})`}(e,t)),""]}).filter((e,t,n)=>""!==e||t<n.length-1)}function sourceLines(e,t){return 0===e.length?["zh"===t?"- 未使用成功证据来源。":"- No successful evidence source was used."]:e.map(e=>`- ${humanSourceLabel(e.source)}${e.count>1?` (${e.count})`:""}: ${e.source}`)}function gapLines(e,t,n){return 0===e.length&&0===t.length?["zh"===n?"- 未观察到未解决的运行时证据缺口或阻塞。":"- No unresolved runtime evidence gaps or blockers were observed."]:"zh"===n?[...e.map(e=>`- 阻塞:${e}`),...t.map(e=>`- 证据缺口:${e}`)]:[...e.map(e=>`- Blocked: ${e}`),...t.map(e=>`- Evidence gap: ${e}`)]}function formatEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1200?`${t.slice(0,1197)}...`:t}function factLines(e){const t=function factLinesFromJson(e){try{const t=JSON.parse(e);return function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(t)?Object.entries(t).filter(([e])=>!/^(?:status|controlStatus)$/iu.test(e)).slice(0,8).map(([e,t])=>`${e}: ${formatEvidence(function stringifyJsonValue(e){return"string"==typeof e?e:JSON.stringify(e)}(t))}`):[]}catch{return[]}}(e);return t.length>0?t:function splitPlainTextFacts(e){const t=function stripControlPreamble(e){return e.replace(/^(?:Status:\s*(?:completed|success|ok|recorded)\b\.?\s*)+/iu,"").replace(/^(?:Evidence tool:\s*[A-Za-z0-9_.-]+\b\.?\s*)+/iu,"").trim()}(e);return t.split(/\r?\n|;\s*/u).map(e=>e.trim()).map(stripControlFragments).filter(e=>e&&!function isControlFact(e){return/^Status:\s*(?:completed|success|ok|recorded)$/iu.test(e)||/^Evidence tool:\s*[A-Za-z0-9_.-]+$/iu.test(e)}(e))}(e).slice(0,8).map(formatEvidence)}function stripControlFragments(e){return e.replace(/\bStatus:\s*(?:completed|success|ok|recorded)\b\.?\s*/giu,"").replace(/\bEvidence tool:\s*[A-Za-z0-9_.-]+\b\.?\s*/giu,"").trim()}function evidenceTextForSource(e,t){return e.find(e=>e.source===t)?.output??""}function fallbackEvidenceText(e){return formatEvidence(e)}function humanSourceLabel(e){const t=e.replace(/([a-z])([A-Z])/gu,"$1 $2").split(/[_:.\-\s]+/u).map(e=>e.trim()).filter(Boolean);return 0===t.length?e:t.map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}
@@ -1 +1 @@
1
- import{toolCircuitOpenEvent as o,toolFailureEvent as t}from"./tool-failure.js";export async function runDirectToolCall(o){const t=o.request.toolCall;if(!t)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const e=await async function resolveDirectToolCall(o){if(o.agent.tools.includes(o.toolId)&&o.gateway.get(o.toolId))return{toolId:o.toolId,args:o.args};const t=await(o.gateway.repairToolCall?.({toolId:o.toolId,args:o.args,allowedToolIds:o.agent.tools,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,requestInput:o.request.input}}));if(t&&o.agent.tools.includes(t.toolId)&&o.gateway.get(t.toolId))return emitToolRepair(o,"repaired",t.toolId),t;if(!o.agent.tools.includes(o.toolId))throw emitToolRepair(o,"blocked",void 0,`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`),new Error(`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`);throw emitToolRepair(o,"blocked",void 0,`Tool is not registered: ${o.toolId}`),new Error(`Tool is not registered: ${o.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:t.toolId,args:t.args});if(o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:e.toolId}),o.toolFailureTracker?.isCircuitOpen(e.toolId)){const t=new Error(`Tool circuit is open: ${e.toolId}`);throw emitToolFailure(o,e.toolId,t),t}const r=await async function invokeToolWithFailureEvents(o,t){try{return await o.gateway.invoke({toolId:t.toolId,args:t.args,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,requestInput:o.request.input}})}catch(e){throw emitToolFailure(o,t.toolId,e),e}}(o,e);return o.toolFailureTracker?.recordSuccess(r.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:r.output}),{text:(s=r.output,"string"==typeof s?s:JSON.stringify(s)),metadata:{toolCall:{toolId:r.toolId}}};var s}function emitToolFailure(e,r,s){const i=t({requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,toolId:r,error:s});e.emit(i),e.toolFailureTracker?.recordFailure(r)&&e.emit(o({requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,toolId:r,reason:"runtime.tool.failure"===i.type?i.failure.reason:"unknown"}))}function emitToolRepair(o,t,e,r){o.emit({type:"runtime.inventory.repair",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,status:t,diagnostic:{layer:"tool",owner:"stable_runtime_policy",originalId:o.toolId,repairedId:e,candidateIds:o.agent.tools,reason:r}})}
1
+ import{toolCircuitOpenEvent as o,toolFailureEvent as t}from"./tool-failure.js";export async function runDirectToolCall(o){const t=o.request.toolCall;if(!t)throw new Error("Direct tool call request is missing");if(!o.gateway)throw new Error("Runtime tool gateway is not configured");const e=await async function resolveDirectToolCall(o){if(o.agent.tools.includes(o.toolId)&&o.gateway.get(o.toolId))return{toolId:o.toolId,args:o.args};const t=await(o.gateway.repairToolCall?.({toolId:o.toolId,args:o.args,allowedToolIds:o.agent.tools,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,requestInput:o.request.input,approvalIds:readApprovalIds(o.request.metadata)}}));if(t&&o.agent.tools.includes(t.toolId)&&o.gateway.get(t.toolId))return emitToolRepair(o,"repaired",t.toolId),t;if(!o.agent.tools.includes(o.toolId))throw emitToolRepair(o,"blocked",void 0,`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`),new Error(`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`);throw emitToolRepair(o,"blocked",void 0,`Tool is not registered: ${o.toolId}`),new Error(`Tool is not registered: ${o.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,emit:o.emit,request:o.request,toolId:t.toolId,args:t.args});if(o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:e.toolId}),o.toolFailureTracker?.isCircuitOpen(e.toolId)){const t=new Error(`Tool circuit is open: ${e.toolId}`);throw emitToolFailure(o,e.toolId,t),t}const r=await async function invokeToolWithFailureEvents(o,t){try{return await o.gateway.invoke({toolId:t.toolId,args:t.args,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,requestInput:o.request.input,approvalIds:readApprovalIds(o.request.metadata)}})}catch(e){throw emitToolFailure(o,t.toolId,e),e}}(o,e);return o.toolFailureTracker?.recordSuccess(r.toolId),o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:r.toolId,output:r.output}),{text:(s=r.output,"string"==typeof s?s:JSON.stringify(s)),metadata:{toolCall:{toolId:r.toolId}}};var s}function emitToolFailure(e,r,s){const a=t({requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,toolId:r,error:s});e.emit(a),e.toolFailureTracker?.recordFailure(r)&&e.emit(o({requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,toolId:r,reason:"runtime.tool.failure"===a.type?a.failure.reason:"unknown"}))}function emitToolRepair(o,t,e,r){o.emit({type:"runtime.inventory.repair",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,status:t,diagnostic:{layer:"tool",owner:"stable_runtime_policy",originalId:o.toolId,repairedId:e,candidateIds:o.agent.tools,reason:r}})}function readApprovalIds(o){const t=o?.approvalIds??o?.approvalId;return"string"==typeof t&&t.trim()?[t.trim()]:Array.isArray(t)?t.filter(o=>"string"==typeof o&&o.trim().length>0):void 0}
@@ -71,6 +71,18 @@ export type RuntimeEvent = {
71
71
  agentId: string;
72
72
  toolId: string;
73
73
  output: unknown;
74
+ } | {
75
+ type: "runtime.approval.requested";
76
+ requestId: string;
77
+ sessionId: string;
78
+ agentId: string;
79
+ approval: ApprovalRequest;
80
+ } | {
81
+ type: "runtime.approval.resolved";
82
+ requestId: string;
83
+ sessionId: string;
84
+ agentId: string;
85
+ approval: ApprovalRequest;
74
86
  } | {
75
87
  type: "runtime.tool.failure";
76
88
  requestId: string;
@@ -0,0 +1,8 @@
1
+ import type { ApprovalQueue } from "@stable-harness/governance";
2
+ import type { CompiledWorkspace, RuntimeEvent, RuntimeToolGateway } from "../../types.js";
3
+ export declare function createApprovalGatedToolGateway(input: {
4
+ gateway?: RuntimeToolGateway;
5
+ approvals?: ApprovalQueue;
6
+ workspace: CompiledWorkspace;
7
+ emit: (event: RuntimeEvent) => void;
8
+ }): RuntimeToolGateway | undefined;
@@ -0,0 +1 @@
1
+ export function createApprovalGatedToolGateway(e){if(!e.gateway)return;const t=function readRequiredToolIds(e){const t=readRecord(e);return new Set(function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}(t?.requiredToolIds??t?.approvalRequiredToolIds))}(e.workspace.runtime.approvals);return 0===t.size?e.gateway:{get:t=>e.gateway?.get(t),repairToolCall:e.gateway.repairToolCall?.bind(e.gateway),async invoke(o){if(!t.has(o.toolId))return e.gateway.invoke(o);const r=await async function resolveApprovedToolInvocation(e,t,o){if(e&&o.approvalIds?.length)for(const r of o.approvalIds){const o=await e.get(r);if("tool_invocation"===o?.kind&&readRecord(o.subject)?.toolId===t){if("approved"===o.status)return"approved";if("rejected"===o.status)return"rejected"}}}(e.approvals,o.toolId,o.context);if("approved"===r)return e.gateway.invoke(o);if("rejected"===r)return{toolId:o.toolId,output:approvalRequiredOutput(o.toolId,"rejected","The linked approval was rejected.")};if(!e.approvals)return{toolId:o.toolId,output:approvalRequiredOutput(o.toolId,"approval_queue_missing","Tool approval is required, but no approval queue is configured.")};const a=await e.approvals.create({kind:"tool_invocation",reason:`Tool '${o.toolId}' requires explicit approval.`,requestId:o.context.requestId,sessionId:o.context.sessionId,agentId:o.context.agentId,subject:{toolId:o.toolId,args:o.args,requestInput:o.context.requestInput}});return e.emit({type:"runtime.approval.requested",requestId:o.context.requestId,sessionId:o.context.sessionId,agentId:o.context.agentId,approval:a}),{toolId:o.toolId,output:approvalRequiredOutput(o.toolId,"pending",a.reason,a.id)}}}}function approvalRequiredOutput(e,t,o,r){return{status:"approval_required",toolId:e,state:t,reason:o,...r?{approvalId:r}:{}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}
@@ -0,0 +1,10 @@
1
+ import type { ApprovalQueue } from "@stable-harness/governance";
2
+ import type { CompiledWorkspace, RuntimeEvent, RuntimeStore, StableHarnessRuntime } from "../../types.js";
3
+ type RuntimeInspectionMethods = Pick<StableHarnessRuntime, "inspect" | "getRuntimePolicy" | "getWorkflow" | "getRun" | "listRequests" | "listSessions" | "inspectRequest" | "listApprovals" | "getApproval" | "resolveApproval">;
4
+ export declare function createRuntimeInspectionMethods(input: {
5
+ workspace: CompiledWorkspace;
6
+ store: RuntimeStore;
7
+ approvals?: ApprovalQueue;
8
+ emit: (event: RuntimeEvent) => void;
9
+ }): RuntimeInspectionMethods;
10
+ export {};
@@ -0,0 +1 @@
1
+ import{projectRequestInspection as e,projectRequestSummary as o,projectSessionSummaries as s}from"../persistence/inspection.js";export function createRuntimeInspectionMethods(t){return{inspect:()=>{return{workspaceRoot:t.workspace.root,agents:[...t.workspace.agents.keys()].sort(),workflows:[...t.workspace.workflows.keys()].sort(),...t.workspace.runtime.specDrivenWorkflow?{specDrivenWorkflow:(e=t.workspace.runtime.specDrivenWorkflow,{enabled:e.enabled,artifactsDir:e.artifactsDir,...e.constitution?{constitution:e.constitution}:{},phases:e.phases.map(e=>e.id)})}:{},evaluations:[...t.workspace.evaluations?.keys()??[]].sort(),...t.workspace.runtime.workflowRouting?.defaultWorkflowId?{defaultWorkflowId:t.workspace.runtime.workflowRouting.defaultWorkflowId}:{},workflowRoutes:(t.workspace.runtime.workflowRouting?.routes??[]).map(e=>e.id).sort(),models:[...t.workspace.models.keys()].sort(),tools:[...t.workspace.tools.keys()].sort(),runs:t.store.listRuns()};var e},getRuntimePolicy:()=>t.workspace.runtime,getWorkflow:e=>t.workspace.workflows.get(e),getRun:e=>t.store.getRun(e),listRequests:e=>t.store.listRuns(e).map(o),listSessions:()=>s(t.store.listRuns()),inspectRequest:o=>{const s=t.store.getRun(o);return s?e(t.workspace,s):void 0},listApprovals:e=>t.approvals?.list(e)??Promise.resolve([]),getApproval:e=>t.approvals?.get(e)??Promise.resolve(void 0),resolveApproval:async(e,o)=>{const s=await(t.approvals?.resolve(e,o));return s&&t.emit({type:"runtime.approval.resolved",requestId:s.requestId??"",sessionId:s.sessionId??"",agentId:s.agentId??"",approval:s}),s}}}
@@ -9,6 +9,7 @@ export type RuntimeToolGatewayContext = {
9
9
  agentId: string;
10
10
  requestInput?: string;
11
11
  observedEvidence?: string;
12
+ approvalIds?: string[];
12
13
  };
13
14
  export type RuntimeToolGatewayTool = {
14
15
  id: string;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{projectRequestInspection as r,projectRequestSummary as s,projectSessionSummaries as o}from"./runtime/persistence/inspection.js";import{assertNoRawToolCallOutput as a,assertNoToolExecutionErrorOutput as n,buildAdapterErrorRecoveryPrompt as i,buildEvidenceSynthesisOutput as u,buildExecutionContractRecoveryRequest as c,buildResultRecoveryRequest as l,containsRawToolCallOutput as p,isRecoverableAdapterError as d,rawToolCallFailureMessage as m,toolCallRecoveryEnabled as w}from"./recovery/tool-call.js";import{recoverQualityReview as f,resolveQualityPolicy as y}from"./quality/index.js";import{completeRun as R,failRun as g}from"./runtime/completion.js";import{runDirectToolCall as I}from"./runtime/direct-tool-call.js";import{createRuntimeCapabilityRegistry as q,normalizeAdapterResult as k}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as v}from"./runtime/memory.js";import{createInMemoryRuntimeStore as b}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as A}from"./runtime/progress-narration.js";import{repairRuntimeSelection as C}from"./runtime/selection-repair.js";import{createToolFailureTracker as x}from"./runtime/tool-failure.js";import{runWorkflowRequest as j}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const a=new Set,n=t.store??b(),u=q([v(t),A({options:t.progressNarration,policy:t.workspace.runtime}),...t.capabilities??[]]),emitBase=e=>{n.appendEvent(e);for(const t of a)t(e)},emit=e=>{emitBase(e),u.emitSideEffects(e,emitBase)},l=x(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async r=>async function runRuntimeRequest(t){const r=t.request.requestId??e(),s=t.request.sessionId??e(),o=[],{agent:a,adapter:n}=await async function resolveExecution(e,t,r){const s=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const s=await C({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return s.ok?s.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),o=e.workspace.agents.get(s);if(!o)throw new Error(`Agent ${s} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const a=e.adapters.find(e=>e.canRun(o));if(!a)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:a}}(t.input,t.request,{requestId:r,sessionId:s,emit:e=>o.push(e)});t.store.createRun(function createRunRecord(e,t,r,s){return{requestId:t,sessionId:r,agentId:s.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,r,s,a)),o.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:r,sessionId:s,agentId:a.id,input:t.request.input});try{if(t.request.workflow){const e=await j({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:r,sessionId:s,agentId:a.id,emit:t.emit});return R({store:t.store,emit:t.emit,requestId:r,sessionId:s,agent:a,result:e})}if(t.request.toolCall){const e=await I({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:r,sessionId:s,agent:a,toolFailureTracker:t.toolFailureTracker});return R({store:t.store,emit:t.emit,requestId:r,sessionId:s,agent:a,result:e})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,r=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),s=r.memory,o=r.pluginMemories??[],a=e.input.workspace.runtime,n=y(e.input.workspace.runtime,e.agent),u=new Map;let l;try{l=await runAdapterOnce(e,t,e.request,s,o,u)}catch(r){if(!d(r,a))throw r;l=await runAdapterOnce(e,t,i(e.request,r,a),s,o,u)}l=await recoverAdapterResultOutput(e,t,e.request,l,s,o,a,u),l=await f(createQualityRuntimeInput(e,s,o,u),e.request,l,n),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:l});try{assertRequestExecutionContract(e)}catch(r){const i=c({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:a});if(!i)throw r;l=await runAdapterOnce(e,t,i,s,o,u),l=await recoverAdapterResultOutput(e,t,i,l,s,o,a,u),l=await f(createQualityRuntimeInput(e,s,o,u),i,l,n),assertRequestExecutionContract(e)}const p=R({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:l});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:l,response:p}),p}({...t,adapter:n,requestId:r,sessionId:s,agent:a})}catch(e){return g({store:t.store,emit:t.emit,requestId:r,sessionId:s,agent:a,error:e})}}({input:t,capabilities:u,store:n,emit:emit,request:r,toolFailureTracker:l}),subscribe:e=>(a.add(e),()=>a.delete(e)),inspect(){return{workspaceRoot:t.workspace.root,agents:[...t.workspace.agents.keys()].sort(),workflows:[...t.workspace.workflows.keys()].sort(),...t.workspace.runtime.specDrivenWorkflow?{specDrivenWorkflow:(e=t.workspace.runtime.specDrivenWorkflow,{enabled:e.enabled,artifactsDir:e.artifactsDir,...e.constitution?{constitution:e.constitution}:{},phases:e.phases.map(e=>e.id)})}:{},evaluations:[...t.workspace.evaluations?.keys()??[]].sort(),...t.workspace.runtime.workflowRouting?.defaultWorkflowId?{defaultWorkflowId:t.workspace.runtime.workflowRouting.defaultWorkflowId}:{},workflowRoutes:(t.workspace.runtime.workflowRouting?.routes??[]).map(e=>e.id).sort(),models:[...t.workspace.models.keys()].sort(),tools:[...t.workspace.tools.keys()].sort(),runs:n.listRuns()};var e},getRuntimePolicy:()=>t.workspace.runtime,getWorkflow:e=>t.workspace.workflows.get(e),getRun:e=>n.getRun(e),listRequests:e=>n.listRuns(e).map(s),listSessions:()=>o(n.listRuns()),inspectRequest(e){const s=n.getRun(e);return s?r(t.workspace,s):void 0},cancel(e,t){const r=n.getRun(e);r&&"running"===r.state&&(n.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await u.stop(),a.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,s){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:o=>runAdapterOnce(e,e.adapter,o,t,r,s),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}async function recoverAdapterResultOutput(e,t,r,s,o,i,c,d){let f=r;const y=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,r="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,s="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof s&&Number.isInteger(s)&&s>0?s:3}(c);let R=0;for(let r=0;r<y;r+=1){const r=e.store.getRun(e.requestId)?.events??[],a=l({request:f,output:s.text,events:r.slice(R),availableToolIds:e.agent.tools,policy:c});if(!a)break;f=a,R=e.store.getRun(e.requestId)?.events.length??0,s=await runAdapterOnce(e,t,a,o,i,d)}if(w(c)){let t=!1;p(s.text,c)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(r.metadata)&&(s={...s,text:m(),metadata:{...s.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const o=u({request:r,output:s.text,events:e.store.getRun(e.requestId)?.events??[],policy:c});o&&(t=!0,s={...s,text:o,metadata:{...s.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(a(s.text,c),n(s.text,c))}return s}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,s,o,a){return k(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:s,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:a,emit:e.emit}))}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
1
+ import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{assertNoRawToolCallOutput as r,assertNoToolExecutionErrorOutput as a,buildAdapterErrorRecoveryPrompt as o,buildEvidenceSynthesisOutput as s,buildExecutionContractRecoveryRequest as n,buildResultRecoveryRequest as i,containsRawToolCallOutput as u,isRecoverableAdapterError as c,rawToolCallFailureMessage as l,toolCallRecoveryEnabled as p}from"./recovery/tool-call.js";import{recoverQualityReview as d,resolveQualityPolicy as m}from"./quality/index.js";import{completeRun as y,failRun as w}from"./runtime/completion.js";import{runDirectToolCall as f}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as I}from"./runtime/governance/approval-gate.js";import{createRuntimeInspectionMethods as g}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as q,normalizeAdapterResult as R}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as v}from"./runtime/memory.js";import{createInMemoryRuntimeStore as k}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as b}from"./runtime/progress-narration.js";import{repairRuntimeSelection as A}from"./runtime/selection-repair.js";import{createToolFailureTracker as C}from"./runtime/tool-failure.js";import{runWorkflowRequest as x}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const r=new Set,a=t.store??k(),s=q([v(t),b({options:t.progressNarration,policy:t.workspace.runtime}),...t.capabilities??[]]),emitBase=e=>{a.appendEvent(e);for(const t of r)t(e)},emit=e=>{emitBase(e),s.emitSideEffects(e,emitBase)},i={...t,toolGateway:I({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit})},u=C(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const r=t.request.requestId??e(),a=t.request.sessionId??e(),s=[],{agent:i,adapter:u}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await A({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:r,sessionId:a,emit:e=>s.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,r,a,i)),s.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:r,sessionId:a,agentId:i.id,input:t.request.input});try{if(t.request.workflow){const e=await x({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:r,sessionId:a,agentId:i.id,emit:t.emit});return y({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e})}if(t.request.toolCall){const e=await f({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:r,sessionId:a,agent:i,toolFailureTracker:t.toolFailureTracker});return y({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,r=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),a=r.memory,s=r.pluginMemories??[],i=e.input.workspace.runtime,u=m(e.input.workspace.runtime,e.agent),l=new Map;let p;try{p=await runAdapterOnce(e,t,e.request,a,s,l)}catch(r){if(!c(r,i))throw r;p=await runAdapterOnce(e,t,o(e.request,r,i),a,s,l)}p=await recoverAdapterResultOutput(e,t,e.request,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),e.request,p,u),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:p});try{assertRequestExecutionContract(e)}catch(r){const o=n({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:i});if(!o)throw r;p=await runAdapterOnce(e,t,o,a,s,l),p=await recoverAdapterResultOutput(e,t,o,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),o,p,u),assertRequestExecutionContract(e)}const w=y({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:p});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:p,response:w}),w}({...t,adapter:u,requestId:r,sessionId:a,agent:i})}catch(e){return w({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,error:e})}}({input:i,capabilities:s,store:a,emit:emit,request:t,toolFailureTracker:u}),subscribe:e=>(r.add(e),()=>r.delete(e)),...g({workspace:t.workspace,store:a,approvals:t.approvals,emit:emit}),cancel(e,t){const r=a.getRun(e);r&&"running"===r.state&&(a.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await s.stop(),r.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:o=>runAdapterOnce(e,e.adapter,o,t,r,a),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}async function recoverAdapterResultOutput(e,t,o,n,c,d,m,y){let w=o;const f=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,r="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,a="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof a&&Number.isInteger(a)&&a>0?a:3}(m);let I=0;for(let r=0;r<f;r+=1){const r=e.store.getRun(e.requestId)?.events??[],a=i({request:w,output:n.text,events:r.slice(I),availableToolIds:e.agent.tools,policy:m});if(!a)break;w=a,I=e.store.getRun(e.requestId)?.events.length??0,n=await runAdapterOnce(e,t,a,c,d,y)}if(p(m)){let t=!1;u(n.text,m)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(o.metadata)&&(n={...n,text:l(),metadata:{...n.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const i=s({request:o,output:n.text,events:e.store.getRun(e.requestId)?.events??[],policy:m});i&&(t=!0,n={...n,text:i,metadata:{...n.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(r(n.text,m),a(n.text,m))}return n}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,o,s){return R(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:s,emit:e.emit}))}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
@@ -1,5 +1,5 @@
1
1
  import type { MemoryProvider, MemoryRecord, RuntimeMemoryStore } from "@stable-harness/memory";
2
- import type { ApprovalQueue } from "@stable-harness/governance";
2
+ import type { ApprovalQueue, ApprovalRequest, ApprovalRequestStatus } from "@stable-harness/governance";
3
3
  import type { RuntimeWorkflowAdapter, RuntimeWorkflowRequest, WorkspaceWorkflow } from "./workflows/index.js";
4
4
  import type { SpecDrivenWorkflowState } from "./spec-driven/index.js";
5
5
  import type { RuntimeEvent, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
@@ -129,6 +129,9 @@ export type RuntimeInspector = {
129
129
  listRequests(filter?: RuntimeRunFilter): RuntimeRequestSummary[];
130
130
  listSessions(): RuntimeSessionSummary[];
131
131
  inspectRequest(requestId: string): RuntimeRequestInspection | undefined;
132
+ listApprovals(status?: ApprovalRequestStatus): Promise<ApprovalRequest[]>;
133
+ getApproval(id: string): Promise<ApprovalRequest | undefined>;
134
+ resolveApproval(id: string, status: Exclude<ApprovalRequestStatus, "pending">): Promise<ApprovalRequest | undefined>;
132
135
  };
133
136
  export type RuntimeLifecycle = {
134
137
  cancel(requestId: string, reason?: string): void;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";export function createInMemoryApprovalQueue(){const t=[];return{async create(n){const s={id:e(),kind:n.kind,reason:n.reason,status:"pending",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agentId,subject:n.subject,createdAt:(new Date).toISOString()};return t.push(s),s},list:async e=>e?t.filter(t=>t.status===e):[...t],async resolve(e,n){const s=t.find(t=>t.id===e);if(s)return s.status=n,s.resolvedAt=(new Date).toISOString(),s}}}
1
+ import{randomUUID as e}from"node:crypto";export function createInMemoryApprovalQueue(){const t=[];return{async create(n){const s={id:e(),kind:n.kind,reason:n.reason,status:"pending",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agentId,subject:n.subject,createdAt:(new Date).toISOString()};return t.push(s),s},get:async e=>t.find(t=>t.id===e),list:async e=>e?t.filter(t=>t.status===e):[...t],async resolve(e,n){const s=t.find(t=>t.id===e);if(s)return s.status=n,s.resolvedAt=(new Date).toISOString(),s}}}
@@ -42,6 +42,7 @@ export type ApprovalQueue = {
42
42
  agentId?: string;
43
43
  subject: Record<string, unknown>;
44
44
  }): Promise<ApprovalRequest>;
45
+ get(id: string): Promise<ApprovalRequest | undefined>;
45
46
  list(status?: ApprovalRequestStatus): Promise<ApprovalRequest[]>;
46
47
  resolve(id: string, status: Exclude<ApprovalRequestStatus, "pending">): Promise<ApprovalRequest | undefined>;
47
48
  };
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{compileWorkflowPlan as o,projectRuntimeTrace as t,renderWorkflowMermaid as r}from"@stable-harness/core";export function createHttpServer(n){return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0});if("GET"===e.method&&"/inspect"===e.url)return void sendJson(s,200,n.inspect());if("GET"===e.method&&"/requests"===e.url)return void sendJson(s,200,n.listRequests());if("GET"===e.method&&"/sessions"===e.url)return void sendJson(s,200,n.listSessions());if("GET"===e.method&&"/workflows"===e.url)return void sendJson(s,200,n.inspect().workflows);const d=function readWorkflowMermaidId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&d){const e=n.getWorkflow(d);return void sendJson(s,e?200:404,e?{mermaid:r(e)}:{error:"workflow_not_found"})}const a=function readWorkflowPlanId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&a){const e=n.getWorkflow(a);return void sendJson(s,e?200:404,e?o(e):{error:"workflow_not_found"})}const i=function readRequestInspectionId(e){const o=(e??"").match(/^\/requests\/([^/]+)$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&i){const e=n.inspectRequest(i);return void sendJson(s,e?200:404,e??{error:"request_not_found"})}const u=function readTraceRequestId(e){const o=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&u){const e=n.getRun(u);return void sendJson(s,e?200:404,e?t(e):{error:"run_not_found"})}if("POST"===e.method&&"/requests"===e.url){const o=await async function readJson(e){const o=[];for await(const t of e)o.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===o.length?{}:JSON.parse(Buffer.concat(o).toString("utf8"))}(e);return void sendJson(s,200,await n.request(function readRuntimeRequest(e){const o=function readToolCall(e){if("object"!=typeof e||null===e)return;const o=e;return"string"==typeof o.toolId?{toolId:o.toolId,args:o.args}:void 0}(e.toolCall),t=function readWorkflow(e){const o=readRecord(e);if(o)return{..."string"==typeof o.workflowId?{workflowId:o.workflowId}:{},..."string"==typeof o.routeId?{routeId:o.routeId}:{},...void 0!==o.input?{input:o.input}:{},..."object"==typeof o.metadata&&o.metadata?{metadata:o.metadata}:{}}}(e.workflow),r=function readMemory(e){const o=readRecord(e);if(o)return{..."string"==typeof o.namespace?{namespace:o.namespace}:{},...!1===o.recall||readRecord(o.recall)?{recall:o.recall}:{},...Array.isArray(o.candidates)?{candidates:o.candidates}:{}}}(e.memory),n=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}:{},...o?{toolCall:o}:{},...t?{workflow:t}:{},...r?{memory:r}:{},...n?{metadata:n}:{}}}(o)))}sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,500,{error:e instanceof Error?e.message:String(e)})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,o,t){e.writeHead(o,{"content-type":"application/json"}),e.end(JSON.stringify(t))}
1
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as o,projectRuntimeTrace as t,renderWorkflowMermaid as r}from"@stable-harness/core";export function createHttpServer(n){return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0});if("GET"===e.method&&"/inspect"===e.url)return void sendJson(s,200,n.inspect());if("GET"===e.method&&"/requests"===e.url)return void sendJson(s,200,n.listRequests());if("GET"===e.method&&"/sessions"===e.url)return void sendJson(s,200,n.listSessions());const a=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const o=new URL(e,"http://stable-harness.local");if("/approvals"!==o.pathname)return;const t=o.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&a)return void sendJson(s,200,await n.listApprovals(a.status));const d=function readApprovalDecision(e){const o=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(o?.[1]&&o[2])return{id:decodeURIComponent(o[1]),status:"approve"===o[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&d){const e=await n.resolveApproval(d.id,d.status);return void sendJson(s,e?200:404,e??{error:"approval_not_found"})}if("GET"===e.method&&"/workflows"===e.url)return void sendJson(s,200,n.inspect().workflows);const i=function readWorkflowMermaidId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&i){const e=n.getWorkflow(i);return void sendJson(s,e?200:404,e?{mermaid:r(e)}:{error:"workflow_not_found"})}const u=function readWorkflowPlanId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&u){const e=n.getWorkflow(u);return void sendJson(s,e?200:404,e?o(e):{error:"workflow_not_found"})}const c=function readRequestInspectionId(e){const o=(e??"").match(/^\/requests\/([^/]+)$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&c){const e=n.inspectRequest(c);return void sendJson(s,e?200:404,e??{error:"request_not_found"})}const f=function readTraceRequestId(e){const o=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&f){const e=n.getRun(f);return void sendJson(s,e?200:404,e?t(e):{error:"run_not_found"})}if("POST"===e.method&&"/requests"===e.url){const o=await async function readJson(e){const o=[];for await(const t of e)o.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===o.length?{}:JSON.parse(Buffer.concat(o).toString("utf8"))}(e);return void sendJson(s,200,await n.request(function readRuntimeRequest(e){const o=function readToolCall(e){if("object"!=typeof e||null===e)return;const o=e;return"string"==typeof o.toolId?{toolId:o.toolId,args:o.args}:void 0}(e.toolCall),t=function readWorkflow(e){const o=readRecord(e);if(o)return{..."string"==typeof o.workflowId?{workflowId:o.workflowId}:{},..."string"==typeof o.routeId?{routeId:o.routeId}:{},...void 0!==o.input?{input:o.input}:{},..."object"==typeof o.metadata&&o.metadata?{metadata:o.metadata}:{}}}(e.workflow),r=function readMemory(e){const o=readRecord(e);if(o)return{..."string"==typeof o.namespace?{namespace:o.namespace}:{},...!1===o.recall||readRecord(o.recall)?{recall:o.recall}:{},...Array.isArray(o.candidates)?{candidates:o.candidates}:{}}}(e.memory),n=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}:{},...o?{toolCall:o}:{},...t?{workflow:t}:{},...r?{memory:r}:{},...n?{metadata:n}:{}}}(o)))}sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,500,{error:e instanceof Error?e.message:String(e)})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,o,t){e.writeHead(o,{"content-type":"application/json"}),e.end(JSON.stringify(t))}
@@ -1 +1 @@
1
- export function createInProcessClient(e){return{request:s=>e.request(s),subscribe:s=>e.subscribe(s),inspect:()=>e.inspect(),getRuntimePolicy:()=>e.getRuntimePolicy(),getWorkflow:s=>e.getWorkflow(s),getRun:s=>e.getRun(s),listRequests:s=>e.listRequests(s),listSessions:()=>e.listSessions(),inspectRequest:s=>e.inspectRequest(s),cancel:(s,t)=>e.cancel(s,t),stop:()=>e.stop()}}
1
+ export function createInProcessClient(e){return{request:s=>e.request(s),subscribe:s=>e.subscribe(s),inspect:()=>e.inspect(),getRuntimePolicy:()=>e.getRuntimePolicy(),getWorkflow:s=>e.getWorkflow(s),getRun:s=>e.getRun(s),listRequests:s=>e.listRequests(s),listSessions:()=>e.listSessions(),inspectRequest:s=>e.inspectRequest(s),listApprovals:s=>e.listApprovals(s),getApproval:s=>e.getApproval(s),resolveApproval:(s,t)=>e.resolveApproval(s,t),cancel:(s,t)=>e.cancel(s,t),stop:()=>e.stop()}}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{createCapabilitiesResponse as r,createModelsResponse as t,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as s}from"./openai-payload.js";import{createContentChunk as a,createRoleChunk as i,createStopChunk as d,missingFinalDelta as c,writeChatSse as l,writeRuntimeStreamEvent as u,writeSseHeaders as f}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(p,m={}){const g=function resolveServerOptions(e,r){const t=function readOpenAiProtocolConfig(e){const r=readRecord(e)??{};return readRecord(r.openaiCompatible)??readRecord(r["openai-compatible"])??readRecord(r.openai)??{}}(e.getRuntimePolicy().protocols);return{bearerToken:r.bearerToken??readConfigString(t.bearerToken),corsOrigins:r.corsOrigins??(o=t.corsOrigins,Array.isArray(o)?o.filter(e=>"string"==typeof e&&e.trim().length>0):void 0),modelAgentMap:r.modelAgentMap??readStringRecord(t.modelAgentMap),defaultModel:r.defaultModel??readConfigString(t.defaultModel)};var o}(p,m);return e(async(e,m)=>{try{if(function applyCorsHeaders(e,r,t){const o=function allowedCorsOrigin(e,r){if(e)return r.corsOrigins?.includes("*")?"*":r.corsOrigins?.includes(e)||function isLoopbackOrigin(e){try{const r=new URL(e);return"http:"===r.protocol&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,t);o&&(r.setHeader("access-control-allow-origin",o),r.setHeader("vary","origin"))}(e,m,g),function handleCors(e,r){return"OPTIONS"===e.method&&(r.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),r.end(),!0)}(e,m))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,g))return void sendJson(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(m,200,t(p,g));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(m,200,r());if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,p){const m=await async function readJson(e){const r=[];for await(const t of e)r.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===r.length?{}:JSON.parse(Buffer.concat(r).toString("utf8"))}(r);if(m.model&&!o(m.model,e,p))return void sendJson(t,404,{error:{message:`model_not_found: ${m.model}`,type:"invalid_request_error"}});const g=s(m,e,p,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(m.stream)return void await async function streamChatCompletion(e,r,t,o){f(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(r,i(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=u(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o),i=c(n,s.output);i&&l(r,a(o.requestId,t.model,i)),l(r,d(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,m,g);const h=await e.request(g);sendJson(t,200,n(m,h))}(p,e,m,g);sendJson(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readStringRecord(e){const r=readRecord(e);if(r)return Object.fromEntries(Object.entries(r).filter(e=>"string"==typeof e[1]))}function readConfigString(e){if("string"!=typeof e||!e.trim())return;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:e}function readHeaderString(e){const r=Array.isArray(e)?e[0]:e;return r&&r.trim()?r:void 0}function sendJson(e,r,t,o){e.writeHead(r,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(t))}
1
+ import{createServer as e}from"node:http";import{createCapabilitiesResponse as r,createModelsResponse as t,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as a}from"./openai-payload.js";import{createContentChunk as s,createRoleChunk as i,createStopChunk as d,missingFinalDelta as c,writeChatSse as l,writeRuntimeStreamEvent as u,writeSseHeaders as p}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(f,m={}){const h=function resolveServerOptions(e,r){const t=function readOpenAiProtocolConfig(e){const r=readRecord(e)??{};return readRecord(r.openaiCompatible)??readRecord(r["openai-compatible"])??readRecord(r.openai)??{}}(e.getRuntimePolicy().protocols);return{bearerToken:r.bearerToken??readConfigString(t.bearerToken),corsOrigins:r.corsOrigins??(o=t.corsOrigins,Array.isArray(o)?o.filter(e=>"string"==typeof e&&e.trim().length>0):void 0),modelAgentMap:r.modelAgentMap??readStringRecord(t.modelAgentMap),defaultModel:r.defaultModel??readConfigString(t.defaultModel)};var o}(f,m);return e(async(e,m)=>{try{if(function applyCorsHeaders(e,r,t){const o=function allowedCorsOrigin(e,r){if(e)return r.corsOrigins?.includes("*")?"*":r.corsOrigins?.includes(e)||function isLoopbackOrigin(e){try{const r=new URL(e);return"http:"===r.protocol&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,t);o&&(r.setHeader("access-control-allow-origin",o),r.setHeader("vary","origin"))}(e,m,h),function handleCors(e,r){return"OPTIONS"===e.method&&(r.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),r.end(),!0)}(e,m))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,h))return void sendJson(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(m,200,t(f,h));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(m,200,r());const v=function readApprovalList(e){if(!e?.startsWith("/v1/approvals"))return;const r=new URL(e,"http://stable-harness.local");if("/v1/approvals"!==r.pathname)return;const t=r.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&v)return void sendJson(m,200,{data:await f.listApprovals(v.status)});const g=function readApprovalDecision(e){const r=(e??"").match(/^\/v1\/approvals\/([^/]+)\/(approve|reject)$/u);if(r?.[1]&&r[2])return{id:decodeURIComponent(r[1]),status:"approve"===r[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&g){const e=await f.resolveApproval(g.id,g.status);return void sendJson(m,e?200:404,e?{data:e}:{error:{message:"approval_not_found",type:"invalid_request_error"}})}if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,f){const m=await async function readJson(e){const r=[];for await(const t of e)r.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===r.length?{}:JSON.parse(Buffer.concat(r).toString("utf8"))}(r);if(m.model&&!o(m.model,e,f))return void sendJson(t,404,{error:{message:`model_not_found: ${m.model}`,type:"invalid_request_error"}});const h=a(m,e,f,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(m.stream)return void await async function streamChatCompletion(e,r,t,o){p(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(r,i(o.requestId,t.model));let n="";const a=e.subscribe(e=>{e.requestId===o.requestId&&(n+=u(r,e,o.requestId,t.model)??"")});try{const a=await e.request(o),i=c(n,a.output);i&&l(r,s(o.requestId,t.model,i)),l(r,d(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{a()}}(e,t,m,h);const v=await e.request(h);sendJson(t,200,n(m,v))}(f,e,m,h);sendJson(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readStringRecord(e){const r=readRecord(e);if(r)return Object.fromEntries(Object.entries(r).filter(e=>"string"==typeof e[1]))}function readConfigString(e){if("string"!=typeof e||!e.trim())return;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:e}function readHeaderString(e){const r=Array.isArray(e)?e[0]:e;return r&&r.trim()?r:void 0}function sendJson(e,r,t,o){e.writeHead(r,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(t))}
@@ -5,6 +5,7 @@ export type ToolGatewayContext = {
5
5
  agentId: string;
6
6
  requestInput?: string;
7
7
  observedEvidence?: string;
8
+ approvalIds?: string[];
8
9
  };
9
10
  export type ToolGatewayTool = {
10
11
  id: string;