stable-harness 0.0.84 → 0.0.85

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 (35) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
  4. package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
  5. package/node_modules/@stable-harness/core/dist/index.d.ts +1 -0
  6. package/node_modules/@stable-harness/core/dist/index.js +1 -1
  7. package/node_modules/@stable-harness/core/dist/runtime/metrics/prometheus.d.ts +25 -0
  8. package/node_modules/@stable-harness/core/dist/runtime/metrics/prometheus.js +1 -0
  9. package/node_modules/@stable-harness/core/package.json +3 -3
  10. package/node_modules/@stable-harness/governance/package.json +1 -1
  11. package/node_modules/@stable-harness/memory/package.json +1 -1
  12. package/node_modules/@stable-harness/protocols/dist/src/http-server.js +1 -1
  13. package/node_modules/@stable-harness/protocols/dist/src/openai-compatible.js +1 -1
  14. package/node_modules/@stable-harness/protocols/dist/src/openai-payload.js +1 -1
  15. package/node_modules/@stable-harness/protocols/package.json +2 -2
  16. package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
  17. package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
  18. package/package.json +9 -9
  19. package/packages/adapter-deepagents/package.json +2 -2
  20. package/packages/adapter-langgraph/package.json +2 -2
  21. package/packages/cli/package.json +8 -8
  22. package/packages/core/dist/index.d.ts +1 -0
  23. package/packages/core/dist/index.js +1 -1
  24. package/packages/core/dist/runtime/metrics/prometheus.d.ts +25 -0
  25. package/packages/core/dist/runtime/metrics/prometheus.js +1 -0
  26. package/packages/core/package.json +3 -3
  27. package/packages/evaluation/package.json +2 -2
  28. package/packages/governance/package.json +1 -1
  29. package/packages/memory/package.json +1 -1
  30. package/packages/protocols/dist/src/http-server.js +1 -1
  31. package/packages/protocols/dist/src/openai-compatible.js +1 -1
  32. package/packages/protocols/dist/src/openai-payload.js +1 -1
  33. package/packages/protocols/package.json +2 -2
  34. package/packages/tool-gateway/package.json +1 -1
  35. package/packages/workspace-yaml/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export type { LangGraphNodeHandler, LangGraphNodeHandlerInput, LangGraphNodeReso
7
7
  export type { LangGraphRegistrySkillOutput } from "@stable-harness/adapter-langgraph";
8
8
  export { createLangMemServiceProvider } from "@stable-harness/memory";
9
9
  export { createInMemoryRuntimeMemoryStore, createJsonFileRuntimeMemoryStore } from "@stable-harness/memory";
10
- export { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, defaultExecutionEvaluatorRules, defaultToolGuardrails, evaluateExecutionRules, evaluateToolGuardrails, projectRuntimeTrace, repeatToolGuardrail, resolveEnabledMemories, requiredPlanToolGuardrail, reviewExecutionEvidence, toolDependencyGuardrail, } from "@stable-harness/core";
10
+ export { applySpecDrivenPhaseTransition, containsRecoverableResultOutput, createSpecDrivenArtifact, createSpecDrivenArtifactEvent, createSpecDrivenPhaseEvent, createSpecDrivenWorkflowPolicy, createSpecDrivenWorkflowState, defaultExecutionEvaluatorRules, defaultToolGuardrails, evaluateExecutionRules, evaluateToolGuardrails, collectStableHarnessPrometheusSamples, projectRuntimeTrace, repeatToolGuardrail, renderStableHarnessPrometheusMetrics, resolveEnabledMemories, STABLE_HARNESS_PROMETHEUS_LABELS, requiredPlanToolGuardrail, reviewExecutionEvidence, toolDependencyGuardrail, } from "@stable-harness/core";
11
11
  export type { CompiledWorkspace, ExecutionEvaluatorRule, RuntimeAdapter, RuntimeEvent, RuntimeWorkflowAdapter, RuntimeRequest, RuntimeResponse, RuntimeRunRecord, RuntimeTraceEntry, StableHarnessRuntime, SpecDrivenPhaseRecord, SpecDrivenPhaseStatus, SpecDrivenPhaseTransition, SpecDrivenWorkflowState, WorkspaceAgent, WorkspaceModel, WorkspaceRuntimePolicy, WorkspaceSpecDrivenPhase, WorkspaceSpecDrivenWorkflowPolicy, WorkspaceTool, ToolGuardrail, ToolGuardrailContext, ToolGuardrailDecision, } from "@stable-harness/core";
12
12
  export { loadWorkspaceFromYaml } from "@stable-harness/workspace-yaml";
13
13
  export { createInMemoryToolGateway, createModuleToolGateway } from "@stable-harness/tool-gateway";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createBackendModel as e,createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as a,createLangGraphWorkflowAdapter as t,createRegistrySkillResolverProvider as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as n}from"@stable-harness/core";import{createModuleToolGateway as i}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";export{createDeepAgentsAdapter,createDeepAgentsMemoryMaintenanceTarget}from"@stable-harness/adapter-deepagents";export{createDeepAgentsMiddlewareSkillProvider,createLangGraphRuntimeAdapter,createLangGraphWorkflowAdapter,createRegistrySkillResolverProvider}from"@stable-harness/adapter-langgraph";export{createLangMemServiceProvider}from"@stable-harness/memory";export{createInMemoryRuntimeMemoryStore,createJsonFileRuntimeMemoryStore}from"@stable-harness/memory";export{applySpecDrivenPhaseTransition,containsRecoverableResultOutput,createSpecDrivenArtifact,createSpecDrivenArtifactEvent,createSpecDrivenPhaseEvent,createSpecDrivenWorkflowPolicy,createSpecDrivenWorkflowState,defaultExecutionEvaluatorRules,defaultToolGuardrails,evaluateExecutionRules,evaluateToolGuardrails,projectRuntimeTrace,repeatToolGuardrail,resolveEnabledMemories,requiredPlanToolGuardrail,reviewExecutionEvidence,toolDependencyGuardrail}from"@stable-harness/core";export{loadWorkspaceFromYaml}from"@stable-harness/workspace-yaml";export{createInMemoryToolGateway,createModuleToolGateway}from"@stable-harness/tool-gateway";export function createStableHarnessRuntime(e){return"string"==typeof e?createStableRuntime({workspaceRoot:e}):"workspaceRoot"in e?createStableRuntime(e):n(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),a=e.toolGateway??await i({tools:r.tools.values()});return n({workspace:r,toolGateway:a,memory:e.memory,qualityReviewModel:createQualityReviewModel(r),toolGuardrails:e.toolGuardrails,executionEvaluatorRules:e.executionEvaluatorRules,adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}function createQualityReviewModel(r){const a=function readQualityModelRef(e){const r=isRecord(e)?e:{};return readString((isRecord(r.reviewer)?r.reviewer:r).modelRef)}(r.runtime.quality),t=a?r.models.get(a):void 0,o=t?e(t):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(o)?o:void 0}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(e,t){const o={deepagents:({policy:e})=>r(e.config?{config:e.config}:{}),langgraph:({policy:e})=>a({...readLangGraphOptions(e.config),name:e.name}),...t.adapterFactories},n=function runtimeAdapterPolicies(e){const r=e.runtime.adapters?.filter(e=>!1!==e.enabled);return r&&r.length>0?r:[...new Set([...e.agents.values()].map(e=>e.backend))].map(e=>({name:e}))}(e);return n.map(r=>{const a=o[r.name];if(a)return a({policy:r,workspace:e});throw new Error(`Unsupported runtime adapter: ${r.name}`)})}function createWorkflowAdapters(e,r){const a={langgraph:({name:e,options:r})=>t({...readLangGraphOptions(r),name:e}),...r.workflowAdapterFactories};return[...new Set([...e.workflows.values()].map(e=>e.adapter??"").filter(Boolean))].map(t=>{const o=a[t];return o?.({name:t,workspace:e,options:readWorkflowAdapterOptions(r,t)})}).filter(e=>Boolean(e))}function readWorkflowAdapterOptions(e,r){return e.workflowAdapterOptions?.[r]??{}}function readLangGraphOptions(e){return isRecord(e)?{...e,...void 0!==readLangGraphSkillProvider(e)?{skillProvider:readLangGraphSkillProvider(e)}:{}}:{}}function readLangGraphSkillProvider(e){if(!1===e.skillProvider)return!1;const r=function readSkillProviderConfig(e){return isRecord(e.skills)?e.skills:isRecord(e.skillProvider)?e.skillProvider:void 0}(e);if(!r)return;const a=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(a))return!1;if("registry-resolver"!==a)throw new Error(`Unsupported LangGraph skill provider: ${a}`);return o({..."boolean"==typeof r.includeContent?{includeContent:r.includeContent}:{},..."number"==typeof r.maxBytes&&Number.isFinite(r.maxBytes)?{maxBytes:r.maxBytes}:{}})}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
1
+ import{createBackendModel as e,createDeepAgentsAdapter as r}from"@stable-harness/adapter-deepagents";import{createLangGraphRuntimeAdapter as a,createLangGraphWorkflowAdapter as t,createRegistrySkillResolverProvider as o}from"@stable-harness/adapter-langgraph";import{createStableHarnessRuntime as n}from"@stable-harness/core";import{createModuleToolGateway as i}from"@stable-harness/tool-gateway";import{loadWorkspaceFromYaml as s}from"@stable-harness/workspace-yaml";export{createDeepAgentsAdapter,createDeepAgentsMemoryMaintenanceTarget}from"@stable-harness/adapter-deepagents";export{createDeepAgentsMiddlewareSkillProvider,createLangGraphRuntimeAdapter,createLangGraphWorkflowAdapter,createRegistrySkillResolverProvider}from"@stable-harness/adapter-langgraph";export{createLangMemServiceProvider}from"@stable-harness/memory";export{createInMemoryRuntimeMemoryStore,createJsonFileRuntimeMemoryStore}from"@stable-harness/memory";export{applySpecDrivenPhaseTransition,containsRecoverableResultOutput,createSpecDrivenArtifact,createSpecDrivenArtifactEvent,createSpecDrivenPhaseEvent,createSpecDrivenWorkflowPolicy,createSpecDrivenWorkflowState,defaultExecutionEvaluatorRules,defaultToolGuardrails,evaluateExecutionRules,evaluateToolGuardrails,collectStableHarnessPrometheusSamples,projectRuntimeTrace,repeatToolGuardrail,renderStableHarnessPrometheusMetrics,resolveEnabledMemories,STABLE_HARNESS_PROMETHEUS_LABELS,requiredPlanToolGuardrail,reviewExecutionEvidence,toolDependencyGuardrail}from"@stable-harness/core";export{loadWorkspaceFromYaml}from"@stable-harness/workspace-yaml";export{createInMemoryToolGateway,createModuleToolGateway}from"@stable-harness/tool-gateway";export function createStableHarnessRuntime(e){return"string"==typeof e?createStableRuntime({workspaceRoot:e}):"workspaceRoot"in e?createStableRuntime(e):n(e)}export async function createStableRuntime(e){const r=await s(e.workspaceRoot),a=e.toolGateway??await i({tools:r.tools.values()});return n({workspace:r,toolGateway:a,memory:e.memory,qualityReviewModel:createQualityReviewModel(r),toolGuardrails:e.toolGuardrails,executionEvaluatorRules:e.executionEvaluatorRules,adapters:e.adapters??createRuntimeAdapters(r,e),workflowAdapters:e.workflowAdapters??createWorkflowAdapters(r,e)})}function createQualityReviewModel(r){const a=function readQualityModelRef(e){const r=isRecord(e)?e:{};return readString((isRecord(r.reviewer)?r.reviewer:r).modelRef)}(r.runtime.quality),t=a?r.models.get(a):void 0,o=t?e(t):void 0;return function isQualityReviewModel(e){return isRecord(e)&&"function"==typeof e.invoke}(o)?o:void 0}export async function requestStableRuntime(e,r){return e.request(r)}function createRuntimeAdapters(e,t){const o={deepagents:({policy:e})=>r(e.config?{config:e.config}:{}),langgraph:({policy:e})=>a({...readLangGraphOptions(e.config),name:e.name}),...t.adapterFactories},n=function runtimeAdapterPolicies(e){const r=e.runtime.adapters?.filter(e=>!1!==e.enabled);return r&&r.length>0?r:[...new Set([...e.agents.values()].map(e=>e.backend))].map(e=>({name:e}))}(e);return n.map(r=>{const a=o[r.name];if(a)return a({policy:r,workspace:e});throw new Error(`Unsupported runtime adapter: ${r.name}`)})}function createWorkflowAdapters(e,r){const a={langgraph:({name:e,options:r})=>t({...readLangGraphOptions(r),name:e}),...r.workflowAdapterFactories};return[...new Set([...e.workflows.values()].map(e=>e.adapter??"").filter(Boolean))].map(t=>{const o=a[t];return o?.({name:t,workspace:e,options:readWorkflowAdapterOptions(r,t)})}).filter(e=>Boolean(e))}function readWorkflowAdapterOptions(e,r){return e.workflowAdapterOptions?.[r]??{}}function readLangGraphOptions(e){return isRecord(e)?{...e,...void 0!==readLangGraphSkillProvider(e)?{skillProvider:readLangGraphSkillProvider(e)}:{}}:{}}function readLangGraphSkillProvider(e){if(!1===e.skillProvider)return!1;const r=function readSkillProviderConfig(e){return isRecord(e.skills)?e.skills:isRecord(e.skillProvider)?e.skillProvider:void 0}(e);if(!r)return;const a=readString(r.provider)??readString(r.name)??"registry-resolver";if(["none","disabled","false"].includes(a))return!1;if("registry-resolver"!==a)throw new Error(`Unsupported LangGraph skill provider: ${a}`);return o({..."boolean"==typeof r.includeContent?{includeContent:r.includeContent}:{},..."number"==typeof r.maxBytes&&Number.isFinite(r.maxBytes)?{maxBytes:r.maxBytes}:{}})}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}function isRecord(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -15,7 +15,7 @@
15
15
  "@langchain/node-vfs": "^0.1.4",
16
16
  "@langchain/ollama": "^1.2.7",
17
17
  "@langchain/openai": "^1.4.5",
18
- "@stable-harness/core": "0.0.84",
18
+ "@stable-harness/core": "0.0.85",
19
19
  "deepagents": "^1.10.1",
20
20
  "langchain": "^1.4.0"
21
21
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-langgraph",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,6 +11,6 @@
11
11
  "types": "dist/src/index.d.ts",
12
12
  "peerDependencies": {
13
13
  "@langchain/langgraph": "^1.3.0",
14
- "@stable-harness/core": "0.0.84"
14
+ "@stable-harness/core": "0.0.85"
15
15
  }
16
16
  }
@@ -4,6 +4,7 @@ export * from "./boundary-scan.js";
4
4
  export * from "./execution-contract.js";
5
5
  export * from "./recovery/tool-call.js";
6
6
  export * from "./runtime/persistence/inspection.js";
7
+ export * from "./runtime/metrics/prometheus.js";
7
8
  export { createWorkspaceSandboxPolicy } from "./runtime/governance/sandbox.js";
8
9
  export * from "./memory-plugins.js";
9
10
  export { resolveEnabledMemories } from "./memory-plugins/shared.js";
@@ -1 +1 @@
1
- export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
1
+ export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./runtime/metrics/prometheus.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
@@ -0,0 +1,25 @@
1
+ import type { RuntimeRunRecord, StableHarnessRuntime } from "../../types.js";
2
+ export declare const STABLE_HARNESS_PROMETHEUS_LABELS: readonly ["workspace_id", "agent_id", "tool_id", "model", "status", "event_type", "layer", "outcome"];
3
+ export type StableHarnessPrometheusInput = {
4
+ runtime?: Pick<StableHarnessRuntime, "getRuntimePolicy" | "inspect">;
5
+ runs?: RuntimeRunRecord[];
6
+ workspaceId?: string;
7
+ };
8
+ type MetricSample = {
9
+ labels: Record<string, string>;
10
+ name: string;
11
+ value: number;
12
+ };
13
+ type MetricMap = Map<string, MetricSample>;
14
+ export declare function renderStableHarnessPrometheusMetrics(input?: StableHarnessPrometheusInput): string;
15
+ export declare function collectStableHarnessPrometheusSamples(input?: StableHarnessPrometheusInput): {
16
+ cost: MetricMap;
17
+ durationBuckets: MetricMap;
18
+ durationSumCount: MetricMap;
19
+ events: MetricMap;
20
+ repairs: MetricMap;
21
+ runs: MetricMap;
22
+ tokens: MetricMap;
23
+ tools: MetricMap;
24
+ };
25
+ export {};
@@ -0,0 +1 @@
1
+ export const STABLE_HARNESS_PROMETHEUS_LABELS=["workspace_id","agent_id","tool_id","model","status","event_type","layer","outcome"];const e=[.5,1,2.5,5,10,30,60,120,240,600,Number.POSITIVE_INFINITY];export function renderStableHarnessPrometheusMetrics(e={}){const t=collectStableHarnessPrometheusSamples(e);return`${["# HELP stable_harness_runs_total Stable Harness runs by low-cardinality status and agent.","# TYPE stable_harness_runs_total counter",...renderMetricLines(t.runs),"# HELP stable_harness_run_duration_seconds Stable Harness run duration histogram.","# TYPE stable_harness_run_duration_seconds histogram",...renderMetricLines(t.durationBuckets),...renderMetricLines(t.durationSumCount),"# HELP stable_harness_events_total Stable Harness runtime events by low-cardinality type.","# TYPE stable_harness_events_total counter",...renderMetricLines(t.events),"# HELP stable_harness_tools_total Stable Harness tool outcomes by tool id.","# TYPE stable_harness_tools_total counter",...renderMetricLines(t.tools),"# HELP stable_harness_repairs_total Stable Harness repair attempts by layer and outcome.","# TYPE stable_harness_repairs_total counter",...renderMetricLines(t.repairs),"# HELP stable_harness_tokens_total Stable Harness token usage reported by runtime metadata.","# TYPE stable_harness_tokens_total counter",...renderMetricLines(t.tokens),"# HELP stable_harness_model_cost_usd_total Stable Harness model cost reported by runtime metadata.","# TYPE stable_harness_model_cost_usd_total counter",...renderMetricLines(t.cost)].filter(Boolean).join("\n")}\n`}export function collectStableHarnessPrometheusSamples(e={}){const t=e.runs??e.runtime?.inspect().runs??[],a=cleanLabel(e.workspaceId??e.runtime?.getRuntimePolicy().workspaceId??"stable-harness"),n={cost:newCounter(),durationBuckets:newCounter(),durationSumCount:newCounter(),events:newCounter(),repairs:newCounter(),runs:newCounter(),tokens:newCounter(),tools:newCounter()};for(const e of t){const t=cleanLabel(readString(e.metadata?.workspaceId)??a),r=cleanLabel(e.agentId),s=cleanLabel(e.state);add(n.runs,"stable_harness_runs_total",{agent_id:r,status:s,workspace_id:t},1),addDuration(n,durationSeconds(e),{agent_id:r,workspace_id:t}),addTokenMetrics(n,e,{agent_id:r,model:cleanLabel(readString(e.metadata?.model)??"unknown"),workspace_id:t});for(const a of e.events)add(n.events,"stable_harness_events_total",{agent_id:cleanLabel(a.agentId),event_type:cleanLabel(a.type),workspace_id:t},1),addEventMetrics(n,a,t)}return n}function addEventMetrics(e,t,a){"runtime.tool.direct.started"!==t.type?"runtime.tool.direct.completed"!==t.type?"runtime.tool.failure"!==t.type?"runtime.repair.started"!==t.type?"runtime.repair.completed"===t.type&&add(e.repairs,"stable_harness_repairs_total",{layer:cleanLabel(t.layer),outcome:cleanLabel(t.outcome),workspace_id:a},1):add(e.repairs,"stable_harness_repairs_total",{layer:cleanLabel(t.layer),outcome:"started",workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"failed",tool_id:cleanLabel(t.toolId),workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"completed",tool_id:cleanLabel(t.toolId),workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"started",tool_id:cleanLabel(t.toolId),workspace_id:a},1)}function addTokenMetrics(e,t,a){const n=function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}(t.metadata?.tokenUsage),r=readNumber(n?.inputTokens),s=readNumber(n?.outputTokens),o=readNumber(n?.totalTokens)??readNumber(t.metadata?.totalTokens),l=cleanLabel(readString(n?.source)??"runtime_metadata");add(e.tokens,"stable_harness_tokens_total",{...a,direction:"input",source:l},r??0),add(e.tokens,"stable_harness_tokens_total",{...a,direction:"output",source:l},s??0),add(e.tokens,"stable_harness_tokens_total",{...a,direction:"total",source:l},o??(r??0)+(s??0)),add(e.cost,"stable_harness_model_cost_usd_total",{model:a.model??"unknown",source:l,workspace_id:a.workspace_id??"stable-harness"},readNumber(t.metadata?.costUsd)??readNumber(n?.costUsd)??0)}function durationSeconds(e){const t=Date.parse(e.startedAt),a=Date.parse(e.completedAt??e.startedAt);return!Number.isFinite(t)||!Number.isFinite(a)||a<t?0:(a-t)/1e3}function addDuration(t,a,n){const r=Number.isFinite(a)&&a>=0?a:0;for(const a of e)r<=a&&add(t.durationBuckets,"stable_harness_run_duration_seconds_bucket",{...n,le:a===Number.POSITIVE_INFINITY?"+Inf":String(a)},1);add(t.durationSumCount,"stable_harness_run_duration_seconds_sum",n,r),add(t.durationSumCount,"stable_harness_run_duration_seconds_count",n,1)}function newCounter(){return new Map}function add(e,t,a,n){const r=Number(n??0);if(!Number.isFinite(r)||0===r)return;const s=function sortLabels(e){return Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)))}(a),o=`${t}\n${JSON.stringify(s)}`,l=e.get(o)??{labels:s,name:t,value:0};l.value+=r,e.set(o,l)}function renderMetricLines(e){return[...e.values()].sort((e,t)=>e.name.localeCompare(t.name)||JSON.stringify(e.labels).localeCompare(JSON.stringify(t.labels))).map(e=>`${e.name}${function formatLabels(e){const t=Object.entries(e).filter(([,e])=>""!==e);return 0===t.length?"":`{${t.map(([e,t])=>`${e}="${function escapeLabel(e){return e.replaceAll("\\","\\\\").replaceAll("\n","\\n").replaceAll('"','\\"')}(t)}"`).join(",")}}`}(e.labels)} ${function formatNumber(e){return Number.isFinite(e)?String(Number(e.toFixed(6))):"0"}(e.value)}`)}function cleanLabel(e){return String(e??"unknown").trim().replace(/[^\w:.-]+/gu,"_").slice(0,80)||"unknown"}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:"string"==typeof e&&e.trim()&&Number.isFinite(Number(e))?Number(e):void 0}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,7 +11,7 @@
11
11
  ".": "./dist/index.js"
12
12
  },
13
13
  "peerDependencies": {
14
- "@stable-harness/governance": "0.0.84",
15
- "@stable-harness/memory": "0.0.84"
14
+ "@stable-harness/governance": "0.0.85",
15
+ "@stable-harness/memory": "0.0.85"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as o,renderWorkflowMermaid as n}from"@stable-harness/core";export function createHttpServer(s){return e((e,a)=>{!async function handleHttpRequest(e,s,a){try{await async function routeHttpRequest(e,s,a){if("GET"===s.method&&"/health"===s.url)return void sendJson(a,200,{ok:!0});if("GET"===s.method&&"/inspect"===s.url)return void sendJson(a,200,e.inspect());if("GET"===s.method&&"/requests"===s.url)return void sendJson(a,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:o}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(o,200,t.listSessions()),!0;const n=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&n)return sendJson(o,200,t.deleteSession(n)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(o,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(o,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(o,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(o,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(o,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(o,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:s,response:a}))return;const d=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(s.url);if("GET"===s.method&&d)return void sendJson(a,200,e.listArtifacts(d));const i=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&i){const t=e.getArtifact(i),r=e.readArtifact(i);return void sendJson(a,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const u=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&u){const t=e.getArtifact(u);return void sendJson(a,t?200:404,t??{error:"artifact_not_found"})}const c=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(s.url);if("GET"===s.method&&c)return void sendJson(a,200,await e.listApprovals(c.status));const f=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(s.url);if("POST"===s.method&&f){const t=await e.resolveApproval(f.id,f.status);return void sendJson(a,t?200:404,t??{error:"approval_not_found"})}if("GET"===s.method&&"/workflows"===s.url)return void sendJson(a,200,e.inspect().workflows);const m=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&m){const t=e.getWorkflow(m);return void sendJson(a,t?200:404,t?{mermaid:n(t)}:{error:"workflow_not_found"})}const l=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&l){const r=e.getWorkflow(l);return void sendJson(a,r?200:404,r?t(r):{error:"workflow_not_found"})}const p=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&p){const t=e.inspectRequest(p);return void sendJson(a,t?200:404,t??{error:"request_not_found"})}const h=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&h){const t=e.getRun(h);return void sendJson(a,t?200:404,t?r(t):{error:"run_not_found"})}const v=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&v){const t=e.getRun(v);return void sendJson(a,t?200:404,t?o(t):{error:"run_not_found"})}const I=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&I){const t=e.exportReplayBundle(I);return void sendJson(a,t?200:404,t??{error:"run_not_found"})}if("POST"===s.method&&"/requests"===s.url){const t=await readJson(s);return void sendJson(a,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),o=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.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}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...o?{memory:o}:{},...n?{metadata:n}:{}}}(t)))}sendJson(a,404,{error:"not_found"})}(e,s,a)}catch(e){sendJson(a,500,{error:e instanceof Error?e.message:String(e)})}}(s,e,a)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
1
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as n,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";export function createHttpServer(a){return e((e,d)=>{!async function handleHttpRequest(e,a,d){try{await async function routeHttpRequest(e,a,d){if("GET"===a.method&&"/health"===a.url)return void sendJson(d,200,{ok:!0});if("GET"===a.method&&"/metrics"===a.url)return void function sendText(e,t,r,n="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":n}),e.end(r)}(d,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8");if("GET"===a.method&&"/inspect"===a.url)return void sendJson(d,200,e.inspect());if("GET"===a.method&&"/requests"===a.url)return void sendJson(d,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:n}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(n,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&o)return sendJson(n,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(n,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(n,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(n,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(n,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(n,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(n,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:a,response:d}))return;const i=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(a.url);if("GET"===a.method&&i)return void sendJson(d,200,e.listArtifacts(i));const u=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&u){const t=e.getArtifact(u),r=e.readArtifact(u);return void sendJson(d,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const c=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&c){const t=e.getArtifact(c);return void sendJson(d,t?200:404,t??{error:"artifact_not_found"})}const f=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(a.url);if("GET"===a.method&&f)return void sendJson(d,200,await e.listApprovals(f.status));const m=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(a.url);if("POST"===a.method&&m){const t=await e.resolveApproval(m.id,m.status);return void sendJson(d,t?200:404,t??{error:"approval_not_found"})}if("GET"===a.method&&"/workflows"===a.url)return void sendJson(d,200,e.inspect().workflows);const l=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&l){const t=e.getWorkflow(l);return void sendJson(d,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"})}const p=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&p){const r=e.getWorkflow(p);return void sendJson(d,r?200:404,r?t(r):{error:"workflow_not_found"})}const h=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&h){const t=e.inspectRequest(h);return void sendJson(d,t?200:404,t??{error:"request_not_found"})}const v=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&v){const t=e.getRun(v);return void sendJson(d,t?200:404,t?r(t):{error:"run_not_found"})}const I=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&I){const t=e.getRun(I);return void sendJson(d,t?200:404,t?n(t):{error:"run_not_found"})}const w=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&w){const t=e.exportReplayBundle(w);return void sendJson(d,t?200:404,t??{error:"run_not_found"})}if("POST"===a.method&&"/requests"===a.url){const t=await readJson(a);return void sendJson(d,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),n=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=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}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...n?{memory:n}:{},...o?{metadata:o}:{}}}(t)))}sendJson(d,404,{error:"not_found"})}(e,a,d)}catch(e){sendJson(d,500,{error:e instanceof Error?e.message:String(e)})}}(a,e,d)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{projectRuntimeTrace as r}from"@stable-harness/core";import{createCapabilitiesResponse as t,createModelsResponse as o,resolveAgentId as n,toChatCompletion as s,toRuntimeErrorResponse as a,toRuntimeRequest as i}from"./openai-payload.js";import{createContentChunk as d,createRoleChunk as c,createStopChunk as u,missingFinalDelta as l,writeChatSse as p,writeErrorSse as f,writeRuntimeStreamEvent as m,writeSseHeaders as h}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(v,g={}){const y=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}(v,g);return e(async(e,g)=>{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,g,y),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,g))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,y))return void sendJson(g,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(g,200,o(v,y));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(g,200,t());const O=function readTraceRequestId(e){const r=(e??"").match(/^\/v1\/stable-harness\/runs\/([^/]+)\/trace$/u);return r?.[1]?decodeURIComponent(r[1]):void 0}(e.url);if("GET"===e.method&&O){const e=v.getRun(O);return void sendJson(g,e?200:404,e?r(e):{error:{message:"run_not_found",type:"invalid_request_error"}})}const S=function readApprovalList(e){if(!e?.startsWith("/v1/approvals"))return;const r=new URL(e,"http://stable-harness.local");if("/v1/approvals"!==r.pathname)return;const t=r.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&S)return void sendJson(g,200,{data:await v.listApprovals(S.status)});const R=function readApprovalDecision(e){const r=(e??"").match(/^\/v1\/approvals\/([^/]+)\/(approve|reject)$/u);if(r?.[1]&&r[2])return{id:decodeURIComponent(r[1]),status:"approve"===r[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&R){const e=await v.resolveApproval(R.id,R.status);return void sendJson(g,e?200:404,e?{data:e}:{error:{message:"approval_not_found",type:"invalid_request_error"}})}if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,o){const v=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(v.model&&!n(v.model,e,o))return void sendJson(t,404,{error:{message:`model_not_found: ${v.model}`,type:"invalid_request_error"}});const g=i(v,e,o,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(v.stream)return void await async function streamChatCompletion(e,r,t,o){h(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),p(r,c(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=m(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o);if(isRuntimeError(s))return f(r,a(s)),r.write("data: [DONE]\n\n"),void r.end();const i=l(n,s.output);i&&p(r,d(o.requestId,t.model,i)),p(r,u(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,v,g);const y=await e.request(g);isRuntimeError(y)?sendJson(t,function runtimeErrorStatus(e){return"cancelled"===e.state?409:500}(y),a(y)):sendJson(t,200,s(v,y))}(v,e,g,y);sendJson(g,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(g,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function isRuntimeError(e){return"failed"===e.state||"cancelled"===e.state}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
+ import{createServer as e}from"node:http";import{projectRuntimeTrace as r,renderStableHarnessPrometheusMetrics as t}from"@stable-harness/core";import{createCapabilitiesResponse as o,createModelsResponse as n,resolveAgentId as s,toChatCompletion as a,toRuntimeErrorResponse as i,toRuntimeRequest as d}from"./openai-payload.js";import{createContentChunk as c,createRoleChunk as u,createStopChunk as l,missingFinalDelta as p,writeChatSse as m,writeErrorSse as f,writeRuntimeStreamEvent as h,writeSseHeaders as v}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(g,y={}){const O=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}(g,y);return e(async(e,y)=>{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,y,O),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,y))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,O))return void sendJson(y,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(y,200,n(g,O));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(y,200,o());if("GET"===e.method&&"/metrics"===e.url)return void function sendText(e,r,t,o){e.writeHead(r,{"content-type":o,"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(t)}(y,200,t({runtime:g}),"text/plain; version=0.0.4; charset=utf-8");const S=function readTraceRequestId(e){const r=(e??"").match(/^\/v1\/stable-harness\/runs\/([^/]+)\/trace$/u);return r?.[1]?decodeURIComponent(r[1]):void 0}(e.url);if("GET"===e.method&&S){const e=g.getRun(S);return void sendJson(y,e?200:404,e?r(e):{error:{message:"run_not_found",type:"invalid_request_error"}})}const w=function readApprovalList(e){if(!e?.startsWith("/v1/approvals"))return;const r=new URL(e,"http://stable-harness.local");if("/v1/approvals"!==r.pathname)return;const t=r.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&w)return void sendJson(y,200,{data:await g.listApprovals(w.status)});const T=function readApprovalDecision(e){const r=(e??"").match(/^\/v1\/approvals\/([^/]+)\/(approve|reject)$/u);if(r?.[1]&&r[2])return{id:decodeURIComponent(r[1]),status:"approve"===r[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&T){const e=await g.resolveApproval(T.id,T.status);return void sendJson(y,e?200:404,e?{data:e}:{error:{message:"approval_not_found",type:"invalid_request_error"}})}if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,o){const n=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(n.model&&!s(n.model,e,o))return void sendJson(t,404,{error:{message:`model_not_found: ${n.model}`,type:"invalid_request_error"}});const g=d(n,e,o,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(n.stream)return void await async function streamChatCompletion(e,r,t,o){v(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),m(r,u(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=h(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o);if(isRuntimeError(s))return f(r,i(s)),r.write("data: [DONE]\n\n"),void r.end();const a=p(n,s.output);a&&m(r,c(o.requestId,t.model,a)),m(r,l(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,n,g);const y=await e.request(g);isRuntimeError(y)?sendJson(t,function runtimeErrorStatus(e){return"cancelled"===e.state?409:500}(y),i(y)):sendJson(t,200,a(n,y))}(g,e,y,O);sendJson(y,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(y,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function isRuntimeError(e){return"failed"===e.state||"cancelled"===e.state}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 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","/v1/stable-harness/runs/{request_id}/trace"],streaming:!0,toolProgressEvents:!0,runtimeTrace:!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}}}export function toRuntimeErrorResponse(e){const t="cancelled"===e.state;return{error:{message:e.output||(t?"Runtime request was cancelled.":"Runtime request failed."),type:t?"request_cancelled":"server_error",code:t?"runtime_cancelled":"runtime_failed",param:null},metadata:{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,state:e.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
+ 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","/metrics","/v1/stable-harness/runs/{request_id}/trace"],streaming:!0,toolProgressEvents:!0,runtimeTrace:!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}}}export function toRuntimeErrorResponse(e){const t="cancelled"===e.state;return{error:{message:e.output||(t?"Runtime request was cancelled.":"Runtime request failed."),type:t?"request_cancelled":"server_error",code:t?"runtime_cancelled":"runtime_failed",param:null},metadata:{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,state:e.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -10,6 +10,6 @@
10
10
  "main": "dist/src/index.js",
11
11
  "types": "dist/src/index.d.ts",
12
12
  "peerDependencies": {
13
- "@stable-harness/core": "0.0.84"
13
+ "@stable-harness/core": "0.0.85"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,6 +11,6 @@
11
11
  ".": "./dist/index.js"
12
12
  },
13
13
  "peerDependencies": {
14
- "@stable-harness/core": "0.0.84"
14
+ "@stable-harness/core": "0.0.85"
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -82,14 +82,14 @@
82
82
  "@langchain/node-vfs": "^0.1.4",
83
83
  "@langchain/ollama": "^1.2.7",
84
84
  "@langchain/openai": "^1.4.5",
85
- "@stable-harness/adapter-deepagents": "0.0.84",
86
- "@stable-harness/adapter-langgraph": "0.0.84",
87
- "@stable-harness/core": "0.0.84",
88
- "@stable-harness/governance": "0.0.84",
89
- "@stable-harness/memory": "0.0.84",
90
- "@stable-harness/protocols": "0.0.84",
91
- "@stable-harness/tool-gateway": "0.0.84",
92
- "@stable-harness/workspace-yaml": "0.0.84",
85
+ "@stable-harness/adapter-deepagents": "0.0.85",
86
+ "@stable-harness/adapter-langgraph": "0.0.85",
87
+ "@stable-harness/core": "0.0.85",
88
+ "@stable-harness/governance": "0.0.85",
89
+ "@stable-harness/memory": "0.0.85",
90
+ "@stable-harness/protocols": "0.0.85",
91
+ "@stable-harness/tool-gateway": "0.0.85",
92
+ "@stable-harness/workspace-yaml": "0.0.85",
93
93
  "deepagents": "^1.10.1",
94
94
  "langchain": "^1.4.0",
95
95
  "yaml": "^2.8.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -15,7 +15,7 @@
15
15
  "@langchain/node-vfs": "^0.1.4",
16
16
  "@langchain/ollama": "^1.2.7",
17
17
  "@langchain/openai": "^1.4.5",
18
- "@stable-harness/core": "0.0.84",
18
+ "@stable-harness/core": "0.0.85",
19
19
  "deepagents": "^1.10.1",
20
20
  "langchain": "^1.4.0"
21
21
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-langgraph",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,6 +11,6 @@
11
11
  "types": "dist/src/index.d.ts",
12
12
  "peerDependencies": {
13
13
  "@langchain/langgraph": "^1.3.0",
14
- "@stable-harness/core": "0.0.84"
14
+ "@stable-harness/core": "0.0.85"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/cli",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -14,12 +14,12 @@
14
14
  "types": "dist/src/index.d.ts",
15
15
  "peerDependencies": {
16
16
  "@langchain/langgraph-api": "^1.2.1",
17
- "@stable-harness/adapter-deepagents": "0.0.84",
18
- "@stable-harness/adapter-langgraph": "0.0.84",
19
- "@stable-harness/core": "0.0.84",
20
- "@stable-harness/memory": "0.0.84",
21
- "@stable-harness/protocols": "0.0.84",
22
- "@stable-harness/tool-gateway": "0.0.84",
23
- "@stable-harness/workspace-yaml": "0.0.84"
17
+ "@stable-harness/adapter-deepagents": "0.0.85",
18
+ "@stable-harness/adapter-langgraph": "0.0.85",
19
+ "@stable-harness/core": "0.0.85",
20
+ "@stable-harness/memory": "0.0.85",
21
+ "@stable-harness/protocols": "0.0.85",
22
+ "@stable-harness/tool-gateway": "0.0.85",
23
+ "@stable-harness/workspace-yaml": "0.0.85"
24
24
  }
25
25
  }
@@ -4,6 +4,7 @@ export * from "./boundary-scan.js";
4
4
  export * from "./execution-contract.js";
5
5
  export * from "./recovery/tool-call.js";
6
6
  export * from "./runtime/persistence/inspection.js";
7
+ export * from "./runtime/metrics/prometheus.js";
7
8
  export { createWorkspaceSandboxPolicy } from "./runtime/governance/sandbox.js";
8
9
  export * from "./memory-plugins.js";
9
10
  export { resolveEnabledMemories } from "./memory-plugins/shared.js";
@@ -1 +1 @@
1
- export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
1
+ export*from"./runtime/persistence/artifacts.js";export*from"./boundary-scan.js";export*from"./execution-contract.js";export*from"./recovery/tool-call.js";export*from"./runtime/persistence/inspection.js";export*from"./runtime/metrics/prometheus.js";export{createWorkspaceSandboxPolicy}from"./runtime/governance/sandbox.js";export*from"./memory-plugins.js";export{resolveEnabledMemories}from"./memory-plugins/shared.js";export*from"./runtime/persistence/queue.js";export*from"./runtime/policy/projection.js";export*from"./runtime.js";export*from"./runtime/selection-repair.js";export*from"./runtime/tool-failure.js";export*from"./runtime/tracing/langsmith.js";export*from"./runtime/policy/tool-invocation.js";export*from"./runtime/persistence/stores.js";export*from"./trace.js";export*from"./types.js";export*from"./workspace/tool-quality.js";export*from"./evaluations/index.js";export*from"./quality/index.js";export*from"./spec-driven/index.js";export*from"./workflows/index.js";
@@ -0,0 +1,25 @@
1
+ import type { RuntimeRunRecord, StableHarnessRuntime } from "../../types.js";
2
+ export declare const STABLE_HARNESS_PROMETHEUS_LABELS: readonly ["workspace_id", "agent_id", "tool_id", "model", "status", "event_type", "layer", "outcome"];
3
+ export type StableHarnessPrometheusInput = {
4
+ runtime?: Pick<StableHarnessRuntime, "getRuntimePolicy" | "inspect">;
5
+ runs?: RuntimeRunRecord[];
6
+ workspaceId?: string;
7
+ };
8
+ type MetricSample = {
9
+ labels: Record<string, string>;
10
+ name: string;
11
+ value: number;
12
+ };
13
+ type MetricMap = Map<string, MetricSample>;
14
+ export declare function renderStableHarnessPrometheusMetrics(input?: StableHarnessPrometheusInput): string;
15
+ export declare function collectStableHarnessPrometheusSamples(input?: StableHarnessPrometheusInput): {
16
+ cost: MetricMap;
17
+ durationBuckets: MetricMap;
18
+ durationSumCount: MetricMap;
19
+ events: MetricMap;
20
+ repairs: MetricMap;
21
+ runs: MetricMap;
22
+ tokens: MetricMap;
23
+ tools: MetricMap;
24
+ };
25
+ export {};
@@ -0,0 +1 @@
1
+ export const STABLE_HARNESS_PROMETHEUS_LABELS=["workspace_id","agent_id","tool_id","model","status","event_type","layer","outcome"];const e=[.5,1,2.5,5,10,30,60,120,240,600,Number.POSITIVE_INFINITY];export function renderStableHarnessPrometheusMetrics(e={}){const t=collectStableHarnessPrometheusSamples(e);return`${["# HELP stable_harness_runs_total Stable Harness runs by low-cardinality status and agent.","# TYPE stable_harness_runs_total counter",...renderMetricLines(t.runs),"# HELP stable_harness_run_duration_seconds Stable Harness run duration histogram.","# TYPE stable_harness_run_duration_seconds histogram",...renderMetricLines(t.durationBuckets),...renderMetricLines(t.durationSumCount),"# HELP stable_harness_events_total Stable Harness runtime events by low-cardinality type.","# TYPE stable_harness_events_total counter",...renderMetricLines(t.events),"# HELP stable_harness_tools_total Stable Harness tool outcomes by tool id.","# TYPE stable_harness_tools_total counter",...renderMetricLines(t.tools),"# HELP stable_harness_repairs_total Stable Harness repair attempts by layer and outcome.","# TYPE stable_harness_repairs_total counter",...renderMetricLines(t.repairs),"# HELP stable_harness_tokens_total Stable Harness token usage reported by runtime metadata.","# TYPE stable_harness_tokens_total counter",...renderMetricLines(t.tokens),"# HELP stable_harness_model_cost_usd_total Stable Harness model cost reported by runtime metadata.","# TYPE stable_harness_model_cost_usd_total counter",...renderMetricLines(t.cost)].filter(Boolean).join("\n")}\n`}export function collectStableHarnessPrometheusSamples(e={}){const t=e.runs??e.runtime?.inspect().runs??[],a=cleanLabel(e.workspaceId??e.runtime?.getRuntimePolicy().workspaceId??"stable-harness"),n={cost:newCounter(),durationBuckets:newCounter(),durationSumCount:newCounter(),events:newCounter(),repairs:newCounter(),runs:newCounter(),tokens:newCounter(),tools:newCounter()};for(const e of t){const t=cleanLabel(readString(e.metadata?.workspaceId)??a),r=cleanLabel(e.agentId),s=cleanLabel(e.state);add(n.runs,"stable_harness_runs_total",{agent_id:r,status:s,workspace_id:t},1),addDuration(n,durationSeconds(e),{agent_id:r,workspace_id:t}),addTokenMetrics(n,e,{agent_id:r,model:cleanLabel(readString(e.metadata?.model)??"unknown"),workspace_id:t});for(const a of e.events)add(n.events,"stable_harness_events_total",{agent_id:cleanLabel(a.agentId),event_type:cleanLabel(a.type),workspace_id:t},1),addEventMetrics(n,a,t)}return n}function addEventMetrics(e,t,a){"runtime.tool.direct.started"!==t.type?"runtime.tool.direct.completed"!==t.type?"runtime.tool.failure"!==t.type?"runtime.repair.started"!==t.type?"runtime.repair.completed"===t.type&&add(e.repairs,"stable_harness_repairs_total",{layer:cleanLabel(t.layer),outcome:cleanLabel(t.outcome),workspace_id:a},1):add(e.repairs,"stable_harness_repairs_total",{layer:cleanLabel(t.layer),outcome:"started",workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"failed",tool_id:cleanLabel(t.toolId),workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"completed",tool_id:cleanLabel(t.toolId),workspace_id:a},1):add(e.tools,"stable_harness_tools_total",{status:"started",tool_id:cleanLabel(t.toolId),workspace_id:a},1)}function addTokenMetrics(e,t,a){const n=function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}(t.metadata?.tokenUsage),r=readNumber(n?.inputTokens),s=readNumber(n?.outputTokens),o=readNumber(n?.totalTokens)??readNumber(t.metadata?.totalTokens),l=cleanLabel(readString(n?.source)??"runtime_metadata");add(e.tokens,"stable_harness_tokens_total",{...a,direction:"input",source:l},r??0),add(e.tokens,"stable_harness_tokens_total",{...a,direction:"output",source:l},s??0),add(e.tokens,"stable_harness_tokens_total",{...a,direction:"total",source:l},o??(r??0)+(s??0)),add(e.cost,"stable_harness_model_cost_usd_total",{model:a.model??"unknown",source:l,workspace_id:a.workspace_id??"stable-harness"},readNumber(t.metadata?.costUsd)??readNumber(n?.costUsd)??0)}function durationSeconds(e){const t=Date.parse(e.startedAt),a=Date.parse(e.completedAt??e.startedAt);return!Number.isFinite(t)||!Number.isFinite(a)||a<t?0:(a-t)/1e3}function addDuration(t,a,n){const r=Number.isFinite(a)&&a>=0?a:0;for(const a of e)r<=a&&add(t.durationBuckets,"stable_harness_run_duration_seconds_bucket",{...n,le:a===Number.POSITIVE_INFINITY?"+Inf":String(a)},1);add(t.durationSumCount,"stable_harness_run_duration_seconds_sum",n,r),add(t.durationSumCount,"stable_harness_run_duration_seconds_count",n,1)}function newCounter(){return new Map}function add(e,t,a,n){const r=Number(n??0);if(!Number.isFinite(r)||0===r)return;const s=function sortLabels(e){return Object.fromEntries(Object.entries(e).sort(([e],[t])=>e.localeCompare(t)))}(a),o=`${t}\n${JSON.stringify(s)}`,l=e.get(o)??{labels:s,name:t,value:0};l.value+=r,e.set(o,l)}function renderMetricLines(e){return[...e.values()].sort((e,t)=>e.name.localeCompare(t.name)||JSON.stringify(e.labels).localeCompare(JSON.stringify(t.labels))).map(e=>`${e.name}${function formatLabels(e){const t=Object.entries(e).filter(([,e])=>""!==e);return 0===t.length?"":`{${t.map(([e,t])=>`${e}="${function escapeLabel(e){return e.replaceAll("\\","\\\\").replaceAll("\n","\\n").replaceAll('"','\\"')}(t)}"`).join(",")}}`}(e.labels)} ${function formatNumber(e){return Number.isFinite(e)?String(Number(e.toFixed(6))):"0"}(e.value)}`)}function cleanLabel(e){return String(e??"unknown").trim().replace(/[^\w:.-]+/gu,"_").slice(0,80)||"unknown"}function readString(e){return"string"==typeof e&&e.trim()?e:void 0}function readNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:"string"==typeof e&&e.trim()&&Number.isFinite(Number(e))?Number(e):void 0}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,7 +11,7 @@
11
11
  ".": "./dist/index.js"
12
12
  },
13
13
  "peerDependencies": {
14
- "@stable-harness/governance": "0.0.84",
15
- "@stable-harness/memory": "0.0.84"
14
+ "@stable-harness/governance": "0.0.85",
15
+ "@stable-harness/memory": "0.0.85"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/evaluation",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -10,6 +10,6 @@
10
10
  "main": "dist/src/index.js",
11
11
  "types": "dist/src/index.d.ts",
12
12
  "peerDependencies": {
13
- "@stable-harness/core": "0.0.84"
13
+ "@stable-harness/core": "0.0.85"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/memory",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as o,renderWorkflowMermaid as n}from"@stable-harness/core";export function createHttpServer(s){return e((e,a)=>{!async function handleHttpRequest(e,s,a){try{await async function routeHttpRequest(e,s,a){if("GET"===s.method&&"/health"===s.url)return void sendJson(a,200,{ok:!0});if("GET"===s.method&&"/inspect"===s.url)return void sendJson(a,200,e.inspect());if("GET"===s.method&&"/requests"===s.url)return void sendJson(a,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:o}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(o,200,t.listSessions()),!0;const n=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&n)return sendJson(o,200,t.deleteSession(n)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(o,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(o,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(o,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(o,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(o,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(o,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:s,response:a}))return;const d=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(s.url);if("GET"===s.method&&d)return void sendJson(a,200,e.listArtifacts(d));const i=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&i){const t=e.getArtifact(i),r=e.readArtifact(i);return void sendJson(a,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const u=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&u){const t=e.getArtifact(u);return void sendJson(a,t?200:404,t??{error:"artifact_not_found"})}const c=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(s.url);if("GET"===s.method&&c)return void sendJson(a,200,await e.listApprovals(c.status));const f=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(s.url);if("POST"===s.method&&f){const t=await e.resolveApproval(f.id,f.status);return void sendJson(a,t?200:404,t??{error:"approval_not_found"})}if("GET"===s.method&&"/workflows"===s.url)return void sendJson(a,200,e.inspect().workflows);const m=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&m){const t=e.getWorkflow(m);return void sendJson(a,t?200:404,t?{mermaid:n(t)}:{error:"workflow_not_found"})}const l=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&l){const r=e.getWorkflow(l);return void sendJson(a,r?200:404,r?t(r):{error:"workflow_not_found"})}const p=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&p){const t=e.inspectRequest(p);return void sendJson(a,t?200:404,t??{error:"request_not_found"})}const h=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&h){const t=e.getRun(h);return void sendJson(a,t?200:404,t?r(t):{error:"run_not_found"})}const v=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&v){const t=e.getRun(v);return void sendJson(a,t?200:404,t?o(t):{error:"run_not_found"})}const I=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(s.url);if("GET"===s.method&&I){const t=e.exportReplayBundle(I);return void sendJson(a,t?200:404,t??{error:"run_not_found"})}if("POST"===s.method&&"/requests"===s.url){const t=await readJson(s);return void sendJson(a,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),o=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.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}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...o?{memory:o}:{},...n?{metadata:n}:{}}}(t)))}sendJson(a,404,{error:"not_found"})}(e,s,a)}catch(e){sendJson(a,500,{error:e instanceof Error?e.message:String(e)})}}(s,e,a)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
1
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as r,projectRuntimeTraceSpans as n,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";export function createHttpServer(a){return e((e,d)=>{!async function handleHttpRequest(e,a,d){try{await async function routeHttpRequest(e,a,d){if("GET"===a.method&&"/health"===a.url)return void sendJson(d,200,{ok:!0});if("GET"===a.method&&"/metrics"===a.url)return void function sendText(e,t,r,n="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":n}),e.end(r)}(d,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8");if("GET"===a.method&&"/inspect"===a.url)return void sendJson(d,200,e.inspect());if("GET"===a.method&&"/requests"===a.url)return void sendJson(d,200,e.listRequests());if(await async function handleAdministrationRoutes(e){const{runtime:t,request:r,response:n}=e;if("GET"===r.method&&"/sessions"===r.url)return sendJson(n,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&o)return sendJson(n,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("DELETE"===r.method&&s)return sendJson(n,200,t.deleteRequest(s)),!0;if("GET"===r.method&&r.url?.startsWith("/memories"))return sendJson(n,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),r=t.searchParams.getAll("status").filter(e=>"active"===e||"stale"===e||"conflicted"===e||"archived"===e||"pending_review"===e);return{...t.searchParams.get("namespace")?{namespace:t.searchParams.get("namespace")}:{},...r.length>0?{statuses:r}:{}}}(r.url))),!0;if("POST"===r.method&&"/memories"===r.url){const e=await readJson(r);return sendJson(n,200,await t.memorize(e)),!0}if("POST"===r.method&&"/memories/recall"===r.url){const e=await readJson(r);return sendJson(n,200,await t.recallMemories(e)),!0}const a=function readMemoryArchiveId(e){const t=(e??"").match(/^\/memories\/([^/]+)\/archive$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);if("POST"===r.method&&a){const e=await readJson(r);return sendJson(n,200,await t.archiveMemory(a,"string"==typeof e.reason?e.reason:void 0)),!0}const d=function readMemoryUpdateId(e){const t=(e??"").match(/^\/memories\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(r.url);return!("PATCH"!==r.method||!d||(sendJson(n,200,await t.updateMemory({id:d,...await readJson(r)})),0))}({runtime:e,request:a,response:d}))return;const i=function readArtifactList(e){if(!e?.startsWith("/artifacts"))return;const t=new URL(e,"http://stable-harness.local");return"/artifacts"===t.pathname?{...t.searchParams.get("requestId")?{requestId:t.searchParams.get("requestId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{}}:void 0}(a.url);if("GET"===a.method&&i)return void sendJson(d,200,e.listArtifacts(i));const u=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&u){const t=e.getArtifact(u),r=e.readArtifact(u);return void sendJson(d,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"})}const c=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&c){const t=e.getArtifact(c);return void sendJson(d,t?200:404,t??{error:"artifact_not_found"})}const f=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const r=t.searchParams.get("status");return{status:"pending"===r||"approved"===r||"rejected"===r?r:void 0}}(a.url);if("GET"===a.method&&f)return void sendJson(d,200,await e.listApprovals(f.status));const m=function readApprovalDecision(e){const t=(e??"").match(/^\/approvals\/([^/]+)\/(approve|reject)$/u);if(t?.[1]&&t[2])return{id:decodeURIComponent(t[1]),status:"approve"===t[2]?"approved":"rejected"}}(a.url);if("POST"===a.method&&m){const t=await e.resolveApproval(m.id,m.status);return void sendJson(d,t?200:404,t??{error:"approval_not_found"})}if("GET"===a.method&&"/workflows"===a.url)return void sendJson(d,200,e.inspect().workflows);const l=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&l){const t=e.getWorkflow(l);return void sendJson(d,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"})}const p=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&p){const r=e.getWorkflow(p);return void sendJson(d,r?200:404,r?t(r):{error:"workflow_not_found"})}const h=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&h){const t=e.inspectRequest(h);return void sendJson(d,t?200:404,t??{error:"request_not_found"})}const v=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&v){const t=e.getRun(v);return void sendJson(d,t?200:404,t?r(t):{error:"run_not_found"})}const I=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&I){const t=e.getRun(I);return void sendJson(d,t?200:404,t?n(t):{error:"run_not_found"})}const w=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(a.url);if("GET"===a.method&&w){const t=e.exportReplayBundle(w);return void sendJson(d,t?200:404,t??{error:"run_not_found"})}if("POST"===a.method&&"/requests"===a.url){const t=await readJson(a);return void sendJson(d,200,await e.request(function readRuntimeRequest(e){const t=function readToolCall(e){if("object"!=typeof e||null===e)return;const t=e;return"string"==typeof t.toolId?{toolId:t.toolId,args:t.args}:void 0}(e.toolCall),r=function readWorkflow(e){const t=readRecord(e);if(t)return{..."string"==typeof t.workflowId?{workflowId:t.workflowId}:{},..."string"==typeof t.routeId?{routeId:t.routeId}:{},...void 0!==t.input?{input:t.input}:{},..."object"==typeof t.metadata&&t.metadata?{metadata:t.metadata}:{}}}(e.workflow),n=function readMemory(e){const t=readRecord(e);if(t)return{..."string"==typeof t.namespace?{namespace:t.namespace}:{},...!1===t.recall||readRecord(t.recall)?{recall:t.recall}:{},...Array.isArray(t.candidates)?{candidates:t.candidates}:{}}}(e.memory),o=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}:{},...t?{toolCall:t}:{},...r?{workflow:r}:{},...n?{memory:n}:{},...o?{metadata:o}:{}}}(t)))}sendJson(d,404,{error:"not_found"})}(e,a,d)}catch(e){sendJson(d,500,{error:e instanceof Error?e.message:String(e)})}}(a,e,d)})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,r){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(r))}async function readJson(e){const t=[];for await(const r of e)t.push(Buffer.isBuffer(r)?r:Buffer.from(r));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{projectRuntimeTrace as r}from"@stable-harness/core";import{createCapabilitiesResponse as t,createModelsResponse as o,resolveAgentId as n,toChatCompletion as s,toRuntimeErrorResponse as a,toRuntimeRequest as i}from"./openai-payload.js";import{createContentChunk as d,createRoleChunk as c,createStopChunk as u,missingFinalDelta as l,writeChatSse as p,writeErrorSse as f,writeRuntimeStreamEvent as m,writeSseHeaders as h}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(v,g={}){const y=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}(v,g);return e(async(e,g)=>{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,g,y),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,g))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,y))return void sendJson(g,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(g,200,o(v,y));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(g,200,t());const O=function readTraceRequestId(e){const r=(e??"").match(/^\/v1\/stable-harness\/runs\/([^/]+)\/trace$/u);return r?.[1]?decodeURIComponent(r[1]):void 0}(e.url);if("GET"===e.method&&O){const e=v.getRun(O);return void sendJson(g,e?200:404,e?r(e):{error:{message:"run_not_found",type:"invalid_request_error"}})}const S=function readApprovalList(e){if(!e?.startsWith("/v1/approvals"))return;const r=new URL(e,"http://stable-harness.local");if("/v1/approvals"!==r.pathname)return;const t=r.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&S)return void sendJson(g,200,{data:await v.listApprovals(S.status)});const R=function readApprovalDecision(e){const r=(e??"").match(/^\/v1\/approvals\/([^/]+)\/(approve|reject)$/u);if(r?.[1]&&r[2])return{id:decodeURIComponent(r[1]),status:"approve"===r[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&R){const e=await v.resolveApproval(R.id,R.status);return void sendJson(g,e?200:404,e?{data:e}:{error:{message:"approval_not_found",type:"invalid_request_error"}})}if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,o){const v=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(v.model&&!n(v.model,e,o))return void sendJson(t,404,{error:{message:`model_not_found: ${v.model}`,type:"invalid_request_error"}});const g=i(v,e,o,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(v.stream)return void await async function streamChatCompletion(e,r,t,o){h(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),p(r,c(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=m(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o);if(isRuntimeError(s))return f(r,a(s)),r.write("data: [DONE]\n\n"),void r.end();const i=l(n,s.output);i&&p(r,d(o.requestId,t.model,i)),p(r,u(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,v,g);const y=await e.request(g);isRuntimeError(y)?sendJson(t,function runtimeErrorStatus(e){return"cancelled"===e.state?409:500}(y),a(y)):sendJson(t,200,s(v,y))}(v,e,g,y);sendJson(g,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(g,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function isRuntimeError(e){return"failed"===e.state||"cancelled"===e.state}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
+ import{createServer as e}from"node:http";import{projectRuntimeTrace as r,renderStableHarnessPrometheusMetrics as t}from"@stable-harness/core";import{createCapabilitiesResponse as o,createModelsResponse as n,resolveAgentId as s,toChatCompletion as a,toRuntimeErrorResponse as i,toRuntimeRequest as d}from"./openai-payload.js";import{createContentChunk as c,createRoleChunk as u,createStopChunk as l,missingFinalDelta as p,writeChatSse as m,writeErrorSse as f,writeRuntimeStreamEvent as h,writeSseHeaders as v}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(g,y={}){const O=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}(g,y);return e(async(e,y)=>{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,y,O),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,y))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,O))return void sendJson(y,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(y,200,n(g,O));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(y,200,o());if("GET"===e.method&&"/metrics"===e.url)return void function sendText(e,r,t,o){e.writeHead(r,{"content-type":o,"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(t)}(y,200,t({runtime:g}),"text/plain; version=0.0.4; charset=utf-8");const S=function readTraceRequestId(e){const r=(e??"").match(/^\/v1\/stable-harness\/runs\/([^/]+)\/trace$/u);return r?.[1]?decodeURIComponent(r[1]):void 0}(e.url);if("GET"===e.method&&S){const e=g.getRun(S);return void sendJson(y,e?200:404,e?r(e):{error:{message:"run_not_found",type:"invalid_request_error"}})}const w=function readApprovalList(e){if(!e?.startsWith("/v1/approvals"))return;const r=new URL(e,"http://stable-harness.local");if("/v1/approvals"!==r.pathname)return;const t=r.searchParams.get("status");return{status:"pending"===t||"approved"===t||"rejected"===t?t:void 0}}(e.url);if("GET"===e.method&&w)return void sendJson(y,200,{data:await g.listApprovals(w.status)});const T=function readApprovalDecision(e){const r=(e??"").match(/^\/v1\/approvals\/([^/]+)\/(approve|reject)$/u);if(r?.[1]&&r[2])return{id:decodeURIComponent(r[1]),status:"approve"===r[2]?"approved":"rejected"}}(e.url);if("POST"===e.method&&T){const e=await g.resolveApproval(T.id,T.status);return void sendJson(y,e?200:404,e?{data:e}:{error:{message:"approval_not_found",type:"invalid_request_error"}})}if("POST"===e.method&&"/v1/chat/completions"===e.url)return void await async function handleChatCompletion(e,r,t,o){const n=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(n.model&&!s(n.model,e,o))return void sendJson(t,404,{error:{message:`model_not_found: ${n.model}`,type:"invalid_request_error"}});const g=d(n,e,o,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(n.stream)return void await async function streamChatCompletion(e,r,t,o){v(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),m(r,u(o.requestId,t.model));let n="";const s=e.subscribe(e=>{e.requestId===o.requestId&&(n+=h(r,e,o.requestId,t.model)??"")});try{const s=await e.request(o);if(isRuntimeError(s))return f(r,i(s)),r.write("data: [DONE]\n\n"),void r.end();const a=p(n,s.output);a&&m(r,c(o.requestId,t.model,a)),m(r,l(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{s()}}(e,t,n,g);const y=await e.request(g);isRuntimeError(y)?sendJson(t,function runtimeErrorStatus(e){return"cancelled"===e.state?409:500}(y),i(y)):sendJson(t,200,a(n,y))}(g,e,y,O);sendJson(y,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(y,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function isRuntimeError(e){return"failed"===e.state||"cancelled"===e.state}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 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","/v1/stable-harness/runs/{request_id}/trace"],streaming:!0,toolProgressEvents:!0,runtimeTrace:!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}}}export function toRuntimeErrorResponse(e){const t="cancelled"===e.state;return{error:{message:e.output||(t?"Runtime request was cancelled.":"Runtime request failed."),type:t?"request_cancelled":"server_error",code:t?"runtime_cancelled":"runtime_failed",param:null},metadata:{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,state:e.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
+ 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","/metrics","/v1/stable-harness/runs/{request_id}/trace"],streaming:!0,toolProgressEvents:!0,runtimeTrace:!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}}}export function toRuntimeErrorResponse(e){const t="cancelled"===e.state;return{error:{message:e.output||(t?"Runtime request was cancelled.":"Runtime request failed."),type:t?"request_cancelled":"server_error",code:t?"runtime_cancelled":"runtime_failed",param:null},metadata:{requestId:e.requestId,sessionId:e.sessionId,agentId:e.agentId,state:e.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -10,6 +10,6 @@
10
10
  "main": "dist/src/index.js",
11
11
  "types": "dist/src/index.d.ts",
12
12
  "peerDependencies": {
13
- "@stable-harness/core": "0.0.84"
13
+ "@stable-harness/core": "0.0.85"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/workspace-yaml",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -11,6 +11,6 @@
11
11
  ".": "./dist/index.js"
12
12
  },
13
13
  "peerDependencies": {
14
- "@stable-harness/core": "0.0.84"
14
+ "@stable-harness/core": "0.0.85"
15
15
  }
16
16
  }