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.
|
|
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.
|
|
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{
|
|
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}
|