stable-harness 0.0.29 → 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +2 -2
  2. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
  3. package/packages/adapter-deepagents/dist/src/internal/gateway/tool-failure-events.d.ts +2 -0
  4. package/packages/adapter-deepagents/dist/src/internal/gateway/tool-failure-events.js +1 -0
  5. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +1 -0
  6. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -1
  7. package/packages/core/dist/index.d.ts +2 -0
  8. package/packages/core/dist/index.js +1 -1
  9. package/packages/core/dist/quality/synthesis/language.d.ts +3 -0
  10. package/packages/core/dist/quality/synthesis/language.js +1 -0
  11. package/packages/core/dist/quality/synthesis.js +1 -1
  12. package/packages/core/dist/recovery/tool-call.js +1 -1
  13. package/packages/core/dist/runtime/direct-tool-call.d.ts +2 -1
  14. package/packages/core/dist/runtime/direct-tool-call.js +1 -1
  15. package/packages/core/dist/runtime/events.d.ts +16 -0
  16. package/packages/core/dist/runtime/tool-failure.d.ts +41 -0
  17. package/packages/core/dist/runtime/tool-failure.js +1 -0
  18. package/packages/core/dist/runtime.js +1 -1
  19. package/packages/core/dist/trace.js +1 -1
  20. package/packages/core/dist/types.d.ts +4 -0
  21. package/packages/core/dist/workspace/tool-quality.d.ts +22 -0
  22. package/packages/core/dist/workspace/tool-quality.js +1 -0
  23. package/packages/core/dist/workspace/types.d.ts +5 -1
  24. package/packages/evaluation/dist/src/index.d.ts +2 -0
  25. package/packages/evaluation/dist/src/index.js +1 -1
  26. package/packages/evaluation/dist/src/tool-call-metrics.d.ts +26 -0
  27. package/packages/evaluation/dist/src/tool-call-metrics.js +1 -0
  28. package/packages/evaluation/dist/src/trace.js +1 -1
  29. package/packages/workspace-yaml/dist/documents.js +1 -1
  30. package/packages/workspace-yaml/dist/loader.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.29",
3
+ "version": "0.0.30",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -62,7 +62,7 @@
62
62
  "benchmark:tool-guard:matrix": "node scripts/benchmarks/tool-argument-guard-matrix.mjs",
63
63
  "test:langmem:sqlite:e2e": "node scripts/run-langmem-sqlite-e2e.mjs",
64
64
  "validate:workspace": "node scripts/validate-workspace.mjs",
65
- "test": "rm -rf dist/test && tsc -b test/tsconfig.json && node --test dist/test/*.test.js dist/test/adapter/*.test.js dist/test/adapter/*/*.test.js dist/test/compat/*.test.js dist/test/memory/*.test.js dist/test/protocol/*.test.js dist/test/retry/*.test.js dist/test/runtime/*.test.js dist/test/runtime/*/*.test.js dist/test/sdk/*.test.js dist/test/workspace/*.test.js",
65
+ "test": "rm -rf dist/test && tsc -b test/tsconfig.json && node --test dist/test/*.test.js dist/test/adapter/*.test.js dist/test/adapter/*/*.test.js dist/test/compat/*.test.js dist/test/evaluation/*.test.js dist/test/memory/*.test.js dist/test/protocol/*.test.js dist/test/retry/*.test.js dist/test/runtime/*.test.js dist/test/runtime/*/*.test.js dist/test/sdk/*.test.js dist/test/workspace/*.test.js",
66
66
  "test:langmem:maintenance:e2e": "node scripts/run-langmem-maintenance-e2e.mjs",
67
67
  "test:skill-mining:e2e": "node scripts/run-skill-candidate-mining-e2e.mjs",
68
68
  "prepublishOnly": "npm run build && npm run release:check-package",
@@ -1 +1 @@
1
- import{realpathSync as e}from"node:fs";import{repairCallSelection as t}from"@botbotgo/better-call";import{ToolMessage as o}from"@langchain/core/messages";import{normalizeArgsRecord as n}from"./builtin-args.js";import{repairBuiltinToolRequest as r}from"./builtin-call-repair.js";import{afterToolInvoke as i,beforeToolInvoke as a,createToolRepeatState as s,stringifyDeepAgentResult as l,toolControlProjection as d}from"./gateway-tools.js";import{observedToolEvidence as u,recordObservedToolEvidence as c}from"./gateway/tool-evidence.js";import{validateSkillFileBuiltinCall as p}from"./skill-file-policy.js";import{filterRepeatLimitedTools as g}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as f}from"./trace-projection.js";const m=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),h=new Set(["write_todos","read_todos","task","execute",...m]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,n){const r=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,r);return n({...o,tools:r,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,t,n){if(isFilesystemDisabled(e)&&m.has(t))return new o({tool_call_id:n.toolCall?.id??`stable-harness-${t}-policy`,name:t,content:`Filesystem builtin tool '${t}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,d={}){const c=d.repeatState??s(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(s,g){const f=s.toolCall?.name;if(!f||!h.has(f))return g(s);const m=await r({toolId:f,request:s,workspaceRoot:e.workspace.root});emitToolEvent(e,f,"agent.tool.start",m.toolCall?.args);const y="task"===f?await async function repairTaskCall(e,r){const i=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const o=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===o?[]:Array.isArray(o)?o.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(e);if(void 0===i)return{request:r};const a=n(r.toolCall?.args),s=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(a);if(s&&i.includes(s))return{request:r};const l=await t({call:{id:s,args:a},candidates:taskCallCandidates(e,i),mode:"repair"});if(l.ok){emitInventoryRepair(e,"repaired",s,l.candidateId,i);const t={...a,...l.args,subagent_type:l.candidateId};return{request:{...r,toolCall:{...r.toolCall,args:t}}}}emitInventoryRepair(e,"blocked",s,void 0,i,l.reason);const d=s?`: ${s}`:"",u=i.length>0?i.join(", "):"none";return{request:r,blocked:new o({tool_call_id:r.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${d}. Allowed task targets: ${u}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}(e,m):{request:m},v=y.request,b=y.blocked;if(b)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:b.content}),b;const k=validateFilesystemBuiltinCall(e,f,v);if(k)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:k.content}),k;const w=p(e,f,v);if(w)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:w.content}),w;try{const t=c?a(f,v.toolCall?.args,c):void 0;if(t)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:t.eventOutput}),builtinToolMessage(s,f,t.modelOutput);const o=await g(v),n=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):l(o)}(f,v,o),r=c?i(f,v.toolCall?.args,n,o,c):{};return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:r.eventOutput??n}),d.observedToolIds?.add(f),void 0===r.modelOutput?o:builtinToolMessage(s,f,r.modelOutput)}catch(t){const n=function recoverableBuiltinToolError(e,t,n,r){const i=formatError(r);if("task"===t&&/repeat limit reached for tool/iu.test(i)){const t=function formatObservedEvidence(e){const t=u(e);if(0===t.length)return"";const o=t.map(e=>[`Agent: ${e.agentId}`,`Tool: ${e.toolId}`,e.output].join("\n")).join("\n\n---\n\n");return o.length>12e3?`${o.slice(0,12e3)}\n[truncated]`:o}(e);return new o({tool_call_id:n.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need and do not send a synthesis task to another subagent for the same need. Finalize only from observedEvidence and other evidence that is already visible in this run. If the visible evidence is insufficient for a requested claim, report an explicit blocker or evidence gap instead of estimating, inventing, or using generic knowledge.",...t?{observedEvidence:t}:{},error:previewError(i)})})}if(/Received tool input did not match expected schema|Invalid input:/iu.test(i))return new o({tool_call_id:n.toolCall?.id??`stable-harness-${t}-argument-error`,name:t,status:"error",content:JSON.stringify({status:"tool_argument_error",toolId:t,instruction:"The upstream builtin tool rejected these arguments. Fix the tool arguments according to the tool schema, or choose a more appropriate available tool.",error:previewError(i)})})}(e,f,s,t);if(n)return emitToolEvent(e,f,"agent.tool.result",m.toolCall?.args,{output:n.content}),n;throw emitToolEvent(e,f,"agent.tool.result",m.toolCall?.args,{error:formatError(t)}),t}}}}function builtinToolMessage(e,t,n){return new o({tool_call_id:e.toolCall?.id??`stable-harness-${t}-repeat-guard`,name:t,content:n})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),n=[];if(n.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,n=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...n?[n,`${n}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return n.length>0?n:void 0}else n.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),n.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||n.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),n.length>0?n:void 0}function emitToolEvent(e,t,o,n,r={}){"string"==typeof r.output&&c(e,e.agent.id,t,r.output),e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:n}:{},...r,..."string"==typeof r.output?d(r.output):{},...f(t,o,n)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),n=t.split("/").filter(Boolean);if(!(n.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==n[e])return;return n.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function emitInventoryRepair(e,t,o,n,r,i){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:"inventory.repair",status:t,diagnostic:{layer:"task",owner:"stable_runtime_policy",originalId:o,repairedId:n,candidateIds:r,reason:i}}})}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&m.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
1
+ import{realpathSync as e}from"node:fs";import{repairCallSelection as t}from"@botbotgo/better-call";import{ToolMessage as o}from"@langchain/core/messages";import{normalizeArgsRecord as n}from"./builtin-args.js";import{repairBuiltinToolRequest as r}from"./builtin-call-repair.js";import{afterToolInvoke as i,beforeToolInvoke as a,createToolRepeatState as s,stringifyDeepAgentResult as l,toolControlProjection as u}from"./gateway-tools.js";import{observedToolEvidence as d,recordObservedToolEvidence as c}from"./gateway/tool-evidence.js";import{validateSkillFileBuiltinCall as p}from"./skill-file-policy.js";import{filterRepeatLimitedTools as g}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as f}from"./trace-projection.js";const m=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),h=new Set(["write_todos","read_todos","task","execute",...m]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,n){const r=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,r);return n({...o,tools:r,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,t,n){if(isFilesystemDisabled(e)&&m.has(t))return new o({tool_call_id:n.toolCall?.id??`stable-harness-${t}-policy`,name:t,content:`Filesystem builtin tool '${t}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,u={}){const c=u.repeatState??s(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(s,g){const f=s.toolCall?.name;if(!f||!h.has(f))return g(s);const m=await r({toolId:f,request:s,workspaceRoot:e.workspace.root});emitToolEvent(e,f,"agent.tool.start",m.toolCall?.args);const y="task"===f?await async function repairTaskCall(e,r){const i=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const o=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===o?[]:Array.isArray(o)?o.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(e);if(void 0===i)return{request:r};const a=n(r.toolCall?.args),s=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(a);if(s&&i.includes(s))return{request:r};const l=await t({call:{id:s,args:a},candidates:taskCallCandidates(e,i),mode:"repair"});if(l.ok){emitInventoryRepair(e,"repaired",s,l.candidateId,i);const t={...a,...l.args,subagent_type:l.candidateId};return{request:{...r,toolCall:{...r.toolCall,args:t}}}}emitInventoryRepair(e,"blocked",s,void 0,i,l.reason);const u=s?`: ${s}`:"",d=i.length>0?i.join(", "):"none";return{request:r,blocked:new o({tool_call_id:r.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${u}. Allowed task targets: ${d}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}(e,m):{request:m},v=y.request,b=y.blocked;if(b)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:b.content}),b;const k=validateFilesystemBuiltinCall(e,f,v);if(k)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:k.content}),k;const w=p(e,f,v);if(w)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:w.content}),w;try{const t=c?a(f,v.toolCall?.args,c):void 0;if(t)return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:t.eventOutput}),builtinToolMessage(s,f,function modelOutputForRepeatedBuiltin(e,t){return"task"!==e?t:function readPreviousOutput(e){try{const t=JSON.parse(e);if(!isRecord(t)||"repeated_tool_call_limit"!==t.status||"string"!=typeof t.previousOutput)return;const o=t.previousOutput.trim();return o.length>0?o:void 0}catch{return}}(t)??t}(f,t.modelOutput));const o=await g(v),n=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):l(o)}(f,v,o),r=c?i(f,v.toolCall?.args,n,o,c):{};return emitToolEvent(e,f,"agent.tool.result",v.toolCall?.args,{output:r.eventOutput??n}),u.observedToolIds?.add(f),void 0===r.modelOutput?o:builtinToolMessage(s,f,r.modelOutput)}catch(t){const n=function recoverableBuiltinToolError(e,t,n,r){const i=formatError(r);if("task"===t&&/repeat limit reached for tool/iu.test(i)){const t=function formatObservedEvidence(e){const t=d(e);if(0===t.length)return"";const o=t.map(e=>[`Agent: ${e.agentId}`,`Tool: ${e.toolId}`,e.output].join("\n")).join("\n\n---\n\n");return o.length>12e3?`${o.slice(0,12e3)}\n[truncated]`:o}(e);return new o({tool_call_id:n.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need and do not send a synthesis task to another subagent for the same need. Finalize only from observedEvidence and other evidence that is already visible in this run. If the visible evidence is insufficient for a requested claim, report an explicit blocker or evidence gap instead of estimating, inventing, or using generic knowledge.",...t?{observedEvidence:t}:{},error:previewError(i)})})}if(/Received tool input did not match expected schema|Invalid input:/iu.test(i))return new o({tool_call_id:n.toolCall?.id??`stable-harness-${t}-argument-error`,name:t,status:"error",content:JSON.stringify({status:"tool_argument_error",toolId:t,instruction:"The upstream builtin tool rejected these arguments. Fix the tool arguments according to the tool schema, or choose a more appropriate available tool.",error:previewError(i)})})}(e,f,s,t);if(n)return emitToolEvent(e,f,"agent.tool.result",m.toolCall?.args,{output:n.content}),n;throw emitToolEvent(e,f,"agent.tool.result",m.toolCall?.args,{error:formatError(t)}),t}}}}function builtinToolMessage(e,t,n){return new o({tool_call_id:e.toolCall?.id??`stable-harness-${t}-repeat-guard`,name:t,content:n})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),n=[];if(n.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,n=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...n?[n,`${n}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return n.length>0?n:void 0}else n.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),n.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||n.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),n.length>0?n:void 0}function emitToolEvent(e,t,o,n,r={}){"string"==typeof r.output&&c(e,e.agent.id,t,r.output),e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:n}:{},...r,..."string"==typeof r.output?u(r.output):{},...f(t,o,n)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),n=t.split("/").filter(Boolean);if(!(n.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==n[e])return;return n.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function emitInventoryRepair(e,t,o,n,r,i){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:"inventory.repair",status:t,diagnostic:{layer:"task",owner:"stable_runtime_policy",originalId:o,repairedId:n,candidateIds:r,reason:i}}})}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&m.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
@@ -0,0 +1,2 @@
1
+ import type { RuntimeAdapter } from "@stable-harness/core";
2
+ export declare function emitStructuredToolFailure(input: Parameters<RuntimeAdapter["run"]>[0], agentId: string, toolId: string, error: unknown): void;
@@ -0,0 +1 @@
1
+ import{toolCircuitOpenEvent as e,toolFailureEvent as o}from"@stable-harness/core";export function emitStructuredToolFailure(r,t,s,n){const i=o({requestId:r.requestId,sessionId:r.sessionId,agentId:t,toolId:s,error:n});r.emit(i),r.toolFailureTracker?.recordFailure(s)&&r.emit(e({requestId:r.requestId,sessionId:r.sessionId,agentId:t,toolId:s,reason:"runtime.tool.failure"===i.type?i.failure.reason:"unknown"}))}
@@ -12,6 +12,7 @@ export type ToolRepeatState = {
12
12
  maxSuccessfulCallsPerTool?: number;
13
13
  maxCallsByTool: Map<string, number>;
14
14
  maxSuccessfulCallsByTool: Map<string, number>;
15
+ returnPreviousOutputOnRepeatLimit: boolean;
15
16
  };
16
17
  export declare function buildGatewayTools(input: Parameters<RuntimeAdapter["run"]>[0], agentId: string, toolIds: string[], repairModel?: RuntimeToolRepairModel, repeatState?: ToolRepeatState | undefined): import("@langchain/core/tools").DynamicTool<string | ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>>, unknown>[];
17
18
  export declare function createToolRepeatState(config: unknown): ToolRepeatState | undefined;
@@ -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";export function buildGatewayTools(o,n,s,a,i=createToolRepeatState(o.workspace.runtime.toolGateway)){return o.toolGateway?s.flatMap(s=>{const l=o.toolGateway?.get(s);if(!l)return[];const u=o.workspace.tools.get(s),c=u?.schema??l.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 s=t.repeatState?beforeToolInvoke(t.toolId,t.args,t.repeatState):void 0;if(s)return emitToolResult(t.input,t.agentId,t.toolId,s.eventOutput),s.modelOutput;const a=await async function invokeGatewayTool(t,o,r,n,s){try{return await t.toolGateway.invoke({toolId:r,args:n,repairModel:s,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:o,requestInput:t.request.input,observedEvidence:formatObservedEvidenceForToolContext(t)}})}catch(o){if(function isToolArgumentValidationError(e){return e instanceof Error&&"ToolArgumentValidationError"===e.name&&"string"==typeof e.toolId&&Array.isArray(e.issues)}(o))return new e({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:formatToolArgumentError(o)});if(t.workspace.runtime.retry?.tools?.enabled)throw o;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(o),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),i=a instanceof e?String(a.content):stringifyDeepAgentResult(a.output),l=t.repeatState?afterToolInvoke(t.toolId,t.args,i,a,t.repeatState):{};return emitToolResult(t.input,t.agentId,t.toolId,l.eventOutput??i),void 0!==l.modelOutput?l.modelOutput:a instanceof e?a:i}({input:o,agentId:n,toolId:s,args:t,repairModel:a,repeatState:i}),{name:s,description:buildToolDescription(u?.description??l.description??s,c,o.workspace.runtime.toolGateway,s),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)}}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=repeatedToolCallLimitContent(e,o.latestSuccessfulOutputByTool.get(e));return{eventOutput:t,modelOutput:t}}const s=o.maxSuccessfulCallsByTool.get(e)??o.maxSuccessfulCallsPerTool;if(void 0!==s&&(o.successfulToolCounts.get(e)??0)>=s){const t=repeatedToolCallLimitContent(e,o.latestSuccessfulOutputByTool.get(e));return{eventOutput:t,modelOutput:t}}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}}}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)}});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)}
@@ -7,9 +7,11 @@ export * from "./memory-plugins.js";
7
7
  export * from "./runtime/persistence/queue.js";
8
8
  export * from "./runtime.js";
9
9
  export * from "./runtime/selection-repair.js";
10
+ export * from "./runtime/tool-failure.js";
10
11
  export * from "./runtime/persistence/stores.js";
11
12
  export * from "./trace.js";
12
13
  export * from "./types.js";
14
+ export * from "./workspace/tool-quality.js";
13
15
  export * from "./evaluations/index.js";
14
16
  export * from "./quality/index.js";
15
17
  export * from "./spec-driven/index.js";
@@ -1 +1 @@
1
- export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
1
+ export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
@@ -0,0 +1,3 @@
1
+ import type { QualityReviewInput } from "../types.js";
2
+ export type SynthesisLanguage = "en" | "zh";
3
+ export declare function detectSynthesisLanguage(input: QualityReviewInput): SynthesisLanguage;
@@ -0,0 +1 @@
1
+ export function detectSynthesisLanguage(e){const t=e.workspace.runtime.responseLanguage;if(function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(t)){const e=readString(t.language)??readString(t.locale)??readString(t.target);if(e&&/^(?:zh|zh-|chinese|中文|汉语|漢語)/iu.test(e))return"zh";if(e&&/^(?:en|en-|english)$/iu.test(e))return"en"}return/\p{Script=Han}/u.test(e.request.input)?"zh":"en"}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
@@ -1 +1 @@
1
- import{controlBlockers as e,controlGaps as t,successfulEvidenceItems as n}from"./event-evidence.js";export function synthesizeEvidenceOnlyReport(s,r,i){if(!i.enabled||!i.synthesis.enabled||"evidence_only"!==i.synthesis.mode)return;if("pass"===r.verdict||!function hasRecoverableSynthesisIssue(e){return e.issues.some(e=>"control_blocker"!==e.code)}(r))return;const o=n(s.events).slice(-i.synthesis.maxEvidenceItems),c=e(s.events),l=t(s.events);if(0===o.length&&0===c.length&&0===l.length)return;const a=function detectSynthesisLanguage(e){const t=e.workspace.runtime.responseLanguage;if(isRecord(t)){const e=readString(t.language)??readString(t.locale)??readString(t.target);if(e&&/^(?:zh|zh-|chinese|中文|汉语|漢語)/iu.test(e))return"zh";if(e&&/^(?:en|en-|english)$/iu.test(e))return"en"}return/\p{Script=Han}/u.test(e.request.input)?"zh":"en"}(s);return"zh"===a?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")}(o,c,l):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")}(o,c,l)}function resultSummaryLines(e,t,n,s,r){if(0===e.length)return["zh"===r?"- 没有成功证据可用于形成调查结论。":"- No successful evidence was available for a result."];const i=[...new Set(e.map(e=>humanSourceLabel(e.source)))],o=function observedResultLines(e,t){const value=(...t)=>function fieldValue(e,t){const n=new Set(t.map(normalizeFieldLabel));return e.find(e=>n.has(normalizeFieldLabel(e.label)))?.value}(e,t),n=value("Resolved Ticker","Symbol","Ticker"),s=value("Company Name","Company"),r=value("Close"),i=value("Open"),o=value("High"),c=value("Low"),l=value("Volume"),a=value("Date"),u=value("Inc Revenue","Revenue"),d=value("Filed 2026 03 06 Net Income","Net Income"),m=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"),h=value("Summary"),g=[];if((n||s)&&g.push("zh"===t?`- 标的已确认:${s??"公司未命名"}${n?`(${n})`:""}。`:`- Confirmed target: ${s??"unnamed company"}${n?` (${n})`:""}.`),r){const e=o&&c?"zh"===t?`,盘中区间 ${c}-${o}`:`, intraday range ${c}-${o}`:"",n=i?"zh"===t?`,开盘 ${i}`:`, open ${i}`:"",s=l?"zh"===t?`,成交量 ${l}`:`, volume ${l}`:"";g.push("zh"===t?`- 报价观察:${a?`${a} `:""}收盘 ${r}${n}${e}${s}。`:`- Quote observed: ${a?`${a} `:""}close ${r}${n}${e}${s}.`)}return(u||d||m||p)&&g.push("zh"===t?`- 财务观察:${compactParts([u&&`营收 ${u}`,d&&`净利润 ${d}`,m&&`经营利润 ${m}`,p&&`摊薄 EPS ${p}`]).join(";")}。`:`- Financials observed: ${compactParts([u&&`revenue ${u}`,d&&`net income ${d}`,m&&`operating income ${m}`,p&&`diluted EPS ${p}`]).join("; ")}.`),(f||h)&&g.push("zh"===t?`- 新闻观察:${compactParts([f,h]).join(";")}。`:`- News observed: ${compactParts([f,h]).join("; ")}.`),g.length>0?g:["zh"===t?"- 只观察到原始证据,无法形成更具体的归纳。":"- Only raw evidence was observed; no more specific synthesis is available."]}(evidenceFields(t),r),c=0===n.length&&0===s.length?"zh"===r?"未观察到运行时阻塞;未覆盖的业务、估值或风险细节不应视为已确认。":"No runtime blocker was observed; uncovered business, valuation, or risk details should not be treated as confirmed.":"zh"===r?`仍有 ${n.length+s.length} 个证据缺口或阻塞,结论应按部分调查读取。`:`${n.length+s.length} evidence gap(s) or blocker(s) remain, so read this as a partial result.`;return["zh"===r?`- 已基于 ${i.join("、")} 形成可交付结果。`:`- Result formed from ${i.join(", ")}.`,`- ${c}`,...o]}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:classifyFact(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,i=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).`,`- ${i}`]}function structuredFactSections(e,t){if(0===e.length)return["zh"===t?"- 证据缺口:没有可用于生成事实性结论的成功证据。":"- Evidence gap: no successful tool or delegated-task evidence was available."];const n=evidenceFields(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=n.filter(e=>e.kind===s.kind).slice(0,12),i=e.filter(e=>e.kind===s.kind&&!function hasExtractableFields(e){return extractKeyValueFields(e).some(e=>!isBoilerplateField(e.label)&&e.value.length>0)}(e.text)).slice(0,5);return 0===r.length&&0===i.length?[]:[s.title,...fieldTableLines(r,t),...i.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 evidenceFields(e){const t=new Set,n=[];for(const s of e)for(const e of parseEvidenceFields(s)){const s=`${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`;t.has(s)||(t.add(s),n.push(e))}return n}function parseEvidenceFields(e){return extractKeyValueFields(e.text).filter(e=>e.value.length>0&&!isBoilerplateField(e.label)).map(t=>{return{source:e.source,label:(n=t.label,/\p{Script=Han}/u.test(n)?n:normalizeFieldLabel(n).split(" ").filter(Boolean).map(e=>/^[A-Z0-9]+$/u.test(e)?e:e.charAt(0).toUpperCase()+e.slice(1)).join(" ")),value:formatEvidence(t.value),kind:"other"===classifyFact(`${t.label}: ${t.value}`)?e.kind:classifyFact(`${t.label}: ${t.value}`)};var n})}function fieldTableLines(e,t){return 0===e.length?[]:[..."zh"===t?["| 项目 | 数值 | 来源 |","|---|---:|---|"]:["| Field | Value | Source |","|---|---:|---|"],...e.map(e=>`| ${escapeTableCell(e.label)} | ${escapeTableCell(e.value)} | ${escapeTableCell(humanSourceLabel(e.source))} |`)]}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 isRecord(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 extractKeyValueFields(e){const t=[...e.matchAll(/(^|[\s,;|])([\p{L}][\p{L}\p{N} _./()%&+\-]{0,48})[::]\s*/gu)].filter(e=>void 0!==e.index);return t.map((n,s)=>{const r=(n.index??0)+n[0].length,i=t[s+1]?.index??e.length,o=t[s-1]?.[2]??"";return{label:cleanFieldLabel(n[2]??"",o),value:(c=e.slice(r,i),c.replace(/^[\s,;|.-]+/u,"").replace(/[\s,;|.-]+$/u,"").replace(/\s+/gu," ").trim())};var c}).filter(e=>e.label.length>0)}function cleanFieldLabel(e,t=""){const n=e.replace(/^(?:[-*]\s*)+/u,"").replace(/\s+/gu," ").trim(),s=n.split(" ").filter(Boolean);return s.length>1&&isBoilerplateField(t)||s.length>1&&/^[A-Z]$/u.test(s[0]??"")?s.slice(1).join(" "):n}function isBoilerplateField(e){return/^(?:status|control status|evidence tool|tool|source|来源)$/iu.test(e.trim())}function normalizeFieldLabel(e){return e.toLowerCase().replace(/[^a-z0-9\p{L}\p{N}]+/giu," ").trim()}function escapeTableCell(e){return e.replace(/\|/gu,"\\|").replace(/\r?\n/gu," ")}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 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 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(" ")}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
1
+ import{controlBlockers as e,controlGaps as t,successfulEvidenceItems as n}from"./event-evidence.js";import{detectSynthesisLanguage as s}from"./synthesis/language.js";export function synthesizeEvidenceOnlyReport(r,i,o){if(!o.enabled||!o.synthesis.enabled||"evidence_only"!==o.synthesis.mode)return;if("pass"===i.verdict||!function hasRecoverableSynthesisIssue(e){return e.issues.some(e=>"control_blocker"!==e.code)}(i))return;const l=n(r.events).slice(-o.synthesis.maxEvidenceItems),c=e(r.events),a=t(r.events);if(0===l.length&&0===c.length&&0===a.length)return;const u=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}(l);return u||("zh"===s(r)?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")}(l,c,a):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")}(l,c,a))}function resultSummaryLines(e,t,n,s,r){if(0===e.length)return["zh"===r?"- 没有成功证据可用于形成调查结论。":"- No successful evidence was available for a result."];const i=[...new Set(e.map(e=>humanSourceLabel(e.source)))],o=function observedResultLines(e,t){const value=(...t)=>function fieldValue(e,t){const n=new Set(t.map(normalizeFieldLabel));return e.find(e=>n.has(normalizeFieldLabel(e.label)))?.value}(e,t),n=value("Resolved Ticker","Symbol","Ticker"),s=value("Company Name","Company"),r=value("Close"),i=value("Open"),o=value("High"),l=value("Low"),c=value("Volume"),a=value("Date"),u=value("Inc Revenue","Revenue"),d=value("Filed 2026 03 06 Net Income","Net Income"),m=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"),h=value("Summary"),v=[];if((n||s)&&v.push("zh"===t?`- 标的已确认:${s??"公司未命名"}${n?`(${n})`:""}。`:`- Confirmed target: ${s??"unnamed company"}${n?` (${n})`:""}.`),r){const e=o&&l?"zh"===t?`,盘中区间 ${l}-${o}`:`, intraday range ${l}-${o}`:"",n=i?"zh"===t?`,开盘 ${i}`:`, open ${i}`:"",s=c?"zh"===t?`,成交量 ${c}`:`, volume ${c}`:"";v.push("zh"===t?`- 报价观察:${a?`${a} `:""}收盘 ${r}${n}${e}${s}。`:`- Quote observed: ${a?`${a} `:""}close ${r}${n}${e}${s}.`)}return(u||d||m||p)&&v.push("zh"===t?`- 财务观察:${compactParts([u&&`营收 ${u}`,d&&`净利润 ${d}`,m&&`经营利润 ${m}`,p&&`摊薄 EPS ${p}`]).join(";")}。`:`- Financials observed: ${compactParts([u&&`revenue ${u}`,d&&`net income ${d}`,m&&`operating income ${m}`,p&&`diluted EPS ${p}`]).join("; ")}.`),(f||h)&&v.push("zh"===t?`- 新闻观察:${compactParts([f,h]).join(";")}。`:`- News observed: ${compactParts([f,h]).join("; ")}.`),v.length>0?v:["zh"===t?"- 只观察到原始证据,无法形成更具体的归纳。":"- Only raw evidence was observed; no more specific synthesis is available."]}(evidenceFields(t),r),l=0===n.length&&0===s.length?"zh"===r?"未观察到运行时阻塞;未覆盖的业务、估值或风险细节不应视为已确认。":"No runtime blocker was observed; uncovered business, valuation, or risk details should not be treated as confirmed.":"zh"===r?`仍有 ${n.length+s.length} 个证据缺口或阻塞,结论应按部分调查读取。`:`${n.length+s.length} evidence gap(s) or blocker(s) remain, so read this as a partial result.`;return["zh"===r?`- 已基于 ${i.join("、")} 形成可交付结果。`:`- Result formed from ${i.join(", ")}.`,`- ${l}`,...o]}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:classifyFact(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,i=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).`,`- ${i}`]}function structuredFactSections(e,t){if(0===e.length)return["zh"===t?"- 证据缺口:没有可用于生成事实性结论的成功证据。":"- Evidence gap: no successful tool or delegated-task evidence was available."];const n=evidenceFields(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=n.filter(e=>e.kind===s.kind).slice(0,12),i=e.filter(e=>e.kind===s.kind&&!function hasExtractableFields(e){return extractKeyValueFields(e).some(e=>!isBoilerplateField(e.label)&&e.value.length>0)}(e.text)).slice(0,5);return 0===r.length&&0===i.length?[]:[s.title,...fieldTableLines(r,t),...i.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 evidenceFields(e){const t=new Set,n=[];for(const s of e)for(const e of parseEvidenceFields(s)){const s=`${normalizeFieldLabel(e.label)}\0${e.value.toLowerCase()}`;t.has(s)||(t.add(s),n.push(e))}return n}function parseEvidenceFields(e){return extractKeyValueFields(e.text).filter(e=>e.value.length>0&&!isBoilerplateField(e.label)).map(t=>{return{source:e.source,label:(n=t.label,/\p{Script=Han}/u.test(n)?n:normalizeFieldLabel(n).split(" ").filter(Boolean).map(e=>/^[A-Z0-9]+$/u.test(e)?e:e.charAt(0).toUpperCase()+e.slice(1)).join(" ")),value:formatEvidence(t.value),kind:"other"===classifyFact(`${t.label}: ${t.value}`)?e.kind:classifyFact(`${t.label}: ${t.value}`)};var n})}function fieldTableLines(e,t){return 0===e.length?[]:[..."zh"===t?["| 项目 | 数值 | 来源 |","|---|---:|---|"]:["| Field | Value | Source |","|---|---:|---|"],...e.map(e=>`| ${escapeTableCell(e.label)} | ${escapeTableCell(e.value)} | ${escapeTableCell(humanSourceLabel(e.source))} |`)]}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 extractKeyValueFields(e){const t=[...e.matchAll(/(^|[\s,;|])([\p{L}][\p{L}\p{N} _./()%&+\-]{0,48})[::]\s*/gu)].filter(e=>void 0!==e.index);return t.map((n,s)=>{const r=(n.index??0)+n[0].length,i=t[s+1]?.index??e.length,o=t[s-1]?.[2]??"";return{label:cleanFieldLabel(n[2]??"",o),value:(l=e.slice(r,i),l.replace(/^[\s,;|.-]+/u,"").replace(/[\s,;|.-]+$/u,"").replace(/\s+/gu," ").trim())};var l}).filter(e=>e.label.length>0)}function cleanFieldLabel(e,t=""){const n=e.replace(/^(?:[-*]\s*)+/u,"").replace(/\s+/gu," ").trim(),s=n.split(" ").filter(Boolean);return s.length>1&&isBoilerplateField(t)||s.length>1&&/^[A-Z]$/u.test(s[0]??"")?s.slice(1).join(" "):n}function isBoilerplateField(e){return/^(?:status|control status|evidence tool|tool|source|来源)$/iu.test(e.trim())}function normalizeFieldLabel(e){return e.toLowerCase().replace(/[^a-z0-9\p{L}\p{N}]+/giu," ").trim()}function escapeTableCell(e){return e.replace(/\|/gu,"\\|").replace(/\r?\n/gu," ")}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 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 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
- export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const t=readToolCallRecovery(e.policy);if(!0!==t.enabled)return;if(containsRawToolCallText(e.output,t)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,t)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const o=function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,t);return o?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",o.output,o.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastMissingEvidenceTools(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return readStringArray(o.missingEvidenceTools)}return[]}(e.events);return 0!==t.length?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(e){const t=readToolCallRecovery(e.policy);if(!0!==t.enabled||!1===t.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(e.output,t))return;const o=recentToolEvidence(e.events,6e3);return 0!==o.length?"zh"==(/\p{Script=Han}/u.test(e.request.input)?"zh":"en")?function buildChineseEvidenceSynthesis(e,t){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(e.output,o):function buildEnglishEvidenceSynthesis(e,t){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(e.output,o):void 0}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o&&"agent.tool.result"===o.phase&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ export function toolCallRecoveryEnabled(e){return!0===readToolCallRecovery(e).enabled}export function isRecoverableAdapterError(e,t){const o=readToolCallRecovery(t);if(!0!==o.enabled)return!1;const n=e instanceof Error?e.message:String(e);return readRegexps(o.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content|repeat limit reached for tool/iu]).some(e=>e.test(n))}export function buildAdapterErrorRecoveryPrompt(e,t,o){const n=t instanceof Error?t.message:String(t),r=readToolCallRecovery(o).instruction;return recoverRequest(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${n}`,"string"==typeof r?r:"Continue the same user request using the backend's normal tool-calling mechanism, then return a final human-readable answer.","Do not print raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer."])}export function buildResultRecoveryRequest(e){const t=readToolCallRecovery(e.policy);if(!0!==t.enabled)return;if(containsRawToolCallText(e.output,t)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer printed raw tool-call markup instead of executing the tool.","Continue the same user request by calling the available upstream tool normally when more evidence is required.","If you call a tool, the next assistant action must be the backend's structured tool call itself, with no prose before it.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"If the conversation context already contains enough evidence to answer, synthesize the final answer from that context instead.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text such as saying you will call or wait for a tool.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}if(containsToolExecutionErrorText(e.output,t)){const t=recentToolEvidence(e.events,1e3);return recoverRequest(e.request,["Stable runtime recovery: your previous final answer exposed a backend tool execution error instead of handling it.","Continue the same user request using the backend's normal structured tool-calling mechanism.","Do not retry the same invalid tool arguments. If the failed tool is not required to answer the user, synthesize the final answer from the available context instead.",...e.availableToolIds?.length?[`Available configured tools: ${e.availableToolIds.join(", ")}`,"Do not invent, print, or call tools that are not in this list."]:[],"Do not print tool error stacks, schema validation diagnostics, raw tool-call markup, JSON tool-call envelopes, or pseudo tool-call text in the final answer.",...t.length>0?["","Recent executed tool evidence:",...t]:[],"","Previous invalid final answer:",e.output])}const o=function lastConfiguredEventHint(e,t){const o=function readEventRecoveryHints(e){return(Array.isArray(e)?e:[]).flatMap(e=>isRecord(e)&&"string"==typeof e.instruction?[{..."string"==typeof e.toolId?{toolId:e.toolId}:{},..."string"==typeof e.phase?{phase:e.phase}:{},..."string"==typeof e.outputIncludes?{outputIncludes:e.outputIncludes}:{},..."string"==typeof e.outputMatches?{outputMatches:e.outputMatches}:{},instruction:e.instruction}]:[])}(t.eventRecoveryHints);if(0!==o.length)return e.flatMap(e=>function readMatchingHints(e,t){const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o?t.filter(e=>function eventMatchesHint(e,t){return(!t.toolId||e.toolId===t.toolId)&&(!t.phase||e.phase===t.phase)&&(t.outputIncludes?"string"==typeof e.output&&e.output.includes(t.outputIncludes):!t.outputMatches||"string"==typeof e.output&&new RegExp(t.outputMatches,"u").test(e.output))}(o,e)).map(e=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:e.instruction})):[]}(e,o)).at(-1)}(e.events,t);return o?recoverRequest(e.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",o.output,o.instruction]):void 0}export function buildExecutionContractRecoveryRequest(e){if(!0!==readToolCallRecovery(e.policy).enabled)return;const t=function lastMissingEvidenceTools(e){for(let t=e.length-1;t>=0;t-=1){const o=e[t];if("runtime.execution.contract.failed"===o?.type)return readStringArray(o.missingEvidenceTools)}return[]}(e.events);return 0!==t.length?recoverRequest(e.request,["Stable runtime recovery: the execution contract was not satisfied.",`Required evidence tool(s) were missing: ${t.join(", ")}`,"Continue the same user request by calling the missing required evidence tool(s) through the backend's normal structured tool-calling mechanism.","Do not produce a final answer until the required evidence tool call has executed and you have synthesized its result.","Do not print XML, JSON, markdown fences, pseudo tool-call text, plans, or future-intent text in the final answer."]):void 0}export function assertNoRawToolCallOutput(e,t){if(containsRawToolCallOutput(e,t))throw new Error(`Adapter returned raw tool-call text as the final answer after recovery. The backend must execute tools instead of printing tool-call markup. Output preview: ${previewOutput(e)}`)}export function containsRawToolCallOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRawToolCallText(e,o)}export function containsRecoverableResultOutput(e,t){const o=readToolCallRecovery(t);return!0===o.enabled&&containsRecoverableResultFailureText(e,o)}export function assertNoToolExecutionErrorOutput(e,t){const o=readToolCallRecovery(t);if(!0===o.enabled&&containsToolExecutionErrorText(e,o))throw new Error(`Adapter returned a tool execution error as the final answer after recovery. Output preview: ${previewOutput(e)}`)}export function rawToolCallFailureMessage(){return["The model attempted to call a tool but returned the tool call as text instead of executing it.","Please retry the request or use a model/backend configuration with reliable tool calling for this workspace."].join(" ")}export function buildEvidenceSynthesisOutput(e){const t=readToolCallRecovery(e.policy);if(!0!==t.enabled||!1===t.synthesizeFromEvidenceOnFailure||!containsRecoverableResultFailureText(e.output,t))return;const o=function latestDelegatedTaskReport(e){return e.flatMap(e=>{const t="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;if(!t||"agent.tool.result"!==t.phase||"task"!==t.toolId)return[];const o="string"==typeof t.output?t.output.trim():"";return function looksLikeFinalReport(e){const t=e.trim();return!(t.length<80)&&(/^#{1,3}\s+\S/mu.test(t)||/\n#{1,3}\s+\S/mu.test(t)||/\n-{3,}\n/u.test(t))}(o)?[o]:[]}).at(-1)||void 0}(e.events);if(o)return o;const n=recentToolEvidence(e.events,6e3);return 0!==n.length?"zh"==(/\p{Script=Han}/u.test(e.request.input)?"zh":"en")?function buildChineseEvidenceSynthesis(e,t){return["上游模型在已有工具证据后仍输出了伪工具调用;runtime 已拒绝该 raw 输出,并直接交付已执行工具返回的证据结果。","","已执行的工具证据:",...t,"",`被拒绝的最终输出预览:${previewRejectedOutput(e)}`].join("\n")}(e.output,n):function buildEnglishEvidenceSynthesis(e,t){return["The upstream model still returned pseudo tool-call text after tool evidence was available. The runtime rejected that raw output and is returning the executed tool evidence directly.","","Executed tool evidence:",...t,"",`Rejected final output preview: ${previewRejectedOutput(e)}`].join("\n")}(e.output,n):void 0}function previewOutput(e){const t=e.replace(/\s+/gu," ").trim();return t.length>300?`${t.slice(0,297)}...`:t}function previewRejectedOutput(e){return previewOutput(e).replace(/[<>]/gu,"")}export function rawToolCallOutputPreview(e){return previewOutput(e)}function recoverRequest(e,t){return{...e,input:[e.input,"",...t].join("\n"),metadata:{...e.metadata,stableHarnessRecovery:"tool_call"}}}function containsRawToolCallText(e,t){const o=readRegexps(t.rawOutputPatterns,[/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/\{\s*"type"\s*:\s*"[^"]+"\s*,\s*"args"\s*:/iu,/^\s*[A-Za-z_][\w.-]*\s*\([^)]{0,2000}\)\s*$/iu,/^\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|task)\s*$/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:tool_name|tool|name|subagent_type)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|task)"\s*:/iu,/```(?:json)?[\s\S]{0,2000}"query"\s*:[\s\S]{0,2000}"(?:max_results|count|freshness|market)"\s*:/iu]);return!![/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu,/<\s*\/?\s*tool_code\b[^>]*>/iu,/<\s*\/?\s*[A-Za-z_][\w.-]*(?:_command|_tool|_analysis|_investigate|_todos|task)\b[^>]*>/iu].some(t=>t.test(e))||function looksLikeStandaloneRecoveryCandidate(e){const t=e.trim();return t.length<=6e3||/^\s*(?:```|\{|\[|[A-Za-z_][\w.-]*\s*\()/u.test(t)}(e)&&(o.some(t=>t.test(e))||[/^[\s\S]{0,2400}\b(?:I need to|I will|I'll|I am going to|I'm going to)\s+(?:call|use|invoke|delegate)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I will|I'll|I am going to|I'm going to)\s+(?:investigate|gather|check)\b[\s\S]{0,1200}\b(?:evidence|cluster|system|results?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:waiting for|wait for)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:Would you like me to|Do you want me to|Should I|I can help with)\b[\s\S]{0,1200}\?[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bCould you please provide\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\b(?:I don't|I do not) have enough information\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|task|context)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}\bLet me\s+(?:call|use|invoke|delegate|check|run|verify|gather|inspect)\b[\s\S]{0,1200}\b(?:tool|function|specialist|subagent|results?|data|evidence|commands?)\b[\s\S]{0,400}$/iu,/^[\s\S]{0,2400}(?:我需要|我要|我会|我将|让我|我来|接下来我(?:会|将)?)\s*(?:先)?(?:调用|使用|运行|执行|检查|读取|收集|调查|验证|查看)[\s\S]{0,1200}$/iu,/^[\s\S]{0,2400}(?:要不要|是否需要|需要我|你想让我)[\s\S]{0,1200}(?:继续|进一步|帮你|分析|检查)[\s\S]{0,1200}[??][\s\S]{0,400}$/iu].some(t=>t.test(e)))}function containsRecoverableResultFailureText(e,t){return containsRawToolCallText(e,t)||containsToolExecutionErrorText(e,t)}function containsToolExecutionErrorText(e,t){return readRegexps(t.toolFailureOutputPatterns,[/^Error invoking tool ['"][^'"]+['"] with kwargs /iu,/Received tool input did not match expected schema/iu,/ToolMessage.*status.*error/iu]).some(t=>t.test(e))}function recentToolEvidence(e,t){return e.flatMap(e=>{const o="runtime.adapter.event"===e.type&&isRecord(e.event)?e.event:void 0;return o&&"agent.tool.result"===o.phase&&"string"==typeof o.toolId?function isControlToolOutput(e){if("string"!=typeof e||!e.trim().startsWith("{"))return!1;try{const t=JSON.parse(e),o=isRecord(t)?t.status:void 0;return"duplicate_tool_call"===o||"repeated_tool_call_limit"===o||"tool_argument_error"===o}catch{return!1}}(o.output)?[]:[`- ${o.toolId}: ${formatToolEvidence(o,t)}`]:[]}).slice(-5)}function formatToolEvidence(e,t=1e3){return"string"==typeof e.output&&e.output.trim()?e.output.slice(0,t):"string"==typeof e.error&&e.error.trim()?`error: ${e.error.slice(0,t)}`:isRecord(e.args)?`completed with args: ${previewOutput(JSON.stringify(e.args))}`:"completed"}function readToolCallRecovery(e){if(!isRecord(e))return{};const t=isRecord(e.recovery)?e.recovery:{};return isRecord(t.toolCall)?t.toolCall:{}}function readRegexps(e,t){const o=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&e.length>0).map(e=>new RegExp(e,"iu"));return o.length>0?o:t}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1,4 +1,4 @@
1
- import type { CompiledWorkspace, RuntimeEvent, RuntimeOutput, RuntimeRequest, RuntimeToolGateway, WorkspaceAgent } from "../types.js";
1
+ import type { CompiledWorkspace, RuntimeEvent, RuntimeOutput, RuntimeRequest, RuntimeToolFailureTracker, RuntimeToolGateway, WorkspaceAgent } from "../types.js";
2
2
  export declare function runDirectToolCall(input: {
3
3
  gateway: RuntimeToolGateway | undefined;
4
4
  workspace: CompiledWorkspace;
@@ -7,4 +7,5 @@ export declare function runDirectToolCall(input: {
7
7
  requestId: string;
8
8
  sessionId: string;
9
9
  agent: WorkspaceAgent;
10
+ toolFailureTracker?: RuntimeToolFailureTracker;
10
11
  }): Promise<RuntimeOutput>;
@@ -1 +1 @@
1
- export async function runDirectToolCall(t){const o=t.request.toolCall;if(!o)throw new Error("Direct tool call request is missing");if(!t.gateway)throw new Error("Runtime tool gateway is not configured");const e=await async function resolveDirectToolCall(t){if(t.agent.tools.includes(t.toolId)&&t.gateway.get(t.toolId))return{toolId:t.toolId,args:t.args};const o=await(t.gateway.repairToolCall?.({toolId:t.toolId,args:t.args,allowedToolIds:t.agent.tools,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input}}));if(o&&t.agent.tools.includes(o.toolId)&&t.gateway.get(o.toolId))return emitToolRepair(t,"repaired",o.toolId),o;if(!t.agent.tools.includes(t.toolId))throw emitToolRepair(t,"blocked",void 0,`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`),new Error(`Tool ${t.toolId} is not assigned to agent ${t.agent.id}`);throw emitToolRepair(t,"blocked",void 0,`Tool is not registered: ${t.toolId}`),new Error(`Tool is not registered: ${t.toolId}`)}({gateway:t.gateway,workspace:t.workspace,requestId:t.requestId,sessionId:t.sessionId,agent:t.agent,emit:t.emit,request:t.request,toolId:o.toolId,args:o.args});t.emit({type:"runtime.tool.direct.started",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:e.toolId});const s=await t.gateway.invoke({toolId:e.toolId,args:e.args,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,requestInput:t.request.input}});return t.emit({type:"runtime.tool.direct.completed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,toolId:s.toolId,output:s.output}),{text:(r=s.output,"string"==typeof r?r:JSON.stringify(r)),metadata:{toolCall:{toolId:s.toolId}}};var r}function emitToolRepair(t,o,e,s){t.emit({type:"runtime.inventory.repair",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,status:o,diagnostic:{layer:"tool",owner:"stable_runtime_policy",originalId:t.toolId,repairedId:e,candidateIds:t.agent.tools,reason:s}})}
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,6 +1,7 @@
1
1
  import type { MemoryCandidate, MemoryDecision, MemoryRecord } from "@stable-harness/memory";
2
2
  import type { ApprovalRequest } from "@stable-harness/governance";
3
3
  import type { RuntimeArtifact } from "../types.js";
4
+ import type { RuntimeToolFailureClassification, RuntimeToolFailureReason } from "./tool-failure.js";
4
5
  export type RuntimeInventoryRepairLayer = "agent" | "workflow_route" | "workflow" | "tool" | "task" | "skill";
5
6
  export type RuntimeInventoryRepairStatus = "repaired" | "blocked";
6
7
  export type RuntimeInventoryRepairDiagnostic = {
@@ -70,6 +71,21 @@ export type RuntimeEvent = {
70
71
  agentId: string;
71
72
  toolId: string;
72
73
  output: unknown;
74
+ } | {
75
+ type: "runtime.tool.failure";
76
+ requestId: string;
77
+ sessionId: string;
78
+ agentId: string;
79
+ toolId: string;
80
+ error: unknown;
81
+ failure: RuntimeToolFailureClassification;
82
+ } | {
83
+ type: "runtime.tool.circuit.opened";
84
+ requestId: string;
85
+ sessionId: string;
86
+ agentId: string;
87
+ toolId: string;
88
+ reason: RuntimeToolFailureReason;
73
89
  } | {
74
90
  type: "runtime.workflow.started";
75
91
  requestId: string;
@@ -0,0 +1,41 @@
1
+ import type { RuntimeEvent } from "./events.js";
2
+ export type RuntimeToolFailureKind = "transient" | "business" | "control" | "unknown";
3
+ export type RuntimeToolFailureReason = "timeout" | "network" | "rate_limit" | "server_error" | "invalid_input" | "not_found" | "permission_denied" | "approval_required" | "schema_repair_failed" | "repeat_limit" | "tool_execution_failed" | "unknown";
4
+ export type RuntimeToolFailureClassification = {
5
+ kind: RuntimeToolFailureKind;
6
+ reason: RuntimeToolFailureReason;
7
+ retryable: boolean;
8
+ message: string;
9
+ };
10
+ export type RuntimeToolFailurePolicy = {
11
+ enabled?: boolean;
12
+ circuitBreaker?: {
13
+ enabled?: boolean;
14
+ failureThreshold?: number;
15
+ };
16
+ };
17
+ export type RuntimeToolFailureTracker = {
18
+ isCircuitOpen(toolId: string): boolean;
19
+ recordFailure(toolId: string): boolean;
20
+ recordSuccess(toolId: string): void;
21
+ };
22
+ export declare function createToolFailureTracker(policy: RuntimeToolFailurePolicy | undefined): RuntimeToolFailureTracker;
23
+ export declare function classifyToolFailure(error: unknown): RuntimeToolFailureClassification;
24
+ export declare function toolFailureEvent(input: {
25
+ requestId: string;
26
+ sessionId: string;
27
+ agentId: string;
28
+ toolId: string;
29
+ error: unknown;
30
+ }): Extract<RuntimeEvent, {
31
+ type: "runtime.tool.failure";
32
+ }>;
33
+ export declare function toolCircuitOpenEvent(input: {
34
+ requestId: string;
35
+ sessionId: string;
36
+ agentId: string;
37
+ toolId: string;
38
+ reason: RuntimeToolFailureReason;
39
+ }): Extract<RuntimeEvent, {
40
+ type: "runtime.tool.circuit.opened";
41
+ }>;
@@ -0,0 +1 @@
1
+ export function createToolFailureTracker(e){const t=function readCircuitThreshold(e){const t=e?.circuitBreaker,i=t?.failureThreshold;return!1!==e?.enabled&&!0===t?.enabled&&"number"==typeof i&&Number.isInteger(i)&&i>0?i:void 0}(e),i=new Map,r=new Set;return{isCircuitOpen:e=>r.has(e),recordFailure(e){if(void 0===t)return!1;const n=(i.get(e)??0)+1;return i.set(e,n),n>=t&&(r.add(e),!0)},recordSuccess(e){i.delete(e),r.delete(e)}}}export function classifyToolFailure(e){const t=function formatToolFailureMessage(e){return e instanceof Error?e.message:String(e)}(e),i=t.toLowerCase();return/\b(timeout|timed out|etimedout)\b/u.test(i)?classification("transient","timeout",!0,t):/\b(econnreset|econnrefused|enotfound|network|fetch failed)\b/u.test(i)?classification("transient","network",!0,t):/\b(rate.?limit|too many requests|429)\b/u.test(i)?classification("transient","rate_limit",!0,t):/\b(500|502|503|504|server error|bad gateway)\b/u.test(i)?classification("transient","server_error",!0,t):/\b(approval_required|requires approval|permission denied|forbidden|401|403)\b/u.test(i)?classification("business",i.includes("approval")?"approval_required":"permission_denied",!1,t):/\b(schema repair failed|tool_argument_validation_failed|invalid input|expected .* received|validation)\b/u.test(i)?classification("control",i.includes("schema repair")?"schema_repair_failed":"invalid_input",!1,t):/\b(repeated_tool_call_limit|repeat limit)\b/u.test(i)?classification("control","repeat_limit",!1,t):/\b(not found|404)\b/u.test(i)?classification("business","not_found",!1,t):classification("unknown","tool_execution_failed",!1,t)}export function toolFailureEvent(e){const t=classifyToolFailure(e.error);return{type:"runtime.tool.failure",...e,failure:t}}export function toolCircuitOpenEvent(e){return{type:"runtime.tool.circuit.opened",...e}}function classification(e,t,i,r){return{kind:e,reason:t,retryable:i,message:r}}
@@ -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{runWorkflowRequest as x}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)};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 x({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});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}),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,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{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 +1 @@
1
- export function projectRuntimeTrace(e){return e.events.map(projectEvent).filter(isTraceEntry)}export function projectEvent(e){return"runtime.request.started"===e.type||"runtime.request.completed"===e.type||"runtime.request.failed"===e.type?base(e,"request",e.type):"runtime.request.cancelled"===e.type?base(e,"request",e.type,{reason:e.reason}):"runtime.execution.contract.failed"===e.type?base(e,"request",e.type,{reason:e.reason,missingEvidenceTools:e.missingEvidenceTools}):"runtime.inventory.repair"===e.type?base(e,"adapter","runtime.inventory.repair",{status:e.status,...e.diagnostic}):"runtime.tool.direct.started"===e.type?base(e,"tool","runtime.tool.direct.started",{toolId:e.toolId}):"runtime.tool.direct.completed"===e.type?base(e,"tool","runtime.tool.direct.completed",{toolId:e.toolId}):"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?base(e,"workflow",e.type,{workflowId:e.workflowId,adapter:e.adapter}):function isSpecDrivenPhaseEvent(e){return e.type.startsWith("runtime.specDriven.phase.")}(e)?base(e,"spec",e.type,{phaseId:e.phaseId,..."workflowId"in e&&e.workflowId?{workflowId:e.workflowId}:{},..."reason"in e?{reason:e.reason}:{},..."artifact"in e&&e.artifact?{artifact:e.artifact}:{}}):"runtime.adapter.event"===e.type?function adapterTrace(e){const t=e.event;if(isRecord(t)&&"string"==typeof t.phase){const r=function semanticAdapterTrace(e,t){if("inventory.repair"===t.phase)return base(e,"adapter","runtime.inventory.repair",{status:t.status,...isRecord(t.diagnostic)?t.diagnostic:{}});const r=function readTraceType(e){const t=readString(e);return"request"===t||"tool"===t||"workflow"===t||"spec"===t||"adapter"===t||"memory"===t||"artifact"===t||"progress"===t||"plan"===t||"delegation"===t?t:void 0}(t.traceType),o=readString(t.traceLabel);return r&&o?base(e,r,o,t):void 0}(e,t);return r||base(e,"adapter",t.phase.startsWith("agent.")?t.phase:`adapter.${t.phase}`,t)}return base(e,"adapter","runtime.adapter.event",{event:t})}(e):"runtime.artifact.created"===e.type?base(e,"artifact","runtime.artifact.created",{artifact:e.artifact}):e.type.startsWith("runtime.memory.")?base(e,"memory",e.type):"runtime.progress.narration"===e.type?base(e,"progress",e.type,{message:e.message,provider:e.provider,sourceEventTypes:e.sourceEventTypes}):void 0}export function readPlanTodos(e){const t=function readPlanRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e),r=function readTodosArray(e){const t=isRecord(e?.args)?e.args:void 0;return Array.isArray(e?.todos)?e.todos:Array.isArray(t?.todos)?t.todos:[]}(t);return r.map(readTodo).filter(isPlanTodoItem)}function readTodo(e){if(isRecord(e)&&"string"==typeof e.content)return{content:e.content,status:"string"==typeof e.status?e.status:"pending"}}function isPlanTodoItem(e){return void 0!==e}function base(e,t,r,o){return{type:t,label:r,agentId:e.agentId,requestId:e.requestId,detail:o}}function isTraceEntry(e){return void 0!==e}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
1
+ export function projectRuntimeTrace(e){return e.events.map(projectEvent).filter(isTraceEntry)}export function projectEvent(e){return"runtime.request.started"===e.type||"runtime.request.completed"===e.type||"runtime.request.failed"===e.type?base(e,"request",e.type):"runtime.request.cancelled"===e.type?base(e,"request",e.type,{reason:e.reason}):"runtime.execution.contract.failed"===e.type?base(e,"request",e.type,{reason:e.reason,missingEvidenceTools:e.missingEvidenceTools}):"runtime.inventory.repair"===e.type?base(e,"adapter","runtime.inventory.repair",{status:e.status,...e.diagnostic}):"runtime.tool.direct.started"===e.type?base(e,"tool","runtime.tool.direct.started",{toolId:e.toolId}):"runtime.tool.direct.completed"===e.type?base(e,"tool","runtime.tool.direct.completed",{toolId:e.toolId}):"runtime.tool.failure"===e.type?base(e,"tool","runtime.tool.failure",{toolId:e.toolId,...e.failure}):"runtime.tool.circuit.opened"===e.type?base(e,"tool","runtime.tool.circuit.opened",{toolId:e.toolId,reason:e.reason}):"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?base(e,"workflow",e.type,{workflowId:e.workflowId,adapter:e.adapter}):function isSpecDrivenPhaseEvent(e){return e.type.startsWith("runtime.specDriven.phase.")}(e)?base(e,"spec",e.type,{phaseId:e.phaseId,..."workflowId"in e&&e.workflowId?{workflowId:e.workflowId}:{},..."reason"in e?{reason:e.reason}:{},..."artifact"in e&&e.artifact?{artifact:e.artifact}:{}}):"runtime.adapter.event"===e.type?function adapterTrace(e){const t=e.event;if(isRecord(t)&&"string"==typeof t.phase){const r=function semanticAdapterTrace(e,t){if("inventory.repair"===t.phase)return base(e,"adapter","runtime.inventory.repair",{status:t.status,...isRecord(t.diagnostic)?t.diagnostic:{}});const r=function readTraceType(e){const t=readString(e);return"request"===t||"tool"===t||"workflow"===t||"spec"===t||"adapter"===t||"memory"===t||"artifact"===t||"progress"===t||"plan"===t||"delegation"===t?t:void 0}(t.traceType),o=readString(t.traceLabel);return r&&o?base(e,r,o,t):void 0}(e,t);return r||base(e,"adapter",t.phase.startsWith("agent.")?t.phase:`adapter.${t.phase}`,t)}return base(e,"adapter","runtime.adapter.event",{event:t})}(e):"runtime.artifact.created"===e.type?base(e,"artifact","runtime.artifact.created",{artifact:e.artifact}):e.type.startsWith("runtime.memory.")?base(e,"memory",e.type):"runtime.progress.narration"===e.type?base(e,"progress",e.type,{message:e.message,provider:e.provider,sourceEventTypes:e.sourceEventTypes}):void 0}export function readPlanTodos(e){const t=function readPlanRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e),r=function readTodosArray(e){const t=isRecord(e?.args)?e.args:void 0;return Array.isArray(e?.todos)?e.todos:Array.isArray(t?.todos)?t.todos:[]}(t);return r.map(readTodo).filter(isPlanTodoItem)}function readTodo(e){if(isRecord(e)&&"string"==typeof e.content)return{content:e.content,status:"string"==typeof e.status?e.status:"pending"}}function isPlanTodoItem(e){return void 0!==e}function base(e,t,r,o){return{type:t,label:r,agentId:e.agentId,requestId:e.requestId,detail:o}}function isTraceEntry(e){return void 0!==e}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
@@ -3,6 +3,7 @@ import type { ApprovalQueue } 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";
6
+ import type { RuntimeToolFailureTracker } from "./runtime/tool-failure.js";
6
7
  import type { RuntimeArtifact, RuntimeOutput, RuntimeRecordState, RuntimeRequest, RuntimeResponse, RuntimeRunFilter, RuntimeRunRecord } from "./runtime/types.js";
7
8
  import type { RuntimeToolGateway } from "./runtime/tool-gateway.js";
8
9
  import type { CompiledWorkspace, WorkspaceAgent, WorkspaceRuntimePolicy } from "./workspace/types.js";
@@ -12,9 +13,11 @@ export type { RuntimeCapabilityContext, RuntimeCapabilityModule, RuntimeCapabili
12
13
  export type { RuntimeProgressNarrationOptions, RuntimeProgressNarrationProvider } from "./runtime/progress-narration.js";
13
14
  export type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactInput, RuntimeArtifactRecord, RuntimeArtifactStore, RuntimeCancelIntentInput, RuntimeHeartbeatInput, RuntimeMemoryCandidateInput, RuntimeOutput, RuntimeQueueClaimInput, RuntimeQueueRecord, RuntimeQueueStore, RuntimeRecoveryIntent, RuntimeRecordState, RuntimeRequest, RuntimeRequestControlRecord, RuntimeRequestMemory, RuntimeResponse, RuntimeRunFilter, RuntimeRunRecord, RuntimeStore, RuntimeStoreRunPatch, RuntimeStuckRequestInput, } from "./runtime/types.js";
14
15
  export type { CompiledWorkspace, WorkspaceAdapterPolicy, WorkspaceAgent, WorkspaceBoundaryDiagnostic, WorkspaceBoundaryDiagnosticCode, WorkspaceBoundaryScanPolicy, WorkspaceMemory, WorkspaceModel, WorkspaceRetryPolicy, WorkspaceRetryReason, WorkspaceRetryTargetPolicy, WorkspaceRuntimePolicy, WorkspaceSkill, WorkspaceTool, WorkspaceToolRetryPolicy, WorkspaceValidationPolicy, } from "./workspace/types.js";
16
+ export type { WorkspaceToolQualityDiagnostic, WorkspaceToolQualityDiagnosticCode, WorkspaceToolQualityPolicy, } from "./workspace/tool-quality.js";
15
17
  export type { WorkspaceEvaluation, WorkspaceEvaluationCase, } from "./evaluations/index.js";
16
18
  export type { SpecDrivenPhaseId, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, } from "./spec-driven/index.js";
17
19
  export type { RuntimeToolCallRequest, RuntimeToolGateway, RuntimeToolGatewayContext, RuntimeToolRepairModel, RuntimeToolGatewayTool, } from "./runtime/tool-gateway.js";
20
+ export type { RuntimeToolFailureClassification, RuntimeToolFailureKind, RuntimeToolFailurePolicy, RuntimeToolFailureReason, RuntimeToolFailureTracker, } from "./runtime/tool-failure.js";
18
21
  export type RuntimeAdapterContext = {
19
22
  workspace: CompiledWorkspace;
20
23
  agent: WorkspaceAgent;
@@ -24,6 +27,7 @@ export type RuntimeAdapterContext = {
24
27
  memory?: RuntimeMemoryContext;
25
28
  pluginMemories?: RuntimeMemoryContext[];
26
29
  toolGateway?: RuntimeToolGateway;
30
+ toolFailureTracker?: RuntimeToolFailureTracker;
27
31
  requestState?: Map<string, unknown>;
28
32
  emit: RuntimeEmit;
29
33
  };
@@ -0,0 +1,22 @@
1
+ import type { CompiledWorkspace } from "./types.js";
2
+ export type WorkspaceToolQualityDiagnosticCode = "missing-description" | "weak-description" | "missing-schema" | "missing-output-contract" | "missing-permission-metadata" | "overlapping-description";
3
+ export type WorkspaceToolQualitySeverity = "error" | "warning";
4
+ export type WorkspaceToolQualityDiagnostic = {
5
+ code: WorkspaceToolQualityDiagnosticCode;
6
+ layer: "tool";
7
+ owner: "stable_runtime_policy" | "workspace_config";
8
+ resources: string[];
9
+ severity: WorkspaceToolQualitySeverity;
10
+ message: string;
11
+ locations: string[];
12
+ details?: Record<string, unknown>;
13
+ };
14
+ export type WorkspaceToolQualityPolicy = {
15
+ enabled?: boolean;
16
+ requireSchema?: boolean;
17
+ requireOutputContract?: boolean;
18
+ requirePermissionMetadata?: boolean;
19
+ failOn?: Partial<Record<WorkspaceToolQualityDiagnosticCode, boolean>>;
20
+ };
21
+ export declare function scanWorkspaceToolQuality(workspace: CompiledWorkspace, policy?: WorkspaceToolQualityPolicy): WorkspaceToolQualityDiagnostic[];
22
+ export declare function assertWorkspaceToolQualityDiagnostics(diagnostics: WorkspaceToolQualityDiagnostic[]): void;
@@ -0,0 +1 @@
1
+ export function scanWorkspaceToolQuality(t,e={}){if(!1===e.enabled)return[];const o=[...t.tools.values()].flatMap(t=>function scanTool(t,e){const o=[],i=t.description?.trim()??"";return i?function isWeakDescription(t,e){const o=t.replace(/[_-]+/gu," ").toLowerCase(),i=e.replace(/[_-]+/gu," ").toLowerCase();return i.length<24||i===o}(t.id,i)&&o.push(diagnostic(t,"weak-description","Tool description is too short or repeats only the tool id.",e)):o.push(diagnostic(t,"missing-description","Tool is missing a description.",e)),!0===e.requireSchema&&void 0===t.schema&&o.push(diagnostic(t,"missing-schema","Tool is missing an input schema.",e)),!0!==e.requireOutputContract||void 0!==t.outputSchema||hasMetadata(t,"outputContract")||o.push(diagnostic(t,"missing-output-contract","Tool is missing output contract metadata.",e)),!0!==e.requirePermissionMetadata||hasMetadata(t,"permissions")||o.push(diagnostic(t,"missing-permission-metadata","Tool is missing permission metadata.",e)),o}(t,e));return[...o,...scanOverlaps([...t.tools.values()])]}export function assertWorkspaceToolQualityDiagnostics(t){const e=t.filter(t=>"error"===t.severity);if(0!==e.length)throw new Error(`Workspace tool quality gate failed:\n${e.map(formatDiagnostic).join("\n")}`)}function scanOverlaps(t){const e=[];for(let o=0;o<t.length;o+=1)for(let i=o+1;i<t.length;i+=1){const s=t[o],n=t[i],a=descriptionOverlap(s.description,n.description);a<.8||e.push({code:"overlapping-description",layer:"tool",owner:"workspace_config",resources:[s.id,n.id],severity:"warning",message:`Tools ${s.id} and ${n.id} have highly overlapping descriptions; add explicit selection boundaries.`,locations:[s.sourcePath,n.sourcePath].filter(isString),details:{score:a}})}return e}function diagnostic(t,e,o,i){return{code:e,layer:"tool",owner:"workspace_config",resources:[t.id],severity:!0===i.failOn?.[e]?"error":"warning",message:o,locations:t.sourcePath?[t.sourcePath]:[]}}function descriptionOverlap(t,e){const o=tokenSet(t),i=tokenSet(e);return o.size<4||i.size<4?0:[...o].filter(t=>i.has(t)).length/Math.min(o.size,i.size)}function tokenSet(t){return new Set((t??"").toLowerCase().split(/[^a-z0-9]+/u).filter(t=>t.length>2))}function hasMetadata(t,e){return"object"==typeof t.metadata&&null!==t.metadata&&!Array.isArray(t.metadata)&&void 0!==t.metadata[e]}function formatDiagnostic(t){return`- ${t.code}: ${t.message} (${t.resources.join(", ")})`}function isString(t){return"string"==typeof t&&t.length>0}
@@ -1,6 +1,7 @@
1
1
  import type { WorkspaceEvaluation } from "../evaluations/index.js";
2
2
  import type { WorkspaceSpecDrivenWorkflowPolicy } from "../spec-driven/index.js";
3
3
  import type { WorkspaceWorkflow, WorkspaceWorkflowEdge, WorkspaceWorkflowRoutingPolicy } from "../workflows/index.js";
4
+ import type { WorkspaceToolQualityDiagnostic, WorkspaceToolQualityPolicy } from "./tool-quality.js";
4
5
  export type WorkspaceModel = {
5
6
  id: string;
6
7
  provider: string;
@@ -11,6 +12,8 @@ export type WorkspaceTool = {
11
12
  id: string;
12
13
  description?: string;
13
14
  schema?: unknown;
15
+ outputSchema?: unknown;
16
+ metadata?: Record<string, unknown>;
14
17
  implementation?: string;
15
18
  sourcePath?: string;
16
19
  };
@@ -77,6 +80,7 @@ export type WorkspaceAgentRoute = {
77
80
  };
78
81
  export type WorkspaceValidationPolicy = {
79
82
  boundaryScan?: WorkspaceBoundaryScanPolicy;
83
+ toolQuality?: WorkspaceToolQualityPolicy;
80
84
  };
81
85
  export type WorkspaceBoundaryScanPolicy = {
82
86
  enabled?: boolean;
@@ -125,5 +129,5 @@ export type CompiledWorkspace = {
125
129
  memories: Map<string, WorkspaceMemory>;
126
130
  workflows: Map<string, WorkspaceWorkflow>;
127
131
  evaluations?: Map<string, WorkspaceEvaluation>;
128
- diagnostics?: WorkspaceBoundaryDiagnostic[];
132
+ diagnostics?: Array<WorkspaceBoundaryDiagnostic | WorkspaceToolQualityDiagnostic>;
129
133
  };
@@ -1,2 +1,4 @@
1
1
  export { createTraceRecorder, createReplayManifest, createEvaluationBundle } from "./trace.js";
2
+ export { computeToolCallMetrics } from "./tool-call-metrics.js";
2
3
  export type { EvaluationBundle, ReplayManifest } from "./types.js";
4
+ export type { PerToolCallMetrics, ToolCallMetrics } from "./tool-call-metrics.js";
@@ -1 +1 @@
1
- export{createTraceRecorder,createReplayManifest,createEvaluationBundle}from"./trace.js";
1
+ export{createTraceRecorder,createReplayManifest,createEvaluationBundle}from"./trace.js";export{computeToolCallMetrics}from"./tool-call-metrics.js";
@@ -0,0 +1,26 @@
1
+ import type { RuntimeEvent } from "@stable-harness/core";
2
+ export type ToolCallMetrics = {
3
+ toolStarted: number;
4
+ toolCompleted: number;
5
+ toolFailures: number;
6
+ inventoryRepairs: number;
7
+ inventoryRepairBlocks: number;
8
+ argumentFailureCount: number;
9
+ transientFailureCount: number;
10
+ controlFailureCount: number;
11
+ businessFailureCount: number;
12
+ circuitOpenCount: number;
13
+ firstAttemptArgumentValidityRate: number | undefined;
14
+ schemaRepairFailureRate: number | undefined;
15
+ repairRate: number | undefined;
16
+ failureRate: number | undefined;
17
+ perTool: Record<string, PerToolCallMetrics>;
18
+ };
19
+ export type PerToolCallMetrics = {
20
+ started: number;
21
+ completed: number;
22
+ failures: number;
23
+ inventoryRepairs: number;
24
+ circuitOpen: number;
25
+ };
26
+ export declare function computeToolCallMetrics(events: RuntimeEvent[]): ToolCallMetrics;
@@ -0,0 +1 @@
1
+ export function computeToolCallMetrics(t){const e=function createCounters(){return{toolStarted:0,toolCompleted:0,toolFailures:0,inventoryRepairs:0,inventoryRepairBlocks:0,argumentFailureCount:0,transientFailureCount:0,controlFailureCount:0,businessFailureCount:0,circuitOpenCount:0,firstAttemptArgumentValidityRate:void 0,schemaRepairFailureRate:void 0,repairRate:void 0,failureRate:void 0,perTool:new Map}}();for(const o of t)applyEvent(e,o);return{...e,firstAttemptArgumentValidityRate:rate(e.toolStarted-e.argumentFailureCount,e.toolStarted),schemaRepairFailureRate:rate(e.argumentFailureCount,e.toolStarted),repairRate:rate(e.inventoryRepairs,e.toolStarted+e.inventoryRepairs),failureRate:rate(e.toolFailures,e.toolStarted),perTool:Object.fromEntries([...e.perTool.entries()])}}function applyEvent(t,e){"runtime.tool.direct.started"===e.type?(t.toolStarted+=1,perTool(t,e.toolId).started+=1):"runtime.tool.direct.completed"===e.type?(t.toolCompleted+=1,perTool(t,e.toolId).completed+=1):"runtime.tool.failure"===e.type?function applyFailure(t,e){t.toolFailures+=1,perTool(t,e.toolId).failures+=1,"transient"===e.failure.kind&&(t.transientFailureCount+=1),"control"===e.failure.kind&&(t.controlFailureCount+=1),"business"===e.failure.kind&&(t.businessFailureCount+=1),"invalid_input"!==e.failure.reason&&"schema_repair_failed"!==e.failure.reason||(t.argumentFailureCount+=1)}(t,e):"runtime.tool.circuit.opened"===e.type?(t.circuitOpenCount+=1,perTool(t,e.toolId).circuitOpen+=1):"runtime.inventory.repair"===e.type?function applyInventoryRepair(t,e){if("repaired"===e.status){t.inventoryRepairs+=1;for(const o of[e.diagnostic.originalId,e.diagnostic.repairedId].filter(isString))perTool(t,o).inventoryRepairs+=1}else t.inventoryRepairBlocks+=1}(t,e):"runtime.adapter.event"===e.type&&function applyAdapterEvent(t,e){(function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)})(e)&&("agent.tool.start"===e.phase&&"string"==typeof e.toolId?(t.toolStarted+=1,perTool(t,e.toolId).started+=1):"agent.tool.result"===e.phase&&"string"==typeof e.toolId&&(t.toolCompleted+=1,perTool(t,e.toolId).completed+=1))}(t,e.event)}function perTool(t,e){const o=t.perTool.get(e);if(o)return o;const r={started:0,completed:0,failures:0,inventoryRepairs:0,circuitOpen:0};return t.perTool.set(e,r),r}function rate(t,e){return e>0?t/e:void 0}function isString(t){return"string"==typeof t&&t.length>0}
@@ -1 +1 @@
1
- export function createTraceRecorder(){const e=[];return{listener(t){e.push(t)},events:()=>[...e]}}export function createReplayManifest(e){return{schemaVersion:1,createdAt:(new Date).toISOString(),runs:e.runs,events:e.events}}export function createEvaluationBundle(e){return{...createReplayManifest({runs:e.runs,events:e.events}),artifacts:e.artifacts??[],metadata:e.metadata}}
1
+ import{computeToolCallMetrics as e}from"./tool-call-metrics.js";export function createTraceRecorder(){const e=[];return{listener(t){e.push(t)},events:()=>[...e]}}export function createReplayManifest(e){return{schemaVersion:1,createdAt:(new Date).toISOString(),runs:e.runs,events:e.events}}export function createEvaluationBundle(t){return{...createReplayManifest({runs:t.runs,events:t.events}),artifacts:t.artifacts??[],metadata:{...t.metadata??{},toolCallMetrics:e(t.events)}}}
@@ -1 +1 @@
1
- import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."object"==typeof r.workspaceValidation&&r.workspaceValidation?{workspaceValidation:r.workspaceValidation}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r){const t=assertRecord(e.spec,"Agent.spec"),o=readName(e),n=readOptionalString(t.backend);if(!n)throw new Error(`Agent ${o} requires spec.backend`);const i="object"==typeof t.config&&t.config?t.config:{},a="string"==typeof t.systemPrompt?t.systemPrompt:"string"==typeof i.systemPrompt?i.systemPrompt:void 0;return{id:o,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:n,..."string"==typeof t.modelRef&&t.modelRef.trim()?{modelRef:(s=t.modelRef,s.replace(/^[^/]+\//u,""))}:{},...void 0!==a?{systemPrompt:a}:{},tools:toStringArray(t.tools),skills:toStringArray(t.skills),memory:Array.isArray(t.memory)?t.memory:[],subagents:toStringArray(t.subagents),...void 0!==t.edges?{edges:readAgentEdges(t.edges,o)}:{},config:i};var s}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e,r){const t=assertRecord(e.spec,"Tool.spec");return{id:readName(e),...r?{sourcePath:r}:{},..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(e){const r=assertRecord(e.spec,"Memory.spec"),t=readName(e),o={...r};return delete o.provider,delete o.profile,delete o.mode,delete o.enabled,delete o.prompts,{id:t,provider:readOptionalString(r.provider)??"langmem",...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...readOptionalString(r.mode)?{mode:readOptionalString(r.mode)}:{},enabled:!1!==r.enabled,..."object"==typeof r.prompts&&r.prompts?{prompts:readMemoryPrompts(r.prompts)}:{},...Object.keys(o).length>0?{config:o}:{}}}function readWorkflowRouting(e){const r=assertRecord(e,"Runtime.spec.workflowRouting"),t=void 0===r.routes?void 0:function readWorkflowRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.workflowRouting.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.workflowId);if(!t||!o)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:t,workflowId:o,...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{},..."object"==typeof r.metadata&&r.metadata?{metadata:r.metadata}:{}}})}(r.routes);return{...readOptionalString(r.defaultWorkflowId)?{defaultWorkflowId:readOptionalString(r.defaultWorkflowId)}:{},...t?{routes:t}:{}}}function readAdapters(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(readAdapter)}function readAgentEdges(e,r){if(!Array.isArray(e))throw new Error(`Agent ${r} spec.edges must be an array`);return e.map(e=>{const t=assertRecord(e,`Agent ${r} spec.edges[]`),o=readOptionalString(t.from),n=readOptionalString(t.to);if(!o||!n)throw new Error(`Agent ${r} spec.edges[] requires from and to`);return{from:o,to:n,...readOptionalString(t.condition)?{condition:readOptionalString(t.condition)}:{}}})}function readAdapter(e){if("string"==typeof e&&e.trim())return{name:e.trim()};const r=assertRecord(e,"Runtime.spec.adapters[]"),t=readOptionalString(r.name)??readOptionalString(r.id)??readOptionalString(r.backend);if(!t)throw new Error("Runtime.spec.adapters[] requires name");return{name:t,..."boolean"==typeof r.enabled?{enabled:r.enabled}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}}function readMemoryPrompts(e){const r=assertRecord(e,"Memory.spec.prompts");return{...readOptionalString(r.semantic)?{semantic:readOptionalString(r.semantic)}:{},...readOptionalString(r.episodic)?{episodic:readOptionalString(r.episodic)}:{},...readOptionalString(r.procedural)?{procedural:readOptionalString(r.procedural)}:{}}}
1
+ import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."object"==typeof r.workspaceValidation&&r.workspaceValidation?{workspaceValidation:r.workspaceValidation}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r){const t=assertRecord(e.spec,"Agent.spec"),o=readName(e),n=readOptionalString(t.backend);if(!n)throw new Error(`Agent ${o} requires spec.backend`);const i="object"==typeof t.config&&t.config?t.config:{},a="string"==typeof t.systemPrompt?t.systemPrompt:"string"==typeof i.systemPrompt?i.systemPrompt:void 0;return{id:o,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:n,..."string"==typeof t.modelRef&&t.modelRef.trim()?{modelRef:(s=t.modelRef,s.replace(/^[^/]+\//u,""))}:{},...void 0!==a?{systemPrompt:a}:{},tools:toStringArray(t.tools),skills:toStringArray(t.skills),memory:Array.isArray(t.memory)?t.memory:[],subagents:toStringArray(t.subagents),...void 0!==t.edges?{edges:readAgentEdges(t.edges,o)}:{},config:i};var s}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e,r){const t=assertRecord(e.spec,"Tool.spec");return{id:readName(e),...r?{sourcePath:r}:{},..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},...void 0!==t.outputSchema?{outputSchema:t.outputSchema}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(e){const r=assertRecord(e.spec,"Memory.spec"),t=readName(e),o={...r};return delete o.provider,delete o.profile,delete o.mode,delete o.enabled,delete o.prompts,{id:t,provider:readOptionalString(r.provider)??"langmem",...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...readOptionalString(r.mode)?{mode:readOptionalString(r.mode)}:{},enabled:!1!==r.enabled,..."object"==typeof r.prompts&&r.prompts?{prompts:readMemoryPrompts(r.prompts)}:{},...Object.keys(o).length>0?{config:o}:{}}}function readWorkflowRouting(e){const r=assertRecord(e,"Runtime.spec.workflowRouting"),t=void 0===r.routes?void 0:function readWorkflowRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.workflowRouting.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.workflowId);if(!t||!o)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:t,workflowId:o,...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{},..."object"==typeof r.metadata&&r.metadata?{metadata:r.metadata}:{}}})}(r.routes);return{...readOptionalString(r.defaultWorkflowId)?{defaultWorkflowId:readOptionalString(r.defaultWorkflowId)}:{},...t?{routes:t}:{}}}function readAdapters(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(readAdapter)}function readAgentEdges(e,r){if(!Array.isArray(e))throw new Error(`Agent ${r} spec.edges must be an array`);return e.map(e=>{const t=assertRecord(e,`Agent ${r} spec.edges[]`),o=readOptionalString(t.from),n=readOptionalString(t.to);if(!o||!n)throw new Error(`Agent ${r} spec.edges[] requires from and to`);return{from:o,to:n,...readOptionalString(t.condition)?{condition:readOptionalString(t.condition)}:{}}})}function readAdapter(e){if("string"==typeof e&&e.trim())return{name:e.trim()};const r=assertRecord(e,"Runtime.spec.adapters[]"),t=readOptionalString(r.name)??readOptionalString(r.id)??readOptionalString(r.backend);if(!t)throw new Error("Runtime.spec.adapters[] requires name");return{name:t,..."boolean"==typeof r.enabled?{enabled:r.enabled}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}}function readMemoryPrompts(e){const r=assertRecord(e,"Memory.spec.prompts");return{...readOptionalString(r.semantic)?{semantic:readOptionalString(r.semantic)}:{},...readOptionalString(r.episodic)?{episodic:readOptionalString(r.episodic)}:{},...readOptionalString(r.procedural)?{procedural:readOptionalString(r.procedural)}:{}}}
@@ -1 +1 @@
1
- import{readFile as o}from"node:fs/promises";import e from"node:path";import{parseAllDocuments as t}from"yaml";import{discoverModuleTools as s,discoverSkills as r,listYamlFiles as n}from"./discovery.js";import{compileAgent as a,compileMemory as l,compileModel as i,compileModelSpec as c,compileRuntime as f,compileTool as u}from"./documents.js";import{compileWorkflow as w,validateWorkflows as m}from"./workflows.js";import{compileEvaluation as d,validateEvaluations as p}from"./evaluations.js";import{assertWorkspaceBoundaryDiagnostics as k,scanWorkspaceBoundaries as g}from"./boundary-scan.js";export async function loadWorkspaceFromYaml(a){const l=e.join(a,"config"),i=await n(l),c=[],f=new Map,u=new Map,w=new Map,d=new Map,v=new Map,M=new Map,y=new Map;for(const e of i){const s=await o(e,"utf8"),r=t(s).map(o=>o.toJSON()).filter(o=>null!==o);for(const o of r)collectWorkspaceDocument(o,e,{runtimeDocs:c,agents:f,models:u,tools:w,memories:v,workflows:M,evaluations:y})}for(const o of await s(a))w.has(o.id)||w.set(o.id,o);for(const o of await r(a))d.set(o.id,o);const W=c.at(-1)??{defaultAgentId:"orchestra"};m({workflows:M,agents:f,tools:w,skills:d}),p({evaluations:y,agents:f,tools:w,workflows:M}),function validateAgentRouting(o,e){for(const t of o.routes??[])if(!e.has(t.agentId))throw new Error(`Runtime routing route ${t.id} references unknown agent ${t.agentId}`)}(W,f),function validateWorkflowRouting(o,e){const t=o.workflowRouting;if(t){if(t.defaultWorkflowId&&!e.has(t.defaultWorkflowId))throw new Error(`Runtime workflowRouting.defaultWorkflowId references unknown workflow ${t.defaultWorkflowId}`);for(const o of t.routes??[])if(!e.has(o.workflowId))throw new Error(`Runtime workflowRouting route ${o.id} references unknown workflow ${o.workflowId}`)}}(W,M);const h={root:a,runtime:W,agents:f,models:u,tools:w,skills:d,memories:v,workflows:M,evaluations:y},R=g(h);return k(R),{...h,...R.length>0?{diagnostics:R}:{}}}function collectWorkspaceDocument(o,e,t){if("string"==typeof o.kind)switch(o.kind){case"Runtime":return void t.runtimeDocs.push(f(o));case"Agent":return collectOne(t.agents,a(o,e));case"Model":return collectOne(t.models,i(o));case"Models":return function collectModelSpecs(o,e){if(Array.isArray(o.spec))for(const t of o.spec)if("object"==typeof t&&null!==t&&!Array.isArray(t)){const o=c(t);e.set(o.id,o)}}(o,t.models);case"Tool":return collectOne(t.tools,u(o,e));case"Memory":return collectOne(t.memories,l(o));case"Workflow":return collectOne(t.workflows,w(o,e));case"Evaluation":return collectOne(t.evaluations,d(o,e));default:return}}function collectOne(o,e){o.set(e.id,e)}
1
+ import{readFile as o}from"node:fs/promises";import e from"node:path";import{parseAllDocuments as s}from"yaml";import{discoverModuleTools as t,discoverSkills as r,listYamlFiles as a}from"./discovery.js";import{compileAgent as n,compileMemory as l,compileModel as i,compileModelSpec as c,compileRuntime as f,compileTool as u}from"./documents.js";import{compileWorkflow as m,validateWorkflows as w}from"./workflows.js";import{compileEvaluation as d,validateEvaluations as p}from"./evaluations.js";import{assertWorkspaceBoundaryDiagnostics as k,scanWorkspaceBoundaries as g}from"./boundary-scan.js";import{assertWorkspaceToolQualityDiagnostics as v,scanWorkspaceToolQuality as y}from"@stable-harness/core";export async function loadWorkspaceFromYaml(n){const l=e.join(n,"config"),i=await a(l),c=[],f=new Map,u=new Map,m=new Map,d=new Map,M=new Map,W=new Map,h=new Map;for(const e of i){const t=await o(e,"utf8"),r=s(t).map(o=>o.toJSON()).filter(o=>null!==o);for(const o of r)collectWorkspaceDocument(o,e,{runtimeDocs:c,agents:f,models:u,tools:m,memories:M,workflows:W,evaluations:h})}for(const o of await t(n))m.has(o.id)||m.set(o.id,o);for(const o of await r(n))d.set(o.id,o);const R=c.at(-1)??{defaultAgentId:"orchestra"};w({workflows:W,agents:f,tools:m,skills:d}),p({evaluations:h,agents:f,tools:m,workflows:W}),function validateAgentRouting(o,e){for(const s of o.routes??[])if(!e.has(s.agentId))throw new Error(`Runtime routing route ${s.id} references unknown agent ${s.agentId}`)}(R,f),function validateWorkflowRouting(o,e){const s=o.workflowRouting;if(s){if(s.defaultWorkflowId&&!e.has(s.defaultWorkflowId))throw new Error(`Runtime workflowRouting.defaultWorkflowId references unknown workflow ${s.defaultWorkflowId}`);for(const o of s.routes??[])if(!e.has(o.workflowId))throw new Error(`Runtime workflowRouting route ${o.id} references unknown workflow ${o.workflowId}`)}}(R,W);const A={root:n,runtime:R,agents:f,models:u,tools:m,skills:d,memories:M,workflows:W,evaluations:h},I=g(A);k(I);const O=y(A,R.workspaceValidation?.toolQuality);return v(O),{...A,...[...I??[],...O].length>0?{diagnostics:[...I,...O]}:{}}}function collectWorkspaceDocument(o,e,s){if("string"==typeof o.kind)switch(o.kind){case"Runtime":return void s.runtimeDocs.push(f(o));case"Agent":return collectOne(s.agents,n(o,e));case"Model":return collectOne(s.models,i(o));case"Models":return function collectModelSpecs(o,e){if(Array.isArray(o.spec))for(const s of o.spec)if("object"==typeof s&&null!==s&&!Array.isArray(s)){const o=c(s);e.set(o.id,o)}}(o,s.models);case"Tool":return collectOne(s.tools,u(o,e));case"Memory":return collectOne(s.memories,l(o));case"Workflow":return collectOne(s.workflows,m(o,e));case"Evaluation":return collectOne(s.evaluations,d(o,e));default:return}}function collectOne(o,e){o.set(e.id,e)}