@akagilnc/pi-workflow-roles 0.1.5207 → 0.1.5212

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,660 +0,0 @@
1
- import { mkdtemp, rm } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import {
5
- createAgentSession,
6
- DefaultResourceLoader,
7
- ModelRegistry,
8
- ModelRuntime,
9
- SettingsManager
10
- } from "@earendil-works/pi-coding-agent";
11
- import {
12
- createAssistantMessageEventStream,
13
- InMemoryCredentialStore
14
- } from "@earendil-works/pi-ai";
15
- import { createRecordSession } from "../archivist-record-entry.js";
16
- import {
17
- createStreamIdleGuard,
18
- isStreamIdleTimeoutError,
19
- StreamIdleTimeoutError
20
- } from "../stream-idle-guard.js";
21
- import {
22
- hasUpstreamErrorTestimony,
23
- isNonSuccessHttpStatus,
24
- projectConfirmedRemotePayload
25
- } from "../upstream-error-testimony.js";
26
- const DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES = 2;
27
- function streamIdleTimeoutFromUnknown(value) {
28
- if (isStreamIdleTimeoutError(value)) return value;
29
- const message = value instanceof Error ? value.message : typeof value === "string" ? value : void 0;
30
- if (message === void 0) return void 0;
31
- const match = /stream idle timeout after (\d+)ms/i.exec(message);
32
- return match !== null && match[1] !== void 0 ? new StreamIdleTimeoutError(Number(match[1])) : void 0;
33
- }
34
- function emptyUsage() {
35
- return {
36
- input: 0,
37
- output: 0,
38
- cacheRead: 0,
39
- cacheWrite: 0,
40
- totalTokens: 0,
41
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
42
- };
43
- }
44
- function addUsage(total, next) {
45
- if (!next) return;
46
- total.input += next.input;
47
- total.output += next.output;
48
- total.cacheRead += next.cacheRead;
49
- total.cacheWrite += next.cacheWrite;
50
- total.totalTokens += next.totalTokens;
51
- total.cost.input += next.cost?.input ?? 0;
52
- total.cost.output += next.cost?.output ?? 0;
53
- total.cost.cacheRead += next.cost?.cacheRead ?? 0;
54
- total.cost.cacheWrite += next.cost?.cacheWrite ?? 0;
55
- total.cost.total += next.cost?.total ?? 0;
56
- }
57
- function numericHttpStatus(value) {
58
- return isNonSuccessHttpStatus(value) ? value : void 0;
59
- }
60
- function projectStructuredRemote(error) {
61
- let httpStatus;
62
- let diagnostics;
63
- let body;
64
- let code;
65
- let errno;
66
- let cursor = error;
67
- const seen = /* @__PURE__ */ new Set();
68
- while (typeof cursor === "object" && cursor !== null && !seen.has(cursor)) {
69
- seen.add(cursor);
70
- const record = cursor;
71
- const nodeStatus = numericHttpStatus(record.statusCode) ?? numericHttpStatus(record.status) ?? numericHttpStatus(record.httpStatus);
72
- const nodeDiagnostics = Array.isArray(record.diagnostics) && record.diagnostics.length > 0 ? record.diagnostics : void 0;
73
- const nodeHasTestimony = hasUpstreamErrorTestimony({
74
- ...nodeStatus === void 0 ? {} : { httpStatus: nodeStatus },
75
- ...nodeDiagnostics === void 0 ? {} : { diagnostics: nodeDiagnostics }
76
- });
77
- if (httpStatus === void 0 && nodeStatus !== void 0) httpStatus = nodeStatus;
78
- if (diagnostics === void 0 && nodeDiagnostics !== void 0) diagnostics = nodeDiagnostics;
79
- if (nodeHasTestimony) {
80
- const payload = projectConfirmedRemotePayload(record);
81
- if (body === void 0 && payload.body !== void 0) body = payload.body;
82
- if (code === void 0 && payload.code !== void 0) code = payload.code;
83
- if (errno === void 0 && payload.errno !== void 0) errno = payload.errno;
84
- }
85
- cursor = record.cause;
86
- }
87
- return {
88
- hasTestimony: hasUpstreamErrorTestimony({
89
- ...httpStatus === void 0 ? {} : { httpStatus },
90
- ...diagnostics === void 0 ? {} : { diagnostics }
91
- }),
92
- ...httpStatus === void 0 ? {} : { httpStatus },
93
- ...diagnostics === void 0 ? {} : { diagnostics },
94
- ...body === void 0 ? {} : { body },
95
- ...code === void 0 ? {} : { code },
96
- ...errno === void 0 ? {} : { errno }
97
- };
98
- }
99
- function attachObservedHttpStatus(message, observedHttpStatus) {
100
- if (observedHttpStatus === void 0) return message;
101
- if (message.stopReason !== "error" && message.stopReason !== "aborted") return message;
102
- if (numericHttpStatus(observedHttpStatus) === void 0) return message;
103
- if (projectStructuredRemote(message).httpStatus !== void 0) return message;
104
- return Object.assign(message, {
105
- status: observedHttpStatus,
106
- statusCode: observedHttpStatus
107
- });
108
- }
109
- function enrichStreamEvent(event, observedHttpStatus) {
110
- if (observedHttpStatus === void 0 || event === null || typeof event !== "object") return event;
111
- const record = event;
112
- if (record.type === "error" && record.error !== null && typeof record.error === "object") {
113
- return {
114
- ...record,
115
- error: attachObservedHttpStatus(record.error, observedHttpStatus)
116
- };
117
- }
118
- if (record.type === "done" && record.message !== null && typeof record.message === "object") {
119
- return {
120
- ...record,
121
- message: attachObservedHttpStatus(record.message, observedHttpStatus)
122
- };
123
- }
124
- if (record.partial !== null && typeof record.partial === "object") {
125
- return {
126
- ...record,
127
- partial: attachObservedHttpStatus(record.partial, observedHttpStatus)
128
- };
129
- }
130
- return event;
131
- }
132
- async function openPiInProcessSession(options) {
133
- const label = options.label ?? "Institutional sub-session";
134
- const selection = options.selection;
135
- let scratchDir;
136
- let resolvedAgentDir = options.agentDir;
137
- if (resolvedAgentDir === void 0) {
138
- scratchDir = await mkdtemp(join(options.credentialScratchParent ?? tmpdir(), "ak-institutional-"));
139
- resolvedAgentDir = scratchDir;
140
- }
141
- try {
142
- const childRuntime = await ModelRuntime.create();
143
- const childRegistry = new ModelRegistry(childRuntime);
144
- const childProvider = typeof childRegistry.getProvider === "function" ? childRegistry.getProvider(selection.provider) : void 0;
145
- const foundModel = typeof childRegistry.find === "function" ? childRegistry.find(selection.provider, selection.model) : void 0;
146
- const withTypedReason = (error, reason) => Object.assign(error, { reason });
147
- if (childProvider === void 0) {
148
- throw withTypedReason(
149
- new Error(`${label} model is unavailable: ${selection.provider}/${selection.model}`),
150
- "model"
151
- );
152
- }
153
- const providerDefaultModel = childProvider.getModels?.()[0];
154
- const fallbackApi = providerDefaultModel?.api ?? childProvider?.api ?? (selection.provider === "openai-codex" ? "openai-codex-responses" : "openai-completions");
155
- const modelToUse = foundModel ?? {
156
- id: selection.model,
157
- name: selection.model,
158
- api: fallbackApi,
159
- provider: selection.provider,
160
- baseUrl: providerDefaultModel?.baseUrl ?? "",
161
- reasoning: providerDefaultModel?.reasoning ?? false,
162
- input: ["text"],
163
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
164
- contextWindow: 128e3,
165
- maxTokens: 16384
166
- };
167
- let resolution;
168
- if (typeof childRegistry.getProviderAuth === "function") {
169
- resolution = await childRegistry.getProviderAuth(selection.provider).catch((error) => {
170
- throw withTypedReason(
171
- new Error(`${label} authentication failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error }),
172
- "auth"
173
- );
174
- });
175
- if (resolution === void 0) {
176
- throw withTypedReason(
177
- new Error(`${label} authentication failed: provider is not configured: ${selection.provider}`),
178
- "auth"
179
- );
180
- }
181
- }
182
- let authResult;
183
- if (typeof childRegistry.getApiKeyAndHeaders === "function") {
184
- authResult = await childRegistry.getApiKeyAndHeaders(modelToUse);
185
- if (authResult && !authResult.ok) {
186
- throw withTypedReason(
187
- new Error(`${label} authentication failed: ${authResult.error}`),
188
- "auth"
189
- );
190
- }
191
- }
192
- const resolvedApiKey = authResult?.apiKey ?? resolution?.auth?.apiKey;
193
- const resolvedHeaders = authResult?.headers ?? resolution?.auth?.headers;
194
- const resolvedEnv = authResult?.env ?? resolution?.env;
195
- const effectiveBaseUrl = resolution?.auth?.baseUrl ?? modelToUse.baseUrl;
196
- const effectiveModel = {
197
- ...modelToUse,
198
- baseUrl: effectiveBaseUrl
199
- };
200
- const credentials = new InMemoryCredentialStore();
201
- const runtime = await ModelRuntime.create({
202
- credentials,
203
- modelsPath: null
204
- });
205
- if (resolvedApiKey !== void 0) {
206
- await credentials.modify(selection.provider, async () => ({
207
- type: "api_key",
208
- key: resolvedApiKey,
209
- ...resolvedEnv === void 0 ? {} : { env: resolvedEnv }
210
- }));
211
- }
212
- const abortReason = (signal) => signal.reason ?? new Error(`${label} provider stream aborted`);
213
- let streamFailureValue;
214
- async function waitForStream(promise, signal) {
215
- if (signal.aborted) throw abortReason(signal);
216
- let onAbort;
217
- try {
218
- return await Promise.race([
219
- promise,
220
- new Promise((_resolve, reject) => {
221
- onAbort = () => reject(abortReason(signal));
222
- signal.addEventListener("abort", onAbort, { once: true });
223
- })
224
- ]);
225
- } finally {
226
- if (onAbort !== void 0) signal.removeEventListener("abort", onAbort);
227
- }
228
- }
229
- const createRetriedStream = (simple, model, context, request) => {
230
- const wrapped = createAssistantMessageEventStream();
231
- void (async () => {
232
- for (let attempt = 0; ; attempt += 1) {
233
- const idle = createStreamIdleGuard(
234
- options.signal === void 0 ? {} : { parentSignal: options.signal }
235
- );
236
- let observedHttpStatus;
237
- let observedHttpPayload;
238
- try {
239
- const requestSignal = request?.signal;
240
- const streamSignal = requestSignal === void 0 ? idle.signal : AbortSignal.any([idle.signal, requestSignal]);
241
- const priorOnResponse = request?.onResponse;
242
- const priorFetch = request?.fetch;
243
- const baseFetch = priorFetch ?? globalThis.fetch.bind(globalThis);
244
- const statusAwareFetch = async (input, init) => {
245
- const response2 = await baseFetch(input, init);
246
- if (isNonSuccessHttpStatus(response2?.status)) {
247
- observedHttpStatus = response2.status;
248
- try {
249
- const parsed = JSON.parse(await response2.clone().text());
250
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
251
- observedHttpPayload = projectConfirmedRemotePayload(parsed);
252
- }
253
- } catch {
254
- }
255
- }
256
- return response2;
257
- };
258
- const retriedRequest = {
259
- ...request ?? {},
260
- ...resolvedEnv === void 0 ? {} : { env: resolvedEnv },
261
- signal: streamSignal,
262
- maxRetries: 0,
263
- fetch: statusAwareFetch,
264
- onResponse: async (response2, resModel) => {
265
- if (typeof response2?.status === "number") observedHttpStatus = response2.status;
266
- await priorOnResponse?.(response2, resModel);
267
- }
268
- };
269
- if (childProvider === void 0) {
270
- throw withTypedReason(
271
- new Error(`${label} provider not found: ${model.provider}`),
272
- "model"
273
- );
274
- }
275
- const source = simple ? childProvider.streamSimple(model, context, retriedRequest) : childProvider.stream(model, context, retriedRequest);
276
- let sawEvent = false;
277
- const attemptEvents = [];
278
- const iterator = source[Symbol.asyncIterator]();
279
- while (true) {
280
- const next = await waitForStream(iterator.next(), idle.signal);
281
- if (next.done) break;
282
- sawEvent = true;
283
- idle.poke();
284
- attemptEvents.push(enrichStreamEvent(next.value, observedHttpStatus));
285
- }
286
- const response = attachObservedHttpStatus(
287
- await waitForStream(source.result(), idle.signal),
288
- observedHttpStatus
289
- );
290
- if (response.stopReason === "error") {
291
- const errorMessage = response.errorMessage ?? `${label} provider stream failed`;
292
- const idleFailure = streamIdleTimeoutFromUnknown(errorMessage);
293
- if (options.idleRetry !== false && idleFailure !== void 0 && attempt < DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES && options.signal?.aborted !== true) {
294
- continue;
295
- }
296
- const failure = idleFailure ?? new Error(errorMessage, { cause: response });
297
- const httpStatus = numericHttpStatus(observedHttpStatus) ?? numericHttpStatus(response.statusCode) ?? numericHttpStatus(response.status);
298
- if (httpStatus !== void 0) {
299
- Object.assign(failure, {
300
- statusCode: httpStatus,
301
- status: httpStatus,
302
- ...observedHttpPayload ?? {}
303
- });
304
- Object.assign(response, {
305
- statusCode: httpStatus,
306
- status: httpStatus,
307
- ...observedHttpPayload ?? {}
308
- });
309
- }
310
- streamFailureValue = failure;
311
- }
312
- for (const ev of attemptEvents) {
313
- wrapped.push(ev);
314
- }
315
- wrapped.end(response);
316
- return;
317
- } catch (error) {
318
- if (request?.signal?.aborted) {
319
- const response2 = {
320
- role: "assistant",
321
- content: [],
322
- api: model.api,
323
- provider: model.provider,
324
- model: model.id,
325
- usage: emptyUsage(),
326
- stopReason: "aborted",
327
- errorMessage: `${label} session aborted`,
328
- timestamp: Date.now()
329
- };
330
- wrapped.push({ type: "error", reason: "aborted", error: response2 });
331
- wrapped.end(response2);
332
- return;
333
- }
334
- const failure = isStreamIdleTimeoutError(idle.signal.reason) ? idle.signal.reason : error;
335
- const idleFailure = streamIdleTimeoutFromUnknown(failure);
336
- if (options.idleRetry !== false && idleFailure !== void 0 && attempt < DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES && options.signal?.aborted !== true) {
337
- continue;
338
- }
339
- const typedFailure = idleFailure ?? failure;
340
- const projected = projectStructuredRemote(failure);
341
- const httpStatus = projected.httpStatus ?? numericHttpStatus(observedHttpStatus);
342
- const payload = projectConfirmedRemotePayload({
343
- body: projected.body ?? observedHttpPayload?.body,
344
- code: projected.code ?? observedHttpPayload?.code,
345
- errno: projected.errno ?? observedHttpPayload?.errno
346
- });
347
- if (httpStatus !== void 0 && typeof typedFailure === "object" && typedFailure !== null) {
348
- Object.assign(typedFailure, { statusCode: httpStatus, status: httpStatus, ...payload });
349
- }
350
- streamFailureValue = typedFailure;
351
- const response = {
352
- role: "assistant",
353
- content: [],
354
- api: model.api,
355
- provider: model.provider,
356
- model: model.id,
357
- usage: emptyUsage(),
358
- stopReason: "error",
359
- errorMessage: failure instanceof Error ? failure.message : String(failure),
360
- timestamp: Date.now(),
361
- ...projected.diagnostics === void 0 ? {} : { diagnostics: projected.diagnostics },
362
- ...httpStatus === void 0 ? {} : { status: httpStatus, statusCode: httpStatus },
363
- ...payload
364
- };
365
- wrapped.push({ type: "error", reason: "error", error: response });
366
- wrapped.end(response);
367
- return;
368
- } finally {
369
- idle.dispose();
370
- }
371
- }
372
- })();
373
- return wrapped;
374
- };
375
- const provider = {
376
- id: selection.provider,
377
- name: childProvider?.name ?? label,
378
- ...effectiveBaseUrl ? { baseUrl: effectiveBaseUrl } : {},
379
- ...resolvedHeaders ? { headers: resolvedHeaders } : {},
380
- auth: {
381
- apiKey: {
382
- name: `${label} authentication`,
383
- async resolve() {
384
- return {
385
- auth: {
386
- ...resolvedApiKey === void 0 ? {} : { apiKey: resolvedApiKey },
387
- ...resolvedHeaders === void 0 ? {} : { headers: resolvedHeaders },
388
- ...effectiveBaseUrl === void 0 ? {} : { baseUrl: effectiveBaseUrl }
389
- },
390
- ...resolvedEnv === void 0 ? {} : { env: resolvedEnv }
391
- };
392
- }
393
- }
394
- },
395
- getModels() {
396
- return [effectiveModel];
397
- },
398
- stream(model, childContext, request) {
399
- return createRetriedStream(false, model, childContext, request);
400
- },
401
- streamSimple(model, childContext, request) {
402
- return createRetriedStream(true, model, childContext, request);
403
- }
404
- };
405
- runtime.registerNativeProvider(provider);
406
- await runtime.refresh({ allowNetwork: false });
407
- const settings = SettingsManager.inMemory({ compaction: { enabled: false }, retry: { enabled: false } });
408
- const loader = new DefaultResourceLoader({
409
- cwd: options.cwd,
410
- agentDir: resolvedAgentDir,
411
- settingsManager: settings,
412
- noExtensions: true,
413
- noSkills: true,
414
- noPromptTemplates: true,
415
- noThemes: true,
416
- noContextFiles: true,
417
- systemPrompt: options.systemPrompt
418
- });
419
- await loader.reload();
420
- let sessionManager;
421
- if (options.sessionManager !== void 0) {
422
- sessionManager = options.sessionManager;
423
- } else if (options.sessionIdentity !== void 0) {
424
- sessionManager = createRecordSession({
425
- cwd: options.cwd,
426
- kind: options.sessionIdentity.kind,
427
- ...options.sessionIdentity.subject === void 0 ? {} : { subject: options.sessionIdentity.subject },
428
- ...options.sessionIdentity.parent === void 0 ? {} : { parent: options.sessionIdentity.parent }
429
- });
430
- } else {
431
- sessionManager = createRecordSession({
432
- cwd: options.cwd,
433
- kind: "institutional"
434
- });
435
- }
436
- const customTools = [];
437
- if (options.customTools !== void 0) {
438
- customTools.push(...options.customTools);
439
- }
440
- if (options.tools !== void 0) {
441
- for (const hostTool of options.tools) {
442
- customTools.push({
443
- name: hostTool.name,
444
- label: hostTool.label,
445
- description: hostTool.description,
446
- parameters: hostTool.parameters,
447
- execute: async (toolCallId, params, signal, update, _ctx) => {
448
- const res = await hostTool.execute(
449
- toolCallId,
450
- params,
451
- signal,
452
- update === void 0 ? void 0 : (u) => update(u),
453
- void 0
454
- );
455
- return res;
456
- }
457
- });
458
- }
459
- }
460
- const requestedThinking = selection.thinking;
461
- const priorEntries = typeof sessionManager.getEntries === "function" ? sessionManager.getEntries() : [];
462
- const reusedSession = priorEntries.some((entry) => {
463
- if (typeof entry !== "object" || entry === null) return false;
464
- const type = entry.type;
465
- return type === "message" || type === "model_change" || type === "thinking_level_change";
466
- });
467
- const { session } = await createAgentSession({
468
- cwd: options.cwd,
469
- model: effectiveModel,
470
- ...requestedThinking === void 0 ? {} : { thinkingLevel: requestedThinking },
471
- modelRuntime: runtime,
472
- sessionManager,
473
- settingsManager: settings,
474
- agentDir: resolvedAgentDir,
475
- resourceLoader: loader,
476
- ...options.noTools === void 0 ? {} : { noTools: options.noTools },
477
- ...options.toolsAllowlist === void 0 ? {} : { tools: options.toolsAllowlist },
478
- ...customTools.length === 0 ? {} : { customTools }
479
- });
480
- if (reusedSession) {
481
- try {
482
- await session.setModel(effectiveModel);
483
- if (requestedThinking !== void 0) {
484
- session.setThinkingLevel(requestedThinking);
485
- }
486
- } catch (error) {
487
- session.dispose();
488
- throw withTypedReason(
489
- error instanceof Error ? error : new Error(
490
- `${label} failed to apply seat model/thinking for ${selection.provider}/${selection.model}`
491
- ),
492
- requestedThinking !== void 0 ? "thinking" : "model"
493
- );
494
- }
495
- }
496
- const listeners = /* @__PURE__ */ new Set();
497
- const accumulatedUsage = emptyUsage();
498
- let lastEmittedAssistant;
499
- const unsubscribeSession = session.subscribe((event) => {
500
- if (event.type === "message_end") {
501
- const msg = event.message;
502
- if (msg.role === "assistant") {
503
- lastEmittedAssistant = msg;
504
- if (msg.usage) {
505
- addUsage(accumulatedUsage, msg.usage);
506
- }
507
- }
508
- for (const listener of listeners) {
509
- listener({
510
- type: "message_end",
511
- role: msg.role,
512
- message: msg,
513
- ...msg.usage === void 0 ? {} : { usage: msg.usage }
514
- });
515
- }
516
- } else if (event.type === "turn_end") {
517
- const msg = event.message;
518
- for (const listener of listeners) {
519
- listener({
520
- type: "turn_end",
521
- ...msg.stopReason === void 0 ? {} : { stopReason: msg.stopReason }
522
- });
523
- }
524
- } else if (event.type === "tool_execution_start") {
525
- for (const listener of listeners) {
526
- listener({
527
- type: "tool_call",
528
- toolCallId: event.toolCallId,
529
- toolName: event.toolName,
530
- args: event.args ?? event.input
531
- });
532
- }
533
- } else if (event.type === "tool_execution_end") {
534
- for (const listener of listeners) {
535
- listener({
536
- type: "tool_result",
537
- toolCallId: event.toolCallId,
538
- toolName: event.toolName,
539
- isError: event.isError,
540
- details: event.result ?? event.details
541
- });
542
- }
543
- }
544
- });
545
- let closed = false;
546
- const sessionFile = sessionManager.getSessionFile();
547
- const sessionId = sessionManager.getHeader?.()?.id;
548
- const handle = {
549
- ...sessionFile === void 0 ? {} : { sessionFile },
550
- ...sessionId === void 0 ? {} : { sessionId },
551
- async prompt(text) {
552
- const abortSession = () => {
553
- void session.abort();
554
- };
555
- if (options.signal?.aborted) abortSession();
556
- else options.signal?.addEventListener("abort", abortSession, { once: true });
557
- const assistantCountBefore = session.messages.filter(
558
- (message) => message.role === "assistant"
559
- ).length;
560
- let promptError;
561
- try {
562
- await session.prompt(text);
563
- } catch (error) {
564
- promptError = error;
565
- } finally {
566
- options.signal?.removeEventListener("abort", abortSession);
567
- }
568
- const lastAssistant = [...session.messages].reverse().find((message) => message.role === "assistant");
569
- if (lastAssistant !== void 0 && lastAssistant !== lastEmittedAssistant) {
570
- lastEmittedAssistant = lastAssistant;
571
- for (const listener of listeners) {
572
- listener({
573
- type: "message_end",
574
- role: lastAssistant.role,
575
- message: lastAssistant,
576
- ...lastAssistant.usage === void 0 ? {} : { usage: lastAssistant.usage }
577
- });
578
- }
579
- }
580
- const producedAssistant = session.messages.filter(
581
- (message) => message.role === "assistant"
582
- ).length > assistantCountBefore;
583
- if (promptError !== void 0 && !producedAssistant) {
584
- throw promptError;
585
- }
586
- return {
587
- text: session.getLastAssistantText() ?? "",
588
- ...lastAssistant?.stopReason === void 0 ? {} : { stopReason: lastAssistant.stopReason },
589
- ...lastAssistant?.errorMessage === void 0 ? {} : { errorMessage: lastAssistant.errorMessage },
590
- usage: accumulatedUsage,
591
- messages: session.messages
592
- };
593
- },
594
- subscribe(listener) {
595
- listeners.add(listener);
596
- return () => {
597
- listeners.delete(listener);
598
- };
599
- },
600
- abort() {
601
- void session.abort();
602
- },
603
- async close() {
604
- if (closed) return;
605
- closed = true;
606
- listeners.clear();
607
- const cleanupFailures = [];
608
- try {
609
- unsubscribeSession();
610
- } catch (error) {
611
- cleanupFailures.push(error);
612
- }
613
- try {
614
- session.dispose();
615
- } catch (error) {
616
- cleanupFailures.push(error);
617
- }
618
- if (scratchDir !== void 0) {
619
- try {
620
- await rm(scratchDir, { recursive: true, force: true });
621
- } catch (error) {
622
- cleanupFailures.push(error);
623
- }
624
- }
625
- if (cleanupFailures.length === 1) throw cleanupFailures[0];
626
- if (cleanupFailures.length > 1) {
627
- throw new AggregateError(cleanupFailures, `${label} close cleanup failures`, {
628
- cause: cleanupFailures[0]
629
- });
630
- }
631
- }
632
- };
633
- return {
634
- handle,
635
- // Read lazily: the provider stream runs during session.prompt, so the
636
- // primary failure is only known after that turn completes. A getter keeps
637
- // this reflecting the latest value instead of freezing it at open time.
638
- get streamFailure() {
639
- return streamFailureValue;
640
- }
641
- };
642
- } catch (openError) {
643
- if (scratchDir !== void 0) {
644
- try {
645
- await rm(scratchDir, { recursive: true, force: true });
646
- } catch (cleanupFailure) {
647
- throw new AggregateError(
648
- [openError, cleanupFailure],
649
- `${label} open failed and its scratch cleanup also failed`,
650
- { cause: openError }
651
- );
652
- }
653
- }
654
- throw openError;
655
- }
656
- }
657
- export {
658
- DEFAULT_COMPLIANCE_IDLE_MAX_RETRIES,
659
- openPiInProcessSession
660
- };