stable-harness 0.0.53 → 0.0.55

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 (85) hide show
  1. package/README.md +0 -1
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +1 -1
  4. package/docs/0.1.0-stable-runtime-development-roadmap.zh.md +14 -14
  5. package/docs/guides/integration-guide.md +12 -0
  6. package/docs/protocols/http-runtime.md +10 -0
  7. package/node_modules/@stable-harness/adapter-deepagents/dist/src/adapter.js +1 -1
  8. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/checkpoint.d.ts +10 -0
  9. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/checkpoint.js +1 -0
  10. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/runtime.d.ts +11 -0
  11. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/runtime.js +1 -0
  12. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/store.d.ts +10 -0
  13. package/node_modules/@stable-harness/adapter-deepagents/dist/src/internal/substrate/store.js +1 -0
  14. package/node_modules/@stable-harness/core/dist/runtime/admin/administration.d.ts +9 -0
  15. package/node_modules/@stable-harness/core/dist/runtime/admin/administration.js +1 -0
  16. package/node_modules/@stable-harness/core/dist/runtime/admin/memory.d.ts +10 -0
  17. package/node_modules/@stable-harness/core/dist/runtime/admin/memory.js +1 -0
  18. package/node_modules/@stable-harness/core/dist/runtime/events.d.ts +11 -0
  19. package/node_modules/@stable-harness/core/dist/runtime/persistence/stores.js +1 -1
  20. package/node_modules/@stable-harness/core/dist/runtime/types.d.ts +6 -0
  21. package/node_modules/@stable-harness/core/dist/runtime.js +1 -1
  22. package/node_modules/@stable-harness/core/dist/types.d.ts +13 -4
  23. package/node_modules/@stable-harness/core/dist/workspace/types.d.ts +1 -0
  24. package/node_modules/@stable-harness/memory/dist/src/index.d.ts +1 -1
  25. package/node_modules/@stable-harness/memory/dist/src/index.js +1 -1
  26. package/node_modules/@stable-harness/memory/dist/src/persistence.js +1 -1
  27. package/node_modules/@stable-harness/memory/dist/src/store.d.ts +3 -0
  28. package/node_modules/@stable-harness/memory/dist/src/store.js +1 -1
  29. package/node_modules/@stable-harness/memory/dist/src/types.d.ts +1 -1
  30. package/node_modules/@stable-harness/protocols/dist/src/http-server.js +1 -1
  31. package/node_modules/@stable-harness/protocols/dist/src/in-process-client.js +1 -1
  32. package/node_modules/@stable-harness/tool-gateway/dist/src/argument-guard.js +1 -1
  33. package/node_modules/@stable-harness/tool-gateway/dist/src/schema-validation.js +1 -1
  34. package/node_modules/@stable-harness/workspace-yaml/dist/documents.d.ts +7 -1
  35. package/node_modules/@stable-harness/workspace-yaml/dist/documents.js +1 -1
  36. package/node_modules/@stable-harness/workspace-yaml/dist/loader.js +1 -1
  37. package/package.json +4 -7
  38. package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
  39. package/packages/adapter-deepagents/dist/src/internal/substrate/checkpoint.d.ts +10 -0
  40. package/packages/adapter-deepagents/dist/src/internal/substrate/checkpoint.js +1 -0
  41. package/packages/adapter-deepagents/dist/src/internal/substrate/runtime.d.ts +11 -0
  42. package/packages/adapter-deepagents/dist/src/internal/substrate/runtime.js +1 -0
  43. package/packages/adapter-deepagents/dist/src/internal/substrate/store.d.ts +10 -0
  44. package/packages/adapter-deepagents/dist/src/internal/substrate/store.js +1 -0
  45. package/packages/cli/dist/src/server.js +1 -1
  46. package/packages/core/dist/runtime/admin/administration.d.ts +9 -0
  47. package/packages/core/dist/runtime/admin/administration.js +1 -0
  48. package/packages/core/dist/runtime/admin/memory.d.ts +10 -0
  49. package/packages/core/dist/runtime/admin/memory.js +1 -0
  50. package/packages/core/dist/runtime/events.d.ts +11 -0
  51. package/packages/core/dist/runtime/persistence/stores.js +1 -1
  52. package/packages/core/dist/runtime/types.d.ts +6 -0
  53. package/packages/core/dist/runtime.js +1 -1
  54. package/packages/core/dist/types.d.ts +13 -4
  55. package/packages/core/dist/workspace/types.d.ts +1 -0
  56. package/packages/memory/dist/src/index.d.ts +1 -1
  57. package/packages/memory/dist/src/index.js +1 -1
  58. package/packages/memory/dist/src/persistence.js +1 -1
  59. package/packages/memory/dist/src/store.d.ts +3 -0
  60. package/packages/memory/dist/src/store.js +1 -1
  61. package/packages/memory/dist/src/types.d.ts +1 -1
  62. package/packages/protocols/dist/src/http-server.js +1 -1
  63. package/packages/protocols/dist/src/in-process-client.js +1 -1
  64. package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
  65. package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
  66. package/packages/workspace-yaml/dist/documents.d.ts +7 -1
  67. package/packages/workspace-yaml/dist/documents.js +1 -1
  68. package/packages/workspace-yaml/dist/loader.js +1 -1
  69. package/dist/cli.d.ts +0 -2
  70. package/dist/cli.js +0 -2
  71. package/dist/compat/agent-harness.d.ts +0 -24
  72. package/dist/compat/agent-harness.js +0 -1
  73. package/dist/runtime/compat/agent-harness-compat-runner.d.ts +0 -2
  74. package/dist/runtime/compat/agent-harness-compat-runner.js +0 -1
  75. package/dist/runtime/compat/json.d.ts +0 -4
  76. package/dist/runtime/compat/json.js +0 -1
  77. package/dist/runtime/compat/presentation.d.ts +0 -1
  78. package/dist/runtime/compat/presentation.js +0 -1
  79. package/dist/runtime/compat/prompts.d.ts +0 -29
  80. package/dist/runtime/compat/prompts.js +0 -1
  81. package/dist/runtime/compat/tool-registry.d.ts +0 -3
  82. package/dist/runtime/compat/tool-registry.js +0 -1
  83. package/dist/runtime/compat/types.d.ts +0 -38
  84. package/dist/runtime/compat/types.js +0 -1
  85. package/docs/compatibility-matrix.md +0 -150
@@ -1 +1 @@
1
- export function createInProcessClient(e){return{request:t=>e.request(t),subscribe:t=>e.subscribe(t),inspect:()=>e.inspect(),getRuntimePolicy:()=>e.getRuntimePolicy(),getWorkflow:t=>e.getWorkflow(t),getRun:t=>e.getRun(t),listArtifacts:t=>e.listArtifacts(t),getArtifact:t=>e.getArtifact(t),readArtifact:t=>e.readArtifact(t),exportReplayBundle:t=>e.exportReplayBundle(t),listRequests:t=>e.listRequests(t),listSessions:()=>e.listSessions(),inspectRequest:t=>e.inspectRequest(t),listApprovals:t=>e.listApprovals(t),getApproval:t=>e.getApproval(t),resolveApproval:(t,s)=>e.resolveApproval(t,s),cancel:(t,s)=>e.cancel(t,s),stop:()=>e.stop()}}
1
+ export function createInProcessClient(e){return{request:t=>e.request(t),subscribe:t=>e.subscribe(t),inspect:()=>e.inspect(),getRuntimePolicy:()=>e.getRuntimePolicy(),getWorkflow:t=>e.getWorkflow(t),getRun:t=>e.getRun(t),listArtifacts:t=>e.listArtifacts(t),getArtifact:t=>e.getArtifact(t),readArtifact:t=>e.readArtifact(t),exportReplayBundle:t=>e.exportReplayBundle(t),listRequests:t=>e.listRequests(t),listSessions:()=>e.listSessions(),inspectRequest:t=>e.inspectRequest(t),memorize:t=>e.memorize(t),recallMemories:t=>e.recallMemories(t),listMemories:t=>e.listMemories(t),updateMemory:t=>e.updateMemory(t),archiveMemory:(t,s)=>e.archiveMemory(t,s),listApprovals:t=>e.listApprovals(t),getApproval:t=>e.getApproval(t),resolveApproval:(t,s)=>e.resolveApproval(t,s),cancel:(t,s)=>e.cancel(t,s),deleteRequest:t=>e.deleteRequest(t),deleteSession:t=>e.deleteSession(t),stop:()=>e.stop()}}
@@ -1 +1 @@
1
- import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@easynet/better-call";import{isRecord as r,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=i(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const l=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?l:"reject"===l.action?r:i(t.schema,l.args)||l:l}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const i=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(l=o.args,r(l)?l:{input:l})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var l;const n=i.ok?i.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}}}
1
+ import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@easynet/better-call";import{isRecord as r,validateWithZodSchema as l}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=l(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const i=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?i:"reject"===i.action?r:l(t.schema,i.args)||i:i}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const l=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(i=o.args,r(i)?i:{input:i})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var i;const n=l.ok?l.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){const t=Boolean(o?.repair||o?.repairModel);return{mode:o?.mode??(t?"repair":"guard"),repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??(t?{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}:{allowCoercion:!1,allowClamp:!1,allowArrayStringSplit:!1,allowModelRepair:!1})}}
@@ -1 +1 @@
1
- import{normalizeArgsBySchema as e}from"@easynet/better-call";export function validateWithZodSchema(a,r){return isZodLike(a)?toZodGuardResult(a.safeParse(r??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(a)?function validateWithZodShape(a,r){const t=function normalizeZodShapeArgs(a,r){const t=isRecord(r)?r:{};return e(a,t,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(a,r),s={},o=[];for(const[e,r]of Object.entries(a)){const a=r.safeParse(t[e]);a.success?void 0!==a.data&&(s[e]=a.data):o.push(...a.error.issues.map(a=>({...a,path:[e,...a.path]})))}return o.length>0?toZodGuardResult({success:!1,error:{issues:o}}):{action:"allow",args:s}}(a,r):void 0}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function toZodGuardResult(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(a=e.path,a.length>0?`$.${a.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var a})}}function isZodLike(e){return isRecord(e)&&"function"==typeof e.safeParse}
1
+ import{normalizeArgsBySchema as e}from"@easynet/better-call";export function validateWithZodSchema(t,r){return isZodLike(t)?toZodGuardResult(t.safeParse(r??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(t)?function validateWithZodShape(t,r){const s=function normalizeZodShapeArgs(t,r){const s=isRecord(r)?r:{};return e(t,s,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(t,r),a={},o=[];for(const[e,r]of Object.entries(t)){const t=r.safeParse(s[e]);t.success?void 0!==t.data&&(a[e]=t.data):o.push(...t.error.issues.map(t=>({...t,path:[e,...t.path]})))}return o.length>0?toZodGuardResult({success:!1,error:{issues:o}}):{action:"allow",args:a}}(t,r):function isJsonObjectSchema(e){return isRecord(e)&&"object"===e.type}(t)?function validateWithJsonObjectSchema(e,t){const r=isRecord(t)?t:{},s=[];for(const t of e.required??[])t in r||s.push({path:`$.${t}`,message:"Required property is missing",expected:"required"});for(const[t,a]of Object.entries(e.properties??{}))t in r&&void 0!==a.type&&jsonType(r[t])!==a.type&&s.push({path:`$.${t}`,message:`Expected ${a.type}`,expected:a.type,actual:r[t]});return s.length>0?{action:"reject",reason:"JSON schema validation failed",issues:s}:{action:"allow",args:r}}(t,r):void 0}export function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function toZodGuardResult(e){return e.success?{action:"allow",args:e.data}:{action:"reject",reason:"Zod schema validation failed",issues:e.error.issues.map(e=>{return{path:(t=e.path,t.length>0?`$.${t.map(String).join(".")}`:"$"),message:e.message,expected:"schema"};var t})}}function isZodLike(e){return isRecord(e)&&"function"==typeof e.safeParse}function jsonType(e){return Array.isArray(e)?"array":null===e?"null":typeof e}
@@ -8,8 +8,14 @@ export type RawDocument = {
8
8
  };
9
9
  spec?: unknown;
10
10
  };
11
+ export type WorkspaceToolSet = {
12
+ id: string;
13
+ tools: string[];
14
+ metadata?: Record<string, unknown>;
15
+ };
11
16
  export declare function compileRuntime(document: RawDocument): WorkspaceRuntimePolicy;
12
- export declare function compileAgent(document: RawDocument, sourcePath: string): WorkspaceAgent;
17
+ export declare function compileAgent(document: RawDocument, sourcePath: string, toolSets?: Map<string, WorkspaceToolSet>): WorkspaceAgent;
18
+ export declare function compileToolSets(document: RawDocument): WorkspaceToolSet[];
13
19
  export declare function compileModel(document: RawDocument): WorkspaceModel;
14
20
  export declare function compileModelSpec(spec: Record<string, unknown>, fallback?: string): WorkspaceModel;
15
21
  export declare function compileTool(document: RawDocument, sourcePath?: string): WorkspaceTool;
@@ -1 +1 @@
1
- import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.tracing&&r.tracing?{tracing:r.tracing}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."object"==typeof r.workspaceValidation&&r.workspaceValidation?{workspaceValidation:r.workspaceValidation}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r){const t=assertRecord(e.spec,"Agent.spec"),o=readName(e),n=readOptionalString(t.backend);if(!n)throw new Error(`Agent ${o} requires spec.backend`);const i="object"==typeof t.config&&t.config?t.config:{},a="string"==typeof t.systemPrompt?t.systemPrompt:"string"==typeof i.systemPrompt?i.systemPrompt:void 0;return{id:o,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:n,..."string"==typeof t.modelRef&&t.modelRef.trim()?{modelRef:(s=t.modelRef,s.replace(/^[^/]+\//u,""))}:{},...void 0!==a?{systemPrompt:a}:{},tools:toStringArray(t.tools),skills:toStringArray(t.skills),memory:Array.isArray(t.memory)?t.memory:[],subagents:toStringArray(t.subagents),...void 0!==t.edges?{edges:readAgentEdges(t.edges,o)}:{},config:i};var s}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e,r){const t=assertRecord(e.spec,"Tool.spec");return{id:readName(e),...r?{sourcePath:r}:{},..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},...void 0!==t.outputSchema?{outputSchema:t.outputSchema}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(e){const r=assertRecord(e.spec,"Memory.spec"),t=readName(e),o={...r};return delete o.provider,delete o.profile,delete o.mode,delete o.enabled,delete o.prompts,{id:t,provider:readOptionalString(r.provider)??"langmem",...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...readOptionalString(r.mode)?{mode:readOptionalString(r.mode)}:{},enabled:!1!==r.enabled,..."object"==typeof r.prompts&&r.prompts?{prompts:readMemoryPrompts(r.prompts)}:{},...Object.keys(o).length>0?{config:o}:{}}}function readWorkflowRouting(e){const r=assertRecord(e,"Runtime.spec.workflowRouting"),t=void 0===r.routes?void 0:function readWorkflowRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.workflowRouting.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.workflowId);if(!t||!o)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:t,workflowId:o,...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{},..."object"==typeof r.metadata&&r.metadata?{metadata:r.metadata}:{}}})}(r.routes);return{...readOptionalString(r.defaultWorkflowId)?{defaultWorkflowId:readOptionalString(r.defaultWorkflowId)}:{},...t?{routes:t}:{}}}function readAdapters(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(readAdapter)}function readAgentEdges(e,r){if(!Array.isArray(e))throw new Error(`Agent ${r} spec.edges must be an array`);return e.map(e=>{const t=assertRecord(e,`Agent ${r} spec.edges[]`),o=readOptionalString(t.from),n=readOptionalString(t.to);if(!o||!n)throw new Error(`Agent ${r} spec.edges[] requires from and to`);return{from:o,to:n,...readOptionalString(t.condition)?{condition:readOptionalString(t.condition)}:{}}})}function readAdapter(e){if("string"==typeof e&&e.trim())return{name:e.trim()};const r=assertRecord(e,"Runtime.spec.adapters[]"),t=readOptionalString(r.name)??readOptionalString(r.id)??readOptionalString(r.backend);if(!t)throw new Error("Runtime.spec.adapters[] requires name");return{name:t,..."boolean"==typeof r.enabled?{enabled:r.enabled}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}}function readMemoryPrompts(e){const r=assertRecord(e,"Memory.spec.prompts");return{...readOptionalString(r.semantic)?{semantic:readOptionalString(r.semantic)}:{},...readOptionalString(r.episodic)?{episodic:readOptionalString(r.episodic)}:{},...readOptionalString(r.procedural)?{procedural:readOptionalString(r.procedural)}:{}}}
1
+ import{assertSpecDrivenWorkflowPolicy as e,createSpecDrivenWorkflowPolicy as r}from"@stable-harness/core";function assertRecord(e,r){if("object"!=typeof e||null===e||Array.isArray(e))throw new Error(`${r} must be an object`);return e}function readName(e,r){const t=e.metadata?.name;if("string"==typeof t&&t.trim())return t.trim();if(r)return r;throw new Error(`Document kind ${String(e.kind)} requires metadata.name`)}function readDescription(e){const r=e.metadata?.description;return"string"==typeof r&&r.trim()?r.trim():void 0}function readOptionalString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function toStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e&&e.trim().length>0):[]}function resolveValue(e){if("string"!=typeof e)return e;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]??"":e}export function compileRuntime(e){const r=assertRecord(e.spec,"Runtime.spec"),t=assertRecord(r.routing??{},"Runtime.spec.routing");return{defaultAgentId:"string"==typeof t.defaultAgentId&&t.defaultAgentId.trim()?t.defaultAgentId.trim():"orchestra",...void 0!==t.routes?{routes:readAgentRoutes(t.routes)}:{},...readOptionalString(r.workspaceId)?{workspaceId:readOptionalString(r.workspaceId)}:{},...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...void 0!==r.adapters?{adapters:readAdapters(r.adapters)}:{},..."object"==typeof r.workflowRouting&&r.workflowRouting?{workflowRouting:readWorkflowRouting(r.workflowRouting)}:{},..."object"==typeof r.specDrivenWorkflow&&r.specDrivenWorkflow?{specDrivenWorkflow:readSpecDrivenWorkflow(r.specDrivenWorkflow)}:{},..."object"==typeof r.approvals&&r.approvals?{approvals:r.approvals}:{},..."object"==typeof r.recovery&&r.recovery?{recovery:r.recovery}:{},..."object"==typeof r.retry&&r.retry?{retry:r.retry}:{},..."object"==typeof r.toolGateway&&r.toolGateway?{toolGateway:r.toolGateway}:{},..."object"==typeof r.memory&&r.memory?{memory:r.memory}:{},..."object"==typeof r.protocols&&r.protocols?{protocols:r.protocols}:{},..."object"==typeof r.tracing&&r.tracing?{tracing:r.tracing}:{},..."object"==typeof r.progress&&r.progress?{progress:r.progress}:{},..."object"==typeof r.cli&&r.cli?{cli:r.cli}:{},..."string"==typeof r.quality||"object"==typeof r.quality&&r.quality?{quality:r.quality}:{},..."object"==typeof r.workspaceValidation&&r.workspaceValidation?{workspaceValidation:r.workspaceValidation}:{},..."object"==typeof r.responseLanguage&&r.responseLanguage?{responseLanguage:r.responseLanguage}:{},..."object"==typeof r.responsePresentation&&r.responsePresentation?{responsePresentation:r.responsePresentation}:{}}}function readAgentRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.routing.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.routing.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.agentId);if(!t||!o)throw new Error("Runtime.spec.routing.routes[] requires id and agentId");const n=void 0===r.keywords?void 0:assertStringArray(r.keywords,"Runtime.spec.routing.routes[].keywords"),i=readOptionalString(r.pattern);if(!(n&&0!==n.length||i))throw new Error("Runtime.spec.routing.routes[] requires keywords or pattern");return{id:t,agentId:o,...n&&n.length>0?{keywords:n}:{},...i?{pattern:i}:{},...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{}}})}function assertStringArray(e,r){if(!Array.isArray(e))throw new Error(`${r} must be an array`);return e.map(e=>{if("string"!=typeof e||!e.trim())throw new Error(`${r} must contain non-empty strings`);return e.trim()})}function readSpecDrivenWorkflow(t){const o=assertRecord(t,"Runtime.spec.specDrivenWorkflow"),n=r({enabled:!0===o.enabled,constitution:readOptionalString(o.constitution),artifactsDir:readOptionalString(o.artifactsDir),phases:void 0===o.phases?void 0:readSpecDrivenPhases(o.phases),..."object"==typeof o.gates&&o.gates?{gates:o.gates}:{},..."object"==typeof o.config&&o.config?{config:o.config}:{}});return e(n),n}function readSpecDrivenPhases(e){if(!Array.isArray(e))throw new Error("Runtime.spec.specDrivenWorkflow.phases must be an array");return e.map(e=>{if("string"==typeof e&&e.trim())return{id:e.trim()};const r=assertRecord(e,"Runtime.spec.specDrivenWorkflow.phases[]"),t=readOptionalString(r.id);if(!t)throw new Error("Runtime.spec.specDrivenWorkflow.phases[] requires id");return{id:t,...readOptionalString(r.artifactKind)?{artifactKind:readOptionalString(r.artifactKind)}:{},..."boolean"==typeof r.required?{required:r.required}:{},...readOptionalString(r.gate)?{gate:readOptionalString(r.gate)}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}})}export function compileAgent(e,r,t=new Map){const o=assertRecord(e.spec,"Agent.spec"),n=readName(e),i=readOptionalString(o.backend);if(!i)throw new Error(`Agent ${n} requires spec.backend`);const a="object"==typeof o.config&&o.config?o.config:{},s="string"==typeof o.systemPrompt?o.systemPrompt:"string"==typeof a.systemPrompt?a.systemPrompt:void 0,p=function readAgentToolBindings(e,r=new Map){return Array.isArray(e)?e.flatMap(e=>function readAgentToolBinding(e,r){return"string"==typeof e&&e.trim()?[toolBinding(e.trim(),void 0,r)]:"object"!=typeof e||null===e||Array.isArray(e)?[]:Object.entries(e).filter(([e])=>e.trim().length>0).map(([e,t])=>toolBinding(e.trim(),function readToolRefMetadata(e){if("object"==typeof e&&null!==e&&!Array.isArray(e))return e}(t),r))}(e,r)):[]}(o.tools,t);return{id:n,...readDescription(e)?{description:readDescription(e)}:{},sourcePath:r,backend:i,...p.some(e=>"toolset"===e.kind||e.metadata)?{metadata:{toolBindings:p}}:{},..."string"==typeof o.modelRef&&o.modelRef.trim()?{modelRef:(c=o.modelRef,c.replace(/^[^/]+\//u,""))}:{},...void 0!==s?{systemPrompt:s}:{},tools:(d=p.flatMap(e=>e.tools),[...new Set(d)]),skills:toStringArray(o.skills),memory:Array.isArray(o.memory)?o.memory:[],subagents:toStringArray(o.subagents),...void 0!==o.edges?{edges:readAgentEdges(o.edges,n)}:{},config:a};var d,c}export function compileToolSets(e){return Array.isArray(e.spec)?e.spec.map(e=>compileToolSetSpec(assertRecord(e,"ToolSets.spec[]"))):[compileToolSetSpec(assertRecord(e.spec,"ToolSets.spec"),readName(e,""))]}function compileToolSetSpec(e,r){const t=readOptionalString(e.name)??r;if(!t)throw new Error("ToolSets.spec[] requires name");const o=assertStringArray(e.tools,`ToolSet ${t}.tools`),n={...e};return delete n.name,delete n.tools,{id:t,tools:o,...Object.keys(n).length>0?{metadata:n}:{}}}function toolBinding(e,r,t){const o=t.get(e);return o?{ref:e,kind:"toolset",tools:o.tools,...r?{metadata:r}:{}}:{ref:e,kind:"tool",tools:[e],...r?{metadata:r}:{}}}export function compileModel(e){return compileModelSpec(assertRecord(e.spec,"Model.spec"),readName(e))}export function compileModelSpec(e,r){const t="string"==typeof e.name&&e.name.trim()?e.name.trim():r??"default",o=resolveValue(e.provider),n=resolveValue(e.model),i="string"==typeof o&&o.trim()?o.trim():"unknown",a="string"==typeof n&&n.trim()?n.trim():t,s={...e};return delete s.name,delete s.provider,delete s.model,{id:t,provider:i,model:a,config:Object.fromEntries(Object.entries(s).map(([e,r])=>[e,resolveValue(r)]))}}export function compileTool(e,r){const t=assertRecord(e.spec,"Tool.spec");return{id:readName(e),...r?{sourcePath:r}:{},..."string"==typeof t.description?{description:t.description}:{},...void 0!==t.schema?{schema:t.schema}:{},...void 0!==t.outputSchema?{outputSchema:t.outputSchema}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{},..."string"==typeof t.implementation?{implementation:t.implementation}:{}}}export function compileMemory(e){const r=assertRecord(e.spec,"Memory.spec"),t=readName(e),o={...r};return delete o.provider,delete o.profile,delete o.mode,delete o.enabled,delete o.prompts,{id:t,provider:readOptionalString(r.provider)??"langmem",...readOptionalString(r.profile)?{profile:readOptionalString(r.profile)}:{},...readOptionalString(r.mode)?{mode:readOptionalString(r.mode)}:{},enabled:!1!==r.enabled,..."object"==typeof r.prompts&&r.prompts?{prompts:readMemoryPrompts(r.prompts)}:{},...Object.keys(o).length>0?{config:o}:{}}}function readWorkflowRouting(e){const r=assertRecord(e,"Runtime.spec.workflowRouting"),t=void 0===r.routes?void 0:function readWorkflowRoutes(e){if(!Array.isArray(e))throw new Error("Runtime.spec.workflowRouting.routes must be an array");return e.map(e=>{const r=assertRecord(e,"Runtime.spec.workflowRouting.routes[]"),t=readOptionalString(r.id),o=readOptionalString(r.workflowId);if(!t||!o)throw new Error("Runtime.spec.workflowRouting.routes[] requires id and workflowId");return{id:t,workflowId:o,...readOptionalString(r.description)?{description:readOptionalString(r.description)}:{},..."object"==typeof r.metadata&&r.metadata?{metadata:r.metadata}:{}}})}(r.routes);return{...readOptionalString(r.defaultWorkflowId)?{defaultWorkflowId:readOptionalString(r.defaultWorkflowId)}:{},...t?{routes:t}:{}}}function readAdapters(e){if(!Array.isArray(e))throw new Error("Runtime.spec.adapters must be an array");return e.map(readAdapter)}function readAgentEdges(e,r){if(!Array.isArray(e))throw new Error(`Agent ${r} spec.edges must be an array`);return e.map(e=>{const t=assertRecord(e,`Agent ${r} spec.edges[]`),o=readOptionalString(t.from),n=readOptionalString(t.to);if(!o||!n)throw new Error(`Agent ${r} spec.edges[] requires from and to`);return{from:o,to:n,...readOptionalString(t.condition)?{condition:readOptionalString(t.condition)}:{}}})}function readAdapter(e){if("string"==typeof e&&e.trim())return{name:e.trim()};const r=assertRecord(e,"Runtime.spec.adapters[]"),t=readOptionalString(r.name)??readOptionalString(r.id)??readOptionalString(r.backend);if(!t)throw new Error("Runtime.spec.adapters[] requires name");return{name:t,..."boolean"==typeof r.enabled?{enabled:r.enabled}:{},..."object"==typeof r.config&&r.config?{config:r.config}:{}}}function readMemoryPrompts(e){const r=assertRecord(e,"Memory.spec.prompts");return{...readOptionalString(r.semantic)?{semantic:readOptionalString(r.semantic)}:{},...readOptionalString(r.episodic)?{episodic:readOptionalString(r.episodic)}:{},...readOptionalString(r.procedural)?{procedural:readOptionalString(r.procedural)}:{}}}
@@ -1 +1 @@
1
- import{readFile as o}from"node:fs/promises";import e from"node:path";import{parseAllDocuments as s}from"yaml";import{discoverModuleTools as t,discoverSkills as r,listYamlFiles as a}from"./discovery.js";import{compileAgent as n,compileMemory as l,compileModel as i,compileModelSpec as c,compileRuntime as f,compileTool as u}from"./documents.js";import{compileWorkflow as m,validateWorkflows as w}from"./workflows.js";import{compileEvaluation as d,validateEvaluations as p}from"./evaluations.js";import{assertWorkspaceBoundaryDiagnostics as k,scanWorkspaceBoundaries as g}from"./boundary-scan.js";import{assertWorkspaceToolQualityDiagnostics as v,scanWorkspaceToolQuality as y}from"@stable-harness/core";export async function loadWorkspaceFromYaml(n){const l=e.join(n,"config"),i=await a(l),c=[],f=new Map,u=new Map,m=new Map,d=new Map,M=new Map,W=new Map,h=new Map;for(const e of i){const t=await o(e,"utf8"),r=s(t).map(o=>o.toJSON()).filter(o=>null!==o);for(const o of r)collectWorkspaceDocument(o,e,{runtimeDocs:c,agents:f,models:u,tools:m,memories:M,workflows:W,evaluations:h})}for(const o of await t(n))m.has(o.id)||m.set(o.id,o);for(const o of await r(n))d.set(o.id,o);const R=c.at(-1)??{defaultAgentId:"orchestra"};w({workflows:W,agents:f,tools:m,skills:d}),p({evaluations:h,agents:f,tools:m,workflows:W}),function validateAgentRouting(o,e){for(const s of o.routes??[])if(!e.has(s.agentId))throw new Error(`Runtime routing route ${s.id} references unknown agent ${s.agentId}`)}(R,f),function validateWorkflowRouting(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}`)}}(R,W);const A={root:n,runtime:R,agents:f,models:u,tools:m,skills:d,memories:M,workflows:W,evaluations:h},I=g(A);k(I);const O=y(A,R.workspaceValidation?.toolQuality);return v(O),{...A,...[...I??[],...O].length>0?{diagnostics:[...I,...O]}:{}}}function collectWorkspaceDocument(o,e,s){if("string"==typeof o.kind)switch(o.kind){case"Runtime":return void s.runtimeDocs.push(f(o));case"Agent":return collectOne(s.agents,n(o,e));case"Model":return collectOne(s.models,i(o));case"Models":return function collectModelSpecs(o,e){if(Array.isArray(o.spec))for(const s of o.spec)if("object"==typeof s&&null!==s&&!Array.isArray(s)){const o=c(s);e.set(o.id,o)}}(o,s.models);case"Tool":return collectOne(s.tools,u(o,e));case"Memory":return collectOne(s.memories,l(o));case"Workflow":return collectOne(s.workflows,m(o,e));case"Evaluation":return collectOne(s.evaluations,d(o,e));default:return}}function collectOne(o,e){o.set(e.id,e)}
1
+ import{readFile as o}from"node:fs/promises";import e from"node:path";import{parseAllDocuments as t}from"yaml";import{discoverModuleTools as s,discoverSkills as n,listYamlFiles as r}from"./discovery.js";import{compileAgent as l,compileMemory as a,compileModel as i,compileModelSpec as c,compileRuntime as f,compileTool as u,compileToolSets as m}from"./documents.js";import{compileWorkflow as d,validateWorkflows as w}from"./workflows.js";import{compileEvaluation as p,validateEvaluations as k}from"./evaluations.js";import{assertWorkspaceBoundaryDiagnostics as g,scanWorkspaceBoundaries as v}from"./boundary-scan.js";import{assertWorkspaceToolQualityDiagnostics as h,scanWorkspaceToolQuality as y}from"@stable-harness/core";export async function loadWorkspaceFromYaml(l){const a=e.join(l,"config"),i=await r(a),c=[],f=new Map,u=new Map,m=new Map,d=new Map,p=new Map,M=new Map,W=new Map,A=new Map,S=[];for(const e of i){const s=await o(e,"utf8"),n=t(s).map(o=>o.toJSON()).filter(o=>null!==o);S.push(...n.map(o=>({document:o,file:e})))}for(const{document:o}of S)collectToolSetDocument(o,A);for(const{document:o,file:e}of S)collectWorkspaceDocument(o,e,{runtimeDocs:c,agents:f,models:u,tools:m,memories:p,workflows:M,evaluations:W,toolSets:A});for(const o of await s(l))m.has(o.id)||m.set(o.id,o);for(const o of await n(l))d.set(o.id,o);const T=c.at(-1)??{defaultAgentId:"orchestra"};w({workflows:M,agents:f,tools:m,skills:d}),k({evaluations:W,agents:f,tools:m,workflows:M}),function validateAgentTools(o,e){for(const t of o.values()){const o=t.tools.filter(o=>!e.has(o));if(o.length>0)throw new Error(`Agent ${t.id} references unknown tools ${o.join(", ")}`)}}(f,m),function validateAgentRouting(o,e){for(const t of o.routes??[])if(!e.has(t.agentId))throw new Error(`Runtime routing route ${t.id} references unknown agent ${t.agentId}`)}(T,f),function validateWorkflowRouting(o,e){const t=o.workflowRouting;if(t){if(t.defaultWorkflowId&&!e.has(t.defaultWorkflowId))throw new Error(`Runtime workflowRouting.defaultWorkflowId references unknown workflow ${t.defaultWorkflowId}`);for(const o of t.routes??[])if(!e.has(o.workflowId))throw new Error(`Runtime workflowRouting route ${o.id} references unknown workflow ${o.workflowId}`)}}(T,M);const R={root:l,runtime:T,agents:f,models:u,tools:m,skills:d,memories:p,workflows:M,evaluations:W},D=v(R);g(D);const I=y(R,T.workspaceValidation?.toolQuality);return h(I),{...R,...[...D??[],...I].length>0?{diagnostics:[...D,...I]}:{}}}function collectWorkspaceDocument(o,e,t){if("string"==typeof o.kind)switch(o.kind){case"Runtime":return void t.runtimeDocs.push(f(o));case"Agent":return collectOne(t.agents,l(o,e,t.toolSets));case"Model":return collectOne(t.models,i(o));case"Models":return function collectModelSpecs(o,e){if(Array.isArray(o.spec))for(const t of o.spec)if("object"==typeof t&&null!==t&&!Array.isArray(t)){const o=c(t);e.set(o.id,o)}}(o,t.models);case"Tool":return collectOne(t.tools,u(o,e));case"Memory":return collectOne(t.memories,a(o));case"Workflow":return collectOne(t.workflows,d(o,e));case"Evaluation":return collectOne(t.evaluations,p(o,e));default:return}}function collectToolSetDocument(o,e){if("ToolSets"===o.kind||"ToolSet"===o.kind)for(const t of m(o))e.set(t.id,t)}function collectOne(o,e){o.set(e.id,e)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -23,8 +23,6 @@
23
23
  "docs/**/*.md",
24
24
  "dist/*.js",
25
25
  "dist/*.d.ts",
26
- "dist/compat/**/*.js",
27
- "dist/compat/**/*.d.ts",
28
26
  "dist/runtime/**/*.js",
29
27
  "dist/runtime/**/*.d.ts",
30
28
  "dist/workspace/**/*.js",
@@ -34,12 +32,11 @@
34
32
  "packages/*/package.json"
35
33
  ],
36
34
  "bin": {
37
- "botbotgo": "dist/cli.js",
35
+ "botbotgo": "packages/cli/dist/src/cli.js",
38
36
  "stable-harness": "packages/cli/dist/src/cli.js"
39
37
  },
40
38
  "exports": {
41
39
  ".": "./dist/index.js",
42
- "./compat/agent-harness.js": "./dist/compat/agent-harness.js",
43
40
  "./tools": "./dist/tools.js",
44
41
  "./workspace/compile.js": "./dist/workspace/compile.js",
45
42
  "./runtime/skills/skill-metadata.js": "./dist/runtime/skills/skill-metadata.js",
@@ -50,7 +47,7 @@
50
47
  },
51
48
  "scripts": {
52
49
  "build": "tsc -b && npm run build:chmod",
53
- "build:chmod": "chmod +x dist/cli.js packages/cli/dist/src/cli.js",
50
+ "build:chmod": "chmod +x packages/cli/dist/src/cli.js",
54
51
  "check": "tsc -b --pretty false",
55
52
  "check:rules": "node scripts/check-project-rules.mjs",
56
53
  "compare:tool-calling": "node scripts/benchmarks/compare-granite-tool-calling.mjs",
@@ -62,7 +59,7 @@
62
59
  "benchmark:tool-guard:matrix": "node scripts/benchmarks/tool-argument-guard-matrix.mjs",
63
60
  "test:langmem:sqlite:e2e": "node scripts/run-langmem-sqlite-e2e.mjs",
64
61
  "validate:workspace": "node scripts/validate-workspace.mjs",
65
- "test": "rm -rf dist/test && tsc -b test/tsconfig.json && npm run build:chmod && node --test dist/test/*.test.js dist/test/adapter/*.test.js dist/test/adapter/*/*.test.js dist/test/compat/*.test.js dist/test/evaluation/*.test.js dist/test/memory/*.test.js dist/test/protocol/*.test.js dist/test/retry/*.test.js dist/test/runtime/*.test.js dist/test/runtime/*/*.test.js dist/test/sdk/*.test.js dist/test/workspace/*.test.js",
62
+ "test": "rm -rf dist/test && tsc -b test/tsconfig.json && npm run build:chmod && node --test dist/test/*.test.js dist/test/adapter/*.test.js dist/test/adapter/*/*.test.js dist/test/evaluation/*.test.js dist/test/memory/*.test.js dist/test/protocol/*.test.js dist/test/retry/*.test.js dist/test/runtime/*.test.js dist/test/runtime/*/*.test.js dist/test/sdk/*.test.js dist/test/workspace/*.test.js dist/test/workspace/*/*.test.js",
66
63
  "test:langmem:maintenance:e2e": "node scripts/run-langmem-maintenance-e2e.mjs",
67
64
  "test:skill-mining:e2e": "node scripts/run-skill-candidate-mining-e2e.mjs",
68
65
  "prepublishOnly": "npm run build && npm run release:check-package",
@@ -1 +1 @@
1
- import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as d}from"./memory.js";import{buildDeepAgentRequest as c}from"./internal/messages.js";import{createRawToolCallParserMiddleware as l}from"./internal/raw-tool-call-parser.js";import{createBackendModel as u}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as m}from"./retry-policy.js";import{streamDeepAgentResult as g}from"./internal/stream-events.js";export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,skills:t.agent.skills,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const r=e.createDeepAgent?void 0:await async function loadDeepAgentsModule(){try{return await async function importOptionalPackage(e){return import(e)}("deepagents")}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${function formatError(e){return e instanceof Error?e.message:String(e)}(e)}`)}}(),n=e.createDeepAgent??function readCreateDeepAgent(e){const t=e?.createDeepAgent;if("function"==typeof t)return t;throw new Error("DeepAgents package does not export createDeepAgent.")}(r),o=n(function buildDeepAgentParams(e,t,r){const n={...readDeepAgentsConfig(t),...readDeepAgentsConfig(e.agent.config.deepagents)},o=resolveDeepAgentsSkills(e,e.agent),a=n.permissions??s(e,e.agent),p=requestScopedRepeatState(e,e.agent.id);return pruneUndefined({name:e.agent.id,model:n.model??resolveAgentModel(e,e.agent),systemPrompt:buildSystemPrompt(e,e.agent),backend:n.backend??resolveDeepAgentsBackend(e,r,o),checkpointer:n.checkpointer,store:n.store,middleware:mergeMiddleware(e,e.agent,n.middleware,p),responseFormat:n.responseFormat,contextSchema:n.contextSchema,interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:a,tools:i(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(e,e.agent,n),p),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),o=n.permissions??s(e,r),a=scopedInput(e,r),p=requestScopedRepeatState(e,t);return pruneUndefined({name:t,description:r?.description??readString(r?.config.description)??r?.id,systemPrompt:buildSystemPrompt(e,r),model:n.model??(r?resolveAgentModel(e,r):void 0),middleware:mergeMiddleware(a,r,n.middleware,p),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:o,responseFormat:n.responseFormat,tools:i(e,t,r?.tools??[],resolveAgentRepairModel(a,r,n),p),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:o})}(t,e.config,r)),a=c(t),d=function buildDeepAgentInvokeConfig(e){return pruneUndefined({recursionLimit:readNumber(readDeepAgentsConfig(e.config.deepagents).recursionLimit)??readNumber(e.config.recursionLimit)})}(t.agent);if(!0===t.request.metadata?.openaiStream&&o.streamEvents){const e=await o.streamEvents(a,{version:"v3",...d});return g(t,e,p)}const l=await o.invoke(a,d);return p(l)}}}function buildSystemPrompt(e,t){const n=t?.systemPrompt??readString(t?.config.systemPrompt);return r({workspace:e.workspace,request:e.request,agent:t},n)}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=[...readAgentMemorySources(e.workspace.root,t),...d(e.workspace).map(e=>`/memories/${e.id}.md`)],o=[...new Set(n)];return o.length>0?o:void 0}function readAgentMemorySources(e,t){return(t?.memory??[]).flatMap(t=>"string"==typeof t&&t.trim()?[backendMemorySourcePath(e,t.trim())]:isRecord(t)&&"string"==typeof t.path&&t.path.trim()?[backendMemorySourcePath(e,t.path.trim())]:[])}function backendMemorySourcePath(e,r){if(r.startsWith("/"))return r;if(t.isAbsolute(r)){const n=t.relative(e,r);return n&&!n.startsWith("..")?`/${n.split(t.sep).join("/")}`:canonicalPath(r)}const n=r.split(t.sep).join("/");return n.startsWith("/")?n:`/${n}`}function resolveDeepAgentsSkills(e,r){const n=readDeepAgentsStringArray(r?.config,"skills");if(n)return n;const o=[...new Set((r?.skills??[]).map(t=>e.workspace.skills.get(t)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(r=>function backendSkillSourcePath(e,r){const n=t.dirname(t.dirname(r)),o=t.relative(e,n);return!o||o.startsWith("..")||t.isAbsolute(o)?""===o?"/":canonicalPath(n):`/${o.split(t.sep).join("/")}`}(e.workspace.root,r)))];return o.length>0?o:void 0}function resolveDeepAgentsBackend(e,t,r){if(t?.FilesystemBackend&&r&&0!==r.length)return()=>new t.FilesystemBackend({rootDir:e.workspace.root})}function mergeMiddleware(e,t,r,s=a(e.workspace.runtime.toolGateway)){const i=Array.isArray(r)?r:[],p=scopedInput(e,t),d=new Set,c=readDeepAgentsConfig(t?.config.deepagents);return[o(p,{observedToolIds:d,repeatState:s,repairModel:resolveAgentRepairModel(p,t,c)}),n(p,{repeatState:s}),...m(e.workspace.runtime.retry),...i,l(p)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=a(e.workspace.runtime.toolGateway);return e.requestState&&o&&e.requestState.set(r,o),o}function scopedInput(e,t){return t?{...e,agent:t}:e}function resolveAgentModel(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?u(r):void 0}function resolveAgentRepairModel(e,t,r){const n=r.model;if(isRepairModel(n))return n;if(!t)return;const o=resolveAgentModel(e,t);return isRepairModel(o)?o:void 0}function readDeepAgentsConfig(e){return isRecord(e)?e:{}}function readDeepAgentsStringArray(e,t){const r=isRecord(e)?e:{},n=readDeepAgentsConfig(r.deepagents),o="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of o){const t=readStringArray(n[e]);if(t)return t}return readStringArray(r[t])}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function canonicalPath(t){try{return e.native(t)}catch{return t}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function isRepairModel(e){return"object"==typeof e&&null!==e&&"invoke"in e&&"function"==typeof e.invoke}
1
+ import{realpathSync as e}from"node:fs";import t from"node:path";import{buildRuntimeSystemPrompt as r}from"@stable-harness/core";import{createBuiltinToolPolicyMiddleware as n,createObserverMiddleware as o}from"./internal/builtin-tool-policy.js";import{resolveFilesystemPermissions as s}from"./internal/builtin/permissions.js";import{createToolRepeatState as a}from"@stable-harness/core";import{buildGatewayTools as i,stringifyDeepAgentResult as p}from"./internal/gateway-tools.js";import{resolveDeepAgentsNativeMemories as d}from"./memory.js";import{buildDeepAgentRequest as c}from"./internal/messages.js";import{createRawToolCallParserMiddleware as l}from"./internal/raw-tool-call-parser.js";import{createBackendModel as u}from"./model-providers.js";import{createDeepAgentsRetryMiddleware as m}from"./retry-policy.js";import{streamDeepAgentResult as g}from"./internal/stream-events.js";import{cleanupDeepAgentsRuntimeSubstrate as f,resolveDeepAgentsRuntimeSubstrate as y}from"./internal/substrate/runtime.js";export function createDeepAgentsAdapter(e={}){return{name:"deepagents",canRun:e=>"deepagents"===e.backend,async run(t){if(t.emit({type:"runtime.adapter.event",requestId:t.requestId,sessionId:t.sessionId,agentId:t.agent.id,event:{adapter:"deepagents",phase:"agent.handoff",modelRef:t.agent.modelRef,tools:t.agent.tools,skills:t.agent.skills,subagents:t.agent.subagents}}),e.runner)return e.runner(t);const r=e.createDeepAgent?void 0:await async function loadDeepAgentsModule(){try{return await async function importOptionalPackage(e){return import(e)}("deepagents")}catch(e){throw new Error(`DeepAgents package is required for the default adapter path: ${function formatError(e){return e instanceof Error?e.message:String(e)}(e)}`)}}(),n=e.createDeepAgent??function readCreateDeepAgent(e){const t=e?.createDeepAgent;if("function"==typeof t)return t;throw new Error("DeepAgents package does not export createDeepAgent.")}(r),o={...readDeepAgentsConfig(e.config),...readDeepAgentsConfig(t.agent.config.deepagents)},a=y(t,o),d=n(function buildDeepAgentParams(e,t,r,n){const o=resolveDeepAgentsSkills(e,e.agent),a=t.permissions??s(e,e.agent),p=requestScopedRepeatState(e,e.agent.id);return pruneUndefined({name:e.agent.id,model:t.model??resolveAgentModel(e,e.agent),systemPrompt:buildSystemPrompt(e,e.agent),backend:t.backend??resolveDeepAgentsBackend(e,n,o),checkpointer:r.checkpointer,store:r.store,middleware:mergeMiddleware(e,e.agent,t.middleware,p),responseFormat:t.responseFormat,contextSchema:t.contextSchema,interruptOn:t.interruptOn,generalPurposeAgent:readBoolean(t.generalPurposeAgent),taskDescription:readString(t.taskDescription),permissions:a,tools:i(e,e.agent.id,e.agent.tools,resolveAgentRepairModel(0,e.agent,t),p),subagents:e.agent.subagents.map(t=>{const r=e.workspace.agents.get(t),n=readDeepAgentsConfig(r?.config.deepagents),o=n.permissions??s(e,r),a=scopedInput(e,r),p=requestScopedRepeatState(e,t);return pruneUndefined({name:t,description:r?.description??readString(r?.config.description)??r?.id,systemPrompt:buildSystemPrompt(e,r),model:n.model??(r?resolveAgentModel(e,r):void 0),middleware:mergeMiddleware(a,r,n.middleware,p),interruptOn:n.interruptOn,generalPurposeAgent:readBoolean(n.generalPurposeAgent),taskDescription:readString(n.taskDescription),permissions:o,responseFormat:n.responseFormat,tools:i(e,t,r?.tools??[],resolveAgentRepairModel(0,0,n),p),memory:resolveDeepAgentsMemory(e,r),skills:resolveDeepAgentsSkills(e,r)})}),memory:resolveDeepAgentsMemory(e,e.agent),skills:o})}(t,o,a,r)),l=c(t),u=function buildDeepAgentInvokeConfig(e,t){return pruneUndefined({recursionLimit:readNumber(readDeepAgentsConfig(e.config.deepagents).recursionLimit)??readNumber(e.config.recursionLimit),configurable:{thread_id:t}})}(t.agent,a.threadId);if(!0===t.request.metadata?.openaiStream&&d.streamEvents){const e=await d.streamEvents(l,{version:"v3",...u}),r=await g(t,e,p);return await f(a),r}const m=await d.invoke(l,u),A=p(m);return await f(a),A}}}function buildSystemPrompt(e,t){const n=t?.systemPrompt??readString(t?.config.systemPrompt);return r({workspace:e.workspace,request:e.request,agent:t},n)}function resolveDeepAgentsMemory(e,t){const r=readDeepAgentsStringArray(t?.config,"memory");if(r)return r;const n=[...readAgentMemorySources(e.workspace.root,t),...d(e.workspace).map(e=>`/memories/${e.id}.md`)],o=[...new Set(n)];return o.length>0?o:void 0}function readAgentMemorySources(e,t){return(t?.memory??[]).flatMap(t=>"string"==typeof t&&t.trim()?[backendMemorySourcePath(e,t.trim())]:isRecord(t)&&"string"==typeof t.path&&t.path.trim()?[backendMemorySourcePath(e,t.path.trim())]:[])}function backendMemorySourcePath(e,r){if(r.startsWith("/"))return r;if(t.isAbsolute(r)){const n=t.relative(e,r);return n&&!n.startsWith("..")?`/${n.split(t.sep).join("/")}`:canonicalPath(r)}const n=r.split(t.sep).join("/");return n.startsWith("/")?n:`/${n}`}function resolveDeepAgentsSkills(e,r){const n=readDeepAgentsStringArray(r?.config,"skills");if(n)return n;const o=[...new Set((r?.skills??[]).map(t=>e.workspace.skills.get(t)?.path).filter(e=>"string"==typeof e&&e.trim().length>0).map(r=>function backendSkillSourcePath(e,r){const n=t.dirname(t.dirname(r)),o=t.relative(e,n);return!o||o.startsWith("..")||t.isAbsolute(o)?""===o?"/":canonicalPath(n):`/${o.split(t.sep).join("/")}`}(e.workspace.root,r)))];return o.length>0?o:void 0}function resolveDeepAgentsBackend(e,t,r){if(t?.FilesystemBackend&&r&&0!==r.length)return()=>new t.FilesystemBackend({rootDir:e.workspace.root})}function mergeMiddleware(e,t,r,s=a(e.workspace.runtime.toolGateway)){const i=Array.isArray(r)?r:[],p=scopedInput(e,t),d=new Set,c=readDeepAgentsConfig(t?.config.deepagents);return[o(p,{observedToolIds:d,repeatState:s,repairModel:resolveAgentRepairModel(0,0,c)}),n(p,{repeatState:s}),...m(e.workspace.runtime.retry),...i,l(p)]}function requestScopedRepeatState(e,t){const r=`deepagents.repeat.${t}`,n=e.requestState?.get(r);if(n)return n;const o=a(e.workspace.runtime.toolGateway);return e.requestState&&o&&e.requestState.set(r,o),o}function scopedInput(e,t){return t?{...e,agent:t}:e}function resolveAgentModel(e,t){const r=t.modelRef?e.workspace.models.get(t.modelRef):void 0;return r?u(r):void 0}function resolveAgentRepairModel(e,t,r){const n=r.model;return function isRepairModel(e){return"object"==typeof e&&null!==e&&"invoke"in e&&"function"==typeof e.invoke}(n)?n:void 0}function readDeepAgentsConfig(e){return isRecord(e)?e:{}}function readDeepAgentsStringArray(e,t){const r=isRecord(e)?e:{},n=readDeepAgentsConfig(r.deepagents),o="memory"===t?["memory","memorySources"]:["skills","skillSources"];for(const e of o){const t=readStringArray(n[e]);if(t)return t}return readStringArray(r[t])}function pruneUndefined(e){return Object.fromEntries(Object.entries(e).filter(([,e])=>void 0!==e))}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readStringArray(e){return Array.isArray(e)?e.filter(e=>"string"==typeof e):void 0}function canonicalPath(t){try{return e.native(t)}catch{return t}}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -0,0 +1,10 @@
1
+ import { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
2
+ export type DeepAgentsCheckpointProvider = "sqlite" | "file";
3
+ export type DeepAgentsCheckpointOptions = {
4
+ provider: DeepAgentsCheckpointProvider;
5
+ path: string;
6
+ busyTimeoutMs?: number;
7
+ wal?: boolean;
8
+ synchronous?: "off" | "normal" | "full";
9
+ };
10
+ export declare function createDeepAgentsCheckpointer(options: DeepAgentsCheckpointOptions): BaseCheckpointSaver;
@@ -0,0 +1 @@
1
+ import{existsSync as e,mkdirSync as t,readFileSync as i,writeFileSync as a}from"node:fs";import c from"node:path";import{DatabaseSync as n}from"node:sqlite";import{BaseCheckpointSaver as r,WRITES_IDX_MAP as s,copyCheckpoint as d,getCheckpointId as o}from"@langchain/langgraph-checkpoint";export function createDeepAgentsCheckpointer(e){return"file"===e.provider?new JsonFileCheckpointSaver(e.path):new SqliteCheckpointSaver(e)}class JsonFileCheckpointSaver extends r{filePath;constructor(e){super(),this.filePath=e}async getTuple(e){return tupleFromState(this.state(),e,this)}async*list(e,t){for(const i of listCheckpoints(this.state(),e,t))yield hydrateTuple(i,this.state().writes,this)}async put(e,t,i){const a=requiredThreadId(e,"put checkpoint"),c=checkpointNamespace(e),n=checkpointConfig(a,c,t.id),r=this.state(),s={threadId:a,namespace:c,checkpointId:t.id,checkpoint:await dump(this,d(t)),metadata:await dump(this,i),parentCheckpointId:o(e)||void 0};return r.checkpoints=r.checkpoints.filter(e=>!function sameCheckpoint(e,t){return e.threadId===t.threadId&&e.namespace===t.namespace&&e.checkpointId===t.checkpointId}(e,s)),r.checkpoints.push(s),this.write(r),n}async putWrites(e,t,i){const a=requiredThreadId(e,"put writes"),c=requiredCheckpointId(e),n=checkpointNamespace(e),r=this.state();for(let e=0;e<t.length;e+=1){const[d,o]=t[e],h=s[d]??e,p=r.writes.some(e=>sameWrite(e,{threadId:a,namespace:n,checkpointId:c,taskId:i,idx:h}));h>=0&&p||(r.writes=r.writes.filter(e=>!sameWrite(e,{threadId:a,namespace:n,checkpointId:c,taskId:i,idx:h})),r.writes.push({threadId:a,namespace:n,checkpointId:c,taskId:i,idx:h,channel:d,value:await dump(this,o)}))}this.write(r)}async deleteThread(e){const t=this.state();this.write({checkpoints:t.checkpoints.filter(t=>t.threadId!==e),writes:t.writes.filter(t=>t.threadId!==e)})}state(){if(!e(this.filePath))return{checkpoints:[],writes:[]};const t=JSON.parse(i(this.filePath,"utf8"));return{checkpoints:t.checkpoints??[],writes:t.writes??[]}}write(e){t(c.dirname(this.filePath),{recursive:!0}),a(this.filePath,`${JSON.stringify(e,null,2)}\n`)}}class SqliteCheckpointSaver extends r{db;constructor(e){super(),t(c.dirname(e.path),{recursive:!0}),this.db=new n(e.path),this.db.exec(`PRAGMA busy_timeout = ${e.busyTimeoutMs??5e3}`),!1!==e.wal&&this.db.exec("PRAGMA journal_mode = WAL"),this.db.exec(`PRAGMA synchronous = ${e.synchronous?.toUpperCase()??"NORMAL"}`),this.db.exec(["CREATE TABLE IF NOT EXISTS checkpoints (thread_id TEXT NOT NULL, namespace TEXT NOT NULL, checkpoint_id TEXT NOT NULL, checkpoint TEXT NOT NULL, metadata TEXT NOT NULL, parent_checkpoint_id TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(thread_id, namespace, checkpoint_id))","CREATE TABLE IF NOT EXISTS writes (thread_id TEXT NOT NULL, namespace TEXT NOT NULL, checkpoint_id TEXT NOT NULL, task_id TEXT NOT NULL, idx INTEGER NOT NULL, channel TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY(thread_id, namespace, checkpoint_id, task_id, idx))","CREATE INDEX IF NOT EXISTS idx_checkpoints_thread ON checkpoints(thread_id, namespace, checkpoint_id DESC)"].join(";"))}async getTuple(e){return tupleFromState(this.state(),e,this)}async*list(e,t){const i=this.state();for(const a of listCheckpoints(i,e,t))yield hydrateTuple(a,i.writes,this)}async put(e,t,i){const a=requiredThreadId(e,"put checkpoint"),c=checkpointNamespace(e);return this.db.prepare("INSERT OR REPLACE INTO checkpoints(thread_id, namespace, checkpoint_id, checkpoint, metadata, parent_checkpoint_id) VALUES (?, ?, ?, ?, ?, ?)").run(a,c,t.id,await dump(this,d(t)),await dump(this,i),o(e)||null),checkpointConfig(a,c,t.id)}async putWrites(e,t,i){const a=requiredThreadId(e,"put writes"),c=checkpointNamespace(e),n=requiredCheckpointId(e),r=this.db.prepare("INSERT OR REPLACE INTO writes(thread_id, namespace, checkpoint_id, task_id, idx, channel, value) VALUES (?, ?, ?, ?, ?, ?, ?)");for(let e=0;e<t.length;e+=1){const[d,o]=t[e],h=s[d]??e;r.run(a,c,n,i,h,d,await dump(this,o))}}async deleteThread(e){this.db.prepare("DELETE FROM writes WHERE thread_id = ?").run(e),this.db.prepare("DELETE FROM checkpoints WHERE thread_id = ?").run(e)}state(){return{checkpoints:this.db.prepare("SELECT thread_id AS threadId, namespace, checkpoint_id AS checkpointId, checkpoint, metadata, parent_checkpoint_id AS parentCheckpointId FROM checkpoints").all(),writes:this.db.prepare("SELECT thread_id AS threadId, namespace, checkpoint_id AS checkpointId, task_id AS taskId, idx, channel, value FROM writes").all()}}}async function tupleFromState(e,t,i){const a=t.configurable?.thread_id;if(!a)return;const c=checkpointNamespace(t),n=o(t),r=e.checkpoints.filter(e=>e.threadId===a&&e.namespace===c),s=n?r.find(e=>e.checkpointId===n):r.sort((e,t)=>t.checkpointId.localeCompare(e.checkpointId))[0];return s?hydrateTuple(s,e.writes,i):void 0}function listCheckpoints(e,t,i){const a=t.configurable?.thread_id,c=t.configurable?.checkpoint_ns,n=o(t);let r=e.checkpoints.filter(e=>!(a&&e.threadId!==a||void 0!==c&&e.namespace!==c));return r=r.filter(e=>!n||e.checkpointId===n),r=r.filter(e=>!i?.before?.configurable?.checkpoint_id||e.checkpointId<String(i.before.configurable.checkpoint_id)),r=r.sort((e,t)=>t.checkpointId.localeCompare(e.checkpointId)),i?.filter&&(r=r.filter(e=>function metadataMatches(e,t){const i=JSON.parse(Buffer.from(e,"base64").toString("utf8"));return Object.entries(t).every(([e,t])=>i[e]===t)}(e.metadata,i.filter??{}))),"number"==typeof i?.limit?r.slice(0,i.limit):r}async function hydrateTuple(e,t,i){const a=await Promise.all(t.filter(t=>t.threadId===e.threadId&&t.namespace===e.namespace&&t.checkpointId===e.checkpointId).sort((e,t)=>e.idx-t.idx).map(async e=>[e.taskId,e.channel,await load(i,e.value)]));return{config:checkpointConfig(e.threadId,e.namespace,e.checkpointId),checkpoint:await load(i,e.checkpoint),metadata:await load(i,e.metadata),pendingWrites:a,...e.parentCheckpointId?{parentConfig:checkpointConfig(e.threadId,e.namespace,e.parentCheckpointId)}:{}}}async function dump(e,t){const[,i]=await e.serde.dumpsTyped(t);return Buffer.from(i).toString("base64")}async function load(e,t){return e.serde.loadsTyped("json",Buffer.from(t,"base64"))}function requiredThreadId(e,t){const i=e.configurable?.thread_id;if("string"!=typeof i||!i)throw new Error(`Failed to ${t}: missing configurable.thread_id.`);return i}function requiredCheckpointId(e){const t=o(e);if(!t)throw new Error("Failed to put writes: missing configurable.checkpoint_id.");return t}function checkpointNamespace(e){return String(e.configurable?.checkpoint_ns??"")}function checkpointConfig(e,t,i){return{configurable:{thread_id:e,checkpoint_ns:t,checkpoint_id:i}}}function sameWrite(e,t){return e.threadId===t.threadId&&e.namespace===t.namespace&&e.checkpointId===t.checkpointId&&e.taskId===t.taskId&&e.idx===t.idx}
@@ -0,0 +1,11 @@
1
+ import type { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
2
+ import type { RuntimeAdapterContext } from "@stable-harness/core";
3
+ export type DeepAgentsRuntimeSubstrate = {
4
+ checkpointer?: BaseCheckpointSaver;
5
+ store?: BaseStore;
6
+ threadId: string;
7
+ cleanupOnCompleted: boolean;
8
+ };
9
+ export declare function resolveDeepAgentsRuntimeSubstrate(input: RuntimeAdapterContext, deepagents: Record<string, unknown>): DeepAgentsRuntimeSubstrate;
10
+ declare function cleanupDeepAgentsRuntimeSubstrate(substrate: DeepAgentsRuntimeSubstrate): Promise<void>;
11
+ export { cleanupDeepAgentsRuntimeSubstrate };
@@ -0,0 +1 @@
1
+ import e from"node:path";import{createDeepAgentsCheckpointer as r}from"./checkpoint.js";import{createDeepAgentsStore as o}from"./store.js";export function resolveDeepAgentsRuntimeSubstrate(e,r){const o=function readSubstrateConfig(e){const r=readRecord(e.workspace.runtime.checkpoint)??{},o=readRecord(r.deepagents)??r;return{...o,performance:readRecord(o.performance),cleanup:readRecord(o.cleanup),checkpointer:readRecord(o.checkpointer),store:readRecord(o.store)}}(e),n=!1!==o.enabled,t=readProvider(o.provider)??"sqlite",i=o.performance??{},a=o.checkpointer??{},c=o.store??{};return{checkpointer:resolveCheckpointer({input:e,configured:r.checkpointer,enabled:n,provider:t,performance:i,config:o,checkpointerConfig:a}),store:resolveStore({input:e,configured:r.store,enabled:n,provider:t,performance:i,storeConfig:c}),threadId:`${workspaceId(e)}:${e.sessionId}:${e.requestId}`,cleanupOnCompleted:readCleanupOnCompleted(o)}}async function cleanupDeepAgentsRuntimeSubstrate(e){e.cleanupOnCompleted&&await(e.checkpointer?.deleteThread?.(e.threadId))}export{cleanupDeepAgentsRuntimeSubstrate};function resolveSubstratePath(r,o,n,t){const i="string"==typeof o&&o.trim()?o.trim():void 0;if(i)return e.isAbsolute(i)?i:e.resolve(r.workspace.root,i);const a="file"===n?"json":"sqlite";return e.resolve(r.workspace.root,function dataRoot(e){const r=e.workspace.runtime.dataRoot;return"string"==typeof r&&r.trim()?r.trim():".stable-harness"}(r),`${t}.${a}`)}function workspaceId(r){return(r.workspace.runtime.workspaceId??e.basename(r.workspace.root))||"workspace"}function resolveCheckpointer(e){return void 0!==e.configured?e.configured:e.enabled&&!1!==e.checkpointerConfig.enabled?r({provider:readProvider(e.checkpointerConfig.provider)??e.provider,path:resolveSubstratePath(e.input,e.checkpointerConfig.path??e.config.path,e.provider,"checkpoints/deepagents"),busyTimeoutMs:readNumber(e.performance.busyTimeoutMs)??readNumber(e.checkpointerConfig.busyTimeoutMs),wal:readBoolean(e.performance.wal)??readBoolean(e.checkpointerConfig.wal),synchronous:readSynchronous(e.performance.synchronous)??readSynchronous(e.checkpointerConfig.synchronous)}):void 0}function resolveStore(e){if(void 0!==e.configured)return e.configured;if(!e.enabled||!1===e.storeConfig.enabled)return;const r=function readStoreProvider(e){return readProvider(e)}(e.storeConfig.provider)??e.provider;return o({provider:r,path:resolveSubstratePath(e.input,e.storeConfig.path,r,"store/deepagents"),busyTimeoutMs:readNumber(e.performance.busyTimeoutMs)??readNumber(e.storeConfig.busyTimeoutMs),wal:readBoolean(e.performance.wal)??readBoolean(e.storeConfig.wal),synchronous:readSynchronous(e.performance.synchronous)??readSynchronous(e.storeConfig.synchronous)})}function readCleanupOnCompleted(e){const r=e.cleanup?.onCompleted;return!1!==r&&"keep"!==r}function readProvider(e){return"file"===e||"json"===e?"file":"sqlite"===e?"sqlite":void 0}function readSynchronous(e){return"off"===e||"normal"===e||"full"===e?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:void 0}function readBoolean(e){return"boolean"==typeof e?e:void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}
@@ -0,0 +1,10 @@
1
+ import { BaseStore } from "@langchain/langgraph-checkpoint";
2
+ export type DeepAgentsStoreProvider = "sqlite" | "file";
3
+ export type DeepAgentsStoreOptions = {
4
+ provider: DeepAgentsStoreProvider;
5
+ path: string;
6
+ busyTimeoutMs?: number;
7
+ wal?: boolean;
8
+ synchronous?: "off" | "normal" | "full";
9
+ };
10
+ export declare function createDeepAgentsStore(options: DeepAgentsStoreOptions): BaseStore;
@@ -0,0 +1 @@
1
+ import{existsSync as e,mkdirSync as t,readFileSync as a,writeFileSync as n}from"node:fs";import s from"node:path";import{DatabaseSync as i}from"node:sqlite";import{BaseStore as r}from"@langchain/langgraph-checkpoint";export function createDeepAgentsStore(e){return"file"===e.provider?new JsonFileStore(e.path):new SqliteStore(e)}class JsonFileStore extends r{filePath;constructor(e){super(),this.filePath=e}async batch(e){const t=this.state(),a=e.map(e=>applyOperation(t,e));return this.write(t),a}state(){return e(this.filePath)?{items:JSON.parse(a(this.filePath,"utf8")).items??[]}:{items:[]}}write(e){t(s.dirname(this.filePath),{recursive:!0}),n(this.filePath,`${JSON.stringify(e,null,2)}\n`)}}class SqliteStore extends r{db;constructor(e){super(),t(s.dirname(e.path),{recursive:!0}),this.db=new i(e.path),this.db.exec(`PRAGMA busy_timeout = ${e.busyTimeoutMs??5e3}`),!1!==e.wal&&this.db.exec("PRAGMA journal_mode = WAL"),this.db.exec(`PRAGMA synchronous = ${e.synchronous?.toUpperCase()??"NORMAL"}`),this.db.exec(["CREATE TABLE IF NOT EXISTS items (namespace TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY(namespace, key))","CREATE INDEX IF NOT EXISTS idx_items_namespace ON items(namespace)"].join(";"))}async batch(e){const t=this.state(),a=e.map(e=>applyOperation(t,e));return this.replace(t),a}state(){return{items:this.db.prepare("SELECT namespace, key, value, created_at AS createdAt, updated_at AS updatedAt FROM items").all().map(e=>({namespace:JSON.parse(e.namespace),key:e.key,value:JSON.parse(e.value),createdAt:e.createdAt,updatedAt:e.updatedAt}))}}replace(e){const t=this.db.prepare("DELETE FROM items"),a=this.db.prepare("INSERT INTO items(namespace, key, value, created_at, updated_at) VALUES (?, ?, ?, ?, ?)");this.db.exec("BEGIN");try{t.run();for(const t of e.items)a.run(JSON.stringify(t.namespace),t.key,JSON.stringify(t.value),t.createdAt,t.updatedAt);this.db.exec("COMMIT")}catch(e){throw this.db.exec("ROLLBACK"),e}}}function applyOperation(e,t){return"key"in t&&"namespace"in t&&!("value"in t)?function toItem(e){return e?toStoredItem(e):null}(e.items.find(e=>sameKey(e,t.namespace,t.key))??null):"value"in t?void function putItem(e,t,a,n){const s=e.items.find(e=>sameKey(e,t,a));if(null===n)return void(e.items=e.items.filter(e=>!sameKey(e,t,a)));const i=(new Date).toISOString();if(s)return s.value=n,void(s.updatedAt=i);e.items.push({namespace:t,key:a,value:n,createdAt:i,updatedAt:i})}(e,t.namespace,t.key,t.value):"namespacePrefix"in t?function searchItems(e,t,a,n,s){return e.items.filter(e=>function startsWithNamespace(e,t){return t.length<=e.length&&t.every((t,a)=>e[a]===t)}(e.namespace,t)).filter(e=>function matchesFilter(e,t){return!t||Object.entries(t).every(([t,a])=>e[t]===a)}(e.value,a)).sort((e,t)=>e.namespace.join("/").localeCompare(t.namespace.join("/"))||e.key.localeCompare(t.key)).slice(n,n+s).map(e=>({...toStoredItem(e),score:0}))}(e,t.namespacePrefix,t.filter,t.offset??0,t.limit??10):"matchConditions"in t?function listNamespaces(e,t){let a=[...new Map(e.items.map(e=>[e.namespace.join("\0"),e.namespace])).values()];const n=t.matchConditions??[];return a=a.filter(e=>n.every(t=>function namespaceMatches(e,t){const a=(Array.isArray(t.path)?t.path:[]).filter(e=>"string"==typeof e);return"suffix"===t.matchType?a.length<=e.length&&a.every((t,n)=>"*"===t||e[e.length-a.length+n]===t):a.length<=e.length&&a.every((t,a)=>"*"===t||e[a]===t)}(e,t))),void 0!==t.maxDepth&&(a=[...new Map(a.map(e=>{const a=e.slice(0,t.maxDepth);return[a.join("\0"),a]})).values()]),a.sort((e,t)=>e.join("/").localeCompare(t.join("/"))).slice(t.offset,t.offset+t.limit)}(e,t):void 0}function toStoredItem(e){return{value:e.value,key:e.key,namespace:e.namespace,createdAt:new Date(e.createdAt),updatedAt:new Date(e.updatedAt)}}function sameKey(e,t,a){return e.key===a&&e.namespace.length===t.length&&e.namespace.every((e,a)=>e===t[a])}
@@ -1 +1 @@
1
- import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createOpenAiCompatibleHttpServer as e}from"@stable-harness/protocols";import{startOfficialLangGraphServer as n}from"./langgraph-official.js";const o="127.0.0.1",s=r(t);export async function serveProtocol(t,r){const e=createConfiguredServers(t,r),n=[];let o=0;for(const r of e)if("http"===r.kind){if(!await listen(r)){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.host}:${r.port}/v1\n`);continue}n.push(()=>closeHttpServer(r.server)),o+=1;const t=r.server.address(),e="object"==typeof t&&t?t.port:r.port;process.stdout.write(`stable-harness ${r.protocol} API listening on http://${r.host}:${e}/v1\n`)}else{const e=await startLangGraphServer(t,r);if(!e){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.config.host}:${r.config.port}\n`);continue}n.push(e.cleanup),o+=1,process.stdout.write(`stable-harness ${r.protocol} API listening on ${e.url}\n`)}0!==o&&await async function waitForShutdown(t){const r=setInterval(()=>{},864e5);await new Promise(e=>{const shutdown=()=>{clearInterval(r),Promise.allSettled(t.map(t=>t())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(n)}export async function stopProtocol(t,r){const e=createConfiguredServers({getRuntimePolicy:()=>t.runtime},r).map(t=>"http"===t.kind?{protocol:t.protocol,host:t.host,port:t.port}:{protocol:t.protocol,host:t.config.host,port:t.config.port}),n=await Promise.all(e.map(async t=>({target:t,pids:await stableHarnessListenerPids(t.port)}))),o=[...new Set(n.flatMap(t=>t.pids))];for(const t of o)process.kill(t,"SIGTERM");for(const{target:t,pids:r}of n)0!==r.length?process.stdout.write(`stable-harness ${t.protocol} API stopped on ${t.host}:${t.port} pid=${r.join(",")}\n`):process.stdout.write(`stable-harness ${t.protocol} API not running on ${t.host}:${t.port}\n`)}function createConfiguredServers(t,r){const e=readRecord(t.getRuntimePolicy().protocols)??{},n=protocolConfig(e,"openaiCompatible","openai-compatible","openai")??{},o=protocolConfig(e,"langgraph")??{};return[...enabled(n)?[openAiServer(t,n,r)]:[],...enabled(o)?[langGraphServer(o)]:[]]}function openAiServer(t,r,n){const s=configString(r.host)??o,i=n.port??configNumber(r.port)??8642,a=n.host??s,c=configString(r.bearerToken)??configString(r.apiKey)??n.apiKey;return{kind:"http",protocol:"openai-compatible",server:e(t,{bearerToken:c}),host:a,port:i,...c?{bearerToken:c}:{}}}function langGraphServer(t){const r=configString(t.host)??o,e=configNumber(t.port)??2024,n=function configStringArray(t){if(Array.isArray(t)&&t.every(t=>"string"==typeof t))return t.filter(t=>t.trim()).map(t=>t.trim())}(t.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:r,port:e,nWorkers:configNumber(t.nWorkers)??10,...n?{exposeAgents:n}:{},...void 0!==t.env?{env:t.env}:{},...void 0!==t.envFile?{envFile:t.envFile}:{}}}}function protocolConfig(t,...r){for(const e of r){const r=readRecord(t[e]);if(r)return r}}function enabled(t){return!1!==t.enabled}function configString(t){if("string"!=typeof t||!t.trim())return;const r=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:t}function configNumber(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}async function listen(t){try{return await new Promise((r,e)=>{t.server.once("error",e),t.server.listen(t.port,t.host,()=>{t.server.off("error",e),r()})}),!0}catch(r){if(isAddressInUse(r)&&await async function isOpenAiServerAlreadyRunning(t){const r=await fetchJson(`http://${t.host}:${t.port}/v1/capabilities`,{...t.bearerToken?{authorization:`Bearer ${t.bearerToken}`}:{}});return"stable_harness.capabilities"===r?.object}(t))return!1;throw portConflictError(r,t.protocol,t.host,t.port)}}async function startLangGraphServer(t,r){if(!await isLangGraphServerAlreadyRunning(r))try{return await n(t,r.config)}catch(t){if(isAddressInUse(t)&&await isLangGraphServerAlreadyRunning(r))return;throw portConflictError(t,r.protocol,r.config.host,r.config.port)}}async function isLangGraphServerAlreadyRunning(t){const r=await fetchJson(`http://${t.config.host}:${t.config.port}/ok`);return!0===r?.ok}async function fetchJson(t,r={}){try{const e=await fetch(t,{headers:r});if(!e.ok)return;return await e.json()}catch{return}}function isAddressInUse(t){return"EADDRINUSE"===function readErrorCode(t){return"object"==typeof t&&null!==t&&"code"in t?t.code:void 0}(t)||String(t).includes("EADDRINUSE")}function portConflictError(t,r,e,n){return isAddressInUse(t)?new Error([`stable-harness ${r} port is already in use: ${e}:${n}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${e}:${n}.`].join("\n")):t}async function stableHarnessListenerPids(t){const r=await async function listenerPids(t){try{const{stdout:r}=await s("lsof",[`-tiTCP:${t}`,"-sTCP:LISTEN"]);return r.split(/\s+/u).map(t=>Number(t)).filter(t=>Number.isInteger(t)&&t>0)}catch{return[]}}(t);return(await Promise.all(r.map(async t=>{const r=await async function processCommand(t){try{const{stdout:r}=await s("ps",["-p",String(t),"-o","command="]);return r.trim()}catch{return""}}(t);return isStableHarnessStartCommand(r)?t:void 0}))).filter(t=>"number"==typeof t)}export function isStableHarnessStartCommand(t){if(function hasUnsafeCommandCharacters(t){return/[\u0000-\u001F\u007F;|`&<>]/u.test(t)}(t))return!1;const r=function splitCommandLine(t){const r=[];let e,n="";for(const o of t)'"'!==o&&"'"!==o||void 0!==e?o!==e?/\s/u.test(o)&&void 0===e?n&&(r.push(n),n=""):n+=o:e=void 0:e=o;return n&&r.push(n),r}(t),e=function stableHarnessCommandIndex(t){return isStableHarnessExecutableToken(t[0]??"")?0:function isNodeExecutableToken(t){const r=t.split(/[\\/]/u).at(-1);return"node"===r||"nodejs"===r}(t[0]??"")&&(isStableHarnessExecutableToken(t[1]??"")||function isStableHarnessScriptToken(t){if(hasTraversalSegment(t))return!1;const r=t.replaceAll("\\","/");return r.includes("/stable-harness/")&&r.endsWith("/packages/cli/dist/src/cli.js")||r.includes("/stable-harness/")&&r.endsWith("/dist/cli.js")}(t[1]??""))?1:-1}(r);return e>=0&&r.slice(e+1).includes("start")}function isStableHarnessExecutableToken(t){if(hasTraversalSegment(t))return!1;const r=t.split(/[\\/]/u).at(-1);return"stable-harness"===r||"botbotgo"===r}function hasTraversalSegment(t){return t.split(/[\\/]/u).some(t=>"."===t||".."===t)}async function closeHttpServer(t){await new Promise((r,e)=>{t.close(t=>{t?e(t):r()})})}
1
+ import{execFile as t}from"node:child_process";import{promisify as r}from"node:util";import{createOpenAiCompatibleHttpServer as e}from"@stable-harness/protocols";import{startOfficialLangGraphServer as n}from"./langgraph-official.js";const o="127.0.0.1",s=r(t);export async function serveProtocol(t,r){const e=createConfiguredServers(t,r),n=[];let o=0;for(const r of e)if("http"===r.kind){if(!await listen(r)){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.host}:${r.port}/v1\n`);continue}n.push(()=>closeHttpServer(r.server)),o+=1;const t=r.server.address(),e="object"==typeof t&&t?t.port:r.port;process.stdout.write(`stable-harness ${r.protocol} API listening on http://${r.host}:${e}/v1\n`)}else{const e=await startLangGraphServer(t,r);if(!e){process.stdout.write(`stable-harness ${r.protocol} API already running on http://${r.config.host}:${r.config.port}\n`);continue}n.push(e.cleanup),o+=1,process.stdout.write(`stable-harness ${r.protocol} API listening on ${e.url}\n`)}0!==o&&await async function waitForShutdown(t){const r=setInterval(()=>{},864e5);await new Promise(e=>{const shutdown=()=>{clearInterval(r),Promise.allSettled(t.map(t=>t())).finally(()=>process.exit(0))};process.once("SIGINT",shutdown),process.once("SIGTERM",shutdown)})}(n)}export async function stopProtocol(t,r){const e=createConfiguredServers({getRuntimePolicy:()=>t.runtime},r).map(t=>"http"===t.kind?{protocol:t.protocol,host:t.host,port:t.port}:{protocol:t.protocol,host:t.config.host,port:t.config.port}),n=await Promise.all(e.map(async t=>({target:t,pids:await stableHarnessListenerPids(t.port)}))),o=[...new Set(n.flatMap(t=>t.pids))];for(const t of o)process.kill(t,"SIGTERM");for(const{target:t,pids:r}of n)0!==r.length?process.stdout.write(`stable-harness ${t.protocol} API stopped on ${t.host}:${t.port} pid=${r.join(",")}\n`):process.stdout.write(`stable-harness ${t.protocol} API not running on ${t.host}:${t.port}\n`)}function createConfiguredServers(t,r){const e=readRecord(t.getRuntimePolicy().protocols)??{},n=protocolConfig(e,"openaiCompatible","openai-compatible","openai")??{},o=protocolConfig(e,"langgraph")??{};return[...enabled(n)?[openAiServer(t,n,r)]:[],...enabled(o)?[langGraphServer(o)]:[]]}function openAiServer(t,r,n){const s=configString(r.host)??o,i=n.port??configNumber(r.port)??8642,a=n.host??s,c=configString(r.bearerToken)??configString(r.apiKey)??n.apiKey;return{kind:"http",protocol:"openai-compatible",server:e(t,{bearerToken:c}),host:a,port:i,...c?{bearerToken:c}:{}}}function langGraphServer(t){const r=configString(t.host)??o,e=configNumber(t.port)??2024,n=function configStringArray(t){if(Array.isArray(t)&&t.every(t=>"string"==typeof t))return t.filter(t=>t.trim()).map(t=>t.trim())}(t.exposeAgents);return{kind:"langgraph",protocol:"langgraph-compatible",config:{host:r,port:e,nWorkers:configNumber(t.nWorkers)??10,...n?{exposeAgents:n}:{},...void 0!==t.env?{env:t.env}:{},...void 0!==t.envFile?{envFile:t.envFile}:{}}}}function protocolConfig(t,...r){for(const e of r){const r=readRecord(t[e]);if(r)return r}}function enabled(t){return!1!==t.enabled}function configString(t){if("string"!=typeof t||!t.trim())return;const r=t.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:t}function configNumber(t){return"number"==typeof t&&Number.isFinite(t)?t:"string"==typeof t&&t.trim()?Number(t):void 0}function readRecord(t){return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}async function listen(t){try{return await new Promise((r,e)=>{t.server.once("error",e),t.server.listen(t.port,t.host,()=>{t.server.off("error",e),r()})}),!0}catch(r){if(isAddressInUse(r)&&await async function isOpenAiServerAlreadyRunning(t){const r=await fetchJson(`http://${t.host}:${t.port}/v1/capabilities`,{...t.bearerToken?{authorization:`Bearer ${t.bearerToken}`}:{}});return"stable_harness.capabilities"===r?.object}(t))return!1;throw portConflictError(r,t.protocol,t.host,t.port)}}async function startLangGraphServer(t,r){if(!await isLangGraphServerAlreadyRunning(r))try{return await n(t,r.config)}catch(t){if(isAddressInUse(t)&&await isLangGraphServerAlreadyRunning(r))return;throw portConflictError(t,r.protocol,r.config.host,r.config.port)}}async function isLangGraphServerAlreadyRunning(t){const r=await fetchJson(`http://${t.config.host}:${t.config.port}/ok`);return!0===r?.ok}async function fetchJson(t,r={}){try{const e=await fetch(t,{headers:r});if(!e.ok)return;return await e.json()}catch{return}}function isAddressInUse(t){return"EADDRINUSE"===function readErrorCode(t){return"object"==typeof t&&null!==t&&"code"in t?t.code:void 0}(t)||String(t).includes("EADDRINUSE")}function portConflictError(t,r,e,n){return isAddressInUse(t)?new Error([`stable-harness ${r} port is already in use: ${e}:${n}.`,`Use --port <port>, update config/runtime/workspace.yaml, or stop the process currently listening on ${e}:${n}.`].join("\n")):t}async function stableHarnessListenerPids(t){const r=await async function listenerPids(t){try{const{stdout:r}=await s("lsof",[`-tiTCP:${t}`,"-sTCP:LISTEN"]);return r.split(/\s+/u).map(t=>Number(t)).filter(t=>Number.isInteger(t)&&t>0)}catch{return[]}}(t);return(await Promise.all(r.map(async t=>{const r=await async function processCommand(t){try{const{stdout:r}=await s("ps",["-p",String(t),"-o","command="]);return r.trim()}catch{return""}}(t);return isStableHarnessStartCommand(r)?t:void 0}))).filter(t=>"number"==typeof t)}export function isStableHarnessStartCommand(t){if(function hasUnsafeCommandCharacters(t){return/[\u0000-\u001F\u007F;|`&<>]/u.test(t)}(t))return!1;const r=function splitCommandLine(t){const r=[];let e,n="";for(const o of t)'"'!==o&&"'"!==o||void 0!==e?o!==e?/\s/u.test(o)&&void 0===e?n&&(r.push(n),n=""):n+=o:e=void 0:e=o;return n&&r.push(n),r}(t),e=function stableHarnessCommandIndex(t){return isStableHarnessExecutableToken(t[0]??"")?0:function isNodeExecutableToken(t){const r=t.split(/[\\/]/u).at(-1);return"node"===r||"nodejs"===r}(t[0]??"")&&(isStableHarnessExecutableToken(t[1]??"")||function isStableHarnessScriptToken(t){if(hasTraversalSegment(t))return!1;const r=t.replaceAll("\\","/");return r.includes("/stable-harness/")&&r.endsWith("/packages/cli/dist/src/cli.js")}(t[1]??""))?1:-1}(r);return e>=0&&r.slice(e+1).includes("start")}function isStableHarnessExecutableToken(t){if(hasTraversalSegment(t))return!1;const r=t.split(/[\\/]/u).at(-1);return"stable-harness"===r||"botbotgo"===r}function hasTraversalSegment(t){return t.split(/[\\/]/u).some(t=>"."===t||".."===t)}async function closeHttpServer(t){await new Promise((r,e)=>{t.close(t=>{t?e(t):r()})})}
@@ -0,0 +1,9 @@
1
+ import type { RuntimeEvent } from "../events.js";
2
+ import type { RuntimeDeletionResult, RuntimeStore } from "../types.js";
3
+ export declare function createRuntimeAdministrationMethods(input: {
4
+ store: RuntimeStore;
5
+ emit: (event: RuntimeEvent) => void;
6
+ }): {
7
+ deleteRequest(requestId: string): RuntimeDeletionResult;
8
+ deleteSession(sessionId: string): RuntimeDeletionResult;
9
+ };
@@ -0,0 +1 @@
1
+ export function createRuntimeAdministrationMethods(e){return{deleteRequest(t){const s=e.store.getRun(t);return s?(e.emit({type:"runtime.request.deleted",requestId:t,sessionId:s.sessionId,agentId:s.agentId}),e.store.deleteRun(t),{deletedRequestIds:[t],deletedCount:1}):{deletedRequestIds:[],deletedCount:0}},deleteSession(t){const s=e.store.listRuns({sessionId:t});if(0===s.length)return{deletedRequestIds:[],deletedCount:0};const d=s.map(e=>e.requestId);return e.emit({type:"runtime.session.deleted",requestId:s[0]?.requestId??"",sessionId:t,agentId:s[0]?.agentId??"",deletedRequestIds:d}),e.store.deleteSession(t)}}}
@@ -0,0 +1,10 @@
1
+ import type { ListMemoriesInput, MemorizeInput, RecallInput, RuntimeMemoryStore, UpdateMemoryInput } from "@stable-harness/memory";
2
+ export declare function createRuntimeMemoryAdministration(input: {
3
+ memory?: RuntimeMemoryStore;
4
+ }): {
5
+ memorize(inputValue: MemorizeInput): Promise<import("@stable-harness/memory").MemoryRecord>;
6
+ recallMemories(inputValue: RecallInput): Promise<import("@stable-harness/memory").MemoryRecallResult>;
7
+ listMemories(inputValue: ListMemoriesInput): Promise<import("@stable-harness/memory").MemoryRecord[]>;
8
+ updateMemory(inputValue: UpdateMemoryInput): Promise<import("@stable-harness/memory").MemoryRecord | undefined>;
9
+ archiveMemory(id: string, reason?: string): Promise<import("@stable-harness/memory").MemoryRecord | undefined>;
10
+ };
@@ -0,0 +1 @@
1
+ export function createRuntimeMemoryAdministration(e){return{memorize:r=>requireMemory(e.memory).memorize(r),recallMemories:r=>requireMemory(e.memory).recall(r),listMemories:r=>requireMemory(e.memory).list(r),updateMemory:r=>requireMemory(e.memory).update(r),archiveMemory:(r,m)=>requireMemory(e.memory).archive(r,m)}}function requireMemory(e){if(!e)throw new Error("Runtime memory store is not configured");return e}
@@ -45,6 +45,17 @@ export type RuntimeEvent = RuntimeEventMetadata & ({
45
45
  sessionId: string;
46
46
  agentId: string;
47
47
  reason?: string;
48
+ } | {
49
+ type: "runtime.request.deleted";
50
+ requestId: string;
51
+ sessionId: string;
52
+ agentId: string;
53
+ } | {
54
+ type: "runtime.session.deleted";
55
+ requestId: string;
56
+ sessionId: string;
57
+ agentId: string;
58
+ deletedRequestIds: string[];
48
59
  } | {
49
60
  type: "runtime.artifact.created";
50
61
  requestId: string;
@@ -1 +1 @@
1
- import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as r}from"node:fs";import u from"node:path";export function createInMemoryRuntimeStore(e=[]){const t=new Map(e.map(e=>[e.requestId,cloneRun(e)]));return{createRun(e){t.set(e.requestId,cloneRun(e))},updateRun(e,n){const r=t.get(e);if(r)return Object.assign(r,function clonePatch(e){return structuredClone(e)}(n)),cloneRun(r)},appendEvent(e){const n=t.get(e.requestId);if(!n)return;const r=function eventArtifact(e){return"artifact"in e&&e.artifact?e.artifact:void 0}(e);return r&&!n.artifacts.some(e=>e.id===r.id)&&n.artifacts.push(structuredClone(r)),n.events.push(function cloneEvent(e){return structuredClone(e)}(e)),cloneRun(n)},getRun:e=>function cloneOptionalRun(e){return e?cloneRun(e):void 0}(t.get(e)),listRuns:e=>[...t.values()].filter(t=>function matchesFilter(e,t){return!t||!(t.agentId&&t.agentId!==e.agentId||t.sessionId&&t.sessionId!==e.sessionId||t.state&&t.state!==e.state)}(t,e)).map(cloneRun)}}export function createJsonFileRuntimeStore(t){const r=u.resolve(t),i=createInMemoryRuntimeStore(function readStoreFile(t){if(!e(t))return[];const r=JSON.parse(n(t,"utf8"));if(!isRecord(r)||!Array.isArray(r.runs))throw new Error(`Invalid runtime store file: ${t}`);return r.runs.map(assertRunRecord)}(r));return{createRun(e){i.createRun(e),writeStoreFile(r,i.listRuns())},updateRun(e,t){const n=i.updateRun(e,t);return writeStoreFile(r,i.listRuns()),n},appendEvent(e){const t=i.appendEvent(e);return writeStoreFile(r,i.listRuns()),t},getRun:e=>i.getRun(e),listRuns:e=>i.listRuns(e)}}function writeStoreFile(e,n){t(u.dirname(e),{recursive:!0}),r(e,`${JSON.stringify({version:1,runs:n},null,2)}\n`)}function assertRunRecord(e){if(!isRecord(e)||"string"!=typeof e.requestId)throw new Error("Invalid runtime run record in store file");return cloneRun(e)}function cloneRun(e){return structuredClone(e)}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{existsSync as e,mkdirSync as t,readFileSync as n,writeFileSync as r}from"node:fs";import s from"node:path";export function createInMemoryRuntimeStore(e=[]){const t=new Map(e.map(e=>[e.requestId,cloneRun(e)]));return{createRun(e){t.set(e.requestId,cloneRun(e))},updateRun(e,n){const r=t.get(e);if(r)return Object.assign(r,function clonePatch(e){return structuredClone(e)}(n)),cloneRun(r)},appendEvent(e){const n=t.get(e.requestId);if(!n)return;const r=function eventArtifact(e){return"artifact"in e&&e.artifact?e.artifact:void 0}(e);return r&&!n.artifacts.some(e=>e.id===r.id)&&n.artifacts.push(structuredClone(r)),n.events.push(function cloneEvent(e){return structuredClone(e)}(e)),cloneRun(n)},getRun:e=>function cloneOptionalRun(e){return e?cloneRun(e):void 0}(t.get(e)),listRuns:e=>[...t.values()].filter(t=>function matchesFilter(e,t){return!t||!(t.agentId&&t.agentId!==e.agentId||t.sessionId&&t.sessionId!==e.sessionId||t.state&&t.state!==e.state)}(t,e)).map(cloneRun),deleteRun(e){const n=t.get(e);if(n)return t.delete(e),cloneRun(n)},deleteSession(e){const n=[...t.values()].filter(t=>t.sessionId===e).map(e=>e.requestId);for(const e of n)t.delete(e);return{deletedRequestIds:n,deletedCount:n.length}}}}export function createJsonFileRuntimeStore(t){const r=s.resolve(t),u=createInMemoryRuntimeStore(function readStoreFile(t){if(!e(t))return[];const r=JSON.parse(n(t,"utf8"));if(!isRecord(r)||!Array.isArray(r.runs))throw new Error(`Invalid runtime store file: ${t}`);return r.runs.map(assertRunRecord)}(r));return{createRun(e){u.createRun(e),writeStoreFile(r,u.listRuns())},updateRun(e,t){const n=u.updateRun(e,t);return writeStoreFile(r,u.listRuns()),n},appendEvent(e){const t=u.appendEvent(e);return writeStoreFile(r,u.listRuns()),t},getRun:e=>u.getRun(e),listRuns:e=>u.listRuns(e),deleteRun(e){const t=u.deleteRun(e);return writeStoreFile(r,u.listRuns()),t},deleteSession(e){const t=u.deleteSession(e);return writeStoreFile(r,u.listRuns()),t}}}function writeStoreFile(e,n){t(s.dirname(e),{recursive:!0}),r(e,`${JSON.stringify({version:1,runs:n},null,2)}\n`)}function assertRunRecord(e){if(!isRecord(e)||"string"!=typeof e.requestId)throw new Error("Invalid runtime run record in store file");return cloneRun(e)}function cloneRun(e){return structuredClone(e)}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -115,12 +115,18 @@ export type RuntimeRunFilter = {
115
115
  sessionId?: string;
116
116
  state?: RuntimeRecordState;
117
117
  };
118
+ export type RuntimeDeletionResult = {
119
+ deletedRequestIds: string[];
120
+ deletedCount: number;
121
+ };
118
122
  export type RuntimeStore = {
119
123
  createRun(record: RuntimeRunRecord): void;
120
124
  updateRun(requestId: string, patch: RuntimeStoreRunPatch): RuntimeRunRecord | undefined;
121
125
  appendEvent(event: RuntimeEvent): RuntimeRunRecord | undefined;
122
126
  getRun(requestId: string): RuntimeRunRecord | undefined;
123
127
  listRuns(filter?: RuntimeRunFilter): RuntimeRunRecord[];
128
+ deleteRun(requestId: string): RuntimeRunRecord | undefined;
129
+ deleteSession(sessionId: string): RuntimeDeletionResult;
124
130
  };
125
131
  export type RuntimeQueueRecord = {
126
132
  requestId: string;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as s}from"./recovery/tool-call.js";import{recoverQualityReview as n,resolveQualityPolicy as o}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{runDirectToolCall as p}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as d}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as m}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as l}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as w,normalizeAdapterResult as f}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as g}from"./runtime/memory.js";import{createInMemoryRuntimeStore as I}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as y}from"./runtime/progress-narration.js";import{repairRuntimeSelection as q}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as R}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as k}from"./runtime/tool-failure.js";import{runWorkflowRequest as v}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const f=new Set,b=t.store??I(),A=w([g(t),y({options:t.progressNarration,policy:t.workspace.runtime}),R({policy:t.workspace.runtime,store:b,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);b.appendEvent(r);for(const e of f)e(r)},emit=e=>{emitBase(e),A.emitSideEffects(e,emitBase)},C=m({gateway:d({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),h={...t,toolGateway:C},x=k(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const d=t.request.requestId??e(),m=t.request.sessionId??e(),l=[],{agent:w,adapter:f}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await q({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),s=e.workspace.agents.get(a);if(!s)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:s,adapter:void 0};const n=e.adapters.find(e=>e.canRun(s));if(!n)throw new Error(`No runtime adapter can run backend ${s.backend} for agent ${s.id}`);return{agent:s,adapter:n}}(t.input,t.request,{requestId:d,sessionId:m,emit:e=>l.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,d,m,w)),l.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:d,sessionId:m,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await v({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:d,sessionId:m,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:d,sessionId:m,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await p({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:d,sessionId:m,agent:w,toolFailureTracker:t.toolFailureTracker});return u({store:t.store,emit:t.emit,requestId:d,sessionId:m,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],m=e.input.workspace.runtime,l=o(e.input.workspace.runtime,e.agent),w=new Map;let f;try{f=await runAdapterOnce(e,t,e.request,p,d,w)}catch(a){if(!s(a,m))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),f=await runAdapterOnce(e,t,r(e.request,a,m),p,d,w),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}f=await i({...e,request:e.request,result:f,recoveryPolicy:m,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w)}),f=await n(createQualityRuntimeInput(e,p,d,w),e.request,f,l),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:f});try{assertRequestExecutionContract(e)}catch(r){const s=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:m});if(!s)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),f=await runAdapterOnce(e,t,s,p,d,w),f=await i({...e,request:s,result:f,recoveryPolicy:m,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w)}),f=await n(createQualityRuntimeInput(e,p,d,w),s,f,l),assertRequestExecutionContract(e),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const g=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:f,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:f,response:g}),g}({...t,adapter:f,requestId:d,sessionId:m,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:d,sessionId:m,agent:w,error:e})}}({input:h,capabilities:A,store:b,emit:emit,request:t,toolFailureTracker:x}),subscribe:e=>(f.add(e),()=>f.delete(e)),...l({workspace:t.workspace,store:b,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),cancel(e,t){const r=b.getRun(e);r&&"running"===r.state&&(b.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await A.stop(),f.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,s,n){return f(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:s,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:n,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,s){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:s}}function errorMessage(e){return e instanceof Error?e.message:String(e)}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
1
+ import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{buildAdapterErrorRecoveryPrompt as r,buildExecutionContractRecoveryRequest as a,isRecoverableAdapterError as s}from"./recovery/tool-call.js";import{recoverQualityReview as n,resolveQualityPolicy as o}from"./quality/index.js";import{recoverAdapterResultOutput as i}from"./runtime/recovery/adapter-result.js";import{completeRun as u,failRun as c}from"./runtime/completion.js";import{createRuntimeAdministrationMethods as p}from"./runtime/admin/administration.js";import{runDirectToolCall as d}from"./runtime/direct-tool-call.js";import{createApprovalGatedToolGateway as m}from"./runtime/governance/approval-gate.js";import{createSandboxedToolGateway as l}from"./runtime/governance/sandbox.js";import{createRuntimeInspectionMethods as w}from"./runtime/inspection/methods.js";import{createRuntimeCapabilityRegistry as f,normalizeAdapterResult as g}from"./runtime/capabilities.js";import{createMemoryRuntimeCapability as I}from"./runtime/memory.js";import{createRuntimeMemoryAdministration as y}from"./runtime/admin/memory.js";import{createInMemoryRuntimeStore as q}from"./runtime/persistence/stores.js";import{createProgressNarrationCapability as R}from"./runtime/progress-narration.js";import{repairRuntimeSelection as k}from"./runtime/selection-repair.js";import{createLangSmithTracingCapability as v}from"./runtime/tracing/langsmith.js";import{createToolFailureTracker as A}from"./runtime/tool-failure.js";import{runWorkflowRequest as b}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const g=new Set,C=t.store??q(),h=f([I(t),R({options:t.progressNarration,policy:t.workspace.runtime}),v({policy:t.workspace.runtime,store:C,options:t.langSmithTracing}),...t.capabilities??[]]),emitBase=t=>{const r=function enrichRuntimeEvent(t){return{...t,eventId:t.eventId??e(),emittedAt:t.emittedAt??(new Date).toISOString()}}(t);C.appendEvent(r);for(const e of g)e(r)},emit=e=>{emitBase(e),h.emitSideEffects(e,emitBase)},j=l({gateway:m({gateway:t.toolGateway,approvals:t.approvals,workspace:t.workspace,emit:emit}),workspace:t.workspace,sandbox:t.sandbox,emit:emit}),x={...t,toolGateway:j},E=A(function readToolFailurePolicy(e){if("object"!=typeof e||null===e||Array.isArray(e))return;const t=e.failurePolicy;return"object"!=typeof t||null===t||Array.isArray(t)?void 0:t}(t.workspace.runtime.toolGateway));return{request:async t=>async function runRuntimeRequest(t){const p=t.request.requestId??e(),m=t.request.sessionId??e(),l=[],{agent:w,adapter:f}=await async function resolveExecution(e,t,r){const a=t.agentId?await async function resolveRequestedAgentId(e,t,r){if(e.agents.has(t))return t;const a=await k({id:t,candidates:[...e.agents.values()].map(e=>({id:e.id,description:e.description})),trace:{...r,agentId:t,layer:"agent",owner:"stable_runtime_policy"}});return a.ok?a.id:t}(e.workspace,t.agentId,r):function resolveRoutedAgentId(e,t){for(const r of e.runtime.routes??[])if(routeMatches(r,t))return r.agentId;return e.runtime.defaultAgentId}(e.workspace,t.input),s=e.workspace.agents.get(a);if(!s)throw new Error(`Agent ${a} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:s,adapter:void 0};const n=e.adapters.find(e=>e.canRun(s));if(!n)throw new Error(`No runtime adapter can run backend ${s.backend} for agent ${s.id}`);return{agent:s,adapter:n}}(t.input,t.request,{requestId:p,sessionId:m,emit:e=>l.push(e)});t.store.createRun(function createRunRecord(e,t,r,a){return{requestId:t,sessionId:r,agentId:a.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,p,m,w)),l.forEach(t.emit),t.emit({type:"runtime.request.started",requestId:p,sessionId:m,agentId:w.id,input:t.request.input});try{if(t.request.workflow){const e=await b({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:p,sessionId:m,agentId:w.id,emit:t.emit});return u({store:t.store,emit:t.emit,requestId:p,sessionId:m,agent:w,result:e,artifacts:t.input.artifacts})}if(t.request.toolCall){const e=await d({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:p,sessionId:m,agent:w,toolFailureTracker:t.toolFailureTracker});return u({store:t.store,emit:t.emit,requestId:p,sessionId:m,agent:w,result:e,artifacts:t.input.artifacts})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,c=await e.capabilities.beforeAdapterRun(createCapabilityContext(e)),p=c.memory,d=c.pluginMemories??[],m=e.input.workspace.runtime,l=o(e.input.workspace.runtime,e.agent),w=new Map;let f;try{f=await runAdapterOnce(e,t,e.request,p,d,w)}catch(a){if(!s(a,m))throw a;e.emit(repairStarted(e,"adapter_error",1,errorMessage(a))),f=await runAdapterOnce(e,t,r(e.request,a,m),p,d,w),e.emit(repairCompleted(e,"adapter_error","retried",1,errorMessage(a)))}f=await i({...e,request:e.request,result:f,recoveryPolicy:m,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w)}),f=await n(createQualityRuntimeInput(e,p,d,w),e.request,f,l),await e.capabilities.beforeAdapterResultContract({...createCapabilityContext(e),result:f});try{assertRequestExecutionContract(e)}catch(r){const s=a({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:m});if(!s)throw r;e.emit(repairStarted(e,"execution_contract",1,errorMessage(r))),f=await runAdapterOnce(e,t,s,p,d,w),f=await i({...e,request:s,result:f,recoveryPolicy:m,runAdapter:r=>runAdapterOnce(e,t,r,p,d,w)}),f=await n(createQualityRuntimeInput(e,p,d,w),s,f,l),assertRequestExecutionContract(e),e.emit(repairCompleted(e,"execution_contract","retried",1,errorMessage(r)))}const g=u({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:f,artifacts:e.input.artifacts});return await e.capabilities.afterAdapterResponse({...createCapabilityContext(e),result:f,response:g}),g}({...t,adapter:f,requestId:p,sessionId:m,agent:w})}catch(e){return c({store:t.store,emit:t.emit,requestId:p,sessionId:m,agent:w,error:e})}}({input:x,capabilities:h,store:C,emit:emit,request:t,toolFailureTracker:E}),subscribe:e=>(g.add(e),()=>g.delete(e)),...w({workspace:t.workspace,store:C,artifacts:t.artifacts,approvals:t.approvals,emit:emit}),...p({store:C,emit:emit}),...y({memory:t.memory}),cancel(e,t){const r=C.getRun(e);r&&"running"===r.state&&(C.updateRun(e,{state:"cancelled",completedAt:(new Date).toISOString()}),emit({type:"runtime.request.cancelled",requestId:e,sessionId:r.sessionId,agentId:r.agentId,reason:t}))},async stop(){await h.stop(),g.clear()}}}function createCapabilityContext(e){return{workspace:e.input.workspace,store:e.store,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent}}function createQualityRuntimeInput(e,t,r,a){return{workspace:e.input.workspace,agent:e.agent,request:e.request,requestId:e.requestId,sessionId:e.sessionId,events:e.store.getRun(e.requestId)?.events??[],emit:e.emit,getEvents:()=>e.store.getRun(e.requestId)?.events??[],runAdapter:s=>runAdapterOnce(e,e.adapter,s,t,r,a),reviewModel:e.input.qualityReviewModel,memory:t,pluginMemories:r}}function assertRequestExecutionContract(e){t({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,metadata:e.request.metadata})}async function runAdapterOnce(e,t,r,a,s,n){return g(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:a,pluginMemories:s,toolGateway:e.input.toolGateway,toolFailureTracker:e.input.toolFailureTracker,requestState:n,getEvents:()=>e.store.getRun(e.requestId)?.events??[],emit:e.emit}))}function repairStarted(e,t,r,a){return{type:"runtime.repair.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,attempt:r,reason:a}}function repairCompleted(e,t,r,a,s){return{type:"runtime.repair.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agent.id,layer:t,outcome:r,attempt:a,reason:s}}function errorMessage(e){return e instanceof Error?e.message:String(e)}function routeMatches(e,t){if(e.pattern)try{if(new RegExp(e.pattern,"iu").test(t))return!0}catch{return!1}const r=t.toLowerCase();return(e.keywords??[]).some(e=>r.includes(e.toLowerCase()))}
@@ -1,17 +1,17 @@
1
- import type { MemoryProvider, MemoryRecord, RuntimeMemoryStore } from "@stable-harness/memory";
1
+ import type { ListMemoriesInput, MemorizeInput, MemoryProvider, MemoryRecord, MemoryRecallResult, RecallInput, RuntimeMemoryStore, UpdateMemoryInput } from "@stable-harness/memory";
2
2
  import type { ApprovalQueue, ApprovalRequest, ApprovalRequestStatus } from "@stable-harness/governance";
3
3
  import type { RuntimeWorkflowAdapter, RuntimeWorkflowRequest, WorkspaceWorkflow } from "./workflows/index.js";
4
4
  import type { SpecDrivenWorkflowState } from "./spec-driven/index.js";
5
5
  import type { RuntimeEvent, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
6
6
  import type { RuntimeToolFailureTracker } from "./runtime/tool-failure.js";
7
- import type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactRecord, RuntimeOutput, RuntimeRecordState, RuntimeRequest, RuntimeResponse, RuntimeReplayBundle, RuntimeRunFilter, RuntimeRunRecord } from "./runtime/types.js";
7
+ import type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactRecord, RuntimeOutput, RuntimeRecordState, RuntimeRequest, RuntimeResponse, RuntimeReplayBundle, RuntimeDeletionResult, RuntimeRunFilter, RuntimeRunRecord } from "./runtime/types.js";
8
8
  import type { RuntimeToolGateway } from "./runtime/tool-gateway.js";
9
9
  import type { CompiledWorkspace, WorkspaceAgent, WorkspaceRuntimePolicy } from "./workspace/types.js";
10
10
  export type { RuntimeEvent, RuntimeMemoryHook, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
11
11
  export type { BoundaryScanFinding, BoundaryScanLayer, BoundaryScanLayerResult, BoundaryScanResource, BoundaryScanResult } from "./boundary-scan.js";
12
12
  export type { RuntimeCapabilityContext, RuntimeCapabilityModule, RuntimeCapabilityRegistry, RuntimeCapabilityState } from "./runtime/capabilities.js";
13
13
  export type { RuntimeProgressNarrationOptions, RuntimeProgressNarrationProvider } from "./runtime/progress-narration.js";
14
- export type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactInput, RuntimeArtifactRecord, RuntimeArtifactStore, RuntimeOutputArtifact, RuntimeCancelIntentInput, RuntimeHeartbeatInput, RuntimeMemoryCandidateInput, RuntimeOutput, RuntimeQueueClaimInput, RuntimeQueueRecord, RuntimeQueueStore, RuntimeRecoveryIntent, RuntimeRecordState, RuntimeRequest, RuntimeRequestControlRecord, RuntimeRequestMemory, RuntimeResponse, RuntimeReplayBundle, RuntimeReplayBundleIntegrity, RuntimeRunFilter, RuntimeRunRecord, RuntimeStore, RuntimeStoreRunPatch, RuntimeStuckRequestInput, } from "./runtime/types.js";
14
+ export type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactInput, RuntimeArtifactRecord, RuntimeArtifactStore, RuntimeOutputArtifact, RuntimeCancelIntentInput, RuntimeHeartbeatInput, RuntimeMemoryCandidateInput, RuntimeOutput, RuntimeQueueClaimInput, RuntimeQueueRecord, RuntimeQueueStore, RuntimeRecoveryIntent, RuntimeRecordState, RuntimeRequest, RuntimeRequestControlRecord, RuntimeRequestMemory, RuntimeResponse, RuntimeDeletionResult, RuntimeReplayBundle, RuntimeReplayBundleIntegrity, RuntimeRunFilter, RuntimeRunRecord, RuntimeStore, RuntimeStoreRunPatch, RuntimeStuckRequestInput, } from "./runtime/types.js";
15
15
  export type { CompiledWorkspace, WorkspaceAdapterPolicy, WorkspaceAgent, WorkspaceBoundaryDiagnostic, WorkspaceBoundaryDiagnosticCode, WorkspaceBoundaryScanPolicy, WorkspaceMemory, WorkspaceModel, WorkspaceRetryPolicy, WorkspaceRetryReason, WorkspaceRetryTargetPolicy, WorkspaceRuntimePolicy, WorkspaceSkill, WorkspaceTool, WorkspaceToolRetryPolicy, WorkspaceValidationPolicy, } from "./workspace/types.js";
16
16
  export type { WorkspaceToolQualityDiagnostic, WorkspaceToolQualityDiagnosticCode, WorkspaceToolQualityPolicy, } from "./workspace/tool-quality.js";
17
17
  export type { WorkspaceEvaluation, WorkspaceEvaluationCase, } from "./evaluations/index.js";
@@ -140,6 +140,15 @@ export type RuntimeInspector = {
140
140
  };
141
141
  export type RuntimeLifecycle = {
142
142
  cancel(requestId: string, reason?: string): void;
143
+ deleteRequest(requestId: string): RuntimeDeletionResult;
144
+ deleteSession(sessionId: string): RuntimeDeletionResult;
143
145
  stop(): Promise<void>;
144
146
  };
145
- export type StableHarnessRuntime = RuntimeClient & RuntimeEventSource & RuntimeInspector & RuntimeLifecycle;
147
+ export type RuntimeMemoryAdministration = {
148
+ memorize(input: MemorizeInput): Promise<MemoryRecord>;
149
+ recallMemories(input: RecallInput): Promise<MemoryRecallResult>;
150
+ listMemories(input: ListMemoriesInput): Promise<MemoryRecord[]>;
151
+ updateMemory(input: UpdateMemoryInput): Promise<MemoryRecord | undefined>;
152
+ archiveMemory(id: string, reason?: string): Promise<MemoryRecord | undefined>;
153
+ };
154
+ export type StableHarnessRuntime = RuntimeClient & RuntimeEventSource & RuntimeInspector & RuntimeLifecycle & RuntimeMemoryAdministration;
@@ -41,6 +41,7 @@ export type WorkspaceAgent = {
41
41
  description?: string;
42
42
  sourcePath?: string;
43
43
  backend: string;
44
+ metadata?: Record<string, unknown>;
44
45
  modelRef?: string;
45
46
  systemPrompt?: string;
46
47
  tools: string[];
@@ -5,5 +5,5 @@ export type { LangMemServiceProviderOptions, } from "./langmem-service.js";
5
5
  export type { MemoryProvider, MemoryProviderApprovalConfig, MemoryProviderConfig, MemoryProviderConsolidateInput, MemoryProviderDefaults, MemoryProviderMode, MemoryProviderProposeInput, MemoryProviderSearchInput, MemoryProviderTypeConfig, } from "./provider.js";
6
6
  export { cloneRecords, createInMemoryMemoryPersistenceAdapter, createMemorySnapshot, } from "./persistence.js";
7
7
  export { createDefaultMemoryPolicy } from "./policy.js";
8
- export { createInMemoryRuntimeMemoryStore } from "./store.js";
8
+ export { createInMemoryRuntimeMemoryStore, createJsonFileRuntimeMemoryStore } from "./store.js";
9
9
  export type { ListMemoriesInput, MemorizeInput, MemoryCandidate, MemoryDecision, MemoryDecisionAction, MemoryKind, MemoryMaintenanceAction, MemoryMaintenanceOperation, MemoryMaintenanceResult, MemoryPolicy, MemoryPersistenceAdapter, MemoryRecallResult, MemoryRecord, MemoryRecordStatus, MemoryScope, MemorySensitivity, MemoryStoreSnapshot, MemorySubmitResult, RecallInput, RuntimeMemoryStore, UpdateMemoryInput, } from "./types.js";
@@ -1 +1 @@
1
- export{applyMemoryMaintenance}from"./maintenance.js";export{createEmbeddedMemoryProvider}from"./provider.js";export{createLangMemServiceProvider}from"./langmem-service.js";export{cloneRecords,createInMemoryMemoryPersistenceAdapter,createMemorySnapshot}from"./persistence.js";export{createDefaultMemoryPolicy}from"./policy.js";export{createInMemoryRuntimeMemoryStore}from"./store.js";
1
+ export{applyMemoryMaintenance}from"./maintenance.js";export{createEmbeddedMemoryProvider}from"./provider.js";export{createLangMemServiceProvider}from"./langmem-service.js";export{cloneRecords,createInMemoryMemoryPersistenceAdapter,createMemorySnapshot}from"./persistence.js";export{createDefaultMemoryPolicy}from"./policy.js";export{createInMemoryRuntimeMemoryStore,createJsonFileRuntimeMemoryStore}from"./store.js";
@@ -1 +1 @@
1
- export async function createMemorySnapshot(e,t){return{namespace:t.namespace,records:await e.list(t),exportedAt:(new Date).toISOString()}}export function createInMemoryMemoryPersistenceAdapter(){const e=new Map;return{load:async t=>cloneRecords(e.get(t)??[]),async save(t){e.set(t.namespace,cloneRecords(t.records))}}}export function cloneRecords(e){return e.map(e=>({...e,sourceRefs:[...e.sourceRefs],tags:[...e.tags],metadata:{...e.metadata},provenance:{...e.provenance},supersedes:[...e.supersedes],conflictsWith:[...e.conflictsWith]}))}
1
+ export async function createMemorySnapshot(e,t){return{namespace:t.namespace??"",records:await e.list(t),exportedAt:(new Date).toISOString()}}export function createInMemoryMemoryPersistenceAdapter(){const e=new Map;return{load:async t=>cloneRecords(e.get(t)??[]),async save(t){e.set(t.namespace,cloneRecords(t.records))}}}export function cloneRecords(e){return e.map(e=>({...e,sourceRefs:[...e.sourceRefs],tags:[...e.tags],metadata:{...e.metadata},provenance:{...e.provenance},supersedes:[...e.supersedes],conflictsWith:[...e.conflictsWith]}))}
@@ -3,3 +3,6 @@ export declare function createInMemoryRuntimeMemoryStore(options?: {
3
3
  policy?: MemoryPolicy;
4
4
  records?: MemoryRecord[];
5
5
  }): RuntimeMemoryStore;
6
+ export declare function createJsonFileRuntimeMemoryStore(filePath: string, options?: {
7
+ policy?: MemoryPolicy;
8
+ }): RuntimeMemoryStore;
@@ -1 +1 @@
1
- import{randomUUID as e}from"node:crypto";import{createDefaultMemoryPolicy as t}from"./policy.js";import{cloneRecords as n}from"./persistence.js";export function createInMemoryRuntimeMemoryStore(e={}){const r=e.policy??t(),o=n(e.records??[]);return{async submitCandidate(e){const t=r.decide(e);if("store"!==t.action)return{candidate:e,decision:t};const n=createRecord({namespace:e.namespace,content:e.content,kind:t.kind,scope:t.scope,summary:e.summary,confidence:t.confidence,tags:e.tags,sensitivity:e.sensitivity,sourceType:e.sourceType,sourceRef:e.sourceRef,metadata:e.metadata,provenance:e.provenance,observedAt:e.observedAt,retrievalPriority:t.retrievalPriority});return o.push(n),{candidate:e,decision:{...t,recordId:n.id},record:cloneRecord(n)}},async memorize(e){const t=createRecord(e);return o.push(t),cloneRecord(t)},async recall(e){const t=e.query.toLowerCase(),n=filterRecords(o,e).filter(e=>function matchesQuery(e,t){return[e.canonicalKey,e.content,e.summary??"",...e.tags].join("\n").toLowerCase().includes(t)}(e,t)).sort(compareRecallPriority).slice(0,e.limit??10);return{records:n,context:buildMemoryContext(n)}},list:async e=>n(filterRecords(o,e)),async update(e){const t=o.find(t=>t.id===e.id);if(t)return function applyUpdate(e,t){e.content=t.content??e.content,e.summary=t.summary??e.summary,e.status=t.status??e.status,e.confidence=normalizeConfidence(t.confidence??e.confidence),e.tags=t.tags??e.tags,e.metadata=t.metadata?{...e.metadata,...t.metadata}:e.metadata,e.lastConfirmedAt=(new Date).toISOString(),e.revision+=1}(t,e),cloneRecord(t)},async archive(e,t){const n=o.find(t=>t.id===e);if(n)return n.status="archived",n.metadata={...n.metadata,archiveReason:t},n.revision+=1,cloneRecord(n)}}}function cloneRecord(e){return n([e])[0]}function createRecord(t){const n=(new Date).toISOString();return{id:e(),namespace:t.namespace,canonicalKey:(r=t.namespace,o=t.content,`${r}:${o.trim().toLowerCase().replace(/\s+/gu," ").slice(0,120)}`),kind:t.kind??"semantic",scope:t.scope??"workspace",status:"active",content:t.content,summary:t.summary,confidence:normalizeConfidence(t.confidence),sourceType:t.sourceType,sourceRefs:t.sourceRef?[t.sourceRef]:[],tags:t.tags??[],sensitivity:t.sensitivity??"internal",metadata:t.metadata??{},createdAt:n,observedAt:t.observedAt??n,lastConfirmedAt:n,provenance:t.provenance??{},revision:1,supersedes:[],conflictsWith:[],retrievalPriority:t.retrievalPriority??0};var r,o}function filterRecords(e,t){return e.filter(e=>e.namespace===t.namespace).filter(e=>!t.kinds||t.kinds.includes(e.kind)).filter(e=>!t.scopes||t.scopes.includes(e.scope)).filter(e=>(t.statuses??["active"]).includes(e.status))}function compareRecallPriority(e,t){return t.retrievalPriority-e.retrievalPriority||t.confidence-e.confidence||t.lastConfirmedAt.localeCompare(e.lastConfirmedAt)}function buildMemoryContext(e){return e.map(e=>`- [${e.kind}/${e.scope}] ${e.summary??e.content}`).join("\n")}function normalizeConfidence(e){return void 0===e||Number.isNaN(e)?.6:Math.min(1,Math.max(0,e))}
1
+ import{existsSync as e,mkdirSync as r,readFileSync as t,writeFileSync as n}from"node:fs";import{randomUUID as o}from"node:crypto";import i from"node:path";import{createDefaultMemoryPolicy as a}from"./policy.js";import{cloneRecords as c}from"./persistence.js";export function createInMemoryRuntimeMemoryStore(e={}){const r=e.policy??a(),t=c(e.records??[]);return{async submitCandidate(e){const n=r.decide(e);if("store"!==n.action)return{candidate:e,decision:n};const o=createRecord({namespace:e.namespace,content:e.content,kind:n.kind,scope:n.scope,summary:e.summary,confidence:n.confidence,tags:e.tags,sensitivity:e.sensitivity,sourceType:e.sourceType,sourceRef:e.sourceRef,metadata:e.metadata,provenance:e.provenance,observedAt:e.observedAt,retrievalPriority:n.retrievalPriority});return t.push(o),{candidate:e,decision:{...n,recordId:o.id},record:cloneRecord(o)}},async memorize(e){const r=createRecord(e);return t.push(r),cloneRecord(r)},async recall(e){const r=e.query.toLowerCase(),n=filterRecords(t,e).filter(e=>function matchesQuery(e,r){return[e.canonicalKey,e.content,e.summary??"",...e.tags].join("\n").toLowerCase().includes(r)}(e,r)).sort(compareRecallPriority).slice(0,e.limit??10);return{records:n,context:buildMemoryContext(n)}},list:async e=>c(filterRecords(t,e)),async update(e){const r=t.find(r=>r.id===e.id);if(r)return function applyUpdate(e,r){e.content=r.content??e.content,e.summary=r.summary??e.summary,e.status=r.status??e.status,e.confidence=normalizeConfidence(r.confidence??e.confidence),e.tags=r.tags??e.tags,e.metadata=r.metadata?{...e.metadata,...r.metadata}:e.metadata,e.lastConfirmedAt=(new Date).toISOString(),e.revision+=1}(r,e),cloneRecord(r)},async archive(e,r){const n=t.find(r=>r.id===e);if(n)return n.status="archived",n.metadata={...n.metadata,archiveReason:r},n.revision+=1,cloneRecord(n)}}}export function createJsonFileRuntimeMemoryStore(e,r={}){const t=i.resolve(e),n=createInMemoryRuntimeMemoryStore({policy:r.policy,records:readStoreFile(t)});return{async submitCandidate(e){const r=await n.submitCandidate(e);return await persistMemoryStore(t,n),r},async memorize(e){const r=await n.memorize(e);return await persistMemoryStore(t,n),r},recall:e=>n.recall(e),list:e=>n.list(e),async update(e){const r=await n.update(e);return await persistMemoryStore(t,n),r},async archive(e,r){const o=await n.archive(e,r);return await persistMemoryStore(t,n),o}}}function cloneRecord(e){return c([e])[0]}async function persistMemoryStore(e,t){!function writeStoreFile(e,t){r(i.dirname(e),{recursive:!0}),n(e,`${JSON.stringify({version:1,records:t},null,2)}\n`)}(e,await t.list({statuses:["active","stale","conflicted","archived","pending_review"]}))}function readStoreFile(r){if(!e(r))return[];const n=JSON.parse(t(r,"utf8"));if(!isRecord(n)||!Array.isArray(n.records))throw new Error(`Invalid runtime memory store file: ${r}`);return c(n.records.map(assertMemoryRecord))}function assertMemoryRecord(e){if(!isRecord(e)||"string"!=typeof e.id||"string"!=typeof e.namespace)throw new Error("Invalid memory record in store file");return e}function createRecord(e){const r=(new Date).toISOString();return{id:o(),namespace:e.namespace,canonicalKey:(t=e.namespace,n=e.content,`${t}:${n.trim().toLowerCase().replace(/\s+/gu," ").slice(0,120)}`),kind:e.kind??"semantic",scope:e.scope??"workspace",status:"active",content:e.content,summary:e.summary,confidence:normalizeConfidence(e.confidence),sourceType:e.sourceType,sourceRefs:e.sourceRef?[e.sourceRef]:[],tags:e.tags??[],sensitivity:e.sensitivity??"internal",metadata:e.metadata??{},createdAt:r,observedAt:e.observedAt??r,lastConfirmedAt:r,provenance:e.provenance??{},revision:1,supersedes:[],conflictsWith:[],retrievalPriority:e.retrievalPriority??0};var t,n}function filterRecords(e,r){return e.filter(e=>!r.namespace||e.namespace===r.namespace).filter(e=>!r.kinds||r.kinds.includes(e.kind)).filter(e=>!r.scopes||r.scopes.includes(e.scope)).filter(e=>(r.statuses??["active"]).includes(e.status))}function compareRecallPriority(e,r){return r.retrievalPriority-e.retrievalPriority||r.confidence-e.confidence||r.lastConfirmedAt.localeCompare(e.lastConfirmedAt)}function buildMemoryContext(e){return e.map(e=>`- [${e.kind}/${e.scope}] ${e.summary??e.content}`).join("\n")}function normalizeConfidence(e){return void 0===e||Number.isNaN(e)?.6:Math.min(1,Math.max(0,e))}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -111,7 +111,7 @@ export type MemoryMaintenanceResult = {
111
111
  reason: string;
112
112
  };
113
113
  export type ListMemoriesInput = {
114
- namespace: string;
114
+ namespace?: string;
115
115
  kinds?: MemoryKind[];
116
116
  scopes?: MemoryScope[];
117
117
  statuses?: MemoryRecordStatus[];