stable-harness 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/compat/agent-harness.js +1 -1
  3. package/dist/index.d.ts +29 -6
  4. package/dist/index.js +1 -1
  5. package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
  6. package/dist/runtime/compat/json.js +1 -1
  7. package/dist/runtime/compat/presentation.js +1 -1
  8. package/dist/runtime/compat/prompts.js +1 -1
  9. package/dist/runtime/model/ollama.js +1 -1
  10. package/dist/runtime/skills/skill-metadata.js +1 -1
  11. package/dist/workspace/compile.js +1 -1
  12. package/package.json +14 -10
  13. package/packages/adapter-deepagents/dist/src/adapter.d.ts +1 -0
  14. package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
  15. package/packages/adapter-deepagents/dist/src/index.d.ts +1 -0
  16. package/packages/adapter-deepagents/dist/src/index.js +1 -1
  17. package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
  18. package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
  19. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +39 -0
  20. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -0
  21. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +32 -0
  22. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -0
  23. package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -0
  24. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
  25. package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
  26. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
  27. package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
  28. package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -0
  29. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
  30. package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
  31. package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +16 -0
  32. package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -0
  33. package/packages/adapter-deepagents/dist/src/memory.d.ts +5 -0
  34. package/packages/adapter-deepagents/dist/src/memory.js +1 -0
  35. package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
  36. package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
  37. package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
  38. package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
  39. package/packages/adapter-deepagents/package.json +1 -0
  40. package/packages/adapter-langgraph/dist/src/graph.d.ts +3 -0
  41. package/packages/adapter-langgraph/dist/src/graph.js +1 -0
  42. package/packages/adapter-langgraph/dist/src/index.d.ts +8 -0
  43. package/packages/adapter-langgraph/dist/src/index.js +1 -0
  44. package/packages/adapter-langgraph/dist/src/runtime.d.ts +3 -0
  45. package/packages/adapter-langgraph/dist/src/runtime.js +1 -0
  46. package/packages/adapter-langgraph/dist/src/skill-providers.d.ts +29 -0
  47. package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -0
  48. package/packages/adapter-langgraph/dist/src/types.d.ts +60 -0
  49. package/packages/adapter-langgraph/dist/src/types.js +1 -0
  50. package/packages/adapter-langgraph/package.json +16 -0
  51. package/packages/cli/dist/src/args.d.ts +25 -0
  52. package/packages/cli/dist/src/args.js +1 -0
  53. package/packages/cli/dist/src/cli.js +1 -1
  54. package/packages/cli/dist/src/event-view.d.ts +9 -0
  55. package/packages/cli/dist/src/event-view.js +1 -0
  56. package/packages/cli/dist/src/index.d.ts +3 -0
  57. package/packages/cli/dist/src/index.js +1 -1
  58. package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
  59. package/packages/cli/dist/src/langgraph-env.js +1 -0
  60. package/packages/cli/dist/src/langgraph-official.d.ts +13 -0
  61. package/packages/cli/dist/src/langgraph-official.js +1 -0
  62. package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
  63. package/packages/cli/dist/src/memory/lifecycle.js +1 -0
  64. package/packages/cli/dist/src/memory/providers.d.ts +3 -0
  65. package/packages/cli/dist/src/memory/providers.js +1 -0
  66. package/packages/cli/dist/src/output.d.ts +8 -0
  67. package/packages/cli/dist/src/output.js +1 -0
  68. package/packages/cli/dist/src/server.d.ts +5 -0
  69. package/packages/cli/dist/src/server.js +1 -0
  70. package/packages/cli/package.json +3 -0
  71. package/packages/core/dist/evaluations/index.d.ts +18 -0
  72. package/packages/core/dist/evaluations/index.js +1 -0
  73. package/packages/core/dist/execution-contract.d.ts +1 -0
  74. package/packages/core/dist/execution-contract.js +1 -1
  75. package/packages/core/dist/index.d.ts +8 -4
  76. package/packages/core/dist/index.js +1 -1
  77. package/packages/core/dist/memory-plugins/maintenance.d.ts +42 -0
  78. package/packages/core/dist/memory-plugins/maintenance.js +1 -0
  79. package/packages/core/dist/memory-plugins/shared.d.ts +8 -0
  80. package/packages/core/dist/memory-plugins/shared.js +1 -0
  81. package/packages/core/dist/memory-plugins.d.ts +5 -48
  82. package/packages/core/dist/memory-plugins.js +1 -1
  83. package/packages/core/dist/recovery/tool-call.d.ts +28 -0
  84. package/packages/core/dist/recovery/tool-call.js +1 -0
  85. package/packages/core/dist/runtime/completion.d.ts +17 -0
  86. package/packages/core/dist/runtime/completion.js +1 -0
  87. package/packages/core/dist/runtime/direct-tool-call.d.ts +10 -0
  88. package/packages/core/dist/runtime/direct-tool-call.js +1 -0
  89. package/packages/core/dist/runtime/events.d.ts +204 -0
  90. package/packages/core/dist/runtime/events.js +1 -0
  91. package/packages/core/dist/runtime/memory.d.ts +23 -0
  92. package/packages/core/dist/runtime/memory.js +1 -0
  93. package/packages/core/dist/{artifacts.d.ts → runtime/persistence/artifacts.d.ts} +1 -1
  94. package/packages/core/dist/runtime/persistence/artifacts.js +1 -0
  95. package/packages/core/dist/{inspection.d.ts → runtime/persistence/inspection.d.ts} +1 -1
  96. package/packages/core/dist/runtime/persistence/inspection.js +1 -0
  97. package/packages/core/dist/{queue.d.ts → runtime/persistence/queue.d.ts} +1 -1
  98. package/packages/core/dist/runtime/persistence/queue.js +1 -0
  99. package/packages/core/dist/{stores.d.ts → runtime/persistence/stores.d.ts} +1 -1
  100. package/packages/core/dist/runtime/persistence/stores.js +1 -0
  101. package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
  102. package/packages/core/dist/runtime/progress-narration.js +1 -0
  103. package/packages/core/dist/runtime/tool-gateway.d.ts +40 -0
  104. package/packages/core/dist/runtime/tool-gateway.js +1 -0
  105. package/packages/core/dist/runtime/types.d.ts +168 -0
  106. package/packages/core/dist/runtime/types.js +1 -0
  107. package/packages/core/dist/runtime.d.ts +7 -3
  108. package/packages/core/dist/runtime.js +1 -1
  109. package/packages/core/dist/spec-driven/config.d.ts +4 -0
  110. package/packages/core/dist/spec-driven/config.js +1 -0
  111. package/packages/core/dist/spec-driven/events.d.ts +11 -0
  112. package/packages/core/dist/spec-driven/events.js +1 -0
  113. package/packages/core/dist/spec-driven/index.d.ts +4 -0
  114. package/packages/core/dist/spec-driven/index.js +1 -0
  115. package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
  116. package/packages/core/dist/spec-driven/lifecycle.js +1 -0
  117. package/packages/core/dist/spec-driven/types.d.ts +38 -0
  118. package/packages/core/dist/spec-driven/types.js +1 -0
  119. package/packages/core/dist/trace.d.ts +1 -1
  120. package/packages/core/dist/trace.js +1 -1
  121. package/packages/core/dist/types.d.ts +31 -426
  122. package/packages/core/dist/workflows/index.d.ts +70 -0
  123. package/packages/core/dist/workflows/index.js +1 -0
  124. package/packages/core/dist/workflows/runtime.d.ts +12 -0
  125. package/packages/core/dist/workflows/runtime.js +1 -0
  126. package/packages/core/dist/workspace/types.d.ts +101 -0
  127. package/packages/core/dist/workspace/types.js +1 -0
  128. package/packages/governance/dist/src/skill-candidates.js +1 -1
  129. package/packages/governance/dist/src/types.d.ts +1 -1
  130. package/packages/memory/dist/src/langmem-service.js +1 -1
  131. package/packages/memory/dist/src/maintenance.js +1 -1
  132. package/packages/memory/dist/src/policy.js +1 -1
  133. package/packages/memory/dist/src/provider.js +1 -1
  134. package/packages/memory/dist/src/store.js +1 -1
  135. package/packages/protocols/dist/src/http-server.js +1 -1
  136. package/packages/protocols/dist/src/in-process-client.js +1 -1
  137. package/packages/protocols/dist/src/openai-compatible.js +1 -1
  138. package/packages/protocols/dist/src/openai-payload.d.ts +74 -0
  139. package/packages/protocols/dist/src/openai-payload.js +1 -0
  140. package/packages/protocols/dist/src/openai-stream.d.ts +39 -0
  141. package/packages/protocols/dist/src/openai-stream.js +1 -0
  142. package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
  143. package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
  144. package/packages/tool-gateway/dist/src/in-memory.js +1 -1
  145. package/packages/tool-gateway/dist/src/module-loader.js +1 -1
  146. package/packages/tool-gateway/dist/src/schema-validation.d.ts +3 -0
  147. package/packages/tool-gateway/dist/src/schema-validation.js +1 -0
  148. package/packages/tool-gateway/dist/src/types.d.ts +3 -0
  149. package/packages/tool-gateway/package.json +1 -1
  150. package/packages/workspace-yaml/dist/discovery.d.ts +4 -0
  151. package/packages/workspace-yaml/dist/discovery.js +1 -0
  152. package/packages/workspace-yaml/dist/documents.d.ts +16 -0
  153. package/packages/workspace-yaml/dist/documents.js +1 -0
  154. package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
  155. package/packages/workspace-yaml/dist/evaluations.js +1 -0
  156. package/packages/workspace-yaml/dist/loader.js +1 -1
  157. package/packages/workspace-yaml/dist/workflows.d.ts +16 -0
  158. package/packages/workspace-yaml/dist/workflows.js +1 -0
  159. package/packages/adapter-deepagents/dist/src/builtin-tool-policy.d.ts +0 -18
  160. package/packages/adapter-deepagents/dist/src/builtin-tool-policy.js +0 -1
  161. package/packages/adapter-deepagents/dist/src/messages.js +0 -1
  162. package/packages/adapter-deepagents/dist/src/stream-events.js +0 -1
  163. package/packages/core/dist/artifacts.js +0 -1
  164. package/packages/core/dist/inspection.js +0 -1
  165. package/packages/core/dist/queue.js +0 -1
  166. package/packages/core/dist/stores.js +0 -1
  167. /package/packages/adapter-deepagents/dist/src/{messages.d.ts → internal/messages.d.ts} +0 -0
  168. /package/packages/adapter-deepagents/dist/src/{stream-events.d.ts → internal/stream-events.d.ts} +0 -0
@@ -0,0 +1,10 @@
1
+ import { ToolMessage } from "@langchain/core/messages";
2
+ import type { RuntimeAdapter } from "@stable-harness/core";
3
+ type AdapterRunInput = Parameters<RuntimeAdapter["run"]>[0];
4
+ export declare function validateSkillFileBuiltinCall(input: AdapterRunInput, toolId: string, request: {
5
+ toolCall?: {
6
+ id?: string;
7
+ args?: unknown;
8
+ };
9
+ }): ToolMessage<import("@langchain/core/messages").MessageStructure<import("@langchain/core/messages").MessageToolSet>> | undefined;
10
+ export {};
@@ -0,0 +1 @@
1
+ import{ToolMessage as e}from"@langchain/core/messages";export function validateSkillFileBuiltinCall(t,l,r){const i=function readFilesystemTarget(e){const t=function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{};for(const e of["file_path","path","pattern","query"]){const l=t[e];if("string"==typeof l&&l.trim())return l.trim()}}(r.toolCall?.args);if(i&&function targetsRegisteredSkill(e,t){return[...e.workspace.skills.values()].some(l=>{const r=function virtualSkillDir(e,t){const l=function pathRelative(e,t){const l=e.split("/").filter(Boolean),r=t.split("/").filter(Boolean);if(!(r.length<l.length)){for(let e=0;e<l.length;e+=1)if(l[e]!==r[e])return;return r.slice(l.length).join("/")}}(e,t);if(void 0===l)return;const r=l.split("/").filter(Boolean),i=r.lastIndexOf("skills");return i<0?`/${r.slice(0,-1).join("/")}`:`/${r.slice(0,i+2).join("/")}`}(e.workspace.root,l.path);return!!r&&function pathMatches(e,t){const l=t.replace(/\/+$/u,"");return e===l||e.startsWith(`${l}/`)||e.includes(`${l}/`)||e.includes("SKILL.md")}(t,r)})}(t,i))return new e({tool_call_id:r.toolCall?.id??`stable-harness-${l}-skill-policy`,name:l,status:"error",content:[`Filesystem builtin tool '${l}' targeted registered skill inventory: ${i}.`,"Skills are already registered with the backend and are not evidence files for the user request.","Do not read, list, glob, or grep SKILL.md files or skill directories.","Continue with the declared agent tools, the skill behavior already loaded by the backend, and the evidence already collected."].join(" ")})}
@@ -0,0 +1 @@
1
+ export async function streamDeepAgentResult(t,e,r){let n,o="";for await(const a of e){const e=readStreamDelta(a);e&&(o+=e,t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.output.delta",text:e}}));const d=readStreamFinalOutput(a,r);d&&(n=d)}return n??o}function readStreamDelta(t){const e=isRecord(t)?t:{};if("on_chat_model_stream"===e.event)return function readChunkText(t){const e=t?.content;return"string"==typeof e?e:Array.isArray(e)&&e.map(readContentPartText).filter(Boolean).join("")||void 0}(readRecord(readRecord(e.data)?.chunk))}function readContentPartText(t){if("string"==typeof t)return t;const e=isRecord(t)?t:{};return"string"==typeof e.text&&e.text.trim()?e.text:void 0}function readStreamFinalOutput(t,e){const r=isRecord(t)?t:{};if("on_chain_end"!==r.event)return;const n=readRecord(r.data)?.output;return e(n)||void 0}function readRecord(t){return isRecord(t)?t:void 0}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}
@@ -0,0 +1,4 @@
1
+ import type { ToolRepeatState } from "./gateway-tools.js";
2
+ export declare function filterRepeatLimitedTools<T extends {
3
+ name?: unknown;
4
+ }>(tools: T[] | undefined, repeatState: ToolRepeatState | undefined): T[] | undefined;
@@ -0,0 +1 @@
1
+ import{isToolRepeatLimitReached as e}from"./gateway-tools.js";export function filterRepeatLimitedTools(t,o){return t&&o?t.filter(t=>"string"!=typeof t.name||!e(t.name,o)):t}
@@ -0,0 +1,16 @@
1
+ export declare function traceProjectionForBuiltinTool(toolId: string, phase: "agent.tool.start" | "agent.tool.result", args: unknown): {
2
+ traceType: string;
3
+ traceLabel: string;
4
+ subagentType: string | undefined;
5
+ todos?: undefined;
6
+ } | {
7
+ traceType: string;
8
+ traceLabel: string;
9
+ todos: any[];
10
+ subagentType?: undefined;
11
+ } | {
12
+ traceType?: undefined;
13
+ traceLabel?: undefined;
14
+ subagentType?: undefined;
15
+ todos?: undefined;
16
+ };
@@ -0,0 +1 @@
1
+ export function traceProjectionForBuiltinTool(t,e,r){return"task"===t?{traceType:"delegation",traceLabel:"agent.tool.start"===e?"delegation.start":"delegation.completed",subagentType:readTaskSubagentType(r)}:"write_todos"===t&&"agent.tool.result"===e?{traceType:"plan",traceLabel:"plan.updated",todos:readTodoArgs(r)}:{}}function readTaskSubagentType(t){const e=isRecord(t)?t:{};return readString(e.subagent_type)??readString(e.subagentType)}function readTodoArgs(t){const e=isRecord(t)?t:{};return Array.isArray(e.todos)?e.todos:[]}function isRecord(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function readString(t){return"string"==typeof t&&t.trim()?t:void 0}
@@ -0,0 +1,5 @@
1
+ import type { CompiledWorkspace, MemoryMaintenanceResult, MemoryMaintenanceTarget, MemoryMaintenanceTargetInput } from "@stable-harness/core";
2
+ export declare function resolveDeepAgentsNativeMemories(workspace: CompiledWorkspace): import("@stable-harness/core").WorkspaceMemory[];
3
+ export declare function createDeepAgentsMemoryMaintenanceTarget(input?: {
4
+ run?: (context: MemoryMaintenanceTargetInput) => Promise<MemoryMaintenanceResult>;
5
+ }): MemoryMaintenanceTarget;
@@ -0,0 +1 @@
1
+ import{resolveEnabledMemories as e}from"@stable-harness/core";export function resolveDeepAgentsNativeMemories(r){return!1===function readMemoryFlag(e,r,n){const o=readRecord(e[r])??readRecord(e[r.toLowerCase()]);return"boolean"==typeof o?.[n]?o[n]:void 0}(r.runtime.memory??{},"deepagentsMem","read")?[]:e(r,"all")}export function createDeepAgentsMemoryMaintenanceTarget(e={}){return{name:"deepagentsMem",run:async r=>e.run?e.run(r):{operations:[],metadata:{skipped:!0,reason:"No DeepAgents memory maintainer configured"}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}
@@ -0,0 +1,4 @@
1
+ import { ChatOllama } from "@langchain/ollama";
2
+ import { ChatOpenAI } from "@langchain/openai";
3
+ import type { WorkspaceModel } from "@stable-harness/core";
4
+ export declare function createBackendModel(model: WorkspaceModel): string | ChatOpenAI<import("@langchain/openai").ChatOpenAICallOptions> | ChatOllama;
@@ -0,0 +1 @@
1
+ import{ChatOllama as e}from"@langchain/ollama";import{ChatOpenAI as r}from"@langchain/openai";export function createBackendModel(r){return"ollama"===r.provider?function usesOpenAiCompatibleToolCalling(e){const r=readString(e.config?.toolCallingMode);return"openai-compatible"===r||"structured"===r}(r)?createOpenAiCompatibleModel({...r,config:{...r.config,baseURL:readOllamaOpenAiBaseUrl(r.config?.baseURL??r.config?.baseUrl)}}):function createOllamaModel(r){return new e(pruneUndefined({model:r.model,baseUrl:readString(r.config?.baseUrl),temperature:readNumber(r.config?.temperature),numCtx:readNumber(r.config?.numCtx),numPredict:readNumber(r.config?.numPredict),timeout:readNumber(r.config?.timeout),think:"boolean"==typeof r.config?.think?r.config.think:void 0}))}(r):"openai-compatible"===r.provider?createOpenAiCompatibleModel(r):r.model}function createOpenAiCompatibleModel(e){return new r(pruneUndefined({model:e.model,apiKey:readString(e.config?.apiKey),temperature:readNumber(e.config?.temperature),maxTokens:readNumber(e.config?.maxTokens)??readNumber(e.config?.numPredict),timeout:readNumber(e.config?.timeout),modelKwargs:readOpenAiCompatibleModelKwargs(e.config),configuration:pruneUndefined({baseURL:readString(e.config?.baseURL)??readString(e.config?.baseUrl),defaultHeaders:readStringRecord(e.config?.headers)})}))}function readOpenAiCompatibleModelKwargs(e){const r={...readRecord(e?.extraBody),...readRecord(e?.modelKwargs)},n=pruneUndefined({...r,think:r.think??("boolean"==typeof e?.think?e.think:void 0),num_ctx:r.num_ctx??readNumber(e?.numCtx),num_predict:r.num_predict??readNumber(e?.numPredict)});return Object.keys(n).length>0?n:void 0}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readStringRecord(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const r=Object.entries(e).filter(e=>"string"==typeof e[1]);return r.length>0?Object.fromEntries(r):void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readOllamaOpenAiBaseUrl(e){const r=readString(e);if(!r)return;const n=r.replace(/\/+$/u,"");return n.endsWith("/v1")?n:`${n}/v1`}
@@ -1 +1 @@
1
- import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(n){const o=[],i=r(n?.model);i&&o.push(e(i));const s=function(e){const t=r(e);if(!t)return;const n=Array.isArray(e?.tools)?e.tools.filter(e=>"string"==typeof e&&e.trim().length>0):void 0;return a({...t,tools:n})}(n?.tools);return s&&o.push(t(s)),o}function r(e){var t;if(e?.enabled)return a({maxRetries:n(e.maxRetries),backoffFactor:n(e.backoffFactor),initialDelayMs:n(e.initialDelayMs),maxDelayMs:n(e.maxDelayMs),jitter:"boolean"==typeof e.jitter?e.jitter:void 0,retryOn:o(e.retryOn),onFailure:(t=e.onFailure,"continue"===t||"error"===t?t:void 0)})}function n(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function o(e){const t=function(e){const t=new Set(["timeout","network","rateLimit","serverError"]);if(!Array.isArray(e))return[...t];const r=e.filter(e=>t.has(e));return r.length>0?r:[...t]}(e);return e=>t.some(t=>i(e,t))}function i(e,t){const r=s(e)?e:{},n=function(e){const t=e.status??e.statusCode??e.code;return"number"==typeof t?t:Number.isInteger(Number(t))?Number(t):void 0}(r),o=`${String(r.name??"")} ${String(r.code??"")} ${String(r.message??e)}`,a=r.cause;return("timeout"===t?/timeout|timedout|etimedout|abort/i.test(o):"network"===t?/network|fetch failed|econnreset|econnrefused|enotfound|eai_again/i.test(o):"rateLimit"===t?429===n||/rate.?limit|too many requests/i.test(o):"serverError"===t&&Boolean(n&&n>=500&&n<600))||s(a)&&i(a,t)}function s(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function a(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}
1
+ import{modelRetryMiddleware as e,toolRetryMiddleware as t}from"langchain";export function createDeepAgentsRetryMiddleware(r){const n=[],o=readRetryOptions(r?.model);o&&n.push(e(o));const i=function readToolRetryOptions(e){const t=readRetryOptions(e);if(!t)return;const r=Array.isArray(e?.tools)?e.tools.filter(e=>"string"==typeof e&&e.trim().length>0):void 0;return pruneUndefined({...t,tools:r})}(r?.tools);return i&&n.push(t(i)),n}function readRetryOptions(e){var t;if(e?.enabled)return pruneUndefined({maxRetries:readNonNegativeNumber(e.maxRetries),backoffFactor:readNonNegativeNumber(e.backoffFactor),initialDelayMs:readNonNegativeNumber(e.initialDelayMs),maxDelayMs:readNonNegativeNumber(e.maxDelayMs),jitter:"boolean"==typeof e.jitter?e.jitter:void 0,retryOn:createRetryPredicate(e.retryOn),onFailure:(t=e.onFailure,"continue"===t||"error"===t?t:void 0)})}function readNonNegativeNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function createRetryPredicate(e){const t=function readRetryReasons(e){const t=new Set(["timeout","network","rateLimit","serverError"]);if(!Array.isArray(e))return[...t];const r=e.filter(e=>t.has(e));return r.length>0?r:[...t]}(e);return e=>t.some(t=>matchesRetryReason(e,t))}function matchesRetryReason(e,t){const r=isRecord(e)?e:{},n=function readStatus(e){const t=e.status??e.statusCode??e.code;return"number"==typeof t?t:Number.isInteger(Number(t))?Number(t):void 0}(r),o=`${String(r.name??"")} ${String(r.code??"")} ${String(r.message??e)}`,i=r.cause;return("timeout"===t?/timeout|timedout|etimedout|abort/i.test(o):"network"===t?/network|fetch failed|terminated|connection error|eof|econnreset|econnrefused|enotfound|eai_again/i.test(o):"rateLimit"===t?429===n||/rate.?limit|too many requests/i.test(o):"serverError"===t&&Boolean(n&&n>=500&&n<600))||isRecord(i)&&matchesRetryReason(i,t)}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}
@@ -1,6 +1,12 @@
1
1
  import type { RuntimeAdapterContext, RuntimeAdapterResult } from "@stable-harness/core";
2
2
  export type DeepAgentFactory = (params?: Record<string, unknown>) => {
3
- invoke(input: Record<string, unknown>): Promise<unknown>;
3
+ invoke(input: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
4
4
  streamEvents?(input: Record<string, unknown>, options?: Record<string, unknown>): AsyncIterable<unknown>;
5
5
  };
6
+ export type DeepAgentsModule = {
7
+ createDeepAgent?: unknown;
8
+ FilesystemBackend?: new (options: {
9
+ rootDir: string;
10
+ }) => unknown;
11
+ };
6
12
  export type DeepAgentsAdapterRunner = (input: RuntimeAdapterContext) => Promise<RuntimeAdapterResult>;
@@ -12,6 +12,7 @@
12
12
  "dependencies": {
13
13
  "@langchain/core": "^1.1.43",
14
14
  "@langchain/ollama": "^1.2.7",
15
+ "@langchain/openai": "^1.4.5",
15
16
  "langchain": "^1.3.1",
16
17
  "@stable-harness/core": "0.0.1"
17
18
  },
@@ -0,0 +1,3 @@
1
+ import { type RuntimeOutput, type RuntimeWorkflowAdapterInput } from "@stable-harness/core";
2
+ import type { LangGraphWorkflowAdapterOptions } from "./types.js";
3
+ export declare function runLangGraphWorkflow(input: RuntimeWorkflowAdapterInput, options: LangGraphWorkflowAdapterOptions, adapterName: string): Promise<RuntimeOutput>;
@@ -0,0 +1 @@
1
+ import{Annotation as o,END as e,START as t,StateGraph as n}from"@langchain/langgraph";import{compileWorkflowPlan as r}from"@stable-harness/core";import{resolveSkillProvider as a}from"./skill-providers.js";export async function runLangGraphWorkflow(a,d,i){!function assertSupportedPlan(o){if(o.cycles.length>0)throw new Error(`LangGraph workflow adapter does not enable cyclic graphs by default: ${o.workflowId}`);if(o.unreachableNodes.length>0)throw new Error(`LangGraph workflow has unreachable nodes: ${o.unreachableNodes.join(", ")}`)}(r(a.workflow));const s=function compileLangGraph(a,d,i){let s=new n(function createStateAnnotation(){return o.Root({input:o,outputs:o({reducer:(o,e)=>({...o,...e}),default:()=>({})}),trace:o({reducer:(o,e)=>[...o,...e],default:()=>[]})})}());for(const o of a.workflow.nodes)s=s.addNode(o.id,async e=>runNode({input:a,node:o,state:e,options:d,adapterName:i}));s=s.addEdge(t,a.workflow.entry??a.workflow.nodes[0].id),s=function addStaticEdges(o,e){for(const t of e.workflow.edges)t.condition||(o=o.addEdge(t.from,t.to));return o}(s,a),s=function addConditionalEdges(o,e,t){for(const[n,r]of function conditionalEdgesBySource(o){const e=new Map;for(const t of o.workflow.edges){if(!t.condition)continue;const o=e.get(t.from)??[];o.push({condition:t.condition,to:t.to}),e.set(t.from,o)}return e}(e)){const a=t.conditionalRouters?.[n];if(!a)throw new Error(`LangGraph workflow conditional edges from ${n} require a conditional router`);o=o.addConditionalEdges(n,async o=>a({...e,from:n,edges:r,state:o}),Object.fromEntries(r.map(o=>[o.condition,o.to])))}return o}(s,a,d);for(const o of r(a.workflow).terminalNodes)s=s.addEdge(o,e);return s.compile()}(a,d,i);a.emit({adapter:i,phase:"agent.langgraph.invoke",workflowId:a.workflow.id});const u=await s.invoke({input:a.request.input,outputs:{},trace:[]});return{text:stringifyWorkflowOutput(u),metadata:{workflowId:a.workflow.id,adapter:i,outputs:u.outputs,trace:u.trace}}}async function runNode(o){const e=await function resolveNodeHandler(o,e){const t=e.nodeHandlers?.[o.id]??e.nodeHandlers?.[o.use];if(t)return t;const n=function parseNodeUse(o){const e=o.indexOf(".");if(!(e<=0||e===o.length-1))return{kind:o.slice(0,e),id:o.slice(e+1)}}(o.use);if("workflows"===n?.kind&&e.enableSubworkflows)return o=>async function runSubworkflow(o,e){const t=o.workspace.workflows.get(o.id);if(!t)throw new Error(`LangGraph subworkflow is not defined: ${o.id}`);const n=function readSubworkflowDepth(o){const e=o?.subworkflowDepth;return"number"==typeof e&&Number.isFinite(e)?e:0}(o.request.metadata),r=e.maxSubworkflowDepth??8;if(n>=r)throw new Error(`LangGraph subworkflow depth exceeded ${r}`);return(await runLangGraphWorkflow({...o,workflow:t,request:{...o.request,input:o.state,metadata:{...o.request.metadata,subworkflowDepth:n+1}}},e,e.name??"langgraph")).text}({...o,id:n.id},e);const r=n?e.nodeResolvers?.[n.kind]:void 0;if(r&&n)return o=>r({...o,kind:n.kind,id:n.id});const d="skills"===n?.kind?a(e):void 0;if(d&&n)return o=>d.resolve({...o,kind:n.kind,id:n.id});const i=e.defaultNodeHandler;if(i)return i;throw new Error(`LangGraph workflow node ${o.id} (${o.use}) has no handler or resolver`)}(o.node,o.options)({...o.input,node:o.node,state:o.state});return o.input.emit({adapter:o.adapterName,phase:"agent.node.completed",workflowId:o.input.workflow.id,nodeId:o.node.id}),{outputs:{[o.node.id]:e},trace:[{nodeId:o.node.id,use:o.node.use,output:e}]}}function stringifyWorkflowOutput(o){const e=o.trace.at(-1)?.output;return"string"==typeof e?e:JSON.stringify(e??o.outputs)}
@@ -0,0 +1,8 @@
1
+ import type { RuntimeWorkflowAdapter } from "@stable-harness/core";
2
+ import { createLangGraphRuntimeAdapter } from "./runtime.js";
3
+ import { createDeepAgentsMiddlewareSkillProvider, createRegistrySkillResolverProvider } from "./skill-providers.js";
4
+ import type { LangGraphWorkflowAdapterOptions } from "./types.js";
5
+ export type { LangGraphConditionalRouter, LangGraphConditionalRouterInput, LangGraphNodeHandler, LangGraphNodeHandlerInput, LangGraphNodeResolver, LangGraphNodeResolverInput, LangGraphSkillMiddlewareProvider, LangGraphSkillMiddlewareProviderInput, LangGraphSkillResolverProvider, LangGraphWorkflowAdapterOptions, LangGraphWorkflowState, LangGraphWorkflowTraceEntry, } from "./types.js";
6
+ export type { LangGraphRegistrySkillOutput } from "./skill-providers.js";
7
+ export { createDeepAgentsMiddlewareSkillProvider, createLangGraphRuntimeAdapter, createRegistrySkillResolverProvider, };
8
+ export declare function createLangGraphWorkflowAdapter(options?: LangGraphWorkflowAdapterOptions): RuntimeWorkflowAdapter;
@@ -0,0 +1 @@
1
+ import{runLangGraphWorkflow as r}from"./graph.js";import{createLangGraphRuntimeAdapter as e}from"./runtime.js";import{createDeepAgentsMiddlewareSkillProvider as a,createRegistrySkillResolverProvider as t}from"./skill-providers.js";export{a as createDeepAgentsMiddlewareSkillProvider,e as createLangGraphRuntimeAdapter,t as createRegistrySkillResolverProvider};export function createLangGraphWorkflowAdapter(e={}){const a=e.name??"langgraph";return{name:a,run:t=>r(t,e,a)}}
@@ -0,0 +1,3 @@
1
+ import type { RuntimeAdapter } from "@stable-harness/core";
2
+ import type { LangGraphWorkflowAdapterOptions } from "./types.js";
3
+ export declare function createLangGraphRuntimeAdapter(options?: LangGraphWorkflowAdapterOptions): RuntimeAdapter;
@@ -0,0 +1 @@
1
+ import{runLangGraphWorkflow as e}from"./graph.js";export function createLangGraphRuntimeAdapter(n={}){const t=n.name??"langgraph";return{name:t,canRun:e=>e.backend===t,run:r=>async function runLangGraphAgent(n,t,r){return e({workspace:n.workspace,workflow:workflowFromAgent(n.agent,r),request:{input:n.request.input,metadata:n.request.metadata},requestId:n.requestId,sessionId:n.sessionId,toolGateway:n.toolGateway,emit:e=>n.emit({type:"runtime.adapter.event",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,event:e})},t,r)}(r,n,t)}}function workflowFromAgent(e,n){const t=e.edges??[],r=t.length>0?agentNodes(e):agentNodes(e).slice(0,1);return function validateAgentGraph(e,n){const t=new Set;for(const r of n){if(t.has(r.id))throw new Error(`LangGraph agent ${e.id} has duplicate graph node ${r.id}`);t.add(r.id)}for(const n of e.edges??[])if(!t.has(n.from)||!t.has(n.to))throw new Error(`LangGraph agent ${e.id} edge references unknown node ${n.from}->${n.to}`)}({...e,edges:t},r),{id:e.id,...e.description?{description:e.description}:{},...e.sourcePath?{sourcePath:e.sourcePath}:{},adapter:n,entry:readAgentEntry(e,r),nodes:r,edges:t,...Object.keys(e.config).length>0?{config:e.config}:{}}}function agentNodes(e){return[...e.subagents.map(e=>inventoryNode("agents",e)),...(e.skills??[]).map(e=>inventoryNode("skills",e)),...e.tools.map(e=>inventoryNode("tools",e))]}function inventoryNode(e,n){return{id:n,use:`${e}.${n}`}}function readAgentEntry(e,n){return e.edges?.[0]?.from??n[0]?.id}
@@ -0,0 +1,29 @@
1
+ import type { LangGraphSkillMiddlewareProvider, LangGraphSkillResolverProvider } from "./types.js";
2
+ type RegistrySkillResolverOptions = {
3
+ includeContent?: boolean;
4
+ maxBytes?: number;
5
+ };
6
+ type DeepAgentsSkillsModule = {
7
+ createSkillsMiddleware?: (options: Record<string, unknown>) => unknown;
8
+ FilesystemBackend?: new (options: {
9
+ rootDir: string;
10
+ }) => unknown;
11
+ };
12
+ type DeepAgentsMiddlewareProviderOptions = {
13
+ backend?: unknown;
14
+ sources?: string[];
15
+ importDeepAgents?: () => Promise<DeepAgentsSkillsModule>;
16
+ };
17
+ export type LangGraphRegistrySkillOutput = {
18
+ id: string;
19
+ path: string;
20
+ description?: string;
21
+ allowedTools: string[];
22
+ content?: string;
23
+ };
24
+ export declare function createRegistrySkillResolverProvider(options?: RegistrySkillResolverOptions): LangGraphSkillResolverProvider;
25
+ export declare function createDeepAgentsMiddlewareSkillProvider(options?: DeepAgentsMiddlewareProviderOptions): LangGraphSkillMiddlewareProvider;
26
+ export declare function resolveSkillProvider(options: {
27
+ skillProvider?: LangGraphSkillResolverProvider | false;
28
+ }): LangGraphSkillResolverProvider | undefined;
29
+ export {};
@@ -0,0 +1 @@
1
+ import{readFile as e}from"node:fs/promises";import r from"node:path";export function createRegistrySkillResolverProvider(e={}){return{name:"registry-resolver",resolve:r=>async function resolveRegistrySkill(e,r){const t=e.workspace.skills.get(e.id);if(!t)throw new Error(`LangGraph skill resolver cannot find skill ${e.id}`);return{id:t.id,path:t.path,...t.description?{description:t.description}:{},allowedTools:t.allowedTools,...!1===r.includeContent?{}:{content:await readSkillContent(t,r.maxBytes)}}}(r,e)}}export function createDeepAgentsMiddlewareSkillProvider(e={}){return{name:"deepagents-middleware",async createMiddleware(r){const t=await async function loadDeepAgentsSkillsModule(e){return e.importDeepAgents?e.importDeepAgents():new Function("specifier","return import(specifier)")("deepagents")}(e),o=t.createSkillsMiddleware;if("function"!=typeof o)throw new Error("deepagents does not export createSkillsMiddleware");return o({backend:e.backend??createDeepAgentsFilesystemBackend(t,r.workspace.root),sources:e.sources??deriveDeepAgentsSkillSources(r)})}}}export function resolveSkillProvider(e){if(!1!==e.skillProvider)return e.skillProvider??createRegistrySkillResolverProvider()}async function readSkillContent(r,t=1048576){const o=await e(r.path,"utf8");if(Buffer.byteLength(o,"utf8")>t)throw new Error(`Skill ${r.id} exceeds registry resolver size limit of ${t} bytes`);return o}function createDeepAgentsFilesystemBackend(e,r){if("function"!=typeof e.FilesystemBackend)throw new Error("deepagents does not export FilesystemBackend");return new e.FilesystemBackend({rootDir:r})}function deriveDeepAgentsSkillSources(e){const t=new Set([...(o=e.agent,o?.skills??[]),...skillIdsFromWorkflow(e)]);var o;const i=new Set;for(const o of t){const t=e.workspace.skills.get(o);t&&i.add(toPosixPath(r.relative(e.workspace.root,r.dirname(r.dirname(t.path)))))}return[...i].filter(e=>e&&!e.startsWith(".."))}function skillIdsFromWorkflow(e){return e.workflow.nodes.map(e=>e.use.match(/^skills\.([^./][^.]*)$/u)?.[1]).filter(e=>Boolean(e))}function toPosixPath(e){return e.split(r.sep).join("/")}
@@ -0,0 +1,60 @@
1
+ import type { RuntimeWorkflowAdapterInput, WorkspaceAgent, WorkspaceWorkflowNode } from "@stable-harness/core";
2
+ export type LangGraphWorkflowState = {
3
+ input?: unknown;
4
+ outputs: Record<string, unknown>;
5
+ trace: LangGraphWorkflowTraceEntry[];
6
+ };
7
+ export type LangGraphWorkflowTraceEntry = {
8
+ nodeId: string;
9
+ use: string;
10
+ output: unknown;
11
+ };
12
+ export type LangGraphNodeHandlerInput = RuntimeWorkflowAdapterInput & {
13
+ node: WorkspaceWorkflowNode;
14
+ state: LangGraphWorkflowState;
15
+ };
16
+ export type LangGraphNodeHandler = (input: LangGraphNodeHandlerInput) => Promise<unknown> | unknown;
17
+ export type LangGraphNodeResolverInput = LangGraphNodeHandlerInput & {
18
+ kind: string;
19
+ id: string;
20
+ };
21
+ export type LangGraphNodeResolver = (input: LangGraphNodeResolverInput) => Promise<unknown> | unknown;
22
+ export type LangGraphSkillResolverProvider = {
23
+ name: string;
24
+ resolve(input: LangGraphNodeResolverInput): Promise<unknown> | unknown;
25
+ };
26
+ export type LangGraphSkillMiddlewareProviderInput = RuntimeWorkflowAdapterInput & {
27
+ agent?: WorkspaceAgent;
28
+ };
29
+ export type LangGraphSkillMiddlewareProvider = {
30
+ name: string;
31
+ createMiddleware(input: LangGraphSkillMiddlewareProviderInput): Promise<unknown> | unknown;
32
+ };
33
+ export type LangGraphConditionalRouterInput = RuntimeWorkflowAdapterInput & {
34
+ from: string;
35
+ edges: Array<{
36
+ condition: string;
37
+ to: string;
38
+ }>;
39
+ state: LangGraphWorkflowState;
40
+ };
41
+ export type LangGraphConditionalRouter = (input: LangGraphConditionalRouterInput) => Promise<string> | string;
42
+ export type LangGraphWorkflowAdapterOptions = {
43
+ name?: string;
44
+ nodeHandlers?: Record<string, LangGraphNodeHandler>;
45
+ nodeResolvers?: Record<string, LangGraphNodeResolver>;
46
+ skillProvider?: LangGraphSkillResolverProvider | false;
47
+ defaultNodeHandler?: LangGraphNodeHandler;
48
+ conditionalRouters?: Record<string, LangGraphConditionalRouter>;
49
+ enableSubworkflows?: boolean;
50
+ maxSubworkflowDepth?: number;
51
+ };
52
+ export type LangGraphCompiled = {
53
+ invoke(input: LangGraphWorkflowState): Promise<LangGraphWorkflowState>;
54
+ };
55
+ export type LangGraphBuilder = {
56
+ addNode(name: string, handler: (state: LangGraphWorkflowState) => Promise<Partial<LangGraphWorkflowState>>): LangGraphBuilder;
57
+ addEdge(from: string, to: string): LangGraphBuilder;
58
+ addConditionalEdges(from: string, router: (state: LangGraphWorkflowState) => Promise<string>, pathMap: Record<string, string>): LangGraphBuilder;
59
+ compile(): LangGraphCompiled;
60
+ };
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@stable-harness/adapter-langgraph",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "files": [
6
+ "dist/**/*.js",
7
+ "dist/**/*.d.ts",
8
+ "package.json"
9
+ ],
10
+ "main": "dist/src/index.js",
11
+ "types": "dist/src/index.d.ts",
12
+ "dependencies": {
13
+ "@langchain/langgraph": "^1.3.0",
14
+ "@stable-harness/core": "0.0.1"
15
+ }
16
+ }
@@ -0,0 +1,25 @@
1
+ export type CliArgs = {
2
+ workspaceRoot: string;
3
+ command: "request" | "start" | "stop";
4
+ workflowRenderId?: string;
5
+ workflowInspectId?: string;
6
+ workflowRunId?: string;
7
+ agentRenderId?: string;
8
+ agentId?: string;
9
+ sessionId?: string;
10
+ toolId?: string;
11
+ toolArgs: unknown;
12
+ trace: boolean;
13
+ traceJson: boolean;
14
+ progress: boolean;
15
+ serveOpenAi: boolean;
16
+ host?: string;
17
+ port?: number;
18
+ apiKey?: string;
19
+ timeoutMs: number;
20
+ help: boolean;
21
+ prompt: string;
22
+ shouldRunRequest: boolean;
23
+ };
24
+ export declare function parseArgs(argv: string[]): CliArgs;
25
+ export declare function helpText(): string;
@@ -0,0 +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 o=0;o<e.length;o+=1)o=parseOneArg(e,o,r,t);return{...r,prompt:t.join(" "),shouldRunRequest:Boolean(r.toolId||r.workflowRunId||t.length>0)}}function parseOneArg(e,r,t,o){const n=function readNextArg(e,r){return{index:r+1,value:e[r+1]}}(e,r);if(0===o.length&&function parseTopLevelCommand(e,r,t){return"start"===e[r]?(t.command="start",t.serveOpenAi=!0,!0):"stop"===e[r]?(t.command="stop",!0):"workflow"!==e[r]||"render"!==e[r+1]&&"inspect"!==e[r+1]?"agent"===e[r]&&"render"===e[r+1]&&(Object.assign(t,function parseAgentCommand(e,r){if("render"===e[r+1])return{index:r+2,agentRenderId:e[r+2]};throw new Error("Usage: stable-harness agent render <agent-id>")}(e,r)),!0):(Object.assign(t,function parseWorkflowCommand(e,r){if("render"===e[r+1])return{index:r+2,workflowRenderId:e[r+2],workflowInspectId:void 0};if("inspect"===e[r+1])return{index:r+2,workflowRenderId:void 0,workflowInspectId:e[r+2]};throw new Error("Usage: stable-harness workflow <render|inspect> <workflow-id>")}(e,r)),!0)}(e,r,t))return function stateIndex(e,r){return"workflow"===e[r]||"agent"===e[r]?r+2:r}(e,r);if("-h"===e[r]||"--help"===e[r])t.help=!0;else if("start"===t.command&&function isProtocolName(e){return"openai"===e||"openai-compatible"===e}(e[r]))t.serveOpenAi=!0;else{if("-w"===e[r]||"--workspace"===e[r])return setString(n,t,"workspaceRoot");if("--agent"===e[r])return setString(n,t,"agentId");if("--workflow"===e[r])return setString(n,t,"workflowRunId");if("--session-id"===e[r])return setString(n,t,"sessionId");if("--tool"===e[r])return setString(n,t,"toolId");if("--tool-args-json"===e[r])return t.toolArgs=function parseJsonArg(e){try{return JSON.parse(e)}catch(e){const r=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${r}`)}}(n.value??"{}"),n.index;if("--trace"===e[r])t.trace=!0;else if("--trace-json"===e[r])t.traceJson=!0;else if("--progress"===e[r])t.progress=!0;else if("--serve-openai"===e[r])t.serveOpenAi=!0;else{if("--host"===e[r])return setString(n,t,"host");if("--port"===e[r])return t.port=Number(n.value??t.port),n.index;if("--api-key"===e[r])return setString(n,t,"apiKey");if("--timeout-ms"===e[r])return t.timeoutMs=Number(n.value??t.timeoutMs),n.index;o.push(e[r])}}return r}function setString(e,r,t){return"string"==typeof e.value&&Object.assign(r,{[t]:e.value}),e.index}export function helpText(){return["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness workflow render <workflow-id> -w <workspace>"," stable-harness workflow inspect <workflow-id> -w <workspace>"," stable-harness agent render <agent-id> -w <workspace>"," stable-harness start -w <workspace>"," stable-harness stop -w <workspace>","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Legacy alias for start."," --agent <id> Select an agent for a request."," --workflow <id> Run a configured workflow."," --session-id <id> Attach the request to an existing runtime session."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --progress Legacy alias; CLI events are controlled by runtime.cli.events."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n")}
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as o}from"@stable-harness/adapter-deepagents";import{createStableHarnessRuntime as r}from"@stable-harness/core";import{projectEvent as s,projectRuntimeTrace as a}from"@stable-harness/core";import{createOpenAiCompatibleHttpServer as n}from"@stable-harness/protocols";import{createModuleToolGateway as i}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as p}from"@stable-harness/workspace-yaml";export async function runCli(e=process.argv.slice(2)){const t=function(e){let t,o,r,s="request",a=process.cwd(),n=!1,i=!1,p=!1,l=process.env.STABLE_HARNESS_OPENAI_HOST??"127.0.0.1",m=Number(process.env.STABLE_HARNESS_OPENAI_PORT??8642),u=process.env.STABLE_HARNESS_OPENAI_API_KEY,d=Number(process.env.STABLE_HARNESS_CLI_TIMEOUT_MS??3e5),h=!1;const w=[];for(let g=0;g<e.length;g+=1)0===g&&"start"===e[g]?(s="start",p=!0):"-h"===e[g]||"--help"===e[g]?h=!0:"start"!==s||"openai"!==e[g]&&"openai-compatible"!==e[g]?"-w"===e[g]||"--workspace"===e[g]?a=e[++g]??a:"--agent"===e[g]?t=e[++g]:"--tool"===e[g]?o=e[++g]:"--tool-args-json"===e[g]?r=c(e[++g]??"{}"):"--trace"===e[g]?n=!0:"--trace-json"===e[g]?i=!0:"--serve-openai"===e[g]?p=!0:"--host"===e[g]?l=e[++g]??l:"--port"===e[g]?m=Number(e[++g]??m):"--api-key"===e[g]?u=e[++g]:"--timeout-ms"===e[g]?d=Number(e[++g]??d):w.push(e[g]):p=!0;return{workspaceRoot:a,command:s,agentId:t,toolId:o,toolArgs:r,trace:n,traceJson:i,serveOpenAi:p,host:l,port:m,apiKey:u,timeoutMs:d,help:h,prompt:w.join(" ")||"hello"}}(e);if(t.help)return void process.stdout.write(["Usage:"," stable-harness -w <workspace> [--agent <id>] [prompt]"," stable-harness start -w <workspace> [--host <host>] [--port <port>] [--api-key <key>]","","Options:"," -w, --workspace <path> Workspace root."," --serve-openai Serve the OpenAI-compatible API."," --agent <id> Select an agent for a request."," --tool <id> Invoke an explicit registered tool."," --tool-args-json <json> Tool arguments for --tool."," --trace Print trace lines."," --trace-json Print trace JSON."," --timeout-ms <ms> Request timeout."," -h, --help Show this help.",""].join("\n"));const l=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),m=t.workspaceRoot;try{const e=await p(m),c=await i({tools:e.tools.values()}),u=r({workspace:e,toolGateway:c,adapters:[o()]});if(t.serveOpenAi)return clearTimeout(l),void await async function(e,t){const o=n(e,{bearerToken:t.apiKey});await new Promise(e=>o.listen(t.port,t.host,e));const r=o.address(),s="object"==typeof r&&r?r.port:t.port;process.stdout.write(`stable-harness OpenAI-compatible API listening on http://${t.host}:${s}/v1\n`)}(u,t);t.trace&&u.subscribe(e=>{const t=s(e);var o;t&&process.stdout.write(`trace:${t.agentId}:${t.label}${o=t.detail,o&&"string"==typeof o.toolId?`:${o.toolId}`:""}\n`)});const d=await u.request({input:t.prompt,agentId:t.agentId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0});if(t.trace||t.traceJson){const e=u.getRun(d.requestId),o=e?a(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:o})}\n`)}process.stdout.write(`${d.output}\n`)}finally{clearTimeout(l)}}function c(e){try{return JSON.parse(e)}catch(e){const t=e instanceof Error?e.message:String(e);throw new Error(`Invalid --tool-args-json value: ${t}`)}}(function(){const o=process.argv[1];return Boolean(o)&&e(t(import.meta.url))===e(o)})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
2
+ import{realpathSync as e}from"node:fs";import{fileURLToPath as t}from"node:url";import{createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphWorkflowAdapter as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as s}from"@stable-harness/core";import{projectEvent as a,projectRuntimeTrace as i}from"@stable-harness/core";import{createModuleToolGateway as n}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as u}from"@stable-harness/workspace-yaml";import{helpText as l,parseArgs as d}from"./args.js";import{formatCliRuntimeEvent as p,readCliEventViewConfig as c,shouldEnableCliProgressNarration as w}from"./event-view.js";import{ensureCliMemoryServices as f}from"./memory/lifecycle.js";import{createCliMemoryProviders as m}from"./memory/providers.js";import{formatDetail as g,inspectWorkflow as I,renderAgent as v,renderWorkflow as y,workspaceStatus as k}from"./output.js";import{serveProtocol as h,stopProtocol as b}from"./server.js";export async function runCli(e=process.argv.slice(2)){const t=d(e);if(t.help)return void process.stdout.write(l());const o=setTimeout(()=>{process.stderr.write(`stable-harness request timed out after ${t.timeoutMs}ms\n`),process.exit(124)},t.timeoutMs),q=t.workspaceRoot;try{const e=await u(q);if(t.workflowRenderId)return void process.stdout.write(y(e,t.workflowRenderId));if(t.workflowInspectId)return void process.stdout.write(I(e,t.workflowInspectId));if(t.agentRenderId)return void process.stdout.write(v(e,t.agentRenderId));if("stop"===t.command)return clearTimeout(o),void await b(e,t);const l=await n({tools:e.tools.values(),options:{betterCall:{mode:"repair"}}});await f(e);const d=m(e),R=c(e.runtime);let C;if(C=s({workspace:e,toolGateway:l,memoryProviders:d,adapters:[r()],workflowAdapters:[createCliWorkflowAdapter(l,()=>C)],progressNarration:w(R,e.runtime)?{enabled:!0,style:"cli"}:void 0}),t.serveOpenAi)return clearTimeout(o),void await h(C,t);if(!t.shouldRunRequest)return void process.stdout.write(k(e,q));t.trace&&C.subscribe(e=>{const t=a(e);t&&process.stdout.write(`trace:${t.agentId}:${t.label}${g(t.detail)}\n`)}),C.subscribe(e=>{const t=p(e,R);t&&process.stdout.write(`${t}\n`)});const $=await C.request({input:t.prompt,agentId:t.agentId,sessionId:t.sessionId,toolCall:t.toolId?{toolId:t.toolId,args:t.toolArgs}:void 0,workflow:t.workflowRunId?{workflowId:t.workflowRunId,input:t.prompt}:void 0});if(t.trace||t.traceJson){const e=C.getRun($.requestId),r=e?i(e):[];t.traceJson&&process.stdout.write(`${JSON.stringify({trace:r})}\n`)}process.stdout.write(`${$.output}\n`)}finally{clearTimeout(o)}}function createCliWorkflowAdapter(e,t){return o({nodeResolvers:{tools:async({id:t,node:r,request:o,requestId:s,sessionId:a,state:i,workspace:n})=>{return(await e.invoke({toolId:t,args:(u=r.config,l=o.input,d=i.outputs,!0===u?.inputFromState?{...u,requestInput:l,outputs:d}:u&&"requiredInput"in u?u.requiredInput:u&&("args"in u||"cwd"in u||"timeoutMs"in u)?u:"object"==typeof l&&null!==l?l:{}),context:{workspaceRoot:n.root,requestId:s,sessionId:a,agentId:`workflow:${r.id}`}})).output;var u,l,d},agents:async({id:e,node:r,request:o,sessionId:s,state:a})=>{var i,n,u,l;return(await t().request({input:(i=e,n=o.input,u=a.outputs,l=r.config,[`Workflow node agents.${i}: synthesize the workflow evidence into the requested final output.`,`Original request: ${"string"==typeof n?n:JSON.stringify(n)}`,"Requirements:","- Produce the final answer now; do not ask follow-up questions.","- Match the original request language unless workflow config explicitly says otherwise.","- Use only the workflow outputs as evidence; call out uncertainty directly.",...l?[`Workflow node config: ${JSON.stringify(l)}`]:[],"Prior workflow outputs:",JSON.stringify(u)].join("\n")),agentId:e,sessionId:s,metadata:o.metadata})).output}}})}(function isCliEntrypoint(){const r=process.argv[1];if(!r)return!1;try{return e(t(import.meta.url))===e(r)}catch{return!1}})()&&runCli().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}\n`),process.exitCode=1});
@@ -0,0 +1,9 @@
1
+ import type { RuntimeEvent } from "@stable-harness/core";
2
+ export type CliEventViewConfig = {
3
+ enabled: boolean;
4
+ include: string[];
5
+ exclude: string[];
6
+ };
7
+ export declare function readCliEventViewConfig(runtimePolicy: Record<string, unknown>): CliEventViewConfig;
8
+ export declare function shouldEnableCliProgressNarration(config: CliEventViewConfig, runtimePolicy: Record<string, unknown>): boolean;
9
+ export declare function formatCliRuntimeEvent(event: RuntimeEvent, config: CliEventViewConfig): string | undefined;
@@ -0,0 +1 @@
1
+ export function readCliEventViewConfig(e){const t=readRecord(e.cli),r=readRecord(t?.events);return{enabled:readBoolean(r?.enabled)??!0,include:readStringList(r?.include)??["runtime.progress.narration"],exclude:readStringList(r?.exclude)??[]}}export function shouldEnableCliProgressNarration(e,t){if(!e.enabled)return!1;const r=readRecord(readRecord(t.progress)?.narration);return!1!==readBoolean(r?.enabled)&&e.include.some(e=>matchesEventPattern("runtime.progress.narration",e))}export function formatCliRuntimeEvent(e,t){if(!t.enabled||!function isIncluded(e,t){const r=function eventKeys(e){return"runtime.adapter.event"===e.type&&isRecord(e.event)&&"string"==typeof e.event.phase?[e.type,e.event.phase]:[e.type]}(e),n=t.include.some(e=>r.some(t=>matchesEventPattern(t,e))),i=t.exclude.some(e=>r.some(t=>matchesEventPattern(t,e)));return n&&!i}(e,t))return;const r=function projectCliEventView(e){return"runtime.progress.narration"===e.type?{group:"Progress",title:e.message}:e.type.startsWith("runtime.request.")?{group:"Run",title:e.type}:"runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type?{group:"Tool",title:e.type,detail:e.toolId}:"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?{group:"Workflow",title:e.type,detail:e.workflowId}:e.type.startsWith("runtime.memory.")?{group:"Memory",title:e.type,detail:memoryDetail(e)}:"runtime.skill.candidate.created"===e.type?{group:"Skill",title:e.type,detail:e.name}:"runtime.artifact.created"===e.type?{group:"Artifact",title:e.type}:"runtime.execution.contract.failed"===e.type?{group:"Contract",title:e.type,detail:e.reason}:"runtime.adapter.event"===e.type?function adapterEventView(e){if(!isRecord(e))return{group:"Adapter",title:"runtime.adapter.event"};const t="string"==typeof e.phase?e.phase:"runtime.adapter.event";return t.startsWith("agent.tool.")?{group:"Agent Tool",title:t,detail:readString(e.toolId)}:t.startsWith("agent.output.")?{group:"Agent Output",title:t,detail:readString(e.text)}:t.startsWith("agent.")?{group:"Agent",title:t,detail:readString(e.adapter)}:{group:"Adapter",title:t,detail:readString(e.adapter)}}(e.event):{group:"event",title:e.type}}(e);return`[${r.group}] ${e.agentId} | ${r.title}${r.detail?` | ${r.detail}`:""}`}function matchesEventPattern(e,t){return"*"===t||(t.endsWith(".*")?e===t.slice(0,-2)||e.startsWith(t.slice(0,-1)):e===t)}function memoryDetail(e){return"runtime.memory.recall.completed"===e.type?`${e.recordIds.length} records`:"provider"in e?e.provider:"target"in e?e.target:void 0}function readRecord(e){return isRecord(e)?e:void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringList(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):void 0}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
@@ -1 +1,4 @@
1
1
  export { runCli } from "./cli.js";
2
+ export { parseArgs } from "./args.js";
3
+ export { ensureCliMemoryServices } from "./memory/lifecycle.js";
4
+ export { createCliMemoryProviders } from "./memory/providers.js";
@@ -1 +1 @@
1
- export{runCli}from"./cli.js";
1
+ export{runCli}from"./cli.js";export{parseArgs}from"./args.js";export{ensureCliMemoryServices}from"./memory/lifecycle.js";export{createCliMemoryProviders}from"./memory/providers.js";
@@ -0,0 +1,5 @@
1
+ export type LangGraphEnvConfig = {
2
+ env?: unknown;
3
+ envFile?: unknown;
4
+ };
5
+ export declare function applyLangGraphEnvironment(workspaceRoot: string, config?: LangGraphEnvConfig): Promise<void>;
@@ -0,0 +1 @@
1
+ import{readFile as n}from"node:fs/promises";import t from"node:path";export async function applyLangGraphEnvironment(e,r={}){const o=function resolveEnvFile(n,e){if(!1===e.envFile)return;const r="string"==typeof e.env?e.env:e.envFile,o="string"==typeof r&&r.trim()?r.trim():".env";return t.isAbsolute(o)?o:t.join(n,o)}(e,r);!function applyEnv(n){for(const[t,e]of Object.entries(n))process.env[t]||(process.env[t]=e)}({...o?await async function readEnvFile(t){try{return function parseEnv(n){const t={};for(const e of n.split(/\r?\n/u)){const n=e.trim();if(!n||n.startsWith("#"))continue;const r=n.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/u);r&&(t[r[1]]=unquoteEnvValue(r[2]??""))}return t}(await n(t,"utf8"))}catch(n){if(function isMissingFileError(n){return"object"==typeof n&&null!==n&&"code"in n&&"ENOENT"===n.code}(n))return{};throw n}}(o):{},...function readInlineEnv(n){return"object"!=typeof n||null===n||Array.isArray(n)?{}:Object.fromEntries(Object.entries(n).flatMap(([n,t])=>{if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(n))return[];const e=function resolveEnvValue(n){if("string"==typeof n){const t=n.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]??"":n}if("number"==typeof n||"boolean"==typeof n)return String(n)}(t);return void 0===e?[]:[[n,e]]}))}(r.env)})}function unquoteEnvValue(n){const t=n.trim();if(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))return t.slice(1,-1);const e=t.match(/^(.*?)\s+#/u);return e?e[1].trim():t}
@@ -0,0 +1,13 @@
1
+ import type { createStableHarnessRuntime } from "@stable-harness/core";
2
+ export type LangGraphOfficialConfig = {
3
+ host: string;
4
+ port: number;
5
+ nWorkers: number;
6
+ exposeAgents?: string[];
7
+ env?: unknown;
8
+ envFile?: unknown;
9
+ };
10
+ export declare function startOfficialLangGraphServer(runtime: ReturnType<typeof createStableHarnessRuntime>, config: LangGraphOfficialConfig): Promise<{
11
+ url: string;
12
+ cleanup: () => Promise<void>;
13
+ }>;
@@ -0,0 +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}`}
@@ -0,0 +1,2 @@
1
+ import type { CompiledWorkspace } from "@stable-harness/core";
2
+ export declare function ensureCliMemoryServices(workspace: CompiledWorkspace): Promise<void>;
@@ -0,0 +1 @@
1
+ import{spawn as e}from"node:child_process";import{existsSync as r}from"node:fs";import t from"node:path";import{fileURLToPath as n}from"node:url";export async function ensureCliMemoryServices(r){const t=function readLangMemServiceConfig(e){const r=readRecord(e.runtime.memory?.LangMem)??readRecord(e.runtime.memory?.langmem);if(!function shouldAutoStart(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(e,r))return;const t=readString(r?.baseUrl)??readString(r?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;var n;return t?{baseUrl:t,autoStart:!1!==r?.autoStart,command:readString(r?.command)??"python3",args:(n=r?.args,(Array.isArray(n)?n.filter(e=>"string"==typeof e&&e.trim().length>0):void 0)??["-m","stable_harness_langmem_service"]),env:{...defaultLangMemEnv(e,t),...readStringRecord(r?.env)}}:void 0}(r);if(!t||!t.autoStart||!function isLocalBaseUrl(e){const r=new URL(e);return["localhost","127.0.0.1","::1"].includes(r.hostname)}(t.baseUrl)||await isHealthy(t.baseUrl))return;const n=e(t.command,t.args,{cwd:r.root,detached:!0,env:{...process.env,...t.env},stdio:"ignore"});n.unref(),await async function waitForStartedService(e,r){await Promise.race([waitForHealth(r.baseUrl),new Promise((r,t)=>e.once("error",t)),new Promise((t,n)=>{e.once("exit",(e,t)=>{n(new Error(`LangMem service exited before becoming healthy: ${r.command} code=${e??"null"} signal=${t??"null"}`))})})])}(n,t)}function defaultLangMemEnv(e,o){const a=new URL(o),i=readString(e.runtime.dataRoot)??".stable-harness",s=t.resolve(function packageRoot(){return t.resolve(t.dirname(n(import.meta.url)),"../../../../..")}(),"services/langmem-python/src");return{HOST:"localhost"===a.hostname?"127.0.0.1":a.hostname,PORT:a.port||("https:"===a.protocol?"443":"80"),LANGMEM_SQLITE_PATH:t.join(t.resolve(e.root,i),"memory/langmem/langmem.sqlite"),...r(s)?{PYTHONPATH:mergePythonPath(s)}:{}}}async function waitForHealth(e){for(let r=0;r<60;r+=1){if(await isHealthy(e))return;await new Promise(e=>setTimeout(e,500))}throw new Error(`LangMem service did not become healthy at ${e}`)}async function isHealthy(e){try{const r=await fetch(`${e.replace(/\/+$/u,"")}/health`,{signal:AbortSignal.timeout(1e3)}),t=await r.json();return r.ok&&!0===t.ok}catch{return!1}}function mergePythonPath(e){return process.env.PYTHONPATH?`${e}${t.delimiter}${process.env.PYTHONPATH}`:e}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readStringRecord(e){const r=readRecord(e);return Object.fromEntries(Object.entries(r??{}).filter(e=>"string"==typeof e[1]))}
@@ -0,0 +1,3 @@
1
+ import type { CompiledWorkspace } from "@stable-harness/core";
2
+ import { type MemoryProvider } from "@stable-harness/memory";
3
+ export declare function createCliMemoryProviders(workspace: CompiledWorkspace): MemoryProvider[];
@@ -0,0 +1 @@
1
+ import{createLangMemServiceProvider as e}from"@stable-harness/memory";export function createCliMemoryProviders(r){const o=readRecord(r.runtime.memory?.LangMem)??readRecord(r.runtime.memory?.langmem);if(!function shouldEnableLangMemProvider(e,r){return!1!==(e.runtime.memory??{}).enabled&&!1!==r?.enabled&&(!1!==r?.read||!1!==r?.write)&&[...e.memories.values()].some(e=>e.enabled&&"langmem"===e.provider)}(r,o))return[];const d=readString(o?.baseUrl)??readString(o?.url)??process.env.STABLE_HARNESS_LANGMEM_URL;return d?[e({baseUrl:d,timeoutMs:(n=o?.timeoutMs,("number"==typeof n&&Number.isFinite(n)?n:void 0)??readEnvNumber("STABLE_HARNESS_LANGMEM_TIMEOUT_MS")),config:readProviderConfig(o)})]:[];var n}function readProviderConfig(e){return{provider:"langmem-service",...readRecord(e?.mode)?{mode:readRecord(e?.mode)}:{},...readRecord(e?.types)?{types:readRecord(e?.types)}:{},...readRecord(e?.approval)?{approval:readRecord(e?.approval)}:{},...readRecord(e?.defaults)?{defaults:readRecord(e?.defaults)}:{}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function readEnvNumber(e){const r=process.env[e];return r?Number(r):void 0}
@@ -0,0 +1,8 @@
1
+ import type { loadWorkspaceFromYaml } from "@stable-harness/workspace-yaml";
2
+ type LoadedWorkspace = Awaited<ReturnType<typeof loadWorkspaceFromYaml>>;
3
+ export declare function workspaceStatus(workspace: LoadedWorkspace, workspaceRoot: string): string;
4
+ export declare function renderWorkflow(workspace: LoadedWorkspace, workflowId: string | undefined): string;
5
+ export declare function renderAgent(workspace: LoadedWorkspace, agentId: string | undefined): string;
6
+ export declare function inspectWorkflow(workspace: LoadedWorkspace, workflowId: string | undefined): string;
7
+ export declare function formatDetail(detail: Record<string, unknown> | undefined): string;
8
+ export {};
@@ -0,0 +1 @@
1
+ import{compileWorkflowPlan as o,renderAgentMermaid as r,renderWorkflowMermaid as e}from"@stable-harness/core";export function workspaceStatus(o,r){return[`stable-harness workspace: ${o.runtime.workspaceId??r}`,`root: ${o.root}`,`default agent: ${o.runtime.defaultAgentId}`,`agents: ${[...o.agents.keys()].sort().join(", ")||"none"}`,`workflows: ${[...o.workflows.keys()].sort().join(", ")||"none"}`,`evaluations: ${[...o.evaluations?.keys()??[]].sort().join(", ")||"none"}`,`default workflow: ${o.runtime.workflowRouting?.defaultWorkflowId??"none"}`,`workflow routes: ${formatWorkflowRoutes(o)}`,`tools: ${o.tools.size}`,"","Run a request with:"," stable-harness [prompt]"," stable-harness --agent <id> [prompt]"," stable-harness workflow render <workflow-id>"," stable-harness workflow inspect <workflow-id>"," stable-harness agent render <agent-id>"," stable-harness start",""].join("\n")}export function renderWorkflow(o,r){const n=readWorkflow(o,r,"render");return`${e(n)}\n`}export function renderAgent(o,e){if(!e)throw new Error("Usage: stable-harness agent render <agent-id>");return`${r(o,e)}\n`}export function inspectWorkflow(r,e){const n=readWorkflow(r,e,"inspect");return`${JSON.stringify({workflow:n,plan:o(n)},null,2)}\n`}export function formatDetail(o){return o&&"string"==typeof o.toolId?`:${o.toolId}`:""}function readWorkflow(o,r,e){if(!r)throw new Error(`Usage: stable-harness workflow ${e} <workflow-id>`);const n=o.workflows.get(r);if(!n)throw new Error(`Workflow is not defined: ${r}`);return n}function formatWorkflowRoutes(o){return(o.runtime.workflowRouting?.routes??[]).map(o=>o.id).sort().join(", ")||"none"}
@@ -0,0 +1,5 @@
1
+ import type { createStableHarnessRuntime } from "@stable-harness/core";
2
+ import type { CompiledWorkspace } from "@stable-harness/core";
3
+ import type { CliArgs } from "./args.js";
4
+ export declare function serveProtocol(runtime: ReturnType<typeof createStableHarnessRuntime>, args: CliArgs): Promise<void>;
5
+ export declare function stopProtocol(workspace: CompiledWorkspace, args: CliArgs): Promise<void>;
@@ -0,0 +1 @@
1
+ import{execFile as r}from"node:child_process";import{promisify as t}from"node:util";import{createOpenAiCompatibleHttpServer as o}from"@stable-harness/protocols";import{startOfficialLangGraphServer as e}from"./langgraph-official.js";const n="127.0.0.1",s=t(r);export async function serveProtocol(r,t){const o=createConfiguredServers(r,t),e=[];let n=0;for(const t of o)if("http"===t.kind){if(!await listen(t)){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.host}:${t.port}/v1\n`);continue}e.push(()=>closeHttpServer(t.server)),n+=1;const r=t.server.address(),o="object"==typeof r&&r?r.port:t.port;process.stdout.write(`stable-harness ${t.protocol} API listening on http://${t.host}:${o}/v1\n`)}else{const o=await startLangGraphServer(r,t);if(!o){process.stdout.write(`stable-harness ${t.protocol} API already running on http://${t.config.host}:${t.config.port}\n`);continue}e.push(o.cleanup),n+=1,process.stdout.write(`stable-harness ${t.protocol} API listening on ${o.url}\n`)}0!==n&&await async function waitForShutdown(r){const t=setInterval(()=>{},864e5);await new Promise(o=>{const shutdown=()=>{clearInterval(t),Promise.allSettled(r.map(r=>r())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(e)}export async function stopProtocol(r,t){const o=createConfiguredServers({getRuntimePolicy:()=>r.runtime},t).map(r=>"http"===r.kind?{protocol:r.protocol,host:r.host,port:r.port}:{protocol:r.protocol,host:r.config.host,port:r.config.port}),e=await Promise.all(o.map(async r=>({target:r,pids:await stableHarnessListenerPids(r.port)}))),n=[...new Set(e.flatMap(r=>r.pids))];for(const r of n)process.kill(r,"SIGTERM");for(const{target:r,pids:t}of e)0!==t.length?process.stdout.write(`stable-harness ${r.protocol} API stopped on ${r.host}:${r.port} pid=${t.join(",")}\n`):process.stdout.write(`stable-harness ${r.protocol} API not running on ${r.host}:${r.port}\n`)}function createConfiguredServers(r,t){const o=readRecord(r.getRuntimePolicy().protocols)??{},e=protocolConfig(o,"openaiCompatible","openai-compatible","openai")??{},n=protocolConfig(o,"langgraph")??{};return[...enabled(e)?[openAiServer(r,e,t)]:[],...enabled(n)?[langGraphServer(n)]:[]]}function openAiServer(r,t,e){const s=configString(t.host)??n,i=e.port??configNumber(t.port)??8642,a=e.host??s,c=configString(t.bearerToken)??configString(t.apiKey)??e.apiKey;return{kind:"http",protocol:"openai-compatible",server:o(r,{bearerToken:c}),host:a,port:i,...c?{bearerToken:c}:{}}}function langGraphServer(r){const t=configString(r.host)??n,o=configNumber(r.port)??2024,e=function configStringArray(r){if(Array.isArray(r)&&r.every(r=>"string"==typeof r))return r.filter(r=>r.trim()).map(r=>r.trim())}(r.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:t,port:o,nWorkers:configNumber(r.nWorkers)??10,...e?{exposeAgents:e}:{},...void 0!==r.env?{env:r.env}:{},...void 0!==r.envFile?{envFile:r.envFile}:{}}}}function protocolConfig(r,...t){for(const o of t){const t=readRecord(r[o]);if(t)return t}}function enabled(r){return!1!==r.enabled}function configString(r){if("string"!=typeof r||!r.trim())return;const t=r.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]:r}function configNumber(r){return"number"==typeof r&&Number.isFinite(r)?r:"string"==typeof r&&r.trim()?Number(r):void 0}function readRecord(r){return"object"!=typeof r||null===r||Array.isArray(r)?void 0:r}async function listen(r){try{return await new Promise((t,o)=>{r.server.once("error",o),r.server.listen(r.port,r.host,()=>{r.server.off("error",o),t()})}),!0}catch(t){if(isAddressInUse(t)&&await async function isOpenAiServerAlreadyRunning(r){const t=await fetchJson(`http://${r.host}:${r.port}/v1/capabilities`,{...r.bearerToken?{authorization:`Bearer ${r.bearerToken}`}:{}});return"stable_harness.capabilities"===t?.object}(r))return!1;throw portConflictError(t,r.protocol,r.host,r.port)}}async function startLangGraphServer(r,t){if(!await isLangGraphServerAlreadyRunning(t))try{return await e(r,t.config)}catch(r){if(isAddressInUse(r)&&await isLangGraphServerAlreadyRunning(t))return;throw portConflictError(r,t.protocol,t.config.host,t.config.port)}}async function isLangGraphServerAlreadyRunning(r){const t=await fetchJson(`http://${r.config.host}:${r.config.port}/ok`);return!0===t?.ok}async function fetchJson(r,t={}){try{const o=await fetch(r,{headers:t});if(!o.ok)return;return await o.json()}catch{return}}function isAddressInUse(r){return"EADDRINUSE"===function readErrorCode(r){return"object"==typeof r&&null!==r&&"code"in r?r.code:void 0}(r)||String(r).includes("EADDRINUSE")}function portConflictError(r,t,o,e){return isAddressInUse(r)?new Error([`stable-harness ${t} port is already in use: ${o}:${e}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${o}:${e}.`].join("\n")):r}async function stableHarnessListenerPids(r){const t=await async function listenerPids(r){try{const{stdout:t}=await s("lsof",[`-tiTCP:${r}`,"-sTCP:LISTEN"]);return t.split(/\s+/u).map(r=>Number(r)).filter(r=>Number.isInteger(r)&&r>0)}catch{return[]}}(r);return(await Promise.all(t.map(async r=>{const t=await async function processCommand(r){try{const{stdout:t}=await s("ps",["-p",String(r),"-o","command="]);return t.trim()}catch{return""}}(r);return function isStableHarnessStartCommand(r){return/\bstable-harness\b/u.test(r)&&/\bstart\b/u.test(r)}(t)?r:void 0}))).filter(r=>"number"==typeof r)}async function closeHttpServer(r){await new Promise((t,o)=>{r.close(r=>{r?o(r):t()})})}
@@ -13,8 +13,11 @@
13
13
  "main": "dist/src/index.js",
14
14
  "types": "dist/src/index.d.ts",
15
15
  "dependencies": {
16
+ "@langchain/langgraph-api": "^1.2.1",
16
17
  "@stable-harness/adapter-deepagents": "0.0.1",
18
+ "@stable-harness/adapter-langgraph": "0.0.1",
17
19
  "@stable-harness/core": "0.0.1",
20
+ "@stable-harness/memory": "0.0.1",
18
21
  "@stable-harness/protocols": "0.0.1",
19
22
  "@stable-harness/tool-gateway": "0.0.1",
20
23
  "@stable-harness/workspace-yaml": "0.0.1"
@@ -0,0 +1,18 @@
1
+ export type WorkspaceEvaluation = {
2
+ id: string;
3
+ description?: string;
4
+ sourcePath?: string;
5
+ suite?: string;
6
+ cases: WorkspaceEvaluationCase[];
7
+ config?: Record<string, unknown>;
8
+ };
9
+ export type WorkspaceEvaluationCase = {
10
+ id: string;
11
+ description?: string;
12
+ agentId?: string;
13
+ workflowId?: string;
14
+ input?: unknown;
15
+ tools?: string[];
16
+ assertions?: Record<string, unknown>;
17
+ metadata?: Record<string, unknown>;
18
+ };
@@ -0,0 +1 @@
1
+ export{};
@@ -5,4 +5,5 @@ export declare function assertExecutionContract(input: {
5
5
  requestId: string;
6
6
  sessionId: string;
7
7
  agent: WorkspaceAgent;
8
+ metadata?: Record<string, unknown>;
8
9
  }): void;
@@ -1 +1 @@
1
- export function assertExecutionContract(n){!function(n){if(!e(n.agent).requiresPlan)return;const o=n.store.getRun(n.requestId);if(!(o?.events??[]).flatMap(t).includes("write_todos"))throw n.emit({type:"execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_plan"}),new Error("Execution contract requires a planning checkpoint: write_todos")}(n),function(n){const o=function(t){const n=e(t).requiredEvidenceTools;return Array.isArray(n)?n.filter(e=>"string"==typeof e):[]}(n.agent);if(0===o.length)return;const r=n.store.getRun(n.requestId),i=new Set((r?.events??[]).flatMap(t)),s=o.filter(e=>!i.has(e));if(0!==s.length)throw n.emit({type:"execution.contract.failed",requestId:n.requestId,sessionId:n.sessionId,agentId:n.agent.id,reason:"missing_required_evidence",missingEvidenceTools:s}),new Error(`Execution contract missing required evidence tools: ${s.join(", ")}`)}(n)}function e(e){return n(e.config.executionContract)?e.config.executionContract:{}}function t(e){return"tool.completed"===e.type?[e.toolId]:"adapter.event"===e.type&&n(e.event)&&"tool.result"===e.event.phase&&"string"==typeof e.event.toolId?[e.event.toolId]:[]}function n(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ export function assertExecutionContract(e){(function contractDisabled(e){const t=isRecord(e?.executionContract)?e.executionContract:void 0;return!1===t?.enabled})(e.metadata)||(function assertRequiredPlan(e){const t=readExecutionContract(e.agent);if(!t.requiresPlan)return;const n=readStringArray(t.planEvidenceTools);if(0===n.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"invalid_plan_contract"}),new Error("Execution contract requires plan evidence tools when requiresPlan is enabled");const r=e.store.getRun(e.requestId),o=new Set((r?.events??[]).flatMap(readEvidenceToolId));if(!n.some(e=>o.has(e)))throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_plan",missingEvidenceTools:n}),new Error(`Execution contract requires a planning checkpoint from one of: ${n.join(", ")}`)}(e),function assertRequiredEvidenceTools(e){const t=function readRequiredEvidenceTools(e){return readStringArray(readExecutionContract(e).requiredEvidenceTools)}(e.agent);if(0===t.length)return;const n=e.store.getRun(e.requestId),r=new Set((n?.events??[]).flatMap(readEvidenceToolId)),o=t.filter(e=>!r.has(e));if(0!==o.length)throw e.emit({type:"runtime.execution.contract.failed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,reason:"missing_required_evidence",missingEvidenceTools:o}),new Error(`Execution contract missing required evidence tools: ${o.join(", ")}`)}(e))}function readExecutionContract(e){return isRecord(e.config.executionContract)?e.config.executionContract:{}}function readEvidenceToolId(e){return"runtime.tool.direct.completed"===e.type?[e.toolId]:"runtime.adapter.event"===e.type&&isRecord(e.event)&&"agent.tool.result"===e.event.phase&&"string"==typeof e.event.toolId?[e.event.toolId]:[]}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.length>0):[]}
@@ -1,9 +1,13 @@
1
- export * from "./artifacts.js";
1
+ export * from "./runtime/persistence/artifacts.js";
2
2
  export * from "./execution-contract.js";
3
- export * from "./inspection.js";
3
+ export * from "./recovery/tool-call.js";
4
+ export * from "./runtime/persistence/inspection.js";
4
5
  export * from "./memory-plugins.js";
5
- export * from "./queue.js";
6
+ export * from "./runtime/persistence/queue.js";
6
7
  export * from "./runtime.js";
7
- export * from "./stores.js";
8
+ export * from "./runtime/persistence/stores.js";
8
9
  export * from "./trace.js";
9
10
  export * from "./types.js";
11
+ export * from "./evaluations/index.js";
12
+ export * from "./spec-driven/index.js";
13
+ export * from "./workflows/index.js";
@@ -1 +1 @@
1
- export*from"./artifacts.js";export*from"./execution-contract.js";export*from"./inspection.js";export*from"./memory-plugins.js";export*from"./queue.js";export*from"./runtime.js";export*from"./stores.js";export*from"./trace.js";export*from"./types.js";
1
+ export*from"./runtime/persistence/artifacts.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./memory-plugins.js";export*from"./runtime/persistence/queue.js";export*from"./runtime.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./evaluations/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";