stable-harness 0.0.40 → 0.0.44
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 +14 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/architecture/runtime-events.md +5 -0
- package/docs/guides/index.md +2 -0
- package/docs/guides/workspace-docker-build.md +88 -0
- package/docs/protocols/langgraph-compatible.md +22 -0
- package/docs/tooling/0.1.0-bettercall-tool-quality.zh.md +3 -3
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/adapter.js +1 -1
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin/task-inventory.js +1 -1
- package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/builtin-call-repair.js +1 -1
- package/node_modules/@stable-harness/adapter-deepagents/package.json +1 -1
- package/node_modules/@stable-harness/core/dist/index.d.ts +2 -0
- package/node_modules/@stable-harness/core/dist/index.js +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/selection-repair.d.ts +14 -4
- package/node_modules/@stable-harness/core/dist/runtime/selection-repair.js +1 -1
- package/node_modules/@stable-harness/core/dist/runtime/tracing/langsmith.d.ts +27 -0
- package/node_modules/@stable-harness/core/dist/runtime/tracing/langsmith.js +1 -0
- package/node_modules/@stable-harness/core/dist/runtime.d.ts +2 -0
- package/node_modules/@stable-harness/core/dist/runtime.js +1 -1
- package/node_modules/@stable-harness/core/dist/workspace/types.d.ts +1 -0
- package/node_modules/@stable-harness/core/package.json +0 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/argument-guard.d.ts +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/argument-guard.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/module-loader.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/dist/src/schema-validation.js +1 -1
- package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
- package/node_modules/@stable-harness/workspace-yaml/dist/documents.js +1 -1
- package/package.json +6 -3
- package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin/task-inventory.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-call-repair.js +1 -1
- package/packages/adapter-deepagents/package.json +1 -1
- package/packages/cli/dist/src/args.d.ts +3 -1
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/build.d.ts +9 -0
- package/packages/cli/dist/src/build.js +1 -0
- package/packages/cli/dist/src/cli.js +1 -1
- package/packages/cli/dist/src/index.d.ts +1 -0
- package/packages/cli/dist/src/index.js +1 -1
- package/packages/cli/dist/src/langgraph-official.js +1 -1
- package/packages/core/dist/index.d.ts +2 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/runtime/selection-repair.d.ts +14 -4
- package/packages/core/dist/runtime/selection-repair.js +1 -1
- package/packages/core/dist/runtime/tracing/langsmith.d.ts +27 -0
- package/packages/core/dist/runtime/tracing/langsmith.js +1 -0
- package/packages/core/dist/runtime.d.ts +2 -0
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/workspace/types.d.ts +1 -0
- package/packages/core/package.json +0 -1
- package/packages/tool-gateway/dist/src/argument-guard.d.ts +1 -1
- package/packages/tool-gateway/dist/src/argument-guard.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/package.json +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stable-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"test:skill-mining:e2e": "node scripts/run-skill-candidate-mining-e2e.mjs",
|
|
68
68
|
"prepublishOnly": "npm run build && npm run release:check-package",
|
|
69
69
|
"prepack": "npm run release:minify",
|
|
70
|
-
"release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle keep_fnames=true --keep-fnames --keep-classnames --module --comments false --output \"$f\"; done' sh {} +",
|
|
70
|
+
"release:minify": "find dist packages -type f -name '*.js' \\( -path 'dist/*' -o -path '*/dist/*' \\) ! -path 'dist/test/*' -exec sh -c 'for f do ./node_modules/.bin/terser \"$f\" --compress passes=2 --mangle keep_fnames=true --keep-fnames --keep-classnames --module --comments false --output \"$f\"; done' sh {} +",
|
|
71
71
|
"release:check-package": "node scripts/release/check-npm-package.mjs",
|
|
72
72
|
"release:smoke": "node scripts/release/smoke-npm-install.mjs",
|
|
73
73
|
"release:pack": "npm run build && npm run release:check-package && npm pack --dry-run",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"packages/*"
|
|
79
79
|
],
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@
|
|
81
|
+
"@easynet/better-call": "^0.1.60",
|
|
82
82
|
"@langchain/core": "^1.1.43",
|
|
83
83
|
"@langchain/langgraph": "^1.3.0",
|
|
84
84
|
"@langchain/langgraph-api": "^1.2.1",
|
|
@@ -102,6 +102,9 @@
|
|
|
102
102
|
"terser": "^5.47.1",
|
|
103
103
|
"typescript": "^5.9.3"
|
|
104
104
|
},
|
|
105
|
+
"optionalDependencies": {
|
|
106
|
+
"langsmith": "^0.6.0"
|
|
107
|
+
},
|
|
105
108
|
"main": "index.js",
|
|
106
109
|
"directories": {
|
|
107
110
|
"doc": "docs",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as
|
|
1
|
+
import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as c}from"./memory.js";import{buildDeepAgentRequest as d}from"./internal/messages.js";import{createRawToolCallParserMiddleware as l}from"./internal/raw-tool-call-parser.js";import{createBackendModel as u}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as m}from"./retry-policy.js";import{streamDeepAgentResult as g}from"./internal/stream-events.js";export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const r=e.createDeepAgent?void 0:await async function loadDeepAgentsModule(){try{return await async function importOptionalPackage(e){return import(e)}("deepagents")}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${function formatError(e){return e instanceof Error?e.message:String(e)}(e)}`)}}(),n=e.createDeepAgent??function readCreateDeepAgent(e){const t=e?.createDeepAgent;if("function"==typeof t)return t;throw new Error("DeepAgents package does not export createDeepAgent.")}(r),o=n(function buildDeepAgentParams(e,t,r){const n={...readDeepAgentsConfig(t),...readDeepAgentsConfig(e.agent.config.deepagents)},o=resolveDeepAgentsSkills(e,e.agent),a=n.permissions??s(e,e.agent),p=requestScopedRepeatState(e,e.agent.id);return pruneUndefined({name:e.agent.id,model:n.model??resolveAgentModel(e,e.agent),systemPrompt:buildSystemPrompt(e,e.agent),backend:n.backend??resolveDeepAgentsBackend(e,r,o),checkpointer:n.checkpointer,store:n.store,middleware:mergeMiddleware(e,e.agent,n.middleware,p),responseFormat:n.responseFormat,contextSchema:n.contextSchema,interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:a,tools:i(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(e,e.agent,n),p),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),o=n.permissions??s(e,r),a=scopedInput(e,r),p=requestScopedRepeatState(e,t);return pruneUndefined({name:t,description:r?.description??readString(r?.config.description)??r?.id,systemPrompt:buildSystemPrompt(e,r),model:n.model??(r?resolveAgentModel(e,r):void 0),middleware:mergeMiddleware(a,r,n.middleware,p),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:o,responseFormat:n.responseFormat,tools:i(e,t,r?.tools??[],resolveAgentRepairModel(a,r,n),p),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:o})}(t,e.config,r)),a=d(t),c=function buildDeepAgentInvokeConfig(e){return pruneUndefined({recursionLimit:readNumber(readDeepAgentsConfig(e.config.deepagents).recursionLimit)??readNumber(e.config.recursionLimit)})}(t.agent);if(!0===t.request.metadata?.openaiStream&&o.streamEvents)return g(t,o.streamEvents(a,{version:"v2",...c}),p);const l=await o.invoke(a,c);return p(l)}}}function buildSystemPrompt(e,t){const n=t?.systemPrompt??readString(t?.config.systemPrompt);return r({workspace:e.workspace,request:e.request,agent:t},n)}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=[...readAgentMemorySources(e.workspace.root,t),...c(e.workspace).map(e=>`/memories/${e.id}.md`)],o=[...new Set(n)];return o.length>0?o:void 0}function readAgentMemorySources(e,t){return(t?.memory??[]).flatMap(t=>"string"==typeof t&&t.trim()?[backendMemorySourcePath(e,t.trim())]:isRecord(t)&&"string"==typeof t.path&&t.path.trim()?[backendMemorySourcePath(e,t.path.trim())]:[])}function backendMemorySourcePath(e,r){if(r.startsWith("/"))return r;if(t.isAbsolute(r)){const n=t.relative(e,r);return n&&!n.startsWith("..")?`/${n.split(t.sep).join("/")}`:canonicalPath(r)}const n=r.split(t.sep).join("/");return n.startsWith("/")?n:`/${n}`}function resolveDeepAgentsSkills(e,r){const n=readDeepAgentsStringArray(r?.config,"skills");if(n)return n;const o=[...new Set((r?.skills??[]).map(t=>e.workspace.skills.get(t)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(r=>function backendSkillSourcePath(e,r){const n=t.dirname(t.dirname(r)),o=t.relative(e,n);return!o||o.startsWith("..")||t.isAbsolute(o)?""===o?"/":canonicalPath(n):`/${o.split(t.sep).join("/")}`}(e.workspace.root,r)))];return o.length>0?o:void 0}function resolveDeepAgentsBackend(e,t,r){if(t?.FilesystemBackend&&r&&0!==r.length)return()=>new t.FilesystemBackend({rootDir:e.workspace.root})}function mergeMiddleware(e,t,r,s=a(e.workspace.runtime.toolGateway)){const i=Array.isArray(r)?r:[],p=scopedInput(e,t),c=new Set;return[o(p,{observedToolIds:c,repeatState:s}),n(p,{repeatState:s}),...m(e.workspace.runtime.retry),...i,l(p)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=a(e.workspace.runtime.toolGateway);return e.requestState&&o&&e.requestState.set(r,o),o}function scopedInput(e,t){return t?{...e,agent:t}:e}function resolveAgentModel(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?u(r):void 0}function resolveAgentRepairModel(e,t,r){const n=r.model;if(isRepairModel(n))return n;if(!t)return;const o=resolveAgentModel(e,t);return isRepairModel(o)?o:void 0}function readDeepAgentsConfig(e){return isRecord(e)?e:{}}function readDeepAgentsStringArray(e,t){const r=isRecord(e)?e:{},n=readDeepAgentsConfig(r.deepagents),o="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of o){const t=readStringArray(n[e]);if(t)return t}return readStringArray(r[t])}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 readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function canonicalPath(t){try{return e.native(t)}catch{return t}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function isRepairModel(e){return"object"==typeof e&&null!==e&&"invoke"in e&&"function"==typeof e.invoke}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as e}from"@
|
|
1
|
+
import{repairCallSelection as e}from"@easynet/better-call";import{ToolMessage as t}from"@langchain/core/messages";import{normalizeArgsRecord as n}from"../builtin-args.js";export async function repairTaskCall(r,a){const s=function allowedTaskTypes(e){const t=readConfigRecord(e.agent.config,"deepagents");if(!0===t?.generalPurposeAgent)return;const n=readConfigRecord(e.agent.config,"builtinTools")?.modelExposed;return!1===n?[]:Array.isArray(n)?n.includes("task")?e.agent.subagents:[]:0===e.agent.subagents.length?[]:void 0}(r);if(void 0===s)return{request:a};const i=n(a.toolCall?.args),o=function readTaskSubagentType(e){const t=isRecord(e)?e:{};return readString(t.subagent_type)??readString(t.subagentType)}(i);if(o&&s.includes(o))return{request:a};const d=await e({call:{id:o,args:i},candidates:taskCallCandidates(r,s),mode:"repair"});if(d.ok){emitInventoryRepair(r,"repaired",o,d.candidateId,s);const e={...i,...d.args,subagent_type:d.candidateId};return{request:{...a,toolCall:{...a.toolCall,args:e}}}}emitInventoryRepair(r,"blocked",o,void 0,s,d.reason);const l=o?`: ${o}`:"",c=s.length>0?s.join(", "):"none";return{request:a,blocked:new t({tool_call_id:a.toolCall?.id??"stable-harness-task-policy",name:"task",status:"error",content:[`Task delegation target is not in the workspace inventory${l}. Allowed task targets: ${c}.`,"Retry with an allowed target only when that target semantically owns the original user request.","Do not substitute another specialist just to continue the same evidence need; synthesize from already collected evidence when no allowed target is a semantic match."].join(" ")})}}function emitInventoryRepair(e,t,n,r,a,s){e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,event:{adapter:"deepagents",phase:"inventory.repair",status:t,diagnostic:{layer:"task",owner:"stable_runtime_policy",originalId:n,repairedId:r,candidateIds:a,reason:s}}})}function taskCallCandidates(e,t){return t.map(t=>({id:t,description:e.workspace.agents.get(t)?.description,schema:{type:"object",properties:{subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"}},required:["description"],additionalProperties:!0}}))}function readConfigRecord(e,t){const n=isRecord(e)?e:{};return isRecord(n[t])?n[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)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{repairCallSelection as t}from"@
|
|
1
|
+
import{repairCallSelection as t}from"@easynet/better-call";import{normalizeArgsRecord as e,normalizeExecuteArgs as r,normalizeFilesystemArgs as o,normalizeWriteTodosArgs as i,shallowEqualRecord as s}from"./builtin-args.js";const a=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export async function repairBuiltinToolRequest(e){const r=normalizeBuiltinArgs(e.toolId,e.request.toolCall?.args,e.workspaceRoot),o=function builtinCandidate(t){const e=n[t];return e?{id:t,description:`DeepAgents builtin tool ${t}`,schema:e}:void 0}(e.toolId);if(!o)return updateRequestArgs(e.request,e.request.toolCall?.args,r);const i=await t({call:{id:e.toolId,args:r},candidates:[o],mode:"repair"}),s=i.ok?normalizeBuiltinArgs(e.toolId,i.args,e.workspaceRoot):r;return updateRequestArgs(e.request,e.request.toolCall?.args,s)}function normalizeBuiltinArgs(t,s,n){return"write_todos"===t?i(s):"read_todos"===t||"task"===t?e(s):"execute"===t?r(s,n):a.has(t)?o(t,s,n):e(s)}function updateRequestArgs(t,e,r){return function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(e)&&s(e,r)?t:{...t,toolCall:{...t.toolCall,args:r}}}const n={write_todos:objectSchema({todos:{type:"array",items:{type:"object",additionalProperties:!0}}}),read_todos:objectSchema({}),task:objectSchema({subagent_type:{type:"string"},subagentType:{type:"string"},description:{type:"string"},task:{type:"string"}}),execute:objectSchema({command:{type:"string"},cwd:{type:"string"},timeoutMs:{type:"number"}}),ls:objectSchema({path:{type:"string"}}),read_file:objectSchema({path:{type:"string"},file_path:{type:"string"}}),write_file:objectSchema({path:{type:"string"},file_path:{type:"string"},content:{type:"string"}}),edit_file:objectSchema({path:{type:"string"},file_path:{type:"string"},old_string:{type:"string"},new_string:{type:"string"}}),glob:objectSchema({path:{type:"string"},pattern:{type:"string"}}),grep:objectSchema({path:{type:"string"},pattern:{type:"string"}})};function objectSchema(t){return{type:"object",properties:t,additionalProperties:!0}}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"types": "dist/src/index.d.ts",
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@
|
|
13
|
+
"@easynet/better-call": "^0.1.60",
|
|
14
14
|
"@langchain/core": "^1.1.43",
|
|
15
15
|
"@langchain/ollama": "^1.2.7",
|
|
16
16
|
"@langchain/openai": "^1.4.5",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type CliArgs = {
|
|
2
2
|
workspaceRoot: string;
|
|
3
|
-
command: "request" | "start" | "stop" | "init";
|
|
3
|
+
command: "request" | "start" | "stop" | "init" | "build";
|
|
4
|
+
buildTarget?: "docker";
|
|
5
|
+
buildOutput?: string;
|
|
4
6
|
workflowRenderId?: string;
|
|
5
7
|
workflowInspectId?: string;
|
|
6
8
|
workflowRunId?: string;
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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):"init"===e[r]?(t.command="init",!0):"build"===e[r]?(t.command="build",!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("--target"===e[r])return function setBuildTarget(e,r){if("docker"===e.value)return r.buildTarget="docker",e.index;throw new Error("Unsupported build target. Supported targets: docker")}(n,t);if("--output"===e[r]||"-o"===e[r])return setString(n,t,"buildOutput");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 init [workspace]"," stable-harness build --target docker -w <workspace> --output <dir>"," 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."," --target docker Build target for workspace artifacts."," -o, --output <dir> Build output directory."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CompiledWorkspace } from "@stable-harness/core";
|
|
2
|
+
export type WorkspaceBuildOptions = {
|
|
3
|
+
workspaceRoot: string;
|
|
4
|
+
outputDir?: string;
|
|
5
|
+
packageVersion?: string;
|
|
6
|
+
target?: "docker";
|
|
7
|
+
workspace: CompiledWorkspace;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildWorkspaceArtifact(options: WorkspaceBuildOptions): Promise<string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cp as e,mkdir as o,readFile as r,rm as s,writeFile as t}from"node:fs/promises";import a from"node:path";import{fileURLToPath as n}from"node:url";const i="/data",c=new Set([".DS_Store",".env",".git","dist","node_modules"]);export async function buildWorkspaceArtifact(r){if("docker"!==(r.target??"docker"))throw new Error("Unsupported build target. Supported targets: docker");const l=await async function resolveStableHarnessVersion(e){if(e?.trim())return e.trim();if(process.env.STABLE_HARNESS_BUILD_VERSION?.trim())return process.env.STABLE_HARNESS_BUILD_VERSION.trim();for(const e of function packageJsonCandidates(){const e=a.dirname(n(import.meta.url));return[a.resolve(e,"../../../../package.json"),a.resolve(e,"../../package.json")]}()){const o=await readPackageVersion(e);if(o)return o}return"latest"}(r.packageVersion),p=a.resolve(r.workspaceRoot,r.outputDir??"dist/workspace-docker");return await s(p,{recursive:!0,force:!0}),await o(p,{recursive:!0}),await async function copyWorkspace(o,r,s){const t=a.resolve(s);await e(o,r,{recursive:!0,force:!0,filter:e=>function shouldCopy(e,o,r){const s=a.resolve(e);return!(s===r||s.startsWith(`${r}${a.sep}`)||s!==a.resolve(o)&&c.has(a.basename(e)))}(e,o,t)})}(r.workspaceRoot,a.join(p,"workspace"),p),await Promise.all([t(a.join(p,"Dockerfile"),dockerfile(l),"utf8"),t(a.join(p,"docker-compose.yaml"),["services:"," stable-harness:"," build: ."," ports:",' - "${STABLE_HARNESS_PORT:-3000}:3000"'," volumes:",` - stable-harness-data:${i}`,"volumes:"," stable-harness-data:",""].join("\n"),"utf8"),t(a.join(p,".dockerignore"),[".env","node_modules","dist",".git","*.log",""].join("\n"),"utf8"),t(a.join(p,".env.example"),["# Add model provider keys or runtime secrets here before deployment.","# OPENAI_API_KEY=","# ANTHROPIC_API_KEY=","# LANGSMITH_API_KEY=",""].join("\n"),"utf8"),t(a.join(p,"stable-workspace.json"),JSON.stringify(manifest(r,p,l),null,2),"utf8")]),function renderBuildSummary(e){return[`stable-harness docker workspace artifact: ${e}`,`Dockerfile: ${a.join(e,"Dockerfile")}`,`Compose: ${a.join(e,"docker-compose.yaml")}`,`Data mount: stable-harness-data -> ${i}`,"Run: docker compose up --build",""].join("\n")}(p)}function dockerfile(e){return["FROM node:24-slim",`ARG STABLE_HARNESS_PACKAGE=stable-harness@${e}`,"WORKDIR /stable-runtime",'RUN npm init -y && npm install "${STABLE_HARNESS_PACKAGE}"',"ENV PATH=/stable-runtime/node_modules/.bin:${PATH}",`ENV STABLE_HARNESS_DATA_DIR=${i}`,"WORKDIR /workspace","COPY workspace/ /workspace/","RUN if [ -f package.json ]; then npm install --omit=dev; fi",`VOLUME ["${i}"]`,"EXPOSE 3000",'CMD ["stable-harness", "start", "-w", "/workspace", "--host", "0.0.0.0", "--port", "3000"]',""].join("\n")}function manifest(e,o,r){return{target:"docker",workspaceRoot:e.workspaceRoot,defaultAgentId:e.workspace.runtime.defaultAgentId,dataDir:i,stableHarnessVersion:r,copiedWorkspaceDir:a.join(o,"workspace"),excludedNames:[...c].sort(),inventory:{agents:e.workspace.agents.size,models:e.workspace.models.size,tools:e.workspace.tools.size,skills:e.workspace.skills.size,memories:e.workspace.memories.size,workflows:e.workspace.workflows.size,evaluations:e.workspace.evaluations?.size??0}}}async function readPackageVersion(e){try{const o=JSON.parse(await r(e,"utf8"));return"string"==typeof o.version&&o.version.trim()?o.version.trim():void 0}catch{return}}
|
|
@@ -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{createBackendModel as r,createDeepAgentsAdapter as o}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as s}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as a}from"@stable-harness/core";import{projectEvent as i,projectRuntimeTrace as n}from"@stable-harness/core";import{createInMemoryApprovalQueue as
|
|
2
|
+
import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createBackendModel as r,createDeepAgentsAdapter as o}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as s}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as a}from"@stable-harness/core";import{projectEvent as i,projectRuntimeTrace as n}from"@stable-harness/core";import{createInMemoryApprovalQueue as u}from"@stable-harness/governance";import{createModuleToolGateway as d}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as l}from"@stable-harness/workspace-yaml";import{helpText as p,parseArgs as c}from"./args.js";import{buildWorkspaceArtifact as f}from"./build.js";import{formatCliRuntimeEvent as m,readCliEventViewConfig as w,shouldEnableCliProgressNarration as v}from"./event-view.js";import{initWorkspace as g}from"./init.js";import{ensureCliMemoryServices as y}from"./memory/lifecycle.js";import{createCliMemoryProviders as I}from"./memory/providers.js";import{formatDetail as k,inspectWorkflow as R,renderAgent as h,renderWorkflow as b,workspaceStatus as q}from"./output.js";import{serveProtocol as A,stopProtocol as C}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=c(e);if(t.help)return void process.stdout.write(p());const r=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),s=t.workspaceRoot;try{if("init"===t.command)return void process.stdout.write(await g(t.prompt||s));const e=await l(s);if(t.workflowRenderId)return void process.stdout.write(b(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(R(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(h(e,t.agentRenderId));if("build"===t.command)return void process.stdout.write(await f({workspace:e,workspaceRoot:s,outputDir:t.buildOutput,target:t.buildTarget}));if("stop"===t.command)return clearTimeout(r),void await C(e,t);const p=await d({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await y(e);const c=I(e),M=u(),j=w(e.runtime);let $;if($=a({workspace:e,toolGateway:p,approvals:M,memoryProviders:c,adapters:[o()],workflowAdapters:[createCliWorkflowAdapter(p,()=>$)],progressNarration:v(j,e.runtime)?{enabled:!0,style:"cli"}:void 0,qualityReviewModel:createQualityReviewModel(e)}),t.serveOpenAi)return clearTimeout(r),void await A($,t);if(!t.shouldRunRequest)return void process.stdout.write(q(e,s));t.trace&&$.subscribe(e=>{const t=i(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${k(t.detail)}\n`)}),$.subscribe(e=>{const t=m(e,j);t&&process.stdout.write(`${t}\n`)});const S=await $.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=$.getRun(S.requestId),r=e?n(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${S.output}\n`)}finally{clearTimeout(r)}}function createQualityReviewModel(e){const t=function readQualityModelRef(e){const t=isRecord(e)?e:{},r=isRecord(t.reviewer)?t.reviewer:t;return"string"==typeof r.modelRef&&r.modelRef.trim()?r.modelRef.trim():void 0}(e.runtime.quality),o=t?e.models.get(t):void 0,s=o?r(o):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(s)?s:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function createCliWorkflowAdapter(e,t){return s({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,d=o.input,l=i.outputs,!0===u?.inputFromState?{...u,requestInput:d,outputs:l}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof d&&null!==d?d:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`,approvalIds:readApprovalIds(o.metadata)}})).output;var u,d,l},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,u,d;return(await t().request({input:(i=e,n=o.input,u=a.outputs,d=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.",...d?[`Workflow node config: ${JSON.stringify(d)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}function readApprovalIds(e){const t=e?.approvalIds??e?.approvalId;return"string"==typeof t&&t.trim()?[t.trim()]:Array.isArray(t)?t.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}(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});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { runCli } from "./cli.js";
|
|
2
2
|
export { parseArgs } from "./args.js";
|
|
3
|
+
export { buildWorkspaceArtifact } from "./build.js";
|
|
3
4
|
export { isStableHarnessStartCommand } from "./server.js";
|
|
4
5
|
export { ensureCliMemoryServices } from "./memory/lifecycle.js";
|
|
5
6
|
export { createCliMemoryProviders } from "./memory/providers.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{runCli}from"./cli.js";export{parseArgs}from"./args.js";export{isStableHarnessStartCommand}from"./server.js";export{ensureCliMemoryServices}from"./memory/lifecycle.js";export{createCliMemoryProviders}from"./memory/providers.js";
|
|
1
|
+
export{runCli}from"./cli.js";export{parseArgs}from"./args.js";export{buildWorkspaceArtifact}from"./build.js";export{isStableHarnessStartCommand}from"./server.js";export{ensureCliMemoryServices}from"./memory/lifecycle.js";export{createCliMemoryProviders}from"./memory/providers.js";
|
|
@@ -1 +1 @@
|
|
|
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}`}
|
|
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,m=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),g=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, resolveEnabledMemories } 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 memories: resolveInspectionMemories(workspace, agent),\n } : { tools: [], subagents: [], skills: [], memories: [] };\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 ...snapshot.memories.map((id) => runnableNode("memory_" + safeNodeId(id), "memory:" + 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 ...snapshot.memories.map((id) => edge(agentId, "memory_" + safeNodeId(id), true)),\n edge(agentId, "__end__", true),\n ];\n return { nodes, edges };\n}\n\nfunction resolveInspectionMemories(workspace, agent) {\n const runtimeMemories = resolveEnabledMemories(workspace, "all").map((memory) => memory.id);\n const agentMemories = Array.isArray(agent.memory) ? agent.memory.flatMap((memory, index) => {\n if (typeof memory === "string" && memory.trim()) return [memory.trim()];\n if (typeof memory?.path === "string" && memory.path.trim()) return [memory.path.trim()];\n return ["agent-memory-" + index];\n }) : [];\n return [...new Set([...agentMemories, ...runtimeMemories])];\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\nfunction safeNodeId(value) {\n return String(value).replace(/[^A-Za-z0-9_$]/gu, "_");\n}\n',i,""].join("\n");var c}(t,o)),{file:c,relativePath:`./${s.relative(t,c).split(s.sep).join("/")}`}}(l,m);await i(l,c);const p=Object.fromEntries(m.map(e=>[e.id,`${g.relativePath}:${e.exportName}`])),d=await o({host:c.host,port:c.port,nWorkers:c.nWorkers,cwd:l,graphs:p});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}`}
|
|
@@ -6,11 +6,13 @@ export * from "./recovery/tool-call.js";
|
|
|
6
6
|
export * from "./runtime/persistence/inspection.js";
|
|
7
7
|
export { createWorkspaceSandboxPolicy } from "./runtime/governance/sandbox.js";
|
|
8
8
|
export * from "./memory-plugins.js";
|
|
9
|
+
export { resolveEnabledMemories } from "./memory-plugins/shared.js";
|
|
9
10
|
export * from "./runtime/persistence/queue.js";
|
|
10
11
|
export * from "./runtime/policy/projection.js";
|
|
11
12
|
export * from "./runtime.js";
|
|
12
13
|
export * from "./runtime/selection-repair.js";
|
|
13
14
|
export * from "./runtime/tool-failure.js";
|
|
15
|
+
export * from "./runtime/tracing/langsmith.js";
|
|
14
16
|
export * from "./runtime/policy/tool-invocation.js";
|
|
15
17
|
export * from "./runtime/persistence/stores.js";
|
|
16
18
|
export * from "./trace.js";
|
|
@@ -1 +1 @@
|
|
|
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{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/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{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import type { CallCandidate, CallSelectionDiagnostics } from "@botbotgo/better-call";
|
|
2
1
|
import type { RuntimeEvent, RuntimeInventoryRepairDiagnostic, RuntimeInventoryRepairLayer } from "./events.js";
|
|
2
|
+
export type RuntimeSelectionCandidate = {
|
|
3
|
+
id: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export type RuntimeSelectionDiagnostics = {
|
|
8
|
+
originalId: string;
|
|
9
|
+
repairedId?: string;
|
|
10
|
+
matchSource?: string;
|
|
11
|
+
confidence?: number;
|
|
12
|
+
};
|
|
3
13
|
export type RuntimeSelectionRepairResult = {
|
|
4
14
|
ok: true;
|
|
5
15
|
id: string;
|
|
6
|
-
diagnostics?:
|
|
16
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
7
17
|
} | {
|
|
8
18
|
ok: false;
|
|
9
19
|
reason: string;
|
|
10
|
-
diagnostics?:
|
|
20
|
+
diagnostics?: RuntimeSelectionDiagnostics;
|
|
11
21
|
};
|
|
12
22
|
export type RuntimeSelectionRepairTrace = {
|
|
13
23
|
layer: RuntimeInventoryRepairLayer;
|
|
@@ -19,6 +29,6 @@ export type RuntimeSelectionRepairTrace = {
|
|
|
19
29
|
};
|
|
20
30
|
export declare function repairRuntimeSelection(input: {
|
|
21
31
|
id: string;
|
|
22
|
-
candidates:
|
|
32
|
+
candidates: RuntimeSelectionCandidate[];
|
|
23
33
|
trace?: RuntimeSelectionRepairTrace;
|
|
24
34
|
}): Promise<RuntimeSelectionRepairResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export async function repairRuntimeSelection(e){const i=function repairSelection(e,i){const n=normalizeSelectionId(e),t=i.filter(e=>normalizeSelectionId(e.id)===n);if(1===t.length){const i=t[0];return{ok:!0,id:i.id,diagnostics:{originalId:e,repairedId:i.id,matchSource:i.id===e?"exact":"normalized_id",confidence:i.id===e?1:.95}}}return{ok:!1,reason:t.length>1?"ambiguous":"no_candidate",diagnostics:{originalId:e}}}(e.id,e.candidates);return i.ok?(emitSelectionRepair(e,"repaired",i.id,i.diagnostics),i):(emitSelectionRepair(e,"blocked",void 0,i.diagnostics,i.reason),i)}function emitSelectionRepair(e,i,n,t,o){const r=e.trace;r&&r.emit({type:"runtime.inventory.repair",requestId:r.requestId,sessionId:r.sessionId,agentId:r.agentId,status:i,diagnostic:{layer:r.layer,owner:r.owner,originalId:t?.originalId??e.id,repairedId:t?.repairedId??n,candidateIds:e.candidates.map(e=>e.id),reason:o,matchSource:t?.matchSource,confidence:t?.confidence}})}function normalizeSelectionId(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/gu,"")}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Client } from "langsmith";
|
|
2
|
+
import type { RuntimeCapabilityModule } from "../capabilities.js";
|
|
3
|
+
import type { RuntimeRunRecord, RuntimeStore } from "../types.js";
|
|
4
|
+
import type { WorkspaceRuntimePolicy } from "../../workspace/types.js";
|
|
5
|
+
export type LangSmithTraceClient = {
|
|
6
|
+
createRun(run: LangSmithRunCreate): Promise<void>;
|
|
7
|
+
flush?(): Promise<void>;
|
|
8
|
+
awaitPendingTraceBatches?(): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
type LangSmithRunCreate = Parameters<Client["createRun"]>[0];
|
|
11
|
+
export type RuntimeLangSmithTracingOptions = {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
projectName?: string;
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
apiUrl?: string;
|
|
16
|
+
workspaceId?: string;
|
|
17
|
+
tags?: string[];
|
|
18
|
+
client?: LangSmithTraceClient;
|
|
19
|
+
failOnError?: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function createLangSmithTracingCapability(input: {
|
|
22
|
+
policy: WorkspaceRuntimePolicy;
|
|
23
|
+
store: RuntimeStore;
|
|
24
|
+
options?: RuntimeLangSmithTracingOptions | false;
|
|
25
|
+
}): RuntimeCapabilityModule | undefined;
|
|
26
|
+
export declare function createLangSmithRunTree(run: RuntimeRunRecord, options?: RuntimeLangSmithTracingOptions): LangSmithRunCreate;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as t}from"node:crypto";import{projectRuntimeTraceSpans as e}from"../../trace.js";export function createLangSmithTracingCapability(t){const e=function resolveOptions(t,e){if(!1===e)return{enabled:!1};const n=function readLangSmithConfig(t){const e=readRecord(t.tracing),n=readRecord(e?.langsmith);return{enabled:readBoolean(n?.enabled),projectName:readString(n?.projectName)??readString(n?.project),apiKey:readString(n?.apiKey),apiUrl:readString(n?.apiUrl),workspaceId:readString(n?.workspaceId),tags:(r=n?.tags,Array.isArray(r)?r.filter(t=>"string"==typeof t&&t.trim().length>0):void 0),failOnError:readBoolean(n?.failOnError)};var r}(t);return{...n,...e,enabled:e?.enabled??n.enabled??!1}}(t.policy,t.options);if(!e.enabled)return;const n=function createClientResolver(t){let e;return()=>(e??=async function resolveClient(t){if(t.client)return t.client;const{Client:e}=await import("langsmith");return new e({apiKey:t.apiKey,apiUrl:t.apiUrl,workspaceId:t.workspaceId,autoBatchTracing:!0})}(t),e)}(e),r=function createLangSmithExporter(t,e,n){return async r=>{const a=await n(),i=e.getRun(r);i&&(await a.createRun(createLangSmithRunTree(i,t)),await flushClient(a))}}(e,t.store,n),a=new Set,i=[];return{id:"runtime.tracing.langsmith",onEvent(t){if(!function isTerminalRequestEvent(t){return"runtime.request.completed"===t.type||"runtime.request.failed"===t.type||"runtime.request.cancelled"===t.type}(t))return;const e=r(t.requestId).catch(t=>{i.push(t)});a.add(e),e.finally(()=>a.delete(e)).catch(()=>{})},async stop(){if(await Promise.allSettled([...a]),await flushClient(await n()),e.failOnError&&i[0])throw i[0]}}}export function createLangSmithRunTree(t,n={}){const r=e(t),a=r.find(t=>"run"===t.kind)??r[0];if(!a)return function createFallbackRun(t,e){return{id:stableUuid(`run:${t.requestId}`),name:t.agentId,run_type:"chain",start_time:t.startedAt,end_time:t.completedAt,inputs:{input:t.input},outputs:t.output?{output:t.output}:void 0,error:t.error,project_name:e.projectName}}(t,n);const i=stableUuid(a.spanId);return{...toLangSmithRun(a,i,n),child_runs:childRuns(a.spanId,r,i,n)}}async function flushClient(t){await(t.flush?.()),await(t.awaitPendingTraceBatches?.())}function toLangSmithRun(t,e,n){return{id:stableUuid(t.spanId),name:t.name,run_type:"tool"===t.kind?"tool":"chain",start_time:t.startedAt,end_time:t.completedAt,inputs:{spanId:t.spanId},outputs:"failed"===t.status?void 0:{status:t.status},error:"failed"===t.status?String(t.attributes?.error??"failed"):void 0,parent_run_id:t.parentSpanId?stableUuid(t.parentSpanId):void 0,trace_id:e,project_name:n.projectName,extra:{metadata:metadataForSpan(t,n)}}}function childRuns(t,e,n,r){return e.filter(e=>e.parentSpanId===t).map(t=>({...toLangSmithRun(t,n,r),child_runs:childRuns(t.spanId,e,n,r)}))}function metadataForSpan(t,e){return{stableHarness:{requestId:t.requestId,sessionId:t.sessionId,agentId:t.agentId,kind:t.kind,status:t.status,startEventIndex:t.startEventIndex,endEventIndex:t.endEventIndex,durationMs:t.durationMs,events:t.events,attributes:t.attributes,tags:["stable-harness",...e.tags??[]]}}}function stableUuid(e){const n=t("sha256").update(e).digest("hex").slice(0,32);return`${n.slice(0,8)}-${n.slice(8,12)}-4${n.slice(13,16)}-8${n.slice(17,20)}-${n.slice(20,32)}`}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function readString(t){return"string"==typeof t&&t.trim()?t.trim():void 0}function readBoolean(t){return"boolean"==typeof t?t:void 0}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ApprovalQueue } from "@stable-harness/governance";
|
|
2
2
|
import type { MemoryProvider, RuntimeMemoryStore } from "@stable-harness/memory";
|
|
3
3
|
import type { QualityReviewModel } from "./quality/index.js";
|
|
4
|
+
import { createLangSmithTracingCapability } from "./runtime/tracing/langsmith.js";
|
|
4
5
|
import type { CompiledWorkspace, RuntimeCapabilityModule, RuntimeToolGateway, RuntimeAdapter, RuntimeArtifactStore, RuntimeSandboxPolicy, RuntimeStore, RuntimeProgressNarrationOptions, RuntimeWorkflowAdapter, StableHarnessRuntime } from "./types.js";
|
|
5
6
|
type RuntimeFactoryInput = {
|
|
6
7
|
workspace: CompiledWorkspace;
|
|
@@ -16,6 +17,7 @@ type RuntimeFactoryInput = {
|
|
|
16
17
|
progressNarration?: RuntimeProgressNarrationOptions | false;
|
|
17
18
|
qualityReviewModel?: QualityReviewModel;
|
|
18
19
|
capabilities?: RuntimeCapabilityModule[];
|
|
20
|
+
langSmithTracing?: false | Parameters<typeof createLangSmithTracingCapability>[0]["options"];
|
|
19
21
|
};
|
|
20
22
|
export declare function createStableHarnessRuntime(input: RuntimeFactoryInput): StableHarnessRuntime;
|
|
21
23
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{assertNoRawToolCallOutput as r,assertNoToolExecutionErrorOutput as a,buildAdapterErrorRecoveryPrompt as o,buildEvidenceSynthesisOutput as s,buildExecutionContractRecoveryRequest as n,buildResultRecoveryRequest as i,containsRawToolCallOutput as u,isRecoverableAdapterError as c,rawToolCallFailureMessage as l,toolCallRecoveryEnabled as p}from"./recovery/tool-call.js";import{recoverQualityReview as d,resolveQualityPolicy as m}from"./quality/index.js";import{completeRun as w,failRun as y}from"./runtime/completion.js";import{runDirectToolCall as f}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{assertNoRawToolCallOutput as r,assertNoToolExecutionErrorOutput as a,buildAdapterErrorRecoveryPrompt as o,buildEvidenceSynthesisOutput as s,buildExecutionContractRecoveryRequest as n,buildResultRecoveryRequest as i,containsRawToolCallOutput as u,isRecoverableAdapterError as c,rawToolCallFailureMessage as l,toolCallRecoveryEnabled as p}from"./recovery/tool-call.js";import{recoverQualityReview as d,resolveQualityPolicy as m}from"./quality/index.js";import{completeRun as w,failRun as y}from"./runtime/completion.js";import{runDirectToolCall as f}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as g}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as I}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as q}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as R,normalizeAdapterResult as v}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as b}from"./runtime/memory.js";import{createInMemoryRuntimeStore as k}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as A}from"./runtime/progress-narration.js";import{repairRuntimeSelection as C}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as x}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as h}from"./runtime/tool-failure.js";import{runWorkflowRequest as j}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const r=new Set,a=t.store??k(),s=R([b(t),A({options:t.progressNarration,policy:t.workspace.runtime}),x({policy:t.workspace.runtime,store:a,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const o=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);a.appendEvent(o);for(const e of r)e(o)},emit=e=>{emitBase(e),s.emitSideEffects(e,emitBase)},i=I({gateway:g({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),u={...t,toolGateway:i},l=h(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const r=t.request.requestId??e(),a=t.request.sessionId??e(),s=[],{agent:i,adapter:u}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await C({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),o=e.workspace.agents.get(a);if(!o)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:o,adapter:void 0};const s=e.adapters.find(e=>e.canRun(o));if(!s)throw new Error(`No runtime adapter can run backend ${o.backend} for agent ${o.id}`);return{agent:o,adapter:s}}(t.input,t.request,{requestId:r,sessionId:a,emit:e=>s.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,r,a,i)),s.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:r,sessionId:a,agentId:i.id,input:t.request.input});try{if(t.request.workflow){const e=await j({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:r,sessionId:a,agentId:i.id,emit:t.emit});return w({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await f({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:r,sessionId:a,agent:i,toolFailureTracker:t.toolFailureTracker});return w({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,result:e,artifacts:t.input.artifacts})}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)),a=r.memory,s=r.pluginMemories??[],i=e.input.workspace.runtime,u=m(e.input.workspace.runtime,e.agent),l=new Map;let p;try{p=await runAdapterOnce(e,t,e.request,a,s,l)}catch(r){if(!c(r,i))throw r;p=await runAdapterOnce(e,t,o(e.request,r,i),a,s,l)}p=await recoverAdapterResultOutput(e,t,e.request,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),e.request,p,u),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:p});try{assertRequestExecutionContract(e)}catch(r){const o=n({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:i});if(!o)throw r;p=await runAdapterOnce(e,t,o,a,s,l),p=await recoverAdapterResultOutput(e,t,o,p,a,s,i,l),p=await d(createQualityRuntimeInput(e,a,s,l),o,p,u),assertRequestExecutionContract(e)}const y=w({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:p,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:p,response:y}),y}({...t,adapter:u,requestId:r,sessionId:a,agent:i})}catch(e){return y({store:t.store,emit:t.emit,requestId:r,sessionId:a,agent:i,error:e})}}({input:u,capabilities:s,store:a,emit:emit,request:t,toolFailureTracker:l}),subscribe:e=>(r.add(e),()=>r.delete(e)),...q({workspace:t.workspace,store:a,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),cancel(e,t){const r=a.getRun(e);r&&"running"===r.state&&(a.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 s.stop(),r.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}}function createQualityRuntimeInput(e,t,r,a){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:o=>runAdapterOnce(e,e.adapter,o,t,r,a),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}async function recoverAdapterResultOutput(e,t,o,n,c,d,m,w){let y=o;const f=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,a="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof a&&Number.isInteger(a)&&a>0?a:3}(m);let g=0;for(let r=0;r<f;r+=1){const r=e.store.getRun(e.requestId)?.events??[],a=i({request:y,output:n.text,events:r.slice(g),availableToolIds:e.agent.tools,policy:m});if(!a)break;y=a,g=e.store.getRun(e.requestId)?.events.length??0,n=await runAdapterOnce(e,t,a,c,d,w)}if(p(m)){let t=!1;u(n.text,m)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(o.metadata)&&(n={...n,text:l(),metadata:{...n.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const i=s({request:o,output:n.text,events:e.store.getRun(e.requestId)?.events??[],policy:m});i&&(t=!0,n={...n,text:i,metadata:{...n.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(r(n.text,m),a(n.text,m))}return n}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,a,o,s){return v(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:o,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:s,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
|
|
@@ -66,6 +66,7 @@ export type WorkspaceRuntimePolicy = {
|
|
|
66
66
|
sandbox?: Record<string, unknown>;
|
|
67
67
|
memory?: Record<string, unknown>;
|
|
68
68
|
protocols?: Record<string, unknown>;
|
|
69
|
+
tracing?: Record<string, unknown>;
|
|
69
70
|
progress?: Record<string, unknown>;
|
|
70
71
|
cli?: Record<string, unknown>;
|
|
71
72
|
quality?: string | Record<string, unknown>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@
|
|
1
|
+
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@easynet/better-call";
|
|
2
2
|
import type { ToolArgumentGuard, ToolArgumentIssue, ToolGatewayContext, ToolGatewayTool } from "./types.js";
|
|
3
3
|
type ToolGatewayRuntimeTool = ToolGatewayTool & {
|
|
4
4
|
validationTool?: BetterToolLike;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@
|
|
1
|
+
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@easynet/better-call";import{isRecord as r,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=i(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const l=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?l:"reject"===l.action?r:i(t.schema,l.args)||l:l}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const i=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(l=o.args,r(l)?l:{input:l})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var l;const n=i.ok?i.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await loadModuleTool(t);o&&e.push(o)}return t(e,o.options)}async function loadModuleTool(t){if(!t.sourcePath)return;const n=await import(e(o.resolve(t.sourcePath)).href),r=n[t.name??t.id]??n.default;return function hasInvoke(o){return"object"==typeof o&&null!==o&&"invoke"in o&&"function"==typeof o.invoke}(r)?{id:t.id,description:t.description??r.description,schema:t.schema??r.schema,validateArgs:r.validateArgs,invoke:(o,e)=>async function invokeWithWorkspaceCwd(o,e,t){const n=process.cwd();try{return process.chdir(t.workspaceRoot),await o(e,t)}finally{process.chdir(n)}}(r.invoke,o,e)}:void 0}
|
|
1
|
+
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await loadModuleTool(t);o&&e.push(o)}return t(e,o.options)}async function loadModuleTool(t){if(!t.sourcePath||!function isJavaScriptModule(e){const t=o.extname(e);return".mjs"===t||".js"===t||".cjs"===t}(t.sourcePath))return;const n=await import(e(o.resolve(t.sourcePath)).href),r=n[t.name??t.id]??n.default;return function hasInvoke(o){return"object"==typeof o&&null!==o&&"invoke"in o&&"function"==typeof o.invoke}(r)?{id:t.id,description:t.description??r.description,schema:t.schema??r.schema,validateArgs:r.validateArgs,invoke:(o,e)=>async function invokeWithWorkspaceCwd(o,e,t){const n=process.cwd();try{return process.chdir(t.workspaceRoot),await o(e,t)}finally{process.chdir(n)}}(r.invoke,o,e)}:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeArgsBySchema as e}from"@
|
|
1
|
+
import{normalizeArgsBySchema as e}from"@easynet/better-call";export function validateWithZodSchema(a,r){return isZodLike(a)?toZodGuardResult(a.safeParse(r??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(a)?function validateWithZodShape(a,r){const t=function normalizeZodShapeArgs(a,r){const t=isRecord(r)?r:{};return e(a,t,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(a,r),s={},o=[];for(const[e,r]of Object.entries(a)){const a=r.safeParse(t[e]);a.success?void 0!==a.data&&(s[e]=a.data):o.push(...a.error.issues.map(a=>({...a,path:[e,...a.path]})))}return o.length>0?toZodGuardResult({success:!1,error:{issues:o}}):{action:"allow",args:s}}(a,r):void 0}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function toZodGuardResult(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(a=e.path,a.length>0?`$.${a.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var a})}}function isZodLike(e){return isRecord(e)&&"function"==typeof e.safeParse}
|
|
@@ -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",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...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}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."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 readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}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}:{},...void 0!==t.outputSchema?{outputSchema:t.outputSchema}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{},..."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
|
+
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",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...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.tracing&&r.tracing?{tracing:r.tracing}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."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 readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}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}:{},...void 0!==t.outputSchema?{outputSchema:t.outputSchema}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{},..."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)}:{}}}
|