stable-harness 0.0.2 → 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 (168) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/compat/agent-harness.js +1 -1
  3. package/dist/index.d.ts +29 -6
  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 +14 -10
  13. package/packages/adapter-deepagents/dist/src/adapter.d.ts +1 -0
  14. package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
  15. package/packages/adapter-deepagents/dist/src/index.d.ts +1 -0
  16. package/packages/adapter-deepagents/dist/src/index.js +1 -1
  17. package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
  18. package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
  19. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +39 -0
  20. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -0
  21. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +32 -0
  22. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -0
  23. package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -0
  24. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
  25. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
  26. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
  27. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
  28. package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -0
  29. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
  30. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
  31. package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +16 -0
  32. package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -0
  33. package/packages/adapter-deepagents/dist/src/memory.d.ts +5 -0
  34. package/packages/adapter-deepagents/dist/src/memory.js +1 -0
  35. package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
  36. package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
  37. package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
  38. package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
  39. package/packages/adapter-deepagents/package.json +1 -0
  40. package/packages/adapter-langgraph/dist/src/graph.d.ts +3 -0
  41. package/packages/adapter-langgraph/dist/src/graph.js +1 -0
  42. package/packages/adapter-langgraph/dist/src/index.d.ts +8 -0
  43. package/packages/adapter-langgraph/dist/src/index.js +1 -0
  44. package/packages/adapter-langgraph/dist/src/runtime.d.ts +3 -0
  45. package/packages/adapter-langgraph/dist/src/runtime.js +1 -0
  46. package/packages/adapter-langgraph/dist/src/skill-providers.d.ts +29 -0
  47. package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -0
  48. package/packages/adapter-langgraph/dist/src/types.d.ts +60 -0
  49. package/packages/adapter-langgraph/dist/src/types.js +1 -0
  50. package/packages/adapter-langgraph/package.json +16 -0
  51. package/packages/cli/dist/src/args.d.ts +25 -0
  52. package/packages/cli/dist/src/args.js +1 -0
  53. package/packages/cli/dist/src/cli.js +1 -1
  54. package/packages/cli/dist/src/event-view.d.ts +9 -0
  55. package/packages/cli/dist/src/event-view.js +1 -0
  56. package/packages/cli/dist/src/index.d.ts +3 -0
  57. package/packages/cli/dist/src/index.js +1 -1
  58. package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
  59. package/packages/cli/dist/src/langgraph-env.js +1 -0
  60. package/packages/cli/dist/src/langgraph-official.d.ts +13 -0
  61. package/packages/cli/dist/src/langgraph-official.js +1 -0
  62. package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
  63. package/packages/cli/dist/src/memory/lifecycle.js +1 -0
  64. package/packages/cli/dist/src/memory/providers.d.ts +3 -0
  65. package/packages/cli/dist/src/memory/providers.js +1 -0
  66. package/packages/cli/dist/src/output.d.ts +8 -0
  67. package/packages/cli/dist/src/output.js +1 -0
  68. package/packages/cli/dist/src/server.d.ts +5 -0
  69. package/packages/cli/dist/src/server.js +1 -0
  70. package/packages/cli/package.json +3 -0
  71. package/packages/core/dist/evaluations/index.d.ts +18 -0
  72. package/packages/core/dist/evaluations/index.js +1 -0
  73. package/packages/core/dist/execution-contract.d.ts +1 -0
  74. package/packages/core/dist/execution-contract.js +1 -1
  75. package/packages/core/dist/index.d.ts +8 -4
  76. package/packages/core/dist/index.js +1 -1
  77. package/packages/core/dist/memory-plugins/maintenance.d.ts +42 -0
  78. package/packages/core/dist/memory-plugins/maintenance.js +1 -0
  79. package/packages/core/dist/memory-plugins/shared.d.ts +8 -0
  80. package/packages/core/dist/memory-plugins/shared.js +1 -0
  81. package/packages/core/dist/memory-plugins.d.ts +5 -48
  82. package/packages/core/dist/memory-plugins.js +1 -1
  83. package/packages/core/dist/recovery/tool-call.d.ts +28 -0
  84. package/packages/core/dist/recovery/tool-call.js +1 -0
  85. package/packages/core/dist/runtime/completion.d.ts +17 -0
  86. package/packages/core/dist/runtime/completion.js +1 -0
  87. package/packages/core/dist/runtime/direct-tool-call.d.ts +10 -0
  88. package/packages/core/dist/runtime/direct-tool-call.js +1 -0
  89. package/packages/core/dist/runtime/events.d.ts +204 -0
  90. package/packages/core/dist/runtime/events.js +1 -0
  91. package/packages/core/dist/runtime/memory.d.ts +23 -0
  92. package/packages/core/dist/runtime/memory.js +1 -0
  93. package/packages/core/dist/{artifacts.d.ts → runtime/persistence/artifacts.d.ts} +1 -1
  94. package/packages/core/dist/runtime/persistence/artifacts.js +1 -0
  95. package/packages/core/dist/{inspection.d.ts → runtime/persistence/inspection.d.ts} +1 -1
  96. package/packages/core/dist/runtime/persistence/inspection.js +1 -0
  97. package/packages/core/dist/{queue.d.ts → runtime/persistence/queue.d.ts} +1 -1
  98. package/packages/core/dist/runtime/persistence/queue.js +1 -0
  99. package/packages/core/dist/{stores.d.ts → runtime/persistence/stores.d.ts} +1 -1
  100. package/packages/core/dist/runtime/persistence/stores.js +1 -0
  101. package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
  102. package/packages/core/dist/runtime/progress-narration.js +1 -0
  103. package/packages/core/dist/runtime/tool-gateway.d.ts +40 -0
  104. package/packages/core/dist/runtime/tool-gateway.js +1 -0
  105. package/packages/core/dist/runtime/types.d.ts +168 -0
  106. package/packages/core/dist/runtime/types.js +1 -0
  107. package/packages/core/dist/runtime.d.ts +7 -3
  108. package/packages/core/dist/runtime.js +1 -1
  109. package/packages/core/dist/spec-driven/config.d.ts +4 -0
  110. package/packages/core/dist/spec-driven/config.js +1 -0
  111. package/packages/core/dist/spec-driven/events.d.ts +11 -0
  112. package/packages/core/dist/spec-driven/events.js +1 -0
  113. package/packages/core/dist/spec-driven/index.d.ts +4 -0
  114. package/packages/core/dist/spec-driven/index.js +1 -0
  115. package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
  116. package/packages/core/dist/spec-driven/lifecycle.js +1 -0
  117. package/packages/core/dist/spec-driven/types.d.ts +38 -0
  118. package/packages/core/dist/spec-driven/types.js +1 -0
  119. package/packages/core/dist/trace.d.ts +1 -1
  120. package/packages/core/dist/trace.js +1 -1
  121. package/packages/core/dist/types.d.ts +31 -426
  122. package/packages/core/dist/workflows/index.d.ts +70 -0
  123. package/packages/core/dist/workflows/index.js +1 -0
  124. package/packages/core/dist/workflows/runtime.d.ts +12 -0
  125. package/packages/core/dist/workflows/runtime.js +1 -0
  126. package/packages/core/dist/workspace/types.d.ts +101 -0
  127. package/packages/core/dist/workspace/types.js +1 -0
  128. package/packages/governance/dist/src/skill-candidates.js +1 -1
  129. package/packages/governance/dist/src/types.d.ts +1 -1
  130. package/packages/memory/dist/src/langmem-service.js +1 -1
  131. package/packages/memory/dist/src/maintenance.js +1 -1
  132. package/packages/memory/dist/src/policy.js +1 -1
  133. package/packages/memory/dist/src/provider.js +1 -1
  134. package/packages/memory/dist/src/store.js +1 -1
  135. package/packages/protocols/dist/src/http-server.js +1 -1
  136. package/packages/protocols/dist/src/in-process-client.js +1 -1
  137. package/packages/protocols/dist/src/openai-compatible.js +1 -1
  138. package/packages/protocols/dist/src/openai-payload.d.ts +74 -0
  139. package/packages/protocols/dist/src/openai-payload.js +1 -0
  140. package/packages/protocols/dist/src/openai-stream.d.ts +39 -0
  141. package/packages/protocols/dist/src/openai-stream.js +1 -0
  142. package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
  143. package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
  144. package/packages/tool-gateway/dist/src/in-memory.js +1 -1
  145. package/packages/tool-gateway/dist/src/module-loader.js +1 -1
  146. package/packages/tool-gateway/dist/src/schema-validation.d.ts +3 -0
  147. package/packages/tool-gateway/dist/src/schema-validation.js +1 -0
  148. package/packages/tool-gateway/dist/src/types.d.ts +3 -0
  149. package/packages/tool-gateway/package.json +1 -1
  150. package/packages/workspace-yaml/dist/discovery.d.ts +4 -0
  151. package/packages/workspace-yaml/dist/discovery.js +1 -0
  152. package/packages/workspace-yaml/dist/documents.d.ts +16 -0
  153. package/packages/workspace-yaml/dist/documents.js +1 -0
  154. package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
  155. package/packages/workspace-yaml/dist/evaluations.js +1 -0
  156. package/packages/workspace-yaml/dist/loader.js +1 -1
  157. package/packages/workspace-yaml/dist/workflows.d.ts +16 -0
  158. package/packages/workspace-yaml/dist/workflows.js +1 -0
  159. package/packages/adapter-deepagents/dist/src/builtin-tool-policy.d.ts +0 -18
  160. package/packages/adapter-deepagents/dist/src/builtin-tool-policy.js +0 -1
  161. package/packages/adapter-deepagents/dist/src/messages.js +0 -1
  162. package/packages/adapter-deepagents/dist/src/stream-events.js +0 -1
  163. package/packages/core/dist/artifacts.js +0 -1
  164. package/packages/core/dist/inspection.js +0 -1
  165. package/packages/core/dist/queue.js +0 -1
  166. package/packages/core/dist/stores.js +0 -1
  167. /package/packages/adapter-deepagents/dist/src/{messages.d.ts → internal/messages.d.ts} +0 -0
  168. /package/packages/adapter-deepagents/dist/src/{stream-events.d.ts → internal/stream-events.d.ts} +0 -0
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{createDeepAgentsAdapter as t}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as o,projectEvent as e,projectRuntimeTrace as a}from"@stable-harness/core";import{createModuleToolGateway as s}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as r}from"@stable-harness/workspace-yaml";import{createAgentHarness as n,request as c,stop as l}from"./compat/agent-harness.js";import{formatCompatDelta as i}from"./runtime/compat/presentation.js";const p=function(t){let o,e,a,s=process.cwd(),r=!1,n=!1,c=!1,l=Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5);const i=[];for(let p=0;p<t.length;p+=1)"-w"===t[p]||"--workspace"===t[p]?s=t[++p]??s:"--agent"===t[p]?o=t[++p]:"--tool"===t[p]?e=t[++p]:"--tool-args-json"===t[p]?a=m(t[++p]??"{}"):"--trace"===t[p]?r=!0:"--trace-json"===t[p]?n=!0:"--native"===t[p]?c=!0:"--timeout-ms"===t[p]?l=Number(t[++p]??l):i.push(t[p]);return{workspaceRoot:s,agentId:o,toolId:e,toolArgs:a,trace:r,traceJson:n,native:c,timeoutMs:l,prompt:i.join(" ")||"hello"}}(process.argv.slice(2));function u(t){return"tool.started"===t.type?[`agent:${t.agentId} Running tool ${t.toolId}`]:[]}function m(t){try{return JSON.parse(t)}catch(t){throw new Error(`Invalid --tool-args-json value: ${t instanceof Error?t.message:String(t)}`)}}p.toolId||p.native?await async function(n){const c=setTimeout(()=>{process.stderr.write(`botbotgo native request timed out after ${n.timeoutMs}ms\n`),process.exit(124)},n.timeoutMs);try{const c=await r(n.workspaceRoot),l=await s({tools:c.tools.values()}),i=o({workspace:c,toolGateway:l,adapters:[t()]});n.trace?i.subscribe(t=>{const o=e(t);var a;o&&console.log(`trace:${o.agentId}:${o.label}${a=o.detail,"string"==typeof a?.toolId?`:${a.toolId}`:""}`);for(const o of u(t))console.log(o)}):i.subscribe(t=>{for(const o of u(t))console.log(o)});const p=await i.request({agentId:n.agentId,input:n.prompt,toolCall:n.toolId?{toolId:n.toolId,args:n.toolArgs}:void 0});!function(t,o,e){if(!e.trace&&!e.traceJson)return;const s=t.getRun(o),r=s?a(s):[];e.traceJson&&console.log(JSON.stringify({trace:r}))}(i,p.requestId,n),console.log(p.output),process.exitCode="completed"===p.state?0:1}finally{clearTimeout(c)}}(p):await async function(t){const o=await n(t.workspaceRoot);try{const e=await c(o,{agentId:t.agentId,input:t.prompt,dataListener(t){for(const o of i(t))console.log(o)}});console.log(e.output),process.exitCode="completed"===e.state?0:1}finally{await l(o)}}(p);
2
+ import{createDeepAgentsAdapter as t}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as o,projectEvent as e,projectRuntimeTrace as a}from"@stable-harness/core";import{createModuleToolGateway as r}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";import{createAgentHarness as n,request as i,stop as c}from"./compat/agent-harness.js";import{formatCompatDelta as l}from"./runtime/compat/presentation.js";const p=function parseArgs(t){let o,e,a,r=process.cwd(),s=!1,n=!1,i=!1,c=!1,l=!1,p=Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5);const g=[];for(let m=0;m<t.length;m+=1)"-h"===t[m]||"--help"===t[m]?l=!0:"-w"===t[m]||"--workspace"===t[m]?r=t[++m]??r:"--agent"===t[m]?o=t[++m]:"--tool"===t[m]?e=t[++m]:"--tool-args-json"===t[m]?a=parseJsonArg(t[++m]??"{}"):"--trace"===t[m]?s=!0:"--trace-json"===t[m]?n=!0:"--progress"===t[m]?i=!0:"--native"===t[m]?c=!0:"--timeout-ms"===t[m]?p=Number(t[++m]??p):g.push(t[m]);return{workspaceRoot:r,agentId:o,toolId:e,toolArgs:a,trace:s,traceJson:n,progress:i,native:c,help:l,timeoutMs:p,prompt:g.join(" ")||"hello"}}(process.argv.slice(2));function formatNativeCompatEvent(t){return"runtime.tool.direct.started"===t.type?[`agent:${t.agentId} Running tool ${t.toolId}`]:[]}function parseJsonArg(t){try{return JSON.parse(t)}catch(t){throw new Error(`Invalid --tool-args-json value: ${t instanceof Error?t.message:String(t)}`)}}p.help?console.log(function legacyHelpText(){return["Usage:"," botbotgo -w <workspace> [--agent <id>] [prompt]"," botbotgo --native -w <workspace> [--agent <id>] [prompt]"," botbotgo --native -w <workspace> --tool <id> --tool-args-json <json>","","Options:"," -w, --workspace <path> Workspace root."," --agent <id> Select an agent."," --native Use the native stable runtime path."," --tool <id> Invoke an explicit registered tool on the native path."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Print native progress narration."," --timeout-ms <ms> Request timeout."," -h, --help Show this help."].join("\n")}()):p.toolId||p.native?await async function runNative(n){const i=setTimeout(()=>{process.stderr.write(`botbotgo native request timed out after ${n.timeoutMs}ms\n`),process.exit(124)},n.timeoutMs);try{const i=await s(n.workspaceRoot),c=await r({tools:i.tools.values()}),l=o({workspace:i,toolGateway:c,adapters:[t()],progressNarration:n.progress?{enabled:!0,style:"cli"}:void 0});n.trace?l.subscribe(t=>{const o=e(t);o&&console.log(`trace:${o.agentId}:${o.label}${function formatTraceDetail(t){return"string"==typeof t?.toolId?`:${t.toolId}`:""}(o.detail)}`);for(const o of formatNativeCompatEvent(t))console.log(o)}):l.subscribe(t=>{for(const o of formatNativeCompatEvent(t))console.log(o)}),n.progress&&l.subscribe(t=>{"runtime.progress.narration"===t.type&&console.log(`progress:${t.agentId}:${t.message}`)});const p=await l.request({agentId:n.agentId,input:n.prompt,toolCall:n.toolId?{toolId:n.toolId,args:n.toolArgs}:void 0});!function printNativeTrace(t,o,e){if(!e.trace&&!e.traceJson)return;const r=t.getRun(o),s=r?a(r):[];e.traceJson&&console.log(JSON.stringify({trace:s}))}(l,p.requestId,n),console.log(p.output),process.exitCode="completed"===p.state?0:1}finally{clearTimeout(i)}}(p):await async function runCompat(t){const o=await n(t.workspaceRoot);try{const e=await i(o,{agentId:t.agentId,input:t.prompt,dataListener(t){for(const o of l(t))console.log(o)}});console.log(e.output),process.exitCode="completed"===e.state?0:1}finally{await c(o)}}(p);
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{runAgentHarnessCompatAgent as t}from"../runtime/compat/agent-harness-compat-runner.js";import{loadWorkspace as n}from"../workspace/compile.js";export{loadWorkspace}from"../workspace/compile.js";export async function createAgentHarness(t){return function(t){const n=new Set;return{workspace:t,async request(r){const s=e();return o(t,r,s,n)},async*streamEvents(r){const s=e();yield{type:"event",event:{eventType:"request.created",requestId:s}};const a=function(){const e=[],t=[];let n=!1;return{push(n){const o=t.shift();o?o({value:n,done:!1}):e.push(n)},close(){for(n=!0;t.length;)t.shift()({value:void 0,done:!0})},[Symbol.asyncIterator]:()=>({next:async()=>{const o=e.shift();return o?{value:o,done:!1}:n?{value:void 0,done:!0}:new Promise(e=>t.push(e))}})}}(),c=o(t,r,s,n,e=>{"tool.start"===e.type?a.push({type:"event",event:{payload:{event:"on_tool_start",run_type:"tool",name:e.toolName}}}):a.push({requestId:s,...e,type:"tool-result"})}).finally(()=>a.close());for await(const e of a)yield e;await c},cancel(e){n.add(e)},async stop(){n.clear()}}}(await n(t))}export async function request(e,t){return e.request(t)}export async function cancelRequest(e,t){t.reason,e.cancel(t.requestId)}export async function stop(e){await e.stop()}async function o(e,n,o,r,s){const a=n.agentId??"orchestra",c=e.agents.get(a);if(!c)throw new Error(`Unknown agent: ${a}`);return t({workspace:e,agent:c,request:{...n,dataListener:s??n.dataListener},requestId:o,cancelled:r})}
1
+ import{randomUUID as e}from"node:crypto";import{runAgentHarnessCompatAgent as t}from"../runtime/compat/agent-harness-compat-runner.js";import{loadWorkspace as n}from"../workspace/compile.js";export{loadWorkspace}from"../workspace/compile.js";export async function createAgentHarness(t){return function createRuntime(t){const n=new Set;return{workspace:t,async request(o){const r=e();return runRequest(t,o,r,n)},async*streamEvents(o){const r=e();yield{type:"event",event:{eventType:"request.created",requestId:r}};const s=function createAsyncQueue(){const e=[],t=[];let n=!1;return{push(n){const o=t.shift();o?o({value:n,done:!1}):e.push(n)},close(){for(n=!0;t.length;)t.shift()({value:void 0,done:!0})},[Symbol.asyncIterator]:()=>({next:async()=>{const o=e.shift();return o?{value:o,done:!1}:n?{value:void 0,done:!0}:new Promise(e=>t.push(e))}})}}(),a=runRequest(t,o,r,n,e=>{"agent.tool.start"===e.type?s.push({type:"event",event:{payload:{event:"on_tool_start",run_type:"tool",name:e.toolName}}}):s.push({requestId:r,...e,type:"tool-result"})}).finally(()=>s.close());for await(const e of s)yield e;await a},cancel(e){n.add(e)},async stop(){n.clear()}}}(await n(t))}export async function request(e,t){return e.request(t)}export async function cancelRequest(e,t){t.reason,e.cancel(t.requestId)}export async function stop(e){await e.stop()}async function runRequest(e,n,o,r,s){const a=n.agentId??"orchestra",c=e.agents.get(a);if(!c)throw new Error(`Unknown agent: ${a}`);return t({workspace:e,agent:c,request:{...n,dataListener:s??n.dataListener},requestId:o,cancelled:r})}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,36 @@
1
- import type { RuntimeAdapter, RuntimeRequest, StableHarnessRuntime } from "@stable-harness/core";
2
- export { createDeepAgentsAdapter } from "@stable-harness/adapter-deepagents";
3
- export { createStableHarnessRuntime } from "@stable-harness/core";
1
+ import { createStableHarnessRuntime as createCoreStableHarnessRuntime } from "@stable-harness/core";
2
+ import type { CompiledWorkspace, RuntimeAdapter, RuntimeRequest, RuntimeToolGateway, RuntimeWorkflowAdapter, StableHarnessRuntime, WorkspaceAdapterPolicy } from "@stable-harness/core";
3
+ export { createDeepAgentsAdapter, createDeepAgentsMemoryMaintenanceTarget } from "@stable-harness/adapter-deepagents";
4
+ export { createDeepAgentsMiddlewareSkillProvider, createLangGraphRuntimeAdapter, createLangGraphWorkflowAdapter, createRegistrySkillResolverProvider, } from "@stable-harness/adapter-langgraph";
5
+ export type { LangGraphNodeHandler, LangGraphNodeHandlerInput, LangGraphNodeResolver, LangGraphNodeResolverInput, LangGraphSkillMiddlewareProvider, LangGraphSkillMiddlewareProviderInput, LangGraphSkillResolverProvider, LangGraphWorkflowAdapterOptions, LangGraphWorkflowState, LangGraphWorkflowTraceEntry, } from "@stable-harness/adapter-langgraph";
6
+ export type { LangGraphRegistrySkillOutput } from "@stable-harness/adapter-langgraph";
4
7
  export { createLangMemServiceProvider } from "@stable-harness/memory";
5
- export type { CompiledWorkspace, RuntimeAdapter, RuntimeEvent, RuntimeRequest, RuntimeResponse, RuntimeRunRecord, StableHarnessRuntime, WorkspaceAgent, WorkspaceModel, WorkspaceRuntimePolicy, WorkspaceTool, } from "@stable-harness/core";
8
+ export { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, projectRuntimeTrace, } from "@stable-harness/core";
9
+ export type { CompiledWorkspace, RuntimeAdapter, RuntimeEvent, RuntimeWorkflowAdapter, RuntimeRequest, RuntimeResponse, RuntimeRunRecord, RuntimeTraceEntry, StableHarnessRuntime, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceAgent, WorkspaceModel, WorkspaceRuntimePolicy, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, WorkspaceTool, } from "@stable-harness/core";
6
10
  export { loadWorkspaceFromYaml } from "@stable-harness/workspace-yaml";
7
11
  export { createInMemoryToolGateway, createModuleToolGateway } from "@stable-harness/tool-gateway";
8
12
  export type { ModuleToolDescriptor, ToolGateway, ToolGatewayContext, ToolGatewayInvokeRequest, ToolGatewayInvokeResult, ToolGatewayTool, } from "@stable-harness/tool-gateway";
9
- export declare function createStableRuntime(input: {
13
+ type RuntimeAssemblyInput = {
10
14
  workspaceRoot: string;
11
15
  adapters?: RuntimeAdapter[];
12
- }): Promise<StableHarnessRuntime>;
16
+ adapterFactories?: Record<string, RuntimeAdapterFactory>;
17
+ workflowAdapters?: RuntimeWorkflowAdapter[];
18
+ workflowAdapterFactories?: Record<string, RuntimeWorkflowAdapterFactory>;
19
+ workflowAdapterOptions?: Record<string, unknown>;
20
+ toolGateway?: RuntimeToolGateway;
21
+ };
22
+ type RuntimeAdapterFactory = (input: {
23
+ policy: WorkspaceAdapterPolicy;
24
+ workspace: CompiledWorkspace;
25
+ }) => RuntimeAdapter;
26
+ type RuntimeWorkflowAdapterFactory = (input: {
27
+ name: string;
28
+ workspace: CompiledWorkspace;
29
+ options: unknown;
30
+ }) => RuntimeWorkflowAdapter | undefined;
31
+ type CoreRuntimeInput = Parameters<typeof createCoreStableHarnessRuntime>[0];
32
+ export declare function createStableHarnessRuntime(workspaceRoot: string): Promise<StableHarnessRuntime>;
33
+ export declare function createStableHarnessRuntime(input: RuntimeAssemblyInput): Promise<StableHarnessRuntime>;
34
+ export declare function createStableHarnessRuntime(input: CoreRuntimeInput): StableHarnessRuntime;
35
+ export declare function createStableRuntime(input: RuntimeAssemblyInput): Promise<StableHarnessRuntime>;
13
36
  export declare function requestStableRuntime(runtime: StableHarnessRuntime, request: RuntimeRequest): Promise<import("@stable-harness/core").RuntimeResponse>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createDeepAgentsAdapter as e}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as a}from"@stable-harness/core";import{loadWorkspaceFromYaml as r}from"@stable-harness/workspace-yaml";export{createDeepAgentsAdapter}from"@stable-harness/adapter-deepagents";export{createStableHarnessRuntime}from"@stable-harness/core";export{createLangMemServiceProvider}from"@stable-harness/memory";export{loadWorkspaceFromYaml}from"@stable-harness/workspace-yaml";export{createInMemoryToolGateway,createModuleToolGateway}from"@stable-harness/tool-gateway";export async function createStableRuntime(t){const s=await r(t.workspaceRoot);return a({workspace:s,adapters:t.adapters??[e()]})}export async function requestStableRuntime(e,a){return e.request(a)}
1
+ import{createDeepAgentsAdapter as e}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as r,createLangGraphWorkflowAdapter as a,createRegistrySkillResolverProvider as t}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as o}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";export{createDeepAgentsAdapter,createDeepAgentsMemoryMaintenanceTarget}from"@stable-harness/adapter-deepagents";export{createDeepAgentsMiddlewareSkillProvider,createLangGraphRuntimeAdapter,createLangGraphWorkflowAdapter,createRegistrySkillResolverProvider}from"@stable-harness/adapter-langgraph";export{createLangMemServiceProvider}from"@stable-harness/memory";export{applySpecDrivenPhaseTransition,containsRecoverableResultOutput,createSpecDrivenArtifact,createSpecDrivenArtifactEvent,createSpecDrivenPhaseEvent,createSpecDrivenWorkflowPolicy,createSpecDrivenWorkflowState,projectRuntimeTrace}from"@stable-harness/core";export{loadWorkspaceFromYaml}from"@stable-harness/workspace-yaml";export{createInMemoryToolGateway,createModuleToolGateway}from"@stable-harness/tool-gateway";export function createStableHarnessRuntime(e){return"string"==typeof e?createStableRuntime({workspaceRoot:e}):"workspaceRoot"in e?createStableRuntime(e):o(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),a=e.toolGateway??await n({tools:r.tools.values()});return o({workspace:r,toolGateway:a,adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(a,t){const o={deepagents:({policy:r})=>e(r.config?{config:r.config}:{}),langgraph:({policy:e})=>r({...readLangGraphOptions(e.config),name:e.name}),...t.adapterFactories},n=function runtimeAdapterPolicies(e){const r=e.runtime.adapters?.filter(e=>!1!==e.enabled);return r&&r.length>0?r:[...new Set([...e.agents.values()].map(e=>e.backend))].map(e=>({name:e}))}(a);return n.map(e=>{const r=o[e.name];if(r)return r({policy:e,workspace:a});throw new Error(`Unsupported runtime adapter: ${e.name}`)})}function createWorkflowAdapters(e,r){const t={langgraph:({name:e,options:r})=>a({...readLangGraphOptions(r),name:e}),...r.workflowAdapterFactories};return[...new Set([...e.workflows.values()].map(e=>e.adapter??"").filter(Boolean))].map(a=>{const o=t[a];return o?.({name:a,workspace:e,options:readWorkflowAdapterOptions(r,a)})}).filter(e=>Boolean(e))}function readWorkflowAdapterOptions(e,r){return e.workflowAdapterOptions?.[r]??{}}function readLangGraphOptions(e){return isRecord(e)?{...e,...void 0!==readLangGraphSkillProvider(e)?{skillProvider:readLangGraphSkillProvider(e)}:{}}:{}}function readLangGraphSkillProvider(e){if(!1===e.skillProvider)return!1;const r=function readSkillProviderConfig(e){return isRecord(e.skills)?e.skills:isRecord(e.skillProvider)?e.skillProvider:void 0}(e);if(!r)return;const a=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(a))return!1;if("registry-resolver"!==a)throw new Error(`Unsupported LangGraph skill provider: ${a}`);return t({..."boolean"==typeof r.includeContent?{includeContent:r.includeContent}:{},..."number"==typeof r.maxBytes&&Number.isFinite(r.maxBytes)?{maxBytes:r.maxBytes}:{}})}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- import{generateWithModel as t}from"../model/ollama.js";import{parseToolCall as e}from"./json.js";import{buildAgentPrompt as n,buildRoutingPrompt as r,buildRoutingRepairPrompt as a,buildToolSelectionPrompt as o}from"./prompts.js";import{loadAgentTools as s}from"./tool-registry.js";export async function runAgentHarnessCompatAgent(o){const m=await async function(e){if(0===e.agent.subagentRefs.length)return[e.agent];const n=l(e.workspace,e.agent),o=e.request.input??"",s=await t(n,r(e.workspace,e.agent,o)),u=await async function(e,n,r,o){let s=o,u=I(s);for(let o=0;o<3&&!i(e,r,u);o+=1)s=await t(n,a({workspace:e.workspace,agent:e.agent,userInput:r,previousResponse:s,issue:c(e,r,u)})),u=I(s);return i(e,r,u)?u:function(t,e){if(d(t,e)<=1)return[];const n=String(t.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(t=>t.trim());for(const r of n){if(!/delegation tree|路由|委托/u.test(r)||!/must|必须|include|包含/u.test(r))continue;const n=t.subagentRefs.filter(t=>{return new RegExp(`\\b${e=t,e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}\\b`,"u").test(r);var e});if(n.length>=d(t,e))return n}return[]}(e.agent,r)}(e,n,o,s);return u.map(t=>e.workspace.agents.get(t)).filter(t=>Boolean(t))}(o),y=m[0]??o.agent,b=[];!function(t,e,n,r){if(e.length>1&&t.request.dataListener?.({type:"delegation.plan",requestId:t.requestId,agentId:t.agent.id,agentIds:e.map(t=>t.id)}),n.id===t.agent.id)return;const a={agentId:t.agent.id,toolName:"task",output:n.id};r.push(a),t.request.dataListener?.({type:"tool.result",requestId:t.requestId,...a}),t.request.dataListener?.({type:"delegation.start",requestId:t.requestId,agentId:n.id})}(o,m,y,b);const N=l(o.workspace,y),R=await s(o.workspace,y.toolRefs),$=[],j=new Set;if(y.toolRefs.length>0){const t=await u(o,y,R,$);t&&R.has(t.name)&&(f(o,b,$,await g(o,y,R.get(t.name),t.arguments)),j.add(k(t.name,t.arguments)),await w(o,y,R,j,b,$))}for(let r=0;r<4;r+=1){if(o.cancelled.has(o.requestId))return q(o.requestId,$.join("\n\n"),b,y.id);const r=n({workspace:o.workspace,agent:y,model:N,userInput:o.request.input??"",tools:R,observations:$}),a=await t(N,r),s=e(a);if(!s&&p(b))return q(o.requestId,a,b,y.id);const i=s??await u(o,y,R,$);if(!i||!R.has(i.name)){const t=await u(o,y,h(R,j),$);if(t&&R.has(t.name)){f(o,b,$,await g(o,y,R.get(t.name),t.arguments)),j.add(k(t.name,t.arguments));continue}return q(o.requestId,a,b,y.id)}const d=k(i.name,i.arguments);if(j.has(d)){const t=await u(o,y,h(R,j),$);if(t&&R.has(t.name)){f(o,b,$,await g(o,y,R.get(t.name),t.arguments)),j.add(k(t.name,t.arguments));continue}return q(o.requestId,$.join("\n\n"),b,y.id)}f(o,b,$,await g(o,y,R.get(i.name),i.arguments)),j.add(d),await w(o,y,R,j,b,$)}return q(o.requestId,$.join("\n\n"),b,y.id)}async function u(n,r,a,s){if(n.cancelled.has(n.requestId)||0===a.size)return;const u=l(n.workspace,r),i=await t(u,o({agent:r,userInput:n.request.input??"",tools:a,observations:s}));return e(i)}function i(t,e,n){return n.filter(e=>t.workspace.agents.has(e)).length>=d(t.agent,e)}function d(t,e){const n=e.split(/\r?\n/u).filter(t=>/^\s*\d+[.)、]/u.test(t)).length;return Math.max(1,Math.min(t.subagentRefs.length,n||1))}function c(t,e,n){const r=d(t.agent,e);return 0===n.length?"No valid available subagent id was returned.":`Only ${n.length} valid subagent ids were returned; at least ${r} are required for this multi-part request.`}function l(t,e){const n=e.modelRef??"default",r=t.models.get(n)??t.models.get("default");if(!r)throw new Error(`No model configured for agent ${e.id}`);return r}async function g(t,e,n,r){return t.request.dataListener?.({type:"tool.start",requestId:t.requestId,agentId:e.id,toolName:n.name}),await new Promise(t=>setImmediate(t)),t.cancelled.has(t.requestId)?{agentId:e.id,toolName:n.name,output:"cancelled before tool invocation",isError:!0}:async function(t,e,n,r){const a=process.cwd();try{process.chdir(t.workspace.workspaceRoot);const a=await n.invoke(r);return{agentId:e.id,toolName:n.name,output:y(a)}}catch(t){return{agentId:e.id,toolName:n.name,output:y(t),isError:!0}}finally{process.chdir(a)}}(t,e,n,r)}function f(t,e,n,r){e.push(r),t.request.dataListener?.({type:"tool.result",requestId:t.requestId,...r}),n.push(`Tool ${r.toolName} returned:\n${r.output}`)}function m(t){return"task"===t}function p(t){return t.some(t=>!t.isError&&!m(t.toolName))}async function w(t,e,n,r,a,o){if(p(a))return;const s=await u(t,e,h(n,r),o);s&&n.has(s.name)&&(f(t,a,o,await g(t,e,n.get(s.name),s.arguments)),r.add(k(s.name,s.arguments)))}function h(t,e){return new Map([...t].filter(([t])=>!m(t)&&!function(t,e){return[...e].some(e=>e.startsWith(`${t}:`))}(t,e)))}function I(t){try{const e=function(t){try{return JSON.parse(t)}catch{const e=t.indexOf("{"),n=t.lastIndexOf("}");if(e>=0&&n>e)return JSON.parse(t.slice(e,n+1));throw new Error("No JSON object found")}}(t),n=function(t){for(const e of["agentIds","routing","agents","delegations","plan"]){const n=t[e],r=Array.isArray(n)?n.filter(t=>"string"==typeof t):[];if(r.length>0)return[...new Set(r)]}return[]}(e);if(n.length>0)return n;const r=function(t){for(const e of["agentId","agent","owner","route"]){const n=t[e];if("string"==typeof n&&n.trim().length>0)return n}}(e);return r?[r]:[]}catch{return[]}}function q(t,e,n,r){return{requestId:t,state:"completed",output:e,metadata:{executedToolResults:n,routedAgentId:r}}}function y(t){return t instanceof Error?t.stack??t.message:"string"==typeof t?t:JSON.stringify(t)}function k(t,e){return`${t}:${JSON.stringify(e)}`}
1
+ import{generateWithModel as e}from"../model/ollama.js";import{parseToolCall as t}from"./json.js";import{buildAgentPrompt as n,buildRoutingPrompt as o,buildRoutingRepairPrompt as r,buildToolSelectionPrompt as a}from"./prompts.js";import{loadAgentTools as s}from"./tool-registry.js";export async function runAgentHarnessCompatAgent(a){const i=await async function resolveAgents(t){if(0===t.agent.subagentRefs.length)return[t.agent];const n=resolveModel(t.workspace,t.agent),a=t.request.input??"",s=await e(n,o(t.workspace,t.agent,a)),i=await async function repairRouting(t,n,o,a){let s=a,i=parseRouting(s);for(let a=0;a<3&&!hasEnoughRouting(t,o,i);a+=1)s=await e(n,r({workspace:t.workspace,agent:t.agent,userInput:o,previousResponse:s,issue:routingIssue(t,o,i)})),i=parseRouting(s);return hasEnoughRouting(t,o,i)?i:function readConfiguredRoutingTree(e,t){if(requiredRoutingCount(e,t)<=1)return[];const n=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim());for(const o of n){if(!/delegation tree|路由|委托/u.test(o)||!/must|必须|include|包含/u.test(o))continue;const n=e.subagentRefs.filter(e=>{return new RegExp(`\\b${t=e,t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}\\b`,"u").test(o);var t});if(n.length>=requiredRoutingCount(e,t))return n}return[]}(t.agent,o)}(t,n,a,s);return i.map(e=>t.workspace.agents.get(e)).filter(e=>Boolean(e))}(a),u=i[0]??a.agent,l=[];!function emitRoutingEvents(e,t,n,o){if(t.length>1&&e.request.dataListener?.({type:"delegation.plan",requestId:e.requestId,agentId:e.agent.id,agentIds:t.map(e=>e.id)}),n.id===e.agent.id)return;const r={agentId:e.agent.id,toolName:"task",output:n.id};o.push(r),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...r}),e.request.dataListener?.({type:"delegation.start",requestId:e.requestId,agentId:n.id})}(a,i,u,l);const d=resolveModel(a.workspace,u),c=await s(a.workspace,u.toolRefs),g=[],f=new Set;if(u.toolRefs.length>0){const e=await forceToolSelection(a,u,c,g);e&&c.has(e.name)&&(recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments)),await ensureEvidenceAfterPlanning(a,u,c,f,l,g))}for(let o=0;o<4;o+=1){if(a.cancelled.has(a.requestId))return completed(a.requestId,g.join("\n\n"),l,u.id);const o=n({workspace:a.workspace,agent:u,model:d,userInput:a.request.input??"",tools:c,observations:g}),r=await e(d,o),s=t(r);if(!s&&hasEvidence(l))return completed(a.requestId,r,l,u.id);const i=s??await forceToolSelection(a,u,c,g);if(!i||!c.has(i.name)){const e=await forceToolSelection(a,u,availableNextTools(c,f),g);if(e&&c.has(e.name)){recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(a.requestId,r,l,u.id)}const p=toolKey(i.name,i.arguments);if(f.has(p)){const e=await forceToolSelection(a,u,availableNextTools(c,f),g);if(e&&c.has(e.name)){recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(a.requestId,g.join("\n\n"),l,u.id)}recordToolResult(a,l,g,await invokeToolWithStart(a,u,c.get(i.name),i.arguments)),f.add(p),await ensureEvidenceAfterPlanning(a,u,c,f,l,g)}return completed(a.requestId,g.join("\n\n"),l,u.id)}async function forceToolSelection(n,o,r,s){if(n.cancelled.has(n.requestId)||0===r.size)return;const i=resolveModel(n.workspace,o),u=await e(i,a({agent:o,userInput:n.request.input??"",tools:r,observations:s}));return t(u)}function hasEnoughRouting(e,t,n){return n.filter(t=>e.workspace.agents.has(t)).length>=requiredRoutingCount(e.agent,t)}function requiredRoutingCount(e,t){const n=t.split(/\r?\n/u).filter(e=>/^\s*\d+[.)、]/u.test(e)).length;return Math.max(1,Math.min(e.subagentRefs.length,n||1))}function routingIssue(e,t,n){const o=requiredRoutingCount(e.agent,t);return 0===n.length?"No valid available subagent id was returned.":`Only ${n.length} valid subagent ids were returned; at least ${o} are required for this multi-part request.`}function resolveModel(e,t){const n=t.modelRef??"default",o=e.models.get(n)??e.models.get("default");if(!o)throw new Error(`No model configured for agent ${t.id}`);return o}async function invokeToolWithStart(e,t,n,o){return e.request.dataListener?.({type:"agent.tool.start",requestId:e.requestId,agentId:t.id,toolName:n.name}),await new Promise(e=>setImmediate(e)),e.cancelled.has(e.requestId)?{agentId:t.id,toolName:n.name,output:"cancelled before tool invocation",isError:!0}:async function invokeTool(e,t,n,o){const r=process.cwd();try{process.chdir(e.workspace.workspaceRoot);const r=await n.invoke(o);return{agentId:t.id,toolName:n.name,output:stringifyOutput(r)}}catch(e){return{agentId:t.id,toolName:n.name,output:stringifyOutput(e),isError:!0}}finally{process.chdir(r)}}(e,t,n,o)}function recordToolResult(e,t,n,o){t.push(o),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...o}),n.push(`Tool ${o.toolName} returned:\n${o.output}`)}function isPlanningTool(e){return"task"===e}function hasEvidence(e){return e.some(e=>!e.isError&&!isPlanningTool(e.toolName))}async function ensureEvidenceAfterPlanning(e,t,n,o,r,a){if(hasEvidence(r))return;const s=await forceToolSelection(e,t,availableNextTools(n,o),a);s&&n.has(s.name)&&(recordToolResult(e,r,a,await invokeToolWithStart(e,t,n.get(s.name),s.arguments)),o.add(toolKey(s.name,s.arguments)))}function availableNextTools(e,t){return new Map([...e].filter(([e])=>!isPlanningTool(e)&&!function hasExecutedTool(e,t){return[...t].some(t=>t.startsWith(`${e}:`))}(e,t)))}function parseRouting(e){try{const t=function parseJsonObject(e){try{return JSON.parse(e)}catch{const t=e.indexOf("{"),n=e.lastIndexOf("}");if(t>=0&&n>t)return JSON.parse(e.slice(t,n+1));throw new Error("No JSON object found")}}(e),n=function readRoutingIds(e){for(const t of["agentIds","routing","agents","delegations","plan"]){const n=e[t],o=Array.isArray(n)?n.filter(e=>"string"==typeof e):[];if(o.length>0)return[...new Set(o)]}return[]}(t);if(n.length>0)return n;const o=function readRoutingId(e){for(const t of["agentId","agent","owner","route"]){const n=e[t];if("string"==typeof n&&n.trim().length>0)return n}}(t);return o?[o]:[]}catch{return[]}}function completed(e,t,n,o){return{requestId:e,state:"completed",output:t,metadata:{executedToolResults:n,routedAgentId:o}}}function stringifyOutput(e){return e instanceof Error?e.stack??e.message:"string"==typeof e?e:JSON.stringify(e)}function toolKey(e,t){return`${e}:${JSON.stringify(t)}`}
@@ -1 +1 @@
1
- export function parseToolCall(r){return function(r){if("string"!=typeof r)return[];const e=[];for(const o of function(t){return[t.trim(),extractJsonObject(t)].filter(t=>Boolean(t))}(r)){const r=n(t(o));r&&e.push(r)}for(const o of function(t){return t.match(/\{[^{}]*"name"\s*:\s*"[^"]+"[\s\S]*?\}\s*\}/gu)??[]}(r)){const r=n(t(o));r&&e.push(r)}return function(t){const n=new Set;return t.filter(t=>{const r=`${t.name}:${JSON.stringify(t.arguments)}`;return!n.has(r)&&(n.add(r),!0)})}(e)}(r)[0]}export function extractJsonObject(t){const n=t.indexOf("{"),r=t.lastIndexOf("}");return n>=0&&r>n?t.slice(n,r+1):void 0}function t(t){try{return JSON.parse(t)}catch{return}}function n(t){if("object"!=typeof t||null===t)return;const n=t;if("string"!=typeof n.name)return;const r="object"==typeof n.arguments&&null!==n.arguments?n.arguments:{};return{name:n.name,arguments:r}}
1
+ export function parseToolCall(n){return function parseToolCalls(n){if("string"!=typeof n)return[];const t=[];for(const e of function jsonCandidates(n){return[n.trim(),extractJsonObject(n)].filter(n=>Boolean(n))}(n)){const n=normalizeToolCall(safeParse(e));n&&t.push(n)}for(const e of function objectCandidates(n){return n.match(/\{[^{}]*"name"\s*:\s*"[^"]+"[\s\S]*?\}\s*\}/gu)??[]}(n)){const n=normalizeToolCall(safeParse(e));n&&t.push(n)}return function uniqueToolCalls(n){const t=new Set;return n.filter(n=>{const e=`${n.name}:${JSON.stringify(n.arguments)}`;return!t.has(e)&&(t.add(e),!0)})}(t)}(n)[0]}export function extractJsonObject(n){const t=n.indexOf("{"),e=n.lastIndexOf("}");return t>=0&&e>t?n.slice(t,e+1):void 0}function safeParse(n){try{return JSON.parse(n)}catch{return}}function normalizeToolCall(n){if("object"!=typeof n||null===n)return;const t=n;if("string"!=typeof t.name)return;const e="object"==typeof t.arguments&&null!==t.arguments?t.arguments:{};return{name:t.name,arguments:e}}
@@ -1 +1 @@
1
- import{readPlanTodos as t}from"@stable-harness/core";export function formatCompatDelta(o){return"delegation.start"===o.type?[`agent:${o.agentId} Starting delegated execution.`]:"delegation.plan"===o.type?[`Planned delegation tree: ${e(o).join(" -> ")}`]:"tool.start"===o.type?[`agent:${o.agentId} Running tool ${a=o.toolName,"write_todos"===a?"Call Write Todos.":String(a)}`]:"tool.result"===o.type?function(e){if("task"===e.toolName)return[`agent:${e.agentId} Delegating to ${e.output}.`];if("write_todos"===e.toolName){const o=t(e.output);return o.length>0?function(t,e){const o=e.filter(t=>"completed"===t.status).length,a=e.filter(t=>"in_progress"===t.status).length,r=e.filter(t=>"pending"===t.status).length,s=[`agent:${t} TODO Burn Down | ${o}/${e.length} done | ${a} active | ${r} pending`];for(const t of e)s.push(` [${n(t.status)}] ${t.content}`);for(const n of e.filter(t=>"completed"===t.status||"failed"===t.status))s.push(`agent:${t} TODO ${n.status}: ${n.content}`);return s}(String(e.agentId),o):[]}return[]}(o):[];var a}function e(t){return Array.isArray(t.agentIds)?t.agentIds.map(String):[]}function n(t){return"completed"===t?"x":"in_progress"===t?"~":"failed"===t?"!":" "}
1
+ import{readPlanTodos as t}from"@stable-harness/core";export function formatCompatDelta(e){return"delegation.start"===e.type?[`agent:${e.agentId} Starting delegated execution.`]:"delegation.plan"===e.type?[`Planned delegation tree: ${readAgentIds(e).join(" -> ")}`]:"agent.tool.start"===e.type?[`agent:${e.agentId} Running tool ${n=e.toolName,"write_todos"===n?"Call Write Todos.":String(n)}`]:"agent.tool.result"===e.type?function formatToolResult(e){if("task"===e.toolName)return[`agent:${e.agentId} Delegating to ${e.output}.`];if("write_todos"===e.toolName){const n=t(e.output);return n.length>0?function formatTodoBurnDown(t,e){const n=e.filter(t=>"completed"===t.status).length,o=e.filter(t=>"in_progress"===t.status).length,r=e.filter(t=>"pending"===t.status).length,a=[`agent:${t} TODO Burn Down | ${n}/${e.length} done | ${o} active | ${r} pending`];for(const t of e)a.push(` [${todoMarker(t.status)}] ${t.content}`);for(const n of e.filter(t=>"completed"===t.status||"failed"===t.status))a.push(`agent:${t} TODO ${n.status}: ${n.content}`);return a}(String(e.agentId),n):[]}return[]}(e):[];var n}function readAgentIds(t){return Array.isArray(t.agentIds)?t.agentIds.map(String):[]}function todoMarker(t){return"completed"===t?"x":"in_progress"===t?"~":"failed"===t?"!":" "}
@@ -1 +1 @@
1
- export function buildAgentPrompt(o){const t=[...o.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[o.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.",e(o.agent,o.tools),'If a tool is needed, return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Before returning a final answer, re-read the tool policy excerpts and run any required follow-up evidence tool for this request.","If enough evidence is available, return the final answer only.","Available tools:",JSON.stringify(t,null,2),o.observations.length?`Tool observations:\n${o.observations.join("\n\n")}`:"",`User request:\n${o.userInput}`].filter(Boolean).join("\n")}export function buildRoutingPrompt(e,t,n){const r=t.subagentRefs.map(o=>e.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return["Choose the best subagent plan for this request.",o(t,r.map(e=>e.id)),t.deepAgentConfig.systemPrompt??"","Routing policy excerpts are authoritative workspace config.","This is a routing-only step. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","If a routing policy says a request must include one or more available subagents, include those subagents.",'If one owner is enough, return exactly JSON: {"agentId":"one_id","reason":"short reason"}.','If the request contains multiple specialist-owned tasks, return exactly JSON: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"For numbered or multi-part requests, check every item and include an owner for each distinct specialist-owned task.","Do not omit later tasks after selecting earlier owners.","Return only ids from Available subagents.","Available subagents:",JSON.stringify(r,null,2),`User request:\n${n}`].filter(Boolean).join("\n")}export function buildRoutingRepairPrompt(e){const t=e.agent.subagentRefs.map(o=>e.workspace.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return[e.issue??"Your previous routing response did not contain a valid complete routing plan.",o(e.agent,t.map(e=>e.id)),"This is a routing-only repair. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","Return JSON only.",'For one owner: {"agentId":"one_id","reason":"short reason"}.','For multiple owners: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"Use only ids from Available subagents.","Available subagents:",JSON.stringify(t,null,2),`Previous response:\n${e.previousResponse}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildToolSelectionPrompt(o){const t=[...o.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[`Agent: ${o.agent.id}`,o.agent.description?`Responsibility: ${o.agent.description}`:"",e(o.agent,o.tools),"Choose the single best evidence tool from the available tools.",'Return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Infer arguments from the user request, tool description, and tool policy excerpts.","If the user explicitly names an available tool, choose that exact tool.","Do not use empty arguments when the tool policy or user request provides a concrete schema, URL, path, question, ticker, or command.","Prefer the most specific evidence tool for the requested artifact or operation before choosing a general search tool.","Available tools:",JSON.stringify(t,null,2),o.observations?.length?`Tool observations already gathered:\n${o.observations.join("\n\n")}`:"",`User request:\n${o.userInput}`].filter(Boolean).join("\n")}function e(e,o){const t=String(e.deepAgentConfig.systemPrompt??""),n=[...o.keys()].filter(e=>"write_todos"!==e&&"read_todos"!==e),r=t.split(/\r?\n/u).map(e=>e.trim()).filter(e=>n.some(o=>e.includes(o))).slice(0,12);return r.length>0?`Tool policy excerpts:\n${r.join("\n")}`:""}function o(e,o){const n=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim()).filter(e=>function(e,o){const t=e.toLowerCase();return o.some(o=>e.includes(o))&&/\b(route|routing|delegate|delegation|owner|specialist|include|tree|task)\b/u.test(t)}(e,o)),r=n.map(e=>({line:e,score:t(e,o)})).sort((e,o)=>o.score-e.score).map(e=>e.line).slice(0,16);return r.length>0?`Routing policy excerpts:\n${r.join("\n")}`:""}function t(e,o){const t=e.toLowerCase();return 10*o.filter(o=>e.includes(o)).length+(t.match(/\b(must|include|order|tree|requires|required|exactly|only)\b/gu)??[]).length}
1
+ export function buildAgentPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[e.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.",toolPolicyExcerpt(e.agent,e.tools),'If a tool is needed, return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Before returning a final answer, re-read the tool policy excerpts and run any required follow-up evidence tool for this request.","If enough evidence is available, return the final answer only.","Available tools:",JSON.stringify(o,null,2),e.observations.length?`Tool observations:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildRoutingPrompt(e,o,t){const n=o.subagentRefs.map(o=>e.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return["Choose the best subagent plan for this request.",routingPolicyExcerpt(o,n.map(e=>e.id)),o.deepAgentConfig.systemPrompt??"","Routing policy excerpts are authoritative workspace config.","This is a routing-only step. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","If a routing policy says a request must include one or more available subagents, include those subagents.",'If one owner is enough, return exactly JSON: {"agentId":"one_id","reason":"short reason"}.','If the request contains multiple specialist-owned tasks, return exactly JSON: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"For numbered or multi-part requests, check every item and include an owner for each distinct specialist-owned task.","Do not omit later tasks after selecting earlier owners.","Return only ids from Available subagents.","Available subagents:",JSON.stringify(n,null,2),`User request:\n${t}`].filter(Boolean).join("\n")}export function buildRoutingRepairPrompt(e){const o=e.agent.subagentRefs.map(o=>e.workspace.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return[e.issue??"Your previous routing response did not contain a valid complete routing plan.",routingPolicyExcerpt(e.agent,o.map(e=>e.id)),"This is a routing-only repair. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","Return JSON only.",'For one owner: {"agentId":"one_id","reason":"short reason"}.','For multiple owners: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"Use only ids from Available subagents.","Available subagents:",JSON.stringify(o,null,2),`Previous response:\n${e.previousResponse}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildToolSelectionPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[`Agent: ${e.agent.id}`,e.agent.description?`Responsibility: ${e.agent.description}`:"",toolPolicyExcerpt(e.agent,e.tools),"Choose the single best evidence tool from the available tools.",'Return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Infer arguments from the user request, tool description, and tool policy excerpts.","If the user explicitly names an available tool, choose that exact tool.","Do not use empty arguments when the tool policy or user request provides a concrete schema, URL, path, question, ticker, or command.","Prefer the most specific evidence tool for the requested artifact or operation before choosing a general search tool.","Available tools:",JSON.stringify(o,null,2),e.observations?.length?`Tool observations already gathered:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}function toolPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??""),n=[...o.keys()].filter(e=>"write_todos"!==e&&"read_todos"!==e),r=t.split(/\r?\n/u).map(e=>e.trim()).filter(e=>n.some(o=>e.includes(o))).slice(0,12);return r.length>0?`Tool policy excerpts:\n${r.join("\n")}`:""}function routingPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim()).filter(e=>function isRoutingPolicyLine(e,o){const t=e.toLowerCase();return o.some(o=>e.includes(o))&&/\b(route|routing|delegate|delegation|owner|specialist|include|tree|task)\b/u.test(t)}(e,o)),n=t.map(e=>({line:e,score:routingPolicyScore(e,o)})).sort((e,o)=>o.score-e.score).map(e=>e.line).slice(0,16);return n.length>0?`Routing policy excerpts:\n${n.join("\n")}`:""}function routingPolicyScore(e,o){const t=e.toLowerCase();return 10*o.filter(o=>e.includes(o)).length+(t.match(/\b(must|include|order|tree|requires|required|exactly|only)\b/gu)??[]).length}
@@ -1 +1 @@
1
- export async function generateWithModel(n,i){if("ollama"!==n.provider&&"openai-compatible"!==n.provider)throw new Error(`Unsupported model provider: ${n.provider}`);const o=function(t){const e=t.trim()||"http://127.0.0.1:11434/";return e.endsWith("/")?e:`${e}/`}(String(n.init.baseUrl??n.init.baseURL??"")),a="ollama"===n.provider?`${o}api/generate`:`${o}v1/chat/completions`,s=Number(n.init.timeout??12e4),c=Number(n.init.retries??2)+1;let u;for(let o=1;o<=c;o+=1)try{return await t(n,a,i,s)}catch(t){if(u=t,o===c||!e(t))throw t;await r(500*o)}throw u instanceof Error?u:new Error(String(u))}async function t(t,e,r,o){const a=new AbortController,s=setTimeout(()=>a.abort(),o),c=await function(t,e,r,o){return fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify("ollama"===t.provider?n(t,r):i(t,r)),signal:o})}(t,e,r,a.signal).finally(()=>clearTimeout(s));if(!c.ok)throw new Error(`Model request failed with HTTP ${c.status}: ${await c.text()}`);const u=await c.json();return function(t,e){if("ollama"===t)return String(e.response??"");const r=(Array.isArray(e.choices)?e.choices:[])[0];return String(r?.message?.content??"")}(t.provider,u)}function e(t){if(t instanceof DOMException&&"AbortError"===t.name)return!1;const e=t instanceof Error?t.message:String(t);return/fetch failed|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket|network/i.test(e)}function r(t){return new Promise(e=>setTimeout(e,t))}function n(t,e){return{model:t.model,prompt:e,stream:!1,think:t.init.think??!1,options:{num_ctx:t.init.numCtx,num_predict:t.init.numPredict,temperature:t.init.temperature}}}function i(t,e){return{model:t.model,messages:[{role:"user",content:e}],temperature:t.init.temperature??0}}
1
+ export async function generateWithModel(e,t){if("ollama"!==e.provider&&"openai-compatible"!==e.provider)throw new Error(`Unsupported model provider: ${e.provider}`);const r=function normalizeBaseUrl(e){const t=e.trim()||"http://127.0.0.1:11434/";return t.endsWith("/")?t:`${t}/`}(String(e.init.baseUrl??e.init.baseURL??"")),n="ollama"===e.provider?`${r}api/generate`:`${function normalizeOpenAiBaseUrl(e){return e.endsWith("/v1/")?e:`${e}v1/`}(r)}chat/completions`,o=Number(e.init.timeout??12e4),i=Number(e.init.retries??2)+1;let a;for(let r=1;r<=i;r+=1)try{return await requestModel(e,n,t,o)}catch(e){if(a=e,r===i||!isRetryableModelError(e))throw e;await delay(500*r)}throw a instanceof Error?a:new Error(String(a))}async function requestModel(e,t,r,n){const o=new AbortController,i=setTimeout(()=>o.abort(),n),a=await function fetchModel(e,t,r,n){return fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify("ollama"===e.provider?ollamaBody(e,r):openAiBody(e,r)),signal:n})}(e,t,r,o.signal).finally(()=>clearTimeout(i));if(!a.ok)throw new Error(`Model request failed with HTTP ${a.status}: ${await a.text()}`);const s=await a.json();return function parseModelResponse(e,t){if("ollama"===e)return String(t.response??"");const r=(Array.isArray(t.choices)?t.choices:[])[0];return String(r?.message?.content??"")}(e.provider,s)}function isRetryableModelError(e){if(e instanceof DOMException&&"AbortError"===e.name)return!1;const t=e instanceof Error?e.message:String(e);return/fetch failed|ECONNRESET|ETIMEDOUT|EAI_AGAIN|socket|network/i.test(t)}function delay(e){return new Promise(t=>setTimeout(t,e))}function ollamaBody(e,t){return{model:e.model,prompt:t,stream:!1,think:e.init.think??!1,options:{num_ctx:e.init.numCtx,num_predict:e.init.numPredict,temperature:e.init.temperature}}}function openAiBody(e,t){return{model:e.model,messages:[{role:"user",content:t}],temperature:e.init.temperature??0}}
@@ -1 +1 @@
1
- import{readFileSync as r,statSync as t}from"node:fs";import*as o from"node:path";import{parse as e}from"yaml";export function validateSkillMetadata(i){const a=t(i).isDirectory()?o.join(i,"SKILL.md"):i,s=r(a,"utf8").match(/^---\n([\s\S]*?)\n---/u);if(!s)throw new Error(`${a} is missing YAML front matter`);const m=e(s[1]),l=n(m.name,"name",a),f=n(m.description,"description",a),d=(c=m["allowed-tools"],Array.isArray(c)?c.filter(r=>"string"==typeof r&&r.trim().length>0):[]);var c;if(0===d.length)throw new Error(`${o.basename(i)} must declare allowed-tools`);return{name:l,description:f,allowedTools:d,path:a}}function n(r,t,o){if("string"!=typeof r||!r.trim())throw new Error(`${o} front matter requires ${t}`);return r.trim()}
1
+ import{readFileSync as r,statSync as t}from"node:fs";import*as e from"node:path";import{parse as n}from"yaml";export function validateSkillMetadata(o){const i=t(o).isDirectory()?e.join(o,"SKILL.md"):o,a=r(i,"utf8").match(/^---\n([\s\S]*?)\n---/u);if(!a)throw new Error(`${i} is missing YAML front matter`);const s=n(a[1]),m=readString(s.name,"name",i),l=readString(s.description,"description",i),d=function readStringList(r){return Array.isArray(r)?r.filter(r=>"string"==typeof r&&r.trim().length>0):[]}(s["allowed-tools"]);if(0===d.length)throw new Error(`${e.basename(o)} must declare allowed-tools`);return{name:m,description:l,allowedTools:d,path:i}}function readString(r,t,e){if("string"!=typeof r||!r.trim())throw new Error(`${e} front matter requires ${t}`);return r.trim()}
@@ -1 +1 @@
1
- import{readdir as e,readFile as t}from"node:fs/promises";import*as s from"node:path";import{parseAllDocuments as n}from"yaml";import{validateSkillMetadata as r}from"../runtime/skills/skill-metadata.js";export async function loadWorkspace(u){const y=s.resolve(u),g=await async function(e){const s=await o(e),r=[];for(const e of s){const s=await t(e,"utf8");for(const e of n(s)){const t=e.toJSON();t?.kind&&r.push({...t,metadata:t.metadata??{},spec:t.spec??{}})}}return r}(s.join(y,"config")),w=function(e){const t=new Map;for(const s of e){if("Models"===s.kind&&Array.isArray(s.spec))for(const e of s.spec){const s=i(e);t.set(String(s.name),s)}if("Model"===s.kind&&d(s.spec)){const e=i({name:s.metadata?.name,...s.spec});t.set(String(e.name),e)}}return t}(g),k=function(e,t){const n=new Map;for(const r of t.filter(e=>"Agent"===e.kind)){if(!d(r.spec))continue;const t=String(r.metadata?.name),o=d(r.spec.config)?r.spec.config:{},i=c(r.spec,o);n.set(t,{id:t,name:t,description:r.metadata?.description??"",sourcePath:s.join(e,"config","agents",`${t}.yaml`),modelRef:l(r.spec.modelRef),toolRefs:f(r.spec.tools),skillPathRefs:f(r.spec.skills).map(t=>s.join(e,"resources","skills",t)),subagentRefs:f(r.spec.subagents),memorySources:m(r.spec.memory),deepAgentConfig:{...o,responseFormat:p(o.responseFormat),systemPrompt:i}})}return n}(y,g),A=await async function(n,r){const o=new Map;for(const e of r.filter(e=>"Tool"===e.kind)){const t=e.metadata?.name;t&&o.set(t,{name:t,id:t,spec:e.spec})}return await async function(n,r){const o=await e(n,{withFileTypes:!0});for(const e of o){if(!e.isFile()||!e.name.endsWith(".mjs")||e.name.startsWith("_"))continue;const o=s.join(n,e.name),i=(await t(o,"utf8")).match(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool/u),a=i?.[1]??s.basename(e.name,".mjs");r.set(a,{name:a,id:a,sourcePath:o})}}(s.join(n,"resources","tools"),o),o}(y,g),h=await async function(t){const n=new Map,o=s.join(t,"resources","skills");for(const t of await e(o,{withFileTypes:!0})){if(!t.isDirectory())continue;const e=s.join(o,t.name),i=r(e);n.set(i.name,{name:i.name,path:e,description:i.description,allowedTools:i.allowedTools})}return n}(y);return function(e,t){for(const n of e.values())n.skillPathRefs=n.skillPathRefs.map(e=>{const n=s.basename(e);return t.get(n)?.path??e})}(k,h),function(e,t){e.has("direct")||e.set("direct",{id:"direct",name:"direct",description:"Direct execution agent.",sourcePath:s.join(t,"config","runtime","workspace.yaml"),toolRefs:[],skillPathRefs:[],subagentRefs:[],memorySources:[],deepAgentConfig:{builtinTools:{filesystem:!1,modelExposed:!1}}})}(k,y),{workspaceRoot:y,models:w,agents:k,tools:A,skills:h,bindings:a(k)}}async function o(t){const n=await e(t,{withFileTypes:!0});return(await Promise.all(n.map(async e=>{const n=s.join(t,e.name);return e.isDirectory()?o(n):e.isFile()&&/\.ya?ml$/iu.test(e.name)?[n]:[]}))).flat().sort()}function i(e){const t=String(e.name),s={...e};return delete s.name,delete s.provider,delete s.model,{name:t,provider:String(u(e.provider)),model:String(u(e.model)),init:(n=s,Object.fromEntries(Object.entries(n).map(([e,t])=>[e,u(t)])))};var n}function a(e){const t=new Map;for(const[s,n]of e){const e=n.deepAgentConfig;t.set(s,{agentId:s,deepAgentParams:{responseFormat:e.responseFormat,systemPrompt:e.systemPrompt}})}return t}function c(e,t){return"string"==typeof e.systemPrompt?e.systemPrompt:"string"==typeof t.systemPrompt?t.systemPrompt:""}function p(e){const t={type:"object",properties:{status:{type:"string",enum:["completed","blocked","failed","refused"]},summary:{type:"array",items:{type:"string"}},findings:{type:"array",items:{type:"string"}},blockers:{type:"array",items:{type:"string"}},nextActions:{type:"array",items:{type:"string"}},report:{type:"string"}},required:["status","summary","findings","blockers","nextActions","report"]};if(!d(e))return t;const s=d(e.properties)?e.properties:{},n=Array.isArray(e.required)?e.required:[];return{...t,...e,properties:{...t.properties,...s},required:[...new Set([...t.required,...n].filter(e=>"string"==typeof e))]}}function m(e){return Array.isArray(e)?e.flatMap(e=>d(e)&&"string"==typeof e.path?[e.path]:[]):[]}function f(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function l(e){return"string"==typeof e?e.replace(/^[^/]+\//u,""):void 0}function u(e){if("string"!=typeof e)return e;const t=e.replace(/\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*?))?\}/gu,(e,t,s)=>process.env[t]??s??"");return/^(true|false)$/iu.test(t)?"true"===t.toLowerCase():/^-?\d+$/u.test(t)?Number(t):t}function d(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{readdir as e,readFile as t}from"node:fs/promises";import*as s from"node:path";import{parseAllDocuments as o}from"yaml";import{validateSkillMetadata as r}from"../runtime/skills/skill-metadata.js";export async function loadWorkspace(n){const i=s.resolve(n),a=await async function readConfigDocuments(e){const s=await listYamlFiles(e),r=[];for(const e of s){const s=await t(e,"utf8");for(const e of o(s)){const t=e.toJSON();t?.kind&&r.push({...t,metadata:t.metadata??{},spec:t.spec??{}})}}return r}(s.join(i,"config")),c=function compileModels(e){const t=new Map;for(const s of e){if("Models"===s.kind&&Array.isArray(s.spec))for(const e of s.spec){const s=compileModel(e);t.set(String(s.name),s)}if("Model"===s.kind&&isRecord(s.spec)){const e=compileModel({name:s.metadata?.name,...s.spec});t.set(String(e.name),e)}}return t}(a),m=function compileAgents(e,t){const o=new Map;for(const r of t.filter(e=>"Agent"===e.kind)){if(!isRecord(r.spec))continue;const t=String(r.metadata?.name),n=isRecord(r.spec.config)?r.spec.config:{},i=readSystemPrompt(r.spec,n);o.set(t,{id:t,name:t,description:r.metadata?.description??"",sourcePath:s.join(e,"config","agents",`${t}.yaml`),modelRef:normalizeRef(r.spec.modelRef),toolRefs:readStringList(r.spec.tools),skillPathRefs:readStringList(r.spec.skills).map(t=>s.join(e,"resources","skills",t)),subagentRefs:readStringList(r.spec.subagents),memorySources:readMemorySources(r.spec.memory),deepAgentConfig:{...n,responseFormat:normalizeResponseFormat(n.responseFormat),systemPrompt:i}})}return o}(i,a),l=await async function compileTools(o,r){const n=new Map;for(const e of r.filter(e=>"Tool"===e.kind)){const t=e.metadata?.name;t&&n.set(t,{name:t,id:t,spec:e.spec})}return await async function loadToolExports(o,r){const n=await e(o,{withFileTypes:!0});for(const e of n){if(!e.isFile()||!e.name.endsWith(".mjs")||e.name.startsWith("_"))continue;const n=s.join(o,e.name),i=(await t(n,"utf8")).match(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool/u),a=i?.[1]??s.basename(e.name,".mjs");r.set(a,{name:a,id:a,sourcePath:n})}}(s.join(o,"resources","tools"),n),n}(i,a),p=await async function compileSkills(t){const o=new Map,n=s.join(t,"resources","skills");for(const t of await e(n,{withFileTypes:!0})){if(!t.isDirectory())continue;const e=s.join(n,t.name),i=r(e);o.set(i.name,{name:i.name,path:e,description:i.description,allowedTools:i.allowedTools})}return o}(i);return function resolveAgentSkillNames(e,t){for(const o of e.values())o.skillPathRefs=o.skillPathRefs.map(e=>{const o=s.basename(e);return t.get(o)?.path??e})}(m,p),function ensureDirectAgent(e,t){e.has("direct")||e.set("direct",{id:"direct",name:"direct",description:"Direct execution agent.",sourcePath:s.join(t,"config","runtime","workspace.yaml"),toolRefs:[],skillPathRefs:[],subagentRefs:[],memorySources:[],deepAgentConfig:{}})}(m,i),{workspaceRoot:i,models:c,agents:m,tools:l,skills:p,bindings:compileBindings(m)}}async function listYamlFiles(t){const o=await e(t,{withFileTypes:!0});return(await Promise.all(o.map(async e=>{const o=s.join(t,e.name);return e.isDirectory()?listYamlFiles(o):e.isFile()&&/\.ya?ml$/iu.test(e.name)?[o]:[]}))).flat().sort()}function compileModel(e){const t=String(e.name),s={...e};return delete s.name,delete s.provider,delete s.model,{name:t,provider:String(resolveValue(e.provider)),model:String(resolveValue(e.model)),init:(o=s,Object.fromEntries(Object.entries(o).map(([e,t])=>[e,resolveValue(t)])))};var o}function compileBindings(e){const t=new Map;for(const[s,o]of e){const e=o.deepAgentConfig;t.set(s,{agentId:s,deepAgentParams:{responseFormat:e.responseFormat,systemPrompt:e.systemPrompt}})}return t}function readSystemPrompt(e,t){return"string"==typeof e.systemPrompt?e.systemPrompt:"string"==typeof t.systemPrompt?t.systemPrompt:""}function normalizeResponseFormat(e){const t={type:"object",properties:{status:{type:"string",enum:["completed","blocked","failed","refused"]},summary:{type:"array",items:{type:"string"}},findings:{type:"array",items:{type:"string"}},blockers:{type:"array",items:{type:"string"}},nextActions:{type:"array",items:{type:"string"}},report:{type:"string"}},required:["status","summary","findings","blockers","nextActions","report"]};if(!isRecord(e))return t;const s=isRecord(e.properties)?e.properties:{},o=Array.isArray(e.required)?e.required:[];return{...t,...e,properties:{...t.properties,...s},required:[...new Set([...t.required,...o].filter(e=>"string"==typeof e))]}}function readMemorySources(e){return Array.isArray(e)?e.flatMap(e=>isRecord(e)&&"string"==typeof e.path?[e.path]:[]):[]}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function normalizeRef(e){return"string"==typeof e?e.replace(/^[^/]+\//u,""):void 0}function resolveValue(e){if("string"!=typeof e)return e;const t=e.replace(/\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*?))?\}/gu,(e,t,s)=>process.env[t]??s??"");return/^(true|false)$/iu.test(t)?"true"===t.toLowerCase():/^-?\d+$/u.test(t)?Number(t):t}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "MIT",
@@ -50,20 +50,21 @@
50
50
  "build:chmod": "chmod +x dist/cli.js packages/cli/dist/src/cli.js",
51
51
  "check": "tsc -b --pretty false",
52
52
  "check:rules": "node scripts/check-project-rules.mjs",
53
- "compare:tool-calling": "node scripts/compare-granite-tool-calling.mjs",
54
- "compare:granite-tools": "node scripts/compare-granite-tool-calling.mjs",
55
- "benchmark:bfcl-tool-guard": "node scripts/benchmark-bfcl-tool-guard.mjs",
56
- "benchmark:retry-policy": "node scripts/benchmark-retry-policy.mjs",
57
- "benchmark:tool-guard": "node scripts/benchmark-tool-argument-guard.mjs",
58
- "benchmark:tool-guard:matrix": "node scripts/benchmark-tool-argument-guard-matrix.mjs",
53
+ "compare:tool-calling": "node scripts/benchmarks/compare-granite-tool-calling.mjs",
54
+ "compare:granite-tools": "node scripts/benchmarks/compare-granite-tool-calling.mjs",
55
+ "benchmark:bfcl-tool-guard": "node scripts/benchmarks/bfcl-tool-guard.mjs",
56
+ "benchmark:backend-comparison": "node scripts/benchmarks/backend-comparison.mjs",
57
+ "benchmark:retry-policy": "node scripts/benchmarks/retry-policy.mjs",
58
+ "benchmark:tool-guard": "node scripts/benchmarks/tool-argument-guard.mjs",
59
+ "benchmark:tool-guard:matrix": "node scripts/benchmarks/tool-argument-guard-matrix.mjs",
59
60
  "test:langmem:sqlite:e2e": "node scripts/run-langmem-sqlite-e2e.mjs",
60
61
  "validate:workspace": "node scripts/validate-workspace.mjs",
61
- "test": "node --test dist/test/*.test.js dist/test/memory/*.test.js dist/test/retry/*.test.js dist/test/workspace/*.test.js",
62
+ "test": "rm -rf dist/test && tsc -b test/tsconfig.json && node --test dist/test/*.test.js dist/test/adapter/*.test.js dist/test/compat/*.test.js dist/test/memory/*.test.js dist/test/protocol/*.test.js dist/test/retry/*.test.js dist/test/runtime/*.test.js dist/test/sdk/*.test.js dist/test/workspace/*.test.js",
62
63
  "test:langmem:maintenance:e2e": "node scripts/run-langmem-maintenance-e2e.mjs",
63
64
  "test:skill-mining:e2e": "node scripts/run-skill-candidate-mining-e2e.mjs",
64
65
  "prepublishOnly": "npm run build && npm run release:check-package",
65
66
  "prepack": "npm run release:minify",
66
- "release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle --module --toplevel --comments false --output \"$f\"; done' sh {} +",
67
+ "release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle keep_fnames=true --keep-fnames --keep-classnames --module --comments false --output \"$f\"; done' sh {} +",
67
68
  "release:check-package": "node scripts/release/check-npm-package.mjs",
68
69
  "release:pack": "npm run build && npm run release:check-package && npm pack --dry-run",
69
70
  "release:publish": "npm publish --access public --registry https://registry.npmjs.org/",
@@ -73,10 +74,13 @@
73
74
  "packages/*"
74
75
  ],
75
76
  "dependencies": {
76
- "@botbotgo/better-call": "^0.1.1",
77
+ "@botbotgo/better-call": "^0.1.18",
77
78
  "@langchain/core": "^1.1.43",
79
+ "@langchain/langgraph": "^1.3.0",
78
80
  "@langchain/ollama": "^1.2.7",
81
+ "@langchain/openai": "^1.4.5",
79
82
  "@stable-harness/adapter-deepagents": "file:packages/adapter-deepagents",
83
+ "@stable-harness/adapter-langgraph": "file:packages/adapter-langgraph",
80
84
  "@stable-harness/core": "file:packages/core",
81
85
  "@stable-harness/governance": "file:packages/governance",
82
86
  "@stable-harness/memory": "file:packages/memory",
@@ -3,6 +3,7 @@ import type { DeepAgentFactory, DeepAgentsAdapterRunner } from "./types.js";
3
3
  type DeepAgentsAdapterOptions = {
4
4
  runner?: DeepAgentsAdapterRunner;
5
5
  createDeepAgent?: DeepAgentFactory;
6
+ config?: Record<string, unknown>;
6
7
  };
7
8
  export declare function createDeepAgentsAdapter(options?: DeepAgentsAdapterOptions): RuntimeAdapter;
8
9
  export {};
@@ -1 +1 @@
1
- import{ToolMessage as e}from"@langchain/core/messages";import{tool as t}from"@langchain/core/tools";import{ChatOllama as n}from"@langchain/ollama";import{resolveDeepAgentsNativeMemories as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as s,validateFilesystemBuiltinCall as o}from"./builtin-tool-policy.js";import{buildDeepAgentRequest as a}from"./messages.js";import{createDeepAgentsRetryMiddleware as i}from"./retry-policy.js";import{streamDeepAgentResult as d}from"./stream-events.js";const u=new Set(["write_todos","task","ls","read_file","write_file","edit_file","glob","grep"]);export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const n=(e.createDeepAgent??await async function(){try{const e=(await async function(){return new Function("specifier","return import(specifier)")("deepagents")}()).createDeepAgent;if("function"==typeof e)return e}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${D(e)}`)}throw new Error("DeepAgents package does not export createDeepAgent.")}())(function(e){const t=w(e.agent.config.deepagents),n=t.permissions??m(e,e.agent);return q({name:e.agent.id,model:t.model??I(e,e.agent),systemPrompt:e.agent.systemPrompt??_(e.agent.config.systemPrompt),backend:t.backend,checkpointer:t.checkpointer,store:t.store,middleware:f(t.middleware,p(e),s(e),i(e.workspace.runtime.retry)),responseFormat:t.responseFormat,contextSchema:t.contextSchema,interruptOn:t.interruptOn,permissions:n,tools:h(e,e.agent.id,e.agent.tools),subagents:e.agent.subagents.map(t=>{const n=e.workspace.agents.get(t),r=w(n?.config.deepagents),s=r.permissions??m(e,n);return q({name:t,description:n?.description??_(n?.config.description)??n?.id,systemPrompt:n?.systemPrompt??_(n?.config.systemPrompt),model:r.model??(n?I(e,n):void 0),middleware:r.middleware,interruptOn:r.interruptOn,permissions:s,responseFormat:r.responseFormat,tools:h(e,t,n?.tools??[]),memory:c(e,n),skills:l(e,n)})}),memory:c(e,e.agent),skills:l(e,e.agent)})}(t)),r=a(t);return!0===t.request.metadata?.openaiStream&&n.streamEvents?d(t,n.streamEvents(r,{version:"v2"}),A):A(await n.invoke(r))}}}function c(e,t){const n=b(t?.config,"memory");if(n)return n;const s=r(e.workspace).map(e=>`/memories/${e.id}.md`);return s.length>0?s:void 0}function l(e,t){const n=b(t?.config,"skills");if(n)return n;const r=(t?.skills??[]).map(t=>e.workspace.skills.get(t)?.path??t);return r.length>0?r:void 0}function p(t){return{name:"StableHarnessObserver",async wrapToolCall(n,r){const s=n.toolCall?.name;if(!s||!u.has(s))return r(n);t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"tool.start",toolId:s,args:n.toolCall?.args}});const a="task"===s?function(t,n){const r=function(e){const t=C(e)?e:{};return _(t.subagent_type)??_(t.subagentType)}(n.toolCall?.args),s=function(e){const t=k(e.agent.config,"builtinTools")?.modelExposed;return!1===t?[]:Array.isArray(t)?t.includes("task")?e.agent.subagents:[]:["general-purpose",...e.agent.subagents]}(t);if(void 0===s||r&&s.includes(r))return;const o=r?`: ${r}`:"",a=s.length>0?s.join(", "):"none";return new e({tool_call_id:n.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:`Task delegation target is not in the workspace inventory${o}. Allowed task targets: ${a}.`})}(t,n):void 0;if(a)return t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"tool.result",toolId:s,output:a.content}}),a;const i=o(t,s,n);if(i)return t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"tool.result",toolId:s,output:i.content}}),i;try{const e=await r(n);return t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"tool.result",toolId:s,output:y(s,n,e)}}),e}catch(e){throw t.emit({type:"adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"tool.result",toolId:s,error:D(e)}}),e}}}}function m(e,t){const n=k(t?.config,"builtinTools"),r=[];if(!1!==n?.filesystem){if(g(e))return}else r.push({operations:["read","write"],paths:["/**"],mode:"deny"});return g(e)||r.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),r.length>0?r:void 0}function g(e){const t=k(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function f(e,t,n,r=[]){return[t,n,...r,...Array.isArray(e)?e:[]]}function y(e,t,n){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):A(n)}function I(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?function(e){return"ollama"===e.provider?new n(q({model:e.model,baseUrl:_(e.config?.baseUrl),temperature:O(e.config?.temperature),numCtx:O(e.config?.numCtx),numPredict:O(e.config?.numPredict),timeout:O(e.config?.timeout),think:"boolean"==typeof e.config?.think?e.config.think:void 0})):e.model}(r):void 0}function h(n,r,s){return n.toolGateway?s.flatMap(s=>{const o=n.toolGateway?.get(s);if(!o)return[];const a=n.workspace.tools.get(s);return[t(async t=>{n.emit({type:"adapter.event",requestId:n.requestId,sessionId:n.sessionId,agentId:r,event:{adapter:"deepagents",phase:"tool.start",toolId:s}});const o=await async function(t,n,r,s){try{return await t.toolGateway.invoke({toolId:r,args:s,context:{workspaceRoot:t.workspace.root,requestId:t.requestId,sessionId:t.sessionId,agentId:n}})}catch(t){if(function(e){return e instanceof Error&&"ToolArgumentValidationError"===e.name&&"string"==typeof e.toolId&&Array.isArray(e.issues)}(t))return new e({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:v(t)});throw t}}(n,r,s,t);return n.emit({type:"adapter.event",requestId:n.requestId,sessionId:n.sessionId,agentId:r,event:{adapter:"deepagents",phase:"tool.result",toolId:s}}),o instanceof e?o:A(o.output)},{name:s,description:a?.description??o.description??s,schema:a?.schema??{type:"object",additionalProperties:!0}})]}):[]}function v(e){return JSON.stringify({error:"tool_argument_validation_failed",toolId:e.toolId,issues:e.issues,retry:"Call the same tool again with arguments that satisfy the reported schema and semantic issues."})}function w(e){return C(e)?e:{}}function k(e,t){const n=C(e)?e:{};return C(n[t])?n[t]:void 0}function b(e,t){const n=C(e)?e:{},r=w(n.deepagents),s="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of s){const t=S(r[e]);if(t)return t}return S(n[t])}function A(e){if("string"==typeof e)return e;if(C(e)){const t=e.structuredResponse??e.structured_response;if(void 0!==t)return"string"==typeof t?t:JSON.stringify(t);const n=(Array.isArray(e.messages)?e.messages:[]).at(-1);if(C(n)&&"string"==typeof n.content)return n.content}return JSON.stringify(e)}function q(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function _(e){return"string"==typeof e&&e.trim()?e:void 0}function O(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function S(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function C(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function D(e){return e instanceof Error?e.message:String(e)}
1
+ import{realpathSync as e}from"node:fs";import t from"node:path";import{createBuiltinToolPolicyMiddleware as r,createObserverMiddleware as n,resolveFilesystemPermissions as o}from"./internal/builtin-tool-policy.js";import{buildGatewayTools as s,createToolRepeatState as i,stringifyDeepAgentResult as a}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as l}from"./memory.js";import{buildDeepAgentRequest as d}from"./internal/messages.js";import{createRawToolCallParserMiddleware as c}from"./internal/raw-tool-call-parser.js";import{createBackendModel as p}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as u}from"./retry-policy.js";import{streamDeepAgentResult as g}from"./internal/stream-events.js";export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const r=e.createDeepAgent?void 0:await async function loadDeepAgentsModule(){try{return await async function importOptionalPackage(e){return new Function("specifier","return import(specifier)")(e)}("deepagents")}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${function formatError(e){return e instanceof Error?e.message:String(e)}(e)}`)}}(),n=e.createDeepAgent??function readCreateDeepAgent(e){const t=e?.createDeepAgent;if("function"==typeof t)return t;throw new Error("DeepAgents package does not export createDeepAgent.")}(r),i=n(function buildDeepAgentParams(e,t,r){const n={...readDeepAgentsConfig(t),...readDeepAgentsConfig(e.agent.config.deepagents)},i=resolveDeepAgentsSkills(e,e.agent),a=n.permissions??o(e,e.agent),l=requestScopedRepeatState(e,e.agent.id);return pruneUndefined({name:e.agent.id,model:n.model??resolveAgentModel(e,e.agent),systemPrompt:buildSystemPrompt(e,e.agent),backend:n.backend??resolveDeepAgentsBackend(e,r,i),checkpointer:n.checkpointer,store:n.store,middleware:mergeMiddleware(e,e.agent,n.middleware,l),responseFormat:n.responseFormat,contextSchema:n.contextSchema,interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:a,tools:s(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(e,e.agent,n),l),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),i=n.permissions??o(e,r),a=scopedInput(e,r),l=requestScopedRepeatState(e,t);return pruneUndefined({name:t,description:r?.description??readString(r?.config.description)??r?.id,systemPrompt:buildSystemPrompt(e,r),model:n.model??(r?resolveAgentModel(e,r):void 0),middleware:mergeMiddleware(a,r,n.middleware,l),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:i,responseFormat:n.responseFormat,tools:s(e,t,r?.tools??[],resolveAgentRepairModel(a,r,n),l),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:i})}(t,e.config,r)),l=d(t),c=function buildDeepAgentInvokeConfig(e){return pruneUndefined({recursionLimit:readNumber(readDeepAgentsConfig(e.config.deepagents).recursionLimit)??readNumber(e.config.recursionLimit)})}(t.agent);if(!0===t.request.metadata?.openaiStream&&i.streamEvents)return g(t,i.streamEvents(l,{version:"v2",...c}),a);const p=await i.invoke(l,c);return a(p)}}}function buildSystemPrompt(e,t){const r=t?.systemPrompt??readString(t?.config.systemPrompt),n=function buildSkillInventoryPolicy(e,t){const r=(t?.skills??[]).map(t=>e.workspace.skills.get(t)).filter(e=>Boolean(e));if(0!==r.length)return["## Stable Harness Skill Inventory","Use only the skills listed in this workspace inventory. Do not infer or invent skill names, skill directories, or SKILL.md paths from generic examples.","Skill files are already registered with the backend. Do not read local SKILL.md files or skill directories through filesystem tools.","If none of these skills match the task, continue with the configured tools and collected evidence instead of reading an unlisted skill path.",r.map(e=>{const t=e.allowedTools.length>0?`; allowed tools: ${e.allowedTools.join(", ")}`:"",r=e.description?`: ${e.description}${t}`:t;return`- ${e.id}${r}`}).join("\n")].join("\n")}(e,t),o=function buildResponseLanguagePolicy(e){const t=isRecord(e.workspace.runtime.responseLanguage)?e.workspace.runtime.responseLanguage:{};if("matchUser"!==(readString(t.mode)??readString(t.strategy)))return;const r=function detectRequestLanguage(e){return/\p{Script=Han}/u.test(e)?"Chinese":/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(e)?"Japanese":/\p{Script=Hangul}/u.test(e)?"Korean":/\p{Script=Arabic}/u.test(e)?"Arabic":/\p{Script=Hebrew}/u.test(e)?"Hebrew":/\p{Script=Thai}/u.test(e)?"Thai":/\p{Script=Cyrillic}/u.test(e)?"Cyrillic-script language":void 0}(e.request.input);return["Stable runtime response language policy:",...r?[`- Detected request language: ${r}.`]:[],"- Match the final answer language to the original user request unless the user explicitly asks for another language.","- If tool or subagent evidence is in a different language, translate the final user-facing answer into the detected request language.","- When delegating to subagents, include the same response-language requirement in delegated instructions.","- Do not call another tool or subagent only to translate, rewrite, format, or synthesize a completed answer.",`Original user request:\n${e.request.input}`].join("\n")}(e),s=function buildResponsePresentationPolicy(e,t){const r=isRecord(e.workspace.runtime.responsePresentation)?e.workspace.runtime.responsePresentation:{};if(!0===r.enabled&&!function hasStructuredResponseFormat(e){return void 0!==e?.config.responseFormat||isRecord(e?.config.deepagents)&&void 0!==e.config.deepagents.responseFormat}(t)&&"markdown"===(readString(r.style)??"markdown"))return["Stable runtime final-answer presentation policy:","- For user-facing natural-language final answers, use GitHub-flavored Markdown.","- Prefer clear section headings, short paragraphs, and concise bullets over dense prose.","- Use tables only when they make comparison or planning details easier to scan.","- For detailed investigations, plans, or reports, include assumptions, findings, recommendations, and concrete next steps.","- Preserve exact plain text, JSON, code, or other structured output when the user or response format asks for it.","- Do not end with generic follow-up offers; deliver the requested answer directly."].join("\n")}(e,t);return[r,n,o,s].filter(Boolean).join("\n\n")||void 0}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=l(e.workspace).map(e=>`/memories/${e.id}.md`);return n.length>0?n:void 0}function resolveDeepAgentsSkills(r,n){const o=readDeepAgentsStringArray(n?.config,"skills");if(o)return o;const s=[...new Set((n?.skills??[]).map(e=>r.workspace.skills.get(e)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(n=>function backendSkillSourcePath(r,n){const o=t.dirname(t.dirname(n)),s=t.relative(r,o);return!s||s.startsWith("..")||t.isAbsolute(s)?""===s?"/":function canonicalPath(t){try{return e.native(t)}catch{return t}}(o):`/${s.split(t.sep).join("/")}`}(r.workspace.root,n)))];return s.length>0?s:void 0}function resolveDeepAgentsBackend(e,t,r){if(t?.FilesystemBackend&&r&&0!==r.length)return()=>new t.FilesystemBackend({rootDir:e.workspace.root})}function mergeMiddleware(e,t,o,s=i(e.workspace.runtime.toolGateway)){const a=Array.isArray(o)?o:[],l=scopedInput(e,t),d=new Set;return[n(l,{observedToolIds:d,repeatState:s}),r(l,{repeatState:s}),...u(e.workspace.runtime.retry),...a,c(l)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=i(e.workspace.runtime.toolGateway);return e.requestState&&o&&e.requestState.set(r,o),o}function scopedInput(e,t){return t?{...e,agent:t}:e}function resolveAgentModel(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?p(r):void 0}function resolveAgentRepairModel(e,t,r){const n=r.model;if(isRepairModel(n))return n;if(!t)return;const o=resolveAgentModel(e,t);return isRepairModel(o)?o:void 0}function readDeepAgentsConfig(e){return isRecord(e)?e:{}}function readDeepAgentsStringArray(e,t){const r=isRecord(e)?e:{},n=readDeepAgentsConfig(r.deepagents),o="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of o){const t=readStringArray(n[e]);if(t)return t}return readStringArray(r[t])}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 readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function isRepairModel(e){return"object"==typeof e&&null!==e&&"invoke"in e&&"function"==typeof e.invoke}
@@ -1,2 +1,3 @@
1
1
  export { createDeepAgentsAdapter } from "./adapter.js";
2
+ export { createDeepAgentsMemoryMaintenanceTarget, resolveDeepAgentsNativeMemories } from "./memory.js";
2
3
  export type { DeepAgentFactory, DeepAgentsAdapterRunner } from "./types.js";
@@ -1 +1 @@
1
- export{createDeepAgentsAdapter}from"./adapter.js";
1
+ export{createDeepAgentsAdapter}from"./adapter.js";export{createDeepAgentsMemoryMaintenanceTarget,resolveDeepAgentsNativeMemories}from"./memory.js";
@@ -0,0 +1,4 @@
1
+ export declare function normalizeArgsRecord(args: unknown): Record<string, unknown>;
2
+ export declare function normalizeWriteTodosArgs(args: unknown): Record<string, unknown>;
3
+ export declare function normalizeFilesystemArgs(toolId: string, args: unknown, workspaceRoot: string): Record<string, unknown>;
4
+ export declare function shallowEqualRecord(left: Record<string, unknown>, right: Record<string, unknown>): boolean;
@@ -0,0 +1 @@
1
+ import r from"node:path";export function normalizeArgsRecord(r){if(isRecord(r))return r;if("string"!=typeof r||!r.trim())return{};try{const t=JSON.parse(r);return isRecord(t)?t:{}}catch{return{}}}export function normalizeWriteTodosArgs(r){if(Array.isArray(r))return{todos:r.map(normalizeTodoItem)};if("string"==typeof r&&r.trim())try{const t=JSON.parse(r);if(Array.isArray(t))return{todos:t.map(normalizeTodoItem)}}catch{return{todos:[{content:r.trim()}]}}const t=normalizeArgsRecord(r),e=readTodoList(t);return e?{...t,todos:e.map(normalizeTodoItem)}:t}export function normalizeFilesystemArgs(r,t,e){const n={...normalizeArgsRecord(t)};return normalizePathField(n,e,"path"),normalizePathField(n,e,"file_path"),normalizePathField(n,e,"pattern","glob"===r),n}export function shallowEqualRecord(r,t){const e=Object.keys(r),n=Object.keys(t);return e.length===n.length&&e.every(e=>r[e]===t[e])}function readTodoList(r){const t=r.todos??r.items??r.tasks??r.plan;if(Array.isArray(t))return t;if("string"==typeof t&&t.trim())try{const r=JSON.parse(t);return Array.isArray(r)?r:isRecord(r)?readTodoList(r):void 0}catch{return}}function normalizeTodoItem(r){if("string"==typeof r&&r.trim())return{content:r.trim()};if(!isRecord(r))return r;const t=readString(r.content)??readString(r.description)??readString(r.task)??readString(r.title)??readString(r.name),e=function normalizeTodoStatus(r){if(!r)return;const t=r.toLowerCase().replaceAll("-","_").replaceAll(" ","_");return"todo"===t||"not_started"===t?"pending":"doing"===t||"in_progress"===t?"in_progress":"done"===t||"complete"===t?"completed":t}(readString(r.status)??readString(r.state));return{...t?{content:t}:{},...e?{status:e}:{}}}function readString(r){return"string"==typeof r&&r.trim()?r.trim():void 0}function normalizePathField(t,e,n,o=!0){const i=o?readString(t[n]):void 0;i&&(t[n]=function workspaceBackendPath(t,e){if(!e.startsWith("/"))return e;if(function isPathInside(t,e){const n=r.relative(t,e);return""===n||!!n&&!n.startsWith("..")&&!r.isAbsolute(n)}(t,e)){const n=r.relative(t,e).split(r.sep).filter(Boolean).join("/");return n?`/${n}`:"/"}return e}(e,i))}function isRecord(r){return"object"==typeof r&&null!==r&&!Array.isArray(r)}
@@ -0,0 +1,39 @@
1
+ import { ToolMessage } from "@langchain/core/messages";
2
+ import type { RuntimeAdapter, WorkspaceAgent } from "@stable-harness/core";
3
+ import { type ToolRepeatState } from "./gateway-tools.js";
4
+ type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
5
+ export declare function createBuiltinToolPolicyMiddleware(input: AdapterRunInput, options?: {
6
+ repeatState?: ToolRepeatState;
7
+ }): {
8
+ name: string;
9
+ wrapModelCall(request: {
10
+ tools?: Array<{
11
+ name?: unknown;
12
+ }>;
13
+ toolChoice?: unknown;
14
+ }, handler: (request: unknown) => Promise<unknown>): Promise<unknown>;
15
+ };
16
+ export declare function validateFilesystemBuiltinCall(input: AdapterRunInput, toolId: string, request: {
17
+ toolCall?: {
18
+ id?: string;
19
+ };
20
+ }): ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>> | undefined;
21
+ export declare function createObserverMiddleware(input: AdapterRunInput, options?: {
22
+ observedToolIds?: Set<string>;
23
+ repeatState?: ToolRepeatState;
24
+ }): {
25
+ name: string;
26
+ wrapToolCall(request: {
27
+ toolCall?: {
28
+ id?: string;
29
+ name?: string;
30
+ args?: unknown;
31
+ };
32
+ }, handler: (request: unknown) => Promise<unknown>): Promise<unknown>;
33
+ };
34
+ export declare function resolveFilesystemPermissions(input: AdapterRunInput, agent: WorkspaceAgent | undefined): {
35
+ operations: string[];
36
+ paths: string[];
37
+ mode: string;
38
+ }[] | undefined;
39
+ export {};
@@ -0,0 +1 @@
1
+ import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as o,normalizeFilesystemArgs as r,normalizeWriteTodosArgs as n,shallowEqualRecord as i}from"./builtin-args.js";import{afterToolInvoke as l,beforeToolInvoke as a,createToolRepeatState as s,stringifyDeepAgentResult as u,toolControlProjection as c}from"./gateway-tools.js";import{validateSkillFileBuiltinCall as d}from"./skill-file-policy.js";import{filterRepeatLimitedTools as g}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as p}from"./trace-projection.js";const f=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),m=new Set(["write_todos","task",...f]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,r){const n=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,n);return r({...o,tools:n,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,o,r){if(isFilesystemDisabled(e)&&f.has(o))return new t({tool_call_id:r.toolCall?.id??`stable-harness-${o}-policy`,name:o,content:`Filesystem builtin tool '${o}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,c={}){const g=c.repeatState??s(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(s,p){const h=s.toolCall?.name;if(!h||!m.has(h))return p(s);const y=function normalizeBuiltinToolRequest(e,t,l){return"write_todos"===t?{...l,toolCall:{...l.toolCall,args:n(l.toolCall?.args)}}:"task"===t?{...l,toolCall:{...l.toolCall,args:o(l.toolCall?.args)}}:f.has(t)?function normalizeFilesystemToolRequest(e,t,n){const l=o(n.toolCall?.args),a=r(t,l,e.workspace.root);return i(l,a)?n:{...n,toolCall:{...n.toolCall,args:a}}}(e,t,l):l}(e,h,s);emitToolEvent(e,h,"agent.tool.start",y.toolCall?.args);const v=g?a(h,y.toolCall?.args,g):void 0;if(v)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:v.eventOutput}),builtinToolMessage(s,h,v.modelOutput);const b="task"===h?function validateTaskCall(e,o){const r=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(o.toolCall?.args),n=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===t?[]:Array.isArray(t)?t.includes("task")?e.agent.subagents:[]:void 0}(e);if(void 0===n||r&&n.includes(r))return;const i=r?`: ${r}`:"",l=n.length>0?n.join(", "):"none";return new t({tool_call_id:o.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:`Task delegation target is not in the workspace inventory${i}. Allowed task targets: ${l}.`})}(e,y):void 0;if(b)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:b.content}),b;const T=validateFilesystemBuiltinCall(e,h,y);if(T)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:T.content}),T;const C=d(e,h,y);if(C)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:C.content}),C;try{const t=await p(y),o=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):u(o)}(h,y,t),r=g?l(h,y.toolCall?.args,o,t,g):{};return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:r.eventOutput??o}),c.observedToolIds?.add(h),void 0===r.modelOutput?t:builtinToolMessage(s,h,r.modelOutput)}catch(o){const r=function recoverableBuiltinToolError(e,o,r){const n=formatError(r);return"task"===e&&/repeat limit reached for tool/iu.test(n)?new t({tool_call_id:o.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need, do not send a synthesis task to another subagent for the same need, and produce the best final answer now from the evidence already returned in this run. If coverage is incomplete, report the exact remaining evidence gap explicitly.",error:previewError(n)})}):/Received tool input did not match expected schema|Invalid input:/iu.test(n)?new t({tool_call_id:o.toolCall?.id??`stable-harness-${e}-argument-error`,name:e,status:"error",content:JSON.stringify({status:"tool_argument_error",toolId:e,instruction:"The upstream builtin tool rejected these arguments. Fix the tool arguments according to the tool schema, or choose a more appropriate available tool.",error:previewError(n)})}):void 0}(h,s,o);if(r)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:r.content}),r;throw emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{error:formatError(o)}),o}}}}function builtinToolMessage(e,o,r){return new t({tool_call_id:e.toolCall?.id??`stable-harness-${o}-repeat-guard`,name:o,content:r})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),r=[];if(r.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,r=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...r?[r,`${r}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return r.length>0?r:void 0}else r.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),r.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||r.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),r.length>0?r:void 0}function emitToolEvent(e,t,o,r,n={}){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:r}:{},...n,..."string"==typeof n.output?c(n.output):{},...p(t,o,r)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),r=t.split("/").filter(Boolean);if(!(r.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==r[e])return;return r.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&f.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
@@ -0,0 +1,32 @@
1
+ import { ToolMessage } from "@langchain/core/messages";
2
+ import type { RuntimeAdapter } from "@stable-harness/core";
3
+ import type { RuntimeToolRepairModel } from "@stable-harness/core";
4
+ export type ToolRepeatState = {
5
+ successfulCalls: Map<string, string>;
6
+ duplicateCallCounts: Map<string, number>;
7
+ latestSuccessfulOutputByTool: Map<string, string>;
8
+ successfulToolCounts: Map<string, number>;
9
+ toolCallCounts: Map<string, number>;
10
+ maxDuplicateCallsPerTool?: number;
11
+ maxCallsPerTool?: number;
12
+ maxSuccessfulCallsPerTool?: number;
13
+ maxCallsByTool: Map<string, number>;
14
+ maxSuccessfulCallsByTool: Map<string, number>;
15
+ };
16
+ export declare function buildGatewayTools(input: Parameters<RuntimeAdapter["run"]>[0], agentId: string, toolIds: string[], repairModel?: RuntimeToolRepairModel, repeatState?: ToolRepeatState | undefined): import("@langchain/core/tools").DynamicTool<string | ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>>, unknown>[];
17
+ export declare function createToolRepeatState(config: unknown): ToolRepeatState | undefined;
18
+ export declare function beforeToolInvoke(toolId: string, args: unknown, state: ToolRepeatState): {
19
+ eventOutput: string;
20
+ modelOutput: string;
21
+ } | undefined;
22
+ export declare function isToolRepeatLimitReached(toolId: string, state: ToolRepeatState | undefined): boolean;
23
+ export declare function afterToolInvoke(toolId: string, args: unknown, output: string, result: unknown, state: ToolRepeatState): {
24
+ eventOutput?: string;
25
+ modelOutput?: string;
26
+ };
27
+ export declare function stringifyDeepAgentResult(result: unknown): string;
28
+ export declare function toolControlProjection(output: string): {
29
+ controlStatus: string;
30
+ } | {
31
+ controlStatus?: undefined;
32
+ };
@@ -0,0 +1 @@
1
+ import{ToolMessage as t}from"@langchain/core/messages";import{tool as e}from"@langchain/core/tools";export function buildGatewayTools(o,r,n,s,a=createToolRepeatState(o.workspace.runtime.toolGateway)){return o.toolGateway?n.flatMap(n=>{const l=o.toolGateway?.get(n);if(!l)return[];const i=o.workspace.tools.get(n),u=i?.schema??l.schema;return[e(async e=>async function invokeGuardedGatewayTool(e){emitToolResult(e.input,e.agentId,e.toolId,void 0);const o=e.repeatState?beforeToolInvoke(e.toolId,e.args,e.repeatState):void 0;if(o)return emitToolResult(e.input,e.agentId,e.toolId,o.eventOutput),o.modelOutput;const r=await async function invokeGatewayTool(e,o,r,n,s){try{return await e.toolGateway.invoke({toolId:r,args:n,repairModel:s,context:{workspaceRoot:e.workspace.root,requestId:e.requestId,sessionId:e.sessionId,agentId:o}})}catch(o){if(function isToolArgumentValidationError(t){return t instanceof Error&&"ToolArgumentValidationError"===t.name&&"string"==typeof t.toolId&&Array.isArray(t.issues)}(o))return new t({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:formatToolArgumentError(o)});if(e.workspace.runtime.retry?.tools?.enabled)throw o;return new t({tool_call_id:`stable-harness-${r}-execution-error`,name:r,status:"error",content:JSON.stringify({error:"tool_execution_failed",toolId:r,message:formatError(o),retry:"Use the error as evidence, adjust the tool arguments if possible, or return a final answer with the blocker."})})}}(e.input,e.agentId,e.toolId,e.args,e.repairModel),n=r instanceof t?String(r.content):stringifyDeepAgentResult(r.output),s=e.repeatState?afterToolInvoke(e.toolId,e.args,n,r,e.repeatState):{};return emitToolResult(e.input,e.agentId,e.toolId,s.eventOutput??n),void 0!==s.modelOutput?s.modelOutput:r instanceof t?r:n}({input:o,agentId:r,toolId:n,args:e,repairModel:s,repeatState:a}),{name:n,description:buildToolDescription(i?.description??l.description??n,u,o.workspace.runtime.toolGateway,n),schema:{type:"object",additionalProperties:!0}})]}):[]}export function createToolRepeatState(t){if(function repeatGuardEnabled(t){return!0===repeatGuardConfig(t).enabled}(t))return{successfulCalls:new Map,duplicateCallCounts:new Map,latestSuccessfulOutputByTool:new Map,successfulToolCounts:new Map,toolCallCounts:new Map,maxDuplicateCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxDuplicateCallsPerTool)??3,maxCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxCallsPerTool),maxSuccessfulCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxSuccessfulCallsPerTool),maxCallsByTool:readPositiveIntegerMap(repeatGuardConfig(t).maxCallsByTool),maxSuccessfulCallsByTool:readPositiveIntegerMap(repeatGuardConfig(t).maxSuccessfulCallsByTool)}}function repeatGuardConfig(t){return isRecord(t)&&isRecord(t.repeatGuard)?t.repeatGuard:{}}function readPositiveInteger(t){return"number"==typeof t&&Number.isInteger(t)&&t>0?t:void 0}function readPositiveIntegerMap(t){return isRecord(t)?new Map(Object.entries(t).map(([t,e])=>[t,readPositiveInteger(e)]).filter(t=>void 0!==t[1])):new Map}export function beforeToolInvoke(t,e,o){const r=o.toolCallCounts.get(t)??0;o.toolCallCounts.set(t,r+1);const n=o.maxCallsByTool.get(t)??o.maxCallsPerTool;if(void 0!==n&&r>=n)throw new Error(`Stable Harness repeat limit reached for tool '${t}' after ${n} call(s).`);const s=o.maxSuccessfulCallsByTool.get(t)??o.maxSuccessfulCallsPerTool;if(void 0!==s&&(o.successfulToolCounts.get(t)??0)>=s){const e=repeatedToolCallLimitContent(t,o.latestSuccessfulOutputByTool.get(t));return{eventOutput:e,modelOutput:e}}const a=stableToolCallKey(t,e),l=o.successfulCalls.get(a);if(void 0!==l){const e=o.duplicateCallCounts.get(a)??0;if(o.duplicateCallCounts.set(a,e+1),void 0!==o.maxDuplicateCallsPerTool&&e>=o.maxDuplicateCallsPerTool){const e=repeatedToolCallLimitContent(t);return{eventOutput:e,modelOutput:e}}const r=function duplicateToolCallContent(t,e){return JSON.stringify({status:"duplicate_tool_call",toolId:t,instruction:"This agent already completed an equivalent tool call. Use the prior evidence instead of calling the tool again.",previousOutput:e})}(t,l);return{eventOutput:r,modelOutput:l}}}export function isToolRepeatLimitReached(t,e){if(!e)return!1;const o=e.maxCallsByTool.get(t)??e.maxCallsPerTool;if(void 0!==o&&(e.toolCallCounts.get(t)??0)>=o)return!0;const r=e.maxSuccessfulCallsByTool.get(t)??e.maxSuccessfulCallsPerTool;return void 0!==r&&(e.successfulToolCounts.get(t)??0)>=r}export function afterToolInvoke(e,o,r,n,s){return n instanceof t&&"error"===n.status||(s.successfulCalls.set(stableToolCallKey(e,o),r),s.latestSuccessfulOutputByTool.set(e,r),s.successfulToolCounts.set(e,(s.successfulToolCounts.get(e)??0)+1)),{}}function emitToolResult(t,e,o,r){t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:e,event:void 0===r?{adapter:"deepagents",phase:"agent.tool.start",toolId:o}:{adapter:"deepagents",phase:"agent.tool.result",toolId:o,output:previewToolOutput(r),...toolControlProjection(r)}})}function repeatedToolCallLimitContent(t,e){return JSON.stringify({status:"repeated_tool_call_limit",toolId:t,instruction:"This tool reached the configured repeat limit for this request. Do not call this tool or a substitute tool for the same evidence need again. Use previousOutput and the collected evidence to produce the final answer now, or report the remaining gap explicitly.",...void 0!==e?{previousOutput:e}:{}})}export function stringifyDeepAgentResult(e){if(e instanceof t)return function stringifyToolMessageContent(t){return"string"==typeof t?t:JSON.stringify(t)}(e.content);if("string"==typeof e)return e;if(isRecord(e)){const t=e.structuredResponse??e.structured_response;if(void 0!==t)return"string"==typeof t?t:JSON.stringify(t);const o=(Array.isArray(e.messages)?e.messages:[]).at(-1);if(isRecord(o)&&"string"==typeof o.content)return o.content;const r=(isRecord(e.update)&&Array.isArray(e.update.messages)?e.update.messages:[]).at(-1);if(isRecord(r)&&isRecord(r.kwargs)&&"string"==typeof r.kwargs.content)return r.kwargs.content;if(isRecord(r)&&"string"==typeof r.content)return r.content}return JSON.stringify(e)}function buildToolDescription(t,e,o,r){const n=function toolRepeatPolicyDescription(t,e){const o=repeatGuardConfig(t),r=readPositiveIntegerMap(o.maxSuccessfulCallsByTool).get(e)??readPositiveInteger(o.maxSuccessfulCallsPerTool);return void 0===r?"":`Stable runtime repeat policy: call this tool at most ${r} successful time(s) for this request. If more detail is needed, include the dimensions in the first call and synthesize after the result returns.`}(o,r),s=n?`${t}\n\n${n}`:t;return e?`${s}\n\nStable tool input schema:\n${previewToolOutput(JSON.stringify(e))}`:s}function previewToolOutput(t){const e=t.replace(/\s+/gu," ").trim();return e.length>500?`${e.slice(0,497)}...`:e}export function toolControlProjection(t){const e=function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}(t);return"string"==typeof e?.status?{controlStatus:e.status}:"string"==typeof e?.error?{controlStatus:e.error}:t.startsWith("Task delegation target is not in the workspace inventory")?{controlStatus:"task_inventory_blocked"}:{}}function stableToolCallKey(t,e){return`${t}:${stableJson(e)}`}function stableJson(t){return Array.isArray(t)?`[${t.map(stableJson).join(",")}]`:isRecord(t)?`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${stableJson(t[e])}`).join(",")}}`:JSON.stringify(t)}function formatToolArgumentError(t){return JSON.stringify({error:"tool_argument_validation_failed",toolId:t.toolId,issues:t.issues,retry:"Call the same tool again with arguments that satisfy the reported schema and semantic issues."})}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function formatError(t){return t instanceof Error?t.message:String(t)}
@@ -0,0 +1 @@
1
+ export function buildDeepAgentRequest(e){return{messages:buildDeepAgentMessages(e)}}function buildDeepAgentMessages(e){const t=function readOpenAiMessages(e){return Array.isArray(e)?e.flatMap(e=>{const t=function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{},n=function readRole(e){if("system"===e||"user"===e||"assistant"===e||"tool"===e)return e}(t.role),r="string"==typeof t.content?t.content:"";return n&&r?[{role:n,content:r}]:[]}):[]}(e.request.metadata?.openaiMessages),n="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function buildMemoryContext(e){const t=(e.pluginMemories??[]).filter(e=>e.context.trim()).map(e=>`Memory namespace: ${e.namespace}\n${e.context}`).join("\n\n");return t?`Relevant long-term memory:\n${t}`:void 0}(e);if(t.length>0){const r=function applyRecoveryPrompt(e,t){return"tool_call"!==t.request.metadata?.stableHarnessRecovery?e:[{role:"user",content:t.request.input}]}(t,e);return n?[{role:"system",content:n},...r]:r}const r={role:"user",content:e.request.input};return n?[{role:"system",content:n},r]:[r]}
@@ -0,0 +1,12 @@
1
+ import type { RuntimeAdapter } from "@stable-harness/core";
2
+ type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
3
+ type ModelRequest = {
4
+ tools?: Array<{
5
+ name?: unknown;
6
+ }>;
7
+ };
8
+ export declare function createRawToolCallParserMiddleware(input: AdapterRunInput): {
9
+ name: string;
10
+ wrapModelCall(request: ModelRequest, handler: (request: unknown) => Promise<unknown>): Promise<unknown>;
11
+ };
12
+ export {};
@@ -0,0 +1 @@
1
+ import{AIMessage as e}from"@langchain/core/messages";export function createRawToolCallParserMiddleware(t){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(n,r){const o=await r(n);if(!function rawToolCallParsingEnabled(e){const t=isRecord(e.workspace.runtime.recovery)?e.workspace.runtime.recovery:{};return!0===(isRecord(t.toolCall)?t.toolCall:{}).enabled}(t))return o;const a=function parseRawToolCallResponse(t,n){if(0===n.size||!function isModelResponse(e){return isRecord(e)}(t)||function hasToolCalls(e){return Array.isArray(e.tool_calls)&&e.tool_calls.length>0}(t))return;const r=function readContentText(e){return"string"==typeof e?e.trim()?e:void 0:Array.isArray(e)&&e.map(e=>isRecord(e)&&"string"==typeof e.text?e.text:"").join("\n").trim()||void 0}(t.content);if(!r)return;const o=function readRawToolCalls(e,t){const n=readXmlToolCalls(e,t);if(n.length>0)return n;const r=function stripToolMarkup(e){return e.replace(/^\s*```(?:json)?/iu,"").replace(/```\s*$/iu,"").replace(/^\s*<\s*tool_call[^>]*>\s*/iu,"").replace(/\s*<\s*\/\s*tool_call\s*>\s*$/iu,"").trim()}(e),o=readXmlToolCalls(r,t);if(o.length>0)return o;const a=function readFunctionToolCalls(e,t){const n=[];for(const r of t){const t=new RegExp(`(?:^|\\s)${escapeRegexp(r)}\\s*\\(([^)]*)\\)`,"giu");for(const o of e.matchAll(t)){const e=readFunctionArgs(o[1]??"");e&&Object.keys(e).length>0&&n.push({id:`stable-harness-${r}-${Date.now().toString(36)}`,name:r,args:e})}}return n}(r,t);return a.length>0?a:function parseJsonObjects(e){const t=[];let n=e.indexOf("{");for(;n>=0;){const r=readBalancedJsonObject(e,n);if(r){try{const e=JSON.parse(r);isRecord(e)&&t.push(e)}catch{}n=e.indexOf("{",n+r.length)}else n=e.indexOf("{",n+1)}return t}(r).flatMap(e=>function readJsonToolCall(e,t){const n=function readNestedToolCall(e,t){const n=Object.entries(e);if(1!==n.length)return;const[r,o]=n[0];return t.has(r)&&isRecord(o)?{id:`stable-harness-${r}-${Date.now().toString(36)}`,name:r,args:o}:void 0}(e,t);if(n)return n;const r=readString(e.name)??readString(e.tool_name)??readString(e.tool)??readString(e.type),o=t.has(r??"")?r:singleToolName(t),a=function readArgs(e){return isRecord(e.arguments)?e.arguments:isRecord(e.parameters)?e.parameters:isRecord(e.args)?e.args:"args"in e&&void 0!==e.args?{args:e.args}:void 0}(e)??function readInlineArgs(e){const t=Object.fromEntries(Object.entries(e).filter(([e])=>!["name","tool_name","tool","type"].includes(e)));return Object.keys(t).length>0?t:void 0}(e);return o&&t.has(o)&&a?{id:`stable-harness-${o}-${Date.now().toString(36)}`,name:o,args:a}:void 0}(e,t)??[])}(r,n);return 0!==o.length?new e({content:"",id:"string"==typeof t.id?t.id:void 0,name:"string"==typeof t.name?t.name:void 0,additional_kwargs:isRecord(t.additional_kwargs)?t.additional_kwargs:{},response_metadata:isRecord(t.response_metadata)?t.response_metadata:{},tool_calls:o.map(e=>({id:e.id,name:e.name,args:e.args,type:"tool_call"}))}):void 0}(o,function visibleToolNames(e){return new Set((e.tools??[]).map(e=>e.name).filter(e=>"string"==typeof e&&e.length>0))}(n));return a??o}}}function readXmlToolCalls(e,t){const n=[],r=/<\s*(tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*\1\s*>)?/giu;for(const o of e.matchAll(r)){const e=readXmlToolCall(o[0],t);e&&n.push(e)}return n}function readXmlToolCall(e,t){const n=function readToolCallAttributes(e){const t=/<\s*(?:tool_call|tool_code)\b([^>]*)>/iu.exec(e);if(!t)return;const n=[...t[1].matchAll(/([a-zA-Z_][\w.-]*)\s*=\s*"([^"]*)"/gu)];return n.length>0?Object.fromEntries(n.map(e=>[e[1],e[2]])):void 0}(e),r=readString(n?.name)??readString(n?.tool_name)??readString(n?.tool)??readXmlTag(e,"tool_name")??readXmlTag(e,"name")??readXmlTag(e,"tool"),o=t.has(r??"")?r:singleToolName(t);if(!o||!t.has(o))return;const a=readXmlTag(e,"tool_args")??readXmlTag(e,"args")??readXmlTag(e,"arguments"),s=a?function readXmlArgs(e){const t=[...e.matchAll(/<\s*([a-zA-Z_][\w.-]*)\s*>([\s\S]*?)<\s*\/\s*\1\s*>/gu)];if(0===t.length){const t=e.trim();return t?{input:t}:void 0}return Object.fromEntries(t.map(e=>[e[1],e[2].trim()]))}(a):function readAttributeArgs(e){if(!e)return;const t=Object.fromEntries(Object.entries(e).filter(([e])=>!["name","tool_name","tool","type"].includes(e)));return Object.keys(t).length>0?t:void 0}(n);return s&&0!==Object.keys(s).length?{id:`stable-harness-${o}-${Date.now().toString(36)}`,name:o,args:s}:void 0}function readXmlTag(e,t){const n=new RegExp(`<\\s*${t}\\s*>([\\s\\S]*?)<\\s*\\/\\s*${t}\\s*>`,"iu").exec(e);return n?.[1]?.trim()||void 0}function readFunctionArgs(e){const t=[...e.matchAll(/([a-zA-Z_][\w.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^,\s]+))/gu)];return t.length>0?Object.fromEntries(t.map(e=>[e[1],e[2]??e[3]??e[4]??""])):void 0}function readBalancedJsonObject(e,t){let n=0,r=!1,o=!1;for(let a=t;a<e.length;a+=1){const s=e[a];if(r)o="\\"===s&&!o,'"'!==s||o||(r=!1),"\\"!==s&&(o=!1);else if('"'===s&&(r=!0),"{"===s&&(n+=1),"}"===s&&(n-=1),0===n)return e.slice(t,a+1)}}function singleToolName(e){return 1===e.size?[...e][0]:void 0}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}