stable-harness 0.0.5 → 0.0.7
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
CHANGED
|
@@ -36,6 +36,14 @@ npm install stable-harness
|
|
|
36
36
|
|
|
37
37
|
Stable Harness currently targets Node.js `>=24 <25`.
|
|
38
38
|
|
|
39
|
+
Create a workspace without cloning this repo:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx stable-harness init ./my-agent-app
|
|
43
|
+
stable-harness -w ./my-agent-app
|
|
44
|
+
stable-harness -w ./my-agent-app --agent orchestra --tool echo_tool --tool-args-json '{"value":"hello"}'
|
|
45
|
+
```
|
|
46
|
+
|
|
39
47
|
## First Run
|
|
40
48
|
|
|
41
49
|
Clone the repo when developing the framework itself:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stable-harness",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Stable application runtime and operator control plane for agent workspaces.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
"@botbotgo/better-call": "^0.1.18",
|
|
78
78
|
"@langchain/core": "^1.1.43",
|
|
79
79
|
"@langchain/langgraph": "^1.3.0",
|
|
80
|
+
"@langchain/langgraph-api": "^1.2.1",
|
|
80
81
|
"@langchain/ollama": "^1.2.7",
|
|
81
82
|
"@langchain/openai": "^1.4.5",
|
|
82
83
|
"@stable-harness/adapter-deepagents": "file:packages/adapter-deepagents",
|
|
@@ -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 n=0;n<e.length;n+=1)n=parseOneArg(e,n,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,n){const o=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===n.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):"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(o,t,"workspaceRoot");if("--agent"===e[r])return setString(o,t,"agentId");if("--workflow"===e[r])return setString(o,t,"workflowRunId");if("--session-id"===e[r])return setString(o,t,"sessionId");if("--tool"===e[r])return setString(o,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}`)}}(o.value??"{}"),o.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(o,t,"host");if("--port"===e[r])return t.port=Number(o.value??t.port),o.index;if("--api-key"===e[r])return setString(o,t,"apiKey");if("--timeout-ms"===e[r])return t.timeoutMs=Number(o.value??t.timeoutMs),o.index;n.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 start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as s}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as i}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as l,parseArgs as d}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as c,shouldEnableCliProgressNarration as
|
|
2
|
+
import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as s}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as i}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as l,parseArgs as d}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as c,shouldEnableCliProgressNarration as m}from"./event-view.js";import{initWorkspace as w}from"./init.js";import{ensureCliMemoryServices as f}from"./memory/lifecycle.js";import{createCliMemoryProviders as g}from"./memory/providers.js";import{formatDetail as v,inspectWorkflow as I,renderAgent as k,renderWorkflow as y,workspaceStatus as h}from"./output.js";import{serveProtocol as b,stopProtocol as q}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=d(e);if(t.help)return void process.stdout.write(l());const o=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),R=t.workspaceRoot;try{if("init"===t.command)return void process.stdout.write(await w(t.prompt||R));const e=await u(R);if(t.workflowRenderId)return void process.stdout.write(y(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(I(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(k(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(o),void await q(e,t);const l=await n({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await f(e);const d=g(e),C=c(e.runtime);let $;if($=s({workspace:e,toolGateway:l,memoryProviders:d,adapters:[r()],workflowAdapters:[createCliWorkflowAdapter(l,()=>$)],progressNarration:m(C,e.runtime)?{enabled:!0,style:"cli"}:void 0}),t.serveOpenAi)return clearTimeout(o),void await b($,t);if(!t.shouldRunRequest)return void process.stdout.write(h(e,R));t.trace&&$.subscribe(e=>{const t=a(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${v(t.detail)}\n`)}),$.subscribe(e=>{const t=p(e,C);t&&process.stdout.write(`${t}\n`)});const j=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(j.requestId),r=e?i(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${j.output}\n`)}finally{clearTimeout(o)}}function createCliWorkflowAdapter(e,t){return o({nodeResolvers:{tools:async({id:t,node:r,request:o,requestId:s,sessionId:a,state:i,workspace:n})=>{return(await e.invoke({toolId:t,args:(u=r.config,l=o.input,d=i.outputs,!0===u?.inputFromState?{...u,requestInput:l,outputs:d}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof l&&null!==l?l:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`}})).output;var u,l,d},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,u,l;return(await t().request({input:(i=e,n=o.input,u=a.outputs,l=r.config,[`Workflow node agents.${i}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...l?[`Workflow node config: ${JSON.stringify(l)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}(function isCliEntrypoint(){const r=process.argv[1];if(!r)return!1;try{return e(t(import.meta.url))===e(r)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initWorkspace(targetRoot: string): Promise<string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mkdir as e,writeFile as o}from"node:fs/promises";import a from"node:path";export async function initWorkspace(o){const t=a.resolve(o||".");return await e(a.join(t,"config","agents"),{recursive:!0}),await e(a.join(t,"config","catalogs"),{recursive:!0}),await e(a.join(t,"config","runtime"),{recursive:!0}),await e(a.join(t,"resources","tools"),{recursive:!0}),await Promise.all([writeScaffoldFile(a.join(t,"config","runtime","workspace.yaml"),["apiVersion: stable-harness.dev/v1","kind: Runtime","metadata:"," name: app-runtime","spec:"," routing:"," defaultAgentId: orchestra"," protocols:"," inProcess: true"," openaiCompatible:"," host: 127.0.0.1"," port: 8642",""].join("\n")),writeScaffoldFile(a.join(t,"config","agents","orchestra.yaml"),["apiVersion: stable-harness.dev/v1","kind: Agent","metadata:"," name: orchestra","spec:"," backend: deepagents"," modelRef: local-dev"," systemPrompt: You are a concise workspace agent."," tools:"," - echo_tool"," subagents: []",""].join("\n")),writeScaffoldFile(a.join(t,"config","catalogs","models.yaml"),["apiVersion: stable-harness.dev/v1","kind: Model","metadata:"," name: local-dev","spec:"," provider: openai-compatible"," model: ${env:STABLE_HARNESS_MODEL:-gpt-4.1-mini}"," baseUrl: ${env:STABLE_HARNESS_OPENAI_BASE_URL:-https://api.openai.com/v1}"," apiKey: ${env:OPENAI_API_KEY}",""].join("\n")),writeScaffoldFile(a.join(t,"resources","tools","echo_tool.mjs"),["export const echo_tool = {"," description: 'Echo input through the Stable Harness tool gateway.',"," schema: {"," type: 'object',"," properties: { value: { type: 'string' } },"," required: ['value'],"," },"," async invoke(args) {"," return JSON.stringify({ echoed: args.value });"," },","};",""].join("\n"))]),[`Initialized Stable Harness workspace at ${t}`,"","Try:",` stable-harness -w ${shellPath(t)}`,` stable-harness -w ${shellPath(t)} --agent orchestra --tool echo_tool --tool-args-json '{"value":"hello"}'`,""].join("\n")}async function writeScaffoldFile(e,a){try{await o(e,a,{flag:"wx"})}catch(o){if(function isFileExists(e){return e instanceof Error&&"code"in e&&"EEXIST"===e.code}(o))throw new Error(`Refusing to overwrite existing scaffold file: ${e}`);throw o}}function shellPath(e){return JSON.stringify(e)}
|