stable-harness 0.0.16 → 0.0.17
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.
- package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
- package/dist/runtime/compat/json.d.ts +1 -0
- package/dist/runtime/compat/json.js +1 -1
- package/dist/runtime/compat/prompts.d.ts +5 -0
- package/dist/runtime/compat/prompts.js +1 -1
- package/package.json +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
- package/packages/cli/dist/src/output.js +1 -1
- package/packages/core/dist/boundary-scan.d.ts +38 -0
- package/packages/core/dist/boundary-scan.js +1 -0
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/runtime/capabilities.d.ts +47 -0
- package/packages/core/dist/runtime/capabilities.js +1 -0
- package/packages/core/dist/runtime/memory.d.ts +7 -1
- package/packages/core/dist/runtime/memory.js +1 -1
- package/packages/core/dist/runtime/progress-narration.d.ts +5 -0
- package/packages/core/dist/runtime/progress-narration.js +1 -1
- package/packages/core/dist/runtime.d.ts +2 -1
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/types.d.ts +3 -1
- package/packages/core/dist/workspace/types.d.ts +19 -0
- package/packages/workspace-yaml/dist/boundary-scan.d.ts +3 -0
- package/packages/workspace-yaml/dist/boundary-scan.js +1 -0
- package/packages/workspace-yaml/dist/discovery.js +1 -1
- package/packages/workspace-yaml/dist/documents.d.ts +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
- package/packages/workspace-yaml/dist/loader.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{generateWithModel as e}from"../model/ollama.js";import{parseToolCall as
|
|
1
|
+
import{generateWithModel as e}from"../model/ollama.js";import{containsRawToolCallText as t,parseToolCall as n}from"./json.js";import{buildAgentPrompt as o,buildFinalAnswerPrompt as r,buildRoutingPrompt as a,buildRoutingRepairPrompt as s,buildToolSelectionPrompt as i}from"./prompts.js";import{loadAgentTools as u}from"./tool-registry.js";export async function runAgentHarnessCompatAgent(t){const r=await async function resolveAgents(t){if(0===t.agent.subagentRefs.length)return[t.agent];const n=resolveModel(t.workspace,t.agent),o=t.request.input??"",r=await e(n,a(t.workspace,t.agent,o)),i=await async function repairRouting(t,n,o,r){let a=r,i=parseRouting(a);for(let r=0;r<3&&!hasEnoughRouting(t,o,i);r+=1)a=await e(n,s({workspace:t.workspace,agent:t.agent,userInput:o,previousResponse:a,issue:routingIssue(t,o,i)})),i=parseRouting(a);return hasEnoughRouting(t,o,i)?i:function readConfiguredRoutingTree(e,t){if(requiredRoutingCount(e,t)<=1)return[];const n=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim());for(const o of n){if(!/delegation tree|路由|委托/u.test(o)||!/must|必须|include|包含/u.test(o))continue;const n=e.subagentRefs.filter(e=>{return new RegExp(`\\b${t=e,t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}\\b`,"u").test(o);var t});if(n.length>=requiredRoutingCount(e,t))return n}return[]}(t.agent,o)}(t,n,o,r);return i.map(e=>t.workspace.agents.get(e)).filter(e=>Boolean(e))}(t),i=r[0]??t.agent,l=[];!function emitRoutingEvents(e,t,n,o){if(t.length>1&&e.request.dataListener?.({type:"delegation.plan",requestId:e.requestId,agentId:e.agent.id,agentIds:t.map(e=>e.id)}),n.id===e.agent.id)return;const r={agentId:e.agent.id,toolName:"task",output:n.id};o.push(r),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...r}),e.request.dataListener?.({type:"delegation.start",requestId:e.requestId,agentId:n.id})}(t,r,i,l);const c=resolveModel(t.workspace,i),d=await u(t.workspace,i.toolRefs),g=[],f=new Set;if(i.toolRefs.length>0){const e=await forceToolSelection(t,i,d,g);e&&d.has(e.name)&&(recordToolResult(t,l,g,await invokeToolWithStart(t,i,d.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments)),await ensureEvidenceAfterPlanning(t,i,d,f,l,g))}for(let r=0;r<4;r+=1){if(t.cancelled.has(t.requestId))return completed(t.requestId,g.join("\n\n"),l,i.id);const r=o({workspace:t.workspace,agent:i,model:c,userInput:t.request.input??"",tools:d,observations:g}),a=await e(c,r),s=n(a);if(!s&&hasEvidence(l)){const e=isUsableFinalAnswer(a)?a:await synthesizeFinalAnswer(t,i,g);return completed(t.requestId,e,l,i.id)}if(s&&shouldSynthesizeRepeatedToolIntent(s,f,g)){const e=await synthesizeFinalAnswer(t,i,g);return completed(t.requestId,e,l,i.id)}const u=s??await forceToolSelection(t,i,d,g);if(!u||!d.has(u.name)){if(hasEvidence(l)){const e=await synthesizeFinalAnswer(t,i,g);return completed(t.requestId,e,l,i.id)}const e=await forceToolSelection(t,i,availableNextTools(d,f),g);if(e&&d.has(e.name)){recordToolResult(t,l,g,await invokeToolWithStart(t,i,d.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(t.requestId,a,l,i.id)}const p=toolKey(u.name,u.arguments);if(f.has(p)){const e=await forceToolSelection(t,i,availableNextTools(d,f),g);if(e&&d.has(e.name)){recordToolResult(t,l,g,await invokeToolWithStart(t,i,d.get(e.name),e.arguments)),f.add(toolKey(e.name,e.arguments));continue}return completed(t.requestId,g.join("\n\n"),l,i.id)}recordToolResult(t,l,g,await invokeToolWithStart(t,i,d.get(u.name),u.arguments)),f.add(p),await ensureEvidenceAfterPlanning(t,i,d,f,l,g)}const p=hasEvidence(l)?await synthesizeFinalAnswer(t,i,g):g.join("\n\n");return completed(t.requestId,p,l,i.id)}async function forceToolSelection(t,o,r,a){if(t.cancelled.has(t.requestId)||0===r.size)return;const s=resolveModel(t.workspace,o),u=await e(s,i({agent:o,userInput:t.request.input??"",tools:r,observations:a}));return n(u)}function hasEnoughRouting(e,t,n){return n.filter(t=>e.workspace.agents.has(t)).length>=requiredRoutingCount(e.agent,t)}function requiredRoutingCount(e,t){const n=t.split(/\r?\n/u).filter(e=>/^\s*\d+[.)、]/u.test(e)).length;return Math.max(1,Math.min(e.subagentRefs.length,n||1))}function routingIssue(e,t,n){const o=requiredRoutingCount(e.agent,t);return 0===n.length?"No valid available subagent id was returned.":`Only ${n.length} valid subagent ids were returned; at least ${o} are required for this multi-part request.`}function resolveModel(e,t){const n=t.modelRef??"default",o=e.models.get(n)??e.models.get("default");if(!o)throw new Error(`No model configured for agent ${t.id}`);return o}async function invokeToolWithStart(e,t,n,o){return e.request.dataListener?.({type:"agent.tool.start",requestId:e.requestId,agentId:t.id,toolName:n.name}),await new Promise(e=>setImmediate(e)),e.cancelled.has(e.requestId)?{agentId:t.id,toolName:n.name,output:"cancelled before tool invocation",isError:!0}:async function invokeTool(e,t,n,o){const r=process.cwd();try{process.chdir(e.workspace.workspaceRoot);const r=await n.invoke(o);return{agentId:t.id,toolName:n.name,output:stringifyOutput(r)}}catch(e){return{agentId:t.id,toolName:n.name,output:stringifyOutput(e),isError:!0}}finally{process.chdir(r)}}(e,t,n,o)}function recordToolResult(e,t,n,o){t.push(o),e.request.dataListener?.({type:"agent.tool.result",requestId:e.requestId,...o}),n.push(`Tool ${o.toolName} returned:\n${o.output}`)}function isPlanningTool(e){return"task"===e}function hasEvidence(e){return e.some(e=>!e.isError&&!isPlanningTool(e.toolName))}async function ensureEvidenceAfterPlanning(e,t,n,o,r,a){if(hasEvidence(r))return;const s=await forceToolSelection(e,t,availableNextTools(n,o),a);s&&n.has(s.name)&&(recordToolResult(e,r,a,await invokeToolWithStart(e,t,n.get(s.name),s.arguments)),o.add(toolKey(s.name,s.arguments)))}async function synthesizeFinalAnswer(t,n,o){if(0===o.length)return"";const a=resolveModel(t.workspace,n);try{const s=await e(a,r({agent:n,userInput:t.request.input??"",observations:o}));return isUsableFinalAnswer(s)?s:o.join("\n\n")}catch{return o.join("\n\n")}}function isUsableFinalAnswer(e){return e.trim().length>0&&!t(e)}function shouldSynthesizeRepeatedToolIntent(e,t,n){if(!hasExecutedTool(e.name,t))return!1;const o=[...t].filter(t=>t.startsWith(`${e.name}:`)).length<2&&!t.has(toolKey(e.name,e.arguments))&&function latestEvidenceNeedsRetry(e){const t=e.at(-1)??"";return/Quality evaluation:\s*status:\s*(?:failed|weak)|nextAction:\s*make one corrected research call/iu.test(t)}(n);return!o}function availableNextTools(e,t){return new Map([...e].filter(([e])=>!isPlanningTool(e)&&!hasExecutedTool(e,t)))}function hasExecutedTool(e,t){return[...t].some(t=>t.startsWith(`${e}:`))}function parseRouting(e){try{const t=function parseJsonObject(e){try{return JSON.parse(e)}catch{const t=e.indexOf("{"),n=e.lastIndexOf("}");if(t>=0&&n>t)return JSON.parse(e.slice(t,n+1));throw new Error("No JSON object found")}}(e),n=function readRoutingIds(e){for(const t of["agentIds","routing","agents","delegations","plan"]){const n=e[t],o=Array.isArray(n)?n.filter(e=>"string"==typeof e):[];if(o.length>0)return[...new Set(o)]}return[]}(t);if(n.length>0)return n;const o=function readRoutingId(e){for(const t of["agentId","agent","owner","route"]){const n=e[t];if("string"==typeof n&&n.trim().length>0)return n}}(t);return o?[o]:[]}catch{return[]}}function completed(e,t,n,o){return{requestId:e,state:"completed",output:t,metadata:{executedToolResults:n,routedAgentId:o}}}function stringifyOutput(e){return e instanceof Error?e.stack??e.message:"string"==typeof e?e:JSON.stringify(e)}function toolKey(e,t){return`${e}:${JSON.stringify(t)}`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function parseToolCall(
|
|
1
|
+
export function parseToolCall(t){return function parseToolCalls(t){if("string"!=typeof t)return[];const n=[];for(const e of function jsonCandidates(t){return[t.trim(),extractJsonObject(t)].filter(t=>Boolean(t))}(t)){const t=normalizeToolCall(safeParse(e));t&&n.push(t)}for(const e of function objectCandidates(t){return t.match(/\{[^{}]*"name"\s*:\s*"[^"]+"[\s\S]*?\}\s*\}/gu)??[]}(t)){const t=normalizeToolCall(safeParse(e));t&&n.push(t)}return function uniqueToolCalls(t){const n=new Set;return t.filter(t=>{const e=`${t.name}:${JSON.stringify(t.arguments)}`;return!n.has(e)&&(n.add(e),!0)})}(n)}(t)[0]}export function containsRawToolCallText(t){return void 0!==parseToolCall(t)||[/<\s*\/?\s*(?:tool_call|tool_code|task)\b[^>]*>/iu,/\{\s*"name"\s*:\s*"[^"]+"\s*,\s*"arguments"\s*:/iu,/\{\s*"tool_name"\s*:\s*"[^"]+"\s*,\s*"parameters"\s*:/iu,/```(?:json)?[\s\S]{0,4000}"(?:name|tool|tool_name)"\s*:[\s\S]{0,4000}"(?:arguments|parameters|args)"\s*:/iu].some(n=>n.test(t))}export function extractJsonObject(t){const n=t.indexOf("{"),e=t.lastIndexOf("}");return n>=0&&e>n?t.slice(n,e+1):void 0}function safeParse(t){try{return JSON.parse(t)}catch{const n=function repairTrailingJson(t){const n=t.trim();if(!n.startsWith("{"))return;let e=0,r=0,o=!1,s=!1;for(const t of n)o?(s="\\"===t&&!s,'"'!==t||s||(o=!1),"\\"!==t&&(s=!1)):('"'===t&&(o=!0),"{"===t&&(e+=1),"}"===t&&(e-=1),"["===t&&(r+=1),"]"===t&&(r-=1));return o||e<0||r<0||e+r>3?void 0:`${n}${"]".repeat(r)}${"}".repeat(e)}`}(t);if(!n)return;try{return JSON.parse(n)}catch{return}}}function normalizeToolCall(t){if("object"!=typeof t||null===t)return;const n=t;if("string"!=typeof n.name)return;const e="object"==typeof n.arguments&&null!==n.arguments?n.arguments:{};return{name:n.name,arguments:e}}
|
|
@@ -22,3 +22,8 @@ export declare function buildToolSelectionPrompt(input: {
|
|
|
22
22
|
tools: Map<string, LoadedTool>;
|
|
23
23
|
observations?: string[];
|
|
24
24
|
}): string;
|
|
25
|
+
export declare function buildFinalAnswerPrompt(input: {
|
|
26
|
+
agent: CompiledAgent;
|
|
27
|
+
userInput: string;
|
|
28
|
+
observations: string[];
|
|
29
|
+
}): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function buildAgentPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[e.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.",toolPolicyExcerpt(e.agent,e.tools),'If a tool is needed, return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Before returning a final answer, re-read the tool policy excerpts and run any required follow-up evidence tool for this request.","If enough evidence is available, return the final answer only.","Available tools:",JSON.stringify(o,null,2),e.observations.length?`Tool observations:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildRoutingPrompt(e,o,t){const n=o.subagentRefs.map(o=>e.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return["Choose the best subagent plan for this request.",routingPolicyExcerpt(o,n.map(e=>e.id)),o.deepAgentConfig.systemPrompt??"","Routing policy excerpts are authoritative workspace config.","This is a routing-only step. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","If a routing policy says a request must include one or more available subagents, include those subagents.",'If one owner is enough, return exactly JSON: {"agentId":"one_id","reason":"short reason"}.','If the request contains multiple specialist-owned tasks, return exactly JSON: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"For numbered or multi-part requests, check every item and include an owner for each distinct specialist-owned task.","Do not omit later tasks after selecting earlier owners.","Return only ids from Available subagents.","Available subagents:",JSON.stringify(n,null,2),`User request:\n${t}`].filter(Boolean).join("\n")}export function buildRoutingRepairPrompt(e){const o=e.agent.subagentRefs.map(o=>e.workspace.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return[e.issue??"Your previous routing response did not contain a valid complete routing plan.",routingPolicyExcerpt(e.agent,o.map(e=>e.id)),"This is a routing-only repair. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","Return JSON only.",'For one owner: {"agentId":"one_id","reason":"short reason"}.','For multiple owners: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"Use only ids from Available subagents.","Available subagents:",JSON.stringify(o,null,2),`Previous response:\n${e.previousResponse}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildToolSelectionPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[`Agent: ${e.agent.id}`,e.agent.description?`Responsibility: ${e.agent.description}`:"",toolPolicyExcerpt(e.agent,e.tools),"Choose the single best evidence tool from the available tools.",'Return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Infer arguments from the user request, tool description, and tool policy excerpts.","If the user explicitly names an available tool, choose that exact tool.","Do not use empty arguments when the tool policy or user request provides a concrete schema, URL, path, question, ticker, or command.","Prefer the most specific evidence tool for the requested artifact or operation before choosing a general search tool.","Available tools:",JSON.stringify(o,null,2),e.observations?.length?`Tool observations already gathered:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}function toolPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??""),n=[...o.keys()].filter(e=>"write_todos"!==e&&"read_todos"!==e),r=t.split(/\r?\n/u).map(e=>e.trim()).filter(e=>n.some(o=>e.includes(o))).slice(0,12);return r.length>0?`Tool policy excerpts:\n${r.join("\n")}`:""}function routingPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim()).filter(e=>function isRoutingPolicyLine(e,o){const t=e.toLowerCase();return o.some(o=>e.includes(o))&&/\b(route|routing|delegate|delegation|owner|specialist|include|tree|task)\b/u.test(t)}(e,o)),n=t.map(e=>({line:e,score:routingPolicyScore(e,o)})).sort((e,o)=>o.score-e.score).map(e=>e.line).slice(0,16);return n.length>0?`Routing policy excerpts:\n${n.join("\n")}`:""}function routingPolicyScore(e,o){const t=e.toLowerCase();return 10*o.filter(o=>e.includes(o)).length+(t.match(/\b(must|include|order|tree|requires|required|exactly|only)\b/gu)??[]).length}
|
|
1
|
+
export function buildAgentPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[e.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.",toolPolicyExcerpt(e.agent,e.tools),'If a tool is needed, return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Before returning a final answer, re-read the tool policy excerpts and run any required follow-up evidence tool for this request.","If enough evidence is available, return the final answer only.","Available tools:",JSON.stringify(o,null,2),e.observations.length?`Tool observations:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildRoutingPrompt(e,o,t){const n=o.subagentRefs.map(o=>e.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return["Choose the best subagent plan for this request.",routingPolicyExcerpt(o,n.map(e=>e.id)),o.deepAgentConfig.systemPrompt??"","Routing policy excerpts are authoritative workspace config.","This is a routing-only step. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","If a routing policy says a request must include one or more available subagents, include those subagents.",'If one owner is enough, return exactly JSON: {"agentId":"one_id","reason":"short reason"}.','If the request contains multiple specialist-owned tasks, return exactly JSON: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"For numbered or multi-part requests, check every item and include an owner for each distinct specialist-owned task.","Do not omit later tasks after selecting earlier owners.","Return only ids from Available subagents.","Available subagents:",JSON.stringify(n,null,2),`User request:\n${t}`].filter(Boolean).join("\n")}export function buildRoutingRepairPrompt(e){const o=e.agent.subagentRefs.map(o=>e.workspace.agents.get(o)).filter(e=>Boolean(e)).map(e=>({id:e.id,description:e.description}));return[e.issue??"Your previous routing response did not contain a valid complete routing plan.",routingPolicyExcerpt(e.agent,o.map(e=>e.id)),"This is a routing-only repair. Tools are unavailable here.","Do not return write_todos, read_todos, task, markdown, or prose.","Return JSON only.",'For one owner: {"agentId":"one_id","reason":"short reason"}.','For multiple owners: {"agentIds":["first_id","second_id"],"reason":"short reason"}.',"Use only ids from Available subagents.","Available subagents:",JSON.stringify(o,null,2),`Previous response:\n${e.previousResponse}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildToolSelectionPrompt(e){const o=[...e.tools.values()].map(e=>({name:e.name,description:e.description??""}));return[`Agent: ${e.agent.id}`,e.agent.description?`Responsibility: ${e.agent.description}`:"",toolPolicyExcerpt(e.agent,e.tools),"Choose the single best evidence tool from the available tools.",'Return exactly one JSON object: {"name":"tool_name","arguments":{...}}.',"Infer arguments from the user request, tool description, and tool policy excerpts.","If the user explicitly names an available tool, choose that exact tool.","Do not use empty arguments when the tool policy or user request provides a concrete schema, URL, path, question, ticker, or command.","Prefer the most specific evidence tool for the requested artifact or operation before choosing a general search tool.","Available tools:",JSON.stringify(o,null,2),e.observations?.length?`Tool observations already gathered:\n${e.observations.join("\n\n")}`:"",`User request:\n${e.userInput}`].filter(Boolean).join("\n")}export function buildFinalAnswerPrompt(e){return[e.agent.deepAgentConfig.systemPrompt??"","","You are running inside stable-harness.","Tools are unavailable in this final-answer step.","Use only the tool observations below as evidence.","Return the final user-facing answer now.","Do not return JSON tool calls, markdown code fences, XML tool markup, pseudo tool calls, or future-intent text.","If the evidence is incomplete, answer with the supported findings and state the concrete gaps.","Match the user's request language unless the user explicitly requested another language.",`Tool observations:\n${e.observations.join("\n\n")}`,`User request:\n${e.userInput}`].filter(Boolean).join("\n")}function toolPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??""),n=[...o.keys()].filter(e=>"write_todos"!==e&&"read_todos"!==e),r=t.split(/\r?\n/u).map(e=>e.trim()).filter(e=>n.some(o=>e.includes(o))).slice(0,12);return r.length>0?`Tool policy excerpts:\n${r.join("\n")}`:""}function routingPolicyExcerpt(e,o){const t=String(e.deepAgentConfig.systemPrompt??"").split(/\r?\n/u).map(e=>e.trim()).filter(e=>function isRoutingPolicyLine(e,o){const t=e.toLowerCase();return o.some(o=>e.includes(o))&&/\b(route|routing|delegate|delegation|owner|specialist|include|tree|task)\b/u.test(t)}(e,o)),n=t.map(e=>({line:e,score:routingPolicyScore(e,o)})).sort((e,o)=>o.score-e.score).map(e=>e.line).slice(0,16);return n.length>0?`Routing policy excerpts:\n${n.join("\n")}`:""}function routingPolicyScore(e,o){const t=e.toLowerCase();return 10*o.filter(o=>e.includes(o)).length+(t.match(/\b(must|include|order|tree|requires|required|exactly|only)\b/gu)??[]).length}
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as o,normalizeFilesystemArgs as
|
|
1
|
+
import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as o,normalizeFilesystemArgs as n,normalizeWriteTodosArgs as r,shallowEqualRecord as i}from"./builtin-args.js";import{afterToolInvoke as a,beforeToolInvoke as l,createToolRepeatState as s,stringifyDeepAgentResult as u,toolControlProjection as c}from"./gateway-tools.js";import{validateSkillFileBuiltinCall as d}from"./skill-file-policy.js";import{filterRepeatLimitedTools as g}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as p}from"./trace-projection.js";const m=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),f=new Set(["write_todos","task",...m]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,n){const r=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,r);return n({...o,tools:r,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,o,n){if(isFilesystemDisabled(e)&&m.has(o))return new t({tool_call_id:n.toolCall?.id??`stable-harness-${o}-policy`,name:o,content:`Filesystem builtin tool '${o}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,c={}){const g=c.repeatState??s(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(s,p){const h=s.toolCall?.name;if(!h||!f.has(h))return p(s);const y=function normalizeBuiltinToolRequest(e,t,a){return"write_todos"===t?{...a,toolCall:{...a.toolCall,args:r(a.toolCall?.args)}}:"task"===t?{...a,toolCall:{...a.toolCall,args:o(a.toolCall?.args)}}:m.has(t)?function normalizeFilesystemToolRequest(e,t,r){const a=o(r.toolCall?.args),l=n(t,a,e.workspace.root);return i(a,l)?r:{...r,toolCall:{...r.toolCall,args:l}}}(e,t,a):a}(e,h,s);emitToolEvent(e,h,"agent.tool.start",y.toolCall?.args);const v=g?l(h,y.toolCall?.args,g):void 0;if(v)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:v.eventOutput}),builtinToolMessage(s,h,v.modelOutput);const b="task"===h?function validateTaskCall(e,o){const n=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(o.toolCall?.args),r=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const o=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===o?[]:Array.isArray(o)?o.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(e);if(void 0===r||n&&r.includes(n))return;const i=n?`: ${n}`:"",a=r.length>0?r.join(", "):"none";return new t({tool_call_id:o.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${i}. Allowed task targets: ${a}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}(e,y):void 0;if(b)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:b.content}),b;const w=validateFilesystemBuiltinCall(e,h,y);if(w)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:w.content}),w;const T=d(e,h,y);if(T)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:T.content}),T;try{const t=await p(y),o=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):u(o)}(h,y,t),n=g?a(h,y.toolCall?.args,o,t,g):{};return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:n.eventOutput??o}),c.observedToolIds?.add(h),void 0===n.modelOutput?t:builtinToolMessage(s,h,n.modelOutput)}catch(o){const n=function recoverableBuiltinToolError(e,o,n){const r=formatError(n);return"task"===e&&/repeat limit reached for tool/iu.test(r)?new t({tool_call_id:o.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need, do not send a synthesis task to another subagent for the same need, and produce the best final answer now from the evidence already returned in this run. If coverage is incomplete, report the exact remaining evidence gap explicitly.",error:previewError(r)})}):/Received tool input did not match expected schema|Invalid input:/iu.test(r)?new t({tool_call_id:o.toolCall?.id??`stable-harness-${e}-argument-error`,name:e,status:"error",content:JSON.stringify({status:"tool_argument_error",toolId:e,instruction:"The upstream builtin tool rejected these arguments. Fix the tool arguments according to the tool schema, or choose a more appropriate available tool.",error:previewError(r)})}):void 0}(h,s,o);if(n)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:n.content}),n;throw emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{error:formatError(o)}),o}}}}function builtinToolMessage(e,o,n){return new t({tool_call_id:e.toolCall?.id??`stable-harness-${o}-repeat-guard`,name:o,content:n})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),n=[];if(n.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,n=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...n?[n,`${n}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return n.length>0?n:void 0}else n.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),n.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||n.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),n.length>0?n:void 0}function emitToolEvent(e,t,o,n,r={}){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:n}:{},...r,..."string"==typeof r.output?c(r.output):{},...p(t,o,n)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),n=t.split("/").filter(Boolean);if(!(n.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==n[e])return;return n.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&m.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{compileWorkflowPlan as o,renderAgentMermaid as r,renderWorkflowMermaid as e}from"@stable-harness/core";export function workspaceStatus(o,r){return[`stable-harness workspace: ${o.runtime.workspaceId??r}`,`root: ${o.root}`,`default agent: ${o.runtime.defaultAgentId}`,`agents: ${[...o.agents.keys()].sort().join(", ")||"none"}`,`workflows: ${[...o.workflows.keys()].sort().join(", ")||"none"}`,`evaluations: ${[...o.evaluations?.keys()??[]].sort().join(", ")||"none"}`,`default workflow: ${o.runtime.workflowRouting?.defaultWorkflowId??"none"}`,`workflow routes: ${formatWorkflowRoutes(o)}`,`tools: ${o.tools.size}`,"","Run a request with:"," stable-harness [prompt]"," stable-harness --agent <id> [prompt]"," stable-harness workflow render <workflow-id>"," stable-harness workflow inspect <workflow-id>"," stable-harness agent render <agent-id>"," stable-harness start",""].join("\n")}export function renderWorkflow(o,r){const n=readWorkflow(o,r,"render");return`${e(n)}\n`}export function renderAgent(o,e){if(!e)throw new Error("Usage: stable-harness agent render <agent-id>");return`${r(o,e)}\n`}export function inspectWorkflow(r,e){const n=readWorkflow(r,e,"inspect");return`${JSON.stringify({workflow:n,plan:o(n)},null,2)}\n`}export function formatDetail(o){return o&&"string"==typeof o.toolId?`:${o.toolId}`:""}function readWorkflow(o,r,e){if(!r)throw new Error(`Usage: stable-harness workflow ${e} <workflow-id>`);const n=o.workflows.get(r);if(!n)throw new Error(`Workflow is not defined: ${r}`);return n}function formatWorkflowRoutes(o){return(o.runtime.workflowRouting?.routes??[]).map(o=>o.id).sort().join(", ")||"none"}
|
|
1
|
+
import{compileWorkflowPlan as o,renderAgentMermaid as r,renderWorkflowMermaid as e}from"@stable-harness/core";export function workspaceStatus(o,r){return[`stable-harness workspace: ${o.runtime.workspaceId??r}`,`root: ${o.root}`,`default agent: ${o.runtime.defaultAgentId}`,`agents: ${[...o.agents.keys()].sort().join(", ")||"none"}`,`workflows: ${[...o.workflows.keys()].sort().join(", ")||"none"}`,`evaluations: ${[...o.evaluations?.keys()??[]].sort().join(", ")||"none"}`,`default workflow: ${o.runtime.workflowRouting?.defaultWorkflowId??"none"}`,`workflow routes: ${formatWorkflowRoutes(o)}`,`tools: ${o.tools.size}`,`diagnostics: ${formatDiagnostics(o)}`,"","Run a request with:"," stable-harness [prompt]"," stable-harness --agent <id> [prompt]"," stable-harness workflow render <workflow-id>"," stable-harness workflow inspect <workflow-id>"," stable-harness agent render <agent-id>"," stable-harness start",""].join("\n")}export function renderWorkflow(o,r){const n=readWorkflow(o,r,"render");return`${e(n)}\n`}export function renderAgent(o,e){if(!e)throw new Error("Usage: stable-harness agent render <agent-id>");return`${r(o,e)}\n`}export function inspectWorkflow(r,e){const n=readWorkflow(r,e,"inspect");return`${JSON.stringify({workflow:n,plan:o(n)},null,2)}\n`}export function formatDetail(o){return o&&"string"==typeof o.toolId?`:${o.toolId}`:""}function readWorkflow(o,r,e){if(!r)throw new Error(`Usage: stable-harness workflow ${e} <workflow-id>`);const n=o.workflows.get(r);if(!n)throw new Error(`Workflow is not defined: ${r}`);return n}function formatWorkflowRoutes(o){return(o.runtime.workflowRouting?.routes??[]).map(o=>o.id).sort().join(", ")||"none"}function formatDiagnostics(o){const r=o.diagnostics??[];return 0===r.length?"none":[`${r.filter(o=>"error"===o.severity).length} error`,`${r.filter(o=>"warning"===o.severity).length} warning`].join(", ")}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { WorkspaceBoundaryDiagnostic, WorkspaceBoundaryDiagnosticCode, WorkspaceBoundaryScanPolicy } from "./workspace/types.js";
|
|
2
|
+
export type BoundaryScanLayer = "tool" | "skill" | "subagent";
|
|
3
|
+
export type BoundaryScanResource = {
|
|
4
|
+
id: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
tools?: string[];
|
|
8
|
+
skills?: string[];
|
|
9
|
+
subagents?: string[];
|
|
10
|
+
locations?: string[];
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
export type BoundaryScanFinding = {
|
|
14
|
+
code: WorkspaceBoundaryDiagnosticCode;
|
|
15
|
+
layer: BoundaryScanLayer;
|
|
16
|
+
resources: string[];
|
|
17
|
+
message: string;
|
|
18
|
+
locations: string[];
|
|
19
|
+
details: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
export type BoundaryScanLayerResult = {
|
|
22
|
+
resources: BoundaryScanResource[];
|
|
23
|
+
findings: BoundaryScanFinding[];
|
|
24
|
+
};
|
|
25
|
+
export type BoundaryScanResult = {
|
|
26
|
+
tools: BoundaryScanLayerResult;
|
|
27
|
+
skills: BoundaryScanLayerResult;
|
|
28
|
+
subagents: BoundaryScanLayerResult;
|
|
29
|
+
findings: BoundaryScanFinding[];
|
|
30
|
+
};
|
|
31
|
+
export declare function scanAgentBoundary(input: {
|
|
32
|
+
tools?: BoundaryScanResource[];
|
|
33
|
+
skills?: BoundaryScanResource[];
|
|
34
|
+
subagents?: BoundaryScanResource[];
|
|
35
|
+
}): BoundaryScanResult;
|
|
36
|
+
export declare function checkBoundaryScanPolicy(result: BoundaryScanResult, policy: WorkspaceBoundaryScanPolicy | undefined): WorkspaceBoundaryDiagnostic[];
|
|
37
|
+
export declare function assertBoundaryPolicyDiagnostics(diagnostics: WorkspaceBoundaryDiagnostic[]): void;
|
|
38
|
+
export declare function formatBoundaryDiagnostic(diagnostic: WorkspaceBoundaryDiagnostic): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function scanAgentBoundary(e){const n=scanLayer("tool",e.tools??[]),i=scanLayer("skill",e.skills??[]),o=scanLayer("subagent",e.subagents??[]);return{tools:n,skills:i,subagents:o,findings:[...n.findings,...i.findings,...o.findings]}}export function checkBoundaryScanPolicy(e,n){return e.findings.map(e=>({code:e.code,layer:e.layer,resources:e.resources,severity:!0===n?.failOn?.[e.code]?"error":"warning",message:e.message,locations:e.locations,details:e.details}))}export function assertBoundaryPolicyDiagnostics(e){if(0!==e.filter(e=>"error"===e.severity).length)throw new Error(`Workspace boundary scan failed:\n${e.map(formatBoundaryDiagnostic).join("\n")}`)}export function formatBoundaryDiagnostic(e){const n=e.locations.length>0?` (${e.locations.join(", ")})`:"";return`- ${e.severity} ${e.code} [${e.layer}] ${e.resources.join(", ")}: ${e.message}${n}`}function scanLayer(e,n){const i=n.flatMap(i=>[...scanSingleResource(e,i),...scanHardcodedSiblingRouting(e,i,n)]);for(let o=0;o<n.length;o+=1)for(let t=o+1;t<n.length;t+=1)i.push(...scanResourcePair(e,n[o],n[t]));return{resources:n,findings:i}}function scanSingleResource(e,n){const i=[],o=resourceText(n);return(0===(n.description??"").trim().length||meaningfulTokens(o).length<3)&&i.push(createFinding("weak-positive-boundary",e,[n],`${e} ${n.id} does not declare a clear positive responsibility boundary.`)),function hasBroadFallbackLanguage(e){return positiveSentences(e).some(e=>/\b(any|anything|all|everything|general|generic|fallback|catch[-\s]?all)\b/iu.test(e))}(o)&&!function hasLocalScopeLanguage(e){return/\b(only|specific|scope|focused|owned|responsible|specialized|local|bounded)\b/iu.test(e)}(o)&&i.push(createFinding("broad-fallback-scope",e,[n],`${e} ${n.id} uses broad fallback language without declaring local scope.`)),function hasOnlyExclusionLanguage(e){return/\b(do not|don't|except|exclude|avoid|not for|never)\b/iu.test(e)&&!/\b(handle|provide|manage|analyze|create|run|execute|inspect|summarize|search|own|owns)\b/iu.test(e)}(o)&&i.push(createFinding("weak-positive-boundary",e,[n],`${e} ${n.id} relies on exclusions without a positive capability boundary.`)),i}function scanHardcodedSiblingRouting(e,n,i){return i.filter(e=>e.id!==n.id).filter(e=>function mentionsSiblingRouting(e,n){const i=n.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&"),o=new RegExp(`\\b(route|delegate|send|handoff|assign|use)\\b[^.!?\\n]{0,80}(?:^|[^\\p{L}\\p{N}_-])${i}(?:$|[^\\p{L}\\p{N}_-])`,"iu");return positiveSentences(e).some(e=>o.test(e))}(resourceText(n),e.id)).map(i=>createFinding("hardcoded-sibling-routing",e,[n,i],`${e} ${n.id} hardcodes routing to sibling ${i.id}; use loaded metadata or explicit runtime policy instead.`,{sibling:i.id}))}function scanResourcePair(e,n,i){const o=normalizeScope(n.description??""),t=normalizeScope(i.description??"");if(o&&o===t)return[createFinding("duplicate-scope",e,[n,i],`${e} resources ${n.id} and ${i.id} declare the same responsibility scope.`)];const s=function lexicalSimilarity(e,n){const i=new Set(meaningfulTokens(e)),o=new Set(meaningfulTokens(n));return 0===i.size||0===o.size?0:[...i].filter(e=>o.has(e)).length/new Set([...i,...o]).size}(resourceText(n),resourceText(i));return s>=.72?[createFinding("possible-sibling-overlap",e,[n,i],`${e} resources ${n.id} and ${i.id} have likely overlapping model-visible boundaries.`,{similarity:s})]:[]}function createFinding(e,n,i,o,t={}){return{code:e,layer:n,resources:i.map(e=>e.id),message:o,locations:[...new Set(i.flatMap(e=>e.locations??[]))],details:{...t,resourceCount:i.length}}}function resourceText(e){return[e.description,e.text,e.tools?.join(" "),e.skills?.join(" "),e.subagents?.join(" ")].filter(Boolean).join("\n")}function normalizeScope(e){return meaningfulTokens(e).join(" ")}function meaningfulTokens(n){return[...n.toLowerCase().matchAll(/[a-z0-9][a-z0-9_-]{2,}/gu)].map(e=>e[0]).filter(n=>!e.has(n))}function positiveSentences(e){return e.split(/[.!?\n]+/u).map(e=>e.trim()).filter(e=>e.length>0).filter(e=>!/\b(do not|does not|don't|not for|never|exclude|avoid)\b/iu.test(e))}const e=new Set(["and","are","for","from","into","that","the","this","with","when","workspace","agent","subagent","skill","tool"]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./runtime/persistence/artifacts.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./evaluations/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
|
|
1
|
+
export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./evaluations/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { RuntimeMemoryContext } from "../types.js";
|
|
2
|
+
import type { RuntimeEvent, RuntimeEmit } from "./events.js";
|
|
3
|
+
import type { RuntimeOutput, RuntimeRequest, RuntimeResponse, RuntimeStore } from "./types.js";
|
|
4
|
+
import type { CompiledWorkspace, WorkspaceAgent } from "../workspace/types.js";
|
|
5
|
+
export type RuntimeCapabilityState = {
|
|
6
|
+
memory?: RuntimeMemoryContext;
|
|
7
|
+
pluginMemories?: RuntimeMemoryContext[];
|
|
8
|
+
};
|
|
9
|
+
export type RuntimeCapabilityContext = {
|
|
10
|
+
workspace: CompiledWorkspace;
|
|
11
|
+
store: RuntimeStore;
|
|
12
|
+
emit: RuntimeEmit;
|
|
13
|
+
request: RuntimeRequest;
|
|
14
|
+
requestId: string;
|
|
15
|
+
sessionId: string;
|
|
16
|
+
agent: WorkspaceAgent;
|
|
17
|
+
state: RuntimeCapabilityState;
|
|
18
|
+
};
|
|
19
|
+
export type RuntimeCapabilityModule = {
|
|
20
|
+
id: string;
|
|
21
|
+
onEvent?(event: RuntimeEvent, emit: RuntimeEmit): void;
|
|
22
|
+
beforeAdapterRun?(context: RuntimeCapabilityContext): Promise<Partial<RuntimeCapabilityState> | void> | Partial<RuntimeCapabilityState> | void;
|
|
23
|
+
beforeAdapterResultContract?(context: RuntimeCapabilityContext & {
|
|
24
|
+
result: RuntimeOutput;
|
|
25
|
+
}): Promise<void> | void;
|
|
26
|
+
afterAdapterResponse?(context: RuntimeCapabilityContext & {
|
|
27
|
+
result: RuntimeOutput;
|
|
28
|
+
response: RuntimeResponse;
|
|
29
|
+
}): Promise<void> | void;
|
|
30
|
+
stop?(): Promise<void> | void;
|
|
31
|
+
};
|
|
32
|
+
export type RuntimeCapabilityRegistry = {
|
|
33
|
+
modules: RuntimeCapabilityModule[];
|
|
34
|
+
state: RuntimeCapabilityState;
|
|
35
|
+
emitSideEffects(event: RuntimeEvent, emit: RuntimeEmit): void;
|
|
36
|
+
beforeAdapterRun(context: Omit<RuntimeCapabilityContext, "state">): Promise<RuntimeCapabilityState>;
|
|
37
|
+
beforeAdapterResultContract(context: Omit<RuntimeCapabilityContext, "state"> & {
|
|
38
|
+
result: RuntimeOutput;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
afterAdapterResponse(context: Omit<RuntimeCapabilityContext, "state"> & {
|
|
41
|
+
result: RuntimeOutput;
|
|
42
|
+
response: RuntimeResponse;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
stop(): Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
export declare function createRuntimeCapabilityRegistry(modules: Array<RuntimeCapabilityModule | undefined>): RuntimeCapabilityRegistry;
|
|
47
|
+
export declare function normalizeAdapterResult(result: string | RuntimeOutput): RuntimeOutput;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createRuntimeCapabilityRegistry(e){const t=e.filter(e=>Boolean(e)),o={};return{modules:t,state:o,emitSideEffects(e,o){for(const r of t)try{r.onEvent?.(e,o)}catch{continue}},async beforeAdapterRun(e){for(const r of t){const t=await(r.beforeAdapterRun?.({...e,state:o}));mergeCapabilityState(o,t)}return o},async beforeAdapterResultContract(e){for(const r of t)await(r.beforeAdapterResultContract?.({...e,state:o}))},async afterAdapterResponse(e){for(const r of t)await(r.afterAdapterResponse?.({...e,state:o}))},async stop(){await Promise.all(t.map(e=>e.stop?.()))}}}export function normalizeAdapterResult(e){return"string"==typeof e?{text:e}:e}function mergeCapabilityState(e,t){t&&(void 0!==t.memory&&(e.memory=t.memory),t.pluginMemories&&(e.pluginMemories=[...e.pluginMemories??[],...t.pluginMemories]))}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApprovalQueue } from "@stable-harness/governance";
|
|
2
|
-
import type { RuntimeMemoryStore } from "@stable-harness/memory";
|
|
2
|
+
import type { MemoryProvider, RuntimeMemoryStore } from "@stable-harness/memory";
|
|
3
3
|
import type { CompiledWorkspace, RuntimeEvent, RuntimeMemoryContext, RuntimeRequest, WorkspaceAgent } from "../types.js";
|
|
4
|
+
import type { RuntimeCapabilityModule } from "./capabilities.js";
|
|
4
5
|
export declare function emitMemoryLifecycle(memory: RuntimeMemoryStore | undefined, emit: (event: RuntimeEvent) => void, requestId: string, sessionId: string, agentId: string, hook: "read-before-plan" | "read-before-finalize" | "write-after-run"): void;
|
|
5
6
|
export declare function runMemoryRecall(input: {
|
|
6
7
|
memory: RuntimeMemoryStore | undefined;
|
|
@@ -21,3 +22,8 @@ export declare function submitMemoryCandidates(input: {
|
|
|
21
22
|
agent: WorkspaceAgent;
|
|
22
23
|
workspace: CompiledWorkspace;
|
|
23
24
|
}): Promise<void>;
|
|
25
|
+
export declare function createMemoryRuntimeCapability(input: {
|
|
26
|
+
memory?: RuntimeMemoryStore;
|
|
27
|
+
memoryProviders?: MemoryProvider[];
|
|
28
|
+
approvals?: ApprovalQueue;
|
|
29
|
+
}): RuntimeCapabilityModule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function emitMemoryLifecycle(e,r,t,
|
|
1
|
+
import{recallMemoryPlugins as e,runMemoryPlugins as r}from"../memory-plugins.js";export function emitMemoryLifecycle(e,r,s,t,o,a){e&&r({type:"runtime.memory.lifecycle",requestId:s,sessionId:t,agentId:o,hook:a})}export async function runMemoryRecall(e){if(!e.memory)return;if(emitMemoryLifecycle(e.memory,e.emit,e.requestId,e.sessionId,e.agent.id,"read-before-plan"),!1===e.request.memory?.recall)return;const r=resolveMemoryNamespace(e.workspace,e.agent,e.request),s=e.request.memory?.recall?.query??e.request.input,t=await e.memory.recall({namespace:r,query:s,limit:e.request.memory?.recall?.limit});return e.emit({type:"runtime.memory.recall.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,namespace:r,recordIds:t.records.map(e=>e.id),context:t.context}),{namespace:r,records:t.records,context:t.context}}export async function submitMemoryCandidates(e){if(!e.memory||!e.request.memory?.candidates?.length)return;emitMemoryLifecycle(e.memory,e.emit,e.requestId,e.sessionId,e.agent.id,"write-after-run");const r=resolveMemoryNamespace(e.workspace,e.agent,e.request);for(const s of e.request.memory.candidates){const t={...s,namespace:s.namespace??r},o=await e.memory.submitCandidate(t);if(e.emit({type:"runtime.memory.candidate.submitted",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,candidate:o.candidate,decision:o.decision,record:o.record}),"review"===o.decision.action&&e.approvals){const r=await e.approvals.create({kind:"memory_write",reason:o.decision.reason,requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,subject:{candidate:o.candidate,decision:o.decision}});e.emit({type:"runtime.memory.approval.requested",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,approval:r})}}}export function createMemoryRuntimeCapability(s){return{id:"runtime.memory",beforeAdapterRun:async r=>({memory:await runMemoryRecall({memory:s.memory,emit:r.emit,request:r.request,requestId:r.requestId,sessionId:r.sessionId,agent:r.agent,workspace:r.workspace}),pluginMemories:await e({providers:s.memoryProviders,request:r.request,agent:r.agent,workspace:r.workspace})}),async beforeAdapterResultContract(e){emitMemoryLifecycle(s.memory,e.emit,e.requestId,e.sessionId,e.agent.id,"read-before-finalize"),await submitMemoryCandidates({memory:s.memory,approvals:s.approvals,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,workspace:e.workspace})},async afterAdapterResponse(e){await r({providers:s.memoryProviders,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,workspace:e.workspace,result:e.result})}}}function resolveMemoryNamespace(e,r,s){return s.memory?.namespace??`${e.root}:${r.id}`}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RuntimeEvent } from "./events.js";
|
|
2
|
+
import type { RuntimeCapabilityModule } from "./capabilities.js";
|
|
2
3
|
export type RuntimeProgressNarrationProvider = {
|
|
3
4
|
name: string;
|
|
4
5
|
narrate(event: RuntimeEvent, context: RuntimeProgressNarrationContext): RuntimeProgressNarrationResult;
|
|
@@ -31,3 +32,7 @@ export declare function createProgressNarrationEvent(input: {
|
|
|
31
32
|
options?: RuntimeProgressNarrationOptions | false;
|
|
32
33
|
policy?: Record<string, unknown>;
|
|
33
34
|
}): RuntimeEvent | Promise<RuntimeEvent | undefined> | undefined;
|
|
35
|
+
export declare function createProgressNarrationCapability(input: {
|
|
36
|
+
options?: RuntimeProgressNarrationOptions | false;
|
|
37
|
+
policy?: Record<string, unknown>;
|
|
38
|
+
}): RuntimeCapabilityModule | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function resolveProgressNarrator(e){if(!1===e.options)return;const t=readProgressPolicy(e.policy);return e.options?.enabled??t.enabled?e.options?.provider??function createTemplateProgressNarrator(){const e=new Map,t=new Map;return{name:"template",narrate(r,n){var o;"runtime.request.started"===r.type&&(e.set(r.requestId,{language:(o=r.input??"",/\p{Script=Han}/u.test(o)?"zh":void 0),input:r.input}),t.delete(r.requestId));const i=function templateMessage(e,t){const r="zh"===t?.language;return"runtime.request.started"===e.type?r?`我开始处理这个请求:${summarizeText(e.input)}`:`I'm starting on this request: ${summarizeText(e.input)}`:"runtime.request.completed"===e.type?r?"我已经完成执行链,正在交付最终结果。":"I've finished the execution chain and am returning the final result.":"runtime.request.failed"===e.type?r?"执行链失败了,我会保留具体错误方便定位。":"The execution chain failed; I'm keeping the concrete error visible for diagnosis.":"runtime.request.cancelled"===e.type?r?"这个请求已取消,后续执行会停止。":"The request was cancelled, so the remaining execution will stop.":"runtime.tool.direct.started"===e.type?r?`我正在运行 ${e.toolId}。`:`I'm running ${e.toolId}.`:"runtime.tool.direct.completed"===e.type?r?`${e.toolId} 已返回结果,我会继续使用这份证据。`:`${e.toolId} returned; I'll keep using that evidence.`:"runtime.workflow.started"===e.type?r?`我正在启动 workflow ${e.workflowId}。`:`I'm starting workflow ${e.workflowId}.`:"runtime.workflow.completed"===e.type?r?`workflow ${e.workflowId} 已完成。`:`Workflow ${e.workflowId} is complete.`:"runtime.specDriven.phase.started"===e.type?r?`我正在进入 spec-driven 阶段 ${e.phaseId}。`:`I'm starting spec-driven phase ${e.phaseId}.`:"runtime.specDriven.phase.blocked"===e.type?r?`spec-driven 阶段 ${e.phaseId} 被 gate 阻塞。`:`Spec-driven phase ${e.phaseId} is blocked by a gate.`:"runtime.specDriven.phase.completed"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已完成。`:`Spec-driven phase ${e.phaseId} is complete.`:"runtime.specDriven.phase.verified"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已验证。`:`Spec-driven phase ${e.phaseId} is verified.`:"runtime.artifact.created"===e.type?r?"我已经保存一份运行产物。":"I've saved a runtime artifact.":"runtime.execution.contract.failed"===e.type?r?"运行证据没有满足执行契约,我会进入恢复或失败路径。":"The run evidence did not satisfy the execution contract, so I'll recover or fail explicitly.":"runtime.skill.candidate.created"===e.type?r?`我发现一个可沉淀的 skill 候选:${e.name}。`:`I found a reusable skill candidate: ${e.name}.`:e.type.startsWith("runtime.memory.")?function memoryMessage(e,t){return"runtime.memory.lifecycle"===e.type?t?`我进入记忆阶段:${e.hook}。`:`I'm in the memory lifecycle phase: ${e.hook}.`:"runtime.memory.recall.completed"===e.type?t?`我召回了 ${e.recordIds.length} 条相关记忆。`:`I recalled ${e.recordIds.length} relevant memory records.`:"runtime.memory.candidate.submitted"===e.type?t?"我提交了一条候选记忆。":"I submitted a memory candidate.":"runtime.memory.approval.requested"===e.type?t?"我提交了记忆审批请求。":"I requested memory approval.":"runtime.memory.plugin.started"===e.type?t?`我正在运行记忆插件 ${e.provider}。`:`I'm running memory plugin ${e.provider}.`:"runtime.memory.plugin.completed"===e.type?t?`记忆插件 ${e.provider} 已完成。`:`Memory plugin ${e.provider} completed.`:"runtime.memory.plugin.failed"===e.type?t?`记忆插件 ${e.provider} 失败了;主任务会继续,不把它当成执行结果。`:`Memory plugin ${e.provider} failed; the main task will continue without treating it as the result.`:"runtime.memory.maintenance.started"===e.type?t?`我正在维护 ${e.target} 记忆。`:`I'm maintaining ${e.target} memory.`:"runtime.memory.maintenance.completed"===e.type?t?`${e.target} 记忆维护完成。`:`${e.target} memory maintenance completed.`:"runtime.memory.maintenance.failed"===e.type?t?`${e.target} 记忆维护失败。`:`${e.target} memory maintenance failed.`:void 0}(e,r):"runtime.adapter.event"===e.type?function adapterMessage(e,t){if(isRecord(e))return"agent.handoff"===e.phase?t?"我把请求交给上游 agent backend,让它负责规划和执行。":"I'm handing the request to the upstream agent backend for planning and execution.":"agent.tool.start"===e.phase&&"string"==typeof e.toolId?function toolStartMessage(e,t,r){if("task"===e){const e=function readTaskTarget(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(t),n=function readDescription(e){const t=isRecord(e)?e:{};return summarizeText(readString(t.description)??readString(t.task))}(t);if(e&&n)return r?`我正在请求 task 工具委派给 ${e}:${n}`:`I'm asking the task tool to delegate to ${e}: ${n}`;if(e)return r?`我正在请求 task 工具委派给 ${e}。`:`I'm asking the task tool to delegate to ${e}.`}return r?`我正在运行 ${e} 收集证据。`:`I'm running ${e} to gather evidence.`}(e.toolId,e.args,t):"agent.tool.result"===e.phase&&"string"==typeof e.toolId?function toolResultMessage(e,t,r,n,o){if(r)return o?`${e} 返回错误:${summarizeText(String(r))}`:`${e} returned an error: ${summarizeText(String(r))}`;const i="string"==typeof n?n:function readToolControlStatus(e){const t=function parseToolOutputRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:"string"==typeof t?.error?t.error:"string"==typeof e&&e.startsWith("Task delegation target is not in the workspace inventory")?"task_inventory_blocked":void 0}(t);if(i)return function toolControlMessage(e,t,r){return"duplicate_tool_call"===t?r?`${e} 重复调用已复用已有证据。`:`${e} repeated an equivalent call, so the existing evidence was reused.`:"repeated_tool_call_limit"===t?r?`${e} 出现重复调用循环,我会基于已有证据继续收敛。`:`${e} entered a repeated-call loop, so I'll continue from the evidence already collected.`:"tool_argument_error"===t||"tool_argument_validation_failed"===t?r?`${e} 参数被工具 schema 拒绝,我会让 agent 修正参数或改用合适工具。`:`${e} arguments were rejected by the tool schema; I'll have the agent repair them or choose a suitable tool.`:"task_inventory_blocked"===t?r?"task 委派目标不在当前 workspace inventory 中,已被运行时策略阻止。":"The task delegation target is not in the current workspace inventory and was blocked by runtime policy.":r?`${e} 返回运行时控制状态:${t}。`:`${e} returned runtime control status: ${t}.`}(e,i,o);if("task"===e)return o?"委派任务已返回结果,我会基于这些证据继续推进。":"The delegated task returned; I'll keep going with that evidence.";const a=function summarizeToolOutput(e){if("string"==typeof e)return summarizeText(e);if(isRecord(e)){if("string"==typeof e.status)return summarizeText(e.status);if("string"==typeof e.summary)return summarizeText(e.summary)}}(t);return o?`${e} 已返回${a?`:${a}`:",我会继续判断下一步。"}`:`${e} returned${a?`: ${a}`:"; I'll decide the next step from here."}`}(e.toolId,e.output,e.error,e.controlStatus,t):"agent.langgraph.invoke"===e.phase?t?"我正在调用 workflow backend。":"I'm invoking the workflow backend.":"agent.node.completed"===e.phase&&"string"==typeof e.nodeId?t?`节点 ${e.nodeId} 已完成。`:`Node ${e.nodeId} is complete.`:void 0}(e.event,r):void 0}(r,e.get(r.requestId));if(i&&i!==t.get(r.requestId))return t.set(r.requestId,i),"runtime.request.failed"!==r.type&&"runtime.request.cancelled"!==r.type||(e.delete(r.requestId),t.delete(r.requestId)),function pruneRequestState(e,t){for(;e.size>200;){const r=e.keys().next().value;if(!r)break;e.delete(r),t.delete(r)}}(e,t),{message:i,style:n.style,model:n.model}}}}():void 0}export function createProgressNarrationEvent(e){if(!e.narrator||"runtime.progress.narration"===e.source.type)return;const t=readProgressPolicy(e.policy),r=!1===e.options?void 0:e.options,n=e.narrator.narrate(e.source,{style:r?.style??t.style,model:r?.model??t.model});return function isPromiseLike(e){return isRecord(e)&&"function"==typeof e.then}(n)?n.then(t=>toNarrationEvent(e.source,e.narrator,t)):toNarrationEvent(e.source,e.narrator,n)}function toNarrationEvent(e,t,r){if(r?.message.trim())return{type:"runtime.progress.narration",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,message:r.message,provider:t.name,sourceEventTypes:r.sourceEventTypes??[e.type],sourceEventIds:r.sourceEventIds,model:r.model,style:r.style}}function summarizeText(e){const t=e?.replace(/\s+/gu," ").trim();return t?t.length>120?`${t.slice(0,117)}...`:t:""}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readProgressPolicy(e){const t=isRecord(e?.progress)?e.progress:{},r=isRecord(t.narration)?t.narration:{};return{enabled:"boolean"==typeof r.enabled?r.enabled:void 0,style:"string"==typeof r.style?r.style:void 0,model:"string"==typeof r.model?r.model:void 0}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
1
|
+
export function resolveProgressNarrator(e){if(!1===e.options)return;const t=readProgressPolicy(e.policy);return e.options?.enabled??t.enabled?e.options?.provider??function createTemplateProgressNarrator(){const e=new Map,t=new Map;return{name:"template",narrate(r,n){var o;"runtime.request.started"===r.type&&(e.set(r.requestId,{language:(o=r.input??"",/\p{Script=Han}/u.test(o)?"zh":void 0),input:r.input}),t.delete(r.requestId));const i=function templateMessage(e,t){const r="zh"===t?.language;return"runtime.request.started"===e.type?r?`我开始处理这个请求:${summarizeText(e.input)}`:`I'm starting on this request: ${summarizeText(e.input)}`:"runtime.request.completed"===e.type?r?"我已经完成执行链,正在交付最终结果。":"I've finished the execution chain and am returning the final result.":"runtime.request.failed"===e.type?r?"执行链失败了,我会保留具体错误方便定位。":"The execution chain failed; I'm keeping the concrete error visible for diagnosis.":"runtime.request.cancelled"===e.type?r?"这个请求已取消,后续执行会停止。":"The request was cancelled, so the remaining execution will stop.":"runtime.tool.direct.started"===e.type?r?`我正在运行 ${e.toolId}。`:`I'm running ${e.toolId}.`:"runtime.tool.direct.completed"===e.type?r?`${e.toolId} 已返回结果,我会继续使用这份证据。`:`${e.toolId} returned; I'll keep using that evidence.`:"runtime.workflow.started"===e.type?r?`我正在启动 workflow ${e.workflowId}。`:`I'm starting workflow ${e.workflowId}.`:"runtime.workflow.completed"===e.type?r?`workflow ${e.workflowId} 已完成。`:`Workflow ${e.workflowId} is complete.`:"runtime.specDriven.phase.started"===e.type?r?`我正在进入 spec-driven 阶段 ${e.phaseId}。`:`I'm starting spec-driven phase ${e.phaseId}.`:"runtime.specDriven.phase.blocked"===e.type?r?`spec-driven 阶段 ${e.phaseId} 被 gate 阻塞。`:`Spec-driven phase ${e.phaseId} is blocked by a gate.`:"runtime.specDriven.phase.completed"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已完成。`:`Spec-driven phase ${e.phaseId} is complete.`:"runtime.specDriven.phase.verified"===e.type?r?`spec-driven 阶段 ${e.phaseId} 已验证。`:`Spec-driven phase ${e.phaseId} is verified.`:"runtime.artifact.created"===e.type?r?"我已经保存一份运行产物。":"I've saved a runtime artifact.":"runtime.execution.contract.failed"===e.type?r?"运行证据没有满足执行契约,我会进入恢复或失败路径。":"The run evidence did not satisfy the execution contract, so I'll recover or fail explicitly.":"runtime.skill.candidate.created"===e.type?r?`我发现一个可沉淀的 skill 候选:${e.name}。`:`I found a reusable skill candidate: ${e.name}.`:e.type.startsWith("runtime.memory.")?function memoryMessage(e,t){return"runtime.memory.lifecycle"===e.type?t?`我进入记忆阶段:${e.hook}。`:`I'm in the memory lifecycle phase: ${e.hook}.`:"runtime.memory.recall.completed"===e.type?t?`我召回了 ${e.recordIds.length} 条相关记忆。`:`I recalled ${e.recordIds.length} relevant memory records.`:"runtime.memory.candidate.submitted"===e.type?t?"我提交了一条候选记忆。":"I submitted a memory candidate.":"runtime.memory.approval.requested"===e.type?t?"我提交了记忆审批请求。":"I requested memory approval.":"runtime.memory.plugin.started"===e.type?t?`我正在运行记忆插件 ${e.provider}。`:`I'm running memory plugin ${e.provider}.`:"runtime.memory.plugin.completed"===e.type?t?`记忆插件 ${e.provider} 已完成。`:`Memory plugin ${e.provider} completed.`:"runtime.memory.plugin.failed"===e.type?t?`记忆插件 ${e.provider} 失败了;主任务会继续,不把它当成执行结果。`:`Memory plugin ${e.provider} failed; the main task will continue without treating it as the result.`:"runtime.memory.maintenance.started"===e.type?t?`我正在维护 ${e.target} 记忆。`:`I'm maintaining ${e.target} memory.`:"runtime.memory.maintenance.completed"===e.type?t?`${e.target} 记忆维护完成。`:`${e.target} memory maintenance completed.`:"runtime.memory.maintenance.failed"===e.type?t?`${e.target} 记忆维护失败。`:`${e.target} memory maintenance failed.`:void 0}(e,r):"runtime.adapter.event"===e.type?function adapterMessage(e,t){if(isRecord(e))return"agent.handoff"===e.phase?t?"我把请求交给上游 agent backend,让它负责规划和执行。":"I'm handing the request to the upstream agent backend for planning and execution.":"agent.tool.start"===e.phase&&"string"==typeof e.toolId?function toolStartMessage(e,t,r){if("task"===e){const e=function readTaskTarget(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(t),n=function readDescription(e){const t=isRecord(e)?e:{};return summarizeText(readString(t.description)??readString(t.task))}(t);if(e&&n)return r?`我正在请求 task 工具委派给 ${e}:${n}`:`I'm asking the task tool to delegate to ${e}: ${n}`;if(e)return r?`我正在请求 task 工具委派给 ${e}。`:`I'm asking the task tool to delegate to ${e}.`}return r?`我正在运行 ${e} 收集证据。`:`I'm running ${e} to gather evidence.`}(e.toolId,e.args,t):"agent.tool.result"===e.phase&&"string"==typeof e.toolId?function toolResultMessage(e,t,r,n,o){if(r)return o?`${e} 返回错误:${summarizeText(String(r))}`:`${e} returned an error: ${summarizeText(String(r))}`;const i="string"==typeof n?n:function readToolControlStatus(e){const t=function parseToolOutputRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e);return"string"==typeof t?.status?t.status:"string"==typeof t?.error?t.error:"string"==typeof e&&e.startsWith("Task delegation target is not in the workspace inventory")?"task_inventory_blocked":void 0}(t);if(i)return function toolControlMessage(e,t,r){return"duplicate_tool_call"===t?r?`${e} 重复调用已复用已有证据。`:`${e} repeated an equivalent call, so the existing evidence was reused.`:"repeated_tool_call_limit"===t?r?`${e} 出现重复调用循环,我会基于已有证据继续收敛。`:`${e} entered a repeated-call loop, so I'll continue from the evidence already collected.`:"tool_argument_error"===t||"tool_argument_validation_failed"===t?r?`${e} 参数被工具 schema 拒绝,我会让 agent 修正参数或改用合适工具。`:`${e} arguments were rejected by the tool schema; I'll have the agent repair them or choose a suitable tool.`:"task_inventory_blocked"===t?r?"task 委派目标不在当前 workspace inventory 中,已被运行时策略阻止。":"The task delegation target is not in the current workspace inventory and was blocked by runtime policy.":r?`${e} 返回运行时控制状态:${t}。`:`${e} returned runtime control status: ${t}.`}(e,i,o);if("task"===e)return o?"委派任务已返回结果,我会基于这些证据继续推进。":"The delegated task returned; I'll keep going with that evidence.";const a=function summarizeToolOutput(e){if("string"==typeof e)return summarizeText(e);if(isRecord(e)){if("string"==typeof e.status)return summarizeText(e.status);if("string"==typeof e.summary)return summarizeText(e.summary)}}(t);return o?`${e} 已返回${a?`:${a}`:",我会继续判断下一步。"}`:`${e} returned${a?`: ${a}`:"; I'll decide the next step from here."}`}(e.toolId,e.output,e.error,e.controlStatus,t):"agent.langgraph.invoke"===e.phase?t?"我正在调用 workflow backend。":"I'm invoking the workflow backend.":"agent.node.completed"===e.phase&&"string"==typeof e.nodeId?t?`节点 ${e.nodeId} 已完成。`:`Node ${e.nodeId} is complete.`:void 0}(e.event,r):void 0}(r,e.get(r.requestId));if(i&&i!==t.get(r.requestId))return t.set(r.requestId,i),"runtime.request.failed"!==r.type&&"runtime.request.cancelled"!==r.type||(e.delete(r.requestId),t.delete(r.requestId)),function pruneRequestState(e,t){for(;e.size>200;){const r=e.keys().next().value;if(!r)break;e.delete(r),t.delete(r)}}(e,t),{message:i,style:n.style,model:n.model}}}}():void 0}export function createProgressNarrationEvent(e){if(!e.narrator||"runtime.progress.narration"===e.source.type)return;const t=readProgressPolicy(e.policy),r=!1===e.options?void 0:e.options,n=e.narrator.narrate(e.source,{style:r?.style??t.style,model:r?.model??t.model});return function isPromiseLike(e){return isRecord(e)&&"function"==typeof e.then}(n)?n.then(t=>toNarrationEvent(e.source,e.narrator,t)):toNarrationEvent(e.source,e.narrator,n)}export function createProgressNarrationCapability(e){const t=resolveProgressNarrator(e);if(t)return{id:"runtime.progress.narration",onEvent(r,n){try{const o=createProgressNarrationEvent({source:r,narrator:t,options:e.options,policy:e.policy});!function isRuntimeEventPromise(e){return isRecord(e)&&"function"==typeof e.then}(o)?o&&n(o):o.then(e=>{e&&n(e)}).catch(()=>{})}catch{return}}}}function toNarrationEvent(e,t,r){if(r?.message.trim())return{type:"runtime.progress.narration",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,message:r.message,provider:t.name,sourceEventTypes:r.sourceEventTypes??[e.type],sourceEventIds:r.sourceEventIds,model:r.model,style:r.style}}function summarizeText(e){const t=e?.replace(/\s+/gu," ").trim();return t?t.length>120?`${t.slice(0,117)}...`:t:""}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readProgressPolicy(e){const t=isRecord(e?.progress)?e.progress:{},r=isRecord(t.narration)?t.narration:{};return{enabled:"boolean"==typeof r.enabled?r.enabled:void 0,style:"string"==typeof r.style?r.style:void 0,model:"string"==typeof r.model?r.model:void 0}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApprovalQueue } from "@stable-harness/governance";
|
|
2
2
|
import type { MemoryProvider, RuntimeMemoryStore } from "@stable-harness/memory";
|
|
3
|
-
import type { CompiledWorkspace, RuntimeToolGateway, RuntimeAdapter, RuntimeStore, RuntimeProgressNarrationOptions, RuntimeWorkflowAdapter, StableHarnessRuntime } from "./types.js";
|
|
3
|
+
import type { CompiledWorkspace, RuntimeCapabilityModule, RuntimeToolGateway, RuntimeAdapter, RuntimeStore, RuntimeProgressNarrationOptions, RuntimeWorkflowAdapter, StableHarnessRuntime } from "./types.js";
|
|
4
4
|
type RuntimeFactoryInput = {
|
|
5
5
|
workspace: CompiledWorkspace;
|
|
6
6
|
adapters: RuntimeAdapter[];
|
|
@@ -11,6 +11,7 @@ type RuntimeFactoryInput = {
|
|
|
11
11
|
toolGateway?: RuntimeToolGateway;
|
|
12
12
|
store?: RuntimeStore;
|
|
13
13
|
progressNarration?: RuntimeProgressNarrationOptions | false;
|
|
14
|
+
capabilities?: RuntimeCapabilityModule[];
|
|
14
15
|
};
|
|
15
16
|
export declare function createStableHarnessRuntime(input: RuntimeFactoryInput): StableHarnessRuntime;
|
|
16
17
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{projectRequestInspection as r,projectRequestSummary as s,projectSessionSummaries as o}from"./runtime/persistence/inspection.js";import{
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{projectRequestInspection as r,projectRequestSummary as s,projectSessionSummaries as o}from"./runtime/persistence/inspection.js";import{assertNoRawToolCallOutput as a,assertNoToolExecutionErrorOutput as n,buildAdapterErrorRecoveryPrompt as i,buildEvidenceSynthesisOutput as u,buildExecutionContractRecoveryRequest as c,buildResultRecoveryRequest as l,containsRawToolCallOutput as p,isRecoverableAdapterError as d,rawToolCallFailureMessage as m,toolCallRecoveryEnabled as w}from"./recovery/tool-call.js";import{completeRun as f,failRun as y}from"./runtime/completion.js";import{runDirectToolCall as R}from"./runtime/direct-tool-call.js";import{createRuntimeCapabilityRegistry as g,normalizeAdapterResult as q}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{createInMemoryRuntimeStore as k}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as v}from"./runtime/progress-narration.js";import{runWorkflowRequest as b}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const a=new Set,n=t.store??k(),u=g([I(t),v({options:t.progressNarration,policy:t.workspace.runtime}),...t.capabilities??[]]),emitBase=e=>{n.appendEvent(e);for(const t of a)t(e)},emit=e=>{emitBase(e),u.emitSideEffects(e,emitBase)};return{request:async r=>async function runRuntimeRequest(t){const{agent:r,adapter:s}=function resolveExecution(e,t){const r=t.agentId??e.workspace.runtime.defaultAgentId,s=e.workspace.agents.get(r);if(!s)throw new Error(`Agent ${r} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:s,adapter:void 0};const o=e.adapters.find(e=>e.canRun(s));if(!o)throw new Error(`No runtime adapter can run backend ${s.backend} for agent ${s.id}`);return{agent:s,adapter:o}}(t.input,t.request),o=t.request.requestId??e(),a=t.request.sessionId??e();t.store.createRun(function createRunRecord(e,t,r,s){return{requestId:t,sessionId:r,agentId:s.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,o,a,r)),t.emit({type:"runtime.request.started",requestId:o,sessionId:a,agentId:r.id,input:t.request.input});try{if(t.request.workflow){const e=await b({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:o,sessionId:a,agentId:r.id,emit:t.emit});return f({store:t.store,emit:t.emit,requestId:o,sessionId:a,agent:r,result:e})}if(t.request.toolCall){const e=await R({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:o,sessionId:a,agent:r});return f({store:t.store,emit:t.emit,requestId:o,sessionId:a,agent:r,result:e})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,r=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),s=r.memory,o=r.pluginMemories??[],a=e.input.workspace.runtime,n=new Map;let u;try{u=await runAdapterOnce(e,t,e.request,s,o,n)}catch(r){if(!d(r,a))throw r;u=await runAdapterOnce(e,t,i(e.request,r,a),s,o,n)}u=await recoverAdapterResultOutput(e,t,e.request,u,s,o,a,n),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:u});try{assertRequestExecutionContract(e)}catch(r){const i=c({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:a});if(!i)throw r;u=await runAdapterOnce(e,t,i,s,o,n),u=await recoverAdapterResultOutput(e,t,i,u,s,o,a,n),assertRequestExecutionContract(e)}const l=f({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:u});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:u,response:l}),l}({...t,adapter:s,requestId:o,sessionId:a,agent:r})}catch(e){return y({store:t.store,emit:t.emit,requestId:o,sessionId:a,agent:r,error:e})}}({input:t,capabilities:u,store:n,emit:emit,request:r}),subscribe:e=>(a.add(e),()=>a.delete(e)),inspect(){return{workspaceRoot:t.workspace.root,agents:[...t.workspace.agents.keys()].sort(),workflows:[...t.workspace.workflows.keys()].sort(),...t.workspace.runtime.specDrivenWorkflow?{specDrivenWorkflow:(e=t.workspace.runtime.specDrivenWorkflow,{enabled:e.enabled,artifactsDir:e.artifactsDir,...e.constitution?{constitution:e.constitution}:{},phases:e.phases.map(e=>e.id)})}:{},evaluations:[...t.workspace.evaluations?.keys()??[]].sort(),...t.workspace.runtime.workflowRouting?.defaultWorkflowId?{defaultWorkflowId:t.workspace.runtime.workflowRouting.defaultWorkflowId}:{},workflowRoutes:(t.workspace.runtime.workflowRouting?.routes??[]).map(e=>e.id).sort(),models:[...t.workspace.models.keys()].sort(),tools:[...t.workspace.tools.keys()].sort(),runs:n.listRuns()};var e},getRuntimePolicy:()=>t.workspace.runtime,getWorkflow:e=>t.workspace.workflows.get(e),getRun:e=>n.getRun(e),listRequests:e=>n.listRuns(e).map(s),listSessions:()=>o(n.listRuns()),inspectRequest(e){const s=n.getRun(e);return s?r(t.workspace,s):void 0},cancel(e,t){const r=n.getRun(e);r&&"running"===r.state&&(n.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await u.stop(),a.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}async function recoverAdapterResultOutput(e,t,r,s,o,i,c,d){let f=r;const y=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,r="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,s="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof s&&Number.isInteger(s)&&s>0?s:3}(c);let R=0;for(let r=0;r<y;r+=1){const r=e.store.getRun(e.requestId)?.events??[],a=l({request:f,output:s.text,events:r.slice(R),availableToolIds:e.agent.tools,policy:c});if(!a)break;f=a,R=e.store.getRun(e.requestId)?.events.length??0,s=await runAdapterOnce(e,t,a,o,i,d)}if(w(c)){let t=!1;p(s.text,c)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(r.metadata)&&(s={...s,text:m(),metadata:{...s.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const o=u({request:r,output:s.text,events:e.store.getRun(e.requestId)?.events??[],policy:c});o&&(t=!0,s={...s,text:o,metadata:{...s.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(a(s.text,c),n(s.text,c))}return s}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,s,o,a){return q(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:s,pluginMemories:o,toolGateway:e.input.toolGateway,requestState:a,emit:e.emit}))}
|
|
@@ -7,9 +7,11 @@ import type { RuntimeArtifact, RuntimeOutput, RuntimeRecordState, RuntimeRequest
|
|
|
7
7
|
import type { RuntimeToolGateway } from "./runtime/tool-gateway.js";
|
|
8
8
|
import type { CompiledWorkspace, WorkspaceAgent, WorkspaceRuntimePolicy } from "./workspace/types.js";
|
|
9
9
|
export type { RuntimeEvent, RuntimeMemoryHook, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
|
|
10
|
+
export type { BoundaryScanFinding, BoundaryScanLayer, BoundaryScanLayerResult, BoundaryScanResource, BoundaryScanResult } from "./boundary-scan.js";
|
|
11
|
+
export type { RuntimeCapabilityContext, RuntimeCapabilityModule, RuntimeCapabilityRegistry, RuntimeCapabilityState } from "./runtime/capabilities.js";
|
|
10
12
|
export type { RuntimeProgressNarrationOptions, RuntimeProgressNarrationProvider } from "./runtime/progress-narration.js";
|
|
11
13
|
export type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactInput, RuntimeArtifactRecord, RuntimeArtifactStore, RuntimeCancelIntentInput, RuntimeHeartbeatInput, RuntimeMemoryCandidateInput, RuntimeOutput, RuntimeQueueClaimInput, RuntimeQueueRecord, RuntimeQueueStore, RuntimeRecoveryIntent, RuntimeRecordState, RuntimeRequest, RuntimeRequestControlRecord, RuntimeRequestMemory, RuntimeResponse, RuntimeRunFilter, RuntimeRunRecord, RuntimeStore, RuntimeStoreRunPatch, RuntimeStuckRequestInput, } from "./runtime/types.js";
|
|
12
|
-
export type { CompiledWorkspace, WorkspaceAdapterPolicy, WorkspaceAgent, WorkspaceMemory, WorkspaceModel, WorkspaceRetryPolicy, WorkspaceRetryReason, WorkspaceRetryTargetPolicy, WorkspaceRuntimePolicy, WorkspaceSkill, WorkspaceTool, WorkspaceToolRetryPolicy, } from "./workspace/types.js";
|
|
14
|
+
export type { CompiledWorkspace, WorkspaceAdapterPolicy, WorkspaceAgent, WorkspaceBoundaryDiagnostic, WorkspaceBoundaryDiagnosticCode, WorkspaceBoundaryScanPolicy, WorkspaceMemory, WorkspaceModel, WorkspaceRetryPolicy, WorkspaceRetryReason, WorkspaceRetryTargetPolicy, WorkspaceRuntimePolicy, WorkspaceSkill, WorkspaceTool, WorkspaceToolRetryPolicy, WorkspaceValidationPolicy, } from "./workspace/types.js";
|
|
13
15
|
export type { WorkspaceEvaluation, WorkspaceEvaluationCase, } from "./evaluations/index.js";
|
|
14
16
|
export type { SpecDrivenPhaseId, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, } from "./spec-driven/index.js";
|
|
15
17
|
export type { RuntimeToolCallRequest, RuntimeToolGateway, RuntimeToolGatewayContext, RuntimeToolRepairModel, RuntimeToolGatewayTool, } from "./runtime/tool-gateway.js";
|
|
@@ -51,6 +51,7 @@ export type WorkspaceRuntimePolicy = {
|
|
|
51
51
|
defaultAgentId: string;
|
|
52
52
|
workspaceId?: string;
|
|
53
53
|
profile?: string;
|
|
54
|
+
workspaceValidation?: WorkspaceValidationPolicy;
|
|
54
55
|
adapters?: WorkspaceAdapterPolicy[];
|
|
55
56
|
workflowRouting?: WorkspaceWorkflowRoutingPolicy;
|
|
56
57
|
specDrivenWorkflow?: WorkspaceSpecDrivenWorkflowPolicy;
|
|
@@ -65,6 +66,23 @@ export type WorkspaceRuntimePolicy = {
|
|
|
65
66
|
responseLanguage?: Record<string, unknown>;
|
|
66
67
|
responsePresentation?: Record<string, unknown>;
|
|
67
68
|
};
|
|
69
|
+
export type WorkspaceValidationPolicy = {
|
|
70
|
+
boundaryScan?: WorkspaceBoundaryScanPolicy;
|
|
71
|
+
};
|
|
72
|
+
export type WorkspaceBoundaryScanPolicy = {
|
|
73
|
+
enabled?: boolean;
|
|
74
|
+
failOn?: Partial<Record<WorkspaceBoundaryDiagnosticCode, boolean>>;
|
|
75
|
+
};
|
|
76
|
+
export type WorkspaceBoundaryDiagnosticCode = "duplicate-scope" | "hardcoded-sibling-routing" | "broad-fallback-scope" | "weak-positive-boundary" | "possible-sibling-overlap";
|
|
77
|
+
export type WorkspaceBoundaryDiagnostic = {
|
|
78
|
+
code: WorkspaceBoundaryDiagnosticCode;
|
|
79
|
+
layer: "subagent" | "skill" | "tool";
|
|
80
|
+
resources: string[];
|
|
81
|
+
severity: "error" | "warning";
|
|
82
|
+
message: string;
|
|
83
|
+
locations: string[];
|
|
84
|
+
details?: Record<string, unknown>;
|
|
85
|
+
};
|
|
68
86
|
export type WorkspaceAdapterPolicy = {
|
|
69
87
|
name: string;
|
|
70
88
|
enabled?: boolean;
|
|
@@ -98,4 +116,5 @@ export type CompiledWorkspace = {
|
|
|
98
116
|
memories: Map<string, WorkspaceMemory>;
|
|
99
117
|
workflows: Map<string, WorkspaceWorkflow>;
|
|
100
118
|
evaluations?: Map<string, WorkspaceEvaluation>;
|
|
119
|
+
diagnostics?: WorkspaceBoundaryDiagnostic[];
|
|
101
120
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CompiledWorkspace, WorkspaceBoundaryDiagnostic } from "@stable-harness/core";
|
|
2
|
+
export declare function scanWorkspaceBoundaries(workspace: CompiledWorkspace): WorkspaceBoundaryDiagnostic[];
|
|
3
|
+
export declare function assertWorkspaceBoundaryDiagnostics(diagnostics: WorkspaceBoundaryDiagnostic[]): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{assertBoundaryPolicyDiagnostics as s,checkBoundaryScanPolicy as o,scanAgentBoundary as t}from"@stable-harness/core";export function scanWorkspaceBoundaries(s){const e=s.runtime.workspaceValidation?.boundaryScan;if(!0!==e?.enabled)return[];const a=t({subagents:readSubagents(s),skills:readSkills(s),tools:readTools(s)});return o(a,e)}export function assertWorkspaceBoundaryDiagnostics(o){s(o)}function readSubagents(s){const o=s.agents.get(s.runtime.defaultAgentId);return(o?.subagents??[]).map(o=>s.agents.get(o)).filter(s=>Boolean(s)).map(s=>({id:s.id,description:s.description,text:s.systemPrompt,tools:s.tools,skills:s.skills,subagents:s.subagents,locations:s.sourcePath?[s.sourcePath]:[]}))}function readSkills(s){return[...s.skills.values()].map(s=>({id:s.id,description:s.description,tools:s.allowedTools,locations:[s.path]}))}function readTools(s){return[...s.tools.values()].map(s=>({id:s.id,description:s.description,text:s.id,locations:s.sourcePath?[s.sourcePath]:[]}))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readdir as t,readFile as
|
|
1
|
+
import{readdir as t,readFile as e}from"node:fs/promises";import r from"node:path";import{parseAllDocuments as i}from"yaml";export async function listYamlFiles(e){const i=await t(e,{withFileTypes:!0});return(await Promise.all(i.map(async t=>{const i=r.join(e,t.name);return t.isDirectory()?listYamlFiles(i):t.isFile()&&/\.ya?ml$/iu.test(t.name)?[i]:[]}))).flat().sort()}export async function discoverModuleTools(i){const o=r.join(i,"resources","tools");let n;try{n=await t(o,{withFileTypes:!0})}catch{return[]}return(await Promise.all(n.filter(t=>t.isFile()&&t.name.endsWith(".mjs")&&!t.name.startsWith("_")).map(t=>async function discoverModuleToolFile(t){const i=await e(t,"utf8"),o=[...i.matchAll(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool\s*\(/gu)].map(t=>t[1]).filter(t=>"default"!==t);return(o.length>0?o:[r.basename(t,".mjs")]).map(e=>({id:e,sourcePath:t,...readModuleToolDescription(i,e)}))}(r.join(o,t.name))))).flat()}export async function discoverSkills(o){const n=r.join(o,"resources","skills");let s;try{s=await t(n,{withFileTypes:!0})}catch{return[]}return(await Promise.all(s.filter(t=>t.isDirectory()).map(t=>async function readSkill(t,o){const n=r.join(t,"SKILL.md");let s;try{s=await e(n,"utf8")}catch{return}const a=function readFrontMatter(t){const e=t.match(/^---\n([\s\S]*?)\n---/u);if(!e)return{};const r=i(e[1]).at(0)?.toJSON();return"object"!=typeof r||null===r||Array.isArray(r)?{}:r}(s);return{id:readOptionalString(a.name)??o,path:n,...readOptionalString(a.description)?{description:readOptionalString(a.description)}:{},allowedTools:(l=a["allowed-tools"],Array.isArray(l)?l.filter(t=>"string"==typeof t&&t.trim().length>0):[])};var l}(r.join(n,t.name),t.name)))).filter(t=>Boolean(t))}function readModuleToolDescription(t,e){const r=t.match(new RegExp(`export\\s+const\\s+${function escapeRegExp(t){return t.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}(e)}\\s*=\\s*tool\\s*\\(\\s*\\{[\\s\\S]{0,4000}?description\\s*:\\s*(["'\`])([\\s\\S]*?)\\1`,"u")),i=t.match(/description\s*:\s*(["'`])([\s\S]*?)\1/u),o=function normalizeDescription(t){const e=t?.replace(/\\n/gu," ").replace(/\s+/gu," ").trim();return e||void 0}(r?.[2]??i?.[2]);return o?{description:o}:{}}function readOptionalString(t){return"string"==typeof t&&t.trim()?t.trim():void 0}
|
|
@@ -12,5 +12,5 @@ export declare function compileRuntime(document: RawDocument): WorkspaceRuntimeP
|
|
|
12
12
|
export declare function compileAgent(document: RawDocument, sourcePath: string): WorkspaceAgent;
|
|
13
13
|
export declare function compileModel(document: RawDocument): WorkspaceModel;
|
|
14
14
|
export declare function compileModelSpec(spec: Record<string, unknown>, fallback?: string): WorkspaceModel;
|
|
15
|
-
export declare function compileTool(document: RawDocument): WorkspaceTool;
|
|
15
|
+
export declare function compileTool(document: RawDocument, sourcePath?: string): WorkspaceTool;
|
|
16
16
|
export declare function compileMemory(document: RawDocument): WorkspaceMemory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r){const t=assertRecord(e.spec,"Agent.spec"),o=readName(e),n=readOptionalString(t.backend);if(!n)throw new Error(`Agent ${o} requires spec.backend`);const i="object"==typeof t.config&&t.config?t.config:{},a="string"==typeof t.systemPrompt?t.systemPrompt:"string"==typeof i.systemPrompt?i.systemPrompt:void 0;return{id:o,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:n,..."string"==typeof t.modelRef&&t.modelRef.trim()?{modelRef:(s=t.modelRef,s.replace(/^[^/]+\//u,""))}:{},...void 0!==a?{systemPrompt:a}:{},tools:toStringArray(t.tools),skills:toStringArray(t.skills),memory:Array.isArray(t.memory)?t.memory:[],subagents:toStringArray(t.subagents),...void 0!==t.edges?{edges:readAgentEdges(t.edges,o)}:{},config:i};var s}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e){const
|
|
1
|
+
import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."object"==typeof r.workspaceValidation&&r.workspaceValidation?{workspaceValidation:r.workspaceValidation}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r){const t=assertRecord(e.spec,"Agent.spec"),o=readName(e),n=readOptionalString(t.backend);if(!n)throw new Error(`Agent ${o} requires spec.backend`);const i="object"==typeof t.config&&t.config?t.config:{},a="string"==typeof t.systemPrompt?t.systemPrompt:"string"==typeof i.systemPrompt?i.systemPrompt:void 0;return{id:o,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:n,..."string"==typeof t.modelRef&&t.modelRef.trim()?{modelRef:(s=t.modelRef,s.replace(/^[^/]+\//u,""))}:{},...void 0!==a?{systemPrompt:a}:{},tools:toStringArray(t.tools),skills:toStringArray(t.skills),memory:Array.isArray(t.memory)?t.memory:[],subagents:toStringArray(t.subagents),...void 0!==t.edges?{edges:readAgentEdges(t.edges,o)}:{},config:i};var s}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e,r){const t=assertRecord(e.spec,"Tool.spec");return{id:readName(e),...r?{sourcePath:r}:{},..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(e){const r=assertRecord(e.spec,"Memory.spec"),t=readName(e),o={...r};return delete o.provider,delete o.profile,delete o.mode,delete o.enabled,delete o.prompts,{id:t,provider:readOptionalString(r.provider)??"langmem",...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...readOptionalString(r.mode)?{mode:readOptionalString(r.mode)}:{},enabled:!1!==r.enabled,..."object"==typeof r.prompts&&r.prompts?{prompts:readMemoryPrompts(r.prompts)}:{},...Object.keys(o).length>0?{config:o}:{}}}function readWorkflowRouting(e){const r=assertRecord(e,"Runtime.spec.workflowRouting"),t=void 0===r.routes?void 0:function readWorkflowRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.workflowRouting.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.workflowId);if(!t||!o)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:t,workflowId:o,...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{},..."object"==typeof r.metadata&&r.metadata?{metadata:r.metadata}:{}}})}(r.routes);return{...readOptionalString(r.defaultWorkflowId)?{defaultWorkflowId:readOptionalString(r.defaultWorkflowId)}:{},...t?{routes:t}:{}}}function readAdapters(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(readAdapter)}function readAgentEdges(e,r){if(!Array.isArray(e))throw new Error(`Agent ${r} spec.edges must be an array`);return e.map(e=>{const t=assertRecord(e,`Agent ${r} spec.edges[]`),o=readOptionalString(t.from),n=readOptionalString(t.to);if(!o||!n)throw new Error(`Agent ${r} spec.edges[] requires from and to`);return{from:o,to:n,...readOptionalString(t.condition)?{condition:readOptionalString(t.condition)}:{}}})}function readAdapter(e){if("string"==typeof e&&e.trim())return{name:e.trim()};const r=assertRecord(e,"Runtime.spec.adapters[]"),t=readOptionalString(r.name)??readOptionalString(r.id)??readOptionalString(r.backend);if(!t)throw new Error("Runtime.spec.adapters[] requires name");return{name:t,..."boolean"==typeof r.enabled?{enabled:r.enabled}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}}function readMemoryPrompts(e){const r=assertRecord(e,"Memory.spec.prompts");return{...readOptionalString(r.semantic)?{semantic:readOptionalString(r.semantic)}:{},...readOptionalString(r.episodic)?{episodic:readOptionalString(r.episodic)}:{},...readOptionalString(r.procedural)?{procedural:readOptionalString(r.procedural)}:{}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFile as o}from"node:fs/promises";import
|
|
1
|
+
import{readFile as o}from"node:fs/promises";import s from"node:path";import{parseAllDocuments as e}from"yaml";import{discoverModuleTools as a,discoverSkills as t,listYamlFiles as r}from"./discovery.js";import{compileAgent as i,compileMemory as n,compileModel as l,compileModelSpec as f,compileRuntime as c,compileTool as w}from"./documents.js";import{compileWorkflow as d,validateWorkflows as m}from"./workflows.js";import{compileEvaluation as k,validateEvaluations as p}from"./evaluations.js";import{assertWorkspaceBoundaryDiagnostics as u,scanWorkspaceBoundaries as g}from"./boundary-scan.js";export async function loadWorkspaceFromYaml(M){const y=s.join(M,"config"),v=await r(y),W=[],b=new Map,h=new Map,A=new Map,R=new Map,j=new Map,I=new Map,E=new Map;for(const s of v){const a=await o(s,"utf8"),t=e(a).map(o=>o.toJSON()).filter(o=>null!==o);for(const o of t)if("string"==typeof o.kind)switch(o.kind){case"Runtime":W.push(c(o));break;case"Agent":{const e=i(o,s);b.set(e.id,e);break}case"Model":{const s=l(o);h.set(s.id,s);break}case"Models":if(Array.isArray(o.spec))for(const s of o.spec)if("object"==typeof s&&null!==s&&!Array.isArray(s)){const o=f(s);h.set(o.id,o)}break;case"Tool":{const e=w(o,s);A.set(e.id,e);break}case"Memory":{const s=n(o);j.set(s.id,s);break}case"Workflow":{const e=d(o,s);I.set(e.id,e);break}case"Evaluation":{const e=k(o,s);E.set(e.id,e);break}}}for(const o of await a(M))A.has(o.id)||A.set(o.id,o);for(const o of await t(M))R.set(o.id,o);const F=W.at(-1)??{defaultAgentId:"orchestra"};m({workflows:I,agents:b,tools:A,skills:R}),p({evaluations:E,agents:b,tools:A,workflows:I}),function validateWorkflowRouting(o,s){const e=o.workflowRouting;if(e){if(e.defaultWorkflowId&&!s.has(e.defaultWorkflowId))throw new Error(`Runtime workflowRouting.defaultWorkflowId references unknown workflow ${e.defaultWorkflowId}`);for(const o of e.routes??[])if(!s.has(o.workflowId))throw new Error(`Runtime workflowRouting route ${o.id} references unknown workflow ${o.workflowId}`)}}(F,I);const S={root:M,runtime:F,agents:b,models:h,tools:A,skills:R,memories:j,workflows:I,evaluations:E},T=g(S);return u(T),{...S,...T.length>0?{diagnostics:T}:{}}}
|