stable-harness 0.0.17 → 0.0.18

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.
@@ -53,6 +53,12 @@ Recovery logic may react to structured adapter events only through typed
53
53
  runtime config. Core recovery code must not know backend tool names, downstream
54
54
  error strings, or application-specific retry instructions.
55
55
 
56
+ Runtime control states are not evidence. Repeat limits, schema repair failures,
57
+ authorization blocks, and incomplete-evidence markers must be preserved as
58
+ blockers or evidence gaps in downstream synthesis. Do not instruct agents to
59
+ turn these states into a best-effort factual answer, estimate missing values, or
60
+ fill gaps from generic knowledge.
61
+
56
62
  Workspace YAML must not default an agent to a specific backend. `Agent.spec.backend`
57
63
  is required so scaffolded examples can choose DeepAgents first without making
58
64
  the generic schema DeepAgents-shaped.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -78,7 +78,7 @@
78
78
  "packages/*"
79
79
  ],
80
80
  "dependencies": {
81
- "@botbotgo/better-call": "^0.1.18",
81
+ "@botbotgo/better-call": "^0.1.54",
82
82
  "@langchain/core": "^1.1.43",
83
83
  "@langchain/langgraph": "^1.3.0",
84
84
  "@langchain/langgraph-api": "^1.2.1",
@@ -1 +1 @@
1
- import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as o,normalizeFilesystemArgs as n,normalizeWriteTodosArgs as r,shallowEqualRecord as i}from"./builtin-args.js";import{afterToolInvoke as a,beforeToolInvoke as l,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 m=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),f=new Set(["write_todos","task",...m]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,n){const r=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,r);return n({...o,tools:r,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,o,n){if(isFilesystemDisabled(e)&&m.has(o))return new t({tool_call_id:n.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||!f.has(h))return p(s);const y=function normalizeBuiltinToolRequest(e,t,a){return"write_todos"===t?{...a,toolCall:{...a.toolCall,args:r(a.toolCall?.args)}}:"task"===t?{...a,toolCall:{...a.toolCall,args:o(a.toolCall?.args)}}:m.has(t)?function normalizeFilesystemToolRequest(e,t,r){const a=o(r.toolCall?.args),l=n(t,a,e.workspace.root);return i(a,l)?r:{...r,toolCall:{...r.toolCall,args:l}}}(e,t,a):a}(e,h,s);emitToolEvent(e,h,"agent.tool.start",y.toolCall?.args);const v=g?l(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 n=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(o.toolCall?.args),r=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const o=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===o?[]:Array.isArray(o)?o.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(e);if(void 0===r||n&&r.includes(n))return;const i=n?`: ${n}`:"",a=r.length>0?r.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: ${a}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}(e,y):void 0;if(b)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:b.content}),b;const w=validateFilesystemBuiltinCall(e,h,y);if(w)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:w.content}),w;const T=d(e,h,y);if(T)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:T.content}),T;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),n=g?a(h,y.toolCall?.args,o,t,g):{};return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:n.eventOutput??o}),c.observedToolIds?.add(h),void 0===n.modelOutput?t:builtinToolMessage(s,h,n.modelOutput)}catch(o){const n=function recoverableBuiltinToolError(e,o,n){const r=formatError(n);return"task"===e&&/repeat limit reached for tool/iu.test(r)?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(r)})}):/Received tool input did not match expected schema|Invalid input:/iu.test(r)?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(r)})}):void 0}(h,s,o);if(n)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:n.content}),n;throw emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{error:formatError(o)}),o}}}}function builtinToolMessage(e,o,n){return new t({tool_call_id:e.toolCall?.id??`stable-harness-${o}-repeat-guard`,name:o,content:n})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),n=[];if(n.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,n=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...n?[n,`${n}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return n.length>0?n:void 0}else n.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),n.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||n.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),n.length>0?n:void 0}function emitToolEvent(e,t,o,n,r={}){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:n}:{},...r,..."string"==typeof r.output?c(r.output):{},...p(t,o,n)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),n=t.split("/").filter(Boolean);if(!(n.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==n[e])return;return n.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&m.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
1
+ import{realpathSync as e}from"node:fs";import{repairCallSelection as t}from"@botbotgo/better-call";import{ToolMessage as o}from"@langchain/core/messages";import{normalizeArgsRecord as n,normalizeFilesystemArgs as r,normalizeWriteTodosArgs as i,shallowEqualRecord as a}from"./builtin-args.js";import{afterToolInvoke as s,beforeToolInvoke as l,createToolRepeatState as u,stringifyDeepAgentResult as c,toolControlProjection as d}from"./gateway-tools.js";import{validateSkillFileBuiltinCall as g}from"./skill-file-policy.js";import{filterRepeatLimitedTools as p}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as f}from"./trace-projection.js";const m=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),h=new Set(["write_todos","task",...m]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,n){const r=Array.isArray(o.tools)?p(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,r);return n({...o,tools:r,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,t,n){if(isFilesystemDisabled(e)&&m.has(t))return new o({tool_call_id:n.toolCall?.id??`stable-harness-${t}-policy`,name:t,content:`Filesystem builtin tool '${t}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,d={}){const p=d.repeatState??u(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(u,f){const y=u.toolCall?.name;if(!y||!h.has(y))return f(u);const b=function normalizeBuiltinToolRequest(e,t,o){return"write_todos"===t?{...o,toolCall:{...o.toolCall,args:i(o.toolCall?.args)}}:"task"===t?{...o,toolCall:{...o.toolCall,args:n(o.toolCall?.args)}}:m.has(t)?function normalizeFilesystemToolRequest(e,t,o){const i=n(o.toolCall?.args),s=r(t,i,e.workspace.root);return a(i,s)?o:{...o,toolCall:{...o.toolCall,args:s}}}(e,t,o):o}(e,y,u);emitToolEvent(e,y,"agent.tool.start",b.toolCall?.args);const v=p?l(y,b.toolCall?.args,p):void 0;if(v)return emitToolEvent(e,y,"agent.tool.result",b.toolCall?.args,{output:v.eventOutput}),builtinToolMessage(u,y,v.modelOutput);const C="task"===y?await async function repairTaskCall(e,r){const i=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const o=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===o?[]:Array.isArray(o)?o.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(e);if(void 0===i)return{request:r};const a=n(r.toolCall?.args),s=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(a);if(s&&i.includes(s))return{request:r};const l=await t({call:{id:s,args:a},candidates:taskCallCandidates(e,i),mode:"repair"});if(l.ok){const e={...a,...l.args,subagent_type:l.candidateId};return{request:{...r,toolCall:{...r.toolCall,args:e}}}}const u=s?`: ${s}`:"",c=i.length>0?i.join(", "):"none";return{request:r,blocked:new o({tool_call_id:r.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${u}. Allowed task targets: ${c}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}(e,b):{request:b},k=C.request,w=C.blocked;if(w)return emitToolEvent(e,y,"agent.tool.result",k.toolCall?.args,{output:w.content}),w;const T=validateFilesystemBuiltinCall(e,y,k);if(T)return emitToolEvent(e,y,"agent.tool.result",k.toolCall?.args,{output:T.content}),T;const R=g(e,y,k);if(R)return emitToolEvent(e,y,"agent.tool.result",k.toolCall?.args,{output:R.content}),R;try{const t=await f(k),o=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):c(o)}(y,k,t),n=p?s(y,k.toolCall?.args,o,t,p):{};return emitToolEvent(e,y,"agent.tool.result",k.toolCall?.args,{output:n.eventOutput??o}),d.observedToolIds?.add(y),void 0===n.modelOutput?t:builtinToolMessage(u,y,n.modelOutput)}catch(t){const n=function recoverableBuiltinToolError(e,t,n){const r=formatError(n);return"task"===e&&/repeat limit reached for tool/iu.test(r)?new o({tool_call_id:t.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need and do not send a synthesis task to another subagent for the same need. Finalize only from evidence that is already visible in this run. If the visible evidence is insufficient for a requested claim, report an explicit blocker or evidence gap instead of estimating, inventing, or using generic knowledge.",error:previewError(r)})}):/Received tool input did not match expected schema|Invalid input:/iu.test(r)?new o({tool_call_id:t.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(r)})}):void 0}(y,u,t);if(n)return emitToolEvent(e,y,"agent.tool.result",b.toolCall?.args,{output:n.content}),n;throw emitToolEvent(e,y,"agent.tool.result",b.toolCall?.args,{error:formatError(t)}),t}}}}function builtinToolMessage(e,t,n){return new o({tool_call_id:e.toolCall?.id??`stable-harness-${t}-repeat-guard`,name:t,content:n})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),n=[];if(n.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,n=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...n?[n,`${n}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return n.length>0?n:void 0}else n.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),n.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||n.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),n.length>0?n:void 0}function emitToolEvent(e,t,o,n,r={}){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:n}:{},...r,..."string"==typeof r.output?d(r.output):{},...f(t,o,n)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),n=t.split("/").filter(Boolean);if(!(n.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==n[e])return;return n.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&m.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
@@ -10,6 +10,7 @@
10
10
  "main": "dist/src/index.js",
11
11
  "types": "dist/src/index.d.ts",
12
12
  "dependencies": {
13
+ "@botbotgo/better-call": "^0.1.54",
13
14
  "@langchain/core": "^1.1.43",
14
15
  "@langchain/ollama": "^1.2.7",
15
16
  "@langchain/openai": "^1.4.5",