stable-harness 0.0.127 → 0.0.129

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 (29) hide show
  1. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin/permissions.js +1 -1
  2. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -1
  3. package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
  4. package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
  5. package/node_modules/@stable-harness/core/dist/runtime/recovery/adapter-result.js +1 -1
  6. package/node_modules/@stable-harness/core/package.json +3 -3
  7. package/node_modules/@stable-harness/governance/package.json +1 -1
  8. package/node_modules/@stable-harness/memory/dist/src/provider.d.ts +1 -0
  9. package/node_modules/@stable-harness/memory/dist/src/provider.js +1 -1
  10. package/node_modules/@stable-harness/memory/package.json +1 -1
  11. package/node_modules/@stable-harness/protocols/package.json +2 -2
  12. package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
  13. package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
  14. package/package.json +9 -9
  15. package/packages/adapter-deepagents/dist/src/internal/builtin/permissions.js +1 -1
  16. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -1
  17. package/packages/adapter-deepagents/package.json +2 -2
  18. package/packages/adapter-langgraph/package.json +2 -2
  19. package/packages/cli/package.json +8 -8
  20. package/packages/core/dist/runtime/recovery/adapter-result.js +1 -1
  21. package/packages/core/package.json +3 -3
  22. package/packages/evaluation/package.json +2 -2
  23. package/packages/governance/package.json +1 -1
  24. package/packages/memory/dist/src/provider.d.ts +1 -0
  25. package/packages/memory/dist/src/provider.js +1 -1
  26. package/packages/memory/package.json +1 -1
  27. package/packages/protocols/package.json +2 -2
  28. package/packages/tool-gateway/package.json +1 -1
  29. package/packages/workspace-yaml/package.json +2 -2
@@ -1 +1 @@
1
- import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";export const filesystemBuiltinToolIds=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export function validateFilesystemBuiltinCall(e,i,o){if(isFilesystemDisabled(e)&&filesystemBuiltinToolIds.has(i))return new t({tool_call_id:o.toolCall?.id??`stable-harness-${i}-policy`,name:i,content:`Filesystem builtin tool '${i}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function resolveFilesystemPermissions(e,t){const i=readConfigRecord(t?.config,"builtinTools"),o=[];if(o.push(...function skillReadPermissions(e,t){const i=[...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,""),i=t.endsWith("/SKILL.md")?t.slice(0,-9):t,o=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(i);return[t,i,`${i}/**`,...o?[o,`${o}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return i.length>0?[{operations:["read"],paths:i,mode:"allow"}]:[]}(e,t)),!1!==i?.filesystem){if(deepagentsMemoryWritable(e))return o.length>0?o:void 0}else o.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),o.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||o.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),o.length>0?o:void 0}export function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}export function isFilesystemTool(e){return"string"==typeof e&&filesystemBuiltinToolIds.has(e)}function backendSkillPath(e,t){const i=function pathRelative(e,t){const i=e.split("/").filter(Boolean),o=t.split("/").filter(Boolean);if(!(o.length<i.length)){for(let e=0;e<i.length;e+=1)if(i[e]!==o[e])return;return o.slice(i.length).join("/")}}(e,t);return void 0===i?t:`/${i.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 readConfigRecord(e,t){const i=isRecord(e)?e:{};return isRecord(i[t])?i[t]:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";export const filesystemBuiltinToolIds=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export function validateFilesystemBuiltinCall(e,n,i){if(isFilesystemDisabled(e)&&filesystemBuiltinToolIds.has(n))return new t({tool_call_id:i.toolCall?.id??`stable-harness-${n}-policy`,name:n,content:`Filesystem builtin tool '${n}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function resolveFilesystemPermissions(e,t){const n=readConfigRecord(t?.config,"builtinTools");if(!1===n?.filesystem)return;const i=[];return i.push(...function skillReadPermissions(e,t){const n=[...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,""),n=t.endsWith("/SKILL.md")?t.slice(0,-9):t,i=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(n);return[t,n,`${n}/**`,...i?[i,`${i}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return n.length>0?[{operations:["read"],paths:n,mode:"allow"}]:[]}(e,t)),deepagentsMemoryWritable(e)||deepagentsMemoryWritable(e)||i.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),i.length>0?i:void 0}export function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}export function isFilesystemTool(e){return"string"==typeof e&&filesystemBuiltinToolIds.has(e)}function backendSkillPath(e,t){const n=function pathRelative(e,t){const n=e.split("/").filter(Boolean),i=t.split("/").filter(Boolean);if(!(i.length<n.length)){for(let e=0;e<n.length;e+=1)if(n[e]!==i[e])return;return i.slice(n.length).join("/")}}(e,t);return void 0===n?t:`/${n.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 readConfigRecord(e,t){const n=isRecord(e)?e:{};return isRecord(n[t])?n[t]:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- import{AIMessage as t}from"@langchain/core/messages";import{parseToolIntent as e,repairCallSelection as o}from"@easynet/better-call";const n=new Set(["edit_file","glob","ls","read_file"]);export function createRawToolCallParserMiddleware(e){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(o,n){const s=await n(o);if(!function rawToolCallParsingEnabled(t){const e=isRecord(t.workspace.runtime.recovery)?t.workspace.runtime.recovery:{};return!0===(isRecord(e.toolCall)?e.toolCall:{}).enabled}(e))return s;const r=await async function parseRawToolCallResponse(e,o){if(0===o.length||!function isModelResponse(t){return isRecord(t)}(e)||function hasToolCalls(t){return Array.isArray(t.tool_calls)&&t.tool_calls.length>0}(e))return;const n=function readContentText(t){return"string"==typeof t?t.trim()?t:void 0:Array.isArray(t)&&t.map(t=>isRecord(t)&&"string"==typeof t.text?t.text:"").join("\n").trim()||void 0}(e.content);if(!n)return;const s=await async function readRawToolCalls(t,e){const o=[],n=new Set,s=new Set(e.map(t=>t.id));for(const r of function rawToolIntentCandidates(t,e){const o=[t.trim()];for(const e of t.matchAll(/```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n```/gu))o.push(e[1].trim());for(const n of t.split(/\r?\n/u)){const t=n.trim();e.has(t)&&o.push(t)}for(const e of t.matchAll(/<\s*(?:tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*(?:tool_call|tool_code)\s*>)?/giu))o.push(e[0]);for(const e of t.matchAll(/<\s*call_tool\b[^>]*>[\s\S]*?<\s*\/\s*call_tool\s*>/giu)){o.push(e[0]);const t=parseCallToolTags(e[0]);t&&o.push(t)}for(const n of e){const e=new RegExp(`(?:^|[\\s<])${escapeRegexp(n)}\\s*\\([^)]*\\)\\s*>?`,"giu");for(const n of t.matchAll(e))o.push(n[0].trim())}return o.push(...function parseJsonObjects(t){const e=[];let o=t.indexOf("{");for(;o>=0;){const n=readBalancedJsonObject(t,o);if(n){try{const t=JSON.parse(n);isRecord(t)&&e.push(t)}catch{}o=t.indexOf("{",o+n.length)}else o=t.indexOf("{",o+1)}return e}(t)),o}(t,s)){const t=await parseCandidate(r,e);if(!t||!s.has(t.tool))continue;const a=normalizeToolArgs(t.args),i=`${t.tool}:${JSON.stringify(a)}`;n.has(i)||(n.add(i),o.push({id:`stable-harness-${t.tool}-${Date.now().toString(36)}`,name:t.tool,args:a}))}return o}(n,o);return 0!==s.length?new t({content:"",id:"string"==typeof e.id?e.id:void 0,name:"string"==typeof e.name?e.name:void 0,additional_kwargs:isRecord(e.additional_kwargs)?e.additional_kwargs:{},response_metadata:isRecord(e.response_metadata)?e.response_metadata:{},tool_calls:s.map(t=>({id:t.id,name:t.name,args:t.args,type:"tool_call"}))}):void 0}(s,function visibleToolCandidates(t,e){return(e.tools??[]).flatMap(readToolName).map(e=>{const o=t.workspace.tools.get(e),n=t.toolGateway?.get(e);return{id:e,description:o?.description??n?.description,schema:o?.schema??n?.schema}})}(e,o));return r??s}}}async function parseCandidate(t,s){const r=new Set(s.map(t=>t.id));if("string"==typeof t){const e=function parseExecuteToolWrapper(t,e){const o=t.trim().match(/^<?\s*execute\s*\(([\s\S]*)\)\s*>?$/iu);if(!o)return;const n=function parseFunctionKwargs(t){const e={};for(const o of function splitTopLevelCommas(t){const e=[];let o,n=0,s=0,r=!1;for(let a=0;a<t.length;a+=1){const i=t[a];o?(r="\\"===i&&!r,i!==o||r||(o=void 0),"\\"!==i&&(r=!1)):'"'!==i&&"'"!==i?("("!==i&&"["!==i&&"{"!==i||(s+=1),")"!==i&&"]"!==i&&"}"!==i||(s-=1),","===i&&0===s&&(e.push(t.slice(n,a).trim()),n=a+1)):o=i}const a=t.slice(n).trim();return a&&e.push(a),e}(t)){const t=o.indexOf("=");if(t<=0)continue;const n=o.slice(0,t).trim();/^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(n)&&(e[n]=parseFunctionValue(o.slice(t+1).trim()))}return e}(o[1]??""),s=n.tool??n.tool_name??n.name;if("string"!=typeof s||!e.has(s))return;const r={...n};return delete r.tool,delete r.tool_name,delete r.name,isRecord(r.args)&&1===Object.keys(r).length?{tool:s,args:r.args}:{tool:s,args:r}}(t,r);if(e)return e}const a=e(t);if(a)return a;const i=function singleToolName(t){return 1===t.size?[...t][0]:void 0}(r);if(i&&isRecord(t))return e({tool:i,args:t});const c=isRecord(t)?await o({call:{name:"__stable_harness_raw_args__",args:t},candidates:s,mode:"repair"}):void 0;if(c?.ok)return{tool:c.candidateId,args:c.args};const l=isRecord(t)?await async function selectUniqueToolByArgs(t,e){const n=[];for(const s of e){const r=await o({call:{name:s.id,args:t},candidates:e,mode:"guard"});r.ok&&r.candidateId===s.id&&n.push({tool:r.candidateId,args:r.args})}return 1===n.length?n[0]:void 0}(t,s):void 0;if(l)return l;if("string"!=typeof t)return;const u=await o({candidates:s,rawIntent:t,mode:"guard"});if(u.ok)return{tool:u.candidateId,args:u.args??{}};const d=function parseFunctionStyleToolName(t,e){const o=t.trim();for(const t of e)if(!n.has(t)&&new RegExp(`^<?\\s*${escapeRegexp(t)}\\s*\\([\\s\\S]*\\)\\s*>?$`,"iu").test(o))return t}(t,r);return d?{tool:d,args:{}}:void 0}function parseFunctionValue(t){if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1).replace(/\\(["'\\])/gu,"$1");if(/^-?\d+(?:\.\d+)?$/u.test(t))return Number(t);if("true"===t)return!0;if("false"===t)return!1;if("null"===t)return null;try{return JSON.parse(t)}catch{return t}}function normalizeToolArgs(t){return isRecord(t)?t:{}}function parseCallToolTags(t){const e=t.match(/<\s*tool\s*>([\s\S]*?)<\s*\/\s*tool\s*>/iu)?.[1]?.trim(),o=t.match(/<\s*tool_args\s*>([\s\S]*?)<\s*\/\s*tool_args\s*>/iu)?.[1]?.trim();if(e&&o)try{const t=JSON.parse(o);return isRecord(t)?{tool:e,args:t}:{tool:e,args:{}}}catch{return{tool:e,args:{}}}}function readBalancedJsonObject(t,e){let o=0,n=!1,s=!1;for(let r=e;r<t.length;r+=1){const a=t[r];if(n)s="\\"===a&&!s,'"'!==a||s||(n=!1),"\\"!==a&&(s=!1);else if('"'===a&&(n=!0),"{"===a&&(o+=1),"}"===a&&(o-=1),0===o)return t.slice(e,r+1)}}function escapeRegexp(t){return t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readToolName(t){const e="string"==typeof t.name&&t.name.length>0?t.name:void 0;if(e)return[e];const o=t,n=isRecord(o.function)&&"string"==typeof o.function.name?o.function.name:void 0;return n&&n.length>0?[n]:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
1
+ import{AIMessage as t}from"@langchain/core/messages";import{parseToolIntent as e,repairCallSelection as o}from"@easynet/better-call";export function createRawToolCallParserMiddleware(e){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(o,n){const r=await n(o);if(!function rawToolCallParsingEnabled(t){const e=isRecord(t.workspace.runtime.recovery)?t.workspace.runtime.recovery:{};return!0===(isRecord(e.toolCall)?e.toolCall:{}).enabled}(e))return r;const s=await async function parseRawToolCallResponse(e,o){if(0===o.length||!function isModelResponse(t){return isRecord(t)}(e)||function hasToolCalls(t){return Array.isArray(t.tool_calls)&&t.tool_calls.length>0}(e))return;const n=function readContentText(t){return"string"==typeof t?t.trim()?t:void 0:Array.isArray(t)&&t.map(t=>isRecord(t)&&"string"==typeof t.text?t.text:"").join("\n").trim()||void 0}(e.content);if(!n)return;const r=await async function readRawToolCalls(t,e){const o=[],n=new Set,r=new Set(e.map(t=>t.id));for(const s of function rawToolIntentCandidates(t,e){const o=[t.trim()];for(const e of t.matchAll(/```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n```/gu))o.push(e[1].trim());for(const e of t.matchAll(/<\s*(?:tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*(?:tool_call|tool_code)\s*>)?/giu))o.push(e[0]);for(const e of t.matchAll(/<\s*call_tool\b[^>]*>[\s\S]*?<\s*\/\s*call_tool\s*>/giu)){o.push(e[0]);const t=parseCallToolTags(e[0]);t&&o.push(t)}for(const n of e){const e=new RegExp(`(?:^|[\\s<])${escapeRegexp(n)}\\s*\\([^)]*\\)\\s*>?`,"giu");for(const n of t.matchAll(e))o.push(n[0].trim())}return o.push(...function parseJsonObjects(t){const e=[];let o=t.indexOf("{");for(;o>=0;){const n=readBalancedJsonObject(t,o);if(n){try{const t=JSON.parse(n);isRecord(t)&&e.push(t)}catch{}o=t.indexOf("{",o+n.length)}else o=t.indexOf("{",o+1)}return e}(t)),o}(t,r)){const t=await parseCandidate(s,e);if(!t||!r.has(t.tool))continue;const a=normalizeToolArgs(t.args),i=`${t.tool}:${JSON.stringify(a)}`;n.has(i)||(n.add(i),o.push({id:`stable-harness-${t.tool}-${Date.now().toString(36)}`,name:t.tool,args:a}))}return o}(n,o);return 0!==r.length?new t({content:"",id:"string"==typeof e.id?e.id:void 0,name:"string"==typeof e.name?e.name:void 0,additional_kwargs:isRecord(e.additional_kwargs)?e.additional_kwargs:{},response_metadata:isRecord(e.response_metadata)?e.response_metadata:{},tool_calls:r.map(t=>({id:t.id,name:t.name,args:t.args,type:"tool_call"}))}):void 0}(r,function visibleToolCandidates(t,e){return(e.tools??[]).flatMap(readToolName).map(e=>{const o=t.workspace.tools.get(e),n=t.toolGateway?.get(e);return{id:e,description:o?.description??n?.description,schema:o?.schema??n?.schema}})}(e,o));return s??r}}}async function parseCandidate(t,n){const r=new Set(n.map(t=>t.id));if("string"==typeof t){if(function isBareToolMarkup(t,e){const o=t.trim().replace(/^<\s*tool_code\b[^>]*>/iu,"").replace(/<\s*\/\s*tool_code\s*>$/iu,"").replace(/^```\w*\s*/u,"").replace(/```$/u,"").trim();return e.has(o)}(t,r))return;const e=function parseExecuteToolWrapper(t,e){const o=t.trim().match(/^<?\s*execute\s*\(([\s\S]*)\)\s*>?$/iu);if(!o)return;const n=function parseFunctionKwargs(t){const e={};for(const o of function splitTopLevelCommas(t){const e=[];let o,n=0,r=0,s=!1;for(let a=0;a<t.length;a+=1){const i=t[a];o?(s="\\"===i&&!s,i!==o||s||(o=void 0),"\\"!==i&&(s=!1)):'"'!==i&&"'"!==i?("("!==i&&"["!==i&&"{"!==i||(r+=1),")"!==i&&"]"!==i&&"}"!==i||(r-=1),","===i&&0===r&&(e.push(t.slice(n,a).trim()),n=a+1)):o=i}const a=t.slice(n).trim();return a&&e.push(a),e}(t)){const t=o.indexOf("=");if(t<=0)continue;const n=o.slice(0,t).trim();/^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(n)&&(e[n]=parseFunctionValue(o.slice(t+1).trim()))}return e}(o[1]??""),r=n.tool??n.tool_name??n.name;if("string"!=typeof r||!e.has(r))return;const s={...n};return delete s.tool,delete s.tool_name,delete s.name,isRecord(s.args)&&1===Object.keys(s).length?{tool:r,args:s.args}:{tool:r,args:s}}(t,r);if(e)return e}const s=e(t);if(s)return s;const a=n.filter(hasSchema),i=isRecord(t)&&a.length>0?await o({call:{name:"__stable_harness_raw_args__",args:t},candidates:a,mode:"repair"}):void 0;if(i?.ok)return{tool:i.candidateId,args:i.args};const c=isRecord(t)?await async function selectUniqueToolByArgs(t,e){const n=[];for(const r of e){const s=await o({call:{name:r.id,args:t},candidates:e,mode:"guard"});s.ok&&s.candidateId===r.id&&n.push({tool:s.candidateId,args:s.args})}return 1===n.length?n[0]:void 0}(t,a):void 0;return c||void 0}function hasSchema(t){return void 0!==t.schema}function parseFunctionValue(t){if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1).replace(/\\(["'\\])/gu,"$1");if(/^-?\d+(?:\.\d+)?$/u.test(t))return Number(t);if("true"===t)return!0;if("false"===t)return!1;if("null"===t)return null;try{return JSON.parse(t)}catch{return t}}function normalizeToolArgs(t){return isRecord(t)?t:{}}function parseCallToolTags(t){const e=t.match(/<\s*tool\s*>([\s\S]*?)<\s*\/\s*tool\s*>/iu)?.[1]?.trim(),o=t.match(/<\s*tool_args\s*>([\s\S]*?)<\s*\/\s*tool_args\s*>/iu)?.[1]?.trim();if(e&&o)try{const t=JSON.parse(o);return isRecord(t)?{tool:e,args:t}:{tool:e,args:{}}}catch{return{tool:e,args:{}}}}function readBalancedJsonObject(t,e){let o=0,n=!1,r=!1;for(let s=e;s<t.length;s+=1){const a=t[s];if(n)r="\\"===a&&!r,'"'!==a||r||(n=!1),"\\"!==a&&(r=!1);else if('"'===a&&(n=!0),"{"===a&&(o+=1),"}"===a&&(o-=1),0===o)return t.slice(e,s+1)}}function escapeRegexp(t){return t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readToolName(t){const e="string"==typeof t.name&&t.name.length>0?t.name:void 0;if(e)return[e];const o=t,n=isRecord(o.function)&&"string"==typeof o.function.name?o.function.name:void 0;return n&&n.length>0?[n]:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -16,7 +16,7 @@
16
16
  "@langchain/node-vfs": "^0.1.4",
17
17
  "@langchain/ollama": "^1.2.7",
18
18
  "@langchain/openai": "^1.4.5",
19
- "@stable-harness/core": "0.0.127",
19
+ "@stable-harness/core": "0.0.129",
20
20
  "deepagents": "^1.10.1",
21
21
  "langchain": "^1.4.0"
22
22
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-langgraph",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,6 +12,6 @@
12
12
  "types": "dist/src/index.d.ts",
13
13
  "peerDependencies": {
14
14
  "@langchain/langgraph": "^1.3.0",
15
- "@stable-harness/core": "0.0.127"
15
+ "@stable-harness/core": "0.0.129"
16
16
  }
17
17
  }
@@ -1 +1 @@
1
- import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{containsProgressOnlyToolIntent as t}from"../../recovery/progress-intent.js";import{buildRawArgsRecoveryRequest as o,buildRawArgsToolEvidenceRecoveryRequest as r,buildRawArgsToolSequenceEvidenceRecoveryRequest as a,hasUniqueRawArgsTool as s,matchRawArgsToolSequence as n,matchUniqueRawArgsTool as l}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as i,assertNoRawToolCallOutput as u,assertNoRawToolResultOutput as c,assertNoStructurallyIncompleteFinalOutput as d,assertNoToolExecutionErrorOutput as y,buildEvidenceSynthesisOutput as p,buildResultRecoveryRequest as v,containsRawToolCallOutput as g,rawToolCallFailureMessage as m,rawToolCallOutputPreview as f,toolCallRecoveryEnabled as w}from"../../recovery/tool-call.js";import{controlGaps as R}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(t){let r=t.result,a=t.request;const n=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(t.recoveryPolicy),R=new Set,T=new Set;for(let e=0;;e+=1){const s=t.store.getRun(t.requestId)?.events??[],i=effectiveRecoveryToolIds(a,t.agent.tools);assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,i);const u=await recoverRawArgsSequenceByToolGateway(t,a,r,T,e+1,i);if(u){a=u.request,r=u.result;continue}const c=l({output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:i,policy:t.recoveryPolicy}),d=c?rawArgsToolKey(c):void 0;if(c&&d&&T.size>0&&!T.has(d)){const o=await recoverRawArgsByToolGateway(t,a,r,c,T,e+1);if(o){a=o.request,r=o.result;continue}}const y=d&&!R.has(d)?o({request:a,output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:i,policy:t.recoveryPolicy}):void 0,p=e<n?v({request:a,output:r.text,events:s,availableToolIds:i,policy:t.recoveryPolicy}):void 0,g=y??p;if(!g)break;if(g===y&&R.add(d),a=g,emitRepair(t,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(r.text,i)),r=await t.runAdapter(g),emitRepair(t,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(r.text,i)),g===y){const o=await recoverRepeatedRawArgsByToolGateway(t,a,r,R,T,e+1);o&&(a=o.request,r=o.result)}}return function finalizeRecoveredOutput(t,o){if(!w(t.recoveryPolicy))return o;let r=!1;if(g(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:m(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const a=p({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return a&&(r=!0,o={...o,text:a,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),r||(g(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),u(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(s({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(e.request,e.agent.tools),policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${f(t)}`)}(t,o.text),i(o.text,t.agent.tools,t.recoveryPolicy),c(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),y(o.text,t.recoveryPolicy),d(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(t,r)}async function recoverRawArgsSequenceByToolGateway(e,t,o,r,s,l){if(!e.runRecoveredToolCall)return;const i=n({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:l,policy:e.recoveryPolicy});if(i.length<2||i.some(e=>r.has(rawArgsToolKey(e))))return;emitRepair(e,"runtime.repair.started","result_output",s,"raw_args_tool_sequence_gateway_recovery",void 0,repairDiagnostics(o.text,l));const u=[];for(const t of i){r.add(rawArgsToolKey(t));const o=await e.runRecoveredToolCall(t.toolId,t.args);u.push({match:t,toolOutput:visibleRecoveredToolOutput(o)})}const c=a({request:t,evidences:u}),d=await e.runAdapter(c),y=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolSequenceBlocker({evidences:u,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool_sequence"}}};return emitRepair(e,"runtime.repair.completed","result_output",s,"raw_args_tool_sequence_gateway_recovery","retried",repairDiagnostics(y.text,l)),{request:c,result:y}}async function recoverRepeatedRawArgsByToolGateway(e,t,o,r,a,s){const n=l({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(t,e.agent.tools),policy:e.recoveryPolicy});if(!n||!e.runRecoveredToolCall)return;const i=rawArgsToolKey(n);return r.has(i)&&!a.has(i)?recoverRawArgsByToolGateway(e,t,o,n,a,s):void 0}async function recoverRawArgsByToolGateway(e,t,o,a,s,n){if(!e.runRecoveredToolCall)return;const l=rawArgsToolKey(a);if(s.has(l))return;s.add(l),emitRepair(e,"runtime.repair.started","result_output",n,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(o.text,e.agent.tools));const i=visibleRecoveredToolOutput(await e.runRecoveredToolCall(a.toolId,a.args)),u=r({request:t,match:a,toolOutput:i}),c=await e.runAdapter(u),d=c.text.trim()?c:{...c,text:buildEmptyRecoveredToolBlocker({match:a,toolOutput:i,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...c.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",n,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(d.text,e.agent.tools)),{request:u,result:d}}function assertNoNonFocusedRecoveryIntent(e,o,r,a){if(0===a.length||a.length===r.length)return;const s=new Set(a),n=visibleToolCandidates(e,r).filter(e=>!s.has(e)),l=g(e,o)||t(e,r)||function containsJsonToolEnvelope(e){const t=e.trim(),o=t.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/iu)?.[1]?.trim(),r=o??t;if(!r.startsWith("{")||!r.endsWith("}")||r.length>6e3)return!1;try{const e=JSON.parse(r);if(!function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e))return!1;const t=["tool","tool_name","name","type","subagent_type"].some(t=>"string"==typeof e[t]),o=["args","arguments","parameters","kwargs"].some(t=>t in e);return t&&o}catch{return!1}}(e);if(n.length>0&&l)throw new Error(`Focused recovery output referenced non-focused tool(s): ${n.join(", ")}. The backend must call one of the focused gateway tools: ${a.join(", ")}.`)}function emitRepair(e,t,o,r,a,s,n){const l={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...n?{diagnostics:n}:{}};e.emit("runtime.repair.started"===t?{type:t,...l}:{type:t,...l,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:f(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])containsDelimitedToken(e,r)&&o.add(r);return[...o]}function containsDelimitedToken(e,t){if(!t)return!1;let o=e.indexOf(t);for(;o>=0;){const r=0===o?"":e[o-1],a=e[o+t.length]??"";if(!isTokenChar(r)&&!isTokenChar(a))return!0;o=e.indexOf(t,o+t.length)}return!1}function isTokenChar(e){return"_"===e||"-"===e||e>="A"&&e<="Z"||e>="a"&&e<="z"||e>="0"&&e<="9"}function effectiveRecoveryToolIds(e,t){const o=function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}(e.metadata?.stableHarnessRequiredEvidenceTools).filter(e=>t.includes(e));return o.length>0?o:t}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}function buildEmptyRecoveredToolBlocker(e){const t=R(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}function buildEmptyRecoveredToolSequenceBlocker(e){const t=R(e.events);return["Stable runtime recovery executed matched declared tools, but the backend returned no user-facing output after receiving the executed evidence.","",...e.evidences.flatMap((e,t)=>[`Executed tool ${t+1}: ${e.match.toolId}`,"Executed JSON arguments:",JSON.stringify(e.match.args),"Executed tool evidence:",e.toolOutput,""]),...t.length>0?["Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
1
+ import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{containsProgressOnlyToolIntent as t}from"../../recovery/progress-intent.js";import{buildRawArgsRecoveryRequest as o,buildRawArgsToolEvidenceRecoveryRequest as r,buildRawArgsToolSequenceEvidenceRecoveryRequest as a,hasUniqueRawArgsTool as s,matchRawArgsToolSequence as n,matchUniqueRawArgsTool as u}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as l,assertNoRawToolCallOutput as c,assertNoRawToolResultOutput as i,assertNoStructurallyIncompleteFinalOutput as d,assertNoToolExecutionErrorOutput as p,buildEvidenceSynthesisOutput as y,buildResultRecoveryRequest as v,containsRawToolCallOutput as g,rawToolCallFailureMessage as m,rawToolCallOutputPreview as f,toolCallRecoveryEnabled as w}from"../../recovery/tool-call.js";import{controlGaps as R,successfulEvidenceItems as h}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(t){let r=t.result,a=t.request;const n=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(t.recoveryPolicy),R=new Set,h=new Set;for(let e=0;;e+=1){const s=t.store.getRun(t.requestId)?.events??[],l=effectiveRecoveryToolIds(a,t.agent.tools),c=buildNonFocusedRecoveryRequest({request:a,output:r.text,events:s,policy:t.recoveryPolicy,declaredToolIds:t.agent.tools,visibleToolIds:l});if(c){e>=n&&assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,l,s),a=c,emitRepair(t,"runtime.repair.started","result_output",e+1,"non_focused_recovery_intent",void 0,repairDiagnostics(r.text,l)),r=await t.runAdapter(c),emitRepair(t,"runtime.repair.completed","result_output",e+1,"non_focused_recovery_intent","retried",repairDiagnostics(r.text,l));continue}assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,l,s);const i=await recoverRawArgsSequenceByToolGateway(t,a,r,h,e+1,l);if(i){a=i.request,r=i.result;continue}const d=u({output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:l,policy:t.recoveryPolicy}),p=d?rawArgsToolKey(d):void 0;if(d&&p&&h.size>0&&!h.has(p)){const o=await recoverRawArgsByToolGateway(t,a,r,d,h,e+1);if(o){a=o.request,r=o.result;continue}}const y=p&&!R.has(p)?o({request:a,output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:l,policy:t.recoveryPolicy}):void 0,g=e<n?v({request:a,output:r.text,events:s,availableToolIds:l,policy:t.recoveryPolicy}):void 0,m=y??g;if(!m)break;if(m===y&&R.add(p),a=m,emitRepair(t,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(r.text,l)),r=await t.runAdapter(m),emitRepair(t,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(r.text,l)),m===y){const o=await recoverRepeatedRawArgsByToolGateway(t,a,r,R,h,e+1);o&&(a=o.request,r=o.result)}}return function finalizeRecoveredOutput(t,o){if(!w(t.recoveryPolicy))return o;let r=!1;if(g(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:m(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const a=y({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return a&&(r=!0,o={...o,text:a,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),r||(g(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),c(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(s({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(e.request,e.agent.tools),policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${f(t)}`)}(t,o.text),l(o.text,t.agent.tools,t.recoveryPolicy),i(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),p(o.text,t.recoveryPolicy),d(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(t,r)}async function recoverRawArgsSequenceByToolGateway(e,t,o,r,s,u){if(!e.runRecoveredToolCall)return;const l=n({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:u,policy:e.recoveryPolicy});if(l.length<2||l.some(e=>r.has(rawArgsToolKey(e))))return;emitRepair(e,"runtime.repair.started","result_output",s,"raw_args_tool_sequence_gateway_recovery",void 0,repairDiagnostics(o.text,u));const c=[];for(const t of l){r.add(rawArgsToolKey(t));const o=await e.runRecoveredToolCall(t.toolId,t.args);c.push({match:t,toolOutput:visibleRecoveredToolOutput(o)})}const i=a({request:t,evidences:c}),d=await e.runAdapter(i),p=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolSequenceBlocker({evidences:c,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool_sequence"}}};return emitRepair(e,"runtime.repair.completed","result_output",s,"raw_args_tool_sequence_gateway_recovery","retried",repairDiagnostics(p.text,u)),{request:i,result:p}}async function recoverRepeatedRawArgsByToolGateway(e,t,o,r,a,s){const n=u({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(t,e.agent.tools),policy:e.recoveryPolicy});if(!n||!e.runRecoveredToolCall)return;const l=rawArgsToolKey(n);return r.has(l)&&!a.has(l)?recoverRawArgsByToolGateway(e,t,o,n,a,s):void 0}async function recoverRawArgsByToolGateway(e,t,o,a,s,n){if(!e.runRecoveredToolCall)return;const u=rawArgsToolKey(a);if(s.has(u))return;s.add(u),emitRepair(e,"runtime.repair.started","result_output",n,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(o.text,e.agent.tools));const l=visibleRecoveredToolOutput(await e.runRecoveredToolCall(a.toolId,a.args)),c=r({request:t,match:a,toolOutput:l}),i=await e.runAdapter(c),d=i.text.trim()?i:{...i,text:buildEmptyRecoveredToolBlocker({match:a,toolOutput:l,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...i.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",n,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(d.text,e.agent.tools)),{request:c,result:d}}function assertNoNonFocusedRecoveryIntent(e,t,o,r,a){const s=nonFocusedRecoveryIntent(e,t,o,r);if(!s)return;const n=new Set(h(a).map(e=>e.source)),u=s.nonFocused.filter(e=>!n.has(e)),l=u.length>0?u:s.nonFocused;throw new Error(`Focused recovery output referenced non-focused tool(s): ${l.join(", ")}. The backend must call one of the focused gateway tools: ${r.join(", ")}.`)}function buildNonFocusedRecoveryRequest(e){const t=nonFocusedRecoveryIntent(e.output,e.policy,e.declaredToolIds,e.visibleToolIds);if(!t)return;const o=h(e.events),r=new Set(o.map(e=>e.source));if(t.nonFocused.some(e=>!r.has(e)))return;const a=o.slice(-5).map(e=>`- ${e.source}: ${function previewRecoveredEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1e3?`${t.slice(0,997)}...`:t}(e.output)}`);return{...e.request,input:[e.request.input,"","Stable runtime recovery: the previous recovery output referenced tool(s) that already have successful evidence but are not allowed targets for this focused turn.",`Already completed non-focused tool(s): ${t.nonFocused.join(", ")}`,`Allowed focused tool target(s): ${e.visibleToolIds.join(", ")}`,"Reuse the prior successful evidence below to build one allowed focused tool call. Do not call already completed non-focused tools again.","The next assistant action must be the backend's structured tool call for one allowed focused target, with no prose before it.","","Prior successful tool evidence:",...a,"","Previous invalid recovery output:",e.output].join("\n"),metadata:{...e.request.metadata,stableHarnessRecovery:"tool_call"}}}function nonFocusedRecoveryIntent(e,o,r,a){if(0===a.length||a.length===r.length)return;const s=new Set(a),n=visibleToolCandidates(e,r).filter(e=>!s.has(e)),u=g(e,o)||t(e,r)||function containsJsonToolEnvelope(e){const t=e.trim(),o=t.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/iu)?.[1]?.trim(),r=o??t;if(!r.startsWith("{")||!r.endsWith("}")||r.length>6e3)return!1;try{const e=JSON.parse(r);if(!function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e))return!1;const t=["tool","tool_name","name","type","subagent_type"].some(t=>"string"==typeof e[t]),o=["args","arguments","parameters","params","kwargs"].some(t=>t in e);return t&&o}catch{return!1}}(e);return n.length>0&&u?{nonFocused:n}:void 0}function emitRepair(e,t,o,r,a,s,n){const u={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...n?{diagnostics:n}:{}};e.emit("runtime.repair.started"===t?{type:t,...u}:{type:t,...u,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:f(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])containsDelimitedToken(e,r)&&o.add(r);return[...o]}function containsDelimitedToken(e,t){if(!t)return!1;let o=e.indexOf(t);for(;o>=0;){const r=0===o?"":e[o-1],a=e[o+t.length]??"";if(!isTokenChar(r)&&!isTokenChar(a))return!0;o=e.indexOf(t,o+t.length)}return!1}function isTokenChar(e){return"_"===e||"-"===e||e>="A"&&e<="Z"||e>="a"&&e<="z"||e>="0"&&e<="9"}function effectiveRecoveryToolIds(e,t){const o=function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}(e.metadata?.stableHarnessRequiredEvidenceTools).filter(e=>t.includes(e));return o.length>0?o:t}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}function buildEmptyRecoveredToolBlocker(e){const t=R(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}function buildEmptyRecoveredToolSequenceBlocker(e){const t=R(e.events);return["Stable runtime recovery executed matched declared tools, but the backend returned no user-facing output after receiving the executed evidence.","",...e.evidences.flatMap((e,t)=>[`Executed tool ${t+1}: ${e.match.toolId}`,"Executed JSON arguments:",JSON.stringify(e.match.args),"Executed tool evidence:",e.toolOutput,""]),...t.length>0?["Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,7 +12,7 @@
12
12
  ".": "./dist/index.js"
13
13
  },
14
14
  "peerDependencies": {
15
- "@stable-harness/governance": "0.0.127",
16
- "@stable-harness/memory": "0.0.127"
15
+ "@stable-harness/governance": "0.0.129",
16
+ "@stable-harness/memory": "0.0.129"
17
17
  }
18
18
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,6 +10,7 @@ export type MemoryProviderApprovalConfig = {
10
10
  restricted?: boolean;
11
11
  };
12
12
  export type MemoryProviderDefaults = {
13
+ kind?: MemoryKind;
13
14
  scope?: MemoryScope;
14
15
  sensitivity?: MemorySensitivity;
15
16
  };
@@ -1 +1 @@
1
- export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const s=n.kindHint??function inferKind(e){const n=e.toLowerCase();return n.includes("must ")||n.includes("should ")?"procedural":n.includes("run ")||n.includes("failed ")?"episodic":"semantic"}(n.content);return function isKindEnabled(e,n){return!1!==e.types?.[n]}(e,s)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:s,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
1
+ export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const t=n.kindHint??e.defaults?.kind??"semantic";return function isKindEnabled(e,n){return!1!==e.types?.[n]}(e,t)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:t,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,6 +11,6 @@
11
11
  "main": "dist/src/index.js",
12
12
  "types": "dist/src/index.d.ts",
13
13
  "peerDependencies": {
14
- "@stable-harness/core": "0.0.127"
14
+ "@stable-harness/core": "0.0.129"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,6 +12,6 @@
12
12
  ".": "./dist/index.js"
13
13
  },
14
14
  "peerDependencies": {
15
- "@stable-harness/core": "0.0.127"
15
+ "@stable-harness/core": "0.0.129"
16
16
  }
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -82,14 +82,14 @@
82
82
  "@langchain/node-vfs": "^0.1.4",
83
83
  "@langchain/ollama": "^1.2.7",
84
84
  "@langchain/openai": "^1.4.5",
85
- "@stable-harness/adapter-deepagents": "0.0.127",
86
- "@stable-harness/adapter-langgraph": "0.0.127",
87
- "@stable-harness/core": "0.0.127",
88
- "@stable-harness/governance": "0.0.127",
89
- "@stable-harness/memory": "0.0.127",
90
- "@stable-harness/protocols": "0.0.127",
91
- "@stable-harness/tool-gateway": "0.0.127",
92
- "@stable-harness/workspace-yaml": "0.0.127",
85
+ "@stable-harness/adapter-deepagents": "0.0.129",
86
+ "@stable-harness/adapter-langgraph": "0.0.129",
87
+ "@stable-harness/core": "0.0.129",
88
+ "@stable-harness/governance": "0.0.129",
89
+ "@stable-harness/memory": "0.0.129",
90
+ "@stable-harness/protocols": "0.0.129",
91
+ "@stable-harness/tool-gateway": "0.0.129",
92
+ "@stable-harness/workspace-yaml": "0.0.129",
93
93
  "deepagents": "^1.10.1",
94
94
  "langchain": "^1.4.0",
95
95
  "yaml": "^2.8.2",
@@ -1 +1 @@
1
- import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";export const filesystemBuiltinToolIds=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export function validateFilesystemBuiltinCall(e,i,o){if(isFilesystemDisabled(e)&&filesystemBuiltinToolIds.has(i))return new t({tool_call_id:o.toolCall?.id??`stable-harness-${i}-policy`,name:i,content:`Filesystem builtin tool '${i}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function resolveFilesystemPermissions(e,t){const i=readConfigRecord(t?.config,"builtinTools"),o=[];if(o.push(...function skillReadPermissions(e,t){const i=[...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,""),i=t.endsWith("/SKILL.md")?t.slice(0,-9):t,o=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(i);return[t,i,`${i}/**`,...o?[o,`${o}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return i.length>0?[{operations:["read"],paths:i,mode:"allow"}]:[]}(e,t)),!1!==i?.filesystem){if(deepagentsMemoryWritable(e))return o.length>0?o:void 0}else o.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),o.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||o.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),o.length>0?o:void 0}export function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}export function isFilesystemTool(e){return"string"==typeof e&&filesystemBuiltinToolIds.has(e)}function backendSkillPath(e,t){const i=function pathRelative(e,t){const i=e.split("/").filter(Boolean),o=t.split("/").filter(Boolean);if(!(o.length<i.length)){for(let e=0;e<i.length;e+=1)if(i[e]!==o[e])return;return o.slice(i.length).join("/")}}(e,t);return void 0===i?t:`/${i.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 readConfigRecord(e,t){const i=isRecord(e)?e:{};return isRecord(i[t])?i[t]:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";export const filesystemBuiltinToolIds=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export function validateFilesystemBuiltinCall(e,n,i){if(isFilesystemDisabled(e)&&filesystemBuiltinToolIds.has(n))return new t({tool_call_id:i.toolCall?.id??`stable-harness-${n}-policy`,name:n,content:`Filesystem builtin tool '${n}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function resolveFilesystemPermissions(e,t){const n=readConfigRecord(t?.config,"builtinTools");if(!1===n?.filesystem)return;const i=[];return i.push(...function skillReadPermissions(e,t){const n=[...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,""),n=t.endsWith("/SKILL.md")?t.slice(0,-9):t,i=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(n);return[t,n,`${n}/**`,...i?[i,`${i}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return n.length>0?[{operations:["read"],paths:n,mode:"allow"}]:[]}(e,t)),deepagentsMemoryWritable(e)||deepagentsMemoryWritable(e)||i.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),i.length>0?i:void 0}export function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}export function isFilesystemTool(e){return"string"==typeof e&&filesystemBuiltinToolIds.has(e)}function backendSkillPath(e,t){const n=function pathRelative(e,t){const n=e.split("/").filter(Boolean),i=t.split("/").filter(Boolean);if(!(i.length<n.length)){for(let e=0;e<n.length;e+=1)if(n[e]!==i[e])return;return i.slice(n.length).join("/")}}(e,t);return void 0===n?t:`/${n.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 readConfigRecord(e,t){const n=isRecord(e)?e:{};return isRecord(n[t])?n[t]:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1 +1 @@
1
- import{AIMessage as t}from"@langchain/core/messages";import{parseToolIntent as e,repairCallSelection as o}from"@easynet/better-call";const n=new Set(["edit_file","glob","ls","read_file"]);export function createRawToolCallParserMiddleware(e){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(o,n){const s=await n(o);if(!function rawToolCallParsingEnabled(t){const e=isRecord(t.workspace.runtime.recovery)?t.workspace.runtime.recovery:{};return!0===(isRecord(e.toolCall)?e.toolCall:{}).enabled}(e))return s;const r=await async function parseRawToolCallResponse(e,o){if(0===o.length||!function isModelResponse(t){return isRecord(t)}(e)||function hasToolCalls(t){return Array.isArray(t.tool_calls)&&t.tool_calls.length>0}(e))return;const n=function readContentText(t){return"string"==typeof t?t.trim()?t:void 0:Array.isArray(t)&&t.map(t=>isRecord(t)&&"string"==typeof t.text?t.text:"").join("\n").trim()||void 0}(e.content);if(!n)return;const s=await async function readRawToolCalls(t,e){const o=[],n=new Set,s=new Set(e.map(t=>t.id));for(const r of function rawToolIntentCandidates(t,e){const o=[t.trim()];for(const e of t.matchAll(/```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n```/gu))o.push(e[1].trim());for(const n of t.split(/\r?\n/u)){const t=n.trim();e.has(t)&&o.push(t)}for(const e of t.matchAll(/<\s*(?:tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*(?:tool_call|tool_code)\s*>)?/giu))o.push(e[0]);for(const e of t.matchAll(/<\s*call_tool\b[^>]*>[\s\S]*?<\s*\/\s*call_tool\s*>/giu)){o.push(e[0]);const t=parseCallToolTags(e[0]);t&&o.push(t)}for(const n of e){const e=new RegExp(`(?:^|[\\s<])${escapeRegexp(n)}\\s*\\([^)]*\\)\\s*>?`,"giu");for(const n of t.matchAll(e))o.push(n[0].trim())}return o.push(...function parseJsonObjects(t){const e=[];let o=t.indexOf("{");for(;o>=0;){const n=readBalancedJsonObject(t,o);if(n){try{const t=JSON.parse(n);isRecord(t)&&e.push(t)}catch{}o=t.indexOf("{",o+n.length)}else o=t.indexOf("{",o+1)}return e}(t)),o}(t,s)){const t=await parseCandidate(r,e);if(!t||!s.has(t.tool))continue;const a=normalizeToolArgs(t.args),i=`${t.tool}:${JSON.stringify(a)}`;n.has(i)||(n.add(i),o.push({id:`stable-harness-${t.tool}-${Date.now().toString(36)}`,name:t.tool,args:a}))}return o}(n,o);return 0!==s.length?new t({content:"",id:"string"==typeof e.id?e.id:void 0,name:"string"==typeof e.name?e.name:void 0,additional_kwargs:isRecord(e.additional_kwargs)?e.additional_kwargs:{},response_metadata:isRecord(e.response_metadata)?e.response_metadata:{},tool_calls:s.map(t=>({id:t.id,name:t.name,args:t.args,type:"tool_call"}))}):void 0}(s,function visibleToolCandidates(t,e){return(e.tools??[]).flatMap(readToolName).map(e=>{const o=t.workspace.tools.get(e),n=t.toolGateway?.get(e);return{id:e,description:o?.description??n?.description,schema:o?.schema??n?.schema}})}(e,o));return r??s}}}async function parseCandidate(t,s){const r=new Set(s.map(t=>t.id));if("string"==typeof t){const e=function parseExecuteToolWrapper(t,e){const o=t.trim().match(/^<?\s*execute\s*\(([\s\S]*)\)\s*>?$/iu);if(!o)return;const n=function parseFunctionKwargs(t){const e={};for(const o of function splitTopLevelCommas(t){const e=[];let o,n=0,s=0,r=!1;for(let a=0;a<t.length;a+=1){const i=t[a];o?(r="\\"===i&&!r,i!==o||r||(o=void 0),"\\"!==i&&(r=!1)):'"'!==i&&"'"!==i?("("!==i&&"["!==i&&"{"!==i||(s+=1),")"!==i&&"]"!==i&&"}"!==i||(s-=1),","===i&&0===s&&(e.push(t.slice(n,a).trim()),n=a+1)):o=i}const a=t.slice(n).trim();return a&&e.push(a),e}(t)){const t=o.indexOf("=");if(t<=0)continue;const n=o.slice(0,t).trim();/^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(n)&&(e[n]=parseFunctionValue(o.slice(t+1).trim()))}return e}(o[1]??""),s=n.tool??n.tool_name??n.name;if("string"!=typeof s||!e.has(s))return;const r={...n};return delete r.tool,delete r.tool_name,delete r.name,isRecord(r.args)&&1===Object.keys(r).length?{tool:s,args:r.args}:{tool:s,args:r}}(t,r);if(e)return e}const a=e(t);if(a)return a;const i=function singleToolName(t){return 1===t.size?[...t][0]:void 0}(r);if(i&&isRecord(t))return e({tool:i,args:t});const c=isRecord(t)?await o({call:{name:"__stable_harness_raw_args__",args:t},candidates:s,mode:"repair"}):void 0;if(c?.ok)return{tool:c.candidateId,args:c.args};const l=isRecord(t)?await async function selectUniqueToolByArgs(t,e){const n=[];for(const s of e){const r=await o({call:{name:s.id,args:t},candidates:e,mode:"guard"});r.ok&&r.candidateId===s.id&&n.push({tool:r.candidateId,args:r.args})}return 1===n.length?n[0]:void 0}(t,s):void 0;if(l)return l;if("string"!=typeof t)return;const u=await o({candidates:s,rawIntent:t,mode:"guard"});if(u.ok)return{tool:u.candidateId,args:u.args??{}};const d=function parseFunctionStyleToolName(t,e){const o=t.trim();for(const t of e)if(!n.has(t)&&new RegExp(`^<?\\s*${escapeRegexp(t)}\\s*\\([\\s\\S]*\\)\\s*>?$`,"iu").test(o))return t}(t,r);return d?{tool:d,args:{}}:void 0}function parseFunctionValue(t){if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1).replace(/\\(["'\\])/gu,"$1");if(/^-?\d+(?:\.\d+)?$/u.test(t))return Number(t);if("true"===t)return!0;if("false"===t)return!1;if("null"===t)return null;try{return JSON.parse(t)}catch{return t}}function normalizeToolArgs(t){return isRecord(t)?t:{}}function parseCallToolTags(t){const e=t.match(/<\s*tool\s*>([\s\S]*?)<\s*\/\s*tool\s*>/iu)?.[1]?.trim(),o=t.match(/<\s*tool_args\s*>([\s\S]*?)<\s*\/\s*tool_args\s*>/iu)?.[1]?.trim();if(e&&o)try{const t=JSON.parse(o);return isRecord(t)?{tool:e,args:t}:{tool:e,args:{}}}catch{return{tool:e,args:{}}}}function readBalancedJsonObject(t,e){let o=0,n=!1,s=!1;for(let r=e;r<t.length;r+=1){const a=t[r];if(n)s="\\"===a&&!s,'"'!==a||s||(n=!1),"\\"!==a&&(s=!1);else if('"'===a&&(n=!0),"{"===a&&(o+=1),"}"===a&&(o-=1),0===o)return t.slice(e,r+1)}}function escapeRegexp(t){return t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readToolName(t){const e="string"==typeof t.name&&t.name.length>0?t.name:void 0;if(e)return[e];const o=t,n=isRecord(o.function)&&"string"==typeof o.function.name?o.function.name:void 0;return n&&n.length>0?[n]:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
1
+ import{AIMessage as t}from"@langchain/core/messages";import{parseToolIntent as e,repairCallSelection as o}from"@easynet/better-call";export function createRawToolCallParserMiddleware(e){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(o,n){const r=await n(o);if(!function rawToolCallParsingEnabled(t){const e=isRecord(t.workspace.runtime.recovery)?t.workspace.runtime.recovery:{};return!0===(isRecord(e.toolCall)?e.toolCall:{}).enabled}(e))return r;const s=await async function parseRawToolCallResponse(e,o){if(0===o.length||!function isModelResponse(t){return isRecord(t)}(e)||function hasToolCalls(t){return Array.isArray(t.tool_calls)&&t.tool_calls.length>0}(e))return;const n=function readContentText(t){return"string"==typeof t?t.trim()?t:void 0:Array.isArray(t)&&t.map(t=>isRecord(t)&&"string"==typeof t.text?t.text:"").join("\n").trim()||void 0}(e.content);if(!n)return;const r=await async function readRawToolCalls(t,e){const o=[],n=new Set,r=new Set(e.map(t=>t.id));for(const s of function rawToolIntentCandidates(t,e){const o=[t.trim()];for(const e of t.matchAll(/```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n```/gu))o.push(e[1].trim());for(const e of t.matchAll(/<\s*(?:tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*(?:tool_call|tool_code)\s*>)?/giu))o.push(e[0]);for(const e of t.matchAll(/<\s*call_tool\b[^>]*>[\s\S]*?<\s*\/\s*call_tool\s*>/giu)){o.push(e[0]);const t=parseCallToolTags(e[0]);t&&o.push(t)}for(const n of e){const e=new RegExp(`(?:^|[\\s<])${escapeRegexp(n)}\\s*\\([^)]*\\)\\s*>?`,"giu");for(const n of t.matchAll(e))o.push(n[0].trim())}return o.push(...function parseJsonObjects(t){const e=[];let o=t.indexOf("{");for(;o>=0;){const n=readBalancedJsonObject(t,o);if(n){try{const t=JSON.parse(n);isRecord(t)&&e.push(t)}catch{}o=t.indexOf("{",o+n.length)}else o=t.indexOf("{",o+1)}return e}(t)),o}(t,r)){const t=await parseCandidate(s,e);if(!t||!r.has(t.tool))continue;const a=normalizeToolArgs(t.args),i=`${t.tool}:${JSON.stringify(a)}`;n.has(i)||(n.add(i),o.push({id:`stable-harness-${t.tool}-${Date.now().toString(36)}`,name:t.tool,args:a}))}return o}(n,o);return 0!==r.length?new t({content:"",id:"string"==typeof e.id?e.id:void 0,name:"string"==typeof e.name?e.name:void 0,additional_kwargs:isRecord(e.additional_kwargs)?e.additional_kwargs:{},response_metadata:isRecord(e.response_metadata)?e.response_metadata:{},tool_calls:r.map(t=>({id:t.id,name:t.name,args:t.args,type:"tool_call"}))}):void 0}(r,function visibleToolCandidates(t,e){return(e.tools??[]).flatMap(readToolName).map(e=>{const o=t.workspace.tools.get(e),n=t.toolGateway?.get(e);return{id:e,description:o?.description??n?.description,schema:o?.schema??n?.schema}})}(e,o));return s??r}}}async function parseCandidate(t,n){const r=new Set(n.map(t=>t.id));if("string"==typeof t){if(function isBareToolMarkup(t,e){const o=t.trim().replace(/^<\s*tool_code\b[^>]*>/iu,"").replace(/<\s*\/\s*tool_code\s*>$/iu,"").replace(/^```\w*\s*/u,"").replace(/```$/u,"").trim();return e.has(o)}(t,r))return;const e=function parseExecuteToolWrapper(t,e){const o=t.trim().match(/^<?\s*execute\s*\(([\s\S]*)\)\s*>?$/iu);if(!o)return;const n=function parseFunctionKwargs(t){const e={};for(const o of function splitTopLevelCommas(t){const e=[];let o,n=0,r=0,s=!1;for(let a=0;a<t.length;a+=1){const i=t[a];o?(s="\\"===i&&!s,i!==o||s||(o=void 0),"\\"!==i&&(s=!1)):'"'!==i&&"'"!==i?("("!==i&&"["!==i&&"{"!==i||(r+=1),")"!==i&&"]"!==i&&"}"!==i||(r-=1),","===i&&0===r&&(e.push(t.slice(n,a).trim()),n=a+1)):o=i}const a=t.slice(n).trim();return a&&e.push(a),e}(t)){const t=o.indexOf("=");if(t<=0)continue;const n=o.slice(0,t).trim();/^[a-zA-Z_][a-zA-Z0-9_]*$/u.test(n)&&(e[n]=parseFunctionValue(o.slice(t+1).trim()))}return e}(o[1]??""),r=n.tool??n.tool_name??n.name;if("string"!=typeof r||!e.has(r))return;const s={...n};return delete s.tool,delete s.tool_name,delete s.name,isRecord(s.args)&&1===Object.keys(s).length?{tool:r,args:s.args}:{tool:r,args:s}}(t,r);if(e)return e}const s=e(t);if(s)return s;const a=n.filter(hasSchema),i=isRecord(t)&&a.length>0?await o({call:{name:"__stable_harness_raw_args__",args:t},candidates:a,mode:"repair"}):void 0;if(i?.ok)return{tool:i.candidateId,args:i.args};const c=isRecord(t)?await async function selectUniqueToolByArgs(t,e){const n=[];for(const r of e){const s=await o({call:{name:r.id,args:t},candidates:e,mode:"guard"});s.ok&&s.candidateId===r.id&&n.push({tool:s.candidateId,args:s.args})}return 1===n.length?n[0]:void 0}(t,a):void 0;return c||void 0}function hasSchema(t){return void 0!==t.schema}function parseFunctionValue(t){if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1).replace(/\\(["'\\])/gu,"$1");if(/^-?\d+(?:\.\d+)?$/u.test(t))return Number(t);if("true"===t)return!0;if("false"===t)return!1;if("null"===t)return null;try{return JSON.parse(t)}catch{return t}}function normalizeToolArgs(t){return isRecord(t)?t:{}}function parseCallToolTags(t){const e=t.match(/<\s*tool\s*>([\s\S]*?)<\s*\/\s*tool\s*>/iu)?.[1]?.trim(),o=t.match(/<\s*tool_args\s*>([\s\S]*?)<\s*\/\s*tool_args\s*>/iu)?.[1]?.trim();if(e&&o)try{const t=JSON.parse(o);return isRecord(t)?{tool:e,args:t}:{tool:e,args:{}}}catch{return{tool:e,args:{}}}}function readBalancedJsonObject(t,e){let o=0,n=!1,r=!1;for(let s=e;s<t.length;s+=1){const a=t[s];if(n)r="\\"===a&&!r,'"'!==a||r||(n=!1),"\\"!==a&&(r=!1);else if('"'===a&&(n=!0),"{"===a&&(o+=1),"}"===a&&(o-=1),0===o)return t.slice(e,s+1)}}function escapeRegexp(t){return t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readToolName(t){const e="string"==typeof t.name&&t.name.length>0?t.name:void 0;if(e)return[e];const o=t,n=isRecord(o.function)&&"string"==typeof o.function.name?o.function.name:void 0;return n&&n.length>0?[n]:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -16,7 +16,7 @@
16
16
  "@langchain/node-vfs": "^0.1.4",
17
17
  "@langchain/ollama": "^1.2.7",
18
18
  "@langchain/openai": "^1.4.5",
19
- "@stable-harness/core": "0.0.127",
19
+ "@stable-harness/core": "0.0.129",
20
20
  "deepagents": "^1.10.1",
21
21
  "langchain": "^1.4.0"
22
22
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-langgraph",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,6 +12,6 @@
12
12
  "types": "dist/src/index.d.ts",
13
13
  "peerDependencies": {
14
14
  "@langchain/langgraph": "^1.3.0",
15
- "@stable-harness/core": "0.0.127"
15
+ "@stable-harness/core": "0.0.129"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/cli",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -15,12 +15,12 @@
15
15
  "types": "dist/src/index.d.ts",
16
16
  "peerDependencies": {
17
17
  "@langchain/langgraph-api": "^1.2.1",
18
- "@stable-harness/adapter-deepagents": "0.0.127",
19
- "@stable-harness/adapter-langgraph": "0.0.127",
20
- "@stable-harness/core": "0.0.127",
21
- "@stable-harness/memory": "0.0.127",
22
- "@stable-harness/protocols": "0.0.127",
23
- "@stable-harness/tool-gateway": "0.0.127",
24
- "@stable-harness/workspace-yaml": "0.0.127"
18
+ "@stable-harness/adapter-deepagents": "0.0.129",
19
+ "@stable-harness/adapter-langgraph": "0.0.129",
20
+ "@stable-harness/core": "0.0.129",
21
+ "@stable-harness/memory": "0.0.129",
22
+ "@stable-harness/protocols": "0.0.129",
23
+ "@stable-harness/tool-gateway": "0.0.129",
24
+ "@stable-harness/workspace-yaml": "0.0.129"
25
25
  }
26
26
  }
@@ -1 +1 @@
1
- import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{containsProgressOnlyToolIntent as t}from"../../recovery/progress-intent.js";import{buildRawArgsRecoveryRequest as o,buildRawArgsToolEvidenceRecoveryRequest as r,buildRawArgsToolSequenceEvidenceRecoveryRequest as a,hasUniqueRawArgsTool as s,matchRawArgsToolSequence as n,matchUniqueRawArgsTool as l}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as i,assertNoRawToolCallOutput as u,assertNoRawToolResultOutput as c,assertNoStructurallyIncompleteFinalOutput as d,assertNoToolExecutionErrorOutput as y,buildEvidenceSynthesisOutput as p,buildResultRecoveryRequest as v,containsRawToolCallOutput as g,rawToolCallFailureMessage as m,rawToolCallOutputPreview as f,toolCallRecoveryEnabled as w}from"../../recovery/tool-call.js";import{controlGaps as R}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(t){let r=t.result,a=t.request;const n=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(t.recoveryPolicy),R=new Set,T=new Set;for(let e=0;;e+=1){const s=t.store.getRun(t.requestId)?.events??[],i=effectiveRecoveryToolIds(a,t.agent.tools);assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,i);const u=await recoverRawArgsSequenceByToolGateway(t,a,r,T,e+1,i);if(u){a=u.request,r=u.result;continue}const c=l({output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:i,policy:t.recoveryPolicy}),d=c?rawArgsToolKey(c):void 0;if(c&&d&&T.size>0&&!T.has(d)){const o=await recoverRawArgsByToolGateway(t,a,r,c,T,e+1);if(o){a=o.request,r=o.result;continue}}const y=d&&!R.has(d)?o({request:a,output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:i,policy:t.recoveryPolicy}):void 0,p=e<n?v({request:a,output:r.text,events:s,availableToolIds:i,policy:t.recoveryPolicy}):void 0,g=y??p;if(!g)break;if(g===y&&R.add(d),a=g,emitRepair(t,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(r.text,i)),r=await t.runAdapter(g),emitRepair(t,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(r.text,i)),g===y){const o=await recoverRepeatedRawArgsByToolGateway(t,a,r,R,T,e+1);o&&(a=o.request,r=o.result)}}return function finalizeRecoveredOutput(t,o){if(!w(t.recoveryPolicy))return o;let r=!1;if(g(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:m(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const a=p({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return a&&(r=!0,o={...o,text:a,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),r||(g(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),u(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(s({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(e.request,e.agent.tools),policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${f(t)}`)}(t,o.text),i(o.text,t.agent.tools,t.recoveryPolicy),c(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),y(o.text,t.recoveryPolicy),d(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(t,r)}async function recoverRawArgsSequenceByToolGateway(e,t,o,r,s,l){if(!e.runRecoveredToolCall)return;const i=n({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:l,policy:e.recoveryPolicy});if(i.length<2||i.some(e=>r.has(rawArgsToolKey(e))))return;emitRepair(e,"runtime.repair.started","result_output",s,"raw_args_tool_sequence_gateway_recovery",void 0,repairDiagnostics(o.text,l));const u=[];for(const t of i){r.add(rawArgsToolKey(t));const o=await e.runRecoveredToolCall(t.toolId,t.args);u.push({match:t,toolOutput:visibleRecoveredToolOutput(o)})}const c=a({request:t,evidences:u}),d=await e.runAdapter(c),y=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolSequenceBlocker({evidences:u,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool_sequence"}}};return emitRepair(e,"runtime.repair.completed","result_output",s,"raw_args_tool_sequence_gateway_recovery","retried",repairDiagnostics(y.text,l)),{request:c,result:y}}async function recoverRepeatedRawArgsByToolGateway(e,t,o,r,a,s){const n=l({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(t,e.agent.tools),policy:e.recoveryPolicy});if(!n||!e.runRecoveredToolCall)return;const i=rawArgsToolKey(n);return r.has(i)&&!a.has(i)?recoverRawArgsByToolGateway(e,t,o,n,a,s):void 0}async function recoverRawArgsByToolGateway(e,t,o,a,s,n){if(!e.runRecoveredToolCall)return;const l=rawArgsToolKey(a);if(s.has(l))return;s.add(l),emitRepair(e,"runtime.repair.started","result_output",n,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(o.text,e.agent.tools));const i=visibleRecoveredToolOutput(await e.runRecoveredToolCall(a.toolId,a.args)),u=r({request:t,match:a,toolOutput:i}),c=await e.runAdapter(u),d=c.text.trim()?c:{...c,text:buildEmptyRecoveredToolBlocker({match:a,toolOutput:i,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...c.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",n,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(d.text,e.agent.tools)),{request:u,result:d}}function assertNoNonFocusedRecoveryIntent(e,o,r,a){if(0===a.length||a.length===r.length)return;const s=new Set(a),n=visibleToolCandidates(e,r).filter(e=>!s.has(e)),l=g(e,o)||t(e,r)||function containsJsonToolEnvelope(e){const t=e.trim(),o=t.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/iu)?.[1]?.trim(),r=o??t;if(!r.startsWith("{")||!r.endsWith("}")||r.length>6e3)return!1;try{const e=JSON.parse(r);if(!function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e))return!1;const t=["tool","tool_name","name","type","subagent_type"].some(t=>"string"==typeof e[t]),o=["args","arguments","parameters","kwargs"].some(t=>t in e);return t&&o}catch{return!1}}(e);if(n.length>0&&l)throw new Error(`Focused recovery output referenced non-focused tool(s): ${n.join(", ")}. The backend must call one of the focused gateway tools: ${a.join(", ")}.`)}function emitRepair(e,t,o,r,a,s,n){const l={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...n?{diagnostics:n}:{}};e.emit("runtime.repair.started"===t?{type:t,...l}:{type:t,...l,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:f(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])containsDelimitedToken(e,r)&&o.add(r);return[...o]}function containsDelimitedToken(e,t){if(!t)return!1;let o=e.indexOf(t);for(;o>=0;){const r=0===o?"":e[o-1],a=e[o+t.length]??"";if(!isTokenChar(r)&&!isTokenChar(a))return!0;o=e.indexOf(t,o+t.length)}return!1}function isTokenChar(e){return"_"===e||"-"===e||e>="A"&&e<="Z"||e>="a"&&e<="z"||e>="0"&&e<="9"}function effectiveRecoveryToolIds(e,t){const o=function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}(e.metadata?.stableHarnessRequiredEvidenceTools).filter(e=>t.includes(e));return o.length>0?o:t}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}function buildEmptyRecoveredToolBlocker(e){const t=R(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}function buildEmptyRecoveredToolSequenceBlocker(e){const t=R(e.events);return["Stable runtime recovery executed matched declared tools, but the backend returned no user-facing output after receiving the executed evidence.","",...e.evidences.flatMap((e,t)=>[`Executed tool ${t+1}: ${e.match.toolId}`,"Executed JSON arguments:",JSON.stringify(e.match.args),"Executed tool evidence:",e.toolOutput,""]),...t.length>0?["Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
1
+ import{assertNoDeclaredActionOmissionOutput as e}from"../../recovery/control-omission.js";import{containsProgressOnlyToolIntent as t}from"../../recovery/progress-intent.js";import{buildRawArgsRecoveryRequest as o,buildRawArgsToolEvidenceRecoveryRequest as r,buildRawArgsToolSequenceEvidenceRecoveryRequest as a,hasUniqueRawArgsTool as s,matchRawArgsToolSequence as n,matchUniqueRawArgsTool as u}from"../../recovery/raw-args.js";import{assertNoProgressOnlyToolIntentOutput as l,assertNoRawToolCallOutput as c,assertNoRawToolResultOutput as i,assertNoStructurallyIncompleteFinalOutput as d,assertNoToolExecutionErrorOutput as p,buildEvidenceSynthesisOutput as y,buildResultRecoveryRequest as v,containsRawToolCallOutput as g,rawToolCallFailureMessage as m,rawToolCallOutputPreview as f,toolCallRecoveryEnabled as w}from"../../recovery/tool-call.js";import{controlGaps as R,successfulEvidenceItems as h}from"../../quality/event-evidence.js";export async function recoverAdapterResultOutput(t){let r=t.result,a=t.request;const n=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,o="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,r="object"!=typeof o||null===o||Array.isArray(o)?void 0:o.maxResultRecoveryAttempts;return"number"==typeof r&&Number.isInteger(r)&&r>0?r:3}(t.recoveryPolicy),R=new Set,h=new Set;for(let e=0;;e+=1){const s=t.store.getRun(t.requestId)?.events??[],l=effectiveRecoveryToolIds(a,t.agent.tools),c=buildNonFocusedRecoveryRequest({request:a,output:r.text,events:s,policy:t.recoveryPolicy,declaredToolIds:t.agent.tools,visibleToolIds:l});if(c){e>=n&&assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,l,s),a=c,emitRepair(t,"runtime.repair.started","result_output",e+1,"non_focused_recovery_intent",void 0,repairDiagnostics(r.text,l)),r=await t.runAdapter(c),emitRepair(t,"runtime.repair.completed","result_output",e+1,"non_focused_recovery_intent","retried",repairDiagnostics(r.text,l));continue}assertNoNonFocusedRecoveryIntent(r.text,t.recoveryPolicy,t.agent.tools,l,s);const i=await recoverRawArgsSequenceByToolGateway(t,a,r,h,e+1,l);if(i){a=i.request,r=i.result;continue}const d=u({output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:l,policy:t.recoveryPolicy}),p=d?rawArgsToolKey(d):void 0;if(d&&p&&h.size>0&&!h.has(p)){const o=await recoverRawArgsByToolGateway(t,a,r,d,h,e+1);if(o){a=o.request,r=o.result;continue}}const y=p&&!R.has(p)?o({request:a,output:r.text,agent:t.agent,workspace:t.workspace,toolGateway:t.toolGateway,events:s,candidateToolIds:l,policy:t.recoveryPolicy}):void 0,g=e<n?v({request:a,output:r.text,events:s,availableToolIds:l,policy:t.recoveryPolicy}):void 0,m=y??g;if(!m)break;if(m===y&&R.add(p),a=m,emitRepair(t,"runtime.repair.started","result_output",e+1,"recoverable_result_output",void 0,repairDiagnostics(r.text,l)),r=await t.runAdapter(m),emitRepair(t,"runtime.repair.completed","result_output",e+1,"recoverable_result_output","retried",repairDiagnostics(r.text,l)),m===y){const o=await recoverRepeatedRawArgsByToolGateway(t,a,r,R,h,e+1);o&&(a=o.request,r=o.result)}}return function finalizeRecoveredOutput(t,o){if(!w(t.recoveryPolicy))return o;let r=!1;if(g(o.text,t.recoveryPolicy)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(t.request.metadata)){const e=o.text;o={...o,text:m(),metadata:{...o.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}},emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(e,t.agent.tools))}const a=y({request:t.request,output:o.text,events:t.store.getRun(t.requestId)?.events??[],policy:t.recoveryPolicy});return a&&(r=!0,o={...o,text:a,metadata:{...o.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}},emitRepair(t,"runtime.repair.completed","evidence_synthesis",void 0,"raw_tool_call_output_with_evidence","synthesized")),r||(g(o.text,t.recoveryPolicy)&&emitRepair(t,"runtime.repair.completed","result_output",void 0,"raw_tool_call_output","blocked",repairDiagnostics(o.text,t.agent.tools)),c(o.text,t.recoveryPolicy),function assertNoRawArgsToolOutput(e,t){if(s({output:t,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(e.request,e.agent.tools),policy:e.recoveryPolicy}))throw new Error(`Adapter returned raw tool argument JSON as the final answer after recovery. The backend must execute the matching tool instead. Output preview: ${f(t)}`)}(t,o.text),l(o.text,t.agent.tools,t.recoveryPolicy),i(o.text,t.store.getRun(t.requestId)?.events??[],t.recoveryPolicy),p(o.text,t.recoveryPolicy),d(o.text,t.recoveryPolicy),e({output:o.text,events:t.store.getRun(t.requestId)?.events??[],availableToolIds:t.agent.tools})),o}(t,r)}async function recoverRawArgsSequenceByToolGateway(e,t,o,r,s,u){if(!e.runRecoveredToolCall)return;const l=n({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:u,policy:e.recoveryPolicy});if(l.length<2||l.some(e=>r.has(rawArgsToolKey(e))))return;emitRepair(e,"runtime.repair.started","result_output",s,"raw_args_tool_sequence_gateway_recovery",void 0,repairDiagnostics(o.text,u));const c=[];for(const t of l){r.add(rawArgsToolKey(t));const o=await e.runRecoveredToolCall(t.toolId,t.args);c.push({match:t,toolOutput:visibleRecoveredToolOutput(o)})}const i=a({request:t,evidences:c}),d=await e.runAdapter(i),p=d.text.trim()?d:{...d,text:buildEmptyRecoveredToolSequenceBlocker({evidences:c,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...d.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool_sequence"}}};return emitRepair(e,"runtime.repair.completed","result_output",s,"raw_args_tool_sequence_gateway_recovery","retried",repairDiagnostics(p.text,u)),{request:i,result:p}}async function recoverRepeatedRawArgsByToolGateway(e,t,o,r,a,s){const n=u({output:o.text,agent:e.agent,workspace:e.workspace,toolGateway:e.toolGateway,events:e.store.getRun(e.requestId)?.events??[],candidateToolIds:effectiveRecoveryToolIds(t,e.agent.tools),policy:e.recoveryPolicy});if(!n||!e.runRecoveredToolCall)return;const l=rawArgsToolKey(n);return r.has(l)&&!a.has(l)?recoverRawArgsByToolGateway(e,t,o,n,a,s):void 0}async function recoverRawArgsByToolGateway(e,t,o,a,s,n){if(!e.runRecoveredToolCall)return;const u=rawArgsToolKey(a);if(s.has(u))return;s.add(u),emitRepair(e,"runtime.repair.started","result_output",n,"raw_args_tool_gateway_recovery",void 0,repairDiagnostics(o.text,e.agent.tools));const l=visibleRecoveredToolOutput(await e.runRecoveredToolCall(a.toolId,a.args)),c=r({request:t,match:a,toolOutput:l}),i=await e.runAdapter(c),d=i.text.trim()?i:{...i,text:buildEmptyRecoveredToolBlocker({match:a,toolOutput:l,events:e.store.getRun(e.requestId)?.events??[]}),metadata:{...i.metadata,toolCallRecovery:{blocked:!0,reason:"empty_output_after_recovered_tool"}}};return emitRepair(e,"runtime.repair.completed","result_output",n,"raw_args_tool_gateway_recovery","retried",repairDiagnostics(d.text,e.agent.tools)),{request:c,result:d}}function assertNoNonFocusedRecoveryIntent(e,t,o,r,a){const s=nonFocusedRecoveryIntent(e,t,o,r);if(!s)return;const n=new Set(h(a).map(e=>e.source)),u=s.nonFocused.filter(e=>!n.has(e)),l=u.length>0?u:s.nonFocused;throw new Error(`Focused recovery output referenced non-focused tool(s): ${l.join(", ")}. The backend must call one of the focused gateway tools: ${r.join(", ")}.`)}function buildNonFocusedRecoveryRequest(e){const t=nonFocusedRecoveryIntent(e.output,e.policy,e.declaredToolIds,e.visibleToolIds);if(!t)return;const o=h(e.events),r=new Set(o.map(e=>e.source));if(t.nonFocused.some(e=>!r.has(e)))return;const a=o.slice(-5).map(e=>`- ${e.source}: ${function previewRecoveredEvidence(e){const t=e.replace(/\s+/gu," ").trim();return t.length>1e3?`${t.slice(0,997)}...`:t}(e.output)}`);return{...e.request,input:[e.request.input,"","Stable runtime recovery: the previous recovery output referenced tool(s) that already have successful evidence but are not allowed targets for this focused turn.",`Already completed non-focused tool(s): ${t.nonFocused.join(", ")}`,`Allowed focused tool target(s): ${e.visibleToolIds.join(", ")}`,"Reuse the prior successful evidence below to build one allowed focused tool call. Do not call already completed non-focused tools again.","The next assistant action must be the backend's structured tool call for one allowed focused target, with no prose before it.","","Prior successful tool evidence:",...a,"","Previous invalid recovery output:",e.output].join("\n"),metadata:{...e.request.metadata,stableHarnessRecovery:"tool_call"}}}function nonFocusedRecoveryIntent(e,o,r,a){if(0===a.length||a.length===r.length)return;const s=new Set(a),n=visibleToolCandidates(e,r).filter(e=>!s.has(e)),u=g(e,o)||t(e,r)||function containsJsonToolEnvelope(e){const t=e.trim(),o=t.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/iu)?.[1]?.trim(),r=o??t;if(!r.startsWith("{")||!r.endsWith("}")||r.length>6e3)return!1;try{const e=JSON.parse(r);if(!function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e))return!1;const t=["tool","tool_name","name","type","subagent_type"].some(t=>"string"==typeof e[t]),o=["args","arguments","parameters","params","kwargs"].some(t=>t in e);return t&&o}catch{return!1}}(e);return n.length>0&&u?{nonFocused:n}:void 0}function emitRepair(e,t,o,r,a,s,n){const u={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:o,attempt:r,reason:a,...n?{diagnostics:n}:{}};e.emit("runtime.repair.started"===t?{type:t,...u}:{type:t,...u,outcome:s??"retried"})}function repairDiagnostics(e,t){return{outputPreview:f(e),toolCandidateIds:visibleToolCandidates(e,t)}}function visibleToolCandidates(e,t){const o=new Set;for(const r of t??[])containsDelimitedToken(e,r)&&o.add(r);return[...o]}function containsDelimitedToken(e,t){if(!t)return!1;let o=e.indexOf(t);for(;o>=0;){const r=0===o?"":e[o-1],a=e[o+t.length]??"";if(!isTokenChar(r)&&!isTokenChar(a))return!0;o=e.indexOf(t,o+t.length)}return!1}function isTokenChar(e){return"_"===e||"-"===e||e>="A"&&e<="Z"||e>="a"&&e<="z"||e>="0"&&e<="9"}function effectiveRecoveryToolIds(e,t){const o=function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}(e.metadata?.stableHarnessRequiredEvidenceTools).filter(e=>t.includes(e));return o.length>0?o:t}function rawArgsToolKey(e){return`${e.toolId}:${JSON.stringify(e.args)}`}function visibleRecoveredToolOutput(e){return("string"==typeof e.text?e.text.trim():"")||"The recovered tool completed successfully but returned no user-visible output."}function buildEmptyRecoveredToolBlocker(e){const t=R(e.events);return["Stable runtime recovery executed the matched declared tool, but the backend returned no user-facing output after receiving the executed evidence.","",`Executed tool: ${e.match.toolId}`,"","Executed JSON arguments:",JSON.stringify(e.match.args),"","Executed tool evidence:",e.toolOutput,...t.length>0?["","Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}function buildEmptyRecoveredToolSequenceBlocker(e){const t=R(e.events);return["Stable runtime recovery executed matched declared tools, but the backend returned no user-facing output after receiving the executed evidence.","",...e.evidences.flatMap((e,t)=>[`Executed tool ${t+1}: ${e.match.toolId}`,"Executed JSON arguments:",JSON.stringify(e.match.args),"Executed tool evidence:",e.toolOutput,""]),...t.length>0?["Unresolved control gaps:",...t.map(e=>`- ${e}`)]:[]].join("\n")}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,7 +12,7 @@
12
12
  ".": "./dist/index.js"
13
13
  },
14
14
  "peerDependencies": {
15
- "@stable-harness/governance": "0.0.127",
16
- "@stable-harness/memory": "0.0.127"
15
+ "@stable-harness/governance": "0.0.129",
16
+ "@stable-harness/memory": "0.0.129"
17
17
  }
18
18
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/evaluation",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,6 +11,6 @@
11
11
  "main": "dist/src/index.js",
12
12
  "types": "dist/src/index.d.ts",
13
13
  "peerDependencies": {
14
- "@stable-harness/core": "0.0.127"
14
+ "@stable-harness/core": "0.0.129"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -10,6 +10,7 @@ export type MemoryProviderApprovalConfig = {
10
10
  restricted?: boolean;
11
11
  };
12
12
  export type MemoryProviderDefaults = {
13
+ kind?: MemoryKind;
13
14
  scope?: MemoryScope;
14
15
  sensitivity?: MemorySensitivity;
15
16
  };
@@ -1 +1 @@
1
- export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const s=n.kindHint??function inferKind(e){const n=e.toLowerCase();return n.includes("must ")||n.includes("should ")?"procedural":n.includes("run ")||n.includes("failed ")?"episodic":"semantic"}(n.content);return function isKindEnabled(e,n){return!1!==e.types?.[n]}(e,s)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:s,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
1
+ export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const t=n.kindHint??e.defaults?.kind??"semantic";return function isKindEnabled(e,n){return!1!==e.types?.[n]}(e,t)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:t,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -11,6 +11,6 @@
11
11
  "main": "dist/src/index.js",
12
12
  "types": "dist/src/index.d.ts",
13
13
  "peerDependencies": {
14
- "@stable-harness/core": "0.0.127"
14
+ "@stable-harness/core": "0.0.129"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.127",
3
+ "version": "0.0.129",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,6 +12,6 @@
12
12
  ".": "./dist/index.js"
13
13
  },
14
14
  "peerDependencies": {
15
- "@stable-harness/core": "0.0.127"
15
+ "@stable-harness/core": "0.0.129"
16
16
  }
17
17
  }