stable-harness 0.0.62 → 0.0.64

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.
@@ -1 +1 @@
1
- export function resolveToolCallRecoveryPolicy(e){const o=e.workspace.runtime;if(!function shouldDefaultToolCallRecovery(e){if(0===e.agent.tools.length)return!1;const o=e.agent.modelRef?e.workspace.models.get(e.agent.modelRef):void 0;return"openai-compatible"===o?.provider||"openai-compatible"===readString(o?.config?.toolCallingMode)||"structured"===readString(o?.config?.toolCallingMode)}(e)||function hasExplicitToolCallRecovery(e){const o=readRecord(e.recovery);return"boolean"==typeof readRecord(o.toolCall).enabled}(o))return o;const r=readRecord(o.recovery),t=readRecord(r.toolCall);return{...o,recovery:{...r,toolCall:{...t,enabled:!0,defaultedForStructuredToolCalling:!0}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
1
+ export function resolveToolCallRecoveryPolicy(e){const o=e.workspace.runtime;if(!function shouldDefaultToolCallRecovery(e){if(0===e.agent.tools.length)return!1;const o=e.agent.modelRef?e.workspace.models.get(e.agent.modelRef):void 0;return"openai-compatible"===o?.provider||"openai-compatible"===readString(o?.config?.toolCallingMode)||"structured"===readString(o?.config?.toolCallingMode)||function hasDirectBackendModel(e){const o=readRecord(e.config);return void 0!==readRecord(o.deepagents).model}(e.agent)}(e)||function hasExplicitToolCallRecovery(e){const o=readRecord(e.recovery);return"boolean"==typeof readRecord(o.toolCall).enabled}(o))return o;const r=readRecord(o.recovery),t=readRecord(r.toolCall);return{...o,recovery:{...r,toolCall:{...t,enabled:!0,defaultedForStructuredToolCalling:!0}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
@@ -1,5 +1,5 @@
1
1
  import { type IncomingMessage, type ServerResponse } from "node:http";
2
- import type { StableHarnessRuntime } from "@stable-harness/core";
2
+ import { type StableHarnessRuntime } from "@stable-harness/core";
3
3
  export type OpenAiCompatibleServerOptions = {
4
4
  bearerToken?: string;
5
5
  corsOrigins?: string[];
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{createCapabilitiesResponse as r,createModelsResponse as t,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as a}from"./openai-payload.js";import{createContentChunk as s,createRoleChunk as i,createStopChunk as d,missingFinalDelta as c,writeChatSse as l,writeRuntimeStreamEvent as u,writeSseHeaders as p}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(f,m={}){const h=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}(f,m);return e(async(e,m)=>{try{if(function applyCorsHeaders(e,r,t){const o=function allowedCorsOrigin(e,r){if(e)return r.corsOrigins?.includes("*")?"*":r.corsOrigins?.includes(e)||function isLoopbackOrigin(e){try{const r=new URL(e);return"http:"===r.protocol&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,t);o&&(r.setHeader("access-control-allow-origin",o),r.setHeader("vary","origin"))}(e,m,h),function handleCors(e,r){return"OPTIONS"===e.method&&(r.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),r.end(),!0)}(e,m))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,h))return void sendJson(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(m,200,t(f,h));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(m,200,r());const v=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&&v)return void sendJson(m,200,{data:await f.listApprovals(v.status)});const g=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&&g){const e=await f.resolveApproval(g.id,g.status);return void sendJson(m,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,f){const m=await async function readJson(e){const r=[];for await(const t of e)r.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===r.length?{}:JSON.parse(Buffer.concat(r).toString("utf8"))}(r);if(m.model&&!o(m.model,e,f))return void sendJson(t,404,{error:{message:`model_not_found: ${m.model}`,type:"invalid_request_error"}});const h=a(m,e,f,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(m.stream)return void await async function streamChatCompletion(e,r,t,o){p(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(r,i(o.requestId,t.model));let n="";const a=e.subscribe(e=>{e.requestId===o.requestId&&(n+=u(r,e,o.requestId,t.model)??"")});try{const a=await e.request(o),i=c(n,a.output);i&&l(r,s(o.requestId,t.model,i)),l(r,d(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{a()}}(e,t,m,h);const v=await e.request(h);sendJson(t,200,n(m,v))}(f,e,m,h);sendJson(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readStringRecord(e){const r=readRecord(e);if(r)return Object.fromEntries(Object.entries(r).filter(e=>"string"==typeof e[1]))}function readConfigString(e){if("string"!=typeof e||!e.trim())return;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:e}function readHeaderString(e){const r=Array.isArray(e)?e[0]:e;return r&&r.trim()?r:void 0}function sendJson(e,r,t,o){e.writeHead(r,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(t))}
1
+ 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))}
@@ -47,6 +47,7 @@ export declare function createCapabilitiesResponse(): {
47
47
  endpoints: string[];
48
48
  streaming: boolean;
49
49
  toolProgressEvents: boolean;
50
+ runtimeTrace: boolean;
50
51
  };
51
52
  export declare function toChatCompletion(body: ChatCompletionRequest, result: RuntimeResponse): {
52
53
  id: string;
@@ -72,3 +73,17 @@ export declare function toChatCompletion(body: ChatCompletionRequest, result: Ru
72
73
  state: import("@stable-harness/core").RuntimeRecordState;
73
74
  };
74
75
  };
76
+ export declare function toRuntimeErrorResponse(result: RuntimeResponse): {
77
+ error: {
78
+ message: string;
79
+ type: string;
80
+ code: string;
81
+ param: null;
82
+ };
83
+ metadata: {
84
+ requestId: string;
85
+ sessionId: string;
86
+ agentId: string;
87
+ state: import("@stable-harness/core").RuntimeRecordState;
88
+ };
89
+ };
@@ -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"],streaming:!0,toolProgressEvents:!0}}export function toChatCompletion(e,t){return{id:t.requestId,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:e.model??t.agentId,choices:[{index:0,message:{role:"assistant",content:t.output},finish_reason:"stop"}],usage:estimateUsage(e.messages,t.output),metadata:{sessionId:t.sessionId,agentId:t.agentId,state:t.state}}}function normalizeOpenAiMessages(e){return Array.isArray(e)?e.map(e=>({role:e.role??"user",content:contentToText(e.content)})):[]}function contentToText(e){if("string"==typeof e)return e;if(!Array.isArray(e))throw new Error("message content must be a string or content part array");return e.map(contentPartToText).join("\n")}function contentPartToText(e){if("object"!=typeof e||null===e)throw new Error("message content parts must be objects");const t=e;if("text"===t.type&&"string"==typeof t.text)return t.text;const n=t.image_url;if("image_url"===t.type&&"string"==typeof n?.url)return`[image:${n.url}]`;throw new Error("unsupported_content_type")}function estimateUsage(e,t){const n=estimateTokens(Array.isArray(e)?e.map(e=>contentToText(e.content)).join("\n"):""),s=estimateTokens(t);return{prompt_tokens:n,completion_tokens:s,total_tokens:n+s}}function estimateTokens(e){return e.trim()?Math.ceil(1.3*e.trim().split(/\s+/u).length):0}
1
+ 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}
@@ -37,3 +37,4 @@ export declare function createStopChunk(id: string, model: string | undefined):
37
37
  export declare function writeRuntimeStreamEvent(response: ServerResponse, event: RuntimeEvent, id: string, model: string | undefined): string | undefined;
38
38
  export declare function writeSseHeaders(response: ServerResponse, headers: Record<string, string>): void;
39
39
  export declare function writeChatSse(response: ServerResponse, body: unknown): void;
40
+ export declare function writeErrorSse(response: ServerResponse, body: unknown): void;
@@ -1 +1 @@
1
- export function missingFinalDelta(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}export function createRoleChunk(e,t){return createChunk(e,t,{role:"assistant"},null)}export function createContentChunk(e,t,n){return createChunk(e,t,{content:n},null)}export function createStopChunk(e,t){return createChunk(e,t,{},"stop")}export function writeRuntimeStreamEvent(e,t,n,r){const o=function readAdapterDelta(e){if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"deepagents.message.coordinator_delta"!==t.eventType&&"agent.output.delta"!==t.phase||"string"!=typeof t.text?void 0:t.text}(t);if(o)return writeChatSse(e,createContentChunk(n,r,o)),o;!function writeProgressEvent(e,t){const n=function readToolProgress(e){if("runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n=function readToolProgressType(e){return"deepagents.tool_execution.start"===e.eventType?"deepagents.tool_execution.start":"deepagents.tool_execution.result"===e.eventType?"deepagents.tool_execution.result":"agent.tool.start"===e.phase||"agent.tool.result"===e.phase?e.phase:void 0}(t),r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&writeSse(e,"stable_harness.tool.progress",n)}(e,t),function writeNarrationEvent(e,t){"runtime.progress.narration"===t.type&&writeSse(e,"stable_harness.progress.narration",{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,message:t.message,provider:t.provider,source_event_types:t.sourceEventTypes,source_event_ids:t.sourceEventIds,model:t.model,style:t.style})}(e,t)}export function writeSseHeaders(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...t})}export function writeChatSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}function createChunk(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function writeSse(e,t,n){e.write(`event: ${t}\n`),e.write(`data: ${JSON.stringify(n)}\n\n`)}
1
+ export function missingFinalDelta(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}export function createRoleChunk(e,t){return createChunk(e,t,{role:"assistant"},null)}export function createContentChunk(e,t,n){return createChunk(e,t,{content:n},null)}export function createStopChunk(e,t){return createChunk(e,t,{},"stop")}export function writeRuntimeStreamEvent(e,t,n,r){const o=function readAdapterDelta(e){if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"deepagents.message.coordinator_delta"!==t.eventType&&"agent.output.delta"!==t.phase||"string"!=typeof t.text?void 0:t.text}(t);if(o)return writeChatSse(e,createContentChunk(n,r,o)),o;!function writeProgressEvent(e,t){const n=function readToolProgress(e){if("runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n=function readToolProgressType(e){return"deepagents.tool_execution.start"===e.eventType?"deepagents.tool_execution.start":"deepagents.tool_execution.result"===e.eventType?"deepagents.tool_execution.result":"agent.tool.start"===e.phase||"agent.tool.result"===e.phase?e.phase:void 0}(t),r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&writeSse(e,"stable_harness.tool.progress",n)}(e,t),function writeNarrationEvent(e,t){"runtime.progress.narration"===t.type&&writeSse(e,"stable_harness.progress.narration",{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,message:t.message,provider:t.provider,source_event_types:t.sourceEventTypes,source_event_ids:t.sourceEventIds,model:t.model,style:t.style})}(e,t)}export function writeSseHeaders(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...t})}export function writeChatSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}export function writeErrorSse(e,t){writeSse(e,"error",t)}function createChunk(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function writeSse(e,t,n){e.write(`event: ${t}\n`),e.write(`data: ${JSON.stringify(n)}\n\n`)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stable-harness",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "description": "Stable application runtime and operator control plane for agent workspaces.",
6
6
  "license": "Apache-2.0",
@@ -1 +1 @@
1
- export function resolveToolCallRecoveryPolicy(e){const o=e.workspace.runtime;if(!function shouldDefaultToolCallRecovery(e){if(0===e.agent.tools.length)return!1;const o=e.agent.modelRef?e.workspace.models.get(e.agent.modelRef):void 0;return"openai-compatible"===o?.provider||"openai-compatible"===readString(o?.config?.toolCallingMode)||"structured"===readString(o?.config?.toolCallingMode)}(e)||function hasExplicitToolCallRecovery(e){const o=readRecord(e.recovery);return"boolean"==typeof readRecord(o.toolCall).enabled}(o))return o;const r=readRecord(o.recovery),t=readRecord(r.toolCall);return{...o,recovery:{...r,toolCall:{...t,enabled:!0,defaultedForStructuredToolCalling:!0}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
1
+ export function resolveToolCallRecoveryPolicy(e){const o=e.workspace.runtime;if(!function shouldDefaultToolCallRecovery(e){if(0===e.agent.tools.length)return!1;const o=e.agent.modelRef?e.workspace.models.get(e.agent.modelRef):void 0;return"openai-compatible"===o?.provider||"openai-compatible"===readString(o?.config?.toolCallingMode)||"structured"===readString(o?.config?.toolCallingMode)||function hasDirectBackendModel(e){const o=readRecord(e.config);return void 0!==readRecord(o.deepagents).model}(e.agent)}(e)||function hasExplicitToolCallRecovery(e){const o=readRecord(e.recovery);return"boolean"==typeof readRecord(o.toolCall).enabled}(o))return o;const r=readRecord(o.recovery),t=readRecord(r.toolCall);return{...o,recovery:{...r,toolCall:{...t,enabled:!0,defaultedForStructuredToolCalling:!0}}}}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?{}:e}function readString(e){return"string"==typeof e&&e.trim()?e.trim():void 0}
@@ -1,5 +1,5 @@
1
1
  import { type IncomingMessage, type ServerResponse } from "node:http";
2
- import type { StableHarnessRuntime } from "@stable-harness/core";
2
+ import { type StableHarnessRuntime } from "@stable-harness/core";
3
3
  export type OpenAiCompatibleServerOptions = {
4
4
  bearerToken?: string;
5
5
  corsOrigins?: string[];
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{createCapabilitiesResponse as r,createModelsResponse as t,resolveAgentId as o,toChatCompletion as n,toRuntimeRequest as a}from"./openai-payload.js";import{createContentChunk as s,createRoleChunk as i,createStopChunk as d,missingFinalDelta as c,writeChatSse as l,writeRuntimeStreamEvent as u,writeSseHeaders as p}from"./openai-stream.js";export function createOpenAiCompatibleHttpServer(f,m={}){const h=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}(f,m);return e(async(e,m)=>{try{if(function applyCorsHeaders(e,r,t){const o=function allowedCorsOrigin(e,r){if(e)return r.corsOrigins?.includes("*")?"*":r.corsOrigins?.includes(e)||function isLoopbackOrigin(e){try{const r=new URL(e);return"http:"===r.protocol&&["localhost","127.0.0.1","[::1]"].includes(r.hostname)}catch{return!1}}(e)?e:void 0}(e.headers.origin,t);o&&(r.setHeader("access-control-allow-origin",o),r.setHeader("vary","origin"))}(e,m,h),function handleCors(e,r){return"OPTIONS"===e.method&&(r.writeHead(204,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),r.end(),!0)}(e,m))return;if(!function isAuthorized(e,r){return!r.bearerToken||e.headers.authorization===`Bearer ${r.bearerToken}`}(e,h))return void sendJson(m,401,{error:{message:"unauthorized",type:"invalid_request_error"}});if("GET"===e.method&&"/v1/models"===e.url)return void sendJson(m,200,t(f,h));if("GET"===e.method&&"/v1/capabilities"===e.url)return void sendJson(m,200,r());const v=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&&v)return void sendJson(m,200,{data:await f.listApprovals(v.status)});const g=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&&g){const e=await f.resolveApproval(g.id,g.status);return void sendJson(m,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,f){const m=await async function readJson(e){const r=[];for await(const t of e)r.push(Buffer.isBuffer(t)?t:Buffer.from(t));return 0===r.length?{}:JSON.parse(Buffer.concat(r).toString("utf8"))}(r);if(m.model&&!o(m.model,e,f))return void sendJson(t,404,{error:{message:`model_not_found: ${m.model}`,type:"invalid_request_error"}});const h=a(m,e,f,{sessionId:readHeaderString(r.headers["x-stable-harness-session-id"])});if(m.stream)return void await async function streamChatCompletion(e,r,t,o){p(r,{"access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),l(r,i(o.requestId,t.model));let n="";const a=e.subscribe(e=>{e.requestId===o.requestId&&(n+=u(r,e,o.requestId,t.model)??"")});try{const a=await e.request(o),i=c(n,a.output);i&&l(r,s(o.requestId,t.model,i)),l(r,d(o.requestId,t.model)),r.write("data: [DONE]\n\n"),r.end()}finally{a()}}(e,t,m,h);const v=await e.request(h);sendJson(t,200,n(m,v))}(f,e,m,h);sendJson(m,404,{error:{message:"not_found",type:"invalid_request_error"}})}catch(e){sendJson(m,400,{error:{message:e instanceof Error?e.message:String(e),type:"invalid_request_error"}})}})}function readRecord(e){return"object"!=typeof e||null===e||Array.isArray(e)?void 0:e}function readStringRecord(e){const r=readRecord(e);if(r)return Object.fromEntries(Object.entries(r).filter(e=>"string"==typeof e[1]))}function readConfigString(e){if("string"!=typeof e||!e.trim())return;const r=e.match(/^\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-(.*))?\}$/u);return r?process.env[r[1]]??r[2]:e}function readHeaderString(e){const r=Array.isArray(e)?e[0]:e;return r&&r.trim()?r:void 0}function sendJson(e,r,t,o){e.writeHead(r,{"content-type":"application/json","access-control-allow-headers":"authorization, content-type, idempotency-key","access-control-allow-methods":"GET, POST, OPTIONS"}),e.end(JSON.stringify(t))}
1
+ 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))}
@@ -47,6 +47,7 @@ export declare function createCapabilitiesResponse(): {
47
47
  endpoints: string[];
48
48
  streaming: boolean;
49
49
  toolProgressEvents: boolean;
50
+ runtimeTrace: boolean;
50
51
  };
51
52
  export declare function toChatCompletion(body: ChatCompletionRequest, result: RuntimeResponse): {
52
53
  id: string;
@@ -72,3 +73,17 @@ export declare function toChatCompletion(body: ChatCompletionRequest, result: Ru
72
73
  state: import("@stable-harness/core").RuntimeRecordState;
73
74
  };
74
75
  };
76
+ export declare function toRuntimeErrorResponse(result: RuntimeResponse): {
77
+ error: {
78
+ message: string;
79
+ type: string;
80
+ code: string;
81
+ param: null;
82
+ };
83
+ metadata: {
84
+ requestId: string;
85
+ sessionId: string;
86
+ agentId: string;
87
+ state: import("@stable-harness/core").RuntimeRecordState;
88
+ };
89
+ };
@@ -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"],streaming:!0,toolProgressEvents:!0}}export function toChatCompletion(e,t){return{id:t.requestId,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:e.model??t.agentId,choices:[{index:0,message:{role:"assistant",content:t.output},finish_reason:"stop"}],usage:estimateUsage(e.messages,t.output),metadata:{sessionId:t.sessionId,agentId:t.agentId,state:t.state}}}function normalizeOpenAiMessages(e){return Array.isArray(e)?e.map(e=>({role:e.role??"user",content:contentToText(e.content)})):[]}function contentToText(e){if("string"==typeof e)return e;if(!Array.isArray(e))throw new Error("message content must be a string or content part array");return e.map(contentPartToText).join("\n")}function contentPartToText(e){if("object"!=typeof e||null===e)throw new Error("message content parts must be objects");const t=e;if("text"===t.type&&"string"==typeof t.text)return t.text;const n=t.image_url;if("image_url"===t.type&&"string"==typeof n?.url)return`[image:${n.url}]`;throw new Error("unsupported_content_type")}function estimateUsage(e,t){const n=estimateTokens(Array.isArray(e)?e.map(e=>contentToText(e.content)).join("\n"):""),s=estimateTokens(t);return{prompt_tokens:n,completion_tokens:s,total_tokens:n+s}}function estimateTokens(e){return e.trim()?Math.ceil(1.3*e.trim().split(/\s+/u).length):0}
1
+ 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}
@@ -37,3 +37,4 @@ export declare function createStopChunk(id: string, model: string | undefined):
37
37
  export declare function writeRuntimeStreamEvent(response: ServerResponse, event: RuntimeEvent, id: string, model: string | undefined): string | undefined;
38
38
  export declare function writeSseHeaders(response: ServerResponse, headers: Record<string, string>): void;
39
39
  export declare function writeChatSse(response: ServerResponse, body: unknown): void;
40
+ export declare function writeErrorSse(response: ServerResponse, body: unknown): void;
@@ -1 +1 @@
1
- export function missingFinalDelta(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}export function createRoleChunk(e,t){return createChunk(e,t,{role:"assistant"},null)}export function createContentChunk(e,t,n){return createChunk(e,t,{content:n},null)}export function createStopChunk(e,t){return createChunk(e,t,{},"stop")}export function writeRuntimeStreamEvent(e,t,n,r){const o=function readAdapterDelta(e){if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"deepagents.message.coordinator_delta"!==t.eventType&&"agent.output.delta"!==t.phase||"string"!=typeof t.text?void 0:t.text}(t);if(o)return writeChatSse(e,createContentChunk(n,r,o)),o;!function writeProgressEvent(e,t){const n=function readToolProgress(e){if("runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n=function readToolProgressType(e){return"deepagents.tool_execution.start"===e.eventType?"deepagents.tool_execution.start":"deepagents.tool_execution.result"===e.eventType?"deepagents.tool_execution.result":"agent.tool.start"===e.phase||"agent.tool.result"===e.phase?e.phase:void 0}(t),r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&writeSse(e,"stable_harness.tool.progress",n)}(e,t),function writeNarrationEvent(e,t){"runtime.progress.narration"===t.type&&writeSse(e,"stable_harness.progress.narration",{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,message:t.message,provider:t.provider,source_event_types:t.sourceEventTypes,source_event_ids:t.sourceEventIds,model:t.model,style:t.style})}(e,t)}export function writeSseHeaders(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...t})}export function writeChatSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}function createChunk(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function writeSse(e,t,n){e.write(`event: ${t}\n`),e.write(`data: ${JSON.stringify(n)}\n\n`)}
1
+ export function missingFinalDelta(e,t){return e?!t||e.includes(t)?"":t.startsWith(e)?t.slice(e.length):t:t}export function createRoleChunk(e,t){return createChunk(e,t,{role:"assistant"},null)}export function createContentChunk(e,t,n){return createChunk(e,t,{content:n},null)}export function createStopChunk(e,t){return createChunk(e,t,{},"stop")}export function writeRuntimeStreamEvent(e,t,n,r){const o=function readAdapterDelta(e){if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event;return"deepagents.message.coordinator_delta"!==t.eventType&&"agent.output.delta"!==t.phase||"string"!=typeof t.text?void 0:t.text}(t);if(o)return writeChatSse(e,createContentChunk(n,r,o)),o;!function writeProgressEvent(e,t){const n=function readToolProgress(e){if("runtime.tool.direct.started"===e.type||"runtime.tool.direct.completed"===e.type)return{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:e.type,tool_id:e.toolId};if("runtime.adapter.event"!==e.type||"object"!=typeof e.event||null===e.event)return;const t=e.event,n=function readToolProgressType(e){return"deepagents.tool_execution.start"===e.eventType?"deepagents.tool_execution.start":"deepagents.tool_execution.result"===e.eventType?"deepagents.tool_execution.result":"agent.tool.start"===e.phase||"agent.tool.result"===e.phase?e.phase:void 0}(t),r="string"==typeof t.toolId?t.toolId:void 0;return n&&r?{request_id:e.requestId,session_id:e.sessionId,agent_id:e.agentId,type:n,tool_id:r,adapter:t.adapter}:void 0}(t);n&&writeSse(e,"stable_harness.tool.progress",n)}(e,t),function writeNarrationEvent(e,t){"runtime.progress.narration"===t.type&&writeSse(e,"stable_harness.progress.narration",{request_id:t.requestId,session_id:t.sessionId,agent_id:t.agentId,type:t.type,message:t.message,provider:t.provider,source_event_types:t.sourceEventTypes,source_event_ids:t.sourceEventIds,model:t.model,style:t.style})}(e,t)}export function writeSseHeaders(e,t){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive",...t})}export function writeChatSse(e,t){e.write(`data: ${JSON.stringify(t)}\n\n`)}export function writeErrorSse(e,t){writeSse(e,"error",t)}function createChunk(e,t,n,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:t??"stable-harness",choices:[{index:0,delta:n,finish_reason:r}]}}function writeSse(e,t,n){e.write(`event: ${t}\n`),e.write(`data: ${JSON.stringify(n)}\n\n`)}