stable-harness 0.0.87 → 0.0.88

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 (28) hide show
  1. package/README.md +1 -1
  2. package/docs/guides/integration-guide.md +11 -5
  3. package/docs/protocols/http-runtime.md +51 -3
  4. package/node_modules/@stable-harness/adapter-deepagents/package.json +2 -2
  5. package/node_modules/@stable-harness/adapter-langgraph/package.json +2 -2
  6. package/node_modules/@stable-harness/core/package.json +3 -3
  7. package/node_modules/@stable-harness/governance/package.json +1 -1
  8. package/node_modules/@stable-harness/memory/package.json +1 -1
  9. package/node_modules/@stable-harness/protocols/dist/src/http-events.d.ts +16 -0
  10. package/node_modules/@stable-harness/protocols/dist/src/http-events.js +1 -0
  11. package/node_modules/@stable-harness/protocols/dist/src/http-server.js +1 -1
  12. package/node_modules/@stable-harness/protocols/package.json +2 -2
  13. package/node_modules/@stable-harness/tool-gateway/package.json +1 -1
  14. package/node_modules/@stable-harness/workspace-yaml/package.json +2 -2
  15. package/package.json +10 -10
  16. package/packages/adapter-deepagents/package.json +2 -2
  17. package/packages/adapter-langgraph/package.json +2 -2
  18. package/packages/cli/package.json +8 -8
  19. package/packages/core/package.json +3 -3
  20. package/packages/evaluation/package.json +2 -2
  21. package/packages/governance/package.json +1 -1
  22. package/packages/memory/package.json +1 -1
  23. package/packages/protocols/dist/src/http-events.d.ts +16 -0
  24. package/packages/protocols/dist/src/http-events.js +1 -0
  25. package/packages/protocols/dist/src/http-server.js +1 -1
  26. package/packages/protocols/package.json +2 -2
  27. package/packages/tool-gateway/package.json +1 -1
  28. package/packages/workspace-yaml/package.json +2 -2
package/README.md CHANGED
@@ -219,9 +219,9 @@ This is constrained repair, not silent magic:
219
219
 
220
220
  ## Protocols
221
221
 
222
+ - Stable Runtime HTTP + SSE: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
222
223
  - OpenAI-compatible facade: [docs/protocols/openai-compatible.md](docs/protocols/openai-compatible.md)
223
224
  - LangGraph-compatible facade: [docs/protocols/langgraph-compatible.md](docs/protocols/langgraph-compatible.md)
224
- - HTTP runtime protocol: [docs/protocols/http-runtime.md](docs/protocols/http-runtime.md)
225
225
 
226
226
  ## Documentation
227
227
 
@@ -88,13 +88,19 @@ Use this path when an existing product or evaluation harness already speaks
88
88
  OpenAI-compatible chat completions. Stable Harness still owns workspace loading,
89
89
  runtime events, tool-gateway policy, memory lifecycle, and adapter selection.
90
90
 
91
- ## HTTP Runtime Protocol
91
+ ## Stable Runtime HTTP + SSE
92
92
 
93
- Use the HTTP runtime protocol when the caller needs Stable Harness concepts
94
- directly: request IDs, sessions, traces, approvals, cancellation, and runtime
95
- inspection.
93
+ Use Stable Runtime HTTP + SSE when the caller needs Stable Harness concepts
94
+ directly: request IDs, sessions, traces, spans, approvals, cancellation,
95
+ artifacts, memory lifecycle, runtime inspection, and real-time control-plane
96
+ updates.
96
97
 
97
- See [HTTP runtime protocol](protocols/http-runtime.md).
98
+ This is the first-party protocol for Studio and product shells. OpenAI
99
+ compatible `/v1` and LangGraph-compatible servers are ecosystem facades; future
100
+ ACP, A2A, and AG-UI support should map to this protocol instead of adding new
101
+ runtime semantics.
102
+
103
+ See [Stable Runtime HTTP + SSE](../protocols/http-runtime.md).
98
104
 
99
105
  ## Backend Adapters
100
106
 
@@ -1,6 +1,14 @@
1
- # HTTP Runtime Protocol
1
+ # Stable Runtime HTTP + SSE Protocol
2
2
 
3
- The native HTTP server exposes stable runtime state and request submission.
3
+ The native Stable Runtime protocol is the product control-plane contract. It is
4
+ the source of truth for first-party clients such as Studio, operator dashboards,
5
+ and embedded product shells.
6
+
7
+ OpenAI-compatible `/v1` and LangGraph-compatible servers are ecosystem
8
+ facades. ACP, A2A, and AG-UI should be added later as adapters over this
9
+ contract, not as new runtime semantics.
10
+
11
+ ## HTTP Commands And Queries
4
12
 
5
13
  `POST /requests` is a protocol adapter over `RuntimeRequest`. It may pass these
6
14
  stable fields through to the runtime:
@@ -22,8 +30,9 @@ runtime requests.
22
30
  Inspection endpoints expose normalized runtime state:
23
31
 
24
32
  - `GET /inspect`
25
- - `GET /requests`
33
+ - `GET /requests?sessionId=...&agentId=...&state=...`
26
34
  - `GET /requests/:id`
35
+ - `POST /requests/:id/cancel`
27
36
  - `DELETE /requests/:id`
28
37
  - `GET /runs/:id/trace`
29
38
  - `GET /runs/:id/spans`
@@ -33,6 +42,12 @@ Inspection endpoints expose normalized runtime state:
33
42
  - `GET /workflows/:id/mermaid`
34
43
  - `GET /workflows/:id/plan`
35
44
 
45
+ Approval endpoints expose operator decisions:
46
+
47
+ - `GET /approvals?status=pending`
48
+ - `POST /approvals/:id/approve`
49
+ - `POST /approvals/:id/reject`
50
+
36
51
  Memory endpoints expose the native runtime memory store when one is configured:
37
52
 
38
53
  - `POST /memories`
@@ -53,3 +68,36 @@ Each span includes `spanId`, optional `parentSpanId`, `kind`, `name`, `status`,
53
68
  event references, and timing when the stored events include `emittedAt`.
54
69
  Clients should render hierarchy from `parentSpanId` instead of inferring it from
55
70
  display labels.
71
+
72
+ ## SSE Runtime Events
73
+
74
+ `GET /events` streams normalized `RuntimeEvent` records as Server-Sent Events.
75
+ It replays matching stored events first, then streams new events from
76
+ `runtime.subscribe`.
77
+
78
+ Supported filters:
79
+
80
+ - `requestId`
81
+ - `sessionId`
82
+ - `agentId`
83
+ - `types`, as comma-separated event names or prefix globs such as
84
+ `runtime.request.*,runtime.approval.*`
85
+ - `since`, as an emitted-at timestamp cursor
86
+
87
+ Example:
88
+
89
+ ```text
90
+ GET /events?sessionId=customer-123&types=runtime.request.*,runtime.approval.*
91
+ ```
92
+
93
+ Event format:
94
+
95
+ ```text
96
+ event: runtime.request.started
97
+ id: <eventId>
98
+ data: {"type":"runtime.request.started","eventId":"...","requestId":"...","sessionId":"...","agentId":"...","emittedAt":"..."}
99
+ ```
100
+
101
+ This stream is for runtime/control-plane updates. OpenAI chat streaming remains
102
+ scoped to a single `/v1/chat/completions` response and must not become the
103
+ first-party control-plane event source.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/adapter-deepagents",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
18
+ "@stable-harness/core": "0.0.88",
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.87",
3
+ "version": "0.0.88",
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.87"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
15
- "@stable-harness/memory": "0.0.87"
14
+ "@stable-harness/governance": "0.0.88",
15
+ "@stable-harness/memory": "0.0.88"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -0,0 +1,16 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { StableHarnessRuntime } from "@stable-harness/core";
3
+ export type RuntimeEventFilter = {
4
+ requestId?: string;
5
+ sessionId?: string;
6
+ agentId?: string;
7
+ types?: string[];
8
+ since?: string;
9
+ };
10
+ export declare function readRuntimeEventFilter(url: string | undefined): RuntimeEventFilter | undefined;
11
+ export declare function streamRuntimeEvents(input: {
12
+ runtime: StableHarnessRuntime;
13
+ request: IncomingMessage;
14
+ response: ServerResponse;
15
+ filter: RuntimeEventFilter;
16
+ }): void;
@@ -0,0 +1 @@
1
+ export function readRuntimeEventFilter(e){if(!e?.startsWith("/events"))return;const t=new URL(e,"http://stable-harness.local");if("/events"!==t.pathname)return;const n=t.searchParams.get("types")?.split(",").map(e=>e.trim()).filter(Boolean);return{...readParam(t,"requestId")?{requestId:readParam(t,"requestId")}:{},...readParam(t,"sessionId")?{sessionId:readParam(t,"sessionId")}:{},...readParam(t,"agentId")?{agentId:readParam(t,"agentId")}:{},...n&&n.length>0?{types:n}:{},...readParam(t,"since")?{since:readParam(t,"since")}:{}}}export function streamRuntimeEvents(e){e.response.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"}),e.response.write(": stable-harness runtime event stream\n\n"),e.response.flushHeaders();for(const t of function replayEvents(e,t){return e.inspect().runs.flatMap(e=>e.events).filter(e=>matchesEventFilter(e,t))}(e.runtime,e.filter))writeRuntimeEvent(e.response,t);const t=e.runtime.subscribe(t=>{matchesEventFilter(t,e.filter)&&writeRuntimeEvent(e.response,t)});e.request.on("close",()=>{t(),e.response.end()})}function matchesEventFilter(e,t){return matchesValue(t.requestId,e.requestId)&&matchesValue(t.sessionId,e.sessionId)&&matchesValue(t.agentId,e.agentId)&&function matchesTypeFilter(e,t){return!t||0===t.length||t.some(t=>t.endsWith("*")?e.startsWith(t.slice(0,-1)):e===t)}(e.type,t.types)&&function matchesSince(e,t){return!t||e.eventId!==t&&Boolean(e.emittedAt&&e.emittedAt>t)}(e,t.since)}function matchesValue(e,t){return!e||e===t}function writeRuntimeEvent(e,t){e.write(`event: ${t.type}\n`),t.eventId&&e.write(`id: ${t.eventId}\n`),e.write(`data: ${JSON.stringify(t)}\n\n`)}function readParam(e,t){const n=e.searchParams.get(t);return n&&n.trim()?n:void 0}
@@ -1 +1 @@
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
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as n,projectRuntimeTraceSpans as r,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";import{readRuntimeEventFilter as a,streamRuntimeEvents as d}from"./http-events.js";export function createHttpServer(u){return e((e,i)=>{!async function handleHttpRequest(e,u,i){try{await async function routeHttpRequest(e,u,i){(function handleSystemRoutes(e,t,n){return"GET"===t.method&&"/health"===t.url?(sendJson(n,200,{ok:!0}),!0):"GET"===t.method&&"/metrics"===t.url?(function sendText(e,t,n,r="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":r}),e.end(n)}(n,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8"),!0):"GET"===t.method&&"/inspect"===t.url&&(sendJson(n,200,e.inspect()),!0)})(e,u,i)||function handleEventRoutes(e,t,n){const r=a(t.url);return!("GET"!==t.method||!r||(d({runtime:e,request:t,response:n,filter:r}),0))}(e,u,i)||await async function handleAdministrationRoutes(e){const{runtime:t,request:n,response:r}=e;if("GET"===n.method&&"/sessions"===n.url)return sendJson(r,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&o)return sendJson(r,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&s)return sendJson(r,200,t.deleteRequest(s)),!0;if("GET"===n.method&&n.url?.startsWith("/memories"))return sendJson(r,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),n=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")}:{},...n.length>0?{statuses:n}:{}}}(n.url))),!0;if("POST"===n.method&&"/memories"===n.url){const e=await readJson(n);return sendJson(r,200,await t.memorize(e)),!0}if("POST"===n.method&&"/memories/recall"===n.url){const e=await readJson(n);return sendJson(r,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}(n.url);if("POST"===n.method&&a){const e=await readJson(n);return sendJson(r,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}(n.url);return!("PATCH"!==n.method||!d||(sendJson(r,200,await t.updateMemory({id:d,...await readJson(n)})),0))}({runtime:e,request:u,response:i})||function handleArtifactRoutes(e,t,n){const r=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}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listArtifacts(r)),!0;const o=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&o){const t=e.getArtifact(o),r=e.readArtifact(o);return sendJson(n,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"}),!0}const s=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.getArtifact(s);return sendJson(n,t?200:404,t??{error:"artifact_not_found"}),!0}return!1}(e,u,i)||await async function handleApprovalRoutes(e,t,n){const r=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const n=t.searchParams.get("status");return{status:"pending"===n||"approved"===n||"rejected"===n?n:void 0}}(t.url);if("GET"===t.method&&r)return sendJson(n,200,await e.listApprovals(r.status)),!0;const o=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"}}(t.url);if("POST"===t.method&&o){const t=await e.resolveApproval(o.id,o.status);return sendJson(n,t?200:404,t??{error:"approval_not_found"}),!0}return!1}(e,u,i)||await async function handleRequestRoutes(e,t,n){if("GET"===t.method&&"/requests"===t.url)return sendJson(n,200,e.listRequests()),!0;const r=function readRequestList(e){if(!e?.startsWith("/requests"))return;const t=new URL(e,"http://stable-harness.local");return"/requests"===t.pathname?{...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...readRunState(t.searchParams.get("state"))?{state:readRunState(t.searchParams.get("state"))}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listRequests(r)),!0;const o=function readRequestCancelId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/cancel$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("POST"===t.method&&o){const r=await readJson(t);return e.cancel(o,"string"==typeof r.reason?r.reason:void 0),sendJson(n,200,e.inspectRequest(o)??{error:"request_not_found"}),!0}if("POST"===t.method&&"/requests"===t.url){const r=await readJson(t);return sendJson(n,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),n=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),r=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}:{},...n?{workflow:n}:{},...r?{memory:r}:{},...o?{metadata:o}:{}}}(r))),!0}return!1}(e,u,i)||function handleWorkflowRoutes(e,n,r){if("GET"===n.method&&"/workflows"===n.url)return sendJson(r,200,e.inspect().workflows),!0;const o=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&o){const t=e.getWorkflow(o);return sendJson(r,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"}),!0}const a=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&a){const n=e.getWorkflow(a);return sendJson(r,n?200:404,n?t(n):{error:"workflow_not_found"}),!0}return!1}(e,u,i)||function handleRunInspectionRoutes(e,t,o){const s=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(o,t?200:404,t??{error:"request_not_found"}),!0}const a=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&a){const t=e.getRun(a);return sendJson(o,t?200:404,t?n(t):{error:"run_not_found"}),!0}const d=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&d){const t=e.getRun(d);return sendJson(o,t?200:404,t?r(t):{error:"run_not_found"}),!0}const u=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&u){const t=e.exportReplayBundle(u);return sendJson(o,t?200:404,t??{error:"run_not_found"}),!0}return!1}(e,u,i)||sendJson(i,404,{error:"not_found"})}(e,u,i)}catch(e){sendJson(i,500,{error:e instanceof Error?e.message:String(e)})}}(u,e,i)})}function readRunState(e){return"queued"===e||"running"===e||"completed"===e||"failed"===e||"cancelled"===e?e:void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,n){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}async function readJson(e){const t=[];for await(const n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
3
+ "version": "0.0.88",
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.87"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -75,21 +75,21 @@
75
75
  "packages/*"
76
76
  ],
77
77
  "dependencies": {
78
- "@easynet/better-call": "^0.1.69",
78
+ "@easynet/better-call": "^0.1.70",
79
79
  "@langchain/core": "^1.1.46",
80
80
  "@langchain/langgraph": "^1.3.0",
81
81
  "@langchain/langgraph-api": "^1.2.1",
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.87",
86
- "@stable-harness/adapter-langgraph": "0.0.87",
87
- "@stable-harness/core": "0.0.87",
88
- "@stable-harness/governance": "0.0.87",
89
- "@stable-harness/memory": "0.0.87",
90
- "@stable-harness/protocols": "0.0.87",
91
- "@stable-harness/tool-gateway": "0.0.87",
92
- "@stable-harness/workspace-yaml": "0.0.87",
85
+ "@stable-harness/adapter-deepagents": "0.0.88",
86
+ "@stable-harness/adapter-langgraph": "0.0.88",
87
+ "@stable-harness/core": "0.0.88",
88
+ "@stable-harness/governance": "0.0.88",
89
+ "@stable-harness/memory": "0.0.88",
90
+ "@stable-harness/protocols": "0.0.88",
91
+ "@stable-harness/tool-gateway": "0.0.88",
92
+ "@stable-harness/workspace-yaml": "0.0.88",
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.87",
3
+ "version": "0.0.88",
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.87",
18
+ "@stable-harness/core": "0.0.88",
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.87",
3
+ "version": "0.0.88",
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.87"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/cli",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
18
- "@stable-harness/adapter-langgraph": "0.0.87",
19
- "@stable-harness/core": "0.0.87",
20
- "@stable-harness/memory": "0.0.87",
21
- "@stable-harness/protocols": "0.0.87",
22
- "@stable-harness/tool-gateway": "0.0.87",
23
- "@stable-harness/workspace-yaml": "0.0.87"
17
+ "@stable-harness/adapter-deepagents": "0.0.88",
18
+ "@stable-harness/adapter-langgraph": "0.0.88",
19
+ "@stable-harness/core": "0.0.88",
20
+ "@stable-harness/memory": "0.0.88",
21
+ "@stable-harness/protocols": "0.0.88",
22
+ "@stable-harness/tool-gateway": "0.0.88",
23
+ "@stable-harness/workspace-yaml": "0.0.88"
24
24
  }
25
25
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/core",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
15
- "@stable-harness/memory": "0.0.87"
14
+ "@stable-harness/governance": "0.0.88",
15
+ "@stable-harness/memory": "0.0.88"
16
16
  }
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/evaluation",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/governance",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
3
+ "version": "0.0.88",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist/**/*.js",
@@ -0,0 +1,16 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { StableHarnessRuntime } from "@stable-harness/core";
3
+ export type RuntimeEventFilter = {
4
+ requestId?: string;
5
+ sessionId?: string;
6
+ agentId?: string;
7
+ types?: string[];
8
+ since?: string;
9
+ };
10
+ export declare function readRuntimeEventFilter(url: string | undefined): RuntimeEventFilter | undefined;
11
+ export declare function streamRuntimeEvents(input: {
12
+ runtime: StableHarnessRuntime;
13
+ request: IncomingMessage;
14
+ response: ServerResponse;
15
+ filter: RuntimeEventFilter;
16
+ }): void;
@@ -0,0 +1 @@
1
+ export function readRuntimeEventFilter(e){if(!e?.startsWith("/events"))return;const t=new URL(e,"http://stable-harness.local");if("/events"!==t.pathname)return;const n=t.searchParams.get("types")?.split(",").map(e=>e.trim()).filter(Boolean);return{...readParam(t,"requestId")?{requestId:readParam(t,"requestId")}:{},...readParam(t,"sessionId")?{sessionId:readParam(t,"sessionId")}:{},...readParam(t,"agentId")?{agentId:readParam(t,"agentId")}:{},...n&&n.length>0?{types:n}:{},...readParam(t,"since")?{since:readParam(t,"since")}:{}}}export function streamRuntimeEvents(e){e.response.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"}),e.response.write(": stable-harness runtime event stream\n\n"),e.response.flushHeaders();for(const t of function replayEvents(e,t){return e.inspect().runs.flatMap(e=>e.events).filter(e=>matchesEventFilter(e,t))}(e.runtime,e.filter))writeRuntimeEvent(e.response,t);const t=e.runtime.subscribe(t=>{matchesEventFilter(t,e.filter)&&writeRuntimeEvent(e.response,t)});e.request.on("close",()=>{t(),e.response.end()})}function matchesEventFilter(e,t){return matchesValue(t.requestId,e.requestId)&&matchesValue(t.sessionId,e.sessionId)&&matchesValue(t.agentId,e.agentId)&&function matchesTypeFilter(e,t){return!t||0===t.length||t.some(t=>t.endsWith("*")?e.startsWith(t.slice(0,-1)):e===t)}(e.type,t.types)&&function matchesSince(e,t){return!t||e.eventId!==t&&Boolean(e.emittedAt&&e.emittedAt>t)}(e,t.since)}function matchesValue(e,t){return!e||e===t}function writeRuntimeEvent(e,t){e.write(`event: ${t.type}\n`),t.eventId&&e.write(`id: ${t.eventId}\n`),e.write(`data: ${JSON.stringify(t)}\n\n`)}function readParam(e,t){const n=e.searchParams.get(t);return n&&n.trim()?n:void 0}
@@ -1 +1 @@
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
+ import{createServer as e}from"node:http";import{compileWorkflowPlan as t,projectRuntimeTrace as n,projectRuntimeTraceSpans as r,renderStableHarnessPrometheusMetrics as o,renderWorkflowMermaid as s}from"@stable-harness/core";import{readRuntimeEventFilter as a,streamRuntimeEvents as d}from"./http-events.js";export function createHttpServer(u){return e((e,i)=>{!async function handleHttpRequest(e,u,i){try{await async function routeHttpRequest(e,u,i){(function handleSystemRoutes(e,t,n){return"GET"===t.method&&"/health"===t.url?(sendJson(n,200,{ok:!0}),!0):"GET"===t.method&&"/metrics"===t.url?(function sendText(e,t,n,r="text/plain; charset=utf-8"){e.writeHead(t,{"content-type":r}),e.end(n)}(n,200,o({runtime:e}),"text/plain; version=0.0.4; charset=utf-8"),!0):"GET"===t.method&&"/inspect"===t.url&&(sendJson(n,200,e.inspect()),!0)})(e,u,i)||function handleEventRoutes(e,t,n){const r=a(t.url);return!("GET"!==t.method||!r||(d({runtime:e,request:t,response:n,filter:r}),0))}(e,u,i)||await async function handleAdministrationRoutes(e){const{runtime:t,request:n,response:r}=e;if("GET"===n.method&&"/sessions"===n.url)return sendJson(r,200,t.listSessions()),!0;const o=function readSessionDeleteId(e){const t=(e??"").match(/^\/sessions\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&o)return sendJson(r,200,t.deleteSession(o)),!0;const s=function readRequestDeleteId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("DELETE"===n.method&&s)return sendJson(r,200,t.deleteRequest(s)),!0;if("GET"===n.method&&n.url?.startsWith("/memories"))return sendJson(r,200,await t.listMemories(function readMemoryList(e){const t=new URL(e??"/memories","http://stable-harness.local"),n=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")}:{},...n.length>0?{statuses:n}:{}}}(n.url))),!0;if("POST"===n.method&&"/memories"===n.url){const e=await readJson(n);return sendJson(r,200,await t.memorize(e)),!0}if("POST"===n.method&&"/memories/recall"===n.url){const e=await readJson(n);return sendJson(r,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}(n.url);if("POST"===n.method&&a){const e=await readJson(n);return sendJson(r,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}(n.url);return!("PATCH"!==n.method||!d||(sendJson(r,200,await t.updateMemory({id:d,...await readJson(n)})),0))}({runtime:e,request:u,response:i})||function handleArtifactRoutes(e,t,n){const r=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}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listArtifacts(r)),!0;const o=function readArtifactContentId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)\/content$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&o){const t=e.getArtifact(o),r=e.readArtifact(o);return sendJson(n,t&&void 0!==r?200:404,t&&void 0!==r?{artifact:t,content:r}:{error:"artifact_content_not_found"}),!0}const s=function readArtifactId(e){const t=(e??"").match(/^\/artifacts\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.getArtifact(s);return sendJson(n,t?200:404,t??{error:"artifact_not_found"}),!0}return!1}(e,u,i)||await async function handleApprovalRoutes(e,t,n){const r=function readApprovalList(e){if(!e?.startsWith("/approvals"))return;const t=new URL(e,"http://stable-harness.local");if("/approvals"!==t.pathname)return;const n=t.searchParams.get("status");return{status:"pending"===n||"approved"===n||"rejected"===n?n:void 0}}(t.url);if("GET"===t.method&&r)return sendJson(n,200,await e.listApprovals(r.status)),!0;const o=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"}}(t.url);if("POST"===t.method&&o){const t=await e.resolveApproval(o.id,o.status);return sendJson(n,t?200:404,t??{error:"approval_not_found"}),!0}return!1}(e,u,i)||await async function handleRequestRoutes(e,t,n){if("GET"===t.method&&"/requests"===t.url)return sendJson(n,200,e.listRequests()),!0;const r=function readRequestList(e){if(!e?.startsWith("/requests"))return;const t=new URL(e,"http://stable-harness.local");return"/requests"===t.pathname?{...t.searchParams.get("agentId")?{agentId:t.searchParams.get("agentId")}:{},...t.searchParams.get("sessionId")?{sessionId:t.searchParams.get("sessionId")}:{},...readRunState(t.searchParams.get("state"))?{state:readRunState(t.searchParams.get("state"))}:{}}:void 0}(t.url);if("GET"===t.method&&r)return sendJson(n,200,e.listRequests(r)),!0;const o=function readRequestCancelId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/cancel$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("POST"===t.method&&o){const r=await readJson(t);return e.cancel(o,"string"==typeof r.reason?r.reason:void 0),sendJson(n,200,e.inspectRequest(o)??{error:"request_not_found"}),!0}if("POST"===t.method&&"/requests"===t.url){const r=await readJson(t);return sendJson(n,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),n=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),r=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}:{},...n?{workflow:n}:{},...r?{memory:r}:{},...o?{metadata:o}:{}}}(r))),!0}return!1}(e,u,i)||function handleWorkflowRoutes(e,n,r){if("GET"===n.method&&"/workflows"===n.url)return sendJson(r,200,e.inspect().workflows),!0;const o=function readWorkflowMermaidId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/mermaid$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&o){const t=e.getWorkflow(o);return sendJson(r,t?200:404,t?{mermaid:s(t)}:{error:"workflow_not_found"}),!0}const a=function readWorkflowPlanId(e){const t=(e??"").match(/^\/workflows\/([^/]+)\/plan$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(n.url);if("GET"===n.method&&a){const n=e.getWorkflow(a);return sendJson(r,n?200:404,n?t(n):{error:"workflow_not_found"}),!0}return!1}(e,u,i)||function handleRunInspectionRoutes(e,t,o){const s=function readRequestInspectionId(e){const t=(e??"").match(/^\/requests\/([^/]+)$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&s){const t=e.inspectRequest(s);return sendJson(o,t?200:404,t??{error:"request_not_found"}),!0}const a=function readTraceRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/trace$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&a){const t=e.getRun(a);return sendJson(o,t?200:404,t?n(t):{error:"run_not_found"}),!0}const d=function readSpanRequestId(e){const t=(e??"").match(/^\/runs\/([^/]+)\/spans$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&d){const t=e.getRun(d);return sendJson(o,t?200:404,t?r(t):{error:"run_not_found"}),!0}const u=function readReplayRequestId(e){const t=(e??"").match(/^\/requests\/([^/]+)\/replay$/u);return t?.[1]?decodeURIComponent(t[1]):void 0}(t.url);if("GET"===t.method&&u){const t=e.exportReplayBundle(u);return sendJson(o,t?200:404,t??{error:"run_not_found"}),!0}return!1}(e,u,i)||sendJson(i,404,{error:"not_found"})}(e,u,i)}catch(e){sendJson(i,500,{error:e instanceof Error?e.message:String(e)})}}(u,e,i)})}function readRunState(e){return"queued"===e||"running"===e||"completed"===e||"failed"===e||"cancelled"===e?e:void 0}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function sendJson(e,t,n){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}async function readJson(e){const t=[];for await(const n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return 0===t.length?{}:JSON.parse(Buffer.concat(t).toString("utf8"))}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/protocols",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87"
13
+ "@stable-harness/core": "0.0.88"
14
14
  }
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stable-harness/tool-gateway",
3
- "version": "0.0.87",
3
+ "version": "0.0.88",
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.87",
3
+ "version": "0.0.88",
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.87"
14
+ "@stable-harness/core": "0.0.88"
15
15
  }
16
16
  }