stable-harness 0.0.2 → 0.0.3

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 (102) hide show
  1. package/dist/index.d.ts +28 -6
  2. package/dist/index.js +1 -1
  3. package/package.json +11 -8
  4. package/packages/adapter-deepagents/dist/src/adapter.d.ts +1 -0
  5. package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
  6. package/packages/adapter-deepagents/dist/src/index.d.ts +1 -0
  7. package/packages/adapter-deepagents/dist/src/index.js +1 -1
  8. package/packages/adapter-deepagents/dist/src/{builtin-tool-policy.d.ts → internal/builtin-tool-policy.d.ts} +16 -0
  9. package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -0
  10. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +4 -0
  11. package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -0
  12. package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +16 -0
  13. package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -0
  14. package/packages/adapter-deepagents/dist/src/memory.d.ts +5 -0
  15. package/packages/adapter-deepagents/dist/src/memory.js +1 -0
  16. package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
  17. package/packages/adapter-langgraph/dist/src/graph.d.ts +3 -0
  18. package/packages/adapter-langgraph/dist/src/graph.js +1 -0
  19. package/packages/adapter-langgraph/dist/src/index.d.ts +8 -0
  20. package/packages/adapter-langgraph/dist/src/index.js +1 -0
  21. package/packages/adapter-langgraph/dist/src/runtime.d.ts +3 -0
  22. package/packages/adapter-langgraph/dist/src/runtime.js +1 -0
  23. package/packages/adapter-langgraph/dist/src/skill-providers.d.ts +29 -0
  24. package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -0
  25. package/packages/adapter-langgraph/dist/src/types.d.ts +60 -0
  26. package/packages/adapter-langgraph/dist/src/types.js +1 -0
  27. package/packages/adapter-langgraph/package.json +16 -0
  28. package/packages/cli/dist/src/args.d.ts +22 -0
  29. package/packages/cli/dist/src/args.js +1 -0
  30. package/packages/cli/dist/src/cli.js +1 -1
  31. package/packages/cli/dist/src/langgraph-official.d.ts +11 -0
  32. package/packages/cli/dist/src/langgraph-official.js +1 -0
  33. package/packages/cli/dist/src/output.d.ts +8 -0
  34. package/packages/cli/dist/src/output.js +1 -0
  35. package/packages/cli/dist/src/server.d.ts +3 -0
  36. package/packages/cli/dist/src/server.js +1 -0
  37. package/packages/cli/package.json +1 -0
  38. package/packages/core/dist/execution-contract.d.ts +1 -0
  39. package/packages/core/dist/execution-contract.js +1 -1
  40. package/packages/core/dist/index.d.ts +5 -4
  41. package/packages/core/dist/index.js +1 -1
  42. package/packages/core/dist/memory-plugins/maintenance.d.ts +42 -0
  43. package/packages/core/dist/memory-plugins/maintenance.js +1 -0
  44. package/packages/core/dist/memory-plugins/shared.d.ts +8 -0
  45. package/packages/core/dist/memory-plugins/shared.js +1 -0
  46. package/packages/core/dist/memory-plugins.d.ts +5 -48
  47. package/packages/core/dist/memory-plugins.js +1 -1
  48. package/packages/core/dist/recovery/tool-call.d.ts +13 -0
  49. package/packages/core/dist/recovery/tool-call.js +1 -0
  50. package/packages/core/dist/runtime/completion.d.ts +17 -0
  51. package/packages/core/dist/runtime/completion.js +1 -0
  52. package/packages/core/dist/runtime/direct-tool-call.d.ts +10 -0
  53. package/packages/core/dist/runtime/direct-tool-call.js +1 -0
  54. package/packages/core/dist/runtime/events.d.ts +147 -0
  55. package/packages/core/dist/runtime/events.js +1 -0
  56. package/packages/core/dist/runtime/memory.d.ts +23 -0
  57. package/packages/core/dist/runtime/memory.js +1 -0
  58. package/packages/core/dist/{artifacts.d.ts → runtime/persistence/artifacts.d.ts} +1 -1
  59. package/packages/core/dist/{inspection.d.ts → runtime/persistence/inspection.d.ts} +1 -1
  60. package/packages/core/dist/{queue.d.ts → runtime/persistence/queue.d.ts} +1 -1
  61. package/packages/core/dist/{stores.d.ts → runtime/persistence/stores.d.ts} +1 -1
  62. package/packages/core/dist/runtime/tool-gateway.d.ts +35 -0
  63. package/packages/core/dist/runtime/tool-gateway.js +1 -0
  64. package/packages/core/dist/runtime/types.d.ts +168 -0
  65. package/packages/core/dist/runtime/types.js +1 -0
  66. package/packages/core/dist/runtime.d.ts +6 -3
  67. package/packages/core/dist/runtime.js +1 -1
  68. package/packages/core/dist/trace.js +1 -1
  69. package/packages/core/dist/types.d.ts +17 -426
  70. package/packages/core/dist/workflows/index.d.ts +70 -0
  71. package/packages/core/dist/workflows/index.js +1 -0
  72. package/packages/core/dist/workflows/runtime.d.ts +12 -0
  73. package/packages/core/dist/workflows/runtime.js +1 -0
  74. package/packages/core/dist/workspace/types.d.ts +92 -0
  75. package/packages/core/dist/workspace/types.js +1 -0
  76. package/packages/governance/dist/src/types.d.ts +1 -1
  77. package/packages/protocols/dist/src/http-server.js +1 -1
  78. package/packages/protocols/dist/src/in-process-client.js +1 -1
  79. package/packages/protocols/dist/src/openai-compatible.js +1 -1
  80. package/packages/protocols/dist/src/openai-payload.d.ts +74 -0
  81. package/packages/protocols/dist/src/openai-payload.js +1 -0
  82. package/packages/protocols/dist/src/openai-stream.d.ts +39 -0
  83. package/packages/protocols/dist/src/openai-stream.js +1 -0
  84. package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
  85. package/packages/tool-gateway/dist/src/schema-validation.d.ts +3 -0
  86. package/packages/tool-gateway/dist/src/schema-validation.js +1 -0
  87. package/packages/workspace-yaml/dist/discovery.d.ts +4 -0
  88. package/packages/workspace-yaml/dist/discovery.js +1 -0
  89. package/packages/workspace-yaml/dist/documents.d.ts +16 -0
  90. package/packages/workspace-yaml/dist/documents.js +1 -0
  91. package/packages/workspace-yaml/dist/loader.js +1 -1
  92. package/packages/workspace-yaml/dist/workflows.d.ts +16 -0
  93. package/packages/workspace-yaml/dist/workflows.js +1 -0
  94. package/packages/adapter-deepagents/dist/src/builtin-tool-policy.js +0 -1
  95. /package/packages/adapter-deepagents/dist/src/{messages.d.ts → internal/messages.d.ts} +0 -0
  96. /package/packages/adapter-deepagents/dist/src/{messages.js → internal/messages.js} +0 -0
  97. /package/packages/adapter-deepagents/dist/src/{stream-events.d.ts → internal/stream-events.d.ts} +0 -0
  98. /package/packages/adapter-deepagents/dist/src/{stream-events.js → internal/stream-events.js} +0 -0
  99. /package/packages/core/dist/{artifacts.js → runtime/persistence/artifacts.js} +0 -0
  100. /package/packages/core/dist/{inspection.js → runtime/persistence/inspection.js} +0 -0
  101. /package/packages/core/dist/{queue.js → runtime/persistence/queue.js} +0 -0
  102. /package/packages/core/dist/{stores.js → runtime/persistence/stores.js} +0 -0
@@ -0,0 +1,92 @@
1
+ import type { WorkspaceWorkflow, WorkspaceWorkflowEdge, WorkspaceWorkflowRoutingPolicy } from "../workflows/index.js";
2
+ export type WorkspaceModel = {
3
+ id: string;
4
+ provider: string;
5
+ model: string;
6
+ config?: Record<string, unknown>;
7
+ };
8
+ export type WorkspaceTool = {
9
+ id: string;
10
+ description?: string;
11
+ schema?: unknown;
12
+ implementation?: string;
13
+ sourcePath?: string;
14
+ };
15
+ export type WorkspaceSkill = {
16
+ id: string;
17
+ path: string;
18
+ description?: string;
19
+ allowedTools: string[];
20
+ };
21
+ export type WorkspaceMemory = {
22
+ id: string;
23
+ provider: string;
24
+ profile?: string;
25
+ mode?: string;
26
+ enabled: boolean;
27
+ prompts?: {
28
+ semantic?: string;
29
+ episodic?: string;
30
+ procedural?: string;
31
+ };
32
+ config?: Record<string, unknown>;
33
+ };
34
+ export type WorkspaceAgent = {
35
+ id: string;
36
+ description?: string;
37
+ sourcePath?: string;
38
+ backend: string;
39
+ modelRef?: string;
40
+ systemPrompt?: string;
41
+ tools: string[];
42
+ skills?: string[];
43
+ memory?: unknown[];
44
+ subagents: string[];
45
+ edges?: WorkspaceWorkflowEdge[];
46
+ config: Record<string, unknown>;
47
+ };
48
+ export type WorkspaceRuntimePolicy = {
49
+ defaultAgentId: string;
50
+ workspaceId?: string;
51
+ profile?: string;
52
+ adapters?: WorkspaceAdapterPolicy[];
53
+ workflowRouting?: WorkspaceWorkflowRoutingPolicy;
54
+ approvals?: Record<string, unknown>;
55
+ recovery?: Record<string, unknown>;
56
+ retry?: WorkspaceRetryPolicy;
57
+ memory?: Record<string, unknown>;
58
+ protocols?: Record<string, unknown>;
59
+ };
60
+ export type WorkspaceAdapterPolicy = {
61
+ name: string;
62
+ enabled?: boolean;
63
+ config?: Record<string, unknown>;
64
+ };
65
+ export type WorkspaceRetryPolicy = {
66
+ model?: WorkspaceRetryTargetPolicy;
67
+ tools?: WorkspaceToolRetryPolicy;
68
+ };
69
+ export type WorkspaceRetryTargetPolicy = {
70
+ enabled?: boolean;
71
+ maxRetries?: number;
72
+ retryOn?: WorkspaceRetryReason[];
73
+ backoffFactor?: number;
74
+ initialDelayMs?: number;
75
+ maxDelayMs?: number;
76
+ jitter?: boolean;
77
+ onFailure?: "continue" | "error";
78
+ };
79
+ export type WorkspaceToolRetryPolicy = WorkspaceRetryTargetPolicy & {
80
+ tools?: string[];
81
+ };
82
+ export type WorkspaceRetryReason = "timeout" | "network" | "rateLimit" | "serverError";
83
+ export type CompiledWorkspace = {
84
+ root: string;
85
+ runtime: WorkspaceRuntimePolicy;
86
+ agents: Map<string, WorkspaceAgent>;
87
+ models: Map<string, WorkspaceModel>;
88
+ tools: Map<string, WorkspaceTool>;
89
+ skills: Map<string, WorkspaceSkill>;
90
+ memories: Map<string, WorkspaceMemory>;
91
+ workflows: Map<string, WorkspaceWorkflow>;
92
+ };
@@ -0,0 +1 @@
1
+ export{};
@@ -66,7 +66,7 @@ export type SkillCandidate = {
66
66
  export type SkillCandidateEvidence = {
67
67
  id: string;
68
68
  candidateId: string;
69
- evidenceType: "memory_record" | "run" | "tool_event" | "validation" | "user_correction" | "deepagents_memory_file";
69
+ evidenceType: "memory_record" | "memory_file" | "run" | "tool_event" | "validation" | "user_correction";
70
70
  evidenceRef: string;
71
71
  summary?: string;
72
72
  weight?: number;
@@ -1 +1 @@
1
- import{createServer as t}from"node:http";import{projectRuntimeTrace as e}from"@stable-harness/core";export function createHttpServer(o){return t(async(t,n)=>{try{if("GET"===t.method&&"/health"===t.url)return void r(n,200,{ok:!0});if("GET"===t.method&&"/inspect"===t.url)return void r(n,200,o.inspect());if("GET"===t.method&&"/requests"===t.url)return void r(n,200,o.listRequests());if("GET"===t.method&&"/sessions"===t.url)return void r(n,200,o.listSessions());const s=function(t){const e=(t??"").match(/^\/requests\/([^/]+)$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&s){const t=o.inspectRequest(s);return void r(n,t?200:404,t??{error:"request_not_found"})}const i=function(t){const e=(t??"").match(/^\/runs\/([^/]+)\/trace$/u);return e?.[1]?decodeURIComponent(e[1]):void 0}(t.url);if("GET"===t.method&&i){const t=o.getRun(i);return void r(n,t?200:404,t?e(t):{error:"run_not_found"})}if("POST"===t.method&&"/requests"===t.url){const e=await async function(t){const e=[];for await(const r of t)e.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===e.length?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}(t),s="string"==typeof e.input?e.input:"",i="string"==typeof e.agentId?e.agentId:void 0,u=function(t){if("object"!=typeof t||null===t)return;const e=t;return"string"==typeof e.toolId?{toolId:e.toolId,args:e.args}:void 0}(e.toolCall);return void r(n,200,await o.request({input:s,agentId:i,toolCall:u}))}r(n,404,{error:"not_found"})}catch(t){r(n,500,{error:t instanceof Error?t.message:String(t)})}})}function r(t,e,r){t.writeHead(e,{"content-type":"application/json"}),t.end(JSON.stringify(r))}
1
+ import{createServer as t}from"node:http";import{compileWorkflowPlan as o,projectRuntimeTrace as e,renderWorkflowMermaid as r}from"@stable-harness/core";export function createHttpServer(i){return t(async(t,u)=>{try{if("GET"===t.method&&"/health"===t.url)return void s(u,200,{ok:!0});if("GET"===t.method&&"/inspect"===t.url)return void s(u,200,i.inspect());if("GET"===t.method&&"/requests"===t.url)return void s(u,200,i.listRequests());if("GET"===t.method&&"/sessions"===t.url)return void s(u,200,i.listSessions());if("GET"===t.method&&"/workflows"===t.url)return void s(u,200,i.inspect().workflows);const a=function(t){const o=(t??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(t.url);if("GET"===t.method&&a){const t=i.getWorkflow(a);return void s(u,t?200:404,t?{mermaid:r(t)}:{error:"workflow_not_found"})}const d=function(t){const o=(t??"").match(/^\/workflows\/([^/]+)\/plan$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(t.url);if("GET"===t.method&&d){const t=i.getWorkflow(d);return void s(u,t?200:404,t?o(t):{error:"workflow_not_found"})}const f=function(t){const o=(t??"").match(/^\/requests\/([^/]+)$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(t.url);if("GET"===t.method&&f){const t=i.inspectRequest(f);return void s(u,t?200:404,t??{error:"request_not_found"})}const c=function(t){const o=(t??"").match(/^\/runs\/([^/]+)\/trace$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(t.url);if("GET"===t.method&&c){const t=i.getRun(c);return void s(u,t?200:404,t?e(t):{error:"run_not_found"})}if("POST"===t.method&&"/requests"===t.url){const o=await async function(t){const o=[];for await(const e of t)o.push(Buffer.isBuffer(e)?e:Buffer.from(e));return 0===o.length?{}:JSON.parse(Buffer.concat(o).toString("utf8"))}(t);return void s(u,200,await i.request(function(t){const o=function(t){if("object"!=typeof t||null===t)return;const o=t;return"string"==typeof o.toolId?{toolId:o.toolId,args:o.args}:void 0}(t.toolCall),e=function(t){const o=n(t);if(o)return{..."string"==typeof o.workflowId?{workflowId:o.workflowId}:{},..."string"==typeof o.routeId?{routeId:o.routeId}:{},...void 0!==o.input?{input:o.input}:{},..."object"==typeof o.metadata&&o.metadata?{metadata:o.metadata}:{}}}(t.workflow),r=function(t){const o=n(t);if(o)return{..."string"==typeof o.namespace?{namespace:o.namespace}:{},...!1===o.recall||n(o.recall)?{recall:o.recall}:{},...Array.isArray(o.candidates)?{candidates:o.candidates}:{}}}(t.memory),s=n(t.metadata);return{input:"string"==typeof t.input?t.input:"",..."string"==typeof t.agentId?{agentId:t.agentId}:{},..."string"==typeof t.sessionId?{sessionId:t.sessionId}:{},..."string"==typeof t.requestId?{requestId:t.requestId}:{},..."string"==typeof t.parentRunId?{parentRunId:t.parentRunId}:{},...o?{toolCall:o}:{},...e?{workflow:e}:{},...r?{memory:r}:{},...s?{metadata:s}:{}}}(o)))}s(u,404,{error:"not_found"})}catch(t){s(u,500,{error:t instanceof Error?t.message:String(t)})}})}function n(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}function s(t,o,e){t.writeHead(o,{"content-type":"application/json"}),t.end(JSON.stringify(e))}
@@ -1 +1 @@
1
- export function createInProcessClient(e){return{request:s=>e.request(s),subscribe:s=>e.subscribe(s),inspect:()=>e.inspect(),getRun:s=>e.getRun(s),listRequests:s=>e.listRequests(s),listSessions:()=>e.listSessions(),inspectRequest:s=>e.inspectRequest(s),cancel:(s,t)=>e.cancel(s,t),stop:()=>e.stop()}}
1
+ export function createInProcessClient(e){return{request:s=>e.request(s),subscribe:s=>e.subscribe(s),inspect:()=>e.inspect(),getRuntimePolicy:()=>e.getRuntimePolicy(),getWorkflow:s=>e.getWorkflow(s),getRun:s=>e.getRun(s),listRequests:s=>e.listRequests(s),listSessions:()=>e.listSessions(),inspectRequest:s=>e.inspectRequest(s),cancel:(s,t)=>e.cancel(s,t),stop:()=>e.stop()}}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{randomUUID as t}from"node:crypto";export function createOpenAiCompatibleHttpServer(r,c={}){return e(async(e,p)=>{try{if(function(e,t,n){return"OPTIONS"===e.method&&(t.writeHead(204,l(n)),t.end(),!0)}(e,p,c))return;if(!function(e,t){return!t.bearerToken||e.headers.authorization===`Bearer ${t.bearerToken}`}(e,c))return void u(p,401,{error:{message:"unauthorized",type:"invalid_request_error"}},c);if("GET"===e.method&&"/v1/models"===e.url)return void u(p,200,function(e,t){const n=Object.keys(t.modelAgentMap??{});return{object:"list",data:[...new Set([...e.inspect().agents,...n])].sort().map(e=>({id:e,object:"model",created:0,owned_by:"stable-harness"}))}}(r,c),c);if("GET"===e.method&&"/v1/capabilities"===e.url)return void u(p,200,{object:"stable_harness.capabilities",endpoints:["/v1/models","/v1/chat/completions","/v1/capabilities"],streaming:!0,toolProgressEvents:!0},c);if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function(e,r,c,p){const f=await async function(e){const t=[];for await(const n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}(r);if(f.model&&!o(f.model,e,p))return void u(c,404,{error:{message:`model_not_found: ${f.model}`,type:"invalid_request_error"}},p);const m=function(e,r,s){const a=function(e){if(!Array.isArray(e)||0===e.length)throw new Error("messages must be a non-empty array");return e.map(e=>`${e.role??"user"}: ${n(e.content)}`).join("\n\n")}(e.messages);return{input:a,requestId:`chatcmpl-${t()}`,agentId:o(e.model,r,s),metadata:{protocol:"openai-compatible",openaiStream:!0===e.stream,openaiMessages:(i=e.messages,Array.isArray(i)?i.map(e=>({role:e.role??"user",content:n(e.content)})):[]),model:e.model,user:e.user,clientMetadata:e.metadata}};var i}(f,e,p);f.stream?await async function(e,t,n,r,o){(function(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...l(t)})})(t,o),d(t,a(r.requestId,n.model,{role:"assistant"},null));let i="";const c=e.subscribe(e=>{e.requestId===r.requestId&&(i+=function(e,t,n,r){const o=function(e){if("adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"output.delta"===t.phase&&"string"==typeof t.text?t.text:void 0}(t);if(o)return d(e,s(n,r,o)),o;!function(e,t){const n=function(e){if("tool.started"===e.type||"tool.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n="tool.start"===t.phase?"tool.started":"tool.result"===t.phase?"tool.completed":void 0,r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&function(e,t,n){e.write("event: stable_harness.tool.progress\n"),e.write(`data: ${JSON.stringify(n)}\n\n`)}(e,0,n)}(e,t)}(t,e,r.requestId,n.model)??"")});try{const o=await e.request(r),c=function(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}(i,o.output);c&&d(t,s(r.requestId,n.model,c)),d(t,function(e,t){return a(e,t,{},"stop")}(r.requestId,n.model)),t.write("data: [DONE]\n\n"),t.end()}finally{c()}}(e,c,f,m,p):u(c,200,function(e,t){return{id:t.requestId,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:e.model??t.agentId,choices:[{index:0,message:{role:"assistant",content:t.output},finish_reason:"stop"}],usage:i(e.messages,t.output),metadata:{sessionId:t.sessionId,agentId:t.agentId,state:t.state}}}(f,await e.request(m)),p)}(r,e,p,c);u(p,404,{error:{message:"not_found",type:"invalid_request_error"}},c)}catch(e){u(p,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}},c)}})}function n(e){if("string"==typeof e)return e;if(!Array.isArray(e))throw new Error("message content must be a string or content part array");return e.map(r).join("\n")}function r(e){if("object"!=typeof e||null===e)throw new Error("message content parts must be objects");const t=e;if("text"===t.type&&"string"==typeof t.text)return t.text;const n=t.image_url;if("image_url"===t.type&&"string"==typeof n?.url)return`[image:${n.url}]`;throw new Error("unsupported_content_type")}function o(e,t,n){if(!e)return;const r=n.modelAgentMap?.[e];return r||(t.inspect().agents.includes(e)?e:void 0)}function s(e,t,n){return a(e,t,{content:n},null)}function a(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function i(e,t){const r=c(Array.isArray(e)?e.map(e=>n(e.content)).join("\n"):""),o=c(t);return{prompt_tokens:r,completion_tokens:o,total_tokens:r+o}}function c(e){return e.trim()?Math.ceil(1.3*e.trim().split(/\s+/u).length):0}function u(e,t,n,r){e.writeHead(t,{"content-type":"application/json",...l(r)}),e.end(JSON.stringify(n))}function d(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}function l(e){return e.corsOrigins?.length?{"access-control-allow-origin":e.corsOrigins.join(", "),"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}:{}}
1
+ import{createServer as e}from"node:http";import{createCapabilitiesResponse as t,createModelsResponse as r,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as s}from"./openai-payload.js";import{createContentChunk as i,createRoleChunk as a,createStopChunk as c,missingFinalDelta as u,writeChatSse as l,writeRuntimeStreamEvent as d,writeSseHeaders as f}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(v,O={}){const w=function(e,t){const r=function(e){const t=m(e)??{};return m(t.openaiCompatible)??m(t["openai-compatible"])??m(t.openai)??{}}(e.getRuntimePolicy().protocols);return{bearerToken:t.bearerToken??y(r.bearerToken),corsOrigins:t.corsOrigins??(o=r.corsOrigins,Array.isArray(o)?o.filter(e=>"string"==typeof e&&e.trim().length>0):void 0),modelAgentMap:t.modelAgentMap??p(r.modelAgentMap),defaultModel:t.defaultModel??y(r.defaultModel)};var o}(v,O);return e(async(e,m)=>{try{if(function(e,t,r){const o=function(e,t){if(e)return t.corsOrigins?.includes("*")?"*":t.corsOrigins?.includes(e)||function(e){try{const t=new URL(e);return"http:"===t.protocol&&["localhost","127.0.0.1","[::1]"].includes(t.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,r);o&&(t.setHeader("access-control-allow-origin",o),t.setHeader("vary","origin"))}(e,m,w),function(e,t){return"OPTIONS"===e.method&&(t.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),t.end(),!0)}(e,m))return;if(!function(e,t){return!t.bearerToken||e.headers.authorization===`Bearer ${t.bearerToken}`}(e,w))return void g(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void g(m,200,r(v,w));if("GET"===e.method&&"/v1/capabilities"===e.url)return void g(m,200,t());if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function(e,t,r,m){const p=await async function(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}(t);if(p.model&&!o(p.model,e,m))return void g(r,404,{error:{message:`model_not_found: ${p.model}`,type:"invalid_request_error"}});const y=s(p,e,m,{sessionId:h(t.headers["x-stable-harness-session-id"])});if(p.stream)return void await async function(e,t,r,o){f(t,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(t,a(o.requestId,r.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=d(t,e,o.requestId,r.model)??"")});try{const s=await e.request(o),a=u(n,s.output);a&&l(t,i(o.requestId,r.model,a)),l(t,c(o.requestId,r.model)),t.write("data: [DONE]\n\n"),t.end()}finally{s()}}(e,r,p,y);const v=await e.request(y);g(r,200,n(p,v))}(v,e,m,w);g(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){g(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function m(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function p(e){const t=m(e);if(t)return Object.fromEntries(Object.entries(t).filter(e=>"string"==typeof e[1]))}function y(e){if("string"!=typeof e||!e.trim())return;const t=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]:e}function h(e){const t=Array.isArray(e)?e[0]:e;return t&&t.trim()?t:void 0}function g(e,t,r,o){e.writeHead(t,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(r))}
@@ -0,0 +1,74 @@
1
+ import type { RuntimeResponse, StableHarnessRuntime } from "@stable-harness/core";
2
+ import type { OpenAiCompatibleServerOptions } from "./openai-compatible.js";
3
+ export type ChatMessage = {
4
+ role?: string;
5
+ content?: unknown;
6
+ };
7
+ export type ChatCompletionRequest = {
8
+ model?: string;
9
+ messages?: ChatMessage[];
10
+ stream?: boolean;
11
+ user?: string;
12
+ metadata?: Record<string, unknown>;
13
+ };
14
+ export type ChatProtocolContext = {
15
+ sessionId?: string;
16
+ };
17
+ export declare function toRuntimeRequest(body: ChatCompletionRequest, runtime: StableHarnessRuntime, options: OpenAiCompatibleServerOptions, context?: ChatProtocolContext): {
18
+ metadata: {
19
+ protocol: string;
20
+ openaiStream: boolean;
21
+ openaiMessages: {
22
+ role: string;
23
+ content: string;
24
+ }[];
25
+ openaiSessionHistory: boolean;
26
+ model: string | undefined;
27
+ user: string | undefined;
28
+ clientMetadata: Record<string, unknown> | undefined;
29
+ };
30
+ sessionId?: string | undefined;
31
+ input: string;
32
+ requestId: string;
33
+ agentId: string | undefined;
34
+ };
35
+ export declare function resolveAgentId(model: string | undefined, runtime: StableHarnessRuntime, options: OpenAiCompatibleServerOptions): string | undefined;
36
+ export declare function createModelsResponse(runtime: StableHarnessRuntime, options: OpenAiCompatibleServerOptions): {
37
+ object: string;
38
+ data: {
39
+ id: string;
40
+ object: string;
41
+ created: number;
42
+ owned_by: string;
43
+ }[];
44
+ };
45
+ export declare function createCapabilitiesResponse(): {
46
+ object: string;
47
+ endpoints: string[];
48
+ streaming: boolean;
49
+ toolProgressEvents: boolean;
50
+ };
51
+ export declare function toChatCompletion(body: ChatCompletionRequest, result: RuntimeResponse): {
52
+ id: string;
53
+ object: string;
54
+ created: number;
55
+ model: string;
56
+ choices: {
57
+ index: number;
58
+ message: {
59
+ role: string;
60
+ content: string;
61
+ };
62
+ finish_reason: string;
63
+ }[];
64
+ usage: {
65
+ prompt_tokens: number;
66
+ completion_tokens: number;
67
+ total_tokens: number;
68
+ };
69
+ metadata: {
70
+ sessionId: string;
71
+ agentId: string;
72
+ state: import("@stable-harness/core").RuntimeRecordState;
73
+ };
74
+ };
@@ -0,0 +1 @@
1
+ import{randomUUID as t}from"node:crypto";export function toRuntimeRequest(o,r,s,a={}){const i=function(t){if(!Array.isArray(t)||0===t.length)throw new Error("messages must be a non-empty array");return t.map(t=>`${t.role??"user"}: ${n(t.content)}`).join("\n\n")}(o.messages),c=`chatcmpl-${t()}`,u=a.sessionId??function(t){const e=t?.sessionId??t?.session_id??t?.conversationId??t?.conversation_id;return"string"==typeof e&&e.trim()?e:void 0}(o.metadata),l=e(o.messages),p=u?function(t,n,o){if(function(t){return t.some(t=>"assistant"===t.role)||t.filter(t=>"user"===t.role).length>1}(o))return o;const r=t.listRequests({sessionId:n,state:"completed"}).flatMap(n=>function(t,n){const o=t.inspectRequest(n),r=o?.output?.trim(),s=(a=o?.metadata?.openaiMessages,e(Array.isArray(a)?a:void 0).filter(t=>"user"===t.role&&t.content.trim()).at(-1))?.content;var a;return s&&r?[{role:"user",content:s},{role:"assistant",content:r}]:[]}(t,n.requestId)).slice(-12);return r.length>0?[...r,...o]:o}(r,u,l):l;return{input:i,requestId:c,agentId:resolveAgentId(o.model,r,s),...u?{sessionId:u}:{},metadata:{protocol:"openai-compatible",openaiStream:!0===o.stream,openaiMessages:p,openaiSessionHistory:p.length>l.length,model:o.model,user:o.user,clientMetadata:o.metadata}}}export function resolveAgentId(t,e,n){if(!t)return;const o=n.modelAgentMap?.[t];return o||(e.inspect().agents.includes(t)?t:void 0)}export function createModelsResponse(t,e){const n=Object.keys(e.modelAgentMap??{});return{object:"list",data:[...new Set([...t.inspect().agents,...n])].sort().map(t=>({id:t,object:"model",created:0,owned_by:"stable-harness"}))}}export function createCapabilitiesResponse(){return{object:"stable_harness.capabilities",endpoints:["/v1/models","/v1/chat/completions","/v1/capabilities"],streaming:!0,toolProgressEvents:!0}}export function toChatCompletion(t,e){return{id:e.requestId,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:t.model??e.agentId,choices:[{index:0,message:{role:"assistant",content:e.output},finish_reason:"stop"}],usage:r(t.messages,e.output),metadata:{sessionId:e.sessionId,agentId:e.agentId,state:e.state}}}function e(t){return Array.isArray(t)?t.map(t=>({role:t.role??"user",content:n(t.content)})):[]}function n(t){if("string"==typeof t)return t;if(!Array.isArray(t))throw new Error("message content must be a string or content part array");return t.map(o).join("\n")}function o(t){if("object"!=typeof t||null===t)throw new Error("message content parts must be objects");const e=t;if("text"===e.type&&"string"==typeof e.text)return e.text;const n=e.image_url;if("image_url"===e.type&&"string"==typeof n?.url)return`[image:${n.url}]`;throw new Error("unsupported_content_type")}function r(t,e){const o=s(Array.isArray(t)?t.map(t=>n(t.content)).join("\n"):""),r=s(e);return{prompt_tokens:o,completion_tokens:r,total_tokens:o+r}}function s(t){return t.trim()?Math.ceil(1.3*t.trim().split(/\s+/u).length):0}
@@ -0,0 +1,39 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import type { RuntimeEvent } from "@stable-harness/core";
3
+ export declare function missingFinalDelta(streamedContent: string, finalOutput: string): string;
4
+ export declare function createRoleChunk(id: string, model: string | undefined): {
5
+ id: string;
6
+ object: string;
7
+ created: number;
8
+ model: string;
9
+ choices: {
10
+ index: number;
11
+ delta: Record<string, unknown>;
12
+ finish_reason: string | null;
13
+ }[];
14
+ };
15
+ export declare function createContentChunk(id: string, model: string | undefined, content: string): {
16
+ id: string;
17
+ object: string;
18
+ created: number;
19
+ model: string;
20
+ choices: {
21
+ index: number;
22
+ delta: Record<string, unknown>;
23
+ finish_reason: string | null;
24
+ }[];
25
+ };
26
+ export declare function createStopChunk(id: string, model: string | undefined): {
27
+ id: string;
28
+ object: string;
29
+ created: number;
30
+ model: string;
31
+ choices: {
32
+ index: number;
33
+ delta: Record<string, unknown>;
34
+ finish_reason: string | null;
35
+ }[];
36
+ };
37
+ export declare function writeRuntimeStreamEvent(response: ServerResponse, event: RuntimeEvent, id: string, model: string | undefined): string | undefined;
38
+ export declare function writeSseHeaders(response: ServerResponse, headers: Record<string, string>): void;
39
+ export declare function writeChatSse(response: ServerResponse, body: unknown): void;
@@ -0,0 +1 @@
1
+ export function missingFinalDelta(t,e){return t?!e||t.includes(e)?"":e.startsWith(t)?e.slice(t.length):e:e}export function createRoleChunk(e,n){return t(e,n,{role:"assistant"},null)}export function createContentChunk(e,n,o){return t(e,n,{content:o},null)}export function createStopChunk(e,n){return t(e,n,{},"stop")}export function writeRuntimeStreamEvent(t,e,n,o){const r=function(t){if("adapter.event"!==t.type||"object"!=typeof t.event||null===t.event)return;const e=t.event;return"output.delta"===e.phase&&"string"==typeof e.text?e.text:void 0}(e);if(r)return writeChatSse(t,createContentChunk(n,o,r)),r;!function(t,e){const n=function(t){if("tool.started"===t.type||"tool.completed"===t.type)return{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,tool_id:t.toolId};if("adapter.event"!==t.type||"object"!=typeof t.event||null===t.event)return;const e=t.event,n="tool.start"===e.phase?"tool.started":"tool.result"===e.phase?"tool.completed":void 0,o="string"==typeof e.toolId?e.toolId:void 0;return n&&o?{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:n,tool_id:o,adapter:e.adapter}:void 0}(e);n&&function(t,e,n){t.write("event: stable_harness.tool.progress\n"),t.write(`data: ${JSON.stringify(n)}\n\n`)}(t,0,n)}(t,e)}export function writeSseHeaders(t,e){t.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...e})}export function writeChatSse(t,e){t.write(`data: ${JSON.stringify(e)}\n\n`)}function t(t,e,n,o){return{id:t,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:e??"stable-harness",choices:[{index:0,delta:n,finish_reason:o}]}}
@@ -1 +1 @@
1
- import{BetterToolValidationError as e,betterTools as t,defaultRepair as r,reliableToolCalls as o}from"@botbotgo/better-call";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(e,t){super(`Tool argument validation failed for ${e}: ${t.map(e=>`${e.path} ${e.message}`).join("; ")}`),this.toolId=e,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(r){const o=r.tool.validateArgs?await r.tool.validateArgs({args:r.args,context:r.context}):{action:"allow",args:r.args};if("reject"===o.action)return o;const n=await async function(t,r,o){const n=function(e,t){return d(e.schema)?a(e.schema.safeParse(t??{})):u(r=e.schema)&&Object.values(r).length>0&&Object.values(r).every(d)?function(e,t){const r=u(t)?t:{},o={},s=[];for(const[t,a]of Object.entries(e)){const e=a.safeParse(r[t]);e.success?void 0!==e.data&&(o[t]=e.data):s.push(...e.error.issues.map(e=>({...e,path:[t,...e.path]})))}return s.length>0?a({success:!1,error:{issues:s}}):{action:"allow",args:o}}(e.schema,t):void 0;var r}(t,r);return n||(u(t.schema)?async function(t,r,o){try{return{action:"allow",args:await s(t,o).invoke(r)}}catch(t){if(t instanceof e)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(c)};throw t}}(t,r,o):{action:"allow",args:r})}(r.tool,o.args,t.betterCall);return"reject"===n.action?n:"repair"===o.action?{...o,args:n.args}:n}}}export function assertToolArguments(e,t,r,o){return Promise.resolve(o.validate({tool:e,args:t,context:r})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(e.id,t.issues);return t.args})}function a(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(t=e.path,t.length>0?`$.${t.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var t})}}export function prepareBetterCallTools(e,r){const o=t(e.map(n),l(r));return e.map((e,t)=>({...e,validationTool:o[t]}))}export async function repairBetterCallToolSelection(e){const t=(a=e.options,a?.repair??(a?.repairModel?r(a.repairModel):void 0));var a;if(!t||0===e.tools.length)return;const s=await o({userInput:JSON.stringify({tool:e.toolId,args:e.args}),tools:e.tools.map(i),calls:[{tool:e.toolId,args:(n=e.args,u(n)?n:{input:n})}],repair:t,mode:e.options?.mode??"repair"});var n;const c=s.ok?s.calls.find(t=>e.tools.some(e=>e.id===t.tool)):void 0;return c?{toolId:c.tool,args:c.args}:void 0}function s(e,r){return e.validationTool??t([n(e)],l(r))[0]}function n(e){return{name:e.id,description:e.description,schema:e.schema,invoke:e=>e}}function i(e){return{name:e.id,description:e.description,schema:u(e.schema)?e.schema:void 0}}function c(e){return{path:e.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:e.message,expected:void 0===e.expected?void 0:String(e.expected),actual:e.actual}}function l(e){return{mode:e?.mode??"repair",repair:e?.repair,repairModel:e?.repairModel}}function u(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function d(e){return u(e)&&"function"==typeof e.safeParse}
1
+ import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as r}from"@botbotgo/better-call";import{isRecord as a,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const r=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===r.action)return r;const n=await async function(t,e,r){return i(t.schema,e)||(a(t.schema)?async function(t,e,r){try{return{action:"allow",args:await s(t,r).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(c)};throw t}}(t,e,r):{action:"allow",args:e})}(e.tool,r.args,t.betterCall);return"reject"===n.action?n:"repair"===r.action?{...r,args:n.args}:n}}}export function assertToolArguments(o,t,e,r){return Promise.resolve(r.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const r=t(o.map(n),d(e));return o.map((o,t)=>({...o,validationTool:r[t]}))}export async function repairBetterCallToolSelection(o){const t=(i=o.options,i?.repair??(i?.repairModel?e(i.repairModel):void 0));var i;if(!t||0===o.tools.length)return;const s=await r({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(l),calls:[{tool:o.toolId,args:(n=o.args,a(n)?n:{input:n})}],repair:t,mode:o.options?.mode??"repair"});var n;const c=s.ok?s.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return c?{toolId:c.tool,args:c.args}:void 0}function s(o,e){return o.validationTool??t([n(o)],d(e))[0]}function n(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function l(o){return{name:o.id,description:o.description,schema:a(o.schema)?o.schema:void 0}}function c(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function d(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel}}
@@ -0,0 +1,3 @@
1
+ import type { ToolArgumentGuardResult } from "./types.js";
2
+ export declare function validateWithZodSchema(schema: unknown, args: unknown): ToolArgumentGuardResult | undefined;
3
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -0,0 +1 @@
1
+ export function validateWithZodSchema(r,a){return s(r)?e(r.safeParse(a??{})):isRecord(t=r)&&Object.values(t).length>0&&Object.values(t).every(s)?function(s,r){const a=isRecord(r)?r:{},t={},o=[];for(const[e,r]of Object.entries(s)){const s=r.safeParse(a[e]);s.success?void 0!==s.data&&(t[e]=s.data):o.push(...s.error.issues.map(s=>({...s,path:[e,...s.path]})))}return o.length>0?e({success:!1,error:{issues:o}}):{action:"allow",args:t}}(r,a):void 0;var t}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function e(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(s=e.path,s.length>0?`$.${s.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var s})}}function s(e){return isRecord(e)&&"function"==typeof e.safeParse}
@@ -0,0 +1,4 @@
1
+ import type { WorkspaceSkill, WorkspaceTool } from "@stable-harness/core";
2
+ export declare function listYamlFiles(root: string): Promise<string[]>;
3
+ export declare function discoverModuleTools(workspaceRoot: string): Promise<WorkspaceTool[]>;
4
+ export declare function discoverSkills(workspaceRoot: string): Promise<WorkspaceSkill[]>;
@@ -0,0 +1 @@
1
+ import{readdir as t,readFile as e}from"node:fs/promises";import r from"node:path";import{parseAllDocuments as i}from"yaml";export async function listYamlFiles(e){const i=await t(e,{withFileTypes:!0});return(await Promise.all(i.map(async t=>{const i=r.join(e,t.name);return t.isDirectory()?listYamlFiles(i):t.isFile()&&/\.ya?ml$/iu.test(t.name)?[i]:[]}))).flat().sort()}export async function discoverModuleTools(i){const a=r.join(i,"resources","tools");let s;try{s=await t(a,{withFileTypes:!0})}catch{return[]}return(await Promise.all(s.filter(t=>t.isFile()&&t.name.endsWith(".mjs")&&!t.name.startsWith("_")).map(t=>async function(t){const i=[...(await e(t,"utf8")).matchAll(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool\s*\(/gu)].map(t=>t[1]).filter(t=>"default"!==t);return(i.length>0?i:[r.basename(t,".mjs")]).map(e=>({id:e,sourcePath:t}))}(r.join(a,t.name))))).flat()}export async function discoverSkills(s){const n=r.join(s,"resources","skills");let o;try{o=await t(n,{withFileTypes:!0})}catch{return[]}return(await Promise.all(o.filter(t=>t.isDirectory()).map(t=>async function(t,s){const n=r.join(t,"SKILL.md");let o;try{o=await e(n,"utf8")}catch{return}const l=function(t){const e=t.match(/^---\n([\s\S]*?)\n---/u);if(!e)return{};const r=i(e[1]).at(0)?.toJSON();return"object"!=typeof r||null===r||Array.isArray(r)?{}:r}(o);return{id:a(l.name)??s,path:n,...a(l.description)?{description:a(l.description)}:{},allowedTools:(c=l["allowed-tools"],Array.isArray(c)?c.filter(t=>"string"==typeof t&&t.trim().length>0):[])};var c}(r.join(n,t.name),t.name)))).filter(t=>Boolean(t))}function a(t){return"string"==typeof t&&t.trim()?t.trim():void 0}
@@ -0,0 +1,16 @@
1
+ import type { WorkspaceAgent, WorkspaceMemory, WorkspaceModel, WorkspaceRuntimePolicy, WorkspaceTool } from "@stable-harness/core";
2
+ export type RawDocument = {
3
+ apiVersion?: unknown;
4
+ kind?: unknown;
5
+ metadata?: {
6
+ name?: unknown;
7
+ description?: unknown;
8
+ };
9
+ spec?: unknown;
10
+ };
11
+ export declare function compileRuntime(document: RawDocument): WorkspaceRuntimePolicy;
12
+ export declare function compileAgent(document: RawDocument, sourcePath: string): WorkspaceAgent;
13
+ export declare function compileModel(document: RawDocument): WorkspaceModel;
14
+ export declare function compileModelSpec(spec: Record<string, unknown>, fallback?: string): WorkspaceModel;
15
+ export declare function compileTool(document: RawDocument): WorkspaceTool;
16
+ export declare function compileMemory(document: RawDocument): WorkspaceMemory;
@@ -0,0 +1 @@
1
+ function e(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function r(e,r){const o=e.metadata?.name;if("string"==typeof o&&o.trim())return o.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function o(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function t(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function n(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function i(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(r){const o=e(r.spec,"Runtime.spec"),n=e(o.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof n.defaultAgentId&&n.defaultAgentId.trim()?n.defaultAgentId.trim():"orchestra",...t(o.workspaceId)?{workspaceId:t(o.workspaceId)}:{},...t(o.profile)?{profile:t(o.profile)}:{},...void 0!==o.adapters?{adapters:p(o.adapters)}:{},..."object"==typeof o.workflowRouting&&o.workflowRouting?{workflowRouting:s(o.workflowRouting)}:{},..."object"==typeof o.approvals&&o.approvals?{approvals:o.approvals}:{},..."object"==typeof o.recovery&&o.recovery?{recovery:o.recovery}:{},..."object"==typeof o.retry&&o.retry?{retry:o.retry}:{},..."object"==typeof o.memory&&o.memory?{memory:o.memory}:{},..."object"==typeof o.protocols&&o.protocols?{protocols:o.protocols}:{}}}export function compileAgent(i,s){const p=e(i.spec,"Agent.spec"),m=r(i),a=t(p.backend);if(!a)throw new Error(`Agent ${m} requires spec.backend`);const d="object"==typeof p.config&&p.config?p.config:{},f="string"==typeof p.systemPrompt?p.systemPrompt:"string"==typeof d.systemPrompt?d.systemPrompt:void 0;return{id:m,...o(i)?{description:o(i)}:{},sourcePath:s,backend:a,..."string"==typeof p.modelRef&&p.modelRef.trim()?{modelRef:(u=p.modelRef,u.replace(/^[^/]+\//u,""))}:{},...void 0!==f?{systemPrompt:f}:{},tools:n(p.tools),skills:n(p.skills),memory:Array.isArray(p.memory)?p.memory:[],subagents:n(p.subagents),...void 0!==p.edges?{edges:c(p.edges,m)}:{},config:d};var u}export function compileModel(o){return compileModelSpec(e(o.spec,"Model.spec"),r(o))}export function compileModelSpec(e,r){const o="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",t=i(e.provider),n=i(e.model),s="string"==typeof t&&t.trim()?t.trim():"unknown",p="string"==typeof n&&n.trim()?n.trim():o,c={...e};return delete c.name,delete c.provider,delete c.model,{id:o,provider:s,model:p,config:Object.fromEntries(Object.entries(c).map(([e,r])=>[e,i(r)]))}}export function compileTool(o){const t=e(o.spec,"Tool.spec");return{id:r(o),..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(o){const n=e(o.spec,"Memory.spec"),i=r(o),s={...n};return delete s.provider,delete s.profile,delete s.mode,delete s.enabled,delete s.prompts,{id:i,provider:t(n.provider)??"langmem",...t(n.profile)?{profile:t(n.profile)}:{},...t(n.mode)?{mode:t(n.mode)}:{},enabled:!1!==n.enabled,..."object"==typeof n.prompts&&n.prompts?{prompts:a(n.prompts)}:{},...Object.keys(s).length>0?{config:s}:{}}}function s(r){const o=e(r,"Runtime.spec.workflowRouting"),n=void 0===o.routes?void 0:function(r){if(!Array.isArray(r))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return r.map(r=>{const o=e(r,"Runtime.spec.workflowRouting.routes[]"),n=t(o.id),i=t(o.workflowId);if(!n||!i)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:n,workflowId:i,...t(o.description)?{description:t(o.description)}:{},..."object"==typeof o.metadata&&o.metadata?{metadata:o.metadata}:{}}})}(o.routes);return{...t(o.defaultWorkflowId)?{defaultWorkflowId:t(o.defaultWorkflowId)}:{},...n?{routes:n}:{}}}function p(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(m)}function c(r,o){if(!Array.isArray(r))throw new Error(`Agent ${o} spec.edges must be an array`);return r.map(r=>{const n=e(r,`Agent ${o} spec.edges[]`),i=t(n.from),s=t(n.to);if(!i||!s)throw new Error(`Agent ${o} spec.edges[] requires from and to`);return{from:i,to:s,...t(n.condition)?{condition:t(n.condition)}:{}}})}function m(r){if("string"==typeof r&&r.trim())return{name:r.trim()};const o=e(r,"Runtime.spec.adapters[]"),n=t(o.name)??t(o.id)??t(o.backend);if(!n)throw new Error("Runtime.spec.adapters[] requires name");return{name:n,..."boolean"==typeof o.enabled?{enabled:o.enabled}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}}}function a(r){const o=e(r,"Memory.spec.prompts");return{...t(o.semantic)?{semantic:t(o.semantic)}:{},...t(o.episodic)?{episodic:t(o.episodic)}:{},...t(o.procedural)?{procedural:t(o.procedural)}:{}}}
@@ -1 +1 @@
1
- import{readdir as e,readFile as t}from"node:fs/promises";import o from"node:path";import{parseAllDocuments as r}from"yaml";async function n(t){const r=await e(t,{withFileTypes:!0});return(await Promise.all(r.map(async e=>{const r=o.join(t,e.name);return e.isDirectory()?n(r):e.isFile()&&/\.ya?ml$/iu.test(e.name)?[r]:[]}))).flat().sort()}function i(e,t){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${t} must be an object`);return e}function s(e,t){const o=e.metadata?.name;if("string"==typeof o&&o.trim())return o.trim();if(t)return t;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function a(e){const t=e.metadata?.description;return"string"==typeof t&&t.trim()?t.trim():void 0}function c(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function p(e){if("string"!=typeof e)return e;const t=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return t?process.env[t[1]]??t[2]??"":e}function m(e){const t=i(e.spec,"Runtime.spec"),o=i(t.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof o.defaultAgentId&&o.defaultAgentId.trim()?o.defaultAgentId.trim():"orchestra",...b(t.workspaceId)?{workspaceId:b(t.workspaceId)}:{},...b(t.profile)?{profile:b(t.profile)}:{},..."object"==typeof t.approvals&&t.approvals?{approvals:t.approvals}:{},..."object"==typeof t.recovery&&t.recovery?{recovery:t.recovery}:{},..."object"==typeof t.retry&&t.retry?{retry:t.retry}:{},..."object"==typeof t.memory&&t.memory?{memory:t.memory}:{},..."object"==typeof t.protocols&&t.protocols?{protocols:t.protocols}:{}}}function l(e,t){const o=i(e.spec,"Agent.spec"),r=s(e),n="string"==typeof o.backend&&o.backend.trim()?o.backend.trim():"deepagents",p="object"==typeof o.config&&o.config?o.config:{},m="string"==typeof o.systemPrompt?o.systemPrompt:"string"==typeof p.systemPrompt?p.systemPrompt:void 0;return{id:r,...a(e)?{description:a(e)}:{},sourcePath:t,backend:"deepagent"===n?"deepagents":n,..."string"==typeof o.modelRef&&o.modelRef.trim()?{modelRef:(l=o.modelRef,l.replace(/^[^/]+\//u,""))}:{},...void 0!==m?{systemPrompt:m}:{},tools:c(o.tools),skills:c(o.skills),memory:Array.isArray(o.memory)?o.memory:[],subagents:c(o.subagents),config:p};var l}function f(e,t){const o="string"==typeof e.name&&e.name.trim()?e.name.trim():t??"default",r=p(e.provider),n=p(e.model),i="string"==typeof r&&r.trim()?r.trim():"unknown",s="string"==typeof n&&n.trim()?n.trim():o,a={...e};return delete a.name,delete a.provider,delete a.model,{id:o,provider:i,model:s,config:Object.fromEntries(Object.entries(a).map(([e,t])=>[e,p(t)]))}}function d(e){return f(i(e.spec,"Model.spec"),s(e))}function u(e){const t=i(e.spec,"Tool.spec");return{id:s(e),..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}function y(e){const t=i(e.spec,"Memory.spec"),o=s(e),r={...t};return delete r.provider,delete r.profile,delete r.mode,delete r.enabled,delete r.prompts,{id:o,provider:b(t.provider)??"langmem",...b(t.profile)?{profile:b(t.profile)}:{},...b(t.mode)?{mode:b(t.mode)}:{},enabled:!1!==t.enabled,..."object"==typeof t.prompts&&t.prompts?{prompts:g(t.prompts)}:{},...Object.keys(r).length>0?{config:r}:{}}}function g(e){const t=i(e,"Memory.spec.prompts");return{...b(t.semantic)?{semantic:b(t.semantic)}:{},...b(t.episodic)?{episodic:b(t.episodic)}:{},...b(t.procedural)?{procedural:b(t.procedural)}:{}}}function b(e){return"string"==typeof e&&e.trim()?e.trim():void 0}export async function loadWorkspaceFromYaml(i){const s=o.join(i,"config"),a=await n(s),p=[],g=new Map,w=new Map,h=new Map,A=new Map,k=new Map;for(const e of a){const o=await t(e,"utf8"),n=r(o).map(e=>e.toJSON()).filter(e=>null!==e);for(const t of n)if("string"==typeof t.kind)switch(t.kind){case"Runtime":p.push(m(t));break;case"Agent":{const o=l(t,e);g.set(o.id,o);break}case"Model":{const e=d(t);w.set(e.id,e);break}case"Models":if(Array.isArray(t.spec))for(const e of t.spec)if("object"==typeof e&&null!==e&&!Array.isArray(e)){const t=f(e);w.set(t.id,t)}break;case"Tool":{const e=u(t);h.set(e.id,e);break}case"Memory":{const e=y(t);k.set(e.id,e);break}}}for(const r of await async function(r){const n=o.join(r,"resources","tools");let i;try{i=await e(n,{withFileTypes:!0})}catch{return[]}return(await Promise.all(i.filter(e=>e.isFile()&&e.name.endsWith(".mjs")&&!e.name.startsWith("_")).map(e=>async function(e){const r=[...(await t(e,"utf8")).matchAll(/export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*tool\s*\(/gu)].map(e=>e[1]).filter(e=>"default"!==e);return(r.length>0?r:[o.basename(e,".mjs")]).map(t=>({id:t,sourcePath:e}))}(o.join(n,e.name))))).flat()}(i))h.has(r.id)||h.set(r.id,r);for(const n of await async function(n){const i=o.join(n,"resources","skills");let s;try{s=await e(i,{withFileTypes:!0})}catch{return[]}return(await Promise.all(s.filter(e=>e.isDirectory()).map(e=>async function(e,n){const i=o.join(e,"SKILL.md");let s;try{s=await t(i,"utf8")}catch{return}const a=function(e){const t=e.match(/^---\n([\s\S]*?)\n---/u);if(!t)return{};const o=r(t[1]).at(0)?.toJSON();return"object"!=typeof o||null===o||Array.isArray(o)?{}:o}(s);return{id:b(a.name)??n,path:i,...b(a.description)?{description:b(a.description)}:{},allowedTools:c(a["allowed-tools"])}}(o.join(i,e.name),e.name)))).filter(e=>Boolean(e))}(i))A.set(n.id,n);return{root:i,runtime:p.at(-1)??{defaultAgentId:"orchestra"},agents:g,models:w,tools:h,skills:A,memories:k}}
1
+ import{readFile as o}from"node:fs/promises";import e from"node:path";import{parseAllDocuments as s}from"yaml";import{discoverModuleTools as r,discoverSkills as t,listYamlFiles as a}from"./discovery.js";import{compileAgent as i,compileMemory as n,compileModel as l,compileModelSpec as f,compileRuntime as c,compileTool as w}from"./documents.js";import{compileWorkflow as d,validateWorkflows as m}from"./workflows.js";export async function loadWorkspaceFromYaml(k){const p=e.join(k,"config"),u=await a(p),M=[],y=new Map,g=new Map,h=new Map,b=new Map,A=new Map,W=new Map;for(const e of u){const r=await o(e,"utf8"),t=s(r).map(o=>o.toJSON()).filter(o=>null!==o);for(const o of t)if("string"==typeof o.kind)switch(o.kind){case"Runtime":M.push(c(o));break;case"Agent":{const s=i(o,e);y.set(s.id,s);break}case"Model":{const e=l(o);g.set(e.id,e);break}case"Models":if(Array.isArray(o.spec))for(const e of o.spec)if("object"==typeof e&&null!==e&&!Array.isArray(e)){const o=f(e);g.set(o.id,o)}break;case"Tool":{const e=w(o);h.set(e.id,e);break}case"Memory":{const e=n(o);A.set(e.id,e);break}case"Workflow":{const s=d(o,e);W.set(s.id,s);break}}}for(const o of await r(k))h.has(o.id)||h.set(o.id,o);for(const o of await t(k))b.set(o.id,o);const I=M.at(-1)??{defaultAgentId:"orchestra"};return m({workflows:W,agents:y,tools:h,skills:b}),function(o,e){const s=o.workflowRouting;if(s){if(s.defaultWorkflowId&&!e.has(s.defaultWorkflowId))throw new Error(`Runtime workflowRouting.defaultWorkflowId references unknown workflow ${s.defaultWorkflowId}`);for(const o of s.routes??[])if(!e.has(o.workflowId))throw new Error(`Runtime workflowRouting route ${o.id} references unknown workflow ${o.workflowId}`)}}(I,W),{root:k,runtime:I,agents:y,models:g,tools:h,skills:b,memories:A,workflows:W}}
@@ -0,0 +1,16 @@
1
+ import type { WorkspaceAgent, WorkspaceSkill, WorkspaceTool, WorkspaceWorkflow } from "@stable-harness/core";
2
+ type RawDocument = {
3
+ metadata?: {
4
+ name?: unknown;
5
+ description?: unknown;
6
+ };
7
+ spec?: unknown;
8
+ };
9
+ export declare function compileWorkflow(document: RawDocument, sourcePath: string): WorkspaceWorkflow;
10
+ export declare function validateWorkflows(input: {
11
+ workflows: Map<string, WorkspaceWorkflow>;
12
+ agents: Map<string, WorkspaceAgent>;
13
+ tools: Map<string, WorkspaceTool>;
14
+ skills: Map<string, WorkspaceSkill>;
15
+ }): void;
16
+ export {};
@@ -0,0 +1 @@
1
+ export function compileWorkflow(n,t){const s=i(n.spec,"Workflow.spec"),d="object"==typeof s.config&&s.config?s.config:{};return{id:f(n),...c(n)?{description:c(n)}:{},sourcePath:t,...a(s.adapter)?{adapter:a(s.adapter)}:{},...a(s.entry)?{entry:a(s.entry)}:{},..."object"==typeof s.state&&s.state?{state:o(s.state)}:{},nodes:e(s.nodes),edges:r(s.edges),..."object"==typeof s.policies&&s.policies?{policies:s.policies}:{},...Object.keys(d).length>0?{config:d}:{}}}export function validateWorkflows(o){for(const e of o.workflows.values()){const r=new Set(e.nodes.map(o=>o.id));n(e,r),t(e,r);for(const r of e.nodes)s(r.use,o)}}function o(o){const e=i(o,"Workflow.spec.state");return{...a(e.schema)?{schema:a(e.schema)}:{},..."object"==typeof e.config&&e.config?{config:e.config}:{}}}function e(o){if(!Array.isArray(o)||0===o.length)throw new Error("Workflow.spec.nodes must be a non-empty array");return o.map(o=>{const e=i(o,"Workflow.spec.nodes[]"),r=a(e.id),n=a(e.use);if(!r||!n)throw new Error("Workflow.spec.nodes[] requires id and use");return{id:r,use:n,...a(e.type)?{type:a(e.type)}:{},..."object"==typeof e.config&&e.config?{config:e.config}:{}}})}function r(o){if(!Array.isArray(o))throw new Error("Workflow.spec.edges must be an array");return o.map(o=>{const e=i(o,"Workflow.spec.edges[]"),r=a(e.from),n=a(e.to);if(!r||!n)throw new Error("Workflow.spec.edges[] requires from and to");return{from:r,to:n,...a(e.condition)?{condition:a(e.condition)}:{}}})}function n(o,e){if(o.entry&&!e.has(o.entry))throw new Error(`Workflow ${o.id} entry references unknown node ${o.entry}`)}function t(o,e){for(const r of o.edges)if(!e.has(r.from)||!e.has(r.to))throw new Error(`Workflow ${o.id} edge references unknown node ${r.from}->${r.to}`)}function s(o,e){const r=o.indexOf("."),n=r>0?o.slice(0,r):"",t=r>0?o.slice(r+1):"";if(!n||!t)throw new Error(`Workflow node use must reference inventory as agents.<id>, tools.<id>, skills.<id>, or workflows.<id>: ${o}`);const s="agents"===n?e.agents:"tools"===n?e.tools:"skills"===n?e.skills:"workflows"===n?e.workflows:void 0;if(!s?.has(t))throw new Error(`Workflow node references unknown ${n}.${t}`)}function i(o,e){if("object"!=typeof o||null===o||Array.isArray(o))throw new Error(`${e} must be an object`);return o}function f(o){const e=o.metadata?.name;if("string"==typeof e&&e.trim())return e.trim();throw new Error("Workflow document requires metadata.name")}function c(o){const e=o.metadata?.description;return"string"==typeof e&&e.trim()?e.trim():void 0}function a(o){return"string"==typeof o&&o.trim()?o.trim():void 0}
@@ -1 +0,0 @@
1
- import{ToolMessage as e}from"@langchain/core/messages";const t=new Set(["ls","read_file","write_file","edit_file","glob","grep"]);export function createBuiltinToolPolicyMiddleware(e){return{name:"StableHarnessBuiltinToolPolicy",async wrapModelCall(t,r){if(!function(e){return n(e)||!o(e)}(e))return r(t);const l=Array.isArray(t.tools)?t.tools.filter(t=>i(e,t.name)):t.tools,s=function(e,t){return"required"===t||function(e,t){return"string"==typeof t?.function?.name&&!i(e,t.function.name)}(e,t)?"auto":t}(e,t.toolChoice);return r({...t,tools:l,toolChoice:s})}}}export function validateFilesystemBuiltinCall(o,i,r){if(n(o)&&t.has(i))return new e({tool_call_id:r.toolCall?.id??`stable-harness-${i}-policy`,name:i,content:`Filesystem builtin tool '${i}' is disabled for this agent. Do not retry filesystem tools; use the agent's registered workspace tools and already collected evidence instead.`})}function n(e){const t=r(e.agent.config,"builtinTools");return!1===t?.filesystem}function o(e){const t=r(e.agent.config,"builtinTools")?.modelExposed;return!1!==t&&(!Array.isArray(t)||t.includes("task"))}function i(e,i){return(!n(e)||!function(e){return"string"==typeof e&&t.has(e)}(i))&&("task"!==i||o(e))}function r(e,t){const n=l(e)?e:{};return l(n[t])?n[t]:void 0}function l(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}