stable-harness 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +189 -9
- package/dist/cli.js +1 -1
- package/dist/compat/agent-harness.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/runtime/compat/agent-harness-compat-runner.js +1 -1
- package/dist/runtime/compat/json.js +1 -1
- package/dist/runtime/compat/presentation.js +1 -1
- package/dist/runtime/compat/prompts.js +1 -1
- package/dist/runtime/model/ollama.js +1 -1
- package/dist/runtime/skills/skill-metadata.js +1 -1
- package/dist/workspace/compile.js +1 -1
- package/package.json +4 -3
- package/packages/adapter-deepagents/dist/src/adapter.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-args.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.d.ts +9 -4
- package/packages/adapter-deepagents/dist/src/internal/builtin-tool-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.d.ts +29 -1
- package/packages/adapter-deepagents/dist/src/internal/gateway-tools.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/messages.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.d.ts +12 -0
- package/packages/adapter-deepagents/dist/src/internal/raw-tool-call-parser.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.d.ts +10 -0
- package/packages/adapter-deepagents/dist/src/internal/skill-file-policy.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/stream-events.js +1 -1
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/internal/tool-repeat-visibility.js +1 -0
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.d.ts +1 -1
- package/packages/adapter-deepagents/dist/src/internal/trace-projection.js +1 -1
- package/packages/adapter-deepagents/dist/src/memory.js +1 -1
- package/packages/adapter-deepagents/dist/src/model-providers.d.ts +4 -0
- package/packages/adapter-deepagents/dist/src/model-providers.js +1 -0
- package/packages/adapter-deepagents/dist/src/retry-policy.js +1 -1
- package/packages/adapter-deepagents/dist/src/types.d.ts +7 -1
- package/packages/adapter-deepagents/package.json +1 -0
- package/packages/adapter-langgraph/dist/src/graph.js +1 -1
- package/packages/adapter-langgraph/dist/src/runtime.js +1 -1
- package/packages/adapter-langgraph/dist/src/skill-providers.js +1 -1
- package/packages/cli/dist/src/args.d.ts +6 -3
- package/packages/cli/dist/src/args.js +1 -1
- package/packages/cli/dist/src/cli.js +1 -1
- package/packages/cli/dist/src/event-view.d.ts +9 -0
- package/packages/cli/dist/src/event-view.js +1 -0
- package/packages/cli/dist/src/index.d.ts +3 -0
- package/packages/cli/dist/src/index.js +1 -1
- package/packages/cli/dist/src/langgraph-env.d.ts +5 -0
- package/packages/cli/dist/src/langgraph-env.js +1 -0
- package/packages/cli/dist/src/langgraph-official.d.ts +2 -0
- package/packages/cli/dist/src/langgraph-official.js +1 -1
- package/packages/cli/dist/src/memory/lifecycle.d.ts +2 -0
- package/packages/cli/dist/src/memory/lifecycle.js +1 -0
- package/packages/cli/dist/src/memory/providers.d.ts +3 -0
- package/packages/cli/dist/src/memory/providers.js +1 -0
- package/packages/cli/dist/src/output.js +1 -1
- package/packages/cli/dist/src/server.d.ts +2 -0
- package/packages/cli/dist/src/server.js +1 -1
- package/packages/cli/package.json +2 -0
- package/packages/core/dist/evaluations/index.d.ts +18 -0
- package/packages/core/dist/evaluations/index.js +1 -0
- package/packages/core/dist/execution-contract.js +1 -1
- package/packages/core/dist/index.d.ts +3 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/memory-plugins/maintenance.js +1 -1
- package/packages/core/dist/memory-plugins/shared.js +1 -1
- package/packages/core/dist/memory-plugins.js +1 -1
- package/packages/core/dist/recovery/tool-call.d.ts +15 -0
- package/packages/core/dist/recovery/tool-call.js +1 -1
- package/packages/core/dist/runtime/completion.js +1 -1
- package/packages/core/dist/runtime/direct-tool-call.js +1 -1
- package/packages/core/dist/runtime/events.d.ts +77 -20
- package/packages/core/dist/runtime/memory.js +1 -1
- package/packages/core/dist/runtime/persistence/artifacts.js +1 -1
- package/packages/core/dist/runtime/persistence/inspection.js +1 -1
- package/packages/core/dist/runtime/persistence/queue.js +1 -1
- package/packages/core/dist/runtime/persistence/stores.js +1 -1
- package/packages/core/dist/runtime/progress-narration.d.ts +33 -0
- package/packages/core/dist/runtime/progress-narration.js +1 -0
- package/packages/core/dist/runtime/tool-gateway.d.ts +5 -0
- package/packages/core/dist/runtime.d.ts +2 -1
- package/packages/core/dist/runtime.js +1 -1
- package/packages/core/dist/spec-driven/config.d.ts +4 -0
- package/packages/core/dist/spec-driven/config.js +1 -0
- package/packages/core/dist/spec-driven/events.d.ts +11 -0
- package/packages/core/dist/spec-driven/events.js +1 -0
- package/packages/core/dist/spec-driven/index.d.ts +4 -0
- package/packages/core/dist/spec-driven/index.js +1 -0
- package/packages/core/dist/spec-driven/lifecycle.d.ts +11 -0
- package/packages/core/dist/spec-driven/lifecycle.js +1 -0
- package/packages/core/dist/spec-driven/types.d.ts +38 -0
- package/packages/core/dist/spec-driven/types.js +1 -0
- package/packages/core/dist/trace.d.ts +1 -1
- package/packages/core/dist/trace.js +1 -1
- package/packages/core/dist/types.d.ts +15 -1
- package/packages/core/dist/workflows/index.js +1 -1
- package/packages/core/dist/workflows/runtime.js +1 -1
- package/packages/core/dist/workspace/types.d.ts +9 -0
- package/packages/governance/dist/src/skill-candidates.js +1 -1
- package/packages/memory/dist/src/langmem-service.js +1 -1
- package/packages/memory/dist/src/maintenance.js +1 -1
- package/packages/memory/dist/src/policy.js +1 -1
- package/packages/memory/dist/src/provider.js +1 -1
- package/packages/memory/dist/src/store.js +1 -1
- package/packages/protocols/dist/src/http-server.js +1 -1
- package/packages/protocols/dist/src/openai-compatible.js +1 -1
- package/packages/protocols/dist/src/openai-payload.js +1 -1
- package/packages/protocols/dist/src/openai-stream.js +1 -1
- package/packages/tool-gateway/dist/src/argument-guard.d.ts +2 -1
- package/packages/tool-gateway/dist/src/argument-guard.js +1 -1
- package/packages/tool-gateway/dist/src/in-memory.js +1 -1
- package/packages/tool-gateway/dist/src/module-loader.js +1 -1
- package/packages/tool-gateway/dist/src/schema-validation.js +1 -1
- package/packages/tool-gateway/dist/src/types.d.ts +3 -0
- package/packages/tool-gateway/package.json +1 -1
- package/packages/workspace-yaml/dist/discovery.js +1 -1
- package/packages/workspace-yaml/dist/documents.js +1 -1
- package/packages/workspace-yaml/dist/evaluations.d.ts +9 -0
- package/packages/workspace-yaml/dist/evaluations.js +1 -0
- package/packages/workspace-yaml/dist/loader.js +1 -1
- package/packages/workspace-yaml/dist/workflows.js +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{projectRequestInspection as
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{assertExecutionContract as t}from"./execution-contract.js";import{projectRequestInspection as r,projectRequestSummary as s,projectSessionSummaries as o}from"./runtime/persistence/inspection.js";import{recallMemoryPlugins as a,runMemoryPlugins as n}from"./memory-plugins.js";import{assertNoRawToolCallOutput as i,assertNoToolExecutionErrorOutput as u,buildAdapterErrorRecoveryPrompt as c,buildEvidenceSynthesisOutput as l,buildExecutionContractRecoveryRequest as p,buildResultRecoveryRequest as d,containsRawToolCallOutput as m,isRecoverableAdapterError as w,rawToolCallFailureMessage as f,toolCallRecoveryEnabled as y}from"./recovery/tool-call.js";import{completeRun as g,failRun as q}from"./runtime/completion.js";import{runDirectToolCall as I}from"./runtime/direct-tool-call.js";import{emitMemoryLifecycle as k,runMemoryRecall as R,submitMemoryCandidates as v}from"./runtime/memory.js";import{createInMemoryRuntimeStore as A}from"./runtime/persistence/stores.js";import{createProgressNarrationEvent as b,resolveProgressNarrator as C}from"./runtime/progress-narration.js";import{runWorkflowRequest as h}from"./workflows/runtime.js";export function createStableHarnessRuntime(t){const i=new Set,u=t.store??A(),l=C({options:t.progressNarration,policy:t.workspace.runtime}),emit=e=>{u.appendEvent(e);for(const t of i)t(e);!function tryEmitProgressNarration(e){try{const t=b({source:e.event,narrator:e.narrator,options:e.options,policy:e.policy});!function isPromiseLike(e){return"object"==typeof e&&null!==e&&"then"in e&&"function"==typeof e.then}(t)?t&&e.emitNarration(t):t.then(t=>{t&&e.emitNarration(t)}).catch(()=>{})}catch{return}}({event:e,narrator:l,options:t.progressNarration,policy:t.workspace.runtime,emitNarration:e=>{u.appendEvent(e);for(const t of i)t(e)}})};return{request:async r=>async function runRuntimeRequest(t){const{agent:r,adapter:s}=function resolveExecution(e,t){const r=t.agentId??e.workspace.runtime.defaultAgentId,s=e.workspace.agents.get(r);if(!s)throw new Error(`Agent ${r} is not defined in the workspace`);if(t.toolCall||t.workflow)return{agent:s,adapter:void 0};const o=e.adapters.find(e=>e.canRun(s));if(!o)throw new Error(`No runtime adapter can run backend ${s.backend} for agent ${s.id}`);return{agent:s,adapter:o}}(t.input,t.request),o=t.request.requestId??e(),i=t.request.sessionId??e();t.store.createRun(function createRunRecord(e,t,r,s){return{requestId:t,sessionId:r,agentId:s.id,input:e.input,state:"running",parentRunId:e.parentRunId,metadata:e.metadata,artifacts:[],startedAt:(new Date).toISOString(),events:[]}}(t.request,o,i,r)),t.emit({type:"runtime.request.started",requestId:o,sessionId:i,agentId:r.id,input:t.request.input});try{if(t.request.workflow){const e=await h({workspace:t.input.workspace,adapters:t.input.workflowAdapters??[],toolGateway:t.input.toolGateway,request:{input:t.request.input,...t.request.workflow},requestId:o,sessionId:i,agentId:r.id,emit:t.emit});return g({store:t.store,emit:t.emit,requestId:o,sessionId:i,agent:r,result:e})}if(t.request.toolCall){const e=await I({gateway:t.input.toolGateway,workspace:t.input.workspace,emit:t.emit,request:t.request,requestId:o,sessionId:i,agent:r});return g({store:t.store,emit:t.emit,requestId:o,sessionId:i,agent:r,result:e})}return await async function runAdapterRequest(e){if(!e.adapter)throw new Error(`No runtime adapter can run backend ${e.agent.backend} for agent ${e.agent.id}`);const t=e.adapter,r=await R({memory:e.input.memory,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,workspace:e.input.workspace}),s=await a({providers:e.input.memoryProviders,request:e.request,agent:e.agent,workspace:e.input.workspace}),o=e.input.workspace.runtime,i=new Map;let u;try{u=await runAdapterOnce(e,t,e.request,r,s,i)}catch(a){if(!w(a,o))throw a;u=await runAdapterOnce(e,t,c(e.request,a,o),r,s,i)}u=await recoverAdapterResultOutput(e,t,e.request,u,r,s,o,i),k(e.input.memory,e.emit,e.requestId,e.sessionId,e.agent.id,"read-before-finalize"),await v({memory:e.input.memory,approvals:e.input.approvals,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,workspace:e.input.workspace});try{assertRequestExecutionContract(e)}catch(a){const n=p({request:e.request,events:e.store.getRun(e.requestId)?.events??[],policy:o});if(!n)throw a;u=await runAdapterOnce(e,t,n,r,s,i),u=await recoverAdapterResultOutput(e,t,n,u,r,s,o,i),assertRequestExecutionContract(e)}const l=g({store:e.store,emit:e.emit,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,result:u});return await n({providers:e.input.memoryProviders,emit:e.emit,request:e.request,requestId:e.requestId,sessionId:e.sessionId,agent:e.agent,workspace:e.input.workspace,result:u}),l}({...t,adapter:s,requestId:o,sessionId:i,agent:r})}catch(e){return q({store:t.store,emit:t.emit,requestId:o,sessionId:i,agent:r,error:e})}}({input:t,store:u,emit:emit,request:r}),subscribe:e=>(i.add(e),()=>i.delete(e)),inspect(){return{workspaceRoot:t.workspace.root,agents:[...t.workspace.agents.keys()].sort(),workflows:[...t.workspace.workflows.keys()].sort(),...t.workspace.runtime.specDrivenWorkflow?{specDrivenWorkflow:(e=t.workspace.runtime.specDrivenWorkflow,{enabled:e.enabled,artifactsDir:e.artifactsDir,...e.constitution?{constitution:e.constitution}:{},phases:e.phases.map(e=>e.id)})}:{},evaluations:[...t.workspace.evaluations?.keys()??[]].sort(),...t.workspace.runtime.workflowRouting?.defaultWorkflowId?{defaultWorkflowId:t.workspace.runtime.workflowRouting.defaultWorkflowId}:{},workflowRoutes:(t.workspace.runtime.workflowRouting?.routes??[]).map(e=>e.id).sort(),models:[...t.workspace.models.keys()].sort(),tools:[...t.workspace.tools.keys()].sort(),runs:u.listRuns()};var e},getRuntimePolicy:()=>t.workspace.runtime,getWorkflow:e=>t.workspace.workflows.get(e),getRun:e=>u.getRun(e),listRequests:e=>u.listRuns(e).map(s),listSessions:()=>o(u.listRuns()),inspectRequest(e){const s=u.getRun(e);return s?r(t.workspace,s):void 0},cancel(e,t){const r=u.getRun(e);r&&"running"===r.state&&(u.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(){i.clear()}}}async function recoverAdapterResultOutput(e,t,r,s,o,a,n,c){let p=r;const w=function resultRecoveryAttempts(e){const t="object"!=typeof e||null===e||Array.isArray(e)?void 0:e.recovery,r="object"!=typeof t||null===t||Array.isArray(t)?void 0:t.toolCall,s="object"!=typeof r||null===r||Array.isArray(r)?void 0:r.maxResultRecoveryAttempts;return"number"==typeof s&&Number.isInteger(s)&&s>0?s:3}(n);let g=0;for(let r=0;r<w;r+=1){const r=e.store.getRun(e.requestId)?.events??[],i=d({request:p,output:s.text,events:r.slice(g),availableToolIds:e.agent.tools,policy:n});if(!i)break;p=i,g=e.store.getRun(e.requestId)?.events.length??0,s=await runAdapterOnce(e,t,i,o,a,c)}if(y(n)){let t=!1;m(s.text,n)&&function rawToolCallFailureReturnsMessage(e){return"message"===("object"!=typeof e?.toolCallRecovery||null===e.toolCallRecovery||Array.isArray(e.toolCallRecovery)?{}:e.toolCallRecovery).onFailure}(r.metadata)&&(s={...s,text:f(),metadata:{...s.metadata,toolCallRecovery:{failed:!0,reason:"raw_tool_call_output"}}});const o=l({request:r,output:s.text,events:e.store.getRun(e.requestId)?.events??[],policy:n});o&&(t=!0,s={...s,text:o,metadata:{...s.metadata,toolCallRecovery:{synthesized:!0,reason:"raw_tool_call_output_with_evidence"}}}),t||(i(s.text,n),u(s.text,n))}return s}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,s,o,a){return function normalizeAdapterResult(e){return"string"==typeof e?{text:e}:e}(await t.run({workspace:e.input.workspace,agent:e.agent,request:r,requestId:e.requestId,sessionId:e.sessionId,memory:s,pluginMemories:o,toolGateway:e.input.toolGateway,requestState:a,emit:e.emit}))}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_SPEC_DRIVEN_PHASES: WorkspaceSpecDrivenPhase[];
|
|
3
|
+
export declare function createSpecDrivenWorkflowPolicy(input?: Partial<WorkspaceSpecDrivenWorkflowPolicy>): WorkspaceSpecDrivenWorkflowPolicy;
|
|
4
|
+
export declare function assertSpecDrivenWorkflowPolicy(policy: WorkspaceSpecDrivenWorkflowPolicy): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_SPEC_DRIVEN_PHASES=[{id:"constitution",artifactKind:"constitution",required:!0},{id:"specify",artifactKind:"spec",required:!0},{id:"plan",artifactKind:"plan",required:!0},{id:"contracts",artifactKind:"contract"},{id:"research",artifactKind:"research"},{id:"tasks",artifactKind:"tasks",required:!0},{id:"implement",artifactKind:"execution"},{id:"verify",artifactKind:"evidence",required:!0}];export function createSpecDrivenWorkflowPolicy(i){return{enabled:i?.enabled??!1,...i?.constitution?{constitution:i.constitution}:{},artifactsDir:i?.artifactsDir??"specs",phases:(t=i?.phases,(t&&t.length>0?t:DEFAULT_SPEC_DRIVEN_PHASES).map(i=>({id:i.id,...i.artifactKind?{artifactKind:i.artifactKind}:{},...void 0!==i.required?{required:i.required}:{},...i.gate?{gate:i.gate}:{},...i.config?{config:i.config}:{}}))),...i?.gates?{gates:i.gates}:{},...i?.config?{config:i.config}:{}};var t}export function assertSpecDrivenWorkflowPolicy(i){const t=new Set;for(const e of i.phases){if(!e.id.trim())throw new Error("specDrivenWorkflow phases require non-empty id");if(t.has(e.id))throw new Error(`specDrivenWorkflow phase is duplicated: ${e.id}`);t.add(e.id)}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RuntimeEvent } from "../runtime/events.js";
|
|
2
|
+
import type { RuntimeArtifact } from "../runtime/types.js";
|
|
3
|
+
import type { SpecDrivenPhaseTransition } from "./types.js";
|
|
4
|
+
export type SpecDrivenEventContext = {
|
|
5
|
+
requestId: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
agentId: string;
|
|
8
|
+
workflowId?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function createSpecDrivenPhaseEvent(context: SpecDrivenEventContext, transition: SpecDrivenPhaseTransition): RuntimeEvent;
|
|
11
|
+
export declare function createSpecDrivenArtifactEvent(context: SpecDrivenEventContext, phaseId: string, artifact: RuntimeArtifact): RuntimeEvent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createSpecDrivenPhaseEvent(e,t){const r={requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,phaseId:t.phaseId,...e.workflowId?{workflowId:e.workflowId}:{}};return"started"===t.status?{type:"runtime.specDriven.phase.started",...r}:"completed"===t.status?{type:"runtime.specDriven.phase.completed",...r,artifact:t.artifact}:"verified"===t.status?{type:"runtime.specDriven.phase.verified",...r,artifact:t.artifact}:{type:"runtime.specDriven.phase.blocked",...r,reason:t.reason??"unspecified"}}export function createSpecDrivenArtifactEvent(e,t,r){return{type:"runtime.artifact.created",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,artifact:{...r,metadata:{...r.metadata,phaseId:t,workflowKind:"spec-driven"}}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export*from"./config.js";export*from"./events.js";export*from"./lifecycle.js";export*from"./types.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RuntimeArtifact } from "../runtime/types.js";
|
|
2
|
+
import type { SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceSpecDrivenWorkflowPolicy } from "./types.js";
|
|
3
|
+
export declare function createSpecDrivenWorkflowState(policy: WorkspaceSpecDrivenWorkflowPolicy): SpecDrivenWorkflowState;
|
|
4
|
+
export declare function applySpecDrivenPhaseTransition(state: SpecDrivenWorkflowState, transition: SpecDrivenPhaseTransition): SpecDrivenWorkflowState;
|
|
5
|
+
export declare function createSpecDrivenArtifact(input: {
|
|
6
|
+
id: string;
|
|
7
|
+
phaseId: string;
|
|
8
|
+
artifactKind: string;
|
|
9
|
+
uri?: string;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
}): RuntimeArtifact;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createSpecDrivenWorkflowState(t){return{enabled:t.enabled,artifactsDir:t.artifactsDir,...t.constitution?{constitution:t.constitution}:{},phases:t.phases.map(t=>({id:t.id,status:"pending",updatedAt:new Date(0).toISOString()}))}}export function applySpecDrivenPhaseTransition(t,a){if(!new Set(t.phases.map(t=>t.id)).has(a.phaseId))throw new Error(`Spec-driven workflow phase is not configured: ${a.phaseId}`);return{...t,phases:t.phases.map(t=>function updatePhase(t,a){return t.id!==a.phaseId?t:{id:t.id,status:a.status,...a.artifact?{artifact:a.artifact}:t.artifact?{artifact:t.artifact}:{},...a.reason?{reason:a.reason}:{},updatedAt:a.timestamp??(new Date).toISOString()}}(t,a))}}export function createSpecDrivenArtifact(t){return{id:t.id,kind:`spec-driven.${t.artifactKind}`,...t.uri?{uri:t.uri}:{},metadata:{phaseId:t.phaseId,...t.metadata}}}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RuntimeArtifact } from "../runtime/types.js";
|
|
2
|
+
export type SpecDrivenPhaseId = string;
|
|
3
|
+
export type SpecDrivenPhaseStatus = "pending" | "started" | "blocked" | "completed" | "verified";
|
|
4
|
+
export type WorkspaceSpecDrivenPhase = {
|
|
5
|
+
id: string;
|
|
6
|
+
artifactKind?: string;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
gate?: string;
|
|
9
|
+
config?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
export type WorkspaceSpecDrivenWorkflowPolicy = {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
constitution?: string;
|
|
14
|
+
artifactsDir: string;
|
|
15
|
+
phases: WorkspaceSpecDrivenPhase[];
|
|
16
|
+
gates?: Record<string, unknown>;
|
|
17
|
+
config?: Record<string, unknown>;
|
|
18
|
+
};
|
|
19
|
+
export type SpecDrivenPhaseRecord = {
|
|
20
|
+
id: string;
|
|
21
|
+
status: SpecDrivenPhaseStatus;
|
|
22
|
+
artifact?: RuntimeArtifact;
|
|
23
|
+
reason?: string;
|
|
24
|
+
updatedAt: string;
|
|
25
|
+
};
|
|
26
|
+
export type SpecDrivenWorkflowState = {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
artifactsDir: string;
|
|
29
|
+
constitution?: string;
|
|
30
|
+
phases: SpecDrivenPhaseRecord[];
|
|
31
|
+
};
|
|
32
|
+
export type SpecDrivenPhaseTransition = {
|
|
33
|
+
phaseId: string;
|
|
34
|
+
status: Exclude<SpecDrivenPhaseStatus, "pending">;
|
|
35
|
+
artifact?: RuntimeArtifact;
|
|
36
|
+
reason?: string;
|
|
37
|
+
timestamp?: string;
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -4,7 +4,7 @@ export type PlanTodoItem = {
|
|
|
4
4
|
status: string;
|
|
5
5
|
};
|
|
6
6
|
export type RuntimeTraceEntry = {
|
|
7
|
-
type: "request" | "tool" | "adapter" | "memory" | "artifact" | "plan" | "delegation";
|
|
7
|
+
type: "request" | "tool" | "workflow" | "spec" | "adapter" | "memory" | "artifact" | "progress" | "plan" | "delegation";
|
|
8
8
|
label: string;
|
|
9
9
|
agentId: string;
|
|
10
10
|
requestId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function projectRuntimeTrace(
|
|
1
|
+
export function projectRuntimeTrace(e){return e.events.map(projectEvent).filter(isTraceEntry)}export function projectEvent(e){return"runtime.request.started"===e.type||"runtime.request.completed"===e.type||"runtime.request.failed"===e.type?base(e,"request",e.type):"runtime.request.cancelled"===e.type?base(e,"request",e.type,{reason:e.reason}):"runtime.execution.contract.failed"===e.type?base(e,"request",e.type,{reason:e.reason,missingEvidenceTools:e.missingEvidenceTools}):"runtime.tool.direct.started"===e.type?base(e,"tool","runtime.tool.direct.started",{toolId:e.toolId}):"runtime.tool.direct.completed"===e.type?base(e,"tool","runtime.tool.direct.completed",{toolId:e.toolId}):"runtime.workflow.started"===e.type||"runtime.workflow.completed"===e.type?base(e,"workflow",e.type,{workflowId:e.workflowId,adapter:e.adapter}):function isSpecDrivenPhaseEvent(e){return e.type.startsWith("runtime.specDriven.phase.")}(e)?base(e,"spec",e.type,{phaseId:e.phaseId,..."workflowId"in e&&e.workflowId?{workflowId:e.workflowId}:{},..."reason"in e?{reason:e.reason}:{},..."artifact"in e&&e.artifact?{artifact:e.artifact}:{}}):"runtime.adapter.event"===e.type?function adapterTrace(e){const t=e.event;if(isRecord(t)&&"string"==typeof t.phase){const r=function semanticAdapterTrace(e,t){const r=function readTraceType(e){const t=readString(e);return"request"===t||"tool"===t||"workflow"===t||"spec"===t||"adapter"===t||"memory"===t||"artifact"===t||"progress"===t||"plan"===t||"delegation"===t?t:void 0}(t.traceType),o=readString(t.traceLabel);if(r&&o)return base(e,r,o,t)}(e,t);return r||base(e,"adapter",t.phase.startsWith("agent.")?t.phase:`adapter.${t.phase}`,t)}return base(e,"adapter","runtime.adapter.event",{event:t})}(e):"runtime.artifact.created"===e.type?base(e,"artifact","runtime.artifact.created",{artifact:e.artifact}):e.type.startsWith("runtime.memory.")?base(e,"memory",e.type):"runtime.progress.narration"===e.type?base(e,"progress",e.type,{message:e.message,provider:e.provider,sourceEventTypes:e.sourceEventTypes}):void 0}export function readPlanTodos(e){const t=function readPlanRecord(e){if(isRecord(e))return e;if("string"==typeof e)try{const t=JSON.parse(e);return isRecord(t)?t:void 0}catch{return}}(e),r=function readTodosArray(e){const t=isRecord(e?.args)?e.args:void 0;return Array.isArray(e?.todos)?e.todos:Array.isArray(t?.todos)?t.todos:[]}(t);return r.map(readTodo).filter(isPlanTodoItem)}function readTodo(e){if(isRecord(e)&&"string"==typeof e.content)return{content:e.content,status:"string"==typeof e.status?e.status:"pending"}}function isPlanTodoItem(e){return void 0!==e}function base(e,t,r,o){return{type:t,label:r,agentId:e.agentId,requestId:e.requestId,detail:o}}function isTraceEntry(e){return void 0!==e}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { MemoryProvider, MemoryRecord, RuntimeMemoryStore } from "@stable-harness/memory";
|
|
2
2
|
import type { ApprovalQueue } from "@stable-harness/governance";
|
|
3
3
|
import type { RuntimeWorkflowAdapter, RuntimeWorkflowRequest, WorkspaceWorkflow } from "./workflows/index.js";
|
|
4
|
+
import type { SpecDrivenWorkflowState } from "./spec-driven/index.js";
|
|
4
5
|
import type { RuntimeEvent, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
|
|
5
6
|
import type { RuntimeArtifact, RuntimeOutput, RuntimeRecordState, RuntimeRequest, RuntimeResponse, RuntimeRunFilter, RuntimeRunRecord } from "./runtime/types.js";
|
|
6
7
|
import type { RuntimeToolGateway } from "./runtime/tool-gateway.js";
|
|
7
8
|
import type { CompiledWorkspace, WorkspaceAgent, WorkspaceRuntimePolicy } from "./workspace/types.js";
|
|
8
9
|
export type { RuntimeEvent, RuntimeMemoryHook, RuntimeEventListener, RuntimeEmit } from "./runtime/events.js";
|
|
10
|
+
export type { RuntimeProgressNarrationOptions, RuntimeProgressNarrationProvider } from "./runtime/progress-narration.js";
|
|
9
11
|
export type { RuntimeArtifact, RuntimeArtifactFilter, RuntimeArtifactInput, RuntimeArtifactRecord, RuntimeArtifactStore, RuntimeCancelIntentInput, RuntimeHeartbeatInput, RuntimeMemoryCandidateInput, RuntimeOutput, RuntimeQueueClaimInput, RuntimeQueueRecord, RuntimeQueueStore, RuntimeRecoveryIntent, RuntimeRecordState, RuntimeRequest, RuntimeRequestControlRecord, RuntimeRequestMemory, RuntimeResponse, RuntimeRunFilter, RuntimeRunRecord, RuntimeStore, RuntimeStoreRunPatch, RuntimeStuckRequestInput, } from "./runtime/types.js";
|
|
10
12
|
export type { CompiledWorkspace, WorkspaceAdapterPolicy, WorkspaceAgent, WorkspaceMemory, WorkspaceModel, WorkspaceRetryPolicy, WorkspaceRetryReason, WorkspaceRetryTargetPolicy, WorkspaceRuntimePolicy, WorkspaceSkill, WorkspaceTool, WorkspaceToolRetryPolicy, } from "./workspace/types.js";
|
|
11
|
-
export type {
|
|
13
|
+
export type { WorkspaceEvaluation, WorkspaceEvaluationCase, } from "./evaluations/index.js";
|
|
14
|
+
export type { SpecDrivenPhaseId, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, } from "./spec-driven/index.js";
|
|
15
|
+
export type { RuntimeToolCallRequest, RuntimeToolGateway, RuntimeToolGatewayContext, RuntimeToolRepairModel, RuntimeToolGatewayTool, } from "./runtime/tool-gateway.js";
|
|
12
16
|
export type RuntimeAdapterContext = {
|
|
13
17
|
workspace: CompiledWorkspace;
|
|
14
18
|
agent: WorkspaceAgent;
|
|
@@ -18,6 +22,7 @@ export type RuntimeAdapterContext = {
|
|
|
18
22
|
memory?: RuntimeMemoryContext;
|
|
19
23
|
pluginMemories?: RuntimeMemoryContext[];
|
|
20
24
|
toolGateway?: RuntimeToolGateway;
|
|
25
|
+
requestState?: Map<string, unknown>;
|
|
21
26
|
emit: RuntimeEmit;
|
|
22
27
|
};
|
|
23
28
|
export type RuntimeMemoryContext = {
|
|
@@ -46,12 +51,20 @@ export type RuntimeInspectionSnapshot = {
|
|
|
46
51
|
workspaceRoot: string;
|
|
47
52
|
agents: string[];
|
|
48
53
|
workflows: string[];
|
|
54
|
+
specDrivenWorkflow?: RuntimeSpecDrivenWorkflowInspection;
|
|
55
|
+
evaluations: string[];
|
|
49
56
|
defaultWorkflowId?: string;
|
|
50
57
|
workflowRoutes: string[];
|
|
51
58
|
models: string[];
|
|
52
59
|
tools: string[];
|
|
53
60
|
runs: RuntimeRunRecord[];
|
|
54
61
|
};
|
|
62
|
+
export type RuntimeSpecDrivenWorkflowInspection = {
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
artifactsDir: string;
|
|
65
|
+
constitution?: string;
|
|
66
|
+
phases: string[];
|
|
67
|
+
};
|
|
55
68
|
export type RuntimeRequestSummary = {
|
|
56
69
|
requestId: string;
|
|
57
70
|
sessionId: string;
|
|
@@ -93,6 +106,7 @@ export type RuntimeRequestInspection = {
|
|
|
93
106
|
metadata?: Record<string, unknown>;
|
|
94
107
|
artifacts: RuntimeArtifact[];
|
|
95
108
|
runtimeSnapshot: RuntimeBindingSnapshot;
|
|
109
|
+
specDrivenWorkflow?: SpecDrivenWorkflowState;
|
|
96
110
|
timeline: RuntimeTimelineItem[];
|
|
97
111
|
};
|
|
98
112
|
export type RuntimeClient = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function compileWorkflowPlan(
|
|
1
|
+
export function compileWorkflowPlan(e){const n=e.entry??e.nodes[0]?.id;if(!n)throw new Error(`Workflow ${e.id} has no entry node`);const t=function buildAdjacency(e){const n=new Map(e.nodes.map(e=>[e.id,[]]));for(const t of e.edges)n.get(t.from)?.push(t.to);return n}(e),o=function collectReachable(e,n){const t=new Set,o=[e];for(;o.length>0;){const e=o.pop();t.has(e)||(t.add(e),o.push(...n.get(e)??[]))}return t}(n,t),a=function findCycles(e,n){const t=[];for(const o of e)collectCycles(o,o,n,[],t);return function dedupeCycles(e){const n=new Set;return e.filter(e=>{const t=function canonicalCycleKey(e){const n=e.slice(0,-1);return n.map((e,t)=>[...n.slice(t),...n.slice(0,t)].join("|")).sort()[0]??""}(e);return!n.has(t)&&(n.add(t),!0)})}(t)}(e.nodes.map(e=>e.id),t);return{workflowId:e.id,...e.adapter?{adapter:e.adapter}:{},entry:n,nodes:e.nodes,edges:e.edges,terminalNodes:e.nodes.map(e=>e.id).filter(e=>0===(t.get(e)??[]).length),unreachableNodes:e.nodes.map(e=>e.id).filter(e=>!o.has(e)),cycles:a}}export function renderWorkflowMermaid(e){const n=["flowchart TD"];for(const t of e.nodes)n.push(` ${escapeId(t.id)}["${escapeLabel(`${t.id}\\n${t.use}`)}"]`);for(const t of e.edges){const e=t.condition?`|${escapeLabel(t.condition)}|`:"";n.push(` ${escapeId(t.from)} --\x3e${e} ${escapeId(t.to)}`)}return n.join("\n")}export function renderAgentMermaid(e,n){const t=e.agents.get(n);if(!t)throw new Error(`Agent is not defined: ${n}`);const o=function agentGraphNodes(e){return[...e.subagents.map(e=>agentGraphNode("agent",e)),...(e.skills??[]).map(e=>agentGraphNode("skill",e)),...e.tools.map(e=>agentGraphNode("tool",e))]}(t),a=["flowchart TD",` START_${escapeId(t.id)}([START])`,` END_${escapeId(t.id)}([END])`];for(const e of o)a.push(` ${e.diagramId}["${escapeLabel(`${e.id}\\n${e.label}`)}"]`);return 0===o.length?(a.push(` START_${escapeId(t.id)} --\x3e END_${escapeId(t.id)}`),a.join("\n")):[...a,...t.edges?.length?explicitAgentMermaidEdges(t,o):inventoryAgentMermaidEdges(t,o)].join("\n")}function agentGraphNode(e,n){return{id:n,diagramId:`${e}_${escapeId(n)}`,label:`${e}: ${n}`}}function explicitAgentMermaidEdges(e,n){const t=new Map(n.map(e=>[e.id,e.diagramId])),o=new Set((e.edges??[]).map(e=>e.to)),a=new Set((e.edges??[]).map(e=>e.from)),d=n.filter(e=>!o.has(e.id)).map(e=>e.diagramId),r=n.filter(e=>!a.has(e.id)).map(e=>e.diagramId);return[...d.map(n=>` START_${escapeId(e.id)} --\x3e ${escapeId(n)}`),...(e.edges??[]).flatMap(e=>function renderAgentEdge(e,n){const t=n.get(e.from),o=n.get(e.to);return t&&o?[` ${t} --\x3e${e.condition?`|${escapeLabel(e.condition)}|`:""} ${o}`]:[]}(e,t)),...r.map(n=>` ${escapeId(n)} --\x3e END_${escapeId(e.id)}`)]}function inventoryAgentMermaidEdges(e,n){return n.flatMap(n=>[` START_${escapeId(e.id)} -. available .-> ${n.diagramId}`,` ${n.diagramId} -.-> END_${escapeId(e.id)}`])}function collectCycles(e,n,t,o,a){const d=[...o,n];for(const r of t.get(n)??[])r===e?a.push([...d,e]):o.includes(r)||collectCycles(e,r,t,d,a)}function escapeId(e){return e.replace(/[^A-Za-z0-9_]/gu,"_")}function escapeLabel(e){return e.replace(/"/gu,'\\"')}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export async function runWorkflowRequest(e){const o=function(e,o){const
|
|
1
|
+
export async function runWorkflowRequest(e){const o=function resolveWorkflow(e,o){const r=o.routeId?e.runtime.workflowRouting?.routes?.find(e=>e.id===o.routeId):void 0;if(o.routeId&&!r)throw new Error(`Workflow route is not defined: ${o.routeId}`);const t=o.workflowId??r?.workflowId??e.runtime.workflowRouting?.defaultWorkflowId;if(!t)throw new Error("Workflow request requires workflowId, routeId, or runtime.workflowRouting.defaultWorkflowId");const n=e.workflows.get(t);if(!n)throw new Error(`Workflow is not defined: ${t}`);return n}(e.workspace,e.request),r=function resolveWorkflowAdapter(e,o){const r=o?e.find(e=>e.name===o):e[0];if(!r)throw new Error("No workflow adapter is configured"+(o?` for ${o}`:""));return r}(e.adapters,o.adapter);e.emit({type:"runtime.workflow.started",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,adapter:r.name,workflowId:o.id});const t=await r.run({workspace:e.workspace,workflow:o,request:e.request,requestId:e.requestId,sessionId:e.sessionId,toolGateway:e.toolGateway,emit:o=>e.emit({type:"runtime.adapter.event",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,event:o})});return e.emit({type:"runtime.workflow.completed",requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,adapter:r.name,workflowId:o.id}),"string"==typeof t?{text:t}:t}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { WorkspaceEvaluation } from "../evaluations/index.js";
|
|
2
|
+
import type { WorkspaceSpecDrivenWorkflowPolicy } from "../spec-driven/index.js";
|
|
1
3
|
import type { WorkspaceWorkflow, WorkspaceWorkflowEdge, WorkspaceWorkflowRoutingPolicy } from "../workflows/index.js";
|
|
2
4
|
export type WorkspaceModel = {
|
|
3
5
|
id: string;
|
|
@@ -51,11 +53,17 @@ export type WorkspaceRuntimePolicy = {
|
|
|
51
53
|
profile?: string;
|
|
52
54
|
adapters?: WorkspaceAdapterPolicy[];
|
|
53
55
|
workflowRouting?: WorkspaceWorkflowRoutingPolicy;
|
|
56
|
+
specDrivenWorkflow?: WorkspaceSpecDrivenWorkflowPolicy;
|
|
54
57
|
approvals?: Record<string, unknown>;
|
|
55
58
|
recovery?: Record<string, unknown>;
|
|
56
59
|
retry?: WorkspaceRetryPolicy;
|
|
60
|
+
toolGateway?: Record<string, unknown>;
|
|
57
61
|
memory?: Record<string, unknown>;
|
|
58
62
|
protocols?: Record<string, unknown>;
|
|
63
|
+
progress?: Record<string, unknown>;
|
|
64
|
+
cli?: Record<string, unknown>;
|
|
65
|
+
responseLanguage?: Record<string, unknown>;
|
|
66
|
+
responsePresentation?: Record<string, unknown>;
|
|
59
67
|
};
|
|
60
68
|
export type WorkspaceAdapterPolicy = {
|
|
61
69
|
name: string;
|
|
@@ -89,4 +97,5 @@ export type CompiledWorkspace = {
|
|
|
89
97
|
skills: Map<string, WorkspaceSkill>;
|
|
90
98
|
memories: Map<string, WorkspaceMemory>;
|
|
91
99
|
workflows: Map<string, WorkspaceWorkflow>;
|
|
100
|
+
evaluations?: Map<string, WorkspaceEvaluation>;
|
|
92
101
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as e}from"node:crypto";import{mkdirSync as t}from"node:fs";import n from"node:path";import{DatabaseSync as
|
|
1
|
+
import{randomUUID as e}from"node:crypto";import{mkdirSync as t}from"node:fs";import n from"node:path";import{DatabaseSync as a}from"node:sqlite";export function createSqliteSkillCandidateStore(d){t(n.dirname(d),{recursive:!0});const r=new a(d);return function init(e){e.exec("\n create table if not exists skill_candidates (\n id text primary key, workspace_id text not null, name text not null,\n status text not null, confidence real not null, evidence_count integer not null,\n proposed_path text, title text not null, summary text not null,\n draft_markdown text not null, created_at text not null, updated_at text not null,\n reviewed_at text, reviewed_by text, rejection_reason text,\n unique(workspace_id, name)\n );\n create table if not exists skill_candidate_evidence (\n id text primary key, candidate_id text not null, evidence_type text not null,\n evidence_ref text not null, summary text, weight real, created_at text not null,\n foreign key(candidate_id) references skill_candidates(id)\n );\n ")}(r),{async upsert(t){const n=(new Date).toISOString(),a=function findByName(e,t,n){const a=e.prepare("select * from skill_candidates where workspace_id = ? and name = ?").get(t,n);return a?rowToCandidate(a):void 0}(r,t.workspaceId,t.name),d=a?function updateCandidate(e,t,n,a){return e.prepare("\n update skill_candidates\n set status = ?, confidence = ?, evidence_count = ?, proposed_path = ?,\n title = ?, summary = ?, draft_markdown = ?, updated_at = ?\n where id = ?\n ").run(n.status??"review_required",n.confidence,n.evidence.length,n.proposedPath??null,n.title,n.summary,n.draftMarkdown,a,t),readCandidate(e,t)}(r,a.id,t,n):function insertCandidate(t,n,a){const d=String(e());return t.prepare("\n insert into skill_candidates\n (id, workspace_id, name, status, confidence, evidence_count, proposed_path,\n title, summary, draft_markdown, created_at, updated_at)\n values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ").run(d,n.workspaceId,n.name,n.status??"review_required",n.confidence,n.evidence.length,n.proposedPath??null,n.title,n.summary,n.draftMarkdown,a,a),readCandidate(t,d)}(r,t,n);return function replaceEvidence(t,n,a,d){t.prepare("delete from skill_candidate_evidence where candidate_id = ?").run(n);const r=t.prepare("\n insert into skill_candidate_evidence\n (id, candidate_id, evidence_type, evidence_ref, summary, weight, created_at)\n values (?, ?, ?, ?, ?, ?, ?)\n ");for(const t of a)r.run(String(e()),n,t.evidenceType,t.evidenceRef,t.summary??null,t.weight??null,d)}(r,d.id,t.evidence,n),d},list:async(e={})=>function listCandidates(e,t,n){return(t&&n?e.prepare("select * from skill_candidates where workspace_id = ? and status = ? order by updated_at desc").all(t,n):t?e.prepare("select * from skill_candidates where workspace_id = ? order by updated_at desc").all(t):e.prepare("select * from skill_candidates order by updated_at desc").all()).map(e=>rowToCandidate(e))}(r,e.workspaceId,e.status),get:async e=>readCandidate(r,e),listEvidence:async e=>function listEvidence(e,t){return e.prepare("select * from skill_candidate_evidence where candidate_id = ? order by created_at asc").all(t).map(e=>function rowToEvidence(e){return{id:String(e.id),candidateId:String(e.candidate_id),evidenceType:e.evidence_type,evidenceRef:String(e.evidence_ref),summary:readOptionalString(e.summary),weight:"number"==typeof e.weight?e.weight:void 0,createdAt:String(e.created_at)}}(e))}(r,e),async updateStatus(e){const t=(new Date).toISOString();return r.prepare("\n update skill_candidates\n set status = ?, reviewed_at = ?, reviewed_by = ?, rejection_reason = ?, updated_at = ?\n where id = ?\n ").run(e.status,t,e.reviewedBy??null,e.rejectionReason??null,t,e.id),readCandidate(r,e.id)}}}function readCandidate(e,t){const n=e.prepare("select * from skill_candidates where id = ?").get(t);return n?rowToCandidate(n):void 0}function rowToCandidate(e){return{id:String(e.id),workspaceId:String(e.workspace_id),name:String(e.name),status:e.status,confidence:Number(e.confidence),evidenceCount:Number(e.evidence_count),proposedPath:readOptionalString(e.proposed_path),title:String(e.title),summary:String(e.summary),draftMarkdown:String(e.draft_markdown),createdAt:String(e.created_at),updatedAt:String(e.updated_at),reviewedAt:readOptionalString(e.reviewed_at),reviewedBy:readOptionalString(e.reviewed_by),rejectionReason:readOptionalString(e.rejection_reason)}}function readOptionalString(e){return"string"==typeof e&&e?e:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function createLangMemServiceProvider(
|
|
1
|
+
export function createLangMemServiceProvider(e){const n=function normalizeBaseUrl(e){return e.replace(/\/+$/u,"")}(e.baseUrl),t={...e.config,provider:"langmem-service"};return{name:"langmem-service",config:t,propose:async o=>(await postJson(n,"/memory/propose",{...o,config:t},e)).candidates??[],async search(o){const r=await(o.store?.list({namespace:o.namespace,kinds:o.kinds}))??[];return(await postJson(n,"/memory/search",{...withoutStore(o),records:r,config:t},e)).records??[]},consolidate:async o=>(await postJson(n,"/memory/consolidate",{...o,config:t},e)).operations??[]}}async function postJson(e,n,t,o){const r=new AbortController,a=setTimeout(()=>r.abort(),o.timeoutMs??3e4);try{const a=await fetch(`${e}${n}`,{method:"POST",headers:{"content-type":"application/json",...o.headers},body:JSON.stringify(t),signal:r.signal});return await async function readResponse(e,n){const t=await e.text();if(!e.ok)throw new Error(`LangMem service ${n} failed with ${e.status}: ${t}`);return t?JSON.parse(t):{}}(a,n)}finally{clearTimeout(a)}}function withoutStore(e){return{namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds,records:[]}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export async function applyMemoryMaintenance(e
|
|
1
|
+
export async function applyMemoryMaintenance(a,e){const t=[];for(const n of e)t.push(await applyOperation(a,n));return t}async function applyOperation(a,e){return"archive"===e.action?result(e,await a.archive(e.recordId,e.reason)):"mark_stale"===e.action?result(e,await a.update({id:e.recordId,status:"stale",metadata:{maintenanceReason:e.reason}})):"refresh"===e.action?result(e,await a.update({id:e.recordId,status:"active",metadata:{maintenanceReason:e.reason}})):result(e,await a.update({id:e.recordId,status:"archived",metadata:{maintenanceReason:e.reason,supersededBy:e.replacementRecordId}}))}function result(a,e){return{operation:a,record:e,applied:Boolean(e),reason:e?a.reason:"record not found"}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function createDefaultMemoryPolicy(){return{decide:
|
|
1
|
+
export function createDefaultMemoryPolicy(){return{decide:e=>e.noStore?{action:"reject",reason:"candidate marked noStore",reviewRequired:!1}:e.content.trim()?"sensitive"===e.sensitivity||"restricted"===e.sensitivity?{action:"review",reason:"sensitive memory requires operator approval",kind:e.kindHint??"semantic",scope:e.scopeHint??"workspace",confidence:normalizeConfidence(e.confidenceHint),reviewRequired:!0}:{action:"store",reason:"candidate accepted by default runtime policy",kind:e.kindHint??"semantic",scope:e.scopeHint??"workspace",confidence:normalizeConfidence(e.confidenceHint),retrievalPriority:0,reviewRequired:!1}:{action:"reject",reason:"candidate content is empty",reviewRequired:!1}}}function normalizeConfidence(e){return void 0===e||Number.isNaN(e)?.6:Math.min(1,Math.max(0,e))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const s=n.kindHint??function(e){const n=e.toLowerCase();return n.includes("must ")||n.includes("should ")?"procedural":n.includes("run ")||n.includes("failed ")?"episodic":"semantic"}(n.content);return function(e,n){return!1!==e.types?.[n]}(e,s)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:s,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
|
|
1
|
+
export function createEmbeddedMemoryProvider(e={}){return{name:"embedded",config:e,async propose(n){const s=n.kindHint??function inferKind(e){const n=e.toLowerCase();return n.includes("must ")||n.includes("should ")?"procedural":n.includes("run ")||n.includes("failed ")?"episodic":"semantic"}(n.content);return function isKindEnabled(e,n){return!1!==e.types?.[n]}(e,s)?[{namespace:n.namespace,content:n.content,sourceType:n.sourceType,sourceRef:n.sourceRef,kindHint:s,scopeHint:n.scopeHint??e.defaults?.scope??"workspace",sensitivity:n.sensitivity??e.defaults?.sensitivity??"internal",confidenceHint:.6,metadata:n.metadata}]:[]},search:async e=>e.store?(await e.store.recall({namespace:e.namespace,query:e.query,limit:e.limit,kinds:e.kinds})).records:[],consolidate:async e=>e.records.filter(e=>"stale"===e.status).map(e=>({action:"archive",recordId:e.id,reason:"embedded provider archives stale records during consolidation"}))}}
|
|
@@ -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
|
|
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 +1 @@
|
|
|
1
|
-
import{createServer as
|
|
1
|
+
import{createServer as e}from"node:http";import{compileWorkflowPlan as o,projectRuntimeTrace as t,renderWorkflowMermaid as r}from"@stable-harness/core";export function createHttpServer(n){return e(async(e,s)=>{try{if("GET"===e.method&&"/health"===e.url)return void sendJson(s,200,{ok:!0});if("GET"===e.method&&"/inspect"===e.url)return void sendJson(s,200,n.inspect());if("GET"===e.method&&"/requests"===e.url)return void sendJson(s,200,n.listRequests());if("GET"===e.method&&"/sessions"===e.url)return void sendJson(s,200,n.listSessions());if("GET"===e.method&&"/workflows"===e.url)return void sendJson(s,200,n.inspect().workflows);const d=function readWorkflowMermaidId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&d){const e=n.getWorkflow(d);return void sendJson(s,e?200:404,e?{mermaid:r(e)}:{error:"workflow_not_found"})}const a=function readWorkflowPlanId(e){const o=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&a){const e=n.getWorkflow(a);return void sendJson(s,e?200:404,e?o(e):{error:"workflow_not_found"})}const i=function readRequestInspectionId(e){const o=(e??"").match(/^\/requests\/([^/]+)$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&i){const e=n.inspectRequest(i);return void sendJson(s,e?200:404,e??{error:"request_not_found"})}const u=function readTraceRequestId(e){const o=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return o?.[1]?decodeURIComponent(o[1]):void 0}(e.url);if("GET"===e.method&&u){const e=n.getRun(u);return void sendJson(s,e?200:404,e?t(e):{error:"run_not_found"})}if("POST"===e.method&&"/requests"===e.url){const o=await async function readJson(e){const o=[];for await(const t of e)o.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===o.length?{}:JSON.parse(Buffer.concat(o).toString("utf8"))}(e);return void sendJson(s,200,await n.request(function readRuntimeRequest(e){const o=function readToolCall(e){if("object"!=typeof e||null===e)return;const o=e;return"string"==typeof o.toolId?{toolId:o.toolId,args:o.args}:void 0}(e.toolCall),t=function readWorkflow(e){const o=readRecord(e);if(o)return{..."string"==typeof o.workflowId?{workflowId:o.workflowId}:{},..."string"==typeof o.routeId?{routeId:o.routeId}:{},...void 0!==o.input?{input:o.input}:{},..."object"==typeof o.metadata&&o.metadata?{metadata:o.metadata}:{}}}(e.workflow),r=function readMemory(e){const o=readRecord(e);if(o)return{..."string"==typeof o.namespace?{namespace:o.namespace}:{},...!1===o.recall||readRecord(o.recall)?{recall:o.recall}:{},...Array.isArray(o.candidates)?{candidates:o.candidates}:{}}}(e.memory),n=readRecord(e.metadata);return{input:"string"==typeof e.input?e.input:"",..."string"==typeof e.agentId?{agentId:e.agentId}:{},..."string"==typeof e.sessionId?{sessionId:e.sessionId}:{},..."string"==typeof e.requestId?{requestId:e.requestId}:{},..."string"==typeof e.parentRunId?{parentRunId:e.parentRunId}:{},...o?{toolCall:o}:{},...t?{workflow:t}:{},...r?{memory:r}:{},...n?{metadata:n}:{}}}(o)))}sendJson(s,404,{error:"not_found"})}catch(e){sendJson(s,500,{error:e instanceof Error?e.message:String(e)})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,o,t){e.writeHead(o,{"content-type":"application/json"}),e.end(JSON.stringify(t))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createServer as e}from"node:http";import{createCapabilitiesResponse as
|
|
1
|
+
import{createServer as e}from"node:http";import{createCapabilitiesResponse as r,createModelsResponse as t,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as s}from"./openai-payload.js";import{createContentChunk as a,createRoleChunk as i,createStopChunk as d,missingFinalDelta as c,writeChatSse as l,writeRuntimeStreamEvent as u,writeSseHeaders as f}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(p,m={}){const g=function resolveServerOptions(e,r){const t=function readOpenAiProtocolConfig(e){const r=readRecord(e)??{};return readRecord(r.openaiCompatible)??readRecord(r["openai-compatible"])??readRecord(r.openai)??{}}(e.getRuntimePolicy().protocols);return{bearerToken:r.bearerToken??readConfigString(t.bearerToken),corsOrigins:r.corsOrigins??(o=t.corsOrigins,Array.isArray(o)?o.filter(e=>"string"==typeof e&&e.trim().length>0):void 0),modelAgentMap:r.modelAgentMap??readStringRecord(t.modelAgentMap),defaultModel:r.defaultModel??readConfigString(t.defaultModel)};var o}(p,m);return e(async(e,m)=>{try{if(function applyCorsHeaders(e,r,t){const o=function allowedCorsOrigin(e,r){if(e)return r.corsOrigins?.includes("*")?"*":r.corsOrigins?.includes(e)||function isLoopbackOrigin(e){try{const r=new URL(e);return"http:"===r.protocol&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,t);o&&(r.setHeader("access-control-allow-origin",o),r.setHeader("vary","origin"))}(e,m,g),function handleCors(e,r){return"OPTIONS"===e.method&&(r.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),r.end(),!0)}(e,m))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,g))return void sendJson(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(m,200,t(p,g));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(m,200,r());if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,p){const m=await async function readJson(e){const r=[];for await(const t of e)r.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===r.length?{}:JSON.parse(Buffer.concat(r).toString("utf8"))}(r);if(m.model&&!o(m.model,e,p))return void sendJson(t,404,{error:{message:`model_not_found: ${m.model}`,type:"invalid_request_error"}});const g=s(m,e,p,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(m.stream)return void await async function streamChatCompletion(e,r,t,o){f(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(r,i(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=u(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o),i=c(n,s.output);i&&l(r,a(o.requestId,t.model,i)),l(r,d(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,m,g);const h=await e.request(g);sendJson(t,200,n(m,h))}(p,e,m,g);sendJson(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readStringRecord(e){const r=readRecord(e);if(r)return Object.fromEntries(Object.entries(r).filter(e=>"string"==typeof e[1]))}function readConfigString(e){if("string"!=typeof e||!e.trim())return;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:e}function readHeaderString(e){const r=Array.isArray(e)?e[0]:e;return r&&r.trim()?r:void 0}function sendJson(e,r,t,o){e.writeHead(r,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(t))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as
|
|
1
|
+
import{randomUUID as e}from"node:crypto";export function toRuntimeRequest(t,n,s,o={}){const r=function messagesToInput(e){if(!Array.isArray(e)||0===e.length)throw new Error("messages must be a non-empty array");return e.map(e=>`${e.role??"user"}: ${contentToText(e.content)}`).join("\n\n")}(t.messages),a=`chatcmpl-${e()}`,i=o.sessionId??function readSessionId(e){const t=e?.sessionId??e?.session_id??e?.conversationId??e?.conversation_id;return"string"==typeof t&&t.trim()?t:void 0}(t.metadata),c=normalizeOpenAiMessages(t.messages),u=i?function prependSessionMessages(e,t,n){if(function hasClientHistory(e){return e.some(e=>"assistant"===e.role)||e.filter(e=>"user"===e.role).length>1}(n))return n;const s=e.listRequests({sessionId:t,state:"completed"}).flatMap(t=>function inspectSessionTurn(e,t){const n=e.inspectRequest(t),s=n?.output?.trim(),o=function lastUserMessage(e){return normalizeOpenAiMessages(Array.isArray(e)?e:void 0).filter(e=>"user"===e.role&&e.content.trim()).at(-1)}(n?.metadata?.openaiMessages)?.content;return o&&s?[{role:"user",content:o},{role:"assistant",content:s}]:[]}(e,t.requestId)).slice(-12);return s.length>0?[...s,...n]:n}(n,i,c):c;return{input:r,requestId:a,agentId:resolveAgentId(t.model,n,s),...i?{sessionId:i}:{},metadata:{protocol:"openai-compatible",openaiStream:!0===t.stream,openaiMessages:u,openaiSessionHistory:u.length>c.length,model:t.model,user:t.user,clientMetadata:t.metadata}}}export function resolveAgentId(e,t,n){if(!e)return;const s=n.modelAgentMap?.[e];return s||(t.inspect().agents.includes(e)?e:void 0)}export function createModelsResponse(e,t){const n=Object.keys(t.modelAgentMap??{});return{object:"list",data:[...new Set([...e.inspect().agents,...n])].sort().map(e=>({id:e,object:"model",created:0,owned_by:"stable-harness"}))}}export function createCapabilitiesResponse(){return{object:"stable_harness.capabilities",endpoints:["/v1/models","/v1/chat/completions","/v1/capabilities"],streaming:!0,toolProgressEvents:!0}}export function toChatCompletion(e,t){return{id:t.requestId,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:e.model??t.agentId,choices:[{index:0,message:{role:"assistant",content:t.output},finish_reason:"stop"}],usage:estimateUsage(e.messages,t.output),metadata:{sessionId:t.sessionId,agentId:t.agentId,state:t.state}}}function normalizeOpenAiMessages(e){return Array.isArray(e)?e.map(e=>({role:e.role??"user",content:contentToText(e.content)})):[]}function contentToText(e){if("string"==typeof e)return e;if(!Array.isArray(e))throw new Error("message content must be a string or content part array");return e.map(contentPartToText).join("\n")}function contentPartToText(e){if("object"!=typeof e||null===e)throw new Error("message content parts must be objects");const t=e;if("text"===t.type&&"string"==typeof t.text)return t.text;const n=t.image_url;if("image_url"===t.type&&"string"==typeof n?.url)return`[image:${n.url}]`;throw new Error("unsupported_content_type")}function estimateUsage(e,t){const n=estimateTokens(Array.isArray(e)?e.map(e=>contentToText(e.content)).join("\n"):""),s=estimateTokens(t);return{prompt_tokens:n,completion_tokens:s,total_tokens:n+s}}function estimateTokens(e){return e.trim()?Math.ceil(1.3*e.trim().split(/\s+/u).length):0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function missingFinalDelta(t
|
|
1
|
+
export function missingFinalDelta(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}export function createRoleChunk(e,t){return createChunk(e,t,{role:"assistant"},null)}export function createContentChunk(e,t,n){return createChunk(e,t,{content:n},null)}export function createStopChunk(e,t){return createChunk(e,t,{},"stop")}export function writeRuntimeStreamEvent(e,t,n,r){const o=function readAdapterDelta(e){if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"agent.output.delta"===t.phase&&"string"==typeof t.text?t.text:void 0}(t);if(o)return writeChatSse(e,createContentChunk(n,r,o)),o;!function writeProgressEvent(e,t){const n=function readToolProgress(e){if("runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n="agent.tool.start"===t.phase||"agent.tool.result"===t.phase?t.phase:void 0,r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&writeSse(e,"stable_harness.tool.progress",n)}(e,t),function writeNarrationEvent(e,t){"runtime.progress.narration"===t.type&&writeSse(e,"stable_harness.progress.narration",{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,message:t.message,provider:t.provider,source_event_types:t.sourceEventTypes,source_event_ids:t.sourceEventIds,model:t.model,style:t.style})}(e,t)}export function writeSseHeaders(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...t})}export function writeChatSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}function createChunk(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function writeSse(e,t,n){e.write(`event: ${t}\n`),e.write(`data: ${JSON.stringify(n)}\n\n`)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BetterToolLike, RepairFunction, RepairModelLike } from "@botbotgo/better-call";
|
|
1
|
+
import type { BetterToolLike, RepairFunction, RepairModelLike, RepairPolicy } from "@botbotgo/better-call";
|
|
2
2
|
import type { ToolArgumentGuard, ToolArgumentIssue, ToolGatewayContext, ToolGatewayTool } from "./types.js";
|
|
3
3
|
type ToolGatewayRuntimeTool = ToolGatewayTool & {
|
|
4
4
|
validationTool?: BetterToolLike;
|
|
@@ -7,6 +7,7 @@ export type BetterCallArgumentRepairOptions = {
|
|
|
7
7
|
mode?: "guard" | "repair" | "review";
|
|
8
8
|
repair?: RepairFunction;
|
|
9
9
|
repairModel?: RepairModelLike;
|
|
10
|
+
repairPolicy?: RepairPolicy;
|
|
10
11
|
};
|
|
11
12
|
export type ToolArgumentGuardOptions = {
|
|
12
13
|
betterCall?: BetterCallArgumentRepairOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as
|
|
1
|
+
import{BetterToolValidationError as o,betterTools as t,defaultRepair as e,reliableToolCalls as a}from"@botbotgo/better-call";import{isRecord as r,validateWithZodSchema as i}from"./schema-validation.js";export class ToolArgumentValidationError extends Error{toolId;issues;constructor(o,t){super(`Tool argument validation failed for ${o}: ${t.map(o=>`${o.path} ${o.message}`).join("; ")}`),this.toolId=o,this.issues=t,this.name="ToolArgumentValidationError"}}export function createDefaultArgumentGuard(t={}){return{async validate(e){const a=e.tool.validateArgs?await e.tool.validateArgs({args:e.args,context:e.context}):{action:"allow",args:e.args};if("reject"===a.action)return a;const r=await async function validateWithBetterCall(t,e,a){const r=i(t.schema,e);if(void 0===t.schema)return r??{action:"allow",args:e};const l=await async function invokeBetterCallValidation(t,e,a){try{return{action:"allow",args:await createBetterCallValidationTool(t,a).invoke(e)}}catch(t){if(t instanceof o)return{action:"reject",reason:"BetterCall validation failed",issues:t.issues.map(toToolArgumentIssue)};throw t}}(t,"allow"===r?.action?r.args:e,a);return r?"allow"===r.action?l:"reject"===l.action?r:i(t.schema,l.args)||l:l}(e.tool,a.args,t.betterCall);return"reject"===r.action?r:"repair"===a.action?{...a,args:r.args}:r}}}export function assertToolArguments(o,t,e,a){return Promise.resolve(a.validate({tool:o,args:t,context:e})).then(t=>{if("reject"===t.action)throw new ToolArgumentValidationError(o.id,t.issues);return t.args})}export function prepareBetterCallTools(o,e){const a=t(o.map(toBetterCallTool),toBetterToolsOptions(e));return o.map((o,t)=>({...o,validationTool:a[t]}))}export async function repairBetterCallToolSelection(o){const t=function resolveRepair(o){return o?.repair??(o?.repairModel?e(o.repairModel):void 0)}(o.options);if(!t||0===o.tools.length)return;const i=await a({userInput:JSON.stringify({tool:o.toolId,args:o.args}),tools:o.tools.map(toToolDefinition),calls:[{tool:o.toolId,args:(l=o.args,r(l)?l:{input:l})}],repair:t,repairPolicy:o.options?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0},mode:o.options?.mode??"repair"});var l;const n=i.ok?i.calls.find(t=>o.tools.some(o=>o.id===t.tool)):void 0;return n?{toolId:n.tool,args:n.args}:void 0}function createBetterCallValidationTool(o,e){return o.validationTool??t([toBetterCallTool(o)],toBetterToolsOptions(e))[0]}function toBetterCallTool(o){return{name:o.id,description:o.description,schema:o.schema,invoke:o=>o}}function toToolDefinition(o){return{name:o.id,description:o.description,schema:o.schema}}function toToolArgumentIssue(o){return{path:o.path.replace(/^\$\.calls\[\d+\]\.args/u,"$"),message:o.message,expected:void 0===o.expected?void 0:String(o.expected),actual:o.actual}}function toBetterToolsOptions(o){return{mode:o?.mode??"repair",repair:o?.repair,repairModel:o?.repairModel,repairPolicy:o?.repairPolicy??{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0,allowModelRepair:!0}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{assertToolArguments as o,createDefaultArgumentGuard as t,prepareBetterCallTools as e,repairBetterCallToolSelection as a}from"./argument-guard.js";export function createInMemoryToolGateway(
|
|
1
|
+
import{assertToolArguments as o,createDefaultArgumentGuard as t,prepareBetterCallTools as e,repairBetterCallToolSelection as r,ToolArgumentValidationError as a}from"./argument-guard.js";export function createInMemoryToolGateway(l,i={}){const s=new Map(e(l,i.betterCall).map(o=>[o.id,o])),n=t(i),repairToolCall=async o=>{const t=o.allowedToolIds?new Set(o.allowedToolIds):void 0,e=[...s.values()].filter(o=>!t||t.has(o.id));return r({toolId:o.toolId,args:o.args,tools:e,options:withRequestRepairModel(i.betterCall,o.repairModel)})};return{list:()=>[...s.values()],get:o=>s.get(o),repairToolCall:repairToolCall,async invoke(t){const e=s.has(t.toolId)?void 0:await repairToolCall(t),r=e?.toolId??t.toolId,l=s.get(r);if(!l)throw new Error(`Tool is not registered: ${t.toolId}`);const i=await async function assertToolArgumentsWithRepair(t){try{return await o(t.tool,t.initialArgs,t.request.context,t.guard)}catch(e){if(!(e instanceof a))throw e;const r=await t.repairToolCall({...t.request,args:t.initialArgs,allowedToolIds:[t.tool.id]});if(!r||r.toolId!==t.tool.id)throw e;return o(t.tool,r.args,t.request.context,t.guard)}}({request:t,tool:l,initialArgs:e?.args??t.args,repairToolCall:repairToolCall,guard:n});return{toolId:l.id,output:await l.invoke(i,t.context)}}}}function withRequestRepairModel(o,t){return!t||o?.repair||o?.repairModel?o:{...o,mode:o?.mode??"repair",repairModel:t}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await
|
|
1
|
+
import o from"node:path";import{pathToFileURL as e}from"node:url";import{createInMemoryToolGateway as t}from"./in-memory.js";export async function createModuleToolGateway(o){const e=[];for(const t of o.tools){const o=await loadModuleTool(t);o&&e.push(o)}return t(e,o.options)}async function loadModuleTool(t){if(!t.sourcePath)return;const n=await import(e(o.resolve(t.sourcePath)).href),r=n[t.name??t.id]??n.default;return function hasInvoke(o){return"object"==typeof o&&null!==o&&"invoke"in o&&"function"==typeof o.invoke}(r)?{id:t.id,description:t.description??r.description,schema:t.schema??r.schema,validateArgs:r.validateArgs,invoke:(o,e)=>async function invokeWithWorkspaceCwd(o,e,t){const n=process.cwd();try{return process.chdir(t.workspaceRoot),await o(e)}finally{process.chdir(n)}}(r.invoke,o,e)}:void 0}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export function validateWithZodSchema(
|
|
1
|
+
import{normalizeArgsBySchema as e}from"@botbotgo/better-call";export function validateWithZodSchema(t,o){return isZodLike(t)?toZodGuardResult(t.safeParse(o??{})):function isZodShape(e){return isRecord(e)&&Object.values(e).length>0&&Object.values(e).every(isZodLike)}(t)?function validateWithZodShape(t,o){const r=function normalizeZodShapeArgs(t,o){const r=isRecord(o)?o:{};return e(t,r,{allowCoercion:!0,allowClamp:!0,allowArrayStringSplit:!0}).args}(t,o),a={},s=[];for(const[e,o]of Object.entries(t)){const t=o.safeParse(r[e]);t.success?void 0!==t.data&&(a[e]=t.data):s.push(...t.error.issues.map(t=>({...t,path:[e,...t.path]})))}return s.length>0?toZodGuardResult({success:!1,error:{issues:s}}):{action:"allow",args:a}}(t,o):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}
|
|
@@ -44,6 +44,9 @@ export type ToolArgumentValidator = (input: {
|
|
|
44
44
|
export type ToolGatewayInvokeRequest = {
|
|
45
45
|
toolId: string;
|
|
46
46
|
args?: unknown;
|
|
47
|
+
repairModel?: {
|
|
48
|
+
invoke(input: unknown): Promise<unknown> | unknown;
|
|
49
|
+
};
|
|
47
50
|
context: ToolGatewayContext;
|
|
48
51
|
};
|
|
49
52
|
export type ToolGatewayRepairRequest = ToolGatewayInvokeRequest & {
|