stable-harness 0.0.3 → 0.0.4

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 (119) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/compat/agent-harness.js +1 -1
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +1 -1
  5. package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
  6. package/dist/runtime/compat/json.js +1 -1
  7. package/dist/runtime/compat/presentation.js +1 -1
  8. package/dist/runtime/compat/prompts.js +1 -1
  9. package/dist/runtime/model/ollama.js +1 -1
  10. package/dist/runtime/skills/skill-metadata.js +1 -1
  11. package/dist/workspace/compile.js +1 -1
  12. package/package.json +4 -3
  13. package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
  14. package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
  15. package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
  16. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +9 -4
  17. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
  18. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +29 -1
  19. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -1
  20. package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -1
  21. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
  22. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
  23. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
  24. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
  25. package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -1
  26. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
  27. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
  28. package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +1 -1
  29. package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -1
  30. package/packages/adapter-deepagents/dist/src/memory.js +1 -1
  31. package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
  32. package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
  33. package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
  34. package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
  35. package/packages/adapter-deepagents/package.json +1 -0
  36. package/packages/adapter-langgraph/dist/src/graph.js +1 -1
  37. package/packages/adapter-langgraph/dist/src/runtime.js +1 -1
  38. package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -1
  39. package/packages/cli/dist/src/args.d.ts +6 -3
  40. package/packages/cli/dist/src/args.js +1 -1
  41. package/packages/cli/dist/src/cli.js +1 -1
  42. package/packages/cli/dist/src/event-view.d.ts +9 -0
  43. package/packages/cli/dist/src/event-view.js +1 -0
  44. package/packages/cli/dist/src/index.d.ts +3 -0
  45. package/packages/cli/dist/src/index.js +1 -1
  46. package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
  47. package/packages/cli/dist/src/langgraph-env.js +1 -0
  48. package/packages/cli/dist/src/langgraph-official.d.ts +2 -0
  49. package/packages/cli/dist/src/langgraph-official.js +1 -1
  50. package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
  51. package/packages/cli/dist/src/memory/lifecycle.js +1 -0
  52. package/packages/cli/dist/src/memory/providers.d.ts +3 -0
  53. package/packages/cli/dist/src/memory/providers.js +1 -0
  54. package/packages/cli/dist/src/output.js +1 -1
  55. package/packages/cli/dist/src/server.d.ts +2 -0
  56. package/packages/cli/dist/src/server.js +1 -1
  57. package/packages/cli/package.json +2 -0
  58. package/packages/core/dist/evaluations/index.d.ts +18 -0
  59. package/packages/core/dist/evaluations/index.js +1 -0
  60. package/packages/core/dist/execution-contract.js +1 -1
  61. package/packages/core/dist/index.d.ts +3 -0
  62. package/packages/core/dist/index.js +1 -1
  63. package/packages/core/dist/memory-plugins/maintenance.js +1 -1
  64. package/packages/core/dist/memory-plugins/shared.js +1 -1
  65. package/packages/core/dist/memory-plugins.js +1 -1
  66. package/packages/core/dist/recovery/tool-call.d.ts +15 -0
  67. package/packages/core/dist/recovery/tool-call.js +1 -1
  68. package/packages/core/dist/runtime/completion.js +1 -1
  69. package/packages/core/dist/runtime/direct-tool-call.js +1 -1
  70. package/packages/core/dist/runtime/events.d.ts +77 -20
  71. package/packages/core/dist/runtime/memory.js +1 -1
  72. package/packages/core/dist/runtime/persistence/artifacts.js +1 -1
  73. package/packages/core/dist/runtime/persistence/inspection.js +1 -1
  74. package/packages/core/dist/runtime/persistence/queue.js +1 -1
  75. package/packages/core/dist/runtime/persistence/stores.js +1 -1
  76. package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
  77. package/packages/core/dist/runtime/progress-narration.js +1 -0
  78. package/packages/core/dist/runtime/tool-gateway.d.ts +5 -0
  79. package/packages/core/dist/runtime.d.ts +2 -1
  80. package/packages/core/dist/runtime.js +1 -1
  81. package/packages/core/dist/spec-driven/config.d.ts +4 -0
  82. package/packages/core/dist/spec-driven/config.js +1 -0
  83. package/packages/core/dist/spec-driven/events.d.ts +11 -0
  84. package/packages/core/dist/spec-driven/events.js +1 -0
  85. package/packages/core/dist/spec-driven/index.d.ts +4 -0
  86. package/packages/core/dist/spec-driven/index.js +1 -0
  87. package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
  88. package/packages/core/dist/spec-driven/lifecycle.js +1 -0
  89. package/packages/core/dist/spec-driven/types.d.ts +38 -0
  90. package/packages/core/dist/spec-driven/types.js +1 -0
  91. package/packages/core/dist/trace.d.ts +1 -1
  92. package/packages/core/dist/trace.js +1 -1
  93. package/packages/core/dist/types.d.ts +15 -1
  94. package/packages/core/dist/workflows/index.js +1 -1
  95. package/packages/core/dist/workflows/runtime.js +1 -1
  96. package/packages/core/dist/workspace/types.d.ts +9 -0
  97. package/packages/governance/dist/src/skill-candidates.js +1 -1
  98. package/packages/memory/dist/src/langmem-service.js +1 -1
  99. package/packages/memory/dist/src/maintenance.js +1 -1
  100. package/packages/memory/dist/src/policy.js +1 -1
  101. package/packages/memory/dist/src/provider.js +1 -1
  102. package/packages/memory/dist/src/store.js +1 -1
  103. package/packages/protocols/dist/src/http-server.js +1 -1
  104. package/packages/protocols/dist/src/openai-compatible.js +1 -1
  105. package/packages/protocols/dist/src/openai-payload.js +1 -1
  106. package/packages/protocols/dist/src/openai-stream.js +1 -1
  107. package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
  108. package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
  109. package/packages/tool-gateway/dist/src/in-memory.js +1 -1
  110. package/packages/tool-gateway/dist/src/module-loader.js +1 -1
  111. package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
  112. package/packages/tool-gateway/dist/src/types.d.ts +3 -0
  113. package/packages/tool-gateway/package.json +1 -1
  114. package/packages/workspace-yaml/dist/discovery.js +1 -1
  115. package/packages/workspace-yaml/dist/documents.js +1 -1
  116. package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
  117. package/packages/workspace-yaml/dist/evaluations.js +1 -0
  118. package/packages/workspace-yaml/dist/loader.js +1 -1
  119. package/packages/workspace-yaml/dist/workflows.js +1 -1
@@ -0,0 +1,4 @@
1
+ import { ChatOllama } from "@langchain/ollama";
2
+ import { ChatOpenAI } from "@langchain/openai";
3
+ import type { WorkspaceModel } from "@stable-harness/core";
4
+ export declare function createBackendModel(model: WorkspaceModel): string | ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions> | ChatOllama;
@@ -0,0 +1 @@
1
+ import{ChatOllama as e}from"@langchain/ollama";import{ChatOpenAI as r}from"@langchain/openai";export function createBackendModel(r){return"ollama"===r.provider?function usesOpenAiCompatibleToolCalling(e){const r=readString(e.config?.toolCallingMode);return"openai-compatible"===r||"structured"===r}(r)?createOpenAiCompatibleModel({...r,config:{...r.config,baseURL:readOllamaOpenAiBaseUrl(r.config?.baseURL??r.config?.baseUrl)}}):function createOllamaModel(r){return new e(pruneUndefined({model:r.model,baseUrl:readString(r.config?.baseUrl),temperature:readNumber(r.config?.temperature),numCtx:readNumber(r.config?.numCtx),numPredict:readNumber(r.config?.numPredict),timeout:readNumber(r.config?.timeout),think:"boolean"==typeof r.config?.think?r.config.think:void 0}))}(r):"openai-compatible"===r.provider?createOpenAiCompatibleModel(r):r.model}function createOpenAiCompatibleModel(e){return new r(pruneUndefined({model:e.model,apiKey:readString(e.config?.apiKey),temperature:readNumber(e.config?.temperature),maxTokens:readNumber(e.config?.maxTokens)??readNumber(e.config?.numPredict),timeout:readNumber(e.config?.timeout),modelKwargs:readOpenAiCompatibleModelKwargs(e.config),configuration:pruneUndefined({baseURL:readString(e.config?.baseURL)??readString(e.config?.baseUrl),defaultHeaders:readStringRecord(e.config?.headers)})}))}function readOpenAiCompatibleModelKwargs(e){const r={...readRecord(e?.extraBody),...readRecord(e?.modelKwargs)},n=pruneUndefined({...r,think:r.think??("boolean"==typeof e?.think?e.think:void 0),num_ctx:r.num_ctx??readNumber(e?.numCtx),num_predict:r.num_predict??readNumber(e?.numPredict)});return Object.keys(n).length>0?n:void 0}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readStringRecord(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const r=Object.entries(e).filter(e=>"string"==typeof e[1]);return r.length>0?Object.fromEntries(r):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readOllamaOpenAiBaseUrl(e){const r=readString(e);if(!r)return;const n=r.replace(/\/+$/u,"");return n.endsWith("/v1")?n:`${n}/v1`}
@@ -1 +1 @@
1
- import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(n){const o=[],i=r(n?.model);i&&o.push(e(i));const a=function(e){const t=r(e);if(!t)return;const n=Array.isArray(e?.tools)?e.tools.filter(e=>"string"==typeof e&&e.trim().length>0):void 0;return s({...t,tools:n})}(n?.tools);return a&&o.push(t(a)),o}function r(e){var t;if(e?.enabled)return s({maxRetries:n(e.maxRetries),backoffFactor:n(e.backoffFactor),initialDelayMs:n(e.initialDelayMs),maxDelayMs:n(e.maxDelayMs),jitter:"boolean"==typeof e.jitter?e.jitter:void 0,retryOn:o(e.retryOn),onFailure:(t=e.onFailure,"continue"===t||"error"===t?t:void 0)})}function n(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function o(e){const t=function(e){const t=new Set(["timeout","network","rateLimit","serverError"]);if(!Array.isArray(e))return[...t];const r=e.filter(e=>t.has(e));return r.length>0?r:[...t]}(e);return e=>t.some(t=>i(e,t))}function i(e,t){const r=a(e)?e:{},n=function(e){const t=e.status??e.statusCode??e.code;return"number"==typeof t?t:Number.isInteger(Number(t))?Number(t):void 0}(r),o=`${String(r.name??"")} ${String(r.code??"")} ${String(r.message??e)}`,s=r.cause;return("timeout"===t?/timeout|timedout|etimedout|abort/i.test(o):"network"===t?/network|fetch failed|terminated|connection error|econnreset|econnrefused|enotfound|eai_again/i.test(o):"rateLimit"===t?429===n||/rate.?limit|too many requests/i.test(o):"serverError"===t&&Boolean(n&&n>=500&&n<600))||a(s)&&i(s,t)}function a(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function s(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}
1
+ import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(r){const n=[],o=readRetryOptions(r?.model);o&&n.push(e(o));const i=function readToolRetryOptions(e){const t=readRetryOptions(e);if(!t)return;const r=Array.isArray(e?.tools)?e.tools.filter(e=>"string"==typeof e&&e.trim().length>0):void 0;return pruneUndefined({...t,tools:r})}(r?.tools);return i&&n.push(t(i)),n}function readRetryOptions(e){var t;if(e?.enabled)return pruneUndefined({maxRetries:readNonNegativeNumber(e.maxRetries),backoffFactor:readNonNegativeNumber(e.backoffFactor),initialDelayMs:readNonNegativeNumber(e.initialDelayMs),maxDelayMs:readNonNegativeNumber(e.maxDelayMs),jitter:"boolean"==typeof e.jitter?e.jitter:void 0,retryOn:createRetryPredicate(e.retryOn),onFailure:(t=e.onFailure,"continue"===t||"error"===t?t:void 0)})}function readNonNegativeNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function createRetryPredicate(e){const t=function readRetryReasons(e){const t=new Set(["timeout","network","rateLimit","serverError"]);if(!Array.isArray(e))return[...t];const r=e.filter(e=>t.has(e));return r.length>0?r:[...t]}(e);return e=>t.some(t=>matchesRetryReason(e,t))}function matchesRetryReason(e,t){const r=isRecord(e)?e:{},n=function readStatus(e){const t=e.status??e.statusCode??e.code;return"number"==typeof t?t:Number.isInteger(Number(t))?Number(t):void 0}(r),o=`${String(r.name??"")} ${String(r.code??"")} ${String(r.message??e)}`,i=r.cause;return("timeout"===t?/timeout|timedout|etimedout|abort/i.test(o):"network"===t?/network|fetch failed|terminated|connection error|eof|econnreset|econnrefused|enotfound|eai_again/i.test(o):"rateLimit"===t?429===n||/rate.?limit|too many requests/i.test(o):"serverError"===t&&Boolean(n&&n>=500&&n<600))||isRecord(i)&&matchesRetryReason(i,t)}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}
@@ -1,6 +1,12 @@
1
1
  import type { RuntimeAdapterContext, RuntimeAdapterResult } from "@stable-harness/core";
2
2
  export type DeepAgentFactory = (params?: Record<string, unknown>) => {
3
- invoke(input: Record<string, unknown>): Promise<unknown>;
3
+ invoke(input: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
4
4
  streamEvents?(input: Record<string, unknown>, options?: Record<string, unknown>): AsyncIterable<unknown>;
5
5
  };
6
+ export type DeepAgentsModule = {
7
+ createDeepAgent?: unknown;
8
+ FilesystemBackend?: new (options: {
9
+ rootDir: string;
10
+ }) => unknown;
11
+ };
6
12
  export type DeepAgentsAdapterRunner = (input: RuntimeAdapterContext) => Promise<RuntimeAdapterResult>;
@@ -12,6 +12,7 @@
12
12
  "dependencies": {
13
13
  "@langchain/core": "^1.1.43",
14
14
  "@langchain/ollama": "^1.2.7",
15
+ "@langchain/openai": "^1.4.5",
15
16
  "langchain": "^1.3.1",
16
17
  "@stable-harness/core": "0.0.1"
17
18
  },
@@ -1 +1 @@
1
- import{Annotation as o,END as e,START as t,StateGraph as n}from"@langchain/langgraph";import{compileWorkflowPlan as r}from"@stable-harness/core";import{resolveSkillProvider as a}from"./skill-providers.js";export async function runLangGraphWorkflow(a,s,u){!function(o){if(o.cycles.length>0)throw new Error(`LangGraph workflow adapter does not enable cyclic graphs by default: ${o.workflowId}`);if(o.unreachableNodes.length>0)throw new Error(`LangGraph workflow has unreachable nodes: ${o.unreachableNodes.join(", ")}`)}(r(a.workflow));const f=function(a,d,s){let u=new n(o.Root({input:o,outputs:o({reducer:(o,e)=>({...o,...e}),default:()=>({})}),trace:o({reducer:(o,e)=>[...o,...e],default:()=>[]})}));for(const o of a.workflow.nodes)u=u.addNode(o.id,async e=>i({input:a,node:o,state:e,options:d,adapterName:s}));u=u.addEdge(t,a.workflow.entry??a.workflow.nodes[0].id),u=function(o,e){for(const t of e.workflow.edges)t.condition||(o=o.addEdge(t.from,t.to));return o}(u,a),u=function(o,e,t){for(const[n,r]of function(o){const e=new Map;for(const t of o.workflow.edges){if(!t.condition)continue;const o=e.get(t.from)??[];o.push({condition:t.condition,to:t.to}),e.set(t.from,o)}return e}(e)){const a=t.conditionalRouters?.[n];if(!a)throw new Error(`LangGraph workflow conditional edges from ${n} require a conditional router`);o=o.addConditionalEdges(n,async o=>a({...e,from:n,edges:r,state:o}),Object.fromEntries(r.map(o=>[o.condition,o.to])))}return o}(u,a,d);for(const o of r(a.workflow).terminalNodes)u=u.addEdge(o,e);return u.compile()}(a,s,u);a.emit({adapter:u,phase:"langgraph.invoke",workflowId:a.workflow.id});const l=await f.invoke({input:a.request.input,outputs:{},trace:[]});return{text:d(l),metadata:{workflowId:a.workflow.id,adapter:u,outputs:l.outputs,trace:l.trace}}}async function i(o){const e=await function(o,e){const t=e.nodeHandlers?.[o.id]??e.nodeHandlers?.[o.use];if(t)return t;const n=function(o){const e=o.indexOf(".");if(!(e<=0||e===o.length-1))return{kind:o.slice(0,e),id:o.slice(e+1)}}(o.use);if("workflows"===n?.kind&&e.enableSubworkflows)return o=>async function(o,e){const t=o.workspace.workflows.get(o.id);if(!t)throw new Error(`LangGraph subworkflow is not defined: ${o.id}`);const n=function(o){const e=o?.subworkflowDepth;return"number"==typeof e&&Number.isFinite(e)?e:0}(o.request.metadata),r=e.maxSubworkflowDepth??8;if(n>=r)throw new Error(`LangGraph subworkflow depth exceeded ${r}`);return(await runLangGraphWorkflow({...o,workflow:t,request:{...o.request,input:o.state,metadata:{...o.request.metadata,subworkflowDepth:n+1}}},e,e.name??"langgraph")).text}({...o,id:n.id},e);const r=n?e.nodeResolvers?.[n.kind]:void 0;if(r&&n)return o=>r({...o,kind:n.kind,id:n.id});const i="skills"===n?.kind?a(e):void 0;if(i&&n)return o=>i.resolve({...o,kind:n.kind,id:n.id});const d=e.defaultNodeHandler;if(d)return d;throw new Error(`LangGraph workflow node ${o.id} (${o.use}) has no handler or resolver`)}(o.node,o.options)({...o.input,node:o.node,state:o.state});return o.input.emit({adapter:o.adapterName,phase:"node.completed",workflowId:o.input.workflow.id,nodeId:o.node.id}),{outputs:{[o.node.id]:e},trace:[{nodeId:o.node.id,use:o.node.use,output:e}]}}function d(o){const e=o.trace.at(-1)?.output;return"string"==typeof e?e:JSON.stringify(e??o.outputs)}
1
+ import{Annotation as o,END as e,START as t,StateGraph as n}from"@langchain/langgraph";import{compileWorkflowPlan as r}from"@stable-harness/core";import{resolveSkillProvider as a}from"./skill-providers.js";export async function runLangGraphWorkflow(a,d,i){!function assertSupportedPlan(o){if(o.cycles.length>0)throw new Error(`LangGraph workflow adapter does not enable cyclic graphs by default: ${o.workflowId}`);if(o.unreachableNodes.length>0)throw new Error(`LangGraph workflow has unreachable nodes: ${o.unreachableNodes.join(", ")}`)}(r(a.workflow));const s=function compileLangGraph(a,d,i){let s=new n(function createStateAnnotation(){return o.Root({input:o,outputs:o({reducer:(o,e)=>({...o,...e}),default:()=>({})}),trace:o({reducer:(o,e)=>[...o,...e],default:()=>[]})})}());for(const o of a.workflow.nodes)s=s.addNode(o.id,async e=>runNode({input:a,node:o,state:e,options:d,adapterName:i}));s=s.addEdge(t,a.workflow.entry??a.workflow.nodes[0].id),s=function addStaticEdges(o,e){for(const t of e.workflow.edges)t.condition||(o=o.addEdge(t.from,t.to));return o}(s,a),s=function addConditionalEdges(o,e,t){for(const[n,r]of function conditionalEdgesBySource(o){const e=new Map;for(const t of o.workflow.edges){if(!t.condition)continue;const o=e.get(t.from)??[];o.push({condition:t.condition,to:t.to}),e.set(t.from,o)}return e}(e)){const a=t.conditionalRouters?.[n];if(!a)throw new Error(`LangGraph workflow conditional edges from ${n} require a conditional router`);o=o.addConditionalEdges(n,async o=>a({...e,from:n,edges:r,state:o}),Object.fromEntries(r.map(o=>[o.condition,o.to])))}return o}(s,a,d);for(const o of r(a.workflow).terminalNodes)s=s.addEdge(o,e);return s.compile()}(a,d,i);a.emit({adapter:i,phase:"agent.langgraph.invoke",workflowId:a.workflow.id});const u=await s.invoke({input:a.request.input,outputs:{},trace:[]});return{text:stringifyWorkflowOutput(u),metadata:{workflowId:a.workflow.id,adapter:i,outputs:u.outputs,trace:u.trace}}}async function runNode(o){const e=await function resolveNodeHandler(o,e){const t=e.nodeHandlers?.[o.id]??e.nodeHandlers?.[o.use];if(t)return t;const n=function parseNodeUse(o){const e=o.indexOf(".");if(!(e<=0||e===o.length-1))return{kind:o.slice(0,e),id:o.slice(e+1)}}(o.use);if("workflows"===n?.kind&&e.enableSubworkflows)return o=>async function runSubworkflow(o,e){const t=o.workspace.workflows.get(o.id);if(!t)throw new Error(`LangGraph subworkflow is not defined: ${o.id}`);const n=function readSubworkflowDepth(o){const e=o?.subworkflowDepth;return"number"==typeof e&&Number.isFinite(e)?e:0}(o.request.metadata),r=e.maxSubworkflowDepth??8;if(n>=r)throw new Error(`LangGraph subworkflow depth exceeded ${r}`);return(await runLangGraphWorkflow({...o,workflow:t,request:{...o.request,input:o.state,metadata:{...o.request.metadata,subworkflowDepth:n+1}}},e,e.name??"langgraph")).text}({...o,id:n.id},e);const r=n?e.nodeResolvers?.[n.kind]:void 0;if(r&&n)return o=>r({...o,kind:n.kind,id:n.id});const d="skills"===n?.kind?a(e):void 0;if(d&&n)return o=>d.resolve({...o,kind:n.kind,id:n.id});const i=e.defaultNodeHandler;if(i)return i;throw new Error(`LangGraph workflow node ${o.id} (${o.use}) has no handler or resolver`)}(o.node,o.options)({...o.input,node:o.node,state:o.state});return o.input.emit({adapter:o.adapterName,phase:"agent.node.completed",workflowId:o.input.workflow.id,nodeId:o.node.id}),{outputs:{[o.node.id]:e},trace:[{nodeId:o.node.id,use:o.node.use,output:e}]}}function stringifyWorkflowOutput(o){const e=o.trace.at(-1)?.output;return"string"==typeof e?e:JSON.stringify(e??o.outputs)}
@@ -1 +1 @@
1
- import{runLangGraphWorkflow as e}from"./graph.js";export function createLangGraphRuntimeAdapter(t={}){const r=t.name??"langgraph";return{name:r,canRun:e=>e.backend===r,run:o=>async function(t,r,o){return e({workspace:t.workspace,workflow:n(t.agent,o),request:{input:t.request.input,metadata:t.request.metadata},requestId:t.requestId,sessionId:t.sessionId,toolGateway:t.toolGateway,emit:e=>t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:e})},r,o)}(o,t,r)}}function n(e,n){const r=e.edges??[],s=r.length>0?t(e):t(e).slice(0,1);return function(e,n){const t=new Set;for(const r of n){if(t.has(r.id))throw new Error(`LangGraph agent ${e.id} has duplicate graph node ${r.id}`);t.add(r.id)}for(const n of e.edges??[])if(!t.has(n.from)||!t.has(n.to))throw new Error(`LangGraph agent ${e.id} edge references unknown node ${n.from}->${n.to}`)}({...e,edges:r},s),{id:e.id,...e.description?{description:e.description}:{},...e.sourcePath?{sourcePath:e.sourcePath}:{},adapter:n,entry:o(e,s),nodes:s,edges:r,...Object.keys(e.config).length>0?{config:e.config}:{}}}function t(e){return[...e.subagents.map(e=>r("agents",e)),...(e.skills??[]).map(e=>r("skills",e)),...e.tools.map(e=>r("tools",e))]}function r(e,n){return{id:n,use:`${e}.${n}`}}function o(e,n){return e.edges?.[0]?.from??n[0]?.id}
1
+ import{runLangGraphWorkflow as e}from"./graph.js";export function createLangGraphRuntimeAdapter(n={}){const t=n.name??"langgraph";return{name:t,canRun:e=>e.backend===t,run:r=>async function runLangGraphAgent(n,t,r){return e({workspace:n.workspace,workflow:workflowFromAgent(n.agent,r),request:{input:n.request.input,metadata:n.request.metadata},requestId:n.requestId,sessionId:n.sessionId,toolGateway:n.toolGateway,emit:e=>n.emit({type:"runtime.adapter.event",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,event:e})},t,r)}(r,n,t)}}function workflowFromAgent(e,n){const t=e.edges??[],r=t.length>0?agentNodes(e):agentNodes(e).slice(0,1);return function validateAgentGraph(e,n){const t=new Set;for(const r of n){if(t.has(r.id))throw new Error(`LangGraph agent ${e.id} has duplicate graph node ${r.id}`);t.add(r.id)}for(const n of e.edges??[])if(!t.has(n.from)||!t.has(n.to))throw new Error(`LangGraph agent ${e.id} edge references unknown node ${n.from}->${n.to}`)}({...e,edges:t},r),{id:e.id,...e.description?{description:e.description}:{},...e.sourcePath?{sourcePath:e.sourcePath}:{},adapter:n,entry:readAgentEntry(e,r),nodes:r,edges:t,...Object.keys(e.config).length>0?{config:e.config}:{}}}function agentNodes(e){return[...e.subagents.map(e=>inventoryNode("agents",e)),...(e.skills??[]).map(e=>inventoryNode("skills",e)),...e.tools.map(e=>inventoryNode("tools",e))]}function inventoryNode(e,n){return{id:n,use:`${e}.${n}`}}function readAgentEntry(e,n){return e.edges?.[0]?.from??n[0]?.id}
@@ -1 +1 @@
1
- import{readFile as e}from"node:fs/promises";import r from"node:path";export function createRegistrySkillResolverProvider(e={}){return{name:"registry-resolver",resolve:r=>async function(e,r){const o=e.workspace.skills.get(e.id);if(!o)throw new Error(`LangGraph skill resolver cannot find skill ${e.id}`);return{id:o.id,path:o.path,...o.description?{description:o.description}:{},allowedTools:o.allowedTools,...!1===r.includeContent?{}:{content:await t(o,r.maxBytes)}}}(r,e)}}export function createDeepAgentsMiddlewareSkillProvider(e={}){return{name:"deepagents-middleware",async createMiddleware(r){const t=await async function(e){return e.importDeepAgents?e.importDeepAgents():new Function("specifier","return import(specifier)")("deepagents")}(e),i=t.createSkillsMiddleware;if("function"!=typeof i)throw new Error("deepagents does not export createSkillsMiddleware");return i({backend:e.backend??o(t,r.workspace.root),sources:e.sources??n(r)})}}}export function resolveSkillProvider(e){if(!1!==e.skillProvider)return e.skillProvider??createRegistrySkillResolverProvider()}async function t(r,t=1048576){const o=await e(r.path,"utf8");if(Buffer.byteLength(o,"utf8")>t)throw new Error(`Skill ${r.id} exceeds registry resolver size limit of ${t} bytes`);return o}function o(e,r){if("function"!=typeof e.FilesystemBackend)throw new Error("deepagents does not export FilesystemBackend");return new e.FilesystemBackend({rootDir:r})}function n(e){const t=new Set([...(o=e.agent,o?.skills??[]),...i(e)]);var o;const n=new Set;for(const o of t){const t=e.workspace.skills.get(o);t&&n.add(s(r.relative(e.workspace.root,r.dirname(r.dirname(t.path)))))}return[...n].filter(e=>e&&!e.startsWith(".."))}function i(e){return e.workflow.nodes.map(e=>e.use.match(/^skills\.([^./][^.]*)$/u)?.[1]).filter(e=>Boolean(e))}function s(e){return e.split(r.sep).join("/")}
1
+ import{readFile as e}from"node:fs/promises";import r from"node:path";export function createRegistrySkillResolverProvider(e={}){return{name:"registry-resolver",resolve:r=>async function resolveRegistrySkill(e,r){const t=e.workspace.skills.get(e.id);if(!t)throw new Error(`LangGraph skill resolver cannot find skill ${e.id}`);return{id:t.id,path:t.path,...t.description?{description:t.description}:{},allowedTools:t.allowedTools,...!1===r.includeContent?{}:{content:await readSkillContent(t,r.maxBytes)}}}(r,e)}}export function createDeepAgentsMiddlewareSkillProvider(e={}){return{name:"deepagents-middleware",async createMiddleware(r){const t=await async function loadDeepAgentsSkillsModule(e){return e.importDeepAgents?e.importDeepAgents():new Function("specifier","return import(specifier)")("deepagents")}(e),o=t.createSkillsMiddleware;if("function"!=typeof o)throw new Error("deepagents does not export createSkillsMiddleware");return o({backend:e.backend??createDeepAgentsFilesystemBackend(t,r.workspace.root),sources:e.sources??deriveDeepAgentsSkillSources(r)})}}}export function resolveSkillProvider(e){if(!1!==e.skillProvider)return e.skillProvider??createRegistrySkillResolverProvider()}async function readSkillContent(r,t=1048576){const o=await e(r.path,"utf8");if(Buffer.byteLength(o,"utf8")>t)throw new Error(`Skill ${r.id} exceeds registry resolver size limit of ${t} bytes`);return o}function createDeepAgentsFilesystemBackend(e,r){if("function"!=typeof e.FilesystemBackend)throw new Error("deepagents does not export FilesystemBackend");return new e.FilesystemBackend({rootDir:r})}function deriveDeepAgentsSkillSources(e){const t=new Set([...(o=e.agent,o?.skills??[]),...skillIdsFromWorkflow(e)]);var o;const i=new Set;for(const o of t){const t=e.workspace.skills.get(o);t&&i.add(toPosixPath(r.relative(e.workspace.root,r.dirname(r.dirname(t.path)))))}return[...i].filter(e=>e&&!e.startsWith(".."))}function skillIdsFromWorkflow(e){return e.workflow.nodes.map(e=>e.use.match(/^skills\.([^./][^.]*)$/u)?.[1]).filter(e=>Boolean(e))}function toPosixPath(e){return e.split(r.sep).join("/")}
@@ -1,17 +1,20 @@
1
1
  export type CliArgs = {
2
2
  workspaceRoot: string;
3
- command: "request" | "start";
3
+ command: "request" | "start" | "stop";
4
4
  workflowRenderId?: string;
5
5
  workflowInspectId?: string;
6
+ workflowRunId?: string;
6
7
  agentRenderId?: string;
7
8
  agentId?: string;
9
+ sessionId?: string;
8
10
  toolId?: string;
9
11
  toolArgs: unknown;
10
12
  trace: boolean;
11
13
  traceJson: boolean;
14
+ progress: boolean;
12
15
  serveOpenAi: boolean;
13
- host: string;
14
- port: number;
16
+ host?: string;
17
+ port?: number;
15
18
  apiKey?: string;
16
19
  timeoutMs: number;
17
20
  help: boolean;
@@ -1 +1 @@
1
- export function parseArgs(s){let a,i,l,d,w,c,p="request",u=process.cwd(),f=!1,g=!1,h=!1,k=process.env.STABLE_HARNESS_OPENAI_HOST??"127.0.0.1",I=Number(process.env.STABLE_HARNESS_OPENAI_PORT??8642),v=process.env.STABLE_HARNESS_OPENAI_API_KEY,S=Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),m=!1;const x=[];for(let E=0;E<s.length;E+=1){const R=o(s,E);if(0===E&&"start"===s[E])p="start",h=!0;else if(0===E&&"workflow"===s[E]){const r=e(s,E);a=r.workflowRenderId,i=r.workflowInspectId,E=r.index}else if(0===E&&"agent"===s[E]){const e=r(s,E);l=e.agentRenderId,E=e.index}else"-h"===s[E]||"--help"===s[E]?m=!0:"start"===p&&t(s[E])?h=!0:"-w"===s[E]||"--workspace"===s[E]?(u=R.value??u,E=R.index):"--agent"===s[E]?(d=R.value,E=R.index):"--tool"===s[E]?(w=R.value,E=R.index):"--tool-args-json"===s[E]?(c=n(R.value??"{}"),E=R.index):"--trace"===s[E]?f=!0:"--trace-json"===s[E]?g=!0:"--serve-openai"===s[E]?h=!0:"--host"===s[E]?(k=R.value??k,E=R.index):"--port"===s[E]?(I=Number(R.value??I),E=R.index):"--api-key"===s[E]?(v=R.value,E=R.index):"--timeout-ms"===s[E]?(S=Number(R.value??S),E=R.index):x.push(s[E])}return{workspaceRoot:u,command:p,workflowRenderId:a,workflowInspectId:i,agentRenderId:l,agentId:d,toolId:w,toolArgs:c,trace:f,traceJson:g,serveOpenAi:h,host:k,port:I,apiKey:v,timeoutMs:S,help:m,prompt:x.join(" "),shouldRunRequest:Boolean(w||x.length>0)}}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness start -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}function e(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}function r(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}function o(e,r){return{index:r+1,value:e[r+1]}}function n(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}function t(e){return"openai"===e||"openai-compatible"===e}
1
+ export function parseArgs(e){const r=function createDefaultArgs(){return{workspaceRoot:process.cwd(),command:"request",toolArgs:void 0,trace:!1,traceJson:!1,progress:!1,serveOpenAi:!1,host:process.env.STABLE_HARNESS_OPENAI_HOST,port:process.env.STABLE_HARNESS_OPENAI_PORT?Number(process.env.STABLE_HARNESS_OPENAI_PORT):void 0,apiKey:process.env.STABLE_HARNESS_OPENAI_API_KEY,timeoutMs:Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),help:!1,prompt:"",shouldRunRequest:!1}}(),t=[];for(let o=0;o<e.length;o+=1)o=parseOneArg(e,o,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,o){const n=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===o.length&&function parseTopLevelCommand(e,r,t){return"start"===e[r]?(t.command="start",t.serveOpenAi=!0,!0):"stop"===e[r]?(t.command="stop",!0):"workflow"!==e[r]||"render"!==e[r+1]&&"inspect"!==e[r+1]?"agent"===e[r]&&"render"===e[r+1]&&(Object.assign(t,function parseAgentCommand(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}(e,r)),!0):(Object.assign(t,function parseWorkflowCommand(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}(e,r)),!0)}(e,r,t))return function stateIndex(e,r){return"workflow"===e[r]||"agent"===e[r]?r+2:r}(e,r);if("-h"===e[r]||"--help"===e[r])t.help=!0;else if("start"===t.command&&function isProtocolName(e){return"openai"===e||"openai-compatible"===e}(e[r]))t.serveOpenAi=!0;else{if("-w"===e[r]||"--workspace"===e[r])return setString(n,t,"workspaceRoot");if("--agent"===e[r])return setString(n,t,"agentId");if("--workflow"===e[r])return setString(n,t,"workflowRunId");if("--session-id"===e[r])return setString(n,t,"sessionId");if("--tool"===e[r])return setString(n,t,"toolId");if("--tool-args-json"===e[r])return t.toolArgs=function parseJsonArg(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}(n.value??"{}"),n.index;if("--trace"===e[r])t.trace=!0;else if("--trace-json"===e[r])t.traceJson=!0;else if("--progress"===e[r])t.progress=!0;else if("--serve-openai"===e[r])t.serveOpenAi=!0;else{if("--host"===e[r])return setString(n,t,"host");if("--port"===e[r])return t.port=Number(n.value??t.port),n.index;if("--api-key"===e[r])return setString(n,t,"apiKey");if("--timeout-ms"===e[r])return t.timeoutMs=Number(n.value??t.timeoutMs),n.index;o.push(e[r])}}return r}function setString(e,r,t){return"string"==typeof e.value&&Object.assign(r,{[t]:e.value}),e.index}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as o}from"@stable-harness/core";import{projectEvent as s,projectRuntimeTrace as a}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as i}from"@stable-harness/workspace-yaml";import{helpText as c,parseArgs as p}from"./args.js";import{formatDetail as l,inspectWorkflow as d,renderAgent as u,renderWorkflow as m,workspaceStatus as f}from"./output.js";import{serveProtocol as w}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=p(e);if(t.help)return void process.stdout.write(c());const g=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),I=t.workspaceRoot;try{const e=await i(I);if(t.workflowRenderId)return void process.stdout.write(m(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(d(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(u(e,t.agentRenderId));const c=await n({tools:e.tools.values()}),p=o({workspace:e,toolGateway:c,adapters:[r()]});if(t.serveOpenAi)return clearTimeout(g),void await w(p,t);if(!t.shouldRunRequest)return void process.stdout.write(f(e,I));t.trace&&p.subscribe(e=>{const t=s(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${l(t.detail)}\n`)});const v=await p.request({input:t.prompt,agentId:t.agentId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0});if(t.trace||t.traceJson){const e=p.getRun(v.requestId),r=e?a(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${v.output}\n`)}finally{clearTimeout(g)}}(function(){const r=process.argv[1];return Boolean(r)&&e(t(import.meta.url))===e(r)})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
2
+ import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as s}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as i}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as l,parseArgs as d}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as c,shouldEnableCliProgressNarration as w}from"./event-view.js";import{ensureCliMemoryServices as f}from"./memory/lifecycle.js";import{createCliMemoryProviders as m}from"./memory/providers.js";import{formatDetail as g,inspectWorkflow as I,renderAgent as v,renderWorkflow as y,workspaceStatus as k}from"./output.js";import{serveProtocol as h,stopProtocol as b}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=d(e);if(t.help)return void process.stdout.write(l());const o=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),q=t.workspaceRoot;try{const e=await u(q);if(t.workflowRenderId)return void process.stdout.write(y(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(I(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(v(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(o),void await b(e,t);const l=await n({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await f(e);const d=m(e),R=c(e.runtime);let C;if(C=s({workspace:e,toolGateway:l,memoryProviders:d,adapters:[r()],workflowAdapters:[createCliWorkflowAdapter(l,()=>C)],progressNarration:w(R,e.runtime)?{enabled:!0,style:"cli"}:void 0}),t.serveOpenAi)return clearTimeout(o),void await h(C,t);if(!t.shouldRunRequest)return void process.stdout.write(k(e,q));t.trace&&C.subscribe(e=>{const t=a(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${g(t.detail)}\n`)}),C.subscribe(e=>{const t=p(e,R);t&&process.stdout.write(`${t}\n`)});const $=await C.request({input:t.prompt,agentId:t.agentId,sessionId:t.sessionId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0,workflow:t.workflowRunId?{workflowId:t.workflowRunId,input:t.prompt}:void 0});if(t.trace||t.traceJson){const e=C.getRun($.requestId),r=e?i(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${$.output}\n`)}finally{clearTimeout(o)}}function createCliWorkflowAdapter(e,t){return o({nodeResolvers:{tools:async({id:t,node:r,request:o,requestId:s,sessionId:a,state:i,workspace:n})=>{return(await e.invoke({toolId:t,args:(u=r.config,l=o.input,d=i.outputs,!0===u?.inputFromState?{...u,requestInput:l,outputs:d}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof l&&null!==l?l:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`}})).output;var u,l,d},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,u,l;return(await t().request({input:(i=e,n=o.input,u=a.outputs,l=r.config,[`Workflow node agents.${i}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...l?[`Workflow node config: ${JSON.stringify(l)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}(function isCliEntrypoint(){const r=process.argv[1];if(!r)return!1;try{return e(t(import.meta.url))===e(r)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
@@ -0,0 +1,9 @@
1
+ import type { RuntimeEvent } from "@stable-harness/core";
2
+ export type CliEventViewConfig = {
3
+ enabled: boolean;
4
+ include: string[];
5
+ exclude: string[];
6
+ };
7
+ export declare function readCliEventViewConfig(runtimePolicy: Record<string, unknown>): CliEventViewConfig;
8
+ export declare function shouldEnableCliProgressNarration(config: CliEventViewConfig, runtimePolicy: Record<string, unknown>): boolean;
9
+ export declare function formatCliRuntimeEvent(event: RuntimeEvent, config: CliEventViewConfig): string | undefined;
@@ -0,0 +1 @@
1
+ export function readCliEventViewConfig(e){const t=readRecord(e.cli),r=readRecord(t?.events);return{enabled:readBoolean(r?.enabled)??!0,include:readStringList(r?.include)??["runtime.progress.narration"],exclude:readStringList(r?.exclude)??[]}}export function shouldEnableCliProgressNarration(e,t){if(!e.enabled)return!1;const r=readRecord(readRecord(t.progress)?.narration);return!1!==readBoolean(r?.enabled)&&e.include.some(e=>matchesEventPattern("runtime.progress.narration",e))}export function formatCliRuntimeEvent(e,t){if(!t.enabled||!function isIncluded(e,t){const r=function eventKeys(e){return"runtime.adapter.event"===e.type&&isRecord(e.event)&&"string"==typeof e.event.phase?[e.type,e.event.phase]:[e.type]}(e),n=t.include.some(e=>r.some(t=>matchesEventPattern(t,e))),i=t.exclude.some(e=>r.some(t=>matchesEventPattern(t,e)));return n&&!i}(e,t))return;const r=function projectCliEventView(e){return"runtime.progress.narration"===e.type?{group:"Progress",title:e.message}:e.type.startsWith("runtime.request.")?{group:"Run",title:e.type}:"runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type?{group:"Tool",title:e.type,detail:e.toolId}:"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?{group:"Workflow",title:e.type,detail:e.workflowId}:e.type.startsWith("runtime.memory.")?{group:"Memory",title:e.type,detail:memoryDetail(e)}:"runtime.skill.candidate.created"===e.type?{group:"Skill",title:e.type,detail:e.name}:"runtime.artifact.created"===e.type?{group:"Artifact",title:e.type}:"runtime.execution.contract.failed"===e.type?{group:"Contract",title:e.type,detail:e.reason}:"runtime.adapter.event"===e.type?function adapterEventView(e){if(!isRecord(e))return{group:"Adapter",title:"runtime.adapter.event"};const t="string"==typeof e.phase?e.phase:"runtime.adapter.event";return t.startsWith("agent.tool.")?{group:"Agent Tool",title:t,detail:readString(e.toolId)}:t.startsWith("agent.output.")?{group:"Agent Output",title:t,detail:readString(e.text)}:t.startsWith("agent.")?{group:"Agent",title:t,detail:readString(e.adapter)}:{group:"Adapter",title:t,detail:readString(e.adapter)}}(e.event):{group:"event",title:e.type}}(e);return`[${r.group}] ${e.agentId} | ${r.title}${r.detail?` | ${r.detail}`:""}`}function matchesEventPattern(e,t){return"*"===t||(t.endsWith(".*")?e===t.slice(0,-2)||e.startsWith(t.slice(0,-1)):e===t)}function memoryDetail(e){return"runtime.memory.recall.completed"===e.type?`${e.recordIds.length} records`:"provider"in e?e.provider:"target"in e?e.target:void 0}function readRecord(e){return isRecord(e)?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
@@ -1 +1,4 @@
1
1
  export { runCli } from "./cli.js";
2
+ export { parseArgs } from "./args.js";
3
+ export { ensureCliMemoryServices } from "./memory/lifecycle.js";
4
+ export { createCliMemoryProviders } from "./memory/providers.js";
@@ -1 +1 @@
1
- export{runCli}from"./cli.js";
1
+ export{runCli}from"./cli.js";export{parseArgs}from"./args.js";export{ensureCliMemoryServices}from"./memory/lifecycle.js";export{createCliMemoryProviders}from"./memory/providers.js";
@@ -0,0 +1,5 @@
1
+ export type LangGraphEnvConfig = {
2
+ env?: unknown;
3
+ envFile?: unknown;
4
+ };
5
+ export declare function applyLangGraphEnvironment(workspaceRoot: string, config?: LangGraphEnvConfig): Promise<void>;
@@ -0,0 +1 @@
1
+ import{readFile as n}from"node:fs/promises";import t from"node:path";export async function applyLangGraphEnvironment(e,r={}){const o=function resolveEnvFile(n,e){if(!1===e.envFile)return;const r="string"==typeof e.env?e.env:e.envFile,o="string"==typeof r&&r.trim()?r.trim():".env";return t.isAbsolute(o)?o:t.join(n,o)}(e,r);!function applyEnv(n){for(const[t,e]of Object.entries(n))process.env[t]||(process.env[t]=e)}({...o?await async function readEnvFile(t){try{return function parseEnv(n){const t={};for(const e of n.split(/\r?\n/u)){const n=e.trim();if(!n||n.startsWith("#"))continue;const r=n.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/u);r&&(t[r[1]]=unquoteEnvValue(r[2]??""))}return t}(await n(t,"utf8"))}catch(n){if(function isMissingFileError(n){return"object"==typeof n&&null!==n&&"code"in n&&"ENOENT"===n.code}(n))return{};throw n}}(o):{},...function readInlineEnv(n){return"object"!=typeof n||null===n||Array.isArray(n)?{}:Object.fromEntries(Object.entries(n).flatMap(([n,t])=>{if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(n))return[];const e=function resolveEnvValue(n){if("string"==typeof n){const t=n.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]??"":n}if("number"==typeof n||"boolean"==typeof n)return String(n)}(t);return void 0===e?[]:[[n,e]]}))}(r.env)})}function unquoteEnvValue(n){const t=n.trim();if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1);const e=t.match(/^(.*?)\s+#/u);return e?e[1].trim():t}
@@ -4,6 +4,8 @@ export type LangGraphOfficialConfig = {
4
4
  port: number;
5
5
  nWorkers: number;
6
6
  exposeAgents?: string[];
7
+ env?: unknown;
8
+ envFile?: unknown;
7
9
  };
8
10
  export declare function startOfficialLangGraphServer(runtime: ReturnType<typeof createStableHarnessRuntime>, config: LangGraphOfficialConfig): Promise<{
9
11
  url: string;
@@ -1 +1 @@
1
- import{mkdir as n,writeFile as e}from"node:fs/promises";import{createRequire as t}from"node:module";import s from"node:path";import{fileURLToPath as a,pathToFileURL as r}from"node:url";import{startServer as o}from"@langchain/langgraph-api/server";export async function startOfficialLangGraphServer(t,c){const u=t.inspect().workspaceRoot,l=function(n,e){const t=new Set(n.inspect().agents),s=e&&e.length>0?e:[...t].sort();for(const n of s)if(!t.has(n))throw new Error(`LangGraph protocol references unknown agent: ${n}`);return s.map((n,e)=>({id:n,exportName:`agent_${e}_${g(n)}`}))}(t,c.exposeAgents),m=await async function(t,o){const g=s.join(t,".stable-harness","langgraph"),c=s.join(g,"bridge.mjs");return await n(g,{recursive:!0}),await e(c,function(n,e){const t=r(s.join(s.resolve(s.dirname(a(import.meta.url)),"../../../.."),"dist","index.js")).toString(),o=r(i.resolve("@langchain/langgraph")).toString(),g=e.map(n=>`export const ${n.exportName} = createBridgeGraph(${JSON.stringify(n.id)});`).join("\n");return`import langgraph from ${JSON.stringify(o)};\nimport { createStableHarnessRuntime } from ${JSON.stringify(t)};\n\nconst { END, MessagesAnnotation, START, StateGraph } = langgraph;\nconst workspaceRoot = ${JSON.stringify(n)};\nlet runtimePromise;\n\nfunction createBridgeGraph(agentId) {\n const graph = new StateGraph(MessagesAnnotation)\n .addNode("stable_harness", async (state, config) => {\n const runtime = await getRuntime();\n const result = await runtime.request({\n input: lastMessageText(state.messages),\n agentId,\n sessionId: readConfigString(config, "thread_id"),\n requestId: readConfigString(config, "run_id"),\n metadata: {\n protocol: "langgraph",\n openaiMessages: normalizeMessages(state.messages),\n executionContract: { enabled: false },\n toolCallRecovery: { onFailure: "message" },\n },\n });\n return { messages: [{ type: "ai", content: result.output }] };\n })\n .addEdge(START, "stable_harness")\n .addEdge("stable_harness", END);\n return graph.compile();\n}\n\nfunction getRuntime() {\n runtimePromise ??= createStableHarnessRuntime(workspaceRoot);\n return runtimePromise;\n}\n\nfunction lastMessageText(messages) {\n const message = Array.isArray(messages) ? messages.at(-1) : undefined;\n const content = message?.content;\n if (typeof content === "string") return content;\n return content == null ? "" : JSON.stringify(content);\n}\n\nfunction normalizeMessages(messages) {\n if (!Array.isArray(messages)) return [];\n return messages.flatMap((message) => {\n const role = messageRole(message);\n const content = messageContent(message);\n return role && content ? [{ role, content }] : [];\n });\n}\n\nfunction messageRole(message) {\n const value = message?.role ?? message?.type;\n if (value === "human") return "user";\n if (value === "ai") return "assistant";\n return value === "system" || value === "user" || value === "assistant" || value === "tool"\n ? value\n : undefined;\n}\n\nfunction messageContent(message) {\n const content = message?.content;\n if (typeof content === "string") return content;\n return content == null ? "" : JSON.stringify(content);\n}\n\nfunction readConfigString(config, key) {\n const value = config?.configurable?.[key];\n return typeof value === "string" && value ? value : undefined;\n}\n\n${g}\n`}(t,o)),{file:c,relativePath:`./${s.relative(t,c).split(s.sep).join("/")}`}}(u,l),p=Object.fromEntries(l.map(n=>[n.id,`${m.relativePath}:${n.exportName}`])),f=await o({host:c.host,port:c.port,nWorkers:c.nWorkers,cwd:u,graphs:p});return{url:`http://${f.host}`,cleanup:f.cleanup}}const i=t(import.meta.url);function g(n){const e=n.replace(/[^A-Za-z0-9_$]/gu,"_");return/^[A-Za-z_$]/u.test(e)?e:`_${e}`}
1
+ import{mkdir as e,writeFile as n}from"node:fs/promises";import{createRequire as t}from"node:module";import s from"node:path";import{fileURLToPath as r,pathToFileURL as a}from"node:url";import{startServer as o}from"@langchain/langgraph-api/server";import{applyLangGraphEnvironment as i}from"./langgraph-env.js";export async function startOfficialLangGraphServer(t,c){const l=t.inspect().workspaceRoot,g=function exposedAgents(e,n){const t=new Set(e.inspect().agents),s=n&&n.length>0?n:[...t].sort();for(const e of s)if(!t.has(e))throw new Error(`LangGraph protocol references unknown agent: ${e}`);return s.map((e,n)=>({id:e,exportName:`agent_${n}_${safeIdentifier(e)}`}))}(t,c.exposeAgents),p=await async function writeBridgeModule(t,o){const i=s.join(t,".stable-harness","langgraph"),c=s.join(i,"bridge.mjs");return await e(i,{recursive:!0}),await n(c,function bridgeSource(e,n){const t=a(s.join(function packageRoot(){return s.resolve(s.dirname(r(import.meta.url)),"../../../..")}(),"dist","index.js")).toString(),o=a(u.resolve("@langchain/langgraph")).toString(),i=n.map(e=>`export const ${e.exportName} = createBridgeGraph(${JSON.stringify(e.id)});`).join("\n");return[(c={workspaceRoot:e,stableHarnessUrl:t,langGraphUrl:o},`import langgraph from ${JSON.stringify(c.langGraphUrl)};\nimport { containsRecoverableResultOutput, createStableHarnessRuntime, loadWorkspaceFromYaml } from ${JSON.stringify(c.stableHarnessUrl)};\n\nconst { END, MessagesZodState, START, StateGraph } = langgraph;\nconst workspaceRoot = ${JSON.stringify(c.workspaceRoot)};\nlet runtimePromise;\nlet workspacePromise;\n`),'function createBridgeGraph(agentId) {\n const graph = new StateGraph(MessagesZodState)\n .addNode("stable_harness", async (state, config) => {\n const runtime = await getRuntime();\n const protocolMessages = normalizeMessages(state.messages);\n const result = await runtime.request({\n input: currentTurnInput(protocolMessages),\n agentId,\n sessionId: readConfigString(config, "thread_id"),\n requestId: readConfigString(config, "run_id"),\n metadata: {\n protocol: "langgraph",\n openaiMessages: contextualMessages(protocolMessages),\n },\n });\n const content = protocolOutput(runtime, result);\n return { messages: [{ type: "ai", content }] };\n })\n .addEdge(START, "stable_harness")\n .addEdge("stable_harness", END);\n const compiled = graph.compile();\n compiled.getGraphAsync = async () => createInspectionGraph(agentId);\n return compiled;\n}\n\nfunction getRuntime() {\n runtimePromise ??= createStableHarnessRuntime(workspaceRoot);\n return runtimePromise;\n}\n','\nfunction currentTurnInput(messages) {\n const message = lastUserMessage(messages) ?? messages.at(-1);\n const content = message?.content ?? "";\n const context = recentContext(messages.slice(0, -1));\n return context ? [\n "Recent conversation context:",\n context,\n "",\n "Current user request:",\n content,\n ].join("\\n") : content;\n}\n\nfunction contextualMessages(messages) {\n const lastUserIndex = messages.findLastIndex((message) => message.role === "user");\n if (lastUserIndex < 0) return messages;\n const context = recentContext(messages.slice(0, lastUserIndex));\n if (!context) return messages;\n return messages.map((message, index) => index === lastUserIndex ? {\n ...message,\n content: [\n "Recent conversation context:",\n context,\n "",\n "Current user request:",\n message.content,\n ].join("\\n"),\n } : message);\n}\n\nfunction lastUserMessage(messages) {\n return [...messages].reverse().find((entry) => entry.role === "user");\n}\n\nfunction recentContext(messages) {\n return messages\n .filter((message) => message.content.trim())\n .slice(-6)\n .map((message) => `${message.role}: ${compactText(message.content)}`)\n .join("\\n\\n");\n}\n\nfunction compactText(value) {\n const normalized = value.replace(/\\s+/gu, " ").trim();\n return normalized.length > 4000 ? `${normalized.slice(0, 4000)}...` : normalized;\n}\n\nfunction normalizeMessages(messages) {\n if (!Array.isArray(messages)) return [];\n return messages.flatMap((message) => {\n const role = messageRole(message);\n const content = messageContent(message);\n return role && content ? [{ role, content }] : [];\n });\n}\n\nfunction messageRole(message) {\n const value = message?.role ?? message?.type;\n if (value === "human") return "user";\n if (value === "ai") return "assistant";\n return value === "system" || value === "user" || value === "assistant" || value === "tool"\n ? value\n : undefined;\n}\n\nfunction messageContent(message) {\n const content = message?.content;\n if (typeof content === "string") return content;\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (typeof part === "string") return part;\n if (typeof part?.text === "string") return part.text;\n if (typeof part?.content === "string") return part.content;\n return "";\n }).filter(Boolean).join("\\n");\n }\n return content == null ? "" : JSON.stringify(content);\n}\n\nfunction readConfigString(config, key) {\n const value = config?.configurable?.[key];\n return typeof value === "string" && value ? value : undefined;\n}\n','function protocolOutput(runtime, result) {\n if (!containsRecoverableResultOutput(result.output, runtime.getRuntimePolicy?.() ?? {})) {\n return result.output;\n }\n return traceFallbackOutput(runtime.inspectRequest(result.requestId)) ?? [\n "The model returned recoverable tool-call or tool-error text instead of a final answer.",\n "stable-harness could not find a completed tool result to project as the Studio response.",\n ].join(" ");\n}\n\nfunction traceFallbackOutput(inspection) {\n const timeline = Array.isArray(inspection?.timeline) ? inspection.timeline : [];\n for (const item of [...timeline].reverse()) {\n const event = item?.event;\n const adapterEvent = event?.type === "runtime.adapter.event" ? event.event : undefined;\n const text = extractText(adapterEvent?.output);\n if (isUsableOutput(text)) return text;\n }\n return undefined;\n}\n\nfunction extractText(value) {\n if (typeof value === "string") {\n const parsed = parseJson(value);\n return parsed === undefined ? value : extractText(parsed);\n }\n if (!value || typeof value !== "object") return undefined;\n if (typeof value.content === "string") return value.content;\n if (typeof value.text === "string") return value.text;\n if (typeof value.output === "string") return extractText(value.output);\n if (typeof value.structuredResponse === "string") return value.structuredResponse;\n const messages = Array.isArray(value.messages) ? value.messages : Array.isArray(value.update?.messages) ? value.update.messages : [];\n for (const message of [...messages].reverse()) {\n const text = extractText(message?.kwargs ?? message);\n if (text) return text;\n }\n return undefined;\n}\n\nfunction parseJson(value) {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction isUsableOutput(value) {\n return typeof value === "string"\n && value.trim().length > 0\n && !containsRecoverableResultOutput(value, { recovery: { toolCall: { enabled: true } } });\n}\n\n','async function createInspectionGraph(agentId) {\n const workspace = await getWorkspace();\n const agent = workspace.agents.get(agentId);\n const snapshot = agent ? {\n tools: agent.tools,\n subagents: agent.subagents,\n skills: agent.skills ?? [],\n } : { tools: [], subagents: [], skills: [] };\n return {\n toJSON() {\n return inventoryGraph(agentId, snapshot);\n },\n };\n}\n\nfunction getWorkspace() {\n workspacePromise ??= loadWorkspaceFromYaml(workspaceRoot);\n return workspacePromise;\n}\n\nfunction inventoryGraph(agentId, snapshot) {\n const nodes = [\n schemaNode("__start__"),\n runnableNode(agentId, "stable-harness agent"),\n ...snapshot.subagents.map((id) => runnableNode("agent_" + id, "agent:" + id)),\n ...snapshot.tools.map((id) => runnableNode("tool_" + id, "tool:" + id)),\n ...snapshot.skills.map((id) => runnableNode("skill_" + id, "skill:" + id)),\n schemaNode("__end__"),\n ];\n const edges = [\n edge("__start__", agentId, false),\n ...snapshot.subagents.map((id) => edge(agentId, "agent_" + id, true)),\n ...snapshot.tools.map((id) => edge(agentId, "tool_" + id, true)),\n ...snapshot.skills.map((id) => edge(agentId, "skill_" + id, true)),\n edge(agentId, "__end__", true),\n ];\n return { nodes, edges };\n}\n\nfunction schemaNode(id) {\n return {\n id,\n type: "schema",\n data: { "$schema": "https://json-schema.org/draft/2020-12/schema" },\n };\n}\n\nfunction runnableNode(id, label) {\n return {\n id,\n type: "runnable",\n data: {\n id: ["stable-harness", label],\n name: label,\n },\n };\n}\n\nfunction edge(source, target, conditional) {\n return { source, target, conditional };\n}\n',i,""].join("\n");var c}(t,o)),{file:c,relativePath:`./${s.relative(t,c).split(s.sep).join("/")}`}}(l,g);await i(l,c);const m=Object.fromEntries(g.map(e=>[e.id,`${p.relativePath}:${e.exportName}`])),d=await o({host:c.host,port:c.port,nWorkers:c.nWorkers,cwd:l,graphs:m});return{url:`http://${d.host}`,cleanup:d.cleanup}}const u=t(import.meta.url);function safeIdentifier(e){const n=e.replace(/[^A-Za-z0-9_$]/gu,"_");return/^[A-Za-z_$]/u.test(n)?n:`_${n}`}
@@ -0,0 +1,2 @@
1
+ import type { CompiledWorkspace } from "@stable-harness/core";
2
+ export declare function ensureCliMemoryServices(workspace: CompiledWorkspace): Promise<void>;
@@ -0,0 +1 @@
1
+ import{spawn as e}from"node:child_process";import{existsSync as r}from"node:fs";import t from"node:path";import{fileURLToPath as n}from"node:url";export async function ensureCliMemoryServices(r){const t=function readLangMemServiceConfig(e){const r=readRecord(e.runtime.memory?.LangMem)??readRecord(e.runtime.memory?.langmem);if(!function shouldAutoStart(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(e,r))return;const t=readString(r?.baseUrl)??readString(r?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;var n;return t?{baseUrl:t,autoStart:!1!==r?.autoStart,command:readString(r?.command)??"python3",args:(n=r?.args,(Array.isArray(n)?n.filter(e=>"string"==typeof e&&e.trim().length>0):void 0)??["-m","stable_harness_langmem_service"]),env:{...defaultLangMemEnv(e,t),...readStringRecord(r?.env)}}:void 0}(r);if(!t||!t.autoStart||!function isLocalBaseUrl(e){const r=new URL(e);return["localhost","127.0.0.1","::1"].includes(r.hostname)}(t.baseUrl)||await isHealthy(t.baseUrl))return;const n=e(t.command,t.args,{cwd:r.root,detached:!0,env:{...process.env,...t.env},stdio:"ignore"});n.unref(),await async function waitForStartedService(e,r){await Promise.race([waitForHealth(r.baseUrl),new Promise((r,t)=>e.once("error",t)),new Promise((t,n)=>{e.once("exit",(e,t)=>{n(new Error(`LangMem service exited before becoming healthy: ${r.command} code=${e??"null"} signal=${t??"null"}`))})})])}(n,t)}function defaultLangMemEnv(e,o){const a=new URL(o),i=readString(e.runtime.dataRoot)??".stable-harness",s=t.resolve(function packageRoot(){return t.resolve(t.dirname(n(import.meta.url)),"../../../../..")}(),"services/langmem-python/src");return{HOST:"localhost"===a.hostname?"127.0.0.1":a.hostname,PORT:a.port||("https:"===a.protocol?"443":"80"),LANGMEM_SQLITE_PATH:t.join(t.resolve(e.root,i),"memory/langmem/langmem.sqlite"),...r(s)?{PYTHONPATH:mergePythonPath(s)}:{}}}async function waitForHealth(e){for(let r=0;r<60;r+=1){if(await isHealthy(e))return;await new Promise(e=>setTimeout(e,500))}throw new Error(`LangMem service did not become healthy at ${e}`)}async function isHealthy(e){try{const r=await fetch(`${e.replace(/\/+$/u,"")}/health`,{signal:AbortSignal.timeout(1e3)}),t=await r.json();return r.ok&&!0===t.ok}catch{return!1}}function mergePythonPath(e){return process.env.PYTHONPATH?`${e}${t.delimiter}${process.env.PYTHONPATH}`:e}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readStringRecord(e){const r=readRecord(e);return Object.fromEntries(Object.entries(r??{}).filter(e=>"string"==typeof e[1]))}
@@ -0,0 +1,3 @@
1
+ import type { CompiledWorkspace } from "@stable-harness/core";
2
+ import { type MemoryProvider } from "@stable-harness/memory";
3
+ export declare function createCliMemoryProviders(workspace: CompiledWorkspace): MemoryProvider[];
@@ -0,0 +1 @@
1
+ import{createLangMemServiceProvider as e}from"@stable-harness/memory";export function createCliMemoryProviders(r){const o=readRecord(r.runtime.memory?.LangMem)??readRecord(r.runtime.memory?.langmem);if(!function shouldEnableLangMemProvider(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(r,o))return[];const d=readString(o?.baseUrl)??readString(o?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;return d?[e({baseUrl:d,timeoutMs:(n=o?.timeoutMs,("number"==typeof n&&Number.isFinite(n)?n:void 0)??readEnvNumber("STABLE_HARNESS_LANGMEM_TIMEOUT_MS")),config:readProviderConfig(o)})]:[];var n}function readProviderConfig(e){return{provider:"langmem-service",...readRecord(e?.mode)?{mode:readRecord(e?.mode)}:{},...readRecord(e?.types)?{types:readRecord(e?.types)}:{},...readRecord(e?.approval)?{approval:readRecord(e?.approval)}:{},...readRecord(e?.defaults)?{defaults:readRecord(e?.defaults)}:{}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readEnvNumber(e){const r=process.env[e];return r?Number(r):void 0}
@@ -1 +1 @@
1
- import{compileWorkflowPlan as r,renderAgentMermaid as o,renderWorkflowMermaid as e}from"@stable-harness/core";export function workspaceStatus(r,o){return[`stable-harness workspace: ${r.runtime.workspaceId??o}`,`root: ${r.root}`,`default agent: ${r.runtime.defaultAgentId}`,`agents: ${[...r.agents.keys()].sort().join(", ")||"none"}`,`workflows: ${[...r.workflows.keys()].sort().join(", ")||"none"}`,`default workflow: ${r.runtime.workflowRouting?.defaultWorkflowId??"none"}`,`workflow routes: ${t(r)}`,`tools: ${r.tools.size}`,"","Run a request with:"," stable-harness [prompt]"," stable-harness --agent <id> [prompt]"," stable-harness workflow render <workflow-id>"," stable-harness workflow inspect <workflow-id>"," stable-harness agent render <agent-id>"," stable-harness start",""].join("\n")}export function renderWorkflow(r,o){const t=n(r,o,"render");return`${e(t)}\n`}export function renderAgent(r,e){if(!e)throw new Error("Usage: stable-harness agent render <agent-id>");return`${o(r,e)}\n`}export function inspectWorkflow(o,e){const t=n(o,e,"inspect");return`${JSON.stringify({workflow:t,plan:r(t)},null,2)}\n`}export function formatDetail(r){return r&&"string"==typeof r.toolId?`:${r.toolId}`:""}function n(r,o,e){if(!o)throw new Error(`Usage: stable-harness workflow ${e} <workflow-id>`);const n=r.workflows.get(o);if(!n)throw new Error(`Workflow is not defined: ${o}`);return n}function t(r){return(r.runtime.workflowRouting?.routes??[]).map(r=>r.id).sort().join(", ")||"none"}
1
+ import{compileWorkflowPlan as o,renderAgentMermaid as r,renderWorkflowMermaid as e}from"@stable-harness/core";export function workspaceStatus(o,r){return[`stable-harness workspace: ${o.runtime.workspaceId??r}`,`root: ${o.root}`,`default agent: ${o.runtime.defaultAgentId}`,`agents: ${[...o.agents.keys()].sort().join(", ")||"none"}`,`workflows: ${[...o.workflows.keys()].sort().join(", ")||"none"}`,`evaluations: ${[...o.evaluations?.keys()??[]].sort().join(", ")||"none"}`,`default workflow: ${o.runtime.workflowRouting?.defaultWorkflowId??"none"}`,`workflow routes: ${formatWorkflowRoutes(o)}`,`tools: ${o.tools.size}`,"","Run a request with:"," stable-harness [prompt]"," stable-harness --agent <id> [prompt]"," stable-harness workflow render <workflow-id>"," stable-harness workflow inspect <workflow-id>"," stable-harness agent render <agent-id>"," stable-harness start",""].join("\n")}export function renderWorkflow(o,r){const n=readWorkflow(o,r,"render");return`${e(n)}\n`}export function renderAgent(o,e){if(!e)throw new Error("Usage: stable-harness agent render <agent-id>");return`${r(o,e)}\n`}export function inspectWorkflow(r,e){const n=readWorkflow(r,e,"inspect");return`${JSON.stringify({workflow:n,plan:o(n)},null,2)}\n`}export function formatDetail(o){return o&&"string"==typeof o.toolId?`:${o.toolId}`:""}function readWorkflow(o,r,e){if(!r)throw new Error(`Usage: stable-harness workflow ${e} <workflow-id>`);const n=o.workflows.get(r);if(!n)throw new Error(`Workflow is not defined: ${r}`);return n}function formatWorkflowRoutes(o){return(o.runtime.workflowRouting?.routes??[]).map(o=>o.id).sort().join(", ")||"none"}
@@ -1,3 +1,5 @@
1
1
  import type { createStableHarnessRuntime } from "@stable-harness/core";
2
+ import type { CompiledWorkspace } from "@stable-harness/core";
2
3
  import type { CliArgs } from "./args.js";
3
4
  export declare function serveProtocol(runtime: ReturnType<typeof createStableHarnessRuntime>, args: CliArgs): Promise<void>;
5
+ export declare function stopProtocol(workspace: CompiledWorkspace, args: CliArgs): Promise<void>;
@@ -1 +1 @@
1
- import{createOpenAiCompatibleHttpServer as t}from"@stable-harness/protocols";import{startOfficialLangGraphServer as o}from"./langgraph-official.js";const e="127.0.0.1";export async function serveProtocol(t,e){const a=function(t,o){const e=p(t.getRuntimePolicy().protocols)??{},a=s(e,"openaiCompatible","openai-compatible","openai")??{},c=s(e,"langgraph")??{};return[...i(a)?[r(t,a,o)]:[],...i(c)?[n(c)]:[]]}(t,e),c=[];for(const e of a)if("http"===e.kind){await l(e),c.push(()=>f(e.server));const t=e.server.address(),o="object"==typeof t&&t?t.port:e.port;process.stdout.write(`stable-harness ${e.protocol} API listening on http://${e.host}:${o}/v1\n`)}else{const r=await o(t,e.config);c.push(r.cleanup),process.stdout.write(`stable-harness ${e.protocol} API listening on ${r.url}\n`)}await async function(t){const o=setInterval(()=>{},864e5);await new Promise(e=>{const r=()=>{clearInterval(o),Promise.allSettled(t.map(t=>t())).finally(()=>process.exit(0))};process.once("SIGINT",r),process.once("SIGTERM",r)})}(c)}function r(o,r,n){const s=a(r.host)??e,i=c(r.port)??8642,p=a(r.bearerToken)??a(r.apiKey)??n.apiKey;return{kind:"http",protocol:"openai-compatible",server:t(o,{bearerToken:p}),host:s,port:i}}function n(t){const o=a(t.host)??e,r=c(t.port)??2024,n=function(t){if(Array.isArray(t)&&t.every(t=>"string"==typeof t))return t.filter(t=>t.trim()).map(t=>t.trim())}(t.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:o,port:r,nWorkers:c(t.nWorkers)??10,...n?{exposeAgents:n}:{}}}}function s(t,...o){for(const e of o){const o=p(t[e]);if(o)return o}}function i(t){return!1!==t.enabled}function a(t){if("string"!=typeof t||!t.trim())return;const o=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return o?process.env[o[1]]??o[2]:t}function c(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}function p(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}async function l(t){await new Promise(o=>t.server.listen(t.port,t.host,o))}async function f(t){await new Promise((o,e)=>{t.close(t=>{t?e(t):o()})})}
1
+ import{execFile as r}from"node:child_process";import{promisify as t}from"node:util";import{createOpenAiCompatibleHttpServer as o}from"@stable-harness/protocols";import{startOfficialLangGraphServer as e}from"./langgraph-official.js";const n="127.0.0.1",s=t(r);export async function serveProtocol(r,t){const o=createConfiguredServers(r,t),e=[];let n=0;for(const t of o)if("http"===t.kind){if(!await listen(t)){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.host}:${t.port}/v1\n`);continue}e.push(()=>closeHttpServer(t.server)),n+=1;const r=t.server.address(),o="object"==typeof r&&r?r.port:t.port;process.stdout.write(`stable-harness ${t.protocol} API listening on http://${t.host}:${o}/v1\n`)}else{const o=await startLangGraphServer(r,t);if(!o){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.config.host}:${t.config.port}\n`);continue}e.push(o.cleanup),n+=1,process.stdout.write(`stable-harness ${t.protocol} API listening on ${o.url}\n`)}0!==n&&await async function waitForShutdown(r){const t=setInterval(()=>{},864e5);await new Promise(o=>{const shutdown=()=>{clearInterval(t),Promise.allSettled(r.map(r=>r())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(e)}export async function stopProtocol(r,t){const o=createConfiguredServers({getRuntimePolicy:()=>r.runtime},t).map(r=>"http"===r.kind?{protocol:r.protocol,host:r.host,port:r.port}:{protocol:r.protocol,host:r.config.host,port:r.config.port}),e=await Promise.all(o.map(async r=>({target:r,pids:await stableHarnessListenerPids(r.port)}))),n=[...new Set(e.flatMap(r=>r.pids))];for(const r of n)process.kill(r,"SIGTERM");for(const{target:r,pids:t}of e)0!==t.length?process.stdout.write(`stable-harness ${r.protocol} API stopped on ${r.host}:${r.port} pid=${t.join(",")}\n`):process.stdout.write(`stable-harness ${r.protocol} API not running on ${r.host}:${r.port}\n`)}function createConfiguredServers(r,t){const o=readRecord(r.getRuntimePolicy().protocols)??{},e=protocolConfig(o,"openaiCompatible","openai-compatible","openai")??{},n=protocolConfig(o,"langgraph")??{};return[...enabled(e)?[openAiServer(r,e,t)]:[],...enabled(n)?[langGraphServer(n)]:[]]}function openAiServer(r,t,e){const s=configString(t.host)??n,i=e.port??configNumber(t.port)??8642,a=e.host??s,c=configString(t.bearerToken)??configString(t.apiKey)??e.apiKey;return{kind:"http",protocol:"openai-compatible",server:o(r,{bearerToken:c}),host:a,port:i,...c?{bearerToken:c}:{}}}function langGraphServer(r){const t=configString(r.host)??n,o=configNumber(r.port)??2024,e=function configStringArray(r){if(Array.isArray(r)&&r.every(r=>"string"==typeof r))return r.filter(r=>r.trim()).map(r=>r.trim())}(r.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:t,port:o,nWorkers:configNumber(r.nWorkers)??10,...e?{exposeAgents:e}:{},...void 0!==r.env?{env:r.env}:{},...void 0!==r.envFile?{envFile:r.envFile}:{}}}}function protocolConfig(r,...t){for(const o of t){const t=readRecord(r[o]);if(t)return t}}function enabled(r){return!1!==r.enabled}function configString(r){if("string"!=typeof r||!r.trim())return;const t=r.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]:r}function configNumber(r){return"number"==typeof r&&Number.isFinite(r)?r:"string"==typeof r&&r.trim()?Number(r):void 0}function readRecord(r){return"object"!=typeof r||null===r||Array.isArray(r)?void 0:r}async function listen(r){try{return await new Promise((t,o)=>{r.server.once("error",o),r.server.listen(r.port,r.host,()=>{r.server.off("error",o),t()})}),!0}catch(t){if(isAddressInUse(t)&&await async function isOpenAiServerAlreadyRunning(r){const t=await fetchJson(`http://${r.host}:${r.port}/v1/capabilities`,{...r.bearerToken?{authorization:`Bearer ${r.bearerToken}`}:{}});return"stable_harness.capabilities"===t?.object}(r))return!1;throw portConflictError(t,r.protocol,r.host,r.port)}}async function startLangGraphServer(r,t){if(!await isLangGraphServerAlreadyRunning(t))try{return await e(r,t.config)}catch(r){if(isAddressInUse(r)&&await isLangGraphServerAlreadyRunning(t))return;throw portConflictError(r,t.protocol,t.config.host,t.config.port)}}async function isLangGraphServerAlreadyRunning(r){const t=await fetchJson(`http://${r.config.host}:${r.config.port}/ok`);return!0===t?.ok}async function fetchJson(r,t={}){try{const o=await fetch(r,{headers:t});if(!o.ok)return;return await o.json()}catch{return}}function isAddressInUse(r){return"EADDRINUSE"===function readErrorCode(r){return"object"==typeof r&&null!==r&&"code"in r?r.code:void 0}(r)||String(r).includes("EADDRINUSE")}function portConflictError(r,t,o,e){return isAddressInUse(r)?new Error([`stable-harness ${t} port is already in use: ${o}:${e}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${o}:${e}.`].join("\n")):r}async function stableHarnessListenerPids(r){const t=await async function listenerPids(r){try{const{stdout:t}=await s("lsof",[`-tiTCP:${r}`,"-sTCP:LISTEN"]);return t.split(/\s+/u).map(r=>Number(r)).filter(r=>Number.isInteger(r)&&r>0)}catch{return[]}}(r);return(await Promise.all(t.map(async r=>{const t=await async function processCommand(r){try{const{stdout:t}=await s("ps",["-p",String(r),"-o","command="]);return t.trim()}catch{return""}}(r);return function isStableHarnessStartCommand(r){return/\bstable-harness\b/u.test(r)&&/\bstart\b/u.test(r)}(t)?r:void 0}))).filter(r=>"number"==typeof r)}async function closeHttpServer(r){await new Promise((t,o)=>{r.close(r=>{r?o(r):t()})})}
@@ -15,7 +15,9 @@
15
15
  "dependencies": {
16
16
  "@langchain/langgraph-api": "^1.2.1",
17
17
  "@stable-harness/adapter-deepagents": "0.0.1",
18
+ "@stable-harness/adapter-langgraph": "0.0.1",
18
19
  "@stable-harness/core": "0.0.1",
20
+ "@stable-harness/memory": "0.0.1",
19
21
  "@stable-harness/protocols": "0.0.1",
20
22
  "@stable-harness/tool-gateway": "0.0.1",
21
23
  "@stable-harness/workspace-yaml": "0.0.1"
@@ -0,0 +1,18 @@
1
+ export type WorkspaceEvaluation = {
2
+ id: string;
3
+ description?: string;
4
+ sourcePath?: string;
5
+ suite?: string;
6
+ cases: WorkspaceEvaluationCase[];
7
+ config?: Record<string, unknown>;
8
+ };
9
+ export type WorkspaceEvaluationCase = {
10
+ id: string;
11
+ description?: string;
12
+ agentId?: string;
13
+ workflowId?: string;
14
+ input?: unknown;
15
+ tools?: string[];
16
+ assertions?: Record<string, unknown>;
17
+ metadata?: Record<string, unknown>;
18
+ };
@@ -0,0 +1 @@
1
+ export{};
@@ -1 +1 @@
1
- export function assertExecutionContract(r){(function(e){const t=n(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(r.metadata)||(function(n){const r=e(n.agent);if(!r.requiresPlan)return;const i=o(r.planEvidenceTools);if(0===i.length)throw n.emit({type:"execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const s=n.store.getRun(n.requestId),c=new Set((s?.events??[]).flatMap(t));if(!i.some(e=>c.has(e)))throw n.emit({type:"execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_plan"}),new Error(`Execution contract requires a planning checkpoint from one of: ${i.join(", ")}`)}(r),function(n){const r=o(e(n.agent).requiredEvidenceTools);if(0===r.length)return;const i=n.store.getRun(n.requestId),s=new Set((i?.events??[]).flatMap(t)),c=r.filter(e=>!s.has(e));if(0!==c.length)throw n.emit({type:"execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_required_evidence",missingEvidenceTools:c}),new Error(`Execution contract missing required evidence tools: ${c.join(", ")}`)}(r))}function e(e){return n(e.config.executionContract)?e.config.executionContract:{}}function t(e){return"tool.completed"===e.type?[e.toolId]:"adapter.event"===e.type&&n(e.event)&&"tool.result"===e.event.phase&&"string"==typeof e.event.toolId?[e.event.toolId]:[]}function n(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function o(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
1
+ export function assertExecutionContract(e){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(e.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(e),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(e))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&"agent.tool.result"===e.event.phase&&"string"==typeof e.event.toolId?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
@@ -1,5 +1,6 @@
1
1
  export * from "./runtime/persistence/artifacts.js";
2
2
  export * from "./execution-contract.js";
3
+ export * from "./recovery/tool-call.js";
3
4
  export * from "./runtime/persistence/inspection.js";
4
5
  export * from "./memory-plugins.js";
5
6
  export * from "./runtime/persistence/queue.js";
@@ -7,4 +8,6 @@ export * from "./runtime.js";
7
8
  export * from "./runtime/persistence/stores.js";
8
9
  export * from "./trace.js";
9
10
  export * from "./types.js";
11
+ export * from "./evaluations/index.js";
12
+ export * from "./spec-driven/index.js";
10
13
  export * from "./workflows/index.js";
@@ -1 +1 @@
1
- export*from"./runtime/persistence/artifacts.js";export*from"./execution-contract.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/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workflows/index.js";
1
+ export*from"./runtime/persistence/artifacts.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/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./evaluations/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
@@ -1 +1 @@
1
- import{formatError as e,readRecord as t,readWorkspaceId as n,resolveEnabledMemories as a,resolveMemoryProvider as r,resolvePluginNamespace as s}from"./shared.js";export function createMemoryMaintenanceDaemon(e){let t;const n=e.emit??(()=>{});return{async runOnce(t={}){const a=t.agent??function(e,t){const n=e.agents.get(t??e.runtime.defaultAgentId);if(!n)throw new Error("Memory maintenance requires a configured default agent");return n}(e.workspace,e.defaultAgentId),r={emit:n,requestId:t.requestId??`memory-maintenance-${Date.now()}`,sessionId:t.sessionId??"memory-maintenance",agent:a,workspace:e.workspace,prompt:e.prompt??u(e.workspace)},s=[];for(const t of e.targets)s.push(await o(r,t));return s},start(){e.intervalMs&&!t&&(t=setInterval(()=>{this.runOnce()},e.intervalMs))},stop(){t&&(clearInterval(t),t=void 0)}}}export function createLangMemMaintenanceTarget(e){return{name:"LangMem",async run(t){const n=[];for(const o of a(t.workspace,"write")){const a=r(e.providers??[],o.provider);if(!a)continue;const c=s(t.workspace,t.agent,{input:"",sessionId:t.sessionId},o),d=i(await a.search({namespace:c,query:t.prompt,limit:e.limit??100}),await a.search({namespace:c,query:"",limit:e.limit??100}));n.push(...await a.consolidate({records:d}))}return{operations:n}}}}export function createSkillCandidateMinerTarget(e){return{name:"skillCandidateMiner",async run(t){const n=await async function(e,t){const n=[];for(const o of a(t.workspace,"all")){const a=r(e??[],o.provider);if(!a)continue;const c=s(t.workspace,t.agent,{input:"",sessionId:t.sessionId},o);n.push(...i(await a.search({namespace:c,query:t.prompt,limit:100}),await a.search({namespace:c,query:"",limit:100})))}return i(n,[])}(e.providers,t),o=function(e){const t=new Map;for(const n of e){const e=c(n);e&&t.set(e,[...t.get(e)??[],n])}return t}(n),m=[];for(const[n,a]of o){if(a.length<(e.minEvidenceCount??3))continue;const r=await e.store.upsert(d(t.workspace,n,a));m.push(r),t.emit({type:"skill.candidate.created",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,candidateId:r.id,name:r.name,confidence:r.confidence,evidenceCount:r.evidenceCount,status:r.status,proposedPath:r.proposedPath}),await(e.approvals?.create({kind:"skill_candidate",reason:`Review mined skill candidate ${r.name}`,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,subject:{candidate:r}}))}return{operations:[],metadata:{candidateCount:m.length}}}}}async function o(t,n){t.emit({type:"memory.maintenance.started",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name});try{const e=await n.run(t);return t.emit({type:"memory.maintenance.completed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name,operationCount:e.operations?.length??0}),e}catch(a){return t.emit({type:"memory.maintenance.failed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name,error:e(a)}),{operations:[],metadata:{error:e(a)}}}}function i(e,t){const n=new Map;for(const a of[...e,...t])n.set(a.id,a);return[...n.values()]}function c(e){const n=t(e.metadata?.skillCandidate),a=t(t(e.metadata?.requestMetadata)?.skillCandidate),r=n?.name??a?.name;return"string"==typeof r&&r.trim()?function(e){return e.toLowerCase().replace(/[^a-z0-9]+/gu,"-").replace(/^-+|-+$/gu,"")}(r):void 0}function d(e,t,a){const r=function(e){return e.split("-").filter(Boolean).map(e=>`${e[0]?.toUpperCase()??""}${e.slice(1)}`).join(" ")}(t);return{workspaceId:n(e),name:t,status:"review_required",confidence:Math.min(.95,.55+.1*a.length),proposedPath:`resources/skills/${t}/SKILL.md`,title:r,summary:`Repeated stable workflow mined from ${a.length} memory records.`,draftMarkdown:m(r,a),evidence:a.map(e=>({evidenceType:"memory_record",evidenceRef:e.id,summary:e.summary??e.content.slice(0,160),weight:e.confidence}))}}function m(e,t){return[`# ${e}`,"","## Purpose","","Apply this stable workflow when the task matches the evidence.","","## Evidence","",t.map(e=>`- ${e.summary??e.content}`).join("\n")].join("\n")}function u(e){const n=t(e.runtime.memory?.maintenance);return"string"==typeof n?.prompt?n.prompt:"durable preferences workspace facts reusable procedures prior corrections stale duplicated contradicted memories"}
1
+ import{formatError as e,readRecord as t,readWorkspaceId as n,resolveEnabledMemories as a,resolveMemoryProvider as r,resolvePluginNamespace as s}from"./shared.js";export function createMemoryMaintenanceDaemon(e){let t;const n=e.emit??(()=>{});return{async runOnce(t={}){const a=t.agent??function resolveMaintenanceAgent(e,t){const n=e.agents.get(t??e.runtime.defaultAgentId);if(!n)throw new Error("Memory maintenance requires a configured default agent");return n}(e.workspace,e.defaultAgentId),r={emit:n,requestId:t.requestId??`memory-maintenance-${Date.now()}`,sessionId:t.sessionId??"memory-maintenance",agent:a,workspace:e.workspace,prompt:e.prompt??defaultMaintenancePrompt(e.workspace)},s=[];for(const t of e.targets)s.push(await runMaintenanceTarget(r,t));return s},start(){e.intervalMs&&!t&&(t=setInterval(()=>{this.runOnce()},e.intervalMs))},stop(){t&&(clearInterval(t),t=void 0)}}}export function createLangMemMaintenanceTarget(e){return{name:"LangMem",async run(t){const n=[];for(const o of a(t.workspace,"write")){const a=r(e.providers??[],o.provider);if(!a)continue;const i=s(t.workspace,t.agent,{input:"",sessionId:t.sessionId},o),c=mergeMemoryRecords(await a.search({namespace:i,query:t.prompt,limit:e.limit??100}),await a.search({namespace:i,query:"",limit:e.limit??100}));n.push(...await a.consolidate({records:c}))}return{operations:n}}}}export function createSkillCandidateMinerTarget(e){return{name:"skillCandidateMiner",async run(t){const n=await async function collectMaintenanceRecords(e,t){const n=[];for(const o of a(t.workspace,"all")){const a=r(e??[],o.provider);if(!a)continue;const i=s(t.workspace,t.agent,{input:"",sessionId:t.sessionId},o);n.push(...mergeMemoryRecords(await a.search({namespace:i,query:t.prompt,limit:100}),await a.search({namespace:i,query:"",limit:100})))}return mergeMemoryRecords(n,[])}(e.providers,t),o=function groupSkillCandidateRecords(e){const t=new Map;for(const n of e){const e=readSkillCandidateName(n);e&&t.set(e,[...t.get(e)??[],n])}return t}(n),i=[];for(const[n,a]of o){if(a.length<(e.minEvidenceCount??3))continue;const r=await e.store.upsert(createSkillCandidateInput(t.workspace,n,a));i.push(r),t.emit({type:"runtime.skill.candidate.created",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,candidateId:r.id,name:r.name,confidence:r.confidence,evidenceCount:r.evidenceCount,status:r.status,proposedPath:r.proposedPath}),await(e.approvals?.create({kind:"skill_candidate",reason:`Review mined skill candidate ${r.name}`,requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,subject:{candidate:r}}))}return{operations:[],metadata:{candidateCount:i.length}}}}}async function runMaintenanceTarget(t,n){t.emit({type:"runtime.memory.maintenance.started",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name});try{const e=await n.run(t);return t.emit({type:"runtime.memory.maintenance.completed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name,operationCount:e.operations?.length??0}),e}catch(a){return t.emit({type:"runtime.memory.maintenance.failed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,target:n.name,error:e(a)}),{operations:[],metadata:{error:e(a)}}}}function mergeMemoryRecords(e,t){const n=new Map;for(const a of[...e,...t])n.set(a.id,a);return[...n.values()]}function readSkillCandidateName(e){const n=t(e.metadata?.skillCandidate),a=t(t(e.metadata?.requestMetadata)?.skillCandidate),r=n?.name??a?.name;return"string"==typeof r&&r.trim()?function slugify(e){return e.toLowerCase().replace(/[^a-z0-9]+/gu,"-").replace(/^-+|-+$/gu,"")}(r):void 0}function createSkillCandidateInput(e,t,a){const r=function titleFromName(e){return e.split("-").filter(Boolean).map(e=>`${e[0]?.toUpperCase()??""}${e.slice(1)}`).join(" ")}(t);return{workspaceId:n(e),name:t,status:"review_required",confidence:Math.min(.95,.55+.1*a.length),proposedPath:`resources/skills/${t}/SKILL.md`,title:r,summary:`Repeated stable workflow mined from ${a.length} memory records.`,draftMarkdown:renderSkillDraft(r,a),evidence:a.map(e=>({evidenceType:"memory_record",evidenceRef:e.id,summary:e.summary??e.content.slice(0,160),weight:e.confidence}))}}function renderSkillDraft(e,t){return[`# ${e}`,"","## Purpose","","Apply this stable workflow when the task matches the evidence.","","## Evidence","",t.map(e=>`- ${e.summary??e.content}`).join("\n")].join("\n")}function defaultMaintenancePrompt(e){const n=t(e.runtime.memory?.maintenance);return"string"==typeof n?.prompt?n.prompt:"durable preferences workspace facts reusable procedures prior corrections stale duplicated contradicted memories"}
@@ -1 +1 @@
1
- export function resolveEnabledMemories(r,o="all"){const n=r.runtime.memory??{};if(!function(r,o){return!1!==r.enabled&&("recall"!==o||!1!==r.recall&&!1!==r.read&&!1!==e(r,"LangMem","read"))&&("write"!==o||!1!==r.write&&!1!==e(r,"LangMem","write"))}(n,o))return[];const t=Array.isArray(n.refs)?n.refs.filter(e=>"string"==typeof e):[];return(t.length>0?t:[...r.memories.keys()]).map(e=>r.memories.get(e)).filter(e=>Boolean(e?.enabled))}export function resolvePluginNamespace(e,r,o,n){const t=readWorkspaceId(e),i=n.profile??n.id;return"agent"===i?`${t}:agent:${r.id}:${n.id}`:"user"===i?`${t}:user:${function(e){return e.metadata?.userId??"local"}(o)}:${n.id}`:"session"===i?`${t}:session:${o.sessionId??"default"}:${n.id}`:`${t}:${i}:${n.id}`}export function resolveMemoryProvider(e,r){return e.find(e=>e.name===r||e.name===`${r}-service`)}export function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}export function readWorkspaceId(e){return e.runtime.workspaceId??e.runtime.profile??e.root}export function formatError(e){return e instanceof Error?e.message:String(e)}function e(e,r,o){const n=readRecord(e[r])??readRecord(e[r.toLowerCase()]);return"boolean"==typeof n?.[o]?n[o]:void 0}
1
+ export function resolveEnabledMemories(e,r="all"){const o=e.runtime.memory??{};if(!function isMemoryPhaseEnabled(e,r){return!1!==e.enabled&&("recall"!==r||!1!==e.recall&&!1!==e.read&&!1!==readMemoryFlag(e,"LangMem","read"))&&("write"!==r||!1!==e.write&&!1!==readMemoryFlag(e,"LangMem","write"))}(o,r))return[];const n=Array.isArray(o.refs)?o.refs.filter(e=>"string"==typeof e):[];return(n.length>0?n:[...e.memories.keys()]).map(r=>e.memories.get(r)).filter(e=>Boolean(e?.enabled))}export function resolvePluginNamespace(e,r,o,n){const t=readWorkspaceId(e),a=n.profile??n.id;return"agent"===a?`${t}:agent:${r.id}:${n.id}`:"user"===a?`${t}:user:${function readUserId(e){return e.metadata?.userId??"local"}(o)}:${n.id}`:"session"===a?`${t}:session:${o.sessionId??"default"}:${n.id}`:`${t}:${a}:${n.id}`}export function resolveMemoryProvider(e,r){return e.find(e=>e.name===r||e.name===`${r}-service`)}export function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}export function readWorkspaceId(e){return e.runtime.workspaceId??e.runtime.profile??e.root}export function formatError(e){return e instanceof Error?e.message:String(e)}function readMemoryFlag(e,r,o){const n=readRecord(e[r])??readRecord(e[r.toLowerCase()]);return"boolean"==typeof n?.[o]?n[o]:void 0}
@@ -1 +1 @@
1
- import{formatError as e,readRecord as r,readWorkspaceId as t,resolveEnabledMemories as n,resolveMemoryProvider as o,resolvePluginNamespace as s}from"./memory-plugins/shared.js";export{resolveEnabledMemories,resolvePluginNamespace}from"./memory-plugins/shared.js";export{createLangMemMaintenanceTarget,createMemoryMaintenanceDaemon,createSkillCandidateMinerTarget}from"./memory-plugins/maintenance.js";export async function runMemoryPlugins(e){const r=n(e.workspace,"write");if(e.providers?.length&&0!==r.length&&!1!==e.request.metadata?.memoryWrite)for(const t of r)await a(e,t)}export async function recallMemoryPlugins(e){const r=n(e.workspace,"recall");if(!e.providers?.length||0===r.length)return[];const t=await Promise.all(r.map(async r=>async function(e,r){const t=o(e.providers??[],r.provider);if(!t)return;const n=s(e.workspace,e.agent,e.request,r),a=await t.search({namespace:n,query:i(e),limit:c(e.workspace)});return{namespace:n,records:a,context:a.map(e=>`- ${e.summary??e.content}\n ${e.content}`).join("\n")}}(e,r)));return t.filter(e=>Boolean(e?.context))}async function a(r,t){const n=o(r.providers??[],t.provider);if(!n)return;const a=s(r.workspace,r.agent,r.request,t);r.emit({type:"memory.plugin.started",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:a});try{const e=await n.propose(function(e,r,t){return{namespace:t,content:[`User input:\n${e.request.input}`,`Agent output:\n${e.result.text}`].join("\n\n"),sourceType:"runtime-run",sourceRef:e.requestId,metadata:{memoryId:r.id,provider:r.provider,profile:r.profile,mode:r.mode,prompts:r.prompts,workspaceRoot:e.workspace.root,agentId:e.agent.id,sessionId:e.sessionId,requestMetadata:e.request.metadata}}}(r,t,a));r.emit({type:"memory.plugin.completed",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:a,candidateCount:e.length})}catch(o){r.emit({type:"memory.plugin.failed",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:a,error:e(o)})}}function i(e){const r=[`task: ${e.request.input}`,`workspace: ${t(e.workspace)}`,`agent: ${e.agent.id}`,"memory_needed: durable preferences, workspace facts, reusable procedures, prior corrections"],n=(o=e.request.metadata,"string"==typeof o?.recentContext?o.recentContext.slice(0,3e3):Array.isArray(o?.recentTurns)?o.recentTurns.filter(e=>"string"==typeof e).slice(-6).join("\n").slice(0,3e3):void 0);var o;return n?[...r,`recent_context: ${n}`].join("\n"):r.join("\n")}function c(e){const t=r(e.runtime.memory?.LangMem),n=r(t?.recall),o=n?.topK??n?.limit;return"number"==typeof o&&Number.isFinite(o)?o:10}
1
+ import{formatError as e,readRecord as r,readWorkspaceId as t,resolveEnabledMemories as n,resolveMemoryProvider as o,resolvePluginNamespace as a}from"./memory-plugins/shared.js";export{resolveEnabledMemories,resolvePluginNamespace}from"./memory-plugins/shared.js";export{createLangMemMaintenanceTarget,createMemoryMaintenanceDaemon,createSkillCandidateMinerTarget}from"./memory-plugins/maintenance.js";export async function runMemoryPlugins(e){const r=n(e.workspace,"write");if(e.providers?.length&&0!==r.length&&!1!==e.request.metadata?.memoryWrite)for(const t of r)await runMemoryPlugin(e,t)}export async function recallMemoryPlugins(e){const r=n(e.workspace,"recall");if(!e.providers?.length||0===r.length)return[];const t=await Promise.all(r.map(async r=>{try{return await async function recallMemoryPlugin(e,r){const t=o(e.providers??[],r.provider);if(!t)return;const n=a(e.workspace,e.agent,e.request,r),s=await t.search({namespace:n,query:buildRecallQuery(e),limit:readRecallLimit(e.workspace)});return{namespace:n,records:s,context:formatRecallContext(e.workspace,s)}}(e,r)}catch{return}}));return t.filter(e=>Boolean(e?.context))}async function runMemoryPlugin(r,t){const n=o(r.providers??[],t.provider);if(!n)return;const s=a(r.workspace,r.agent,r.request,t);r.emit({type:"runtime.memory.plugin.started",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:s});try{const e=await n.propose(function createPluginProposeInput(e,r,t){return{namespace:t,content:[`User input:\n${e.request.input}`,`Agent output:\n${e.result.text}`].join("\n\n"),sourceType:"runtime-run",sourceRef:e.requestId,metadata:{memoryId:r.id,provider:r.provider,profile:r.profile,mode:r.mode,prompts:r.prompts,workspaceRoot:e.workspace.root,agentId:e.agent.id,sessionId:e.sessionId,requestMetadata:e.request.metadata}}}(r,t,s));r.emit({type:"runtime.memory.plugin.completed",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:s,candidateCount:e.length})}catch(o){r.emit({type:"runtime.memory.plugin.failed",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agent.id,memoryId:t.id,provider:n.name,namespace:s,error:e(o)})}}function buildRecallQuery(e){const r=[`task: ${e.request.input}`,`workspace: ${t(e.workspace)}`,`agent: ${e.agent.id}`,"memory_needed: durable preferences, workspace facts, reusable procedures, prior corrections"],n=function readRecentContext(e){return"string"==typeof e?.recentContext?e.recentContext.slice(0,3e3):Array.isArray(e?.recentTurns)?e.recentTurns.filter(e=>"string"==typeof e).slice(-6).join("\n").slice(0,3e3):void 0}(e.request.metadata);return n?[...r,`recent_context: ${n}`].join("\n"):r.join("\n")}function readRecallLimit(e){const t=r(e.runtime.memory?.LangMem),n=r(t?.recall),o=n?.topK??n?.limit;return"number"==typeof o&&Number.isFinite(o)?o:10}function formatRecallContext(e,t){const n=function readRecallContextLimits(e){const t=r(e.runtime.memory?.LangMem),n=r(t?.recall);return{maxContextChars:readPositiveNumber(n?.maxContextChars)??4e3,maxRecordChars:readPositiveNumber(n?.maxRecordChars)??1e3}}(e),o=[];let a=n.maxContextChars;for(const e of t){if(a<=0)break;const r=truncateText(formatRecallRecord(e),Math.min(n.maxRecordChars,a));r.trim()&&(o.push(r),a-=r.length+1)}return o.join("\n")}function formatRecallRecord(e){return`- ${e.summary??e.content}\n ${e.content}`}function readPositiveNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>0?Math.floor(e):void 0}function truncateText(e,r){return e.length>r?`${e.slice(0,Math.max(0,r-13))}\n [truncated]`:e}
@@ -6,8 +6,23 @@ export declare function buildResultRecoveryRequest(input: {
6
6
  request: RuntimeRequest;
7
7
  output: string;
8
8
  events: RuntimeEvent[];
9
+ availableToolIds?: string[];
10
+ policy: unknown;
11
+ }): RuntimeRequest | undefined;
12
+ export declare function buildExecutionContractRecoveryRequest(input: {
13
+ request: RuntimeRequest;
14
+ events: RuntimeEvent[];
9
15
  policy: unknown;
10
16
  }): RuntimeRequest | undefined;
11
17
  export declare function assertNoRawToolCallOutput(output: string, policy: unknown): void;
12
18
  export declare function containsRawToolCallOutput(output: string, policy: unknown): boolean;
19
+ export declare function containsRecoverableResultOutput(output: string, policy: unknown): boolean;
20
+ export declare function assertNoToolExecutionErrorOutput(output: string, policy: unknown): void;
13
21
  export declare function rawToolCallFailureMessage(): string;
22
+ export declare function buildEvidenceSynthesisOutput(input: {
23
+ request: RuntimeRequest;
24
+ output: string;
25
+ events: RuntimeEvent[];
26
+ policy: unknown;
27
+ }): string | undefined;
28
+ export declare function rawToolCallOutputPreview(output: string): string;
@@ -1 +1 @@
1
- export function toolCallRecoveryEnabled(t){return!0===o(t).enabled}export function isRecoverableAdapterError(t,e){const n=o(e);if(!0!==n.enabled)return!1;const a=t instanceof Error?t.message:String(t);return r(n.adapterErrorPatterns,[/XML syntax error|tool.?call.*syntax|malformed.*(?:XML|tool)|Non string tool message content/iu]).some(t=>t.test(a))}export function buildAdapterErrorRecoveryPrompt(e,r,n){const a=r instanceof Error?r.message:String(r),l=o(n).instruction;return t(e,["Stable runtime recovery: the backend failed while parsing a tool call.",`Parser error: ${a}`,"string"==typeof l?l:"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(r){const a=o(r.policy);if(!0!==a.enabled)return;if(e(r.output,a))return t(r.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, then return a final human-readable answer. Do not print XML, JSON, or pseudo tool-call text in the final answer.","","Previous invalid final answer:",r.output]);const l=function(t,e){const o=(r=e.eventRecoveryHints,(Array.isArray(r)?r:[]).flatMap(t=>n(t)&&"string"==typeof t.instruction?[{..."string"==typeof t.toolId?{toolId:t.toolId}:{},..."string"==typeof t.phase?{phase:t.phase}:{},..."string"==typeof t.outputIncludes?{outputIncludes:t.outputIncludes}:{},instruction:t.instruction}]:[]));var r;if(0!==o.length)return t.flatMap(t=>function(t,e){const o="adapter.event"===t.type&&n(t.event)?t.event:void 0;return o?e.filter(t=>function(t,e){return(!e.toolId||t.toolId===e.toolId)&&(!e.phase||t.phase===e.phase)&&(!e.outputIncludes||"string"==typeof t.output&&t.output.includes(e.outputIncludes))}(o,t)).map(t=>({output:"string"==typeof o.output?o.output:"Adapter event matched configured recovery hint.",instruction:t.instruction})):[]}(t,o)).at(-1)}(r.events,a);return l?t(r.request,["Stable runtime recovery: a previous adapter event matched a configured recovery hint.",l.output,l.instruction]):void 0}export function assertNoRawToolCallOutput(t,e){if(containsRawToolCallOutput(t,e))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.")}export function containsRawToolCallOutput(t,r){const n=o(r);return!0===n.enabled&&e(t,n)}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(" ")}function t(t,e){return{...t,input:[t.input,"",...e].join("\n"),metadata:{...t.metadata,stableHarnessRecovery:"tool_call"}}}function e(t,e){return r(e.rawOutputPatterns,[/<\s*(?:tool_call|task)\b[^>]*>/iu,/<\s*\/\s*(?:tool_call|task)\s*>/iu]).some(e=>e.test(t))}function o(t){if(!n(t))return{};const e=n(t.recovery)?t.recovery:{};return n(e.toolCall)?e.toolCall:{}}function r(t,e){const o=(Array.isArray(t)?t:[]).filter(t=>"string"==typeof t&&t.length>0).map(t=>new RegExp(t,"iu"));return o.length>0?o:e}function n(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
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 +1 @@
1
- export function completeRun(e){const s=e.store.getRun(e.requestId);if("cancelled"===s?.state)return t(e,"cancelled");s&&e.store.updateRun(e.requestId,{state:"completed",output:e.result.text,metadata:{...s.metadata,...e.result.metadata},artifacts:[...s.artifacts,...e.result.artifacts??[]],completedAt:(new Date).toISOString()});for(const t of e.result.artifacts??[])e.emit({type:"artifact.created",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,artifact:t});return e.emit({type:"request.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,output:e.result.text}),t(e,"completed")}export function failRun(t){const e=t.error instanceof Error?t.error.message:String(t.error);return t.store.getRun(t.requestId)&&t.store.updateRun(t.requestId,{state:"failed",error:e,completedAt:(new Date).toISOString()}),t.emit({type:"request.failed",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,error:e}),{requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,state:"failed",output:e}}function t(t,e){return{requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,state:e,output:t.result.text,metadata:t.result.metadata,artifacts:t.result.artifacts}}
1
+ export function completeRun(e){const t=e.store.getRun(e.requestId);if("cancelled"===t?.state)return response(e,"cancelled",t.artifacts);if(t){const s=function mergeArtifacts(e,t){const s=new Map;for(const r of[...e??[],...t??[]])s.set(r.id,r);return[...s.values()]}(t.artifacts,e.result.artifacts??[]);e.store.updateRun(e.requestId,{state:"completed",output:e.result.text,metadata:{...t.metadata,...e.result.metadata},artifacts:s,completedAt:(new Date).toISOString()})}for(const t of e.result.artifacts??[])e.emit({type:"runtime.artifact.created",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,artifact:t});return e.emit({type:"runtime.request.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,output:e.result.text}),response(e,"completed",e.store.getRun(e.requestId)?.artifacts)}export function failRun(e){const t=e.error instanceof Error?e.error.message:String(e.error);return e.store.getRun(e.requestId)&&e.store.updateRun(e.requestId,{state:"failed",error:t,completedAt:(new Date).toISOString()}),e.emit({type:"runtime.request.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,error:t}),{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,state:"failed",output:t}}function response(e,t,s){return{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,state:t,output:e.result.text,metadata:e.result.metadata,artifacts:s??e.result.artifacts}}
@@ -1 +1 @@
1
- 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(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}}));if(t&&o.agent.tools.includes(t.toolId)&&o.gateway.get(t.toolId))return t;if(!o.agent.tools.includes(o.toolId))throw new Error(`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`);throw new Error(`Tool is not registered: ${o.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,toolId:t.toolId,args:t.args});o.emit({type:"tool.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:e.toolId});const s=await o.gateway.invoke({toolId:e.toolId,args:e.args,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id}});return o.emit({type:"tool.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:s.toolId,output:s.output}),{text:(a=s.output,"string"==typeof a?a:JSON.stringify(a)),metadata:{toolCall:{toolId:s.toolId}}};var a}
1
+ 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}}));if(t&&o.agent.tools.includes(t.toolId)&&o.gateway.get(t.toolId))return t;if(!o.agent.tools.includes(o.toolId))throw new Error(`Tool ${o.toolId} is not assigned to agent ${o.agent.id}`);throw new Error(`Tool is not registered: ${o.toolId}`)}({gateway:o.gateway,workspace:o.workspace,requestId:o.requestId,sessionId:o.sessionId,agent:o.agent,toolId:t.toolId,args:t.args});o.emit({type:"runtime.tool.direct.started",requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id,toolId:e.toolId});const s=await o.gateway.invoke({toolId:e.toolId,args:e.args,context:{workspaceRoot:o.workspace.root,requestId:o.requestId,sessionId:o.sessionId,agentId:o.agent.id}});return o.emit({type:"runtime.tool.direct.completed",requestId:o.requestId,sessionId:o.sessionId,agentId:o.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}