stable-harness 0.0.3 → 0.0.5
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/README.md +189 -9
- package/dist/cli.js +1 -1
- package/dist/compat/agent-harness.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
- package/dist/runtime/compat/json.js +1 -1
- package/dist/runtime/compat/presentation.js +1 -1
- package/dist/runtime/compat/prompts.js +1 -1
- package/dist/runtime/model/ollama.js +1 -1
- package/dist/runtime/skills/skill-metadata.js +1 -1
- package/dist/workspace/compile.js +1 -1
- package/package.json +4 -3
- package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +9 -4
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +29 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +1 -1
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -1
- package/packages/adapter-deepagents/dist/src/memory.js +1 -1
- package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
- package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
- package/packages/adapter-deepagents/package.json +1 -0
- package/packages/adapter-langgraph/dist/src/graph.js +1 -1
- package/packages/adapter-langgraph/dist/src/runtime.js +1 -1
- package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -1
- package/packages/cli/dist/src/args.d.ts +6 -3
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/cli.js +1 -1
- package/packages/cli/dist/src/event-view.d.ts +9 -0
- package/packages/cli/dist/src/event-view.js +1 -0
- package/packages/cli/dist/src/index.d.ts +3 -0
- package/packages/cli/dist/src/index.js +1 -1
- package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
- package/packages/cli/dist/src/langgraph-env.js +1 -0
- package/packages/cli/dist/src/langgraph-official.d.ts +2 -0
- package/packages/cli/dist/src/langgraph-official.js +1 -1
- package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
- package/packages/cli/dist/src/memory/lifecycle.js +1 -0
- package/packages/cli/dist/src/memory/providers.d.ts +3 -0
- package/packages/cli/dist/src/memory/providers.js +1 -0
- package/packages/cli/dist/src/output.js +1 -1
- package/packages/cli/dist/src/server.d.ts +2 -0
- package/packages/cli/dist/src/server.js +1 -1
- package/packages/cli/package.json +2 -0
- package/packages/core/dist/evaluations/index.d.ts +18 -0
- package/packages/core/dist/evaluations/index.js +1 -0
- package/packages/core/dist/execution-contract.js +1 -1
- package/packages/core/dist/index.d.ts +3 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/memory-plugins/maintenance.js +1 -1
- package/packages/core/dist/memory-plugins/shared.js +1 -1
- package/packages/core/dist/memory-plugins.js +1 -1
- package/packages/core/dist/recovery/tool-call.d.ts +15 -0
- package/packages/core/dist/recovery/tool-call.js +1 -1
- package/packages/core/dist/runtime/completion.js +1 -1
- package/packages/core/dist/runtime/direct-tool-call.js +1 -1
- package/packages/core/dist/runtime/events.d.ts +77 -20
- package/packages/core/dist/runtime/memory.js +1 -1
- package/packages/core/dist/runtime/persistence/artifacts.js +1 -1
- package/packages/core/dist/runtime/persistence/inspection.js +1 -1
- package/packages/core/dist/runtime/persistence/queue.js +1 -1
- package/packages/core/dist/runtime/persistence/stores.js +1 -1
- package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
- package/packages/core/dist/runtime/progress-narration.js +1 -0
- package/packages/core/dist/runtime/tool-gateway.d.ts +5 -0
- package/packages/core/dist/runtime.d.ts +2 -1
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/spec-driven/config.d.ts +4 -0
- package/packages/core/dist/spec-driven/config.js +1 -0
- package/packages/core/dist/spec-driven/events.d.ts +11 -0
- package/packages/core/dist/spec-driven/events.js +1 -0
- package/packages/core/dist/spec-driven/index.d.ts +4 -0
- package/packages/core/dist/spec-driven/index.js +1 -0
- package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
- package/packages/core/dist/spec-driven/lifecycle.js +1 -0
- package/packages/core/dist/spec-driven/types.d.ts +38 -0
- package/packages/core/dist/spec-driven/types.js +1 -0
- package/packages/core/dist/trace.d.ts +1 -1
- package/packages/core/dist/trace.js +1 -1
- package/packages/core/dist/types.d.ts +15 -1
- package/packages/core/dist/workflows/index.js +1 -1
- package/packages/core/dist/workflows/runtime.js +1 -1
- package/packages/core/dist/workspace/types.d.ts +9 -0
- package/packages/governance/dist/src/skill-candidates.js +1 -1
- package/packages/memory/dist/src/langmem-service.js +1 -1
- package/packages/memory/dist/src/maintenance.js +1 -1
- package/packages/memory/dist/src/policy.js +1 -1
- package/packages/memory/dist/src/provider.js +1 -1
- package/packages/memory/dist/src/store.js +1 -1
- package/packages/protocols/dist/src/http-server.js +1 -1
- package/packages/protocols/dist/src/openai-compatible.js +1 -1
- package/packages/protocols/dist/src/openai-payload.js +1 -1
- package/packages/protocols/dist/src/openai-stream.js +1 -1
- package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
- package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
- package/packages/tool-gateway/dist/src/in-memory.js +1 -1
- package/packages/tool-gateway/dist/src/module-loader.js +1 -1
- package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
- package/packages/tool-gateway/dist/src/types.d.ts +3 -0
- package/packages/tool-gateway/package.json +1 -1
- package/packages/workspace-yaml/dist/discovery.js +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
- package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
- package/packages/workspace-yaml/dist/evaluations.js +1 -0
- package/packages/workspace-yaml/dist/loader.js +1 -1
- package/packages/workspace-yaml/dist/workflows.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ToolMessage as t}from"@langchain/core/messages";import{stringifyDeepAgentResult as
|
|
1
|
+
import{realpathSync as e}from"node:fs";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as o,normalizeFilesystemArgs as r,normalizeWriteTodosArgs as n,shallowEqualRecord as i}from"./builtin-args.js";import{afterToolInvoke as l,beforeToolInvoke as a,createToolRepeatState as s,stringifyDeepAgentResult as u,toolControlProjection as c}from"./gateway-tools.js";import{validateSkillFileBuiltinCall as d}from"./skill-file-policy.js";import{filterRepeatLimitedTools as g}from"./tool-repeat-visibility.js";import{traceProjectionForBuiltinTool as p}from"./trace-projection.js";const f=new Set(["ls","read_file","write_file","edit_file","glob","grep"]),m=new Set(["write_todos","task",...f]);export function createBuiltinToolPolicyMiddleware(e,t={}){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(o,r){const n=Array.isArray(o.tools)?g(o.tools.filter(t=>!function hasHiddenBuiltins(e){return isFilesystemDisabled(e)||!isTaskVisible(e)}(e)||isModelVisibleBuiltin(e,t.name)),t.repeatState):o.tools,i=function normalizeToolChoice(e,t,o){return"required"===t?o&&o.length>0?t:"auto":function isForcedHiddenTool(e,t){return"string"==typeof t?.function?.name&&!isModelVisibleBuiltin(e,t.function.name)}(e,t)?"auto":t}(e,o.toolChoice,n);return r({...o,tools:n,toolChoice:i})}}}export function validateFilesystemBuiltinCall(e,o,r){if(isFilesystemDisabled(e)&&f.has(o))return new t({tool_call_id:r.toolCall?.id??`stable-harness-${o}-policy`,name:o,content:`Filesystem builtin tool '${o}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}export function createObserverMiddleware(e,c={}){const g=c.repeatState??s(e.workspace.runtime.toolGateway);return{name:"StableHarnessObserver",async wrapToolCall(s,p){const h=s.toolCall?.name;if(!h||!m.has(h))return p(s);const y=function normalizeBuiltinToolRequest(e,t,l){return"write_todos"===t?{...l,toolCall:{...l.toolCall,args:n(l.toolCall?.args)}}:"task"===t?{...l,toolCall:{...l.toolCall,args:o(l.toolCall?.args)}}:f.has(t)?function normalizeFilesystemToolRequest(e,t,n){const l=o(n.toolCall?.args),a=r(t,l,e.workspace.root);return i(l,a)?n:{...n,toolCall:{...n.toolCall,args:a}}}(e,t,l):l}(e,h,s);emitToolEvent(e,h,"agent.tool.start",y.toolCall?.args);const v=g?a(h,y.toolCall?.args,g):void 0;if(v)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:v.eventOutput}),builtinToolMessage(s,h,v.modelOutput);const b="task"===h?function validateTaskCall(e,o){const r=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(o.toolCall?.args),n=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===t?[]:Array.isArray(t)?t.includes("task")?e.agent.subagents:[]:void 0}(e);if(void 0===n||r&&n.includes(r))return;const i=r?`: ${r}`:"",l=n.length>0?n.join(", "):"none";return new t({tool_call_id:o.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:`Task delegation target is not in the workspace inventory${i}. Allowed task targets: ${l}.`})}(e,y):void 0;if(b)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:b.content}),b;const T=validateFilesystemBuiltinCall(e,h,y);if(T)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:T.content}),T;const C=d(e,h,y);if(C)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:C.content}),C;try{const t=await p(y),o=function observedToolOutput(e,t,o){return"write_todos"===e?JSON.stringify({status:"recorded",args:t.toolCall?.args}):u(o)}(h,y,t),r=g?l(h,y.toolCall?.args,o,t,g):{};return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:r.eventOutput??o}),c.observedToolIds?.add(h),void 0===r.modelOutput?t:builtinToolMessage(s,h,r.modelOutput)}catch(o){const r=function recoverableBuiltinToolError(e,o,r){const n=formatError(r);return"task"===e&&/repeat limit reached for tool/iu.test(n)?new t({tool_call_id:o.toolCall?.id??"stable-harness-task-repeat-limit",name:"task",content:JSON.stringify({status:"delegated_task_repeat_limit",finalizationRequired:!0,instruction:"The delegated agent reached a configured tool repeat limit. Stop delegating this evidence need, do not send a synthesis task to another subagent for the same need, and produce the best final answer now from the evidence already returned in this run. If coverage is incomplete, report the exact remaining evidence gap explicitly.",error:previewError(n)})}):/Received tool input did not match expected schema|Invalid input:/iu.test(n)?new t({tool_call_id:o.toolCall?.id??`stable-harness-${e}-argument-error`,name:e,status:"error",content:JSON.stringify({status:"tool_argument_error",toolId:e,instruction:"The upstream builtin tool rejected these arguments. Fix the tool arguments according to the tool schema, or choose a more appropriate available tool.",error:previewError(n)})}):void 0}(h,s,o);if(r)return emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{output:r.content}),r;throw emitToolEvent(e,h,"agent.tool.result",y.toolCall?.args,{error:formatError(o)}),o}}}}function builtinToolMessage(e,o,r){return new t({tool_call_id:e.toolCall?.id??`stable-harness-${o}-repeat-guard`,name:o,content:r})}export function resolveFilesystemPermissions(e,t){const o=readConfigRecord(t?.config,"builtinTools"),r=[];if(r.push(...function skillReadPermissions(e,t){const o=[...new Set((t?.skills??[]).flatMap(t=>function skillReadPaths(e,t){return t?[...new Set([t,canonicalPath(t),backendSkillPath(e,t)])].flatMap(e=>function skillReadPathCandidates(e){const t=e.replace(/\/+$/u,""),o=t.endsWith("/SKILL.md")?t.slice(0,-9):t,r=function parentPath(e){const t=e.lastIndexOf("/");return t>0?e.slice(0,t):void 0}(o);return[t,o,`${o}/**`,...r?[r,`${r}/**`]:[]]}(e)):[]}(e.workspace.root,e.workspace.skills.get(t)?.path)).filter(e=>e.startsWith("/")))];return o.length>0?[{operations:["read"],paths:o,mode:"allow"}]:[]}(e,t)),!1!==o?.filesystem){if(deepagentsMemoryWritable(e))return r.length>0?r:void 0}else r.push({operations:["read"],paths:["/memories/**"],mode:"allow"}),r.push({operations:["read","write"],paths:["/**"],mode:"deny"});return deepagentsMemoryWritable(e)||r.push({operations:["write"],paths:["/memories/**"],mode:"deny"}),r.length>0?r:void 0}function emitToolEvent(e,t,o,r,n={}){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:o,toolId:t,..."agent.tool.start"===o?{args:r}:{},...n,..."string"==typeof n.output?c(n.output):{},...p(t,o,r)}})}function backendSkillPath(e,t){const o=function pathRelative(e,t){const o=e.split("/").filter(Boolean),r=t.split("/").filter(Boolean);if(!(r.length<o.length)){for(let e=0;e<o.length;e+=1)if(o[e]!==r[e])return;return r.slice(o.length).join("/")}}(e,t);return void 0===o?t:`/${o.split("/").filter(Boolean).join("/")}`}function canonicalPath(t){try{return e.native(t)}catch{return t}}function deepagentsMemoryWritable(e){const t=readConfigRecord(e.workspace.runtime.memory,"deepagentsMem");return!1!==t?.write}function isFilesystemDisabled(e){const t=readConfigRecord(e.agent.config,"builtinTools");return!1===t?.filesystem}function isTaskVisible(e){const t=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function isModelVisibleBuiltin(e,t){return(!isFilesystemDisabled(e)||!function isFilesystemTool(e){return"string"==typeof e&&f.has(e)}(t))&&("task"!==t||isTaskVisible(e))}function readConfigRecord(e,t){const o=isRecord(e)?e:{};return isRecord(o[t])?o[t]:void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function formatError(e){return e instanceof Error?e.message:String(e)}function previewError(e){const t=e.replace(/\s+/gu," ").trim();return t.length>800?`${t.slice(0,797)}...`:t}
|
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
import { ToolMessage } from "@langchain/core/messages";
|
|
2
2
|
import type { RuntimeAdapter } from "@stable-harness/core";
|
|
3
|
-
|
|
3
|
+
import type { RuntimeToolRepairModel } from "@stable-harness/core";
|
|
4
|
+
export type ToolRepeatState = {
|
|
5
|
+
successfulCalls: Map<string, string>;
|
|
6
|
+
duplicateCallCounts: Map<string, number>;
|
|
7
|
+
latestSuccessfulOutputByTool: Map<string, string>;
|
|
8
|
+
successfulToolCounts: Map<string, number>;
|
|
9
|
+
toolCallCounts: Map<string, number>;
|
|
10
|
+
maxDuplicateCallsPerTool?: number;
|
|
11
|
+
maxCallsPerTool?: number;
|
|
12
|
+
maxSuccessfulCallsPerTool?: number;
|
|
13
|
+
maxCallsByTool: Map<string, number>;
|
|
14
|
+
maxSuccessfulCallsByTool: Map<string, number>;
|
|
15
|
+
};
|
|
16
|
+
export declare function buildGatewayTools(input: Parameters<RuntimeAdapter["run"]>[0], agentId: string, toolIds: string[], repairModel?: RuntimeToolRepairModel, repeatState?: ToolRepeatState | undefined): import("@langchain/core/tools").DynamicTool<string | ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>>, unknown>[];
|
|
17
|
+
export declare function createToolRepeatState(config: unknown): ToolRepeatState | undefined;
|
|
18
|
+
export declare function beforeToolInvoke(toolId: string, args: unknown, state: ToolRepeatState): {
|
|
19
|
+
eventOutput: string;
|
|
20
|
+
modelOutput: string;
|
|
21
|
+
} | undefined;
|
|
22
|
+
export declare function isToolRepeatLimitReached(toolId: string, state: ToolRepeatState | undefined): boolean;
|
|
23
|
+
export declare function afterToolInvoke(toolId: string, args: unknown, output: string, result: unknown, state: ToolRepeatState): {
|
|
24
|
+
eventOutput?: string;
|
|
25
|
+
modelOutput?: string;
|
|
26
|
+
};
|
|
4
27
|
export declare function stringifyDeepAgentResult(result: unknown): string;
|
|
28
|
+
export declare function toolControlProjection(output: string): {
|
|
29
|
+
controlStatus: string;
|
|
30
|
+
} | {
|
|
31
|
+
controlStatus?: undefined;
|
|
32
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ToolMessage as
|
|
1
|
+
import{ToolMessage as t}from"@langchain/core/messages";import{tool as e}from"@langchain/core/tools";export function buildGatewayTools(o,r,n,s,a=createToolRepeatState(o.workspace.runtime.toolGateway)){return o.toolGateway?n.flatMap(n=>{const l=o.toolGateway?.get(n);if(!l)return[];const i=o.workspace.tools.get(n),u=i?.schema??l.schema;return[e(async e=>async function invokeGuardedGatewayTool(e){emitToolResult(e.input,e.agentId,e.toolId,void 0);const o=e.repeatState?beforeToolInvoke(e.toolId,e.args,e.repeatState):void 0;if(o)return emitToolResult(e.input,e.agentId,e.toolId,o.eventOutput),o.modelOutput;const r=await async function invokeGatewayTool(e,o,r,n,s){try{return await e.toolGateway.invoke({toolId:r,args:n,repairModel:s,context:{workspaceRoot:e.workspace.root,requestId:e.requestId,sessionId:e.sessionId,agentId:o}})}catch(o){if(function isToolArgumentValidationError(t){return t instanceof Error&&"ToolArgumentValidationError"===t.name&&"string"==typeof t.toolId&&Array.isArray(t.issues)}(o))return new t({tool_call_id:`stable-harness-${r}-argument-guard`,name:r,status:"error",content:formatToolArgumentError(o)});if(e.workspace.runtime.retry?.tools?.enabled)throw o;return new t({tool_call_id:`stable-harness-${r}-execution-error`,name:r,status:"error",content:JSON.stringify({error:"tool_execution_failed",toolId:r,message:formatError(o),retry:"Use the error as evidence, adjust the tool arguments if possible, or return a final answer with the blocker."})})}}(e.input,e.agentId,e.toolId,e.args,e.repairModel),n=r instanceof t?String(r.content):stringifyDeepAgentResult(r.output),s=e.repeatState?afterToolInvoke(e.toolId,e.args,n,r,e.repeatState):{};return emitToolResult(e.input,e.agentId,e.toolId,s.eventOutput??n),void 0!==s.modelOutput?s.modelOutput:r instanceof t?r:n}({input:o,agentId:r,toolId:n,args:e,repairModel:s,repeatState:a}),{name:n,description:buildToolDescription(i?.description??l.description??n,u,o.workspace.runtime.toolGateway,n),schema:{type:"object",additionalProperties:!0}})]}):[]}export function createToolRepeatState(t){if(function repeatGuardEnabled(t){return!0===repeatGuardConfig(t).enabled}(t))return{successfulCalls:new Map,duplicateCallCounts:new Map,latestSuccessfulOutputByTool:new Map,successfulToolCounts:new Map,toolCallCounts:new Map,maxDuplicateCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxDuplicateCallsPerTool)??3,maxCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxCallsPerTool),maxSuccessfulCallsPerTool:readPositiveInteger(repeatGuardConfig(t).maxSuccessfulCallsPerTool),maxCallsByTool:readPositiveIntegerMap(repeatGuardConfig(t).maxCallsByTool),maxSuccessfulCallsByTool:readPositiveIntegerMap(repeatGuardConfig(t).maxSuccessfulCallsByTool)}}function repeatGuardConfig(t){return isRecord(t)&&isRecord(t.repeatGuard)?t.repeatGuard:{}}function readPositiveInteger(t){return"number"==typeof t&&Number.isInteger(t)&&t>0?t:void 0}function readPositiveIntegerMap(t){return isRecord(t)?new Map(Object.entries(t).map(([t,e])=>[t,readPositiveInteger(e)]).filter(t=>void 0!==t[1])):new Map}export function beforeToolInvoke(t,e,o){const r=o.toolCallCounts.get(t)??0;o.toolCallCounts.set(t,r+1);const n=o.maxCallsByTool.get(t)??o.maxCallsPerTool;if(void 0!==n&&r>=n)throw new Error(`Stable Harness repeat limit reached for tool '${t}' after ${n} call(s).`);const s=o.maxSuccessfulCallsByTool.get(t)??o.maxSuccessfulCallsPerTool;if(void 0!==s&&(o.successfulToolCounts.get(t)??0)>=s){const e=repeatedToolCallLimitContent(t,o.latestSuccessfulOutputByTool.get(t));return{eventOutput:e,modelOutput:e}}const a=stableToolCallKey(t,e),l=o.successfulCalls.get(a);if(void 0!==l){const e=o.duplicateCallCounts.get(a)??0;if(o.duplicateCallCounts.set(a,e+1),void 0!==o.maxDuplicateCallsPerTool&&e>=o.maxDuplicateCallsPerTool){const e=repeatedToolCallLimitContent(t);return{eventOutput:e,modelOutput:e}}const r=function duplicateToolCallContent(t,e){return JSON.stringify({status:"duplicate_tool_call",toolId:t,instruction:"This agent already completed an equivalent tool call. Use the prior evidence instead of calling the tool again.",previousOutput:e})}(t,l);return{eventOutput:r,modelOutput:l}}}export function isToolRepeatLimitReached(t,e){if(!e)return!1;const o=e.maxCallsByTool.get(t)??e.maxCallsPerTool;if(void 0!==o&&(e.toolCallCounts.get(t)??0)>=o)return!0;const r=e.maxSuccessfulCallsByTool.get(t)??e.maxSuccessfulCallsPerTool;return void 0!==r&&(e.successfulToolCounts.get(t)??0)>=r}export function afterToolInvoke(e,o,r,n,s){return n instanceof t&&"error"===n.status||(s.successfulCalls.set(stableToolCallKey(e,o),r),s.latestSuccessfulOutputByTool.set(e,r),s.successfulToolCounts.set(e,(s.successfulToolCounts.get(e)??0)+1)),{}}function emitToolResult(t,e,o,r){t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:e,event:void 0===r?{adapter:"deepagents",phase:"agent.tool.start",toolId:o}:{adapter:"deepagents",phase:"agent.tool.result",toolId:o,output:previewToolOutput(r),...toolControlProjection(r)}})}function repeatedToolCallLimitContent(t,e){return JSON.stringify({status:"repeated_tool_call_limit",toolId:t,instruction:"This tool reached the configured repeat limit for this request. Do not call this tool or a substitute tool for the same evidence need again. Use previousOutput and the collected evidence to produce the final answer now, or report the remaining gap explicitly.",...void 0!==e?{previousOutput:e}:{}})}export function stringifyDeepAgentResult(e){if(e instanceof t)return function stringifyToolMessageContent(t){return"string"==typeof t?t:JSON.stringify(t)}(e.content);if("string"==typeof e)return e;if(isRecord(e)){const t=e.structuredResponse??e.structured_response;if(void 0!==t)return"string"==typeof t?t:JSON.stringify(t);const o=(Array.isArray(e.messages)?e.messages:[]).at(-1);if(isRecord(o)&&"string"==typeof o.content)return o.content;const r=(isRecord(e.update)&&Array.isArray(e.update.messages)?e.update.messages:[]).at(-1);if(isRecord(r)&&isRecord(r.kwargs)&&"string"==typeof r.kwargs.content)return r.kwargs.content;if(isRecord(r)&&"string"==typeof r.content)return r.content}return JSON.stringify(e)}function buildToolDescription(t,e,o,r){const n=function toolRepeatPolicyDescription(t,e){const o=repeatGuardConfig(t),r=readPositiveIntegerMap(o.maxSuccessfulCallsByTool).get(e)??readPositiveInteger(o.maxSuccessfulCallsPerTool);return void 0===r?"":`Stable runtime repeat policy: call this tool at most ${r} successful time(s) for this request. If more detail is needed, include the dimensions in the first call and synthesize after the result returns.`}(o,r),s=n?`${t}\n\n${n}`:t;return e?`${s}\n\nStable tool input schema:\n${previewToolOutput(JSON.stringify(e))}`:s}function previewToolOutput(t){const e=t.replace(/\s+/gu," ").trim();return e.length>500?`${e.slice(0,497)}...`:e}export function toolControlProjection(t){const e=function parseJsonRecord(t){try{const e=JSON.parse(t);return isRecord(e)?e:void 0}catch{return}}(t);return"string"==typeof e?.status?{controlStatus:e.status}:"string"==typeof e?.error?{controlStatus:e.error}:t.startsWith("Task delegation target is not in the workspace inventory")?{controlStatus:"task_inventory_blocked"}:{}}function stableToolCallKey(t,e){return`${t}:${stableJson(e)}`}function stableJson(t){return Array.isArray(t)?`[${t.map(stableJson).join(",")}]`:isRecord(t)?`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${stableJson(t[e])}`).join(",")}}`:JSON.stringify(t)}function formatToolArgumentError(t){return JSON.stringify({error:"tool_argument_validation_failed",toolId:t.toolId,issues:t.issues,retry:"Call the same tool again with arguments that satisfy the reported schema and semantic issues."})}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function formatError(t){return t instanceof Error?t.message:String(t)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function buildDeepAgentRequest(
|
|
1
|
+
export function buildDeepAgentRequest(e){return{messages:buildDeepAgentMessages(e)}}function buildDeepAgentMessages(e){const t=function readOpenAiMessages(e){return Array.isArray(e)?e.flatMap(e=>{const t=function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{},n=function readRole(e){if("system"===e||"user"===e||"assistant"===e||"tool"===e)return e}(t.role),r="string"==typeof t.content?t.content:"";return n&&r?[{role:n,content:r}]:[]}):[]}(e.request.metadata?.openaiMessages),n="tool_call"===e.request.metadata?.stableHarnessRecovery?void 0:function buildMemoryContext(e){const t=(e.pluginMemories??[]).filter(e=>e.context.trim()).map(e=>`Memory namespace: ${e.namespace}\n${e.context}`).join("\n\n");return t?`Relevant long-term memory:\n${t}`:void 0}(e);if(t.length>0){const r=function applyRecoveryPrompt(e,t){return"tool_call"!==t.request.metadata?.stableHarnessRecovery?e:[{role:"user",content:t.request.input}]}(t,e);return n?[{role:"system",content:n},...r]:r}const r={role:"user",content:e.request.input};return n?[{role:"system",content:n},r]:[r]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RuntimeAdapter } from "@stable-harness/core";
|
|
2
|
+
type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
|
|
3
|
+
type ModelRequest = {
|
|
4
|
+
tools?: Array<{
|
|
5
|
+
name?: unknown;
|
|
6
|
+
}>;
|
|
7
|
+
};
|
|
8
|
+
export declare function createRawToolCallParserMiddleware(input: AdapterRunInput): {
|
|
9
|
+
name: string;
|
|
10
|
+
wrapModelCall(request: ModelRequest, handler: (request: unknown) => Promise<unknown>): Promise<unknown>;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AIMessage as e}from"@langchain/core/messages";export function createRawToolCallParserMiddleware(t){return{name:"StableHarnessRawToolCallParser",async wrapModelCall(n,r){const o=await r(n);if(!function rawToolCallParsingEnabled(e){const t=isRecord(e.workspace.runtime.recovery)?e.workspace.runtime.recovery:{};return!0===(isRecord(t.toolCall)?t.toolCall:{}).enabled}(t))return o;const a=function parseRawToolCallResponse(t,n){if(0===n.size||!function isModelResponse(e){return isRecord(e)}(t)||function hasToolCalls(e){return Array.isArray(e.tool_calls)&&e.tool_calls.length>0}(t))return;const r=function readContentText(e){return"string"==typeof e?e.trim()?e:void 0:Array.isArray(e)&&e.map(e=>isRecord(e)&&"string"==typeof e.text?e.text:"").join("\n").trim()||void 0}(t.content);if(!r)return;const o=function readRawToolCalls(e,t){const n=readXmlToolCalls(e,t);if(n.length>0)return n;const r=function stripToolMarkup(e){return e.replace(/^\s*```(?:json)?/iu,"").replace(/```\s*$/iu,"").replace(/^\s*<\s*tool_call[^>]*>\s*/iu,"").replace(/\s*<\s*\/\s*tool_call\s*>\s*$/iu,"").trim()}(e),o=readXmlToolCalls(r,t);if(o.length>0)return o;const a=function readFunctionToolCalls(e,t){const n=[];for(const r of t){const t=new RegExp(`(?:^|\\s)${escapeRegexp(r)}\\s*\\(([^)]*)\\)`,"giu");for(const o of e.matchAll(t)){const e=readFunctionArgs(o[1]??"");e&&Object.keys(e).length>0&&n.push({id:`stable-harness-${r}-${Date.now().toString(36)}`,name:r,args:e})}}return n}(r,t);return a.length>0?a:function parseJsonObjects(e){const t=[];let n=e.indexOf("{");for(;n>=0;){const r=readBalancedJsonObject(e,n);if(r){try{const e=JSON.parse(r);isRecord(e)&&t.push(e)}catch{}n=e.indexOf("{",n+r.length)}else n=e.indexOf("{",n+1)}return t}(r).flatMap(e=>function readJsonToolCall(e,t){const n=function readNestedToolCall(e,t){const n=Object.entries(e);if(1!==n.length)return;const[r,o]=n[0];return t.has(r)&&isRecord(o)?{id:`stable-harness-${r}-${Date.now().toString(36)}`,name:r,args:o}:void 0}(e,t);if(n)return n;const r=readString(e.name)??readString(e.tool_name)??readString(e.tool)??readString(e.type),o=t.has(r??"")?r:singleToolName(t),a=function readArgs(e){return isRecord(e.arguments)?e.arguments:isRecord(e.parameters)?e.parameters:isRecord(e.args)?e.args:"args"in e&&void 0!==e.args?{args:e.args}:void 0}(e)??function readInlineArgs(e){const t=Object.fromEntries(Object.entries(e).filter(([e])=>!["name","tool_name","tool","type"].includes(e)));return Object.keys(t).length>0?t:void 0}(e);return o&&t.has(o)&&a?{id:`stable-harness-${o}-${Date.now().toString(36)}`,name:o,args:a}:void 0}(e,t)??[])}(r,n);return 0!==o.length?new e({content:"",id:"string"==typeof t.id?t.id:void 0,name:"string"==typeof t.name?t.name:void 0,additional_kwargs:isRecord(t.additional_kwargs)?t.additional_kwargs:{},response_metadata:isRecord(t.response_metadata)?t.response_metadata:{},tool_calls:o.map(e=>({id:e.id,name:e.name,args:e.args,type:"tool_call"}))}):void 0}(o,function visibleToolNames(e){return new Set((e.tools??[]).map(e=>e.name).filter(e=>"string"==typeof e&&e.length>0))}(n));return a??o}}}function readXmlToolCalls(e,t){const n=[],r=/<\s*(tool_call|tool_code)\b[^>]*>(?:[\s\S]*?<\s*\/\s*\1\s*>)?/giu;for(const o of e.matchAll(r)){const e=readXmlToolCall(o[0],t);e&&n.push(e)}return n}function readXmlToolCall(e,t){const n=function readToolCallAttributes(e){const t=/<\s*(?:tool_call|tool_code)\b([^>]*)>/iu.exec(e);if(!t)return;const n=[...t[1].matchAll(/([a-zA-Z_][\w.-]*)\s*=\s*"([^"]*)"/gu)];return n.length>0?Object.fromEntries(n.map(e=>[e[1],e[2]])):void 0}(e),r=readString(n?.name)??readString(n?.tool_name)??readString(n?.tool)??readXmlTag(e,"tool_name")??readXmlTag(e,"name")??readXmlTag(e,"tool"),o=t.has(r??"")?r:singleToolName(t);if(!o||!t.has(o))return;const a=readXmlTag(e,"tool_args")??readXmlTag(e,"args")??readXmlTag(e,"arguments"),s=a?function readXmlArgs(e){const t=[...e.matchAll(/<\s*([a-zA-Z_][\w.-]*)\s*>([\s\S]*?)<\s*\/\s*\1\s*>/gu)];if(0===t.length){const t=e.trim();return t?{input:t}:void 0}return Object.fromEntries(t.map(e=>[e[1],e[2].trim()]))}(a):function readAttributeArgs(e){if(!e)return;const t=Object.fromEntries(Object.entries(e).filter(([e])=>!["name","tool_name","tool","type"].includes(e)));return Object.keys(t).length>0?t:void 0}(n);return s&&0!==Object.keys(s).length?{id:`stable-harness-${o}-${Date.now().toString(36)}`,name:o,args:s}:void 0}function readXmlTag(e,t){const n=new RegExp(`<\\s*${t}\\s*>([\\s\\S]*?)<\\s*\\/\\s*${t}\\s*>`,"iu").exec(e);return n?.[1]?.trim()||void 0}function readFunctionArgs(e){const t=[...e.matchAll(/([a-zA-Z_][\w.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^,\s]+))/gu)];return t.length>0?Object.fromEntries(t.map(e=>[e[1],e[2]??e[3]??e[4]??""])):void 0}function readBalancedJsonObject(e,t){let n=0,r=!1,o=!1;for(let a=t;a<e.length;a+=1){const s=e[a];if(r)o="\\"===s&&!o,'"'!==s||o||(r=!1),"\\"!==s&&(o=!1);else if('"'===s&&(r=!0),"{"===s&&(n+=1),"}"===s&&(n-=1),0===n)return e.slice(t,a+1)}}function singleToolName(e){return 1===e.size?[...e][0]:void 0}function escapeRegexp(e){return e.replace(/[.*+?^${}()|[\]\\]/gu,"\\$&")}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ToolMessage } from "@langchain/core/messages";
|
|
2
|
+
import type { RuntimeAdapter } from "@stable-harness/core";
|
|
3
|
+
type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
|
|
4
|
+
export declare function validateSkillFileBuiltinCall(input: AdapterRunInput, toolId: string, request: {
|
|
5
|
+
toolCall?: {
|
|
6
|
+
id?: string;
|
|
7
|
+
args?: unknown;
|
|
8
|
+
};
|
|
9
|
+
}): ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>> | undefined;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ToolMessage as e}from"@langchain/core/messages";export function validateSkillFileBuiltinCall(t,l,r){const i=function readFilesystemTarget(e){const t=function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{};for(const e of["file_path","path","pattern","query"]){const l=t[e];if("string"==typeof l&&l.trim())return l.trim()}}(r.toolCall?.args);if(i&&function targetsRegisteredSkill(e,t){return[...e.workspace.skills.values()].some(l=>{const r=function virtualSkillDir(e,t){const l=function pathRelative(e,t){const l=e.split("/").filter(Boolean),r=t.split("/").filter(Boolean);if(!(r.length<l.length)){for(let e=0;e<l.length;e+=1)if(l[e]!==r[e])return;return r.slice(l.length).join("/")}}(e,t);if(void 0===l)return;const r=l.split("/").filter(Boolean),i=r.lastIndexOf("skills");return i<0?`/${r.slice(0,-1).join("/")}`:`/${r.slice(0,i+2).join("/")}`}(e.workspace.root,l.path);return!!r&&function pathMatches(e,t){const l=t.replace(/\/+$/u,"");return e===l||e.startsWith(`${l}/`)||e.includes(`${l}/`)||e.includes("SKILL.md")}(t,r)})}(t,i))return new e({tool_call_id:r.toolCall?.id??`stable-harness-${l}-skill-policy`,name:l,status:"error",content:[`Filesystem builtin tool '${l}' targeted registered skill inventory: ${i}.`,"Skills are already registered with the backend and are not evidence files for the user request.","Do not read, list, glob, or grep SKILL.md files or skill directories.","Continue with the declared agent tools, the skill behavior already loaded by the backend, and the evidence already collected."].join(" ")})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export async function streamDeepAgentResult(e,r
|
|
1
|
+
export async function streamDeepAgentResult(t,e,r){let n,o="";for await(const a of e){const e=readStreamDelta(a);e&&(o+=e,t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.output.delta",text:e}}));const d=readStreamFinalOutput(a,r);d&&(n=d)}return n??o}function readStreamDelta(t){const e=isRecord(t)?t:{};if("on_chat_model_stream"===e.event)return function readChunkText(t){const e=t?.content;return"string"==typeof e?e:Array.isArray(e)&&e.map(readContentPartText).filter(Boolean).join("")||void 0}(readRecord(readRecord(e.data)?.chunk))}function readContentPartText(t){if("string"==typeof t)return t;const e=isRecord(t)?t:{};return"string"==typeof e.text&&e.text.trim()?e.text:void 0}function readStreamFinalOutput(t,e){const r=isRecord(t)?t:{};if("on_chain_end"!==r.event)return;const n=readRecord(r.data)?.output;return e(n)||void 0}function readRecord(t){return isRecord(t)?t:void 0}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isToolRepeatLimitReached as e}from"./gateway-tools.js";export function filterRepeatLimitedTools(t,o){return t&&o?t.filter(t=>"string"!=typeof t.name||!e(t.name,o)):t}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function traceProjectionForBuiltinTool(toolId: string, phase: "tool.start" | "tool.result", args: unknown): {
|
|
1
|
+
export declare function traceProjectionForBuiltinTool(toolId: string, phase: "agent.tool.start" | "agent.tool.result", args: unknown): {
|
|
2
2
|
traceType: string;
|
|
3
3
|
traceLabel: string;
|
|
4
4
|
subagentType: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function traceProjectionForBuiltinTool(
|
|
1
|
+
export function traceProjectionForBuiltinTool(t,e,r){return"task"===t?{traceType:"delegation",traceLabel:"agent.tool.start"===e?"delegation.start":"delegation.completed",subagentType:readTaskSubagentType(r)}:"write_todos"===t&&"agent.tool.result"===e?{traceType:"plan",traceLabel:"plan.updated",todos:readTodoArgs(r)}:{}}function readTaskSubagentType(t){const e=isRecord(t)?t:{};return readString(e.subagent_type)??readString(e.subagentType)}function readTodoArgs(t){const e=isRecord(t)?t:{};return Array.isArray(e.todos)?e.todos:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveEnabledMemories as e}from"@stable-harness/core";export function resolveDeepAgentsNativeMemories(
|
|
1
|
+
import{resolveEnabledMemories as e}from"@stable-harness/core";export function resolveDeepAgentsNativeMemories(r){return!1===function readMemoryFlag(e,r,n){const o=readRecord(e[r])??readRecord(e[r.toLowerCase()]);return"boolean"==typeof o?.[n]?o[n]:void 0}(r.runtime.memory??{},"deepagentsMem","read")?[]:e(r,"all")}export function createDeepAgentsMemoryMaintenanceTarget(e={}){return{name:"deepagentsMem",run:async r=>e.run?e.run(r):{operations:[],metadata:{skipped:!0,reason:"No DeepAgents memory maintainer configured"}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ChatOllama } from "@langchain/ollama";
|
|
2
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
3
|
+
import type { WorkspaceModel } from "@stable-harness/core";
|
|
4
|
+
export declare function createBackendModel(model: WorkspaceModel): string | ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions> | ChatOllama;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ChatOllama as e}from"@langchain/ollama";import{ChatOpenAI as r}from"@langchain/openai";export function createBackendModel(r){return"ollama"===r.provider?function usesOpenAiCompatibleToolCalling(e){const r=readString(e.config?.toolCallingMode);return"openai-compatible"===r||"structured"===r}(r)?createOpenAiCompatibleModel({...r,config:{...r.config,baseURL:readOllamaOpenAiBaseUrl(r.config?.baseURL??r.config?.baseUrl)}}):function createOllamaModel(r){return new e(pruneUndefined({model:r.model,baseUrl:readString(r.config?.baseUrl),temperature:readNumber(r.config?.temperature),numCtx:readNumber(r.config?.numCtx),numPredict:readNumber(r.config?.numPredict),timeout:readNumber(r.config?.timeout),think:"boolean"==typeof r.config?.think?r.config.think:void 0}))}(r):"openai-compatible"===r.provider?createOpenAiCompatibleModel(r):r.model}function createOpenAiCompatibleModel(e){return new r(pruneUndefined({model:e.model,apiKey:readString(e.config?.apiKey),temperature:readNumber(e.config?.temperature),maxTokens:readNumber(e.config?.maxTokens)??readNumber(e.config?.numPredict),timeout:readNumber(e.config?.timeout),modelKwargs:readOpenAiCompatibleModelKwargs(e.config),configuration:pruneUndefined({baseURL:readString(e.config?.baseURL)??readString(e.config?.baseUrl),defaultHeaders:readStringRecord(e.config?.headers)})}))}function readOpenAiCompatibleModelKwargs(e){const r={...readRecord(e?.extraBody),...readRecord(e?.modelKwargs)},n=pruneUndefined({...r,think:r.think??("boolean"==typeof e?.think?e.think:void 0),num_ctx:r.num_ctx??readNumber(e?.numCtx),num_predict:r.num_predict??readNumber(e?.numPredict)});return Object.keys(n).length>0?n:void 0}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readStringRecord(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const r=Object.entries(e).filter(e=>"string"==typeof e[1]);return r.length>0?Object.fromEntries(r):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readOllamaOpenAiBaseUrl(e){const r=readString(e);if(!r)return;const n=r.replace(/\/+$/u,"");return n.endsWith("/v1")?n:`${n}/v1`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(
|
|
1
|
+
import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(r){const n=[],o=readRetryOptions(r?.model);o&&n.push(e(o));const i=function readToolRetryOptions(e){const t=readRetryOptions(e);if(!t)return;const r=Array.isArray(e?.tools)?e.tools.filter(e=>"string"==typeof e&&e.trim().length>0):void 0;return pruneUndefined({...t,tools:r})}(r?.tools);return i&&n.push(t(i)),n}function readRetryOptions(e){var t;if(e?.enabled)return pruneUndefined({maxRetries:readNonNegativeNumber(e.maxRetries),backoffFactor:readNonNegativeNumber(e.backoffFactor),initialDelayMs:readNonNegativeNumber(e.initialDelayMs),maxDelayMs:readNonNegativeNumber(e.maxDelayMs),jitter:"boolean"==typeof e.jitter?e.jitter:void 0,retryOn:createRetryPredicate(e.retryOn),onFailure:(t=e.onFailure,"continue"===t||"error"===t?t:void 0)})}function readNonNegativeNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function createRetryPredicate(e){const t=function readRetryReasons(e){const t=new Set(["timeout","network","rateLimit","serverError"]);if(!Array.isArray(e))return[...t];const r=e.filter(e=>t.has(e));return r.length>0?r:[...t]}(e);return e=>t.some(t=>matchesRetryReason(e,t))}function matchesRetryReason(e,t){const r=isRecord(e)?e:{},n=function readStatus(e){const t=e.status??e.statusCode??e.code;return"number"==typeof t?t:Number.isInteger(Number(t))?Number(t):void 0}(r),o=`${String(r.name??"")} ${String(r.code??"")} ${String(r.message??e)}`,i=r.cause;return("timeout"===t?/timeout|timedout|etimedout|abort/i.test(o):"network"===t?/network|fetch failed|terminated|connection error|eof|econnreset|econnrefused|enotfound|eai_again/i.test(o):"rateLimit"===t?429===n||/rate.?limit|too many requests/i.test(o):"serverError"===t&&Boolean(n&&n>=500&&n<600))||isRecord(i)&&matchesRetryReason(i,t)}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { RuntimeAdapterContext, RuntimeAdapterResult } from "@stable-harness/core";
|
|
2
2
|
export type DeepAgentFactory = (params?: Record<string, unknown>) => {
|
|
3
|
-
invoke(input: Record<string, unknown>): Promise<unknown>;
|
|
3
|
+
invoke(input: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
4
4
|
streamEvents?(input: Record<string, unknown>, options?: Record<string, unknown>): AsyncIterable<unknown>;
|
|
5
5
|
};
|
|
6
|
+
export type DeepAgentsModule = {
|
|
7
|
+
createDeepAgent?: unknown;
|
|
8
|
+
FilesystemBackend?: new (options: {
|
|
9
|
+
rootDir: string;
|
|
10
|
+
}) => unknown;
|
|
11
|
+
};
|
|
6
12
|
export type DeepAgentsAdapterRunner = (input: RuntimeAdapterContext) => Promise<RuntimeAdapterResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Annotation as o,END as e,START as t,StateGraph as n}from"@langchain/langgraph";import{compileWorkflowPlan as r}from"@stable-harness/core";import{resolveSkillProvider as a}from"./skill-providers.js";export async function runLangGraphWorkflow(a,
|
|
1
|
+
import{Annotation as o,END as e,START as t,StateGraph as n}from"@langchain/langgraph";import{compileWorkflowPlan as r}from"@stable-harness/core";import{resolveSkillProvider as a}from"./skill-providers.js";export async function runLangGraphWorkflow(a,d,i){!function assertSupportedPlan(o){if(o.cycles.length>0)throw new Error(`LangGraph workflow adapter does not enable cyclic graphs by default: ${o.workflowId}`);if(o.unreachableNodes.length>0)throw new Error(`LangGraph workflow has unreachable nodes: ${o.unreachableNodes.join(", ")}`)}(r(a.workflow));const s=function compileLangGraph(a,d,i){let s=new n(function createStateAnnotation(){return o.Root({input:o,outputs:o({reducer:(o,e)=>({...o,...e}),default:()=>({})}),trace:o({reducer:(o,e)=>[...o,...e],default:()=>[]})})}());for(const o of a.workflow.nodes)s=s.addNode(o.id,async e=>runNode({input:a,node:o,state:e,options:d,adapterName:i}));s=s.addEdge(t,a.workflow.entry??a.workflow.nodes[0].id),s=function addStaticEdges(o,e){for(const t of e.workflow.edges)t.condition||(o=o.addEdge(t.from,t.to));return o}(s,a),s=function addConditionalEdges(o,e,t){for(const[n,r]of function conditionalEdgesBySource(o){const e=new Map;for(const t of o.workflow.edges){if(!t.condition)continue;const o=e.get(t.from)??[];o.push({condition:t.condition,to:t.to}),e.set(t.from,o)}return e}(e)){const a=t.conditionalRouters?.[n];if(!a)throw new Error(`LangGraph workflow conditional edges from ${n} require a conditional router`);o=o.addConditionalEdges(n,async o=>a({...e,from:n,edges:r,state:o}),Object.fromEntries(r.map(o=>[o.condition,o.to])))}return o}(s,a,d);for(const o of r(a.workflow).terminalNodes)s=s.addEdge(o,e);return s.compile()}(a,d,i);a.emit({adapter:i,phase:"agent.langgraph.invoke",workflowId:a.workflow.id});const u=await s.invoke({input:a.request.input,outputs:{},trace:[]});return{text:stringifyWorkflowOutput(u),metadata:{workflowId:a.workflow.id,adapter:i,outputs:u.outputs,trace:u.trace}}}async function runNode(o){const e=await function resolveNodeHandler(o,e){const t=e.nodeHandlers?.[o.id]??e.nodeHandlers?.[o.use];if(t)return t;const n=function parseNodeUse(o){const e=o.indexOf(".");if(!(e<=0||e===o.length-1))return{kind:o.slice(0,e),id:o.slice(e+1)}}(o.use);if("workflows"===n?.kind&&e.enableSubworkflows)return o=>async function runSubworkflow(o,e){const t=o.workspace.workflows.get(o.id);if(!t)throw new Error(`LangGraph subworkflow is not defined: ${o.id}`);const n=function readSubworkflowDepth(o){const e=o?.subworkflowDepth;return"number"==typeof e&&Number.isFinite(e)?e:0}(o.request.metadata),r=e.maxSubworkflowDepth??8;if(n>=r)throw new Error(`LangGraph subworkflow depth exceeded ${r}`);return(await runLangGraphWorkflow({...o,workflow:t,request:{...o.request,input:o.state,metadata:{...o.request.metadata,subworkflowDepth:n+1}}},e,e.name??"langgraph")).text}({...o,id:n.id},e);const r=n?e.nodeResolvers?.[n.kind]:void 0;if(r&&n)return o=>r({...o,kind:n.kind,id:n.id});const d="skills"===n?.kind?a(e):void 0;if(d&&n)return o=>d.resolve({...o,kind:n.kind,id:n.id});const i=e.defaultNodeHandler;if(i)return i;throw new Error(`LangGraph workflow node ${o.id} (${o.use}) has no handler or resolver`)}(o.node,o.options)({...o.input,node:o.node,state:o.state});return o.input.emit({adapter:o.adapterName,phase:"agent.node.completed",workflowId:o.input.workflow.id,nodeId:o.node.id}),{outputs:{[o.node.id]:e},trace:[{nodeId:o.node.id,use:o.node.use,output:e}]}}function stringifyWorkflowOutput(o){const e=o.trace.at(-1)?.output;return"string"==typeof e?e:JSON.stringify(e??o.outputs)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{runLangGraphWorkflow as e}from"./graph.js";export function createLangGraphRuntimeAdapter(
|
|
1
|
+
import{runLangGraphWorkflow as e}from"./graph.js";export function createLangGraphRuntimeAdapter(n={}){const t=n.name??"langgraph";return{name:t,canRun:e=>e.backend===t,run:r=>async function runLangGraphAgent(n,t,r){return e({workspace:n.workspace,workflow:workflowFromAgent(n.agent,r),request:{input:n.request.input,metadata:n.request.metadata},requestId:n.requestId,sessionId:n.sessionId,toolGateway:n.toolGateway,emit:e=>n.emit({type:"runtime.adapter.event",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,event:e})},t,r)}(r,n,t)}}function workflowFromAgent(e,n){const t=e.edges??[],r=t.length>0?agentNodes(e):agentNodes(e).slice(0,1);return function validateAgentGraph(e,n){const t=new Set;for(const r of n){if(t.has(r.id))throw new Error(`LangGraph agent ${e.id} has duplicate graph node ${r.id}`);t.add(r.id)}for(const n of e.edges??[])if(!t.has(n.from)||!t.has(n.to))throw new Error(`LangGraph agent ${e.id} edge references unknown node ${n.from}->${n.to}`)}({...e,edges:t},r),{id:e.id,...e.description?{description:e.description}:{},...e.sourcePath?{sourcePath:e.sourcePath}:{},adapter:n,entry:readAgentEntry(e,r),nodes:r,edges:t,...Object.keys(e.config).length>0?{config:e.config}:{}}}function agentNodes(e){return[...e.subagents.map(e=>inventoryNode("agents",e)),...(e.skills??[]).map(e=>inventoryNode("skills",e)),...e.tools.map(e=>inventoryNode("tools",e))]}function inventoryNode(e,n){return{id:n,use:`${e}.${n}`}}function readAgentEntry(e,n){return e.edges?.[0]?.from??n[0]?.id}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFile as e}from"node:fs/promises";import r from"node:path";export function createRegistrySkillResolverProvider(e={}){return{name:"registry-resolver",resolve:r=>async function(e,r){const
|
|
1
|
+
import{readFile as e}from"node:fs/promises";import r from"node:path";export function createRegistrySkillResolverProvider(e={}){return{name:"registry-resolver",resolve:r=>async function resolveRegistrySkill(e,r){const t=e.workspace.skills.get(e.id);if(!t)throw new Error(`LangGraph skill resolver cannot find skill ${e.id}`);return{id:t.id,path:t.path,...t.description?{description:t.description}:{},allowedTools:t.allowedTools,...!1===r.includeContent?{}:{content:await readSkillContent(t,r.maxBytes)}}}(r,e)}}export function createDeepAgentsMiddlewareSkillProvider(e={}){return{name:"deepagents-middleware",async createMiddleware(r){const t=await async function loadDeepAgentsSkillsModule(e){return e.importDeepAgents?e.importDeepAgents():new Function("specifier","return import(specifier)")("deepagents")}(e),o=t.createSkillsMiddleware;if("function"!=typeof o)throw new Error("deepagents does not export createSkillsMiddleware");return o({backend:e.backend??createDeepAgentsFilesystemBackend(t,r.workspace.root),sources:e.sources??deriveDeepAgentsSkillSources(r)})}}}export function resolveSkillProvider(e){if(!1!==e.skillProvider)return e.skillProvider??createRegistrySkillResolverProvider()}async function readSkillContent(r,t=1048576){const o=await e(r.path,"utf8");if(Buffer.byteLength(o,"utf8")>t)throw new Error(`Skill ${r.id} exceeds registry resolver size limit of ${t} bytes`);return o}function createDeepAgentsFilesystemBackend(e,r){if("function"!=typeof e.FilesystemBackend)throw new Error("deepagents does not export FilesystemBackend");return new e.FilesystemBackend({rootDir:r})}function deriveDeepAgentsSkillSources(e){const t=new Set([...(o=e.agent,o?.skills??[]),...skillIdsFromWorkflow(e)]);var o;const i=new Set;for(const o of t){const t=e.workspace.skills.get(o);t&&i.add(toPosixPath(r.relative(e.workspace.root,r.dirname(r.dirname(t.path)))))}return[...i].filter(e=>e&&!e.startsWith(".."))}function skillIdsFromWorkflow(e){return e.workflow.nodes.map(e=>e.use.match(/^skills\.([^./][^.]*)$/u)?.[1]).filter(e=>Boolean(e))}function toPosixPath(e){return e.split(r.sep).join("/")}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
export type CliArgs = {
|
|
2
2
|
workspaceRoot: string;
|
|
3
|
-
command: "request" | "start";
|
|
3
|
+
command: "request" | "start" | "stop";
|
|
4
4
|
workflowRenderId?: string;
|
|
5
5
|
workflowInspectId?: string;
|
|
6
|
+
workflowRunId?: string;
|
|
6
7
|
agentRenderId?: string;
|
|
7
8
|
agentId?: string;
|
|
9
|
+
sessionId?: string;
|
|
8
10
|
toolId?: string;
|
|
9
11
|
toolArgs: unknown;
|
|
10
12
|
trace: boolean;
|
|
11
13
|
traceJson: boolean;
|
|
14
|
+
progress: boolean;
|
|
12
15
|
serveOpenAi: boolean;
|
|
13
|
-
host
|
|
14
|
-
port
|
|
16
|
+
host?: string;
|
|
17
|
+
port?: number;
|
|
15
18
|
apiKey?: string;
|
|
16
19
|
timeoutMs: number;
|
|
17
20
|
help: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function parseArgs(
|
|
1
|
+
export function parseArgs(e){const r=function createDefaultArgs(){return{workspaceRoot:process.cwd(),command:"request",toolArgs:void 0,trace:!1,traceJson:!1,progress:!1,serveOpenAi:!1,host:process.env.STABLE_HARNESS_OPENAI_HOST,port:process.env.STABLE_HARNESS_OPENAI_PORT?Number(process.env.STABLE_HARNESS_OPENAI_PORT):void 0,apiKey:process.env.STABLE_HARNESS_OPENAI_API_KEY,timeoutMs:Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),help:!1,prompt:"",shouldRunRequest:!1}}(),t=[];for(let o=0;o<e.length;o+=1)o=parseOneArg(e,o,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,o){const n=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===o.length&&function parseTopLevelCommand(e,r,t){return"start"===e[r]?(t.command="start",t.serveOpenAi=!0,!0):"stop"===e[r]?(t.command="stop",!0):"workflow"!==e[r]||"render"!==e[r+1]&&"inspect"!==e[r+1]?"agent"===e[r]&&"render"===e[r+1]&&(Object.assign(t,function parseAgentCommand(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}(e,r)),!0):(Object.assign(t,function parseWorkflowCommand(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}(e,r)),!0)}(e,r,t))return function stateIndex(e,r){return"workflow"===e[r]||"agent"===e[r]?r+2:r}(e,r);if("-h"===e[r]||"--help"===e[r])t.help=!0;else if("start"===t.command&&function isProtocolName(e){return"openai"===e||"openai-compatible"===e}(e[r]))t.serveOpenAi=!0;else{if("-w"===e[r]||"--workspace"===e[r])return setString(n,t,"workspaceRoot");if("--agent"===e[r])return setString(n,t,"agentId");if("--workflow"===e[r])return setString(n,t,"workflowRunId");if("--session-id"===e[r])return setString(n,t,"sessionId");if("--tool"===e[r])return setString(n,t,"toolId");if("--tool-args-json"===e[r])return t.toolArgs=function parseJsonArg(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}(n.value??"{}"),n.index;if("--trace"===e[r])t.trace=!0;else if("--trace-json"===e[r])t.traceJson=!0;else if("--progress"===e[r])t.progress=!0;else if("--serve-openai"===e[r])t.serveOpenAi=!0;else{if("--host"===e[r])return setString(n,t,"host");if("--port"===e[r])return t.port=Number(n.value??t.port),n.index;if("--api-key"===e[r])return setString(n,t,"apiKey");if("--timeout-ms"===e[r])return t.timeoutMs=Number(n.value??t.timeoutMs),n.index;o.push(e[r])}}return r}function setString(e,r,t){return"string"==typeof e.value&&Object.assign(r,{[t]:e.value}),e.index}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{
|
|
2
|
+
import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as s}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as i}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as l,parseArgs as d}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as c,shouldEnableCliProgressNarration as w}from"./event-view.js";import{ensureCliMemoryServices as f}from"./memory/lifecycle.js";import{createCliMemoryProviders as m}from"./memory/providers.js";import{formatDetail as g,inspectWorkflow as I,renderAgent as v,renderWorkflow as y,workspaceStatus as k}from"./output.js";import{serveProtocol as h,stopProtocol as b}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=d(e);if(t.help)return void process.stdout.write(l());const o=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),q=t.workspaceRoot;try{const e=await u(q);if(t.workflowRenderId)return void process.stdout.write(y(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(I(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(v(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(o),void await b(e,t);const l=await n({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await f(e);const d=m(e),R=c(e.runtime);let C;if(C=s({workspace:e,toolGateway:l,memoryProviders:d,adapters:[r()],workflowAdapters:[createCliWorkflowAdapter(l,()=>C)],progressNarration:w(R,e.runtime)?{enabled:!0,style:"cli"}:void 0}),t.serveOpenAi)return clearTimeout(o),void await h(C,t);if(!t.shouldRunRequest)return void process.stdout.write(k(e,q));t.trace&&C.subscribe(e=>{const t=a(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${g(t.detail)}\n`)}),C.subscribe(e=>{const t=p(e,R);t&&process.stdout.write(`${t}\n`)});const $=await C.request({input:t.prompt,agentId:t.agentId,sessionId:t.sessionId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0,workflow:t.workflowRunId?{workflowId:t.workflowRunId,input:t.prompt}:void 0});if(t.trace||t.traceJson){const e=C.getRun($.requestId),r=e?i(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${$.output}\n`)}finally{clearTimeout(o)}}function createCliWorkflowAdapter(e,t){return o({nodeResolvers:{tools:async({id:t,node:r,request:o,requestId:s,sessionId:a,state:i,workspace:n})=>{return(await e.invoke({toolId:t,args:(u=r.config,l=o.input,d=i.outputs,!0===u?.inputFromState?{...u,requestInput:l,outputs:d}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof l&&null!==l?l:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`}})).output;var u,l,d},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,u,l;return(await t().request({input:(i=e,n=o.input,u=a.outputs,l=r.config,[`Workflow node agents.${i}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...l?[`Workflow node config: ${JSON.stringify(l)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}(function isCliEntrypoint(){const r=process.argv[1];if(!r)return!1;try{return e(t(import.meta.url))===e(r)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RuntimeEvent } from "@stable-harness/core";
|
|
2
|
+
export type CliEventViewConfig = {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
include: string[];
|
|
5
|
+
exclude: string[];
|
|
6
|
+
};
|
|
7
|
+
export declare function readCliEventViewConfig(runtimePolicy: Record<string, unknown>): CliEventViewConfig;
|
|
8
|
+
export declare function shouldEnableCliProgressNarration(config: CliEventViewConfig, runtimePolicy: Record<string, unknown>): boolean;
|
|
9
|
+
export declare function formatCliRuntimeEvent(event: RuntimeEvent, config: CliEventViewConfig): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function readCliEventViewConfig(e){const t=readRecord(e.cli),r=readRecord(t?.events);return{enabled:readBoolean(r?.enabled)??!0,include:readStringList(r?.include)??["runtime.progress.narration"],exclude:readStringList(r?.exclude)??[]}}export function shouldEnableCliProgressNarration(e,t){if(!e.enabled)return!1;const r=readRecord(readRecord(t.progress)?.narration);return!1!==readBoolean(r?.enabled)&&e.include.some(e=>matchesEventPattern("runtime.progress.narration",e))}export function formatCliRuntimeEvent(e,t){if(!t.enabled||!function isIncluded(e,t){const r=function eventKeys(e){return"runtime.adapter.event"===e.type&&isRecord(e.event)&&"string"==typeof e.event.phase?[e.type,e.event.phase]:[e.type]}(e),n=t.include.some(e=>r.some(t=>matchesEventPattern(t,e))),i=t.exclude.some(e=>r.some(t=>matchesEventPattern(t,e)));return n&&!i}(e,t))return;const r=function projectCliEventView(e){return"runtime.progress.narration"===e.type?{group:"Progress",title:e.message}:e.type.startsWith("runtime.request.")?{group:"Run",title:e.type}:"runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type?{group:"Tool",title:e.type,detail:e.toolId}:"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?{group:"Workflow",title:e.type,detail:e.workflowId}:e.type.startsWith("runtime.memory.")?{group:"Memory",title:e.type,detail:memoryDetail(e)}:"runtime.skill.candidate.created"===e.type?{group:"Skill",title:e.type,detail:e.name}:"runtime.artifact.created"===e.type?{group:"Artifact",title:e.type}:"runtime.execution.contract.failed"===e.type?{group:"Contract",title:e.type,detail:e.reason}:"runtime.adapter.event"===e.type?function adapterEventView(e){if(!isRecord(e))return{group:"Adapter",title:"runtime.adapter.event"};const t="string"==typeof e.phase?e.phase:"runtime.adapter.event";return t.startsWith("agent.tool.")?{group:"Agent Tool",title:t,detail:readString(e.toolId)}:t.startsWith("agent.output.")?{group:"Agent Output",title:t,detail:readString(e.text)}:t.startsWith("agent.")?{group:"Agent",title:t,detail:readString(e.adapter)}:{group:"Adapter",title:t,detail:readString(e.adapter)}}(e.event):{group:"event",title:e.type}}(e);return`[${r.group}] ${e.agentId} | ${r.title}${r.detail?` | ${r.detail}`:""}`}function matchesEventPattern(e,t){return"*"===t||(t.endsWith(".*")?e===t.slice(0,-2)||e.startsWith(t.slice(0,-1)):e===t)}function memoryDetail(e){return"runtime.memory.recall.completed"===e.type?`${e.recordIds.length} records`:"provider"in e?e.provider:"target"in e?e.target:void 0}function readRecord(e){return isRecord(e)?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{runCli}from"./cli.js";
|
|
1
|
+
export{runCli}from"./cli.js";export{parseArgs}from"./args.js";export{ensureCliMemoryServices}from"./memory/lifecycle.js";export{createCliMemoryProviders}from"./memory/providers.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as n}from"node:fs/promises";import t from"node:path";export async function applyLangGraphEnvironment(e,r={}){const o=function resolveEnvFile(n,e){if(!1===e.envFile)return;const r="string"==typeof e.env?e.env:e.envFile,o="string"==typeof r&&r.trim()?r.trim():".env";return t.isAbsolute(o)?o:t.join(n,o)}(e,r);!function applyEnv(n){for(const[t,e]of Object.entries(n))process.env[t]||(process.env[t]=e)}({...o?await async function readEnvFile(t){try{return function parseEnv(n){const t={};for(const e of n.split(/\r?\n/u)){const n=e.trim();if(!n||n.startsWith("#"))continue;const r=n.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/u);r&&(t[r[1]]=unquoteEnvValue(r[2]??""))}return t}(await n(t,"utf8"))}catch(n){if(function isMissingFileError(n){return"object"==typeof n&&null!==n&&"code"in n&&"ENOENT"===n.code}(n))return{};throw n}}(o):{},...function readInlineEnv(n){return"object"!=typeof n||null===n||Array.isArray(n)?{}:Object.fromEntries(Object.entries(n).flatMap(([n,t])=>{if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(n))return[];const e=function resolveEnvValue(n){if("string"==typeof n){const t=n.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]??"":n}if("number"==typeof n||"boolean"==typeof n)return String(n)}(t);return void 0===e?[]:[[n,e]]}))}(r.env)})}function unquoteEnvValue(n){const t=n.trim();if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1);const e=t.match(/^(.*?)\s+#/u);return e?e[1].trim():t}
|
|
@@ -4,6 +4,8 @@ export type LangGraphOfficialConfig = {
|
|
|
4
4
|
port: number;
|
|
5
5
|
nWorkers: number;
|
|
6
6
|
exposeAgents?: string[];
|
|
7
|
+
env?: unknown;
|
|
8
|
+
envFile?: unknown;
|
|
7
9
|
};
|
|
8
10
|
export declare function startOfficialLangGraphServer(runtime: ReturnType<typeof createStableHarnessRuntime>, config: LangGraphOfficialConfig): Promise<{
|
|
9
11
|
url: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{mkdir as
|
|
1
|
+
import{mkdir as e,writeFile as n}from"node:fs/promises";import{createRequire as t}from"node:module";import s from"node:path";import{fileURLToPath as r,pathToFileURL as a}from"node:url";import{startServer as o}from"@langchain/langgraph-api/server";import{applyLangGraphEnvironment as i}from"./langgraph-env.js";export async function startOfficialLangGraphServer(t,c){const l=t.inspect().workspaceRoot,g=function exposedAgents(e,n){const t=new Set(e.inspect().agents),s=n&&n.length>0?n:[...t].sort();for(const e of s)if(!t.has(e))throw new Error(`LangGraph protocol references unknown agent: ${e}`);return s.map((e,n)=>({id:e,exportName:`agent_${n}_${safeIdentifier(e)}`}))}(t,c.exposeAgents),p=await async function writeBridgeModule(t,o){const i=s.join(t,".stable-harness","langgraph"),c=s.join(i,"bridge.mjs");return await e(i,{recursive:!0}),await n(c,function bridgeSource(e,n){const t=a(s.join(function packageRoot(){return s.resolve(s.dirname(r(import.meta.url)),"../../../..")}(),"dist","index.js")).toString(),o=a(u.resolve("@langchain/langgraph")).toString(),i=n.map(e=>`export const ${e.exportName} = createBridgeGraph(${JSON.stringify(e.id)});`).join("\n");return[(c={workspaceRoot:e,stableHarnessUrl:t,langGraphUrl:o},`import langgraph from ${JSON.stringify(c.langGraphUrl)};\nimport { containsRecoverableResultOutput, createStableHarnessRuntime, loadWorkspaceFromYaml } from ${JSON.stringify(c.stableHarnessUrl)};\n\nconst { END, MessagesZodState, START, StateGraph } = langgraph;\nconst workspaceRoot = ${JSON.stringify(c.workspaceRoot)};\nlet runtimePromise;\nlet workspacePromise;\n`),'function createBridgeGraph(agentId) {\n const graph = new StateGraph(MessagesZodState)\n .addNode("stable_harness", async (state, config) => {\n const runtime = await getRuntime();\n const protocolMessages = normalizeMessages(state.messages);\n const result = await runtime.request({\n input: currentTurnInput(protocolMessages),\n agentId,\n sessionId: readConfigString(config, "thread_id"),\n requestId: readConfigString(config, "run_id"),\n metadata: {\n protocol: "langgraph",\n openaiMessages: contextualMessages(protocolMessages),\n },\n });\n const content = protocolOutput(runtime, result);\n return { messages: [{ type: "ai", content }] };\n })\n .addEdge(START, "stable_harness")\n .addEdge("stable_harness", END);\n const compiled = graph.compile();\n compiled.getGraphAsync = async () => createInspectionGraph(agentId);\n return compiled;\n}\n\nfunction getRuntime() {\n runtimePromise ??= createStableHarnessRuntime(workspaceRoot);\n return runtimePromise;\n}\n','\nfunction currentTurnInput(messages) {\n const message = lastUserMessage(messages) ?? messages.at(-1);\n const content = message?.content ?? "";\n const context = recentContext(messages.slice(0, -1));\n return context ? [\n "Recent conversation context:",\n context,\n "",\n "Current user request:",\n content,\n ].join("\\n") : content;\n}\n\nfunction contextualMessages(messages) {\n const lastUserIndex = messages.findLastIndex((message) => message.role === "user");\n if (lastUserIndex < 0) return messages;\n const context = recentContext(messages.slice(0, lastUserIndex));\n if (!context) return messages;\n return messages.map((message, index) => index === lastUserIndex ? {\n ...message,\n content: [\n "Recent conversation context:",\n context,\n "",\n "Current user request:",\n message.content,\n ].join("\\n"),\n } : message);\n}\n\nfunction lastUserMessage(messages) {\n return [...messages].reverse().find((entry) => entry.role === "user");\n}\n\nfunction recentContext(messages) {\n return messages\n .filter((message) => message.content.trim())\n .slice(-6)\n .map((message) => `${message.role}: ${compactText(message.content)}`)\n .join("\\n\\n");\n}\n\nfunction compactText(value) {\n const normalized = value.replace(/\\s+/gu, " ").trim();\n return normalized.length > 4000 ? `${normalized.slice(0, 4000)}...` : normalized;\n}\n\nfunction normalizeMessages(messages) {\n if (!Array.isArray(messages)) return [];\n return messages.flatMap((message) => {\n const role = messageRole(message);\n const content = messageContent(message);\n return role && content ? [{ role, content }] : [];\n });\n}\n\nfunction messageRole(message) {\n const value = message?.role ?? message?.type;\n if (value === "human") return "user";\n if (value === "ai") return "assistant";\n return value === "system" || value === "user" || value === "assistant" || value === "tool"\n ? value\n : undefined;\n}\n\nfunction messageContent(message) {\n const content = message?.content;\n if (typeof content === "string") return content;\n if (Array.isArray(content)) {\n return content.map((part) => {\n if (typeof part === "string") return part;\n if (typeof part?.text === "string") return part.text;\n if (typeof part?.content === "string") return part.content;\n return "";\n }).filter(Boolean).join("\\n");\n }\n return content == null ? "" : JSON.stringify(content);\n}\n\nfunction readConfigString(config, key) {\n const value = config?.configurable?.[key];\n return typeof value === "string" && value ? value : undefined;\n}\n','function protocolOutput(runtime, result) {\n if (!containsRecoverableResultOutput(result.output, runtime.getRuntimePolicy?.() ?? {})) {\n return result.output;\n }\n return traceFallbackOutput(runtime.inspectRequest(result.requestId)) ?? [\n "The model returned recoverable tool-call or tool-error text instead of a final answer.",\n "stable-harness could not find a completed tool result to project as the Studio response.",\n ].join(" ");\n}\n\nfunction traceFallbackOutput(inspection) {\n const timeline = Array.isArray(inspection?.timeline) ? inspection.timeline : [];\n for (const item of [...timeline].reverse()) {\n const event = item?.event;\n const adapterEvent = event?.type === "runtime.adapter.event" ? event.event : undefined;\n const text = extractText(adapterEvent?.output);\n if (isUsableOutput(text)) return text;\n }\n return undefined;\n}\n\nfunction extractText(value) {\n if (typeof value === "string") {\n const parsed = parseJson(value);\n return parsed === undefined ? value : extractText(parsed);\n }\n if (!value || typeof value !== "object") return undefined;\n if (typeof value.content === "string") return value.content;\n if (typeof value.text === "string") return value.text;\n if (typeof value.output === "string") return extractText(value.output);\n if (typeof value.structuredResponse === "string") return value.structuredResponse;\n const messages = Array.isArray(value.messages) ? value.messages : Array.isArray(value.update?.messages) ? value.update.messages : [];\n for (const message of [...messages].reverse()) {\n const text = extractText(message?.kwargs ?? message);\n if (text) return text;\n }\n return undefined;\n}\n\nfunction parseJson(value) {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction isUsableOutput(value) {\n return typeof value === "string"\n && value.trim().length > 0\n && !containsRecoverableResultOutput(value, { recovery: { toolCall: { enabled: true } } });\n}\n\n','async function createInspectionGraph(agentId) {\n const workspace = await getWorkspace();\n const agent = workspace.agents.get(agentId);\n const snapshot = agent ? {\n tools: agent.tools,\n subagents: agent.subagents,\n skills: agent.skills ?? [],\n } : { tools: [], subagents: [], skills: [] };\n return {\n toJSON() {\n return inventoryGraph(agentId, snapshot);\n },\n };\n}\n\nfunction getWorkspace() {\n workspacePromise ??= loadWorkspaceFromYaml(workspaceRoot);\n return workspacePromise;\n}\n\nfunction inventoryGraph(agentId, snapshot) {\n const nodes = [\n schemaNode("__start__"),\n runnableNode(agentId, "stable-harness agent"),\n ...snapshot.subagents.map((id) => runnableNode("agent_" + id, "agent:" + id)),\n ...snapshot.tools.map((id) => runnableNode("tool_" + id, "tool:" + id)),\n ...snapshot.skills.map((id) => runnableNode("skill_" + id, "skill:" + id)),\n schemaNode("__end__"),\n ];\n const edges = [\n edge("__start__", agentId, false),\n ...snapshot.subagents.map((id) => edge(agentId, "agent_" + id, true)),\n ...snapshot.tools.map((id) => edge(agentId, "tool_" + id, true)),\n ...snapshot.skills.map((id) => edge(agentId, "skill_" + id, true)),\n edge(agentId, "__end__", true),\n ];\n return { nodes, edges };\n}\n\nfunction schemaNode(id) {\n return {\n id,\n type: "schema",\n data: { "$schema": "https://json-schema.org/draft/2020-12/schema" },\n };\n}\n\nfunction runnableNode(id, label) {\n return {\n id,\n type: "runnable",\n data: {\n id: ["stable-harness", label],\n name: label,\n },\n };\n}\n\nfunction edge(source, target, conditional) {\n return { source, target, conditional };\n}\n',i,""].join("\n");var c}(t,o)),{file:c,relativePath:`./${s.relative(t,c).split(s.sep).join("/")}`}}(l,g);await i(l,c);const m=Object.fromEntries(g.map(e=>[e.id,`${p.relativePath}:${e.exportName}`])),d=await o({host:c.host,port:c.port,nWorkers:c.nWorkers,cwd:l,graphs:m});return{url:`http://${d.host}`,cleanup:d.cleanup}}const u=t(import.meta.url);function safeIdentifier(e){const n=e.replace(/[^A-Za-z0-9_$]/gu,"_");return/^[A-Za-z_$]/u.test(n)?n:`_${n}`}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spawn as e}from"node:child_process";import{existsSync as r}from"node:fs";import t from"node:path";import{fileURLToPath as n}from"node:url";export async function ensureCliMemoryServices(r){const t=function readLangMemServiceConfig(e){const r=readRecord(e.runtime.memory?.LangMem)??readRecord(e.runtime.memory?.langmem);if(!function shouldAutoStart(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(e,r))return;const t=readString(r?.baseUrl)??readString(r?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;var n;return t?{baseUrl:t,autoStart:!1!==r?.autoStart,command:readString(r?.command)??"python3",args:(n=r?.args,(Array.isArray(n)?n.filter(e=>"string"==typeof e&&e.trim().length>0):void 0)??["-m","stable_harness_langmem_service"]),env:{...defaultLangMemEnv(e,t),...readStringRecord(r?.env)}}:void 0}(r);if(!t||!t.autoStart||!function isLocalBaseUrl(e){const r=new URL(e);return["localhost","127.0.0.1","::1"].includes(r.hostname)}(t.baseUrl)||await isHealthy(t.baseUrl))return;const n=e(t.command,t.args,{cwd:r.root,detached:!0,env:{...process.env,...t.env},stdio:"ignore"});n.unref(),await async function waitForStartedService(e,r){await Promise.race([waitForHealth(r.baseUrl),new Promise((r,t)=>e.once("error",t)),new Promise((t,n)=>{e.once("exit",(e,t)=>{n(new Error(`LangMem service exited before becoming healthy: ${r.command} code=${e??"null"} signal=${t??"null"}`))})})])}(n,t)}function defaultLangMemEnv(e,o){const a=new URL(o),i=readString(e.runtime.dataRoot)??".stable-harness",s=t.resolve(function packageRoot(){return t.resolve(t.dirname(n(import.meta.url)),"../../../../..")}(),"services/langmem-python/src");return{HOST:"localhost"===a.hostname?"127.0.0.1":a.hostname,PORT:a.port||("https:"===a.protocol?"443":"80"),LANGMEM_SQLITE_PATH:t.join(t.resolve(e.root,i),"memory/langmem/langmem.sqlite"),...r(s)?{PYTHONPATH:mergePythonPath(s)}:{}}}async function waitForHealth(e){for(let r=0;r<60;r+=1){if(await isHealthy(e))return;await new Promise(e=>setTimeout(e,500))}throw new Error(`LangMem service did not become healthy at ${e}`)}async function isHealthy(e){try{const r=await fetch(`${e.replace(/\/+$/u,"")}/health`,{signal:AbortSignal.timeout(1e3)}),t=await r.json();return r.ok&&!0===t.ok}catch{return!1}}function mergePythonPath(e){return process.env.PYTHONPATH?`${e}${t.delimiter}${process.env.PYTHONPATH}`:e}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readStringRecord(e){const r=readRecord(e);return Object.fromEntries(Object.entries(r??{}).filter(e=>"string"==typeof e[1]))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createLangMemServiceProvider as e}from"@stable-harness/memory";export function createCliMemoryProviders(r){const o=readRecord(r.runtime.memory?.LangMem)??readRecord(r.runtime.memory?.langmem);if(!function shouldEnableLangMemProvider(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(r,o))return[];const d=readString(o?.baseUrl)??readString(o?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;return d?[e({baseUrl:d,timeoutMs:(n=o?.timeoutMs,("number"==typeof n&&Number.isFinite(n)?n:void 0)??readEnvNumber("STABLE_HARNESS_LANGMEM_TIMEOUT_MS")),config:readProviderConfig(o)})]:[];var n}function readProviderConfig(e){return{provider:"langmem-service",...readRecord(e?.mode)?{mode:readRecord(e?.mode)}:{},...readRecord(e?.types)?{types:readRecord(e?.types)}:{},...readRecord(e?.approval)?{approval:readRecord(e?.approval)}:{},...readRecord(e?.defaults)?{defaults:readRecord(e?.defaults)}:{}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readEnvNumber(e){const r=process.env[e];return r?Number(r):void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{compileWorkflowPlan as
|
|
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,3 +1,5 @@
|
|
|
1
1
|
import type { createStableHarnessRuntime } from "@stable-harness/core";
|
|
2
|
+
import type { CompiledWorkspace } from "@stable-harness/core";
|
|
2
3
|
import type { CliArgs } from "./args.js";
|
|
3
4
|
export declare function serveProtocol(runtime: ReturnType<typeof createStableHarnessRuntime>, args: CliArgs): Promise<void>;
|
|
5
|
+
export declare function stopProtocol(workspace: CompiledWorkspace, args: CliArgs): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{execFile as r}from"node:child_process";import{promisify as t}from"node:util";import{createOpenAiCompatibleHttpServer as o}from"@stable-harness/protocols";import{startOfficialLangGraphServer as e}from"./langgraph-official.js";const n="127.0.0.1",s=t(r);export async function serveProtocol(r,t){const o=createConfiguredServers(r,t),e=[];let n=0;for(const t of o)if("http"===t.kind){if(!await listen(t)){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.host}:${t.port}/v1\n`);continue}e.push(()=>closeHttpServer(t.server)),n+=1;const r=t.server.address(),o="object"==typeof r&&r?r.port:t.port;process.stdout.write(`stable-harness ${t.protocol} API listening on http://${t.host}:${o}/v1\n`)}else{const o=await startLangGraphServer(r,t);if(!o){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.config.host}:${t.config.port}\n`);continue}e.push(o.cleanup),n+=1,process.stdout.write(`stable-harness ${t.protocol} API listening on ${o.url}\n`)}0!==n&&await async function waitForShutdown(r){const t=setInterval(()=>{},864e5);await new Promise(o=>{const shutdown=()=>{clearInterval(t),Promise.allSettled(r.map(r=>r())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(e)}export async function stopProtocol(r,t){const o=createConfiguredServers({getRuntimePolicy:()=>r.runtime},t).map(r=>"http"===r.kind?{protocol:r.protocol,host:r.host,port:r.port}:{protocol:r.protocol,host:r.config.host,port:r.config.port}),e=await Promise.all(o.map(async r=>({target:r,pids:await stableHarnessListenerPids(r.port)}))),n=[...new Set(e.flatMap(r=>r.pids))];for(const r of n)process.kill(r,"SIGTERM");for(const{target:r,pids:t}of e)0!==t.length?process.stdout.write(`stable-harness ${r.protocol} API stopped on ${r.host}:${r.port} pid=${t.join(",")}\n`):process.stdout.write(`stable-harness ${r.protocol} API not running on ${r.host}:${r.port}\n`)}function createConfiguredServers(r,t){const o=readRecord(r.getRuntimePolicy().protocols)??{},e=protocolConfig(o,"openaiCompatible","openai-compatible","openai")??{},n=protocolConfig(o,"langgraph")??{};return[...enabled(e)?[openAiServer(r,e,t)]:[],...enabled(n)?[langGraphServer(n)]:[]]}function openAiServer(r,t,e){const s=configString(t.host)??n,i=e.port??configNumber(t.port)??8642,a=e.host??s,c=configString(t.bearerToken)??configString(t.apiKey)??e.apiKey;return{kind:"http",protocol:"openai-compatible",server:o(r,{bearerToken:c}),host:a,port:i,...c?{bearerToken:c}:{}}}function langGraphServer(r){const t=configString(r.host)??n,o=configNumber(r.port)??2024,e=function configStringArray(r){if(Array.isArray(r)&&r.every(r=>"string"==typeof r))return r.filter(r=>r.trim()).map(r=>r.trim())}(r.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:t,port:o,nWorkers:configNumber(r.nWorkers)??10,...e?{exposeAgents:e}:{},...void 0!==r.env?{env:r.env}:{},...void 0!==r.envFile?{envFile:r.envFile}:{}}}}function protocolConfig(r,...t){for(const o of t){const t=readRecord(r[o]);if(t)return t}}function enabled(r){return!1!==r.enabled}function configString(r){if("string"!=typeof r||!r.trim())return;const t=r.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]:r}function configNumber(r){return"number"==typeof r&&Number.isFinite(r)?r:"string"==typeof r&&r.trim()?Number(r):void 0}function readRecord(r){return"object"!=typeof r||null===r||Array.isArray(r)?void 0:r}async function listen(r){try{return await new Promise((t,o)=>{r.server.once("error",o),r.server.listen(r.port,r.host,()=>{r.server.off("error",o),t()})}),!0}catch(t){if(isAddressInUse(t)&&await async function isOpenAiServerAlreadyRunning(r){const t=await fetchJson(`http://${r.host}:${r.port}/v1/capabilities`,{...r.bearerToken?{authorization:`Bearer ${r.bearerToken}`}:{}});return"stable_harness.capabilities"===t?.object}(r))return!1;throw portConflictError(t,r.protocol,r.host,r.port)}}async function startLangGraphServer(r,t){if(!await isLangGraphServerAlreadyRunning(t))try{return await e(r,t.config)}catch(r){if(isAddressInUse(r)&&await isLangGraphServerAlreadyRunning(t))return;throw portConflictError(r,t.protocol,t.config.host,t.config.port)}}async function isLangGraphServerAlreadyRunning(r){const t=await fetchJson(`http://${r.config.host}:${r.config.port}/ok`);return!0===t?.ok}async function fetchJson(r,t={}){try{const o=await fetch(r,{headers:t});if(!o.ok)return;return await o.json()}catch{return}}function isAddressInUse(r){return"EADDRINUSE"===function readErrorCode(r){return"object"==typeof r&&null!==r&&"code"in r?r.code:void 0}(r)||String(r).includes("EADDRINUSE")}function portConflictError(r,t,o,e){return isAddressInUse(r)?new Error([`stable-harness ${t} port is already in use: ${o}:${e}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${o}:${e}.`].join("\n")):r}async function stableHarnessListenerPids(r){const t=await async function listenerPids(r){try{const{stdout:t}=await s("lsof",[`-tiTCP:${r}`,"-sTCP:LISTEN"]);return t.split(/\s+/u).map(r=>Number(r)).filter(r=>Number.isInteger(r)&&r>0)}catch{return[]}}(r);return(await Promise.all(t.map(async r=>{const t=await async function processCommand(r){try{const{stdout:t}=await s("ps",["-p",String(r),"-o","command="]);return t.trim()}catch{return""}}(r);return function isStableHarnessStartCommand(r){return/\bstable-harness\b/u.test(r)&&/\bstart\b/u.test(r)}(t)?r:void 0}))).filter(r=>"number"==typeof r)}async function closeHttpServer(r){await new Promise((t,o)=>{r.close(r=>{r?o(r):t()})})}
|