@urun-sh/openai 0.2.53 → 0.2.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
2
  import { Api, Model, Context, SimpleStreamOptions, AssistantMessageEventStream } from '@earendil-works/pi-ai';
3
- import { a as UrunSessionLike, U as UrunResponses } from '../ResponsesClient-CQ3l8Wft.cjs';
3
+ import { a as UrunSessionLike, U as UrunResponses } from '../ResponsesClient-DrccxHTc.cjs';
4
4
 
5
5
  /**
6
6
  * Pi coding-agent extension: register the `urun` model provider, with one pi
@@ -40,22 +40,26 @@ import { a as UrunSessionLike, U as UrunResponses } from '../ResponsesClient-CQ3
40
40
  * empty provider.
41
41
  * 2. `streamSimple` (pi's custom-API hook, keyed by `api: "urun-serve"`):
42
42
  * opens ONE uRun session per app slug, POOLED and reused across turns,
43
- * translates pi's Context into Responses `input` items, drives
44
- * `UrunResponses.responses.create({stream: true})`, and maps
45
- * `response.output_text.delta` events into pi `text_delta` events.
46
- * Text-only, matching the precedent extension's fidelity (no tool-call
47
- * streaming through the serve lane; pi's toolResult turns are folded
48
- * into user-role input items the Responses `function_call_output` item
49
- * form needs call ids this lane never emits).
43
+ * translates pi's Context into Responses `input` items (tool loop
44
+ * included: assistant `toolCall` content → `function_call` items,
45
+ * `toolResult` turns `function_call_output` items), forwards
46
+ * `context.tools` upstream, drives
47
+ * `UrunResponses.responses.create({stream: true})`, and maps the lane's
48
+ * decoded events onto pi-ai's REAL AssistantMessageEvent union:
49
+ * output_text text_*, reasoning_text thinking_*, function-call
50
+ * argument fragments → toolcall_* (stopReason "toolUse" ends a
51
+ * tool-calling turn, which is what makes pi's agent loop execute the
52
+ * tools and feed results back).
50
53
  * 3. ABORT/CANCEL (loud contract): the Responses lane has NO request-cancel
51
54
  * primitive (`SdkTransport.sendResponses` writes an envelope and the
52
55
  * decode loop just reads the reply lane), so Ctrl-C / the stall watchdog
53
- * stop consuming AND evict+close the pooled session tearing down the
54
- * WebRTC transport is what stops server-side generation. A clean turn
55
- * keeps its session for the next turn; a failed/aborted/stalled turn
56
- * always reopens.
57
- * 4. cleanup: pooled sessions are released (`session.close()`) on pi's
58
- * `session_shutdown` lifecycle event.
56
+ * stop consuming AND evict+END the pooled session (core's own
57
+ * `Session.end()`) — tearing down the WebRTC transport is what stops
58
+ * server-side generation. A clean turn keeps its session for the next
59
+ * turn; a failed/aborted/stalled turn always reopens — and a turn whose
60
+ * session DIED re-homes once and replays (see REHOME_ATTEMPTS).
61
+ * 4. cleanup: pooled sessions are released (core's `Session.end()`) on
62
+ * pi's `session_shutdown` lifecycle event.
59
63
  *
60
64
  * RUNTIME DEPENDENCY STORY (owner requirement — stated honestly): token
61
65
  * streams are named-DATA session streams, i.e. SCTP data channels over
@@ -115,7 +119,24 @@ declare function resolveSessionEnv(env: NodeJS.ProcessEnv): {
115
119
  * `@urun-sh/core`.
116
120
  */
117
121
  interface SessionFactory {
118
- (env: NodeJS.ProcessEnv, appSlug: string, fnName: string): Promise<UrunSessionLike> | UrunSessionLike;
122
+ (env: NodeJS.ProcessEnv, appSlug: string, fnName: string): Promise<OwnedSession> | OwnedSession;
123
+ }
124
+ /**
125
+ * A session this lane OWNS: core's transport surface plus core's own terminal
126
+ * release, `Session.end()` (the same requirement the compat proxy asserts in
127
+ * `sessionOf`). A session without it could never be released — it would leak
128
+ * the backend session and its capacity lease until the platform's abandonment
129
+ * backstop reclaimed it — so the factory refuses to hand one back.
130
+ */
131
+ type OwnedSession = UrunSessionLike & {
132
+ end: () => Promise<unknown>;
133
+ };
134
+ /** The structured diagnostic shape core's transports emit (typed locally). */
135
+ interface CoreDiagnosticLike {
136
+ level: string;
137
+ kind: string;
138
+ message: string;
139
+ detail?: Record<string, unknown>;
119
140
  }
120
141
  /**
121
142
  * The subset of `@urun-sh/core` a session factory needs — typed locally so
@@ -128,6 +149,7 @@ interface CoreModuleLike {
128
149
  orgId: string;
129
150
  jwt?: string;
130
151
  getAccessToken?: () => Promise<string>;
152
+ diagnosticSink?: (diagnostic: CoreDiagnosticLike) => void;
131
153
  }) => Record<string, (args?: Record<string, unknown>) => UrunSessionLike>;
132
154
  createClientToken: (apiKey: string, opts: {
133
155
  baseUrl?: string;
@@ -149,10 +171,20 @@ interface CoreModuleLike {
149
171
  * self-contained bundle INLINES core (and, through core's own dynamic
150
172
  * werift import, the WebRTC backend) — that lane has no node_modules.
151
173
  */
174
+ /**
175
+ * A diagnostic sink appending formatted lines to `path` — SYNCHRONOUS writes
176
+ * (a crashing pi must still leave the lines on disk), lazy directory
177
+ * creation, one open descriptor per sink. An open/write failure THROWS: core's
178
+ * `_emitDiagnostic` catches it, complains once on the console, and keeps the
179
+ * structured event flowing — loud once, never TUI spam, never silent.
180
+ */
181
+ declare function createFileDiagnosticSink(path: string): (diagnostic: CoreDiagnosticLike) => void;
182
+ /** Where the pi lane's quiet diagnostics land (the compat log convention). */
183
+ declare function piDiagnosticLogPath(home?: string): string;
152
184
  declare function makeSessionFactory(loadCore: () => Promise<CoreModuleLike> | CoreModuleLike): SessionFactory;
153
185
  /** One pooled backhaul: the session plus its Responses client (cli.ts shape). */
154
186
  interface PoolEntry {
155
- session: UrunSessionLike;
187
+ session: OwnedSession;
156
188
  responses: UrunResponses;
157
189
  }
158
190
  /**
@@ -174,22 +206,36 @@ declare class SessionPool {
174
206
  /** Release everything (pi session_shutdown). */
175
207
  closeAll(): void;
176
208
  }
177
- /** A Responses input item (message form; content stays a plain string). */
178
- interface ResponsesInputItem {
209
+ /** A Responses input item: role message or function-call round-trip item. */
210
+ type ResponsesInputItem = {
179
211
  role: 'system' | 'user' | 'assistant';
180
212
  content: string;
181
- }
213
+ } | {
214
+ type: 'function_call';
215
+ call_id: string;
216
+ name: string;
217
+ arguments: string;
218
+ } | {
219
+ type: 'function_call_output';
220
+ call_id: string;
221
+ output: string;
222
+ };
182
223
  /**
183
- * Pi `Context` -> Responses `input` items. Pi models tool results as
184
- * `role:"toolResult"`; the Responses item form for those
185
- * (`{type:'function_call_output', call_id, …}`) needs call ids this text-only
186
- * lane never emits, so tool results are folded into `user`-role items, then
187
- * any consecutive same-role runs are coalesced into one item so a chat
188
- * template that assumes strict role alternation never sees two turns of the
189
- * same role back-to-back (same rationale as the precedent extension).
224
+ * Pi `Context` -> Responses `input` items, in ENCOUNTER ORDER (the tool
225
+ * loop's canonical Responses shapes the same translation the compat proxy
226
+ * performs):
227
+ * - assistant `toolCall` content `{type:'function_call', call_id, name,
228
+ * arguments}` (arguments re-serialized: pi stores the PARSED object);
229
+ * - `role:"toolResult"` turns `{type:'function_call_output', call_id,
230
+ * output}` keyed by pi's `toolCallId`;
231
+ * - assistant `thinking` content is the producing model's internal state —
232
+ * not re-submittable — and is skipped by design (same rule as the proxy);
233
+ * - text runs become role messages where they sit; adjacent same-role
234
+ * messages are coalesced so chat templates that assume strict role
235
+ * alternation never see two turns of one role back-to-back.
190
236
  */
191
237
  declare function toResponsesInput(context: Context): ResponsesInputItem[];
192
- /** Fold consecutive same-role items into one, joining with a blank line. */
238
+ /** Fold consecutive same-role message items into one, joining with a blank line. */
193
239
  declare function coalesceSameRole(items: ResponsesInputItem[]): ResponsesInputItem[];
194
240
  /** Tunable timeouts (overridable so tests can drive them deterministically). */
195
241
  interface StreamTiming {
@@ -219,4 +265,4 @@ declare function createUrunExtension(opts?: UrunExtensionOptions): (pi: Extensio
219
265
  /** The pi extension factory (default export — what pi's loader invokes). */
220
266
  declare const _default: (pi: ExtensionAPI) => Promise<void>;
221
267
 
222
- export { type CoreModuleLike, type ResponsesInputItem, type SessionFactory, SessionPool, type StreamTiming, URUN_API, type UrunExtensionOptions, coalesceSameRole, createUrunExtension, _default as default, makeSessionFactory, makeStreamSimple, resolveSessionEnv, toResponsesInput };
268
+ export { type CoreDiagnosticLike, type CoreModuleLike, type OwnedSession, type ResponsesInputItem, type SessionFactory, SessionPool, type StreamTiming, URUN_API, type UrunExtensionOptions, coalesceSameRole, createFileDiagnosticSink, createUrunExtension, _default as default, makeSessionFactory, makeStreamSimple, piDiagnosticLogPath, resolveSessionEnv, toResponsesInput };
@@ -1,6 +1,6 @@
1
1
  import { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
2
  import { Api, Model, Context, SimpleStreamOptions, AssistantMessageEventStream } from '@earendil-works/pi-ai';
3
- import { a as UrunSessionLike, U as UrunResponses } from '../ResponsesClient-CQ3l8Wft.js';
3
+ import { a as UrunSessionLike, U as UrunResponses } from '../ResponsesClient-DrccxHTc.js';
4
4
 
5
5
  declare const URUN_API: Api;
6
6
 
@@ -18,7 +18,18 @@ declare function resolveSessionEnv(env: NodeJS.ProcessEnv): {
18
18
  };
19
19
 
20
20
  interface SessionFactory {
21
- (env: NodeJS.ProcessEnv, appSlug: string, fnName: string): Promise<UrunSessionLike> | UrunSessionLike;
21
+ (env: NodeJS.ProcessEnv, appSlug: string, fnName: string): Promise<OwnedSession> | OwnedSession;
22
+ }
23
+
24
+ type OwnedSession = UrunSessionLike & {
25
+ end: () => Promise<unknown>;
26
+ };
27
+
28
+ interface CoreDiagnosticLike {
29
+ level: string;
30
+ kind: string;
31
+ message: string;
32
+ detail?: Record<string, unknown>;
22
33
  }
23
34
 
24
35
  interface CoreModuleLike {
@@ -27,6 +38,7 @@ interface CoreModuleLike {
27
38
  orgId: string;
28
39
  jwt?: string;
29
40
  getAccessToken?: () => Promise<string>;
41
+ diagnosticSink?: (diagnostic: CoreDiagnosticLike) => void;
30
42
  }) => Record<string, (args?: Record<string, unknown>) => UrunSessionLike>;
31
43
  createClientToken: (apiKey: string, opts: {
32
44
  baseUrl?: string;
@@ -37,10 +49,13 @@ interface CoreModuleLike {
37
49
  }>;
38
50
  }
39
51
 
52
+ declare function createFileDiagnosticSink(path: string): (diagnostic: CoreDiagnosticLike) => void;
53
+
54
+ declare function piDiagnosticLogPath(home?: string): string;
40
55
  declare function makeSessionFactory(loadCore: () => Promise<CoreModuleLike> | CoreModuleLike): SessionFactory;
41
56
 
42
57
  interface PoolEntry {
43
- session: UrunSessionLike;
58
+ session: OwnedSession;
44
59
  responses: UrunResponses;
45
60
  }
46
61
 
@@ -57,10 +72,19 @@ declare class SessionPool {
57
72
  closeAll(): void;
58
73
  }
59
74
 
60
- interface ResponsesInputItem {
75
+ type ResponsesInputItem = {
61
76
  role: 'system' | 'user' | 'assistant';
62
77
  content: string;
63
- }
78
+ } | {
79
+ type: 'function_call';
80
+ call_id: string;
81
+ name: string;
82
+ arguments: string;
83
+ } | {
84
+ type: 'function_call_output';
85
+ call_id: string;
86
+ output: string;
87
+ };
64
88
 
65
89
  declare function toResponsesInput(context: Context): ResponsesInputItem[];
66
90
 
@@ -85,4 +109,4 @@ declare function createUrunExtension(opts?: UrunExtensionOptions): (pi: Extensio
85
109
 
86
110
  declare const _default: (pi: ExtensionAPI) => Promise<void>;
87
111
 
88
- export { type CoreModuleLike, type ResponsesInputItem, type SessionFactory, SessionPool, type StreamTiming, URUN_API, type UrunExtensionOptions, coalesceSameRole, createUrunExtension, _default as default, makeSessionFactory, makeStreamSimple, resolveSessionEnv, toResponsesInput };
112
+ export { type CoreDiagnosticLike, type CoreModuleLike, type OwnedSession, type ResponsesInputItem, type SessionFactory, SessionPool, type StreamTiming, URUN_API, type UrunExtensionOptions, coalesceSameRole, createFileDiagnosticSink, createUrunExtension, _default as default, makeSessionFactory, makeStreamSimple, piDiagnosticLogPath, resolveSessionEnv, toResponsesInput };
@@ -1,3 +1,4 @@
1
- import{c as C}from"../chunk-FSS2A7IU.js";import{a as A,c as D}from"../chunk-PS4BLX7J.js";import{createAssistantMessageEventStream as J}from"@earendil-works/pi-ai";async function F(t){let e=await D(t),n=e.filter(o=>o.function_name===t.fnName&&o.deployment_status==="active").map(o=>o.app_slug);if(n.length===0)throw new Error(`urun pi extension: the org has no active deployed app exposing "${t.fnName}" (GET ${t.apiUrl}/apps returned ${e.length} app(s), none servable) \u2014 deploy one with \`urun serve <model>\` first`);return n.sort()}var O="urun-serve",G="serve",H=131072,Y=16384,$=3,B=42e4,X=6e4,z=15e3;function Q(t){let e=(t.URUN_API_KEY??"").trim();if(!e)throw new Error("urun pi extension: URUN_API_KEY is required \u2014 model discovery lists the org's deployed serve apps via the org API (URUN_JWT alone cannot list apps)");return{apiKey:e,apiUrl:(t.URUN_API_URL??"").trim()||A,fnName:(t.URUN_FUNCTION??"").trim()||G}}function V(t){let e=(t.URUN_BASE_URL??"").trim(),n=(t.URUN_ORG_ID??"").trim();if(!e)throw new Error("urun pi extension: URUN_BASE_URL is required to open a session");if(/\/v1\/*$/.test(new URL(e).pathname))throw new Error(`urun pi extension: URUN_BASE_URL must be the session-gateway base (e.g. https://api.urun.sh), got ${e} \u2014 a trailing /v1 is the org API form (URUN_API_URL); session allocation 404s against it. Drop the /v1.`);if(!n)throw new Error("urun pi extension: URUN_ORG_ID is required to open a session");let o=(t.URUN_JWT??"").trim();if(o)return{baseUrl:e,orgId:n,auth:{lane:"jwt",jwt:o}};let i=(t.URUN_API_KEY??"").trim();if(!i)throw new Error("urun pi extension: URUN_JWT or URUN_API_KEY is required to open a session");let a=(t.URUN_GATEWAY_URL??"").trim()||void 0;return{baseUrl:e,orgId:n,auth:{lane:"api-key",apiKey:i,gatewayUrl:a}}}function Z(t){return async(e,n,o)=>{let{baseUrl:i,orgId:a,auth:s}=V(e),{App:u,createClientToken:l}=await t(),r=(s.lane==="jwt"?u(n,{baseUrl:i,orgId:a,jwt:s.jwt}):u(n,{baseUrl:i,orgId:a,getAccessToken:async()=>(await l(s.apiKey,{baseUrl:s.gatewayUrl,expiresIn:300,allowedFunctions:[`${n}/${o}`]})).token}))[o];if(typeof r!="function")throw new Error(`urun pi extension: app "${n}" has no function "${o}"`);let d=r(),p=d.connect;return typeof p=="function"&&await p.call(d),d}}var ee=Z(async()=>{let{createRequire:t}=await import("module"),e=t(import.meta.url);if(typeof globalThis.RTCPeerConnection>"u")try{e.resolve("werift")}catch{throw new Error('urun pi extension: token streams require a WebRTC data channel, and this Node runtime has no RTCPeerConnection and no "werift" backend installed (@urun-sh/core\'s optional Node WebRTC backend \u2014 it was probably stripped by a --no-optional install). Install werift (`npm i werift`) and retry.')}return e("@urun-sh/core")});function te(t){try{Promise.resolve(t.close?.()).catch(()=>{})}catch{}}var x=class{constructor(e,n,o){this.open=e;this.env=n;this.fnName=o}open;env;fnName;pool=new Map;acquire(e){let n=this.pool.get(e);if(!n){let o=Promise.resolve(this.open(this.env,e,this.fnName)).then(i=>({session:i,responses:new C(i)}));n=o,this.pool.set(e,o),o.catch(()=>{this.pool.get(e)===o&&this.pool.delete(e)})}return n}evict(e){let n=this.pool.get(e);n&&(this.pool.delete(e),n.then(o=>te(o.session),()=>{}))}closeAll(){for(let e of[...this.pool.keys()])this.evict(e)}};function ne(t){let e=[];t.systemPrompt&&e.push({role:"system",content:t.systemPrompt});for(let n of t.messages){let o=n.role==="assistant"?"assistant":"user";e.push({role:o,content:re(n.content)})}return oe(e)}function oe(t){let e=[];for(let n of t){let o=e[e.length-1];o&&o.role===n.role?o.content=[o.content,n.content].filter(i=>i.length>0).join(`
1
+ import{c as G}from"../chunk-HQVV5UN5.js";import{a as F,c as H}from"../chunk-5CEMLB6H.js";import{createAssistantMessageEventStream as oe}from"@earendil-works/pi-ai";import{mkdirSync as re,openSync as se,writeSync as ie}from"fs";import ae from"os";import{dirname as le,join as ce}from"path";async function Y(t){let e=await H(t),n=e.filter(o=>o.function_name===t.fnName&&o.deployment_status==="active").map(o=>o.app_slug);if(n.length===0)throw new Error(`urun pi extension: the org has no active deployed app exposing "${t.fnName}" (GET ${t.apiUrl}/apps returned ${e.length} app(s), none servable) \u2014 deploy one with \`urun serve <model>\` first`);return n.sort()}var V="urun-serve",ue="serve",pe=131072,de=16384,B=3,fe=42e4,ge=6e4,X=2,me=15e3;function ye(t){let e=(t.URUN_API_KEY??"").trim();if(!e)throw new Error("urun pi extension: URUN_API_KEY is required \u2014 model discovery lists the org's deployed serve apps via the org API (URUN_JWT alone cannot list apps)");return{apiKey:e,apiUrl:(t.URUN_API_URL??"").trim()||F,fnName:(t.URUN_FUNCTION??"").trim()||ue}}function he(t){let e=(t.URUN_BASE_URL??"").trim(),n=(t.URUN_ORG_ID??"").trim();if(!e)throw new Error("urun pi extension: URUN_BASE_URL is required to open a session");if(/\/v1\/*$/.test(new URL(e).pathname))throw new Error(`urun pi extension: URUN_BASE_URL must be the session-gateway base (e.g. https://api.urun.sh), got ${e} \u2014 a trailing /v1 is the org API form (URUN_API_URL); session allocation 404s against it. Drop the /v1.`);if(!n)throw new Error("urun pi extension: URUN_ORG_ID is required to open a session");let o=(t.URUN_JWT??"").trim();if(o)return{baseUrl:e,orgId:n,auth:{lane:"jwt",jwt:o}};let i=(t.URUN_API_KEY??"").trim();if(!i)throw new Error("urun pi extension: URUN_JWT or URUN_API_KEY is required to open a session");let u=(t.URUN_GATEWAY_URL??"").trim()||void 0;return{baseUrl:e,orgId:n,auth:{lane:"api-key",apiKey:i,gatewayUrl:u}}}function we(t){let e=null;return n=>{e===null&&(re(le(t),{recursive:!0}),e=se(t,"a"));let o=n.detail?` ${JSON.stringify(n.detail)}`:"";ie(e,`${new Date().toISOString()} [${n.level}] [urun] ${n.message}${o}
2
+ `)}}function _e(t=ae.homedir()){return ce(t,".urun","logs","pi-extension.log")}function xe(t){let e=we(_e());return async(n,o,i)=>{let{baseUrl:u,orgId:s,auth:d}=he(n),{App:x,createClientToken:a}=await t(),w=(d.lane==="jwt"?x(o,{baseUrl:u,orgId:s,jwt:d.jwt,diagnosticSink:e}):x(o,{baseUrl:u,orgId:s,diagnosticSink:e,getAccessToken:async()=>(await a(d.apiKey,{baseUrl:d.gatewayUrl,expiresIn:300,allowedFunctions:[`${o}/${i}`]})).token}))[i];if(typeof w!="function")throw new Error(`urun pi extension: app "${o}" has no function "${i}"`);let m=w();if(typeof m.end!="function")throw new Error(`urun pi extension: app "${o}" function "${i}" returned a session without end()`);let E=m.connect;return typeof E=="function"&&await E.call(m),m}}var ve=xe(async()=>{let{createRequire:t}=await import("module"),e=t(import.meta.url);if(typeof globalThis.RTCPeerConnection>"u")try{e.resolve("werift")}catch{throw new Error('urun pi extension: token streams require a WebRTC data channel, and this Node runtime has no RTCPeerConnection and no "werift" backend installed (@urun-sh/core\'s optional Node WebRTC backend \u2014 it was probably stripped by a --no-optional install). Install werift (`npm i werift`) and retry.')}return e("@urun-sh/core")});function ke(t){try{Promise.resolve(t.end()).catch(()=>{})}catch{}}var K=class{constructor(e,n,o){this.open=e;this.env=n;this.fnName=o}open;env;fnName;pool=new Map;acquire(e){let n=this.pool.get(e);if(!n){let o=Promise.resolve(this.open(this.env,e,this.fnName)).then(i=>({session:i,responses:new G(i)}));n=o,this.pool.set(e,o),o.catch(()=>{this.pool.get(e)===o&&this.pool.delete(e)})}return n}evict(e){let n=this.pool.get(e);n&&(this.pool.delete(e),n.then(o=>ke(o.session),()=>{}))}closeAll(){for(let e of[...this.pool.keys()])this.evict(e)}};function Re(t){let e=[];t.systemPrompt&&e.push({role:"system",content:t.systemPrompt});for(let n of t.messages){if(n.role==="toolResult"){e.push({type:"function_call_output",call_id:n.toolCallId,output:Ae(n.content)});continue}if(n.role==="assistant"&&Array.isArray(n.content)){let i="",u=()=>{i&&(e.push({role:"assistant",content:i}),i="")};for(let s of n.content)if(s.type==="text")i+=String(s.text??"");else if(s.type==="toolCall")u(),e.push({type:"function_call",call_id:String(s.id??""),name:String(s.name??""),arguments:JSON.stringify(s.arguments??{})});else{if(s.type==="thinking")continue;throw new Error(`urun pi extension: unsupported assistant content part "${String(s.type)}"`)}u();continue}let o=n.role==="assistant"?"assistant":"user";e.push({role:o,content:be(n.content)})}return Te(e)}function Te(t){let e=[];for(let n of t){let o=e[e.length-1];o&&"role"in o&&"role"in n&&o.role===n.role?o.content=[o.content,n.content].filter(i=>i.length>0).join(`
2
3
 
3
- `):e.push({...n})}return e}function re(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>e&&typeof e=="object"&&"text"in e?String(e.text):"").join(""):""}function se(t){let e=t instanceof Error?t.message:String(t),n=t?.code;return/\b529\b/.test(e)||/overloaded/i.test(e)||n===529||n==="529"}function ie(t){return new Promise(e=>setTimeout(e,t))}function ae(t,e,n,o){return new Promise((i,a)=>{let s=!1,u=r=>{s||(s=!0,clearTimeout(l),o?.removeEventListener("abort",c),r())},l=setTimeout(()=>u(()=>a(n())),e),c=()=>u(()=>a(new Error("aborted")));if(o?.aborted){c();return}o?.addEventListener("abort",c,{once:!0}),Promise.resolve(t).then(r=>u(()=>i(r)),r=>u(()=>a(r)))})}async function*ce(t,e){let n=t[Symbol.asyncIterator](),o,i=new Promise((a,s)=>{o=()=>s(new Error("aborted")),e.addEventListener("abort",o,{once:!0})});try{for(;;){if(e.aborted)throw new Error("aborted");let a=await Promise.race([n.next(),i]);if(a.done)return;yield a.value}}finally{o&&e.removeEventListener("abort",o),n.return?.(void 0)}}function ue(t,e){let n=t?.sendMessage;if(typeof n=="function")try{n.call(t,{customType:"urun-status",content:e,display:!0},{triggerTurn:!1})}catch{}}function pe(t,e={},n){let o=e.connectDeadlineMs??B,i=e.stallTimeoutMs??X,a=e.phaseHeartbeatMs??z;return(s,u,l)=>{let c=J(),r={role:"assistant",content:[{type:"text",text:""}],api:s.api,provider:s.provider,model:s.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()},d=r.content[0],p=new AbortController,m=l?.signal,P=!1,b=()=>{P=!0,p.abort()};m&&(m.aborted?b():m.addEventListener("abort",b,{once:!0}));let E=!1,y,I=()=>{y&&clearTimeout(y),y=setTimeout(()=>{E=!0,p.abort()},i)},_=()=>{y&&clearTimeout(y),y=void 0};return(async()=>{c.push({type:"start",partial:r}),c.push({type:"text_start",contentIndex:0,partial:r});let k=!1,v,S=()=>{v&&clearInterval(v),v=void 0},K=Date.now();v=setInterval(()=>{let f=Math.round((Date.now()-K)/1e3);ue(n,`uRun: still opening the "${s.id}" session\u2026 (${f}s \u2014 serve apps scale to zero, first turn can cold-start ~6min)`)},a);try{let{responses:f}=await ae(t.acquire(s.id),o,()=>new Error(`uRun session for "${s.id}" did not connect within ${Math.round(o/1e3)}s \u2014 likely cold-starting (scaled to zero) or queued behind capacity. Retry shortly.`),p.signal);S();let T=ne(u),N,U;for(let R=1;R<=$;R++)try{let h=await f.responses.create({model:s.id,input:T,stream:!0,max_output_tokens:s.maxTokens});I();for await(let W of ce(h,p.signal)){I();let g=W;if(g.type==="response.output_text.delta"){let w=typeof g.delta=="string"?g.delta:"";w&&(d.text+=w,c.push({type:"text_delta",contentIndex:0,delta:w,partial:r}))}else if(g.type==="response.completed"){N=g.response;break}else if(g.type==="error"){let w=g.error,L=new Error(`uRun serve error: ${w?.message??"unknown error"}`);throw L.code=w?.code??null,L}}U=void 0;break}catch(h){if(U=h,p.signal.aborted)throw h;if(R<$&&se(h)&&d.text.length===0){await ie(R*500);continue}throw h}if(U)throw U;_(),c.push({type:"text_end",contentIndex:0,content:d.text,partial:r});let M=N?.status==="incomplete"?"length":"stop";r.stopReason=M,c.push({type:"done",reason:M,message:r}),c.end(r)}catch(f){if(k=!0,_(),P||m?.aborted&&!E)r.stopReason="aborted",r.errorMessage="aborted by user",c.push({type:"error",reason:"aborted",error:r});else{let T=E?`uRun stream stalled \u2014 no output for ${Math.round(i/1e3)}s; the model may be hung. Retry, or Ctrl-C to abort.`:f instanceof Error?f.message:String(f);r.stopReason="error",r.errorMessage=T,c.push({type:"error",reason:"error",error:r})}c.end(r)}finally{_(),S(),m&&m.removeEventListener("abort",b),k&&t.evict(s.id)}})(),c}}function le(t){return{id:t,name:`uRun ${t}`,api:O,reasoning:!1,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:H,maxTokens:Y}}function de(t={}){return async e=>{let n=t.env??process.env,{apiKey:o,apiUrl:i,fnName:a}=Q(n),s=await F({apiUrl:i,apiKey:o,fnName:a,fetchImpl:t.fetchImpl}),u=new x(t.openSession??ee,n,a);e.on("session_shutdown",()=>u.closeAll());let l={name:"uRun",api:O,baseUrl:"urun://session",apiKey:"$URUN_API_KEY",models:s.map(le),streamSimple:pe(u,t.timing??{},e)};e.registerProvider("urun",l)}}var Ue=de();export{x as SessionPool,O as URUN_API,oe as coalesceSameRole,de as createUrunExtension,Ue as default,Z as makeSessionFactory,pe as makeStreamSimple,V as resolveSessionEnv,ne as toResponsesInput};
4
+ `):e.push({...n})}return e}function be(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>e&&typeof e=="object"&&"text"in e?String(e.text):"").join(""):""}function Ae(t){if(typeof t=="string")return t;if(!Array.isArray(t))return"";let e="";for(let n of t){let o=String(n.type??"");if(o!=="text")throw new Error(`urun pi extension: unsupported toolResult content part "${o}" \u2014 the serve lane carries text tool results only`);e+=String(n.text??"")}return e}function Ee(t){if(!(!Array.isArray(t)||t.length===0))return t.map(e=>({type:"function",name:e.name,description:e.description,parameters:e.parameters}))}function z(t,e){if(!e.trim())return{};try{return JSON.parse(e)}catch{throw new Error(`uRun serve error: tool call "${t}" arguments are not valid JSON: ${e}`)}}function Ue(t){let e=t?.output;return Array.isArray(e)?e.filter(n=>n.type==="function_call"):[]}function Se(t){let e=t?.output;if(!Array.isArray(e))return"";let n="";for(let o of e)if(!(o.type!=="reasoning"||!Array.isArray(o.content)))for(let i of o.content)i.type==="reasoning_text"&&(n+=String(i.text??""));return n}var Ie=["<tool_call>","<function="];function Pe(t,e){let n=Ie.find(o=>e.includes(o));if(n)return new Error(`serving row emitted a prose tool call (literal ${JSON.stringify(n)} in the assistant text) for a request that carried tools, and no structured tool_calls arrived \u2014 the tool-call parser is not configured on the model row "${t}" (see catalog parser_defaults)`)}var L=class extends Error{};function Ce(t){let e=t instanceof Error?t.message:String(t),n=t?.code;return/\b529\b/.test(e)||/overloaded/i.test(e)||n===529||n==="529"}function Ne(t){return new Promise(e=>setTimeout(e,t))}function Me(t,e,n,o){return new Promise((i,u)=>{let s=!1,d=r=>{s||(s=!0,clearTimeout(x),o?.removeEventListener("abort",a),r())},x=setTimeout(()=>d(()=>u(n())),e),a=()=>d(()=>u(new Error("aborted")));if(o?.aborted){a();return}o?.addEventListener("abort",a,{once:!0}),Promise.resolve(t).then(r=>d(()=>i(r)),r=>d(()=>u(r)))})}async function*Oe(t,e){let n=t[Symbol.asyncIterator](),o,i=new Promise((u,s)=>{o=()=>s(new Error("aborted")),e.addEventListener("abort",o,{once:!0})});try{for(;;){if(e.aborted)throw new Error("aborted");let u=await Promise.race([n.next(),i]);if(u.done)return;yield u.value}}finally{o&&e.removeEventListener("abort",o),n.return?.(void 0)}}function Q(t,e){let n=t?.sendMessage;if(typeof n=="function")try{n.call(t,{customType:"urun-status",content:e,display:!0},{triggerTurn:!1})}catch{}}function Le(t,e={},n){let o=e.connectDeadlineMs??fe,i=e.stallTimeoutMs??ge,u=e.phaseHeartbeatMs??me;return(s,d,x)=>{let a=oe(),r={role:"assistant",content:[],api:s.api,provider:s.provider,model:s.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()},w=new AbortController,m=x?.signal,E=!1,M=()=>{E=!0,w.abort()};m&&(m.aborted?M():m.addEventListener("abort",M,{once:!0}));let P=!1,U,J=()=>{U&&clearTimeout(U),U=setTimeout(()=>{P=!0,w.abort()},i)},O=()=>{U&&clearTimeout(U),U=void 0},v=null,k=null,y=null,W="",C=!1,$=()=>{if(!k)return;let p=k;k=null,a.push({type:"thinking_end",contentIndex:r.content.indexOf(p),content:p.thinking,partial:r})},q=()=>{if(!v)return;let p=v;v=null,a.push({type:"text_end",contentIndex:r.content.indexOf(p),content:p.text,partial:r})},D=()=>{if(!y)return;let{call:p,args:h,contentIndex:R}=y;y=null,p.arguments=z(p.name,h),a.push({type:"toolcall_end",contentIndex:R,toolCall:p,partial:r})},Z=p=>{let h={type:"toolCall",id:String(p.call_id??p.id??`call_${r.content.length}`),name:String(p.name??""),arguments:{}};r.content.push(h);let R=r.content.length-1;a.push({type:"toolcall_start",contentIndex:R,partial:r});let N=typeof p.arguments=="string"?p.arguments:"";N&&a.push({type:"toolcall_delta",contentIndex:R,delta:N,partial:r}),h.arguments=z(h.name,N),a.push({type:"toolcall_end",contentIndex:R,toolCall:h,partial:r})};return(async()=>{a.push({type:"start",partial:r});let p=!1,h,R=()=>{h&&clearInterval(h),h=void 0},N=Date.now();h=setInterval(()=>{let _=Math.round((Date.now()-N)/1e3);Q(n,`uRun: still opening the "${s.id}" session\u2026 (${_}s \u2014 serve apps scale to zero, first turn can cold-start ~6min)`)},u);let ee=async _=>{let{responses:T}=await Me(t.acquire(s.id),o,()=>new Error(`uRun session for "${s.id}" did not connect within ${Math.round(o/1e3)}s \u2014 likely cold-starting (scaled to zero) or queued behind capacity. Retry shortly.`),w.signal);R();let S,b;for(let l=1;l<=B;l++)try{let g=await T.responses.create({model:s.id,input:_,stream:!0,tools:Ee(d.tools),max_output_tokens:s.maxTokens});J();for await(let A of Oe(g,w.signal)){J();let f=A;if(f.type==="response.output_text.delta"){let c=typeof f.delta=="string"?f.delta:"";c&&(C=!0,$(),D(),v||(v={type:"text",text:""},r.content.push(v),a.push({type:"text_start",contentIndex:r.content.length-1,partial:r})),v.text+=c,W+=c,a.push({type:"text_delta",contentIndex:r.content.indexOf(v),delta:c,partial:r}))}else if(f.type==="response.reasoning_text.delta"){let c=typeof f.delta=="string"?f.delta:"";c&&(C=!0,k||(k={type:"thinking",thinking:""},r.content.push(k),a.push({type:"thinking_start",contentIndex:r.content.length-1,partial:r})),k.thinking+=c,a.push({type:"thinking_delta",contentIndex:r.content.indexOf(k),delta:c,partial:r}))}else if(f.type==="response.function_call_arguments.delta"&&typeof f.tool_index=="number"){let c=f;if(C=!0,$(),q(),!y||y.toolIndex!==c.tool_index){D();let j={type:"toolCall",id:c.call_id??`call_${c.tool_index}`,name:c.name??"",arguments:{}};r.content.push(j),y={call:j,args:"",contentIndex:r.content.length-1,toolIndex:c.tool_index},a.push({type:"toolcall_start",contentIndex:y.contentIndex,partial:r})}c.call_id&&(y.call.id=c.call_id),c.name&&(y.call.name=c.name);let I=typeof c.delta=="string"?c.delta:"";I&&(y.args+=I,a.push({type:"toolcall_delta",contentIndex:y.contentIndex,delta:I,partial:r}))}else if(f.type==="response.completed"){S=f.response;break}else if(f.type==="error"){let c=f.error,I=new Error(`uRun serve error: ${c?.message??"unknown error"}`);throw I.code=c?.code??null,I}}if(!S)throw new L(`uRun backhaul for "${s.id}" closed before the turn completed \u2014 the serving session ended mid-turn (its pod was restarted, drained or deleted).`);b=void 0;break}catch(g){if(b=g,w.signal.aborted)throw g;if(l<B&&Ce(g)&&!C){await Ne(l*500);continue}throw g}if(b)throw b;return S};try{let _=Re(d),T;for(let l=1;l<=X;l++)try{T=await ee(_);break}catch(g){let A=P||g instanceof L;if(l>=X||!A||C||E)throw g;O(),t.evict(s.id),P=!1,w=new AbortController,Q(n,`uRun: the "${s.id}" session ended mid-turn \u2014 re-homing to a fresh assignment and replaying the turn\u2026`)}if(O(),$(),q(),D(),!r.content.some(l=>l.type==="toolCall"))for(let l of Ue(T))Z(l);if(!r.content.some(l=>l.type==="thinking")){let l=Se(T);if(l){let g={type:"thinking",thinking:""};r.content.push(g);let A=r.content.length-1;a.push({type:"thinking_start",contentIndex:A,partial:r}),g.thinking=l,a.push({type:"thinking_delta",contentIndex:A,delta:l,partial:r}),a.push({type:"thinking_end",contentIndex:A,content:l,partial:r})}}let S=r.content.filter(l=>l.type==="toolCall").length;if((d.tools?.length??0)>0&&S===0){let l=Pe(s.id,W);if(l)throw l}let b=S>0?"toolUse":T?.status==="incomplete"?"length":"stop";r.stopReason=b,a.push({type:"done",reason:b,message:r}),a.end(r)}catch(_){if(p=!0,O(),E||m?.aborted&&!P)r.stopReason="aborted",r.errorMessage="aborted by user",a.push({type:"error",reason:"aborted",error:r});else{let T=P?`uRun stream stalled \u2014 no output for ${Math.round(i/1e3)}s; the model may be hung. Retry, or Ctrl-C to abort.`:_ instanceof Error?_.message:String(_);r.stopReason="error",r.errorMessage=T,a.push({type:"error",reason:"error",error:r})}a.end(r)}finally{O(),R(),m&&m.removeEventListener("abort",M),p&&t.evict(s.id)}})(),a}}function $e(t){return{id:t,name:`uRun ${t}`,api:V,reasoning:!0,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:pe,maxTokens:de}}function De(t={}){return async e=>{let n=t.env??process.env,{apiKey:o,apiUrl:i,fnName:u}=ye(n),s=await Y({apiUrl:i,apiKey:o,fnName:u,fetchImpl:t.fetchImpl}),d=new K(t.openSession??ve,n,u);e.on("session_shutdown",()=>d.closeAll());let x={name:"uRun",api:V,baseUrl:"urun://session",apiKey:"$URUN_API_KEY",models:s.map($e),streamSimple:Le(d,t.timing??{},e)};e.registerProvider("urun",x)}}var Xe=De();export{K as SessionPool,V as URUN_API,Te as coalesceSameRole,we as createFileDiagnosticSink,De as createUrunExtension,Xe as default,xe as makeSessionFactory,Le as makeStreamSimple,_e as piDiagnosticLogPath,he as resolveSessionEnv,Re as toResponsesInput};