@agentknight/sdk 0.9.35

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.
@@ -0,0 +1,2659 @@
1
+ import {
2
+ isContentfulAssistant
3
+ } from "./chunk-MVUOCZFL.js";
4
+ import {
5
+ SessionOperationError
6
+ } from "./chunk-SJHJER3B.js";
7
+ import {
8
+ CONVERSATION_SOURCE_ID_LIMIT,
9
+ SESSION_DIRECTORY_LIST_OPERATION,
10
+ SESSION_FILE_READ_OPERATION,
11
+ SESSION_PROJECT_IDENTITY_OPERATION,
12
+ SESSION_RESOURCE_READ_OPERATION,
13
+ SessionGitProjectSchema,
14
+ TIMELINE_IMAGE_OPERATION,
15
+ TIMELINE_SYNC_OPERATION,
16
+ WORKBENCH_OPERATIONS,
17
+ WorkbenchAskResultSchema,
18
+ WorkbenchCommandResultSchema,
19
+ WorkbenchCommandSchema,
20
+ WorkbenchCompactResultSchema,
21
+ WorkbenchInsightsSchema,
22
+ WorkbenchQueueMutationResultSchema,
23
+ WorkbenchQueueSnapshotSchema
24
+ } from "./chunk-ZKAN2FO7.js";
25
+
26
+ // src/operation-diagnostics.ts
27
+ import { AsyncLocalStorage } from "node:async_hooks";
28
+ import { createHash } from "node:crypto";
29
+ import { performance } from "node:perf_hooks";
30
+ import { getSystemErrorMap } from "node:util";
31
+ import { ZodError } from "zod";
32
+
33
+ // src/copilot-connection-errors.ts
34
+ function isClosedCopilotConnection(error) {
35
+ if (!(error instanceof Error)) {
36
+ return false;
37
+ }
38
+ switch (error.message) {
39
+ case "Connection is closed.":
40
+ return !("code" in error) || error.code === 1;
41
+ case "Connection is disposed.":
42
+ return !("code" in error) || error.code === 2;
43
+ case "Pending response rejected since connection got disposed":
44
+ return !("code" in error) || error.code === -32097;
45
+ default:
46
+ return false;
47
+ }
48
+ }
49
+ function isDisposedCopilotSession(error) {
50
+ return error instanceof Error && (error.message === "Session has been disposed" || (!("code" in error) || error.code === -32603) && /^Request session\.[A-Za-z0-9.]+ failed with message: Session has been disposed$/u.test(error.message));
51
+ }
52
+
53
+ // src/session-restore-error.ts
54
+ var restoreMessages = /* @__PURE__ */ new Set([
55
+ "First event must be session.start or session.resume",
56
+ "session resume failed: First event must be session.start or session.resume",
57
+ "Request session.resume failed with message: session resume failed: First event must be session.start or session.resume"
58
+ ]);
59
+ function isSessionRestoreFailure(error) {
60
+ for (let depth = 0; depth < 8 && error instanceof Error; depth++) {
61
+ if ("code" in error && error.code === "SESSION_RESTORE_FAILED" || restoreMessages.has(error.message)) return true;
62
+ error = error.cause;
63
+ }
64
+ return false;
65
+ }
66
+ function sessionRestoreFailure(error) {
67
+ return isSessionRestoreFailure(error) ? {
68
+ code: "SESSION_RESTORE_FAILED",
69
+ message: "The session could not be restored. Retry to reconnect."
70
+ } : void 0;
71
+ }
72
+
73
+ // src/operation-diagnostics.ts
74
+ var current = new AsyncLocalStorage();
75
+ var systemCodes = new Set([...getSystemErrorMap().values()].map(([code]) => code));
76
+ var errorTypes = /* @__PURE__ */ new Set(["Error", "TypeError", "RangeError", "SyntaxError", "AbortError", "TimeoutError", "AggregateError"]);
77
+ var operations = /* @__PURE__ */ new Set([
78
+ TIMELINE_SYNC_OPERATION,
79
+ TIMELINE_IMAGE_OPERATION,
80
+ SESSION_DIRECTORY_LIST_OPERATION,
81
+ SESSION_FILE_READ_OPERATION,
82
+ SESSION_PROJECT_IDENTITY_OPERATION,
83
+ SESSION_RESOURCE_READ_OPERATION,
84
+ ...WORKBENCH_OPERATIONS
85
+ ]);
86
+ var operationName = (request) => operations.has(request.operation) ? request.operation : "unknown";
87
+ function diagnosticReference(kind, value) {
88
+ return `${kind}:${createHash("sha256").update(`${kind}:${value}`).digest("hex").slice(0, 16)}`;
89
+ }
90
+ function diagnosticRequest(request) {
91
+ return {
92
+ requestRef: diagnosticReference("request", request.requestId),
93
+ machineRef: diagnosticReference("machine", request.machineId),
94
+ ...request.sessionId === void 0 ? {} : { sessionRef: diagnosticReference("session", request.sessionId) },
95
+ ...request.operationId === void 0 ? {} : { operationRef: diagnosticReference("operation", request.operationId) }
96
+ };
97
+ }
98
+ function diagnosticError(error) {
99
+ const fields = {
100
+ errorType: error instanceof ZodError ? "ZodError" : error instanceof Error ? errorTypes.has(error.name) ? error.name : "CustomError" : "NonError"
101
+ };
102
+ fields.errorCategory = errorCategory(error);
103
+ if (error instanceof Error && "code" in error && typeof error.code === "string" && systemCodes.has(error.code)) fields.errorCode = error.code;
104
+ if (error instanceof Error && "code" in error && typeof error.code === "number" && [-32700, -32600, -32601, -32602, -32603, -32097].includes(error.code)) fields.rpcCode = error.code;
105
+ if (error instanceof ZodError) {
106
+ fields.issueCount = error.issues.length;
107
+ fields.issueCodes = [...new Set(error.issues.map((issue) => issue.code))].slice(0, 8);
108
+ }
109
+ if (error instanceof Error && error.cause !== void 0 && error.cause !== error) {
110
+ const cause = error.cause;
111
+ fields.causeType = cause instanceof Error && errorTypes.has(cause.name) ? cause.name : "Other";
112
+ fields.causeCategory = errorCategory(cause);
113
+ if (cause instanceof Error && "code" in cause && typeof cause.code === "string" && systemCodes.has(cause.code)) fields.causeCode = cause.code;
114
+ }
115
+ if (error instanceof AggregateError) {
116
+ fields.childCount = error.errors.length;
117
+ fields.childCategories = error.errors.slice(0, 2).map((child) => errorCategory(child));
118
+ }
119
+ return fields;
120
+ }
121
+ function errorCategory(error) {
122
+ if (isSessionRestoreFailure(error)) return "SESSION_RESTORE_FAILED";
123
+ if (isClosedCopilotConnection(error)) return "COPILOT_CONNECTION_CLOSED";
124
+ if (isDisposedCopilotSession(error)) return "COPILOT_SESSION_DISPOSED";
125
+ if (error instanceof ZodError) return "SCHEMA_VALIDATION";
126
+ if (error instanceof Error && "code" in error && (error.code === "SESSION_NOT_SHARED" || error.code === "AGENT_OPERATION_UNSUPPORTED")) return error.code;
127
+ return "OTHER";
128
+ }
129
+ function recordOperationFailure(error, phase) {
130
+ const diagnostics = current.getStore();
131
+ if (diagnostics?.running === true) diagnostics.recordFailure(error, phase);
132
+ }
133
+ function validateOperationResponse(action) {
134
+ try {
135
+ return action();
136
+ } catch (error) {
137
+ recordOperationFailure(error, "validate-response");
138
+ throw error;
139
+ }
140
+ }
141
+ async function measureOperationPhase(phase, action) {
142
+ const diagnostics = current.getStore();
143
+ if (diagnostics?.running !== true) return action();
144
+ const start = performance.now();
145
+ try {
146
+ return await action();
147
+ } catch (error) {
148
+ diagnostics.recordFailure(error, phase);
149
+ throw error;
150
+ } finally {
151
+ diagnostics.durations[phase] += performance.now() - start;
152
+ if (phase === "read") diagnostics.readCount += 1;
153
+ }
154
+ }
155
+ async function measureOperationStage(stage, action) {
156
+ const diagnostics = current.getStore();
157
+ if (diagnostics?.running !== true) return action();
158
+ const start = performance.now();
159
+ let failed = false;
160
+ try {
161
+ return await action();
162
+ } catch (error) {
163
+ failed = true;
164
+ diagnostics.recordStageFailure(error, stage);
165
+ throw error;
166
+ } finally {
167
+ diagnostics.recordStage(stage, performance.now() - start, failed);
168
+ }
169
+ }
170
+ function recordOperationTimelineRead(diagnostic) {
171
+ const diagnostics = current.getStore();
172
+ if (diagnostics?.running === true) diagnostics.recordTimelineRead(diagnostic);
173
+ }
174
+ var OperationDiagnostics = class {
175
+ durations = { handle: 0, read: 0, wait: 0, emit: 0 };
176
+ readCount = 0;
177
+ #failure;
178
+ #running = false;
179
+ #stages = /* @__PURE__ */ new Map();
180
+ #stageFailures = /* @__PURE__ */ new Map();
181
+ #timelineReads = [];
182
+ async run(action) {
183
+ this.#running = true;
184
+ try {
185
+ return await current.run(this, () => measureOperationPhase("handle", action));
186
+ } finally {
187
+ this.#running = false;
188
+ this.#stageFailures.clear();
189
+ }
190
+ }
191
+ get running() {
192
+ return this.#running;
193
+ }
194
+ recordFailure(error, phase) {
195
+ const stage = [...this.#stageFailures].find(([, failure]) => Object.is(error, failure))?.[0];
196
+ this.#failure ??= { phase, ...diagnosticError(error), ...stage === void 0 ? {} : { stage } };
197
+ }
198
+ recordStageFailure(error, stage) {
199
+ if (!this.#running) return;
200
+ if (![...this.#stageFailures.values()].some((failure) => Object.is(error, failure))) {
201
+ this.#stageFailures.set(stage, error);
202
+ }
203
+ }
204
+ recordStage(stage, elapsedMs, failed) {
205
+ if (!this.#running) return;
206
+ const sample = this.#stages.get(stage) ?? { calls: 0, elapsedMs: 0, failedCalls: 0 };
207
+ sample.calls++;
208
+ sample.elapsedMs += elapsedMs;
209
+ sample.failedCalls += failed ? 1 : 0;
210
+ this.#stages.set(stage, sample);
211
+ }
212
+ recordTimelineRead(diagnostic) {
213
+ if (this.#timelineReads.length < 8) {
214
+ this.#timelineReads.push({ readRef: diagnostic.readRef, joined: diagnostic.readJoins > 0 });
215
+ }
216
+ }
217
+ get activeMs() {
218
+ return Math.max(0, this.durations.handle - this.durations.wait) + this.durations.emit;
219
+ }
220
+ fields(request) {
221
+ return {
222
+ operation: operationName(request),
223
+ version: request.version,
224
+ ...diagnosticRequest(request),
225
+ ...this.details()
226
+ };
227
+ }
228
+ details() {
229
+ return {
230
+ diagnosticVersion: 2,
231
+ handleMs: Math.round(this.durations.handle),
232
+ readMs: Math.round(this.durations.read),
233
+ readCount: this.readCount,
234
+ waitMs: Math.round(this.durations.wait),
235
+ emitMs: Math.round(this.durations.emit),
236
+ activeMs: Math.round(this.activeMs),
237
+ phase: "handle",
238
+ ...this.#failure,
239
+ // Nested or parallel stage durations overlap; they must not be summed as wall-clock time.
240
+ ...this.#stages.size === 0 ? {} : { stages: [...this.#stages].map(([stage, sample]) => ({
241
+ stage,
242
+ ...sample,
243
+ elapsedMs: Math.round(sample.elapsedMs)
244
+ })) },
245
+ ...this.#timelineReads.length === 0 ? {} : { timelineReads: [...this.#timelineReads] }
246
+ };
247
+ }
248
+ };
249
+ var OperationMetrics = class {
250
+ #samples = /* @__PURE__ */ new Map();
251
+ record(request, diagnostics, failed) {
252
+ const operation = operationName(request);
253
+ const sample = this.#samples.get(operation) ?? {
254
+ operation,
255
+ requests: 0,
256
+ failures: 0,
257
+ handleMs: 0,
258
+ readMs: 0,
259
+ waitMs: 0,
260
+ emitMs: 0,
261
+ maxActiveMs: 0,
262
+ maxReadMs: 0
263
+ };
264
+ sample.requests += 1;
265
+ sample.failures += failed ? 1 : 0;
266
+ for (const [field, phase] of [
267
+ ["handleMs", "handle"],
268
+ ["readMs", "read"],
269
+ ["waitMs", "wait"],
270
+ ["emitMs", "emit"]
271
+ ]) sample[field] += Math.round(diagnostics.durations[phase]);
272
+ sample.maxActiveMs = Math.max(sample.maxActiveMs, Math.round(diagnostics.activeMs));
273
+ sample.maxReadMs = Math.max(sample.maxReadMs, Math.round(diagnostics.durations.read));
274
+ this.#samples.set(operation, sample);
275
+ }
276
+ drain() {
277
+ const samples = [...this.#samples.values()];
278
+ this.#samples.clear();
279
+ return samples;
280
+ }
281
+ };
282
+
283
+ // src/conversation-normalizer.ts
284
+ import { createHash as createHash2 } from "node:crypto";
285
+ var SUMMARY_LIMIT = 4096;
286
+ var IDENTIFIER_LIMIT = 256;
287
+ var TASK_COMPLETE_TOOL_NAME = "task_complete";
288
+ var MAX_INLINE_IMAGE_BYTES = 3 * 1024 * 1024;
289
+ var MAX_INLINE_IMAGE_MESSAGE_BYTES = 3 * 1024 * 1024;
290
+ var MAX_INLINE_IMAGES_PER_MESSAGE = 4;
291
+ var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
292
+ "image/gif",
293
+ "image/jpeg",
294
+ "image/png",
295
+ "image/webp"
296
+ ]);
297
+ function splitToolImages(images) {
298
+ const groups = [];
299
+ let group = [];
300
+ let groupBytes = 0;
301
+ for (const image of images) {
302
+ const imageBytes = image.attachment.byteLength ?? 0;
303
+ if (group.length > 0 && (group.length >= MAX_INLINE_IMAGES_PER_MESSAGE || groupBytes + imageBytes > MAX_INLINE_IMAGE_MESSAGE_BYTES)) {
304
+ groups.push(group);
305
+ group = [];
306
+ groupBytes = 0;
307
+ }
308
+ group.push(image);
309
+ groupBytes += imageBytes;
310
+ }
311
+ if (group.length > 0) {
312
+ groups.push(group);
313
+ }
314
+ return groups;
315
+ }
316
+ function protocolId(value) {
317
+ if (value.length <= IDENTIFIER_LIMIT) {
318
+ return value;
319
+ }
320
+ const digest = createHash2("sha256").update(value).digest("hex");
321
+ return `sha256:${digest}`;
322
+ }
323
+ function scopedId(scope, value) {
324
+ const id = `${scope}:${value}`;
325
+ if (id.length <= IDENTIFIER_LIMIT) {
326
+ return id;
327
+ }
328
+ const digest = createHash2("sha256").update(value).digest("hex");
329
+ return `${scope}:sha256:${digest}`;
330
+ }
331
+ function assistantMessageId(data) {
332
+ return scopedId("assistant", data.apiCallId ?? data.messageId);
333
+ }
334
+ function eventRevision(timestamp, previousRevision) {
335
+ const timestampRevision = Date.parse(timestamp);
336
+ if (!Number.isSafeInteger(timestampRevision) || timestampRevision < 0) {
337
+ throw new Error(`Invalid SDK event timestamp: ${timestamp}`);
338
+ }
339
+ const revision = previousRevision === void 0 || timestampRevision > previousRevision ? timestampRevision : previousRevision + 1;
340
+ if (!Number.isSafeInteger(revision)) {
341
+ throw new Error(`SDK event revision exceeds the safe integer range: ${timestamp}`);
342
+ }
343
+ return revision;
344
+ }
345
+ function removeInternalMarkup(value) {
346
+ return value.replace(
347
+ /<skill-context\b[^>]*>[\s\S]*?<\/skill-context\s*>/giu,
348
+ " "
349
+ ).replace(/<skill-context\b[^>]*>[\s\S]*/giu, " ").replace(/<\/?system_notification\b[^>]*>/giu, " ");
350
+ }
351
+ function stripInternalMarkup(value) {
352
+ return removeInternalMarkup(value).replace(/\s+/gu, " ").trim();
353
+ }
354
+ function limitSummary(value, limit = SUMMARY_LIMIT) {
355
+ if (value.length <= limit) {
356
+ return value;
357
+ }
358
+ return `${value.slice(0, limit - 1)}\u2026`;
359
+ }
360
+ function boundedText(value, limit, fallback) {
361
+ const normalized = stripInternalMarkup(value);
362
+ return limitSummary(normalized.length === 0 ? fallback : normalized, limit);
363
+ }
364
+ function boundedOptionalText(value, limit) {
365
+ if (value === void 0) {
366
+ return void 0;
367
+ }
368
+ const normalized = stripInternalMarkup(value);
369
+ return normalized.length === 0 ? void 0 : limitSummary(normalized, limit);
370
+ }
371
+ function boundedOptionalMessageContent(value, limit) {
372
+ if (value === void 0) {
373
+ return void 0;
374
+ }
375
+ const normalized = removeInternalMarkup(value).trim();
376
+ return normalized.length === 0 ? void 0 : limitSummary(normalized, limit);
377
+ }
378
+ function appendSourceEventId(sourceEventIds, eventId) {
379
+ return [...sourceEventIds, eventId].slice(-CONVERSATION_SOURCE_ID_LIMIT);
380
+ }
381
+ function appendOptionalSourceId(sourceIds, sourceId) {
382
+ if (sourceId === void 0) {
383
+ return sourceIds === void 0 ? void 0 : [...sourceIds];
384
+ }
385
+ return [...sourceIds ?? [], sourceId].filter((candidate, index, all) => all.indexOf(candidate) === index).slice(-CONVERSATION_SOURCE_ID_LIMIT);
386
+ }
387
+ function appendAssistantContent(existing, next) {
388
+ const combined = existing.length > 0 && next.length > 0 ? `${existing}
389
+
390
+ ${next}` : existing || next;
391
+ if (combined.length <= 5e5) {
392
+ return combined;
393
+ }
394
+ return `\u2026${combined.slice(combined.length - 499999)}`;
395
+ }
396
+ function boundAttachment(attachment) {
397
+ return {
398
+ ...attachment,
399
+ name: boundedText(attachment.name, 1024, "Attachment"),
400
+ ...attachment.mimeType === void 0 ? {} : {
401
+ mimeType: boundedText(
402
+ attachment.mimeType,
403
+ 256,
404
+ "application/octet-stream"
405
+ )
406
+ },
407
+ ...attachment.uri === void 0 ? {} : { uri: boundedText(attachment.uri, 32768, "unknown") }
408
+ };
409
+ }
410
+ function summarizeUnknown(value) {
411
+ if (typeof value === "string") {
412
+ return limitSummary(stripInternalMarkup(value));
413
+ }
414
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
415
+ return String(value);
416
+ }
417
+ if (value === null) {
418
+ return "null";
419
+ }
420
+ try {
421
+ return limitSummary(stripInternalMarkup(JSON.stringify(value)));
422
+ } catch {
423
+ return "[unserializable]";
424
+ }
425
+ }
426
+ function isDisplayableHumanMessage(event) {
427
+ if (event.type !== "user.message" || event.agentId !== void 0) {
428
+ return false;
429
+ }
430
+ const source = event.data.source;
431
+ return event.data.isAutopilotContinuation !== true && (source === void 0 || source === "user" || source === "mobile");
432
+ }
433
+ function projectAttachment(attachment) {
434
+ switch (attachment.type) {
435
+ case "file":
436
+ return {
437
+ kind: attachment.mimeType?.startsWith("image/") ? "image" : "file",
438
+ name: attachment.displayName,
439
+ ...attachment.mimeType === void 0 ? {} : { mimeType: attachment.mimeType },
440
+ uri: attachment.path
441
+ };
442
+ case "directory":
443
+ return {
444
+ kind: "file",
445
+ name: attachment.displayName,
446
+ uri: attachment.path
447
+ };
448
+ case "selection":
449
+ return {
450
+ kind: "selection",
451
+ name: attachment.displayName,
452
+ uri: attachment.filePath
453
+ };
454
+ case "github_reference":
455
+ return {
456
+ kind: "github-reference",
457
+ name: attachment.title,
458
+ uri: attachment.url
459
+ };
460
+ case "github_commit":
461
+ return {
462
+ kind: "github-reference",
463
+ name: attachment.message,
464
+ uri: attachment.url
465
+ };
466
+ case "github_release":
467
+ return {
468
+ kind: "github-reference",
469
+ name: attachment.name,
470
+ uri: attachment.url
471
+ };
472
+ case "github_actions_job":
473
+ return {
474
+ kind: "github-reference",
475
+ name: attachment.jobName,
476
+ uri: attachment.url
477
+ };
478
+ case "github_repository":
479
+ return {
480
+ kind: "github-reference",
481
+ name: `${attachment.repo.owner}/${attachment.repo.name}`,
482
+ uri: attachment.url
483
+ };
484
+ case "github_file_diff":
485
+ return {
486
+ kind: "github-reference",
487
+ name: "GitHub file diff",
488
+ uri: attachment.url
489
+ };
490
+ case "github_tree_comparison":
491
+ return {
492
+ kind: "github-reference",
493
+ name: "GitHub tree comparison",
494
+ uri: attachment.url
495
+ };
496
+ case "github_url":
497
+ return {
498
+ kind: "github-reference",
499
+ name: attachment.url,
500
+ uri: attachment.url
501
+ };
502
+ case "github_file":
503
+ case "github_snippet":
504
+ return {
505
+ kind: "github-reference",
506
+ name: attachment.path,
507
+ uri: attachment.url
508
+ };
509
+ case "blob":
510
+ return {
511
+ kind: attachment.mimeType.startsWith("image/") ? "image" : "file",
512
+ name: attachment.displayName ?? "Attachment",
513
+ mimeType: attachment.mimeType
514
+ };
515
+ case "extension_context":
516
+ return {
517
+ kind: "unknown",
518
+ name: attachment.title
519
+ };
520
+ }
521
+ }
522
+ function projectCitation(source) {
523
+ if (source.url !== void 0) {
524
+ return {
525
+ kind: "github-reference",
526
+ name: source.title ?? source.path ?? source.id,
527
+ uri: source.url
528
+ };
529
+ }
530
+ if (source.path !== void 0) {
531
+ return {
532
+ kind: "file",
533
+ name: source.title ?? source.path,
534
+ uri: source.path
535
+ };
536
+ }
537
+ return {
538
+ kind: "unknown",
539
+ name: source.title ?? source.id
540
+ };
541
+ }
542
+ function normalizeImageMimeType(value) {
543
+ return value.toLowerCase() === "image/jpg" ? "image/jpeg" : value.toLowerCase();
544
+ }
545
+ function hasImageSignature(bytes, mimeType) {
546
+ switch (mimeType) {
547
+ case "image/jpeg":
548
+ return bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
549
+ case "image/png":
550
+ return bytes.length >= 8 && bytes.subarray(0, 8).equals(
551
+ Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
552
+ );
553
+ case "image/webp":
554
+ return bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP";
555
+ case "image/gif":
556
+ return bytes.length >= 6 && (bytes.subarray(0, 6).toString("ascii") === "GIF87a" || bytes.subarray(0, 6).toString("ascii") === "GIF89a");
557
+ default:
558
+ return false;
559
+ }
560
+ }
561
+ function decodeImageBase64(value, expectedMimeType) {
562
+ const dataUrlMatch = /^data:([^;,]+);base64,(.*)$/isu.exec(value);
563
+ if (dataUrlMatch !== null && normalizeImageMimeType(dataUrlMatch[1] ?? "") !== expectedMimeType) {
564
+ return void 0;
565
+ }
566
+ const payload = dataUrlMatch?.[2] ?? value;
567
+ const normalized = payload.replaceAll("-", "+").replaceAll("_", "/");
568
+ if (normalized.length === 0 || normalized.length % 4 === 1 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(normalized)) {
569
+ return void 0;
570
+ }
571
+ const bytes = Buffer.from(normalized, "base64");
572
+ return bytes.toString("base64").replace(/=+$/u, "") === normalized.replace(/=+$/u, "") ? bytes : void 0;
573
+ }
574
+ function projectInlineToolImage(data, mimeTypeValue, name, expectedByteLength, source = "binary-result") {
575
+ const mimeType = normalizeImageMimeType(mimeTypeValue);
576
+ const metadata = {
577
+ kind: "image",
578
+ name: boundedText(name, 1024, "Copilot image"),
579
+ mimeType: boundedText(mimeType, 256, "application/octet-stream")
580
+ };
581
+ if (!SUPPORTED_IMAGE_MIME_TYPES.has(mimeType)) {
582
+ return {
583
+ attachment: metadata,
584
+ failure: { source, reason: "mime-unsupported", mimeType }
585
+ };
586
+ }
587
+ const bytes = decodeImageBase64(data, mimeType);
588
+ if (bytes === void 0) {
589
+ return {
590
+ attachment: metadata,
591
+ failure: { source, reason: "data-invalid", mimeType }
592
+ };
593
+ }
594
+ if (bytes.length === 0) {
595
+ return {
596
+ attachment: metadata,
597
+ failure: { source, reason: "data-empty", mimeType, actualByteLength: 0 }
598
+ };
599
+ }
600
+ if (bytes.length > MAX_INLINE_IMAGE_BYTES) {
601
+ return {
602
+ attachment: metadata,
603
+ failure: {
604
+ source,
605
+ reason: "data-too-large",
606
+ mimeType,
607
+ actualByteLength: bytes.length
608
+ }
609
+ };
610
+ }
611
+ if (expectedByteLength !== void 0 && bytes.length !== expectedByteLength) {
612
+ return {
613
+ attachment: metadata,
614
+ failure: {
615
+ source,
616
+ reason: "length-mismatch",
617
+ mimeType,
618
+ expectedByteLength,
619
+ actualByteLength: bytes.length
620
+ }
621
+ };
622
+ }
623
+ if (!hasImageSignature(bytes, mimeType)) {
624
+ return {
625
+ attachment: metadata,
626
+ failure: {
627
+ source,
628
+ reason: "signature-mismatch",
629
+ mimeType,
630
+ actualByteLength: bytes.length
631
+ }
632
+ };
633
+ }
634
+ return {
635
+ attachment: {
636
+ ...metadata,
637
+ data: bytes.toString("base64"),
638
+ byteLength: bytes.length,
639
+ sha256: createHash2("sha256").update(bytes).digest("hex")
640
+ }
641
+ };
642
+ }
643
+ function projectToolBinaryResult(result, name, assets) {
644
+ if (result.type !== "image") {
645
+ return void 0;
646
+ }
647
+ if ("data" in result) {
648
+ return projectInlineToolImage(
649
+ result.data,
650
+ result.mimeType,
651
+ result.description ?? name
652
+ );
653
+ }
654
+ if ("assetId" in result) {
655
+ const asset = assets.get(result.assetId);
656
+ if (asset === void 0 || asset.data.type !== "image") {
657
+ return {
658
+ attachment: {
659
+ kind: "image",
660
+ name: boundedText(result.description ?? name, 1024, "Copilot image"),
661
+ mimeType: boundedText(
662
+ normalizeImageMimeType(result.mimeType),
663
+ 256,
664
+ "application/octet-stream"
665
+ )
666
+ },
667
+ failure: {
668
+ source: "binary-asset",
669
+ reason: "asset-missing",
670
+ mimeType: normalizeImageMimeType(result.mimeType),
671
+ expectedByteLength: result.byteLength
672
+ }
673
+ };
674
+ }
675
+ const projected = projectInlineToolImage(
676
+ asset.data.data,
677
+ asset.data.mimeType,
678
+ result.description ?? asset.data.description ?? name,
679
+ void 0,
680
+ "binary-asset"
681
+ );
682
+ const attachment = projected.attachment;
683
+ if (projected.failure !== void 0) {
684
+ return { ...projected, sourceEventId: asset.eventId, sourceTimestamp: asset.timestamp };
685
+ }
686
+ if (attachment.sha256 !== void 0 && result.assetId !== `sha256:${attachment.sha256}`) {
687
+ return {
688
+ attachment: {
689
+ kind: "image",
690
+ name: attachment.name,
691
+ ...attachment.mimeType === void 0 ? {} : { mimeType: attachment.mimeType }
692
+ },
693
+ sourceEventId: asset.eventId,
694
+ sourceTimestamp: asset.timestamp,
695
+ failure: {
696
+ source: "binary-asset",
697
+ reason: "asset-hash-mismatch",
698
+ mimeType: attachment.mimeType ?? "application/octet-stream",
699
+ expectedByteLength: result.byteLength,
700
+ ...attachment.byteLength === void 0 ? {} : { actualByteLength: attachment.byteLength }
701
+ }
702
+ };
703
+ }
704
+ return {
705
+ attachment,
706
+ sourceEventId: asset.eventId,
707
+ sourceTimestamp: asset.timestamp
708
+ };
709
+ }
710
+ return {
711
+ attachment: {
712
+ kind: "image",
713
+ name: boundedText(result.description ?? name, 1024, "Copilot image"),
714
+ mimeType: boundedText(
715
+ normalizeImageMimeType(result.mimeType),
716
+ 256,
717
+ "application/octet-stream"
718
+ )
719
+ },
720
+ failure: {
721
+ source: "binary-result",
722
+ reason: "asset-missing",
723
+ mimeType: normalizeImageMimeType(result.mimeType),
724
+ expectedByteLength: result.byteLength
725
+ }
726
+ };
727
+ }
728
+ function projectToolContentImage(image, name) {
729
+ return projectInlineToolImage(
730
+ image.data,
731
+ image.mimeType,
732
+ name,
733
+ void 0,
734
+ "content"
735
+ );
736
+ }
737
+ function permissionName(kind) {
738
+ const words = kind.replaceAll("_", " ").replaceAll("-", " ");
739
+ return `${words.charAt(0).toUpperCase()}${words.slice(1)} permission`;
740
+ }
741
+ var ConversationToolLifecycles = class {
742
+ #starts = /* @__PURE__ */ new Map();
743
+ get(toolCallId) {
744
+ return this.#starts.get(toolCallId);
745
+ }
746
+ remember(toolCallId, start) {
747
+ const previous = this.#starts.get(toolCallId);
748
+ const lifecycle = previous?.sourceEventId === start.sourceEventId && !start.hasTurnContext ? previous : start;
749
+ this.#starts.delete(toolCallId);
750
+ this.#starts.set(toolCallId, lifecycle);
751
+ while (this.#starts.size > 1024) {
752
+ const oldest = this.#starts.keys().next().value;
753
+ if (oldest === void 0) break;
754
+ this.#starts.delete(oldest);
755
+ }
756
+ return lifecycle;
757
+ }
758
+ clear() {
759
+ this.#starts.clear();
760
+ }
761
+ };
762
+ var ConversationNormalizer = class {
763
+ constructor(onImageFailure, toolLifecycles = new ConversationToolLifecycles(), resolveInputProvenance) {
764
+ this.onImageFailure = onImageFailure;
765
+ this.toolLifecycles = toolLifecycles;
766
+ this.resolveInputProvenance = resolveInputProvenance;
767
+ }
768
+ onImageFailure;
769
+ toolLifecycles;
770
+ resolveInputProvenance;
771
+ #items = /* @__PURE__ */ new Map();
772
+ #binaryAssets = /* @__PURE__ */ new Map();
773
+ #pendingToolImages = /* @__PURE__ */ new Map();
774
+ #toolTurns = /* @__PURE__ */ new Map();
775
+ #taskCompletionToolCalls = /* @__PURE__ */ new Map();
776
+ #subagentTurns = /* @__PURE__ */ new Map();
777
+ #permissionTurns = /* @__PURE__ */ new Map();
778
+ #loopTurns = /* @__PURE__ */ new Map();
779
+ #currentTurnId;
780
+ #hasCurrentTurnContext = false;
781
+ #reportedImageFailures = /* @__PURE__ */ new Set();
782
+ apply(events) {
783
+ const changed = [];
784
+ for (const event of events) {
785
+ if (event.type === "session.binary_asset" && event.data.type === "image") {
786
+ this.#binaryAssets.set(event.data.assetId, {
787
+ eventId: event.id,
788
+ timestamp: event.timestamp,
789
+ data: event.data
790
+ });
791
+ }
792
+ if (event.type === "tool.execution_start" && event.data.toolName === TASK_COMPLETE_TOOL_NAME) {
793
+ this.#taskCompletionToolCalls.set(
794
+ event.data.toolCallId,
795
+ event.agentId !== void 0 || event.data.parentToolCallId !== void 0 || this.#taskCompletionToolCalls.get(event.data.toolCallId) === "subagent" ? "subagent" : "assistant"
796
+ );
797
+ }
798
+ }
799
+ for (const event of events) {
800
+ if (event.type === "session.binary_asset") {
801
+ const pending = this.#pendingToolImages.get(event.data.assetId);
802
+ if (pending !== void 0) {
803
+ for (const projection of pending.values()) {
804
+ if (this.#unresolvedToolImageAssetIds(projection.event).length === 0) {
805
+ this.#removePendingToolImage(projection.event);
806
+ changed.push(
807
+ ...this.#projectToolImages(
808
+ projection.event,
809
+ projection.turnId,
810
+ projection.toolName
811
+ )
812
+ );
813
+ }
814
+ }
815
+ }
816
+ continue;
817
+ }
818
+ const inputProvenance = event.type === "user.message" && event.agentId === void 0 && event.data.isAutopilotContinuation !== true ? this.resolveInputProvenance?.(event) : void 0;
819
+ if (event.type === "user.message" && (inputProvenance !== void 0 || isDisplayableHumanMessage(event))) {
820
+ const turnId = protocolId(event.data.interactionId ?? event.id);
821
+ const item = {
822
+ id: scopedId("message", event.id),
823
+ revision: 1,
824
+ turnId,
825
+ timestamp: event.timestamp,
826
+ ...inputProvenance?.actor === "subagent" ? {
827
+ kind: "assistant-message",
828
+ actor: "subagent",
829
+ agentId: inputProvenance.agentId,
830
+ final: true,
831
+ sourceMessageIds: [event.id]
832
+ } : { kind: "human-message", actor: "human" },
833
+ sourceEventIds: [event.id],
834
+ ...event.data.interactionId === void 0 ? {} : { sourceInteractionIds: [protocolId(event.data.interactionId)] },
835
+ content: limitSummary(event.data.content, 1e5),
836
+ attachments: event.data.attachments?.slice(0, 100).map(projectAttachment).map(boundAttachment) ?? []
837
+ };
838
+ this.#currentTurnId = turnId;
839
+ this.#hasCurrentTurnContext = true;
840
+ this.#items.set(item.id, item);
841
+ changed.push(item);
842
+ continue;
843
+ }
844
+ if (event.type === "assistant.turn_start") {
845
+ if (event.agentId !== void 0) {
846
+ this.#loopTurns.set(event.data.turnId, {
847
+ turnId: protocolId(
848
+ this.#currentTurnId ?? event.data.interactionId ?? event.data.turnId
849
+ ),
850
+ hasTurnContext: this.#currentTurnId === void 0 ? event.data.interactionId !== void 0 : this.#hasCurrentTurnContext
851
+ });
852
+ continue;
853
+ }
854
+ const turnId = protocolId(
855
+ event.data.interactionId ?? this.#currentTurnId ?? event.data.turnId
856
+ );
857
+ const hasTurnContext = event.data.interactionId !== void 0 || this.#hasCurrentTurnContext;
858
+ this.#loopTurns.set(event.data.turnId, { turnId, hasTurnContext });
859
+ this.#currentTurnId = turnId;
860
+ this.#hasCurrentTurnContext = hasTurnContext;
861
+ continue;
862
+ }
863
+ if (event.type === "skill.invoked") {
864
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
865
+ const step = {
866
+ id: scopedId("skill", event.id),
867
+ kind: "skill",
868
+ name: boundedText(event.data.name, 256, "Skill"),
869
+ ...boundedOptionalText(event.data.description, 2048) === void 0 ? {} : {
870
+ description: boundedOptionalText(
871
+ event.data.description,
872
+ 2048
873
+ )
874
+ },
875
+ status: "completed",
876
+ ...boundedOptionalText(event.data.source, 256) === void 0 ? {} : { source: boundedOptionalText(event.data.source, 256) }
877
+ };
878
+ const item = this.#upsertActivityStep(
879
+ event.id,
880
+ event.timestamp,
881
+ turnId,
882
+ step
883
+ );
884
+ changed.push(item);
885
+ continue;
886
+ }
887
+ if (event.type === "tool.execution_start") {
888
+ const lifecycle = this.toolLifecycles.remember(event.data.toolCallId, {
889
+ sourceEventId: protocolId(event.id),
890
+ timestamp: event.timestamp,
891
+ turnId: this.#resolveTurnId(
892
+ event.data.turnId,
893
+ this.#currentTurnId ?? protocolId(event.id)
894
+ ),
895
+ hasTurnContext: event.data.turnId === void 0 ? this.#hasCurrentTurnContext : this.#loopTurns.get(event.data.turnId)?.hasTurnContext ?? false,
896
+ name: boundedText(event.data.toolName, 256, "Tool"),
897
+ actor: event.agentId !== void 0 || event.data.parentToolCallId !== void 0 ? "subagent" : "assistant"
898
+ });
899
+ const turnId = lifecycle.turnId;
900
+ this.#toolTurns.set(event.data.toolCallId, turnId);
901
+ if (event.data.toolName === TASK_COMPLETE_TOOL_NAME) {
902
+ continue;
903
+ }
904
+ const step = {
905
+ id: scopedId("tool", event.data.toolCallId),
906
+ kind: "tool",
907
+ name: lifecycle.name,
908
+ status: "running"
909
+ };
910
+ changed.push(
911
+ this.#upsertActivityStep(
912
+ protocolId(event.id),
913
+ event.timestamp,
914
+ turnId,
915
+ step
916
+ )
917
+ );
918
+ continue;
919
+ }
920
+ if (event.type === "tool.execution_complete") {
921
+ const lifecycle = this.toolLifecycles.get(event.data.toolCallId);
922
+ const turnId = lifecycle?.turnId ?? this.#resolveTurnId(
923
+ event.data.turnId,
924
+ this.#toolTurns.get(event.data.toolCallId) ?? this.#currentTurnId ?? event.id
925
+ );
926
+ if (this.#taskCompletionToolCalls.has(event.data.toolCallId) || lifecycle?.name === TASK_COMPLETE_TOOL_NAME || event.data.toolDescription?.name === TASK_COMPLETE_TOOL_NAME) {
927
+ if (lifecycle !== void 0) {
928
+ this.#taskCompletionToolCalls.set(event.data.toolCallId, lifecycle.actor);
929
+ }
930
+ const taskCompletion = this.#projectTaskCompletion(event, turnId);
931
+ if (taskCompletion !== void 0) {
932
+ changed.push(taskCompletion);
933
+ continue;
934
+ }
935
+ const step2 = {
936
+ id: scopedId("tool", event.data.toolCallId),
937
+ kind: "tool",
938
+ name: TASK_COMPLETE_TOOL_NAME,
939
+ status: event.data.success ? "completed" : "failed"
940
+ };
941
+ changed.push(
942
+ this.#upsertActivityStep(
943
+ event.id,
944
+ event.timestamp,
945
+ turnId,
946
+ step2
947
+ )
948
+ );
949
+ continue;
950
+ }
951
+ const existingStep = this.#findActivityStep(
952
+ scopedId("tool", event.data.toolCallId)
953
+ );
954
+ const step = {
955
+ id: scopedId("tool", event.data.toolCallId),
956
+ kind: "tool",
957
+ name: lifecycle?.name ?? (existingStep?.kind === "tool" ? existingStep.name : boundedText(event.data.toolDescription?.name ?? "unknown-tool", 256, "Tool")),
958
+ status: event.data.success ? "completed" : "failed"
959
+ };
960
+ changed.push(
961
+ this.#upsertActivityStep(
962
+ event.id,
963
+ event.timestamp,
964
+ turnId,
965
+ step,
966
+ lifecycle
967
+ )
968
+ );
969
+ const toolName = lifecycle?.name ?? existingStep?.name ?? "Copilot tool";
970
+ const unresolvedAssetIds = this.#unresolvedToolImageAssetIds(event);
971
+ if (unresolvedAssetIds.length > 0) {
972
+ const projection = { event, turnId, toolName };
973
+ for (const assetId of unresolvedAssetIds) {
974
+ let projections = this.#pendingToolImages.get(assetId);
975
+ if (projections === void 0) {
976
+ projections = /* @__PURE__ */ new Map();
977
+ this.#pendingToolImages.set(assetId, projections);
978
+ }
979
+ projections.set(event.data.toolCallId, projection);
980
+ }
981
+ } else {
982
+ changed.push(
983
+ ...this.#projectToolImages(event, turnId, toolName)
984
+ );
985
+ }
986
+ continue;
987
+ }
988
+ if (event.type === "subagent.started") {
989
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
990
+ const subagentId = event.agentId ?? event.data.toolCallId;
991
+ this.#subagentTurns.set(subagentId, turnId);
992
+ const step = {
993
+ id: scopedId("subagent", subagentId),
994
+ kind: "subagent",
995
+ name: boundedText(event.data.agentDisplayName, 256, "Subagent"),
996
+ status: "running",
997
+ description: boundedText(
998
+ event.data.agentDescription,
999
+ 2048,
1000
+ "Subagent task"
1001
+ ),
1002
+ ...boundedOptionalText(event.data.model, 256) === void 0 ? {} : { model: boundedOptionalText(event.data.model, 256) }
1003
+ };
1004
+ changed.push(
1005
+ this.#upsertActivityStep(
1006
+ event.id,
1007
+ event.timestamp,
1008
+ turnId,
1009
+ step
1010
+ )
1011
+ );
1012
+ continue;
1013
+ }
1014
+ if (event.type === "subagent.completed") {
1015
+ const subagentId = event.agentId ?? event.data.toolCallId;
1016
+ const turnId = this.#subagentTurns.get(subagentId) ?? this.#currentTurnId ?? protocolId(event.id);
1017
+ const existingStep = this.#findActivityStep(
1018
+ scopedId("subagent", subagentId)
1019
+ );
1020
+ const step = {
1021
+ id: scopedId("subagent", subagentId),
1022
+ kind: "subagent",
1023
+ name: boundedText(event.data.agentDisplayName, 256, "Subagent"),
1024
+ status: "completed",
1025
+ ...existingStep?.kind === "subagent" && existingStep.description !== void 0 ? { description: existingStep.description } : {},
1026
+ ...event.data.model === void 0 ? existingStep?.kind === "subagent" && existingStep.model !== void 0 ? { model: existingStep.model } : {} : {
1027
+ model: boundedText(event.data.model, 256, "unknown-model")
1028
+ },
1029
+ ...event.data.durationMs === void 0 ? {} : { durationMs: event.data.durationMs }
1030
+ };
1031
+ changed.push(
1032
+ this.#upsertActivityStep(
1033
+ event.id,
1034
+ event.timestamp,
1035
+ turnId,
1036
+ step
1037
+ )
1038
+ );
1039
+ continue;
1040
+ }
1041
+ if (event.type === "subagent.failed") {
1042
+ const subagentId = event.agentId ?? event.data.toolCallId;
1043
+ const turnId = this.#subagentTurns.get(subagentId) ?? this.#currentTurnId ?? protocolId(event.id);
1044
+ const existingStep = this.#findActivityStep(
1045
+ scopedId("subagent", subagentId)
1046
+ );
1047
+ const step = {
1048
+ id: scopedId("subagent", subagentId),
1049
+ kind: "subagent",
1050
+ name: boundedText(event.data.agentDisplayName, 256, "Subagent"),
1051
+ status: "failed",
1052
+ ...existingStep?.kind === "subagent" && existingStep.description !== void 0 ? { description: existingStep.description } : {},
1053
+ ...event.data.model === void 0 ? existingStep?.kind === "subagent" && existingStep.model !== void 0 ? { model: existingStep.model } : {} : {
1054
+ model: boundedText(event.data.model, 256, "unknown-model")
1055
+ },
1056
+ ...event.data.durationMs === void 0 ? {} : { durationMs: event.data.durationMs },
1057
+ error: summarizeUnknown(event.data.error)
1058
+ };
1059
+ changed.push(
1060
+ this.#upsertActivityStep(
1061
+ event.id,
1062
+ event.timestamp,
1063
+ turnId,
1064
+ step
1065
+ )
1066
+ );
1067
+ continue;
1068
+ }
1069
+ if (event.type === "permission.requested") {
1070
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1071
+ const request = event.data.permissionRequest;
1072
+ this.#permissionTurns.set(event.data.requestId, turnId);
1073
+ const step = {
1074
+ id: scopedId("permission", event.data.requestId),
1075
+ kind: "permission",
1076
+ name: permissionName(request.kind),
1077
+ status: "running",
1078
+ ...request.kind === "shell" ? {
1079
+ description: boundedText(
1080
+ request.intention,
1081
+ 2048,
1082
+ "Shell command"
1083
+ )
1084
+ } : {}
1085
+ };
1086
+ changed.push(
1087
+ this.#upsertActivityStep(
1088
+ event.id,
1089
+ event.timestamp,
1090
+ turnId,
1091
+ step
1092
+ )
1093
+ );
1094
+ continue;
1095
+ }
1096
+ if (event.type === "permission.completed") {
1097
+ const turnId = this.#permissionTurns.get(event.data.requestId) ?? this.#currentTurnId ?? protocolId(event.id);
1098
+ const stepId = scopedId("permission", event.data.requestId);
1099
+ const existingStep = this.#findActivityStep(stepId);
1100
+ const result = event.data.result.kind;
1101
+ const failed = result === "cancelled" || result.startsWith("denied");
1102
+ const step = {
1103
+ id: stepId,
1104
+ kind: "permission",
1105
+ name: existingStep?.kind === "permission" ? existingStep.name : "Permission",
1106
+ status: failed ? "failed" : "completed",
1107
+ ...existingStep?.kind === "permission" && existingStep.description !== void 0 ? { description: existingStep.description } : {},
1108
+ resultSummary: result
1109
+ };
1110
+ changed.push(
1111
+ this.#upsertActivityStep(
1112
+ event.id,
1113
+ event.timestamp,
1114
+ turnId,
1115
+ step
1116
+ )
1117
+ );
1118
+ continue;
1119
+ }
1120
+ if (event.type === "user_input.requested") {
1121
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1122
+ this.#permissionTurns.set(event.data.requestId, turnId);
1123
+ const step = {
1124
+ id: scopedId("permission", event.data.requestId),
1125
+ kind: "permission",
1126
+ name: "User input",
1127
+ status: "running",
1128
+ description: boundedText(event.data.question, 2048, "User input")
1129
+ };
1130
+ changed.push(
1131
+ this.#upsertActivityStep(
1132
+ event.id,
1133
+ event.timestamp,
1134
+ turnId,
1135
+ step
1136
+ )
1137
+ );
1138
+ continue;
1139
+ }
1140
+ if (event.type === "user_input.completed") {
1141
+ const turnId = this.#permissionTurns.get(event.data.requestId) ?? this.#currentTurnId ?? protocolId(event.id);
1142
+ const stepId = scopedId("permission", event.data.requestId);
1143
+ const existingStep = this.#findActivityStep(stepId);
1144
+ const step = {
1145
+ id: stepId,
1146
+ kind: "permission",
1147
+ name: "User input",
1148
+ status: "completed",
1149
+ ...existingStep?.kind === "permission" && existingStep.description !== void 0 ? { description: existingStep.description } : {},
1150
+ resultSummary: event.data.answer === void 0 ? "completed" : boundedText(event.data.answer, 4096, "completed")
1151
+ };
1152
+ changed.push(
1153
+ this.#upsertActivityStep(
1154
+ event.id,
1155
+ event.timestamp,
1156
+ turnId,
1157
+ step
1158
+ )
1159
+ );
1160
+ continue;
1161
+ }
1162
+ if (event.type === "session.warning") {
1163
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1164
+ const warningType = event.data.warningType.replaceAll("_", " ");
1165
+ const warningLabel = boundedText(warningType, 240, "Session");
1166
+ const item = {
1167
+ id: scopedId("notice", event.id),
1168
+ kind: "notice",
1169
+ revision: 1,
1170
+ turnId,
1171
+ timestamp: event.timestamp,
1172
+ actor: event.agentId === void 0 ? "system" : "subagent",
1173
+ sourceEventIds: [event.id],
1174
+ severity: "warning",
1175
+ title: limitSummary(
1176
+ `${warningLabel.charAt(0).toUpperCase()}${warningLabel.slice(1)} warning`,
1177
+ 256
1178
+ ),
1179
+ content: boundedText(event.data.message, 4096, "Session warning")
1180
+ };
1181
+ this.#items.set(item.id, item);
1182
+ changed.push(item);
1183
+ continue;
1184
+ }
1185
+ if (event.type === "session.error") {
1186
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1187
+ const errorType = event.data.errorType.replaceAll("_", " ");
1188
+ const errorLabel = boundedText(errorType, 240, "Session");
1189
+ const title = `${errorLabel.charAt(0).toUpperCase()}${errorLabel.slice(1)} error`;
1190
+ const item = {
1191
+ id: scopedId("notice", event.id),
1192
+ kind: "notice",
1193
+ revision: 1,
1194
+ turnId,
1195
+ timestamp: event.timestamp,
1196
+ actor: event.agentId === void 0 ? "system" : "subagent",
1197
+ sourceEventIds: [event.id],
1198
+ severity: "error",
1199
+ title: limitSummary(
1200
+ event.data.errorCode === void 0 ? title : `${title} \xB7 ${event.data.errorCode}`,
1201
+ 256
1202
+ ),
1203
+ content: boundedText(event.data.message, 4096, "Session error")
1204
+ };
1205
+ this.#items.set(item.id, item);
1206
+ changed.push(item);
1207
+ continue;
1208
+ }
1209
+ if (event.type === "system.notification" && event.data.kind.type === "shell_completed") {
1210
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1211
+ const kind = event.data.kind;
1212
+ const description = boundedText(
1213
+ kind.description ?? `Shell ${kind.shellId}`,
1214
+ 4096,
1215
+ "Shell completed"
1216
+ );
1217
+ const item = {
1218
+ id: scopedId("notice", event.id),
1219
+ kind: "notice",
1220
+ revision: 1,
1221
+ turnId,
1222
+ timestamp: event.timestamp,
1223
+ actor: event.agentId === void 0 ? "system" : "subagent",
1224
+ sourceEventIds: [event.id],
1225
+ severity: kind.exitCode === void 0 || kind.exitCode === 0 ? "success" : "error",
1226
+ title: "Shell completed",
1227
+ content: kind.exitCode === void 0 ? description : limitSummary(`${description} \xB7 exit ${kind.exitCode}`, 4096)
1228
+ };
1229
+ this.#items.set(item.id, item);
1230
+ changed.push(item);
1231
+ continue;
1232
+ }
1233
+ if (event.type === "session.plan_changed") {
1234
+ const turnId = this.#currentTurnId ?? protocolId(event.id);
1235
+ const id = scopedId("plan", turnId);
1236
+ const existing = this.#items.get(id);
1237
+ const operation = event.data.operation === "create" ? "created" : event.data.operation === "update" ? "updated" : "deleted";
1238
+ const item = {
1239
+ id,
1240
+ kind: "plan-update",
1241
+ revision: eventRevision(
1242
+ event.timestamp,
1243
+ existing?.kind === "plan-update" ? existing.revision : void 0
1244
+ ),
1245
+ turnId,
1246
+ timestamp: existing?.kind === "plan-update" ? existing.timestamp : event.timestamp,
1247
+ actor: event.agentId === void 0 ? "assistant" : "subagent",
1248
+ sourceEventIds: existing?.kind === "plan-update" ? appendSourceEventId(existing.sourceEventIds, event.id) : [event.id],
1249
+ operation,
1250
+ content: `Plan ${operation}`,
1251
+ todos: []
1252
+ };
1253
+ this.#items.set(id, item);
1254
+ changed.push(item);
1255
+ continue;
1256
+ }
1257
+ if (event.type === "assistant.message") {
1258
+ if (event.agentId !== void 0 || event.data.parentToolCallId !== void 0) {
1259
+ continue;
1260
+ }
1261
+ if (event.data.content.trim().length === 0 && (event.data.citations?.sources.length ?? 0) === 0) {
1262
+ continue;
1263
+ }
1264
+ const turnId = protocolId(
1265
+ event.data.interactionId ?? (event.data.turnId === void 0 ? this.#currentTurnId : this.#loopTurns.get(event.data.turnId)?.turnId) ?? event.data.turnId ?? event.id
1266
+ );
1267
+ const id = assistantMessageId(event.data);
1268
+ const existing = this.#items.get(id);
1269
+ const citationAttachments = event.data.citations?.sources.slice(0, 100).map(projectCitation).map(boundAttachment) ?? [];
1270
+ const attachments = [
1271
+ ...existing?.kind === "assistant-message" ? existing.attachments ?? [] : [],
1272
+ ...citationAttachments
1273
+ ].filter(
1274
+ (attachment, index, all) => all.findIndex(
1275
+ (candidate) => candidate.kind === attachment.kind && candidate.name === attachment.name && candidate.uri === attachment.uri
1276
+ ) === index
1277
+ ).slice(-100);
1278
+ const sourceMessageIds = appendOptionalSourceId(
1279
+ existing?.kind === "assistant-message" ? existing.sourceMessageIds : void 0,
1280
+ protocolId(event.data.messageId)
1281
+ );
1282
+ const sourceInteractionIds = appendOptionalSourceId(
1283
+ existing?.kind === "assistant-message" ? existing.sourceInteractionIds : void 0,
1284
+ event.data.interactionId === void 0 ? void 0 : protocolId(event.data.interactionId)
1285
+ );
1286
+ const item = {
1287
+ id,
1288
+ kind: "assistant-message",
1289
+ revision: eventRevision(
1290
+ event.timestamp,
1291
+ existing?.kind === "assistant-message" ? existing.revision : void 0
1292
+ ),
1293
+ turnId,
1294
+ timestamp: existing?.kind === "assistant-message" ? existing.timestamp : event.timestamp,
1295
+ actor: "assistant",
1296
+ sourceEventIds: existing?.kind === "assistant-message" ? appendSourceEventId(existing.sourceEventIds, event.id) : [event.id],
1297
+ ...sourceMessageIds === void 0 ? {} : { sourceMessageIds },
1298
+ ...sourceInteractionIds === void 0 ? {} : { sourceInteractionIds },
1299
+ content: appendAssistantContent(
1300
+ existing?.kind === "assistant-message" ? existing.content : "",
1301
+ event.data.content
1302
+ ),
1303
+ ...attachments.length === 0 ? {} : { attachments },
1304
+ ...event.data.model === void 0 ? existing?.kind === "assistant-message" && existing.model !== void 0 ? { model: existing.model } : {} : {
1305
+ model: boundedText(event.data.model, 256, "unknown-model")
1306
+ },
1307
+ final: event.data.chunkCount === void 0 || event.data.chunkIndex === void 0 || event.data.chunkIndex + 1 >= event.data.chunkCount
1308
+ };
1309
+ this.#currentTurnId = turnId;
1310
+ this.#hasCurrentTurnContext = event.data.interactionId !== void 0 || (event.data.turnId === void 0 ? this.#hasCurrentTurnContext : this.#loopTurns.get(event.data.turnId)?.hasTurnContext ?? false);
1311
+ this.#items.set(id, item);
1312
+ changed.push(item);
1313
+ }
1314
+ }
1315
+ return changed;
1316
+ }
1317
+ snapshot() {
1318
+ return [...this.#items.values()];
1319
+ }
1320
+ pendingImageAssetIds() {
1321
+ return [...this.#pendingToolImages.keys()];
1322
+ }
1323
+ #projectTaskCompletion(event, turnId) {
1324
+ if (!event.data.success) {
1325
+ return void 0;
1326
+ }
1327
+ const content = boundedOptionalMessageContent(
1328
+ event.data.result?.content,
1329
+ 4096
1330
+ );
1331
+ if (content === void 0) {
1332
+ return void 0;
1333
+ }
1334
+ const id = scopedId("notice", event.data.toolCallId);
1335
+ const existing = this.#items.get(id);
1336
+ const item = {
1337
+ id,
1338
+ kind: "notice",
1339
+ revision: eventRevision(
1340
+ event.timestamp,
1341
+ existing?.kind === "notice" ? existing.revision : void 0
1342
+ ),
1343
+ turnId,
1344
+ timestamp: existing?.kind === "notice" ? existing.timestamp : event.timestamp,
1345
+ actor: event.agentId !== void 0 || event.data.parentToolCallId !== void 0 || this.#taskCompletionToolCalls.get(event.data.toolCallId) === "subagent" || existing?.kind === "notice" && existing.actor === "subagent" ? "subagent" : "assistant",
1346
+ sourceEventIds: existing?.kind === "notice" ? appendSourceEventId(existing.sourceEventIds, event.id) : [event.id],
1347
+ severity: "success",
1348
+ title: "Task completed",
1349
+ content
1350
+ };
1351
+ this.#items.set(id, item);
1352
+ return item;
1353
+ }
1354
+ #unresolvedToolImageAssetIds(event) {
1355
+ return (event.data.result?.binaryResultsForLlm?.filter(
1356
+ (result) => result.type === "image" && "assetId" in result
1357
+ ).map((result) => result.assetId).filter((assetId) => !this.#binaryAssets.has(assetId)) ?? []).filter((assetId, index, all) => all.indexOf(assetId) === index);
1358
+ }
1359
+ #removePendingToolImage(event) {
1360
+ for (const [assetId, projections] of this.#pendingToolImages) {
1361
+ projections.delete(event.data.toolCallId);
1362
+ if (projections.size === 0) {
1363
+ this.#pendingToolImages.delete(assetId);
1364
+ }
1365
+ }
1366
+ }
1367
+ #projectToolImages(event, turnId, toolName) {
1368
+ const binaryImages = event.data.result?.binaryResultsForLlm?.map(
1369
+ (result, index) => projectToolBinaryResult(
1370
+ result,
1371
+ `${toolName} image ${index + 1}`,
1372
+ this.#binaryAssets
1373
+ )
1374
+ ).filter(
1375
+ (image) => image !== void 0
1376
+ ) ?? [];
1377
+ const contentImages = event.data.result?.contents?.filter(
1378
+ (content) => content.type === "image"
1379
+ ).map(
1380
+ (image, index) => projectToolContentImage(
1381
+ image,
1382
+ `${toolName} image ${binaryImages.length + index + 1}`
1383
+ )
1384
+ ) ?? [];
1385
+ const projectedImages = [...binaryImages, ...contentImages].filter(
1386
+ (image, index, all) => all.findIndex(
1387
+ (candidate) => [
1388
+ candidate.attachment.mimeType,
1389
+ candidate.attachment.sha256 ?? candidate.attachment.data ?? candidate.attachment.name
1390
+ ].join("\0") === [
1391
+ image.attachment.mimeType,
1392
+ image.attachment.sha256 ?? image.attachment.data ?? image.attachment.name
1393
+ ].join("\0")
1394
+ ) === index
1395
+ );
1396
+ for (const image of projectedImages) {
1397
+ if (image.failure === void 0) {
1398
+ continue;
1399
+ }
1400
+ const fingerprint = [
1401
+ event.data.toolCallId,
1402
+ image.failure.source,
1403
+ image.failure.reason,
1404
+ image.failure.mimeType,
1405
+ image.failure.expectedByteLength,
1406
+ image.failure.actualByteLength
1407
+ ].join("\0");
1408
+ if (this.#reportedImageFailures.has(fingerprint)) {
1409
+ continue;
1410
+ }
1411
+ this.#reportedImageFailures.add(fingerprint);
1412
+ this.onImageFailure?.({
1413
+ ...image.failure,
1414
+ toolCallId: event.data.toolCallId,
1415
+ toolName
1416
+ });
1417
+ }
1418
+ const imageGroups = splitToolImages(projectedImages);
1419
+ return imageGroups.map((imageGroup, groupIndex) => {
1420
+ const multipleGroups = imageGroups.length > 1;
1421
+ const id = scopedId(
1422
+ "tool-image",
1423
+ multipleGroups ? `${event.data.toolCallId}:${groupIndex + 1}` : event.data.toolCallId
1424
+ );
1425
+ const existing = this.#items.get(id);
1426
+ const imageItem = {
1427
+ id,
1428
+ kind: "assistant-message",
1429
+ // The same source events must produce the same revision in live and batched history reads.
1430
+ revision: Math.max(
1431
+ eventRevision(event.timestamp, existing?.kind === "assistant-message" ? existing.revision : void 0),
1432
+ ...imageGroup.flatMap((image) => image.sourceTimestamp === void 0 ? [] : [eventRevision(image.sourceTimestamp)])
1433
+ ),
1434
+ turnId,
1435
+ timestamp: existing?.kind === "assistant-message" ? existing.timestamp : event.timestamp,
1436
+ actor: event.agentId !== void 0 || event.data.parentToolCallId !== void 0 || this.toolLifecycles.get(event.data.toolCallId)?.actor === "subagent" ? "subagent" : "assistant",
1437
+ sourceEventIds: [
1438
+ ...imageGroup.flatMap(
1439
+ (image) => image.sourceEventId === void 0 ? [] : [image.sourceEventId]
1440
+ ),
1441
+ event.id
1442
+ ].filter(
1443
+ (sourceEventId, index, all) => all.indexOf(sourceEventId) === index
1444
+ ),
1445
+ content: "",
1446
+ attachments: imageGroup.map((image) => image.attachment),
1447
+ ...event.agentId === void 0 ? {} : { agentId: protocolId(event.agentId) },
1448
+ final: true
1449
+ };
1450
+ this.#items.set(imageItem.id, imageItem);
1451
+ return imageItem;
1452
+ });
1453
+ }
1454
+ #resolveTurnId(loopTurnId, fallback) {
1455
+ return protocolId(
1456
+ (loopTurnId === void 0 ? void 0 : this.#loopTurns.get(loopTurnId)?.turnId) ?? loopTurnId ?? fallback
1457
+ );
1458
+ }
1459
+ #findActivityStep(stepId) {
1460
+ const activity = this.#items.get(scopedId("activity", stepId));
1461
+ return activity?.kind === "activity-group" ? activity.steps[0] : void 0;
1462
+ }
1463
+ #upsertActivityStep(eventId, timestamp, turnId, step, lifecycle) {
1464
+ const id = scopedId("activity", step.id);
1465
+ const existing = this.#items.get(id);
1466
+ const existingGroup = existing?.kind === "activity-group" ? existing : void 0;
1467
+ const item = {
1468
+ id,
1469
+ kind: "activity-group",
1470
+ revision: eventRevision(
1471
+ timestamp,
1472
+ existingGroup?.revision ?? (lifecycle === void 0 ? void 0 : Date.parse(lifecycle.timestamp))
1473
+ ),
1474
+ turnId,
1475
+ timestamp: lifecycle?.timestamp ?? existingGroup?.timestamp ?? timestamp,
1476
+ actor: "assistant",
1477
+ sourceEventIds: existingGroup === void 0 ? lifecycle === void 0 ? [eventId] : appendSourceEventId([lifecycle.sourceEventId], eventId) : appendSourceEventId(existingGroup.sourceEventIds, eventId),
1478
+ status: step.status,
1479
+ steps: [{
1480
+ ...step,
1481
+ timestamp: lifecycle?.timestamp ?? existingGroup?.steps[0]?.timestamp ?? timestamp
1482
+ }]
1483
+ };
1484
+ this.#items.set(id, item);
1485
+ return item;
1486
+ }
1487
+ };
1488
+
1489
+ // src/session-runtime.ts
1490
+ import { randomUUID as randomUUID2 } from "node:crypto";
1491
+
1492
+ // src/timeline-read-flight.ts
1493
+ import { randomUUID } from "node:crypto";
1494
+ var MAX_IN_FLIGHT_KEYS = 32;
1495
+ var TimelineReadFlights = class {
1496
+ #reads = /* @__PURE__ */ new Map();
1497
+ get size() {
1498
+ return this.#reads.size;
1499
+ }
1500
+ invalidate() {
1501
+ this.#reads.clear();
1502
+ }
1503
+ invalidateContent() {
1504
+ for (const [key, flight] of this.#reads) {
1505
+ if (flight.state.reading) this.#reads.delete(key);
1506
+ }
1507
+ }
1508
+ read(options, load, report) {
1509
+ const key = JSON.stringify([
1510
+ options.cursor ?? null,
1511
+ options.pageSize,
1512
+ options.metadataOnly,
1513
+ options.assistantPreview === true
1514
+ ]);
1515
+ const existing = this.#reads.get(key);
1516
+ if (existing !== void 0) {
1517
+ report?.({
1518
+ readRef: existing.readRef,
1519
+ readStarts: 0,
1520
+ readJoins: 1,
1521
+ untrackedReads: 0,
1522
+ inFlightReads: this.size
1523
+ });
1524
+ return existing.promise;
1525
+ }
1526
+ const readRef = randomUUID();
1527
+ const state = { reading: false };
1528
+ const pending = load(() => {
1529
+ state.reading = true;
1530
+ });
1531
+ if (this.size >= MAX_IN_FLIGHT_KEYS) {
1532
+ report?.({
1533
+ readRef,
1534
+ readStarts: 1,
1535
+ readJoins: 0,
1536
+ untrackedReads: 1,
1537
+ inFlightReads: this.size
1538
+ });
1539
+ return pending;
1540
+ }
1541
+ const read = pending.finally(() => {
1542
+ if (this.#reads.get(key)?.promise === read) {
1543
+ this.#reads.delete(key);
1544
+ }
1545
+ });
1546
+ this.#reads.set(key, { readRef, promise: read, state });
1547
+ report?.({
1548
+ readRef,
1549
+ readStarts: 1,
1550
+ readJoins: 0,
1551
+ untrackedReads: 0,
1552
+ inFlightReads: this.size
1553
+ });
1554
+ return read;
1555
+ }
1556
+ };
1557
+
1558
+ // src/session-runtime.ts
1559
+ var SessionRuntime = class {
1560
+ workbench = {
1561
+ identity: randomUUID2(),
1562
+ activeMutations: 0,
1563
+ activeQuestions: 0,
1564
+ compacting: false
1565
+ };
1566
+ admitMutation() {
1567
+ if (this.workbench.compacting) {
1568
+ throw new Error("Session context compaction is in progress");
1569
+ }
1570
+ this.workbench.activeMutations += 1;
1571
+ return () => {
1572
+ this.workbench.activeMutations -= 1;
1573
+ };
1574
+ }
1575
+ timelineReads = new TimelineReadFlights();
1576
+ toolLifecycles = new ConversationToolLifecycles();
1577
+ connection = {
1578
+ generation: 0,
1579
+ session: void 0,
1580
+ reload: void 0,
1581
+ sendAwareReload: false,
1582
+ sendReady: false,
1583
+ sendInProgressCount: 0,
1584
+ sendCompletionWaiters: /* @__PURE__ */ new Set()
1585
+ };
1586
+ follower = {
1587
+ current: void 0,
1588
+ recoveryAttempt: 0,
1589
+ recoveryTimer: void 0
1590
+ };
1591
+ live = {
1592
+ recentEventIds: { ids: /* @__PURE__ */ new Set(), order: [] },
1593
+ normalizer: void 0,
1594
+ initializingEvents: void 0
1595
+ };
1596
+ activity = {
1597
+ activeRootTurn: false,
1598
+ busy: false,
1599
+ turnEndIdleTimer: void 0
1600
+ };
1601
+ plan = {
1602
+ timer: void 0,
1603
+ generation: 0,
1604
+ pending: void 0,
1605
+ run: void 0,
1606
+ snapshotRevision: 0
1607
+ };
1608
+ configuration = {
1609
+ transition: void 0
1610
+ };
1611
+ watched = false;
1612
+ stopFollower() {
1613
+ if (this.follower.current !== void 0) {
1614
+ this.follower.current.stopped = true;
1615
+ this.follower.current = void 0;
1616
+ }
1617
+ }
1618
+ clearFollowerRecovery() {
1619
+ if (this.follower.recoveryTimer !== void 0) {
1620
+ clearTimeout(this.follower.recoveryTimer);
1621
+ this.follower.recoveryTimer = void 0;
1622
+ }
1623
+ this.follower.recoveryAttempt = 0;
1624
+ }
1625
+ clearTurnEndIdleTimer() {
1626
+ if (this.activity.turnEndIdleTimer !== void 0) {
1627
+ clearTimeout(this.activity.turnEndIdleTimer);
1628
+ this.activity.turnEndIdleTimer = void 0;
1629
+ }
1630
+ }
1631
+ clearPlanSyncScheduling() {
1632
+ if (this.plan.timer !== void 0) {
1633
+ clearTimeout(this.plan.timer);
1634
+ this.plan.timer = void 0;
1635
+ }
1636
+ this.plan.pending = void 0;
1637
+ this.plan.generation = 0;
1638
+ }
1639
+ unwatch() {
1640
+ this.watched = false;
1641
+ this.clearPlanSyncScheduling();
1642
+ this.clearTurnEndIdleTimer();
1643
+ this.activity.activeRootTurn = false;
1644
+ this.stopFollower();
1645
+ this.clearFollowerRecovery();
1646
+ this.live.initializingEvents = void 0;
1647
+ }
1648
+ prepareReload(preserveTimelineState) {
1649
+ this.timelineReads.invalidate();
1650
+ this.connection.sendReady = false;
1651
+ this.stopFollower();
1652
+ if (!preserveTimelineState) {
1653
+ this.live.recentEventIds.ids.clear();
1654
+ this.live.recentEventIds.order.length = 0;
1655
+ this.live.normalizer = void 0;
1656
+ }
1657
+ this.clearTurnEndIdleTimer();
1658
+ this.live.initializingEvents = this.watched ? [] : void 0;
1659
+ }
1660
+ stopResources() {
1661
+ this.timelineReads.invalidate();
1662
+ this.stopFollower();
1663
+ this.clearFollowerRecovery();
1664
+ this.clearTurnEndIdleTimer();
1665
+ this.clearPlanSyncScheduling();
1666
+ }
1667
+ invalidateConnection() {
1668
+ this.connection.generation += 1;
1669
+ this.stopResources();
1670
+ this.prepareReload(true);
1671
+ this.connection.session = void 0;
1672
+ this.connection.reload = void 0;
1673
+ this.connection.sendAwareReload = false;
1674
+ this.activity.activeRootTurn = false;
1675
+ this.activity.busy = false;
1676
+ }
1677
+ canDisposeAfterConnectionFailure() {
1678
+ return this.connection.session === void 0 && this.connection.reload === void 0 && this.connection.sendInProgressCount === 0 && this.connection.sendCompletionWaiters.size === 0 && this.workbench.activeMutations === 0 && this.workbench.activeQuestions === 0 && !this.workbench.compacting && !this.watched && this.follower.current === void 0 && this.follower.recoveryTimer === void 0 && this.activity.turnEndIdleTimer === void 0 && this.plan.timer === void 0 && this.plan.pending === void 0 && this.plan.run === void 0 && this.timelineReads.size === 0 && this.configuration.transition === void 0;
1679
+ }
1680
+ dispose() {
1681
+ this.connection.generation += 1;
1682
+ this.stopResources();
1683
+ this.toolLifecycles.clear();
1684
+ for (const resolve2 of this.connection.sendCompletionWaiters) {
1685
+ resolve2();
1686
+ }
1687
+ this.connection.sendCompletionWaiters.clear();
1688
+ this.connection.session = void 0;
1689
+ this.connection.reload = void 0;
1690
+ this.connection.sendAwareReload = false;
1691
+ this.connection.sendReady = false;
1692
+ this.connection.sendInProgressCount = 0;
1693
+ this.live.recentEventIds.ids.clear();
1694
+ this.live.recentEventIds.order.length = 0;
1695
+ this.live.normalizer = void 0;
1696
+ this.live.initializingEvents = void 0;
1697
+ this.activity.activeRootTurn = false;
1698
+ this.activity.busy = false;
1699
+ this.plan.pending = void 0;
1700
+ this.plan.generation = 0;
1701
+ this.plan.run = void 0;
1702
+ this.plan.snapshotRevision = 0;
1703
+ this.configuration.transition = void 0;
1704
+ this.watched = false;
1705
+ }
1706
+ };
1707
+ var SessionRuntimeRegistry = class {
1708
+ #runtimes = /* @__PURE__ */ new Map();
1709
+ getOrCreate(sessionId) {
1710
+ const existing = this.#runtimes.get(sessionId);
1711
+ if (existing !== void 0) {
1712
+ return existing;
1713
+ }
1714
+ const runtime = new SessionRuntime();
1715
+ this.#runtimes.set(sessionId, runtime);
1716
+ return runtime;
1717
+ }
1718
+ peek(sessionId) {
1719
+ return this.#runtimes.get(sessionId);
1720
+ }
1721
+ delete(sessionId) {
1722
+ const runtime = this.#runtimes.get(sessionId);
1723
+ if (runtime !== void 0) {
1724
+ runtime.dispose();
1725
+ this.#runtimes.delete(sessionId);
1726
+ }
1727
+ }
1728
+ deleteIf(sessionId, runtime) {
1729
+ if (this.#runtimes.get(sessionId) !== runtime) {
1730
+ return false;
1731
+ }
1732
+ runtime.dispose();
1733
+ this.#runtimes.delete(sessionId);
1734
+ return true;
1735
+ }
1736
+ stopResources() {
1737
+ for (const runtime of this.#runtimes.values()) {
1738
+ runtime.stopResources();
1739
+ }
1740
+ }
1741
+ invalidateConnections() {
1742
+ const watched = [];
1743
+ for (const [sessionId, runtime] of this.#runtimes) {
1744
+ runtime.invalidateConnection();
1745
+ if (runtime.watched) {
1746
+ watched.push(sessionId);
1747
+ }
1748
+ }
1749
+ return watched;
1750
+ }
1751
+ clear() {
1752
+ for (const runtime of this.#runtimes.values()) {
1753
+ runtime.dispose();
1754
+ }
1755
+ this.#runtimes.clear();
1756
+ }
1757
+ };
1758
+
1759
+ // src/adapters/copilot/timeline/live-projection.ts
1760
+ var RECENT_EVENT_ID_LIMIT = 4e3;
1761
+ var TURN_END_IDLE_FALLBACK_MS = 5e3;
1762
+ var LiveTimelineProjection = class {
1763
+ constructor(ports) {
1764
+ this.ports = ports;
1765
+ }
1766
+ ports;
1767
+ #previews = /* @__PURE__ */ new WeakMap();
1768
+ watch(sessionId) {
1769
+ const runtime = this.ports.state(sessionId);
1770
+ if (!runtime.watched) {
1771
+ runtime.live.normalizer = void 0;
1772
+ runtime.live.recentEventIds.ids.clear();
1773
+ runtime.live.recentEventIds.order.length = 0;
1774
+ this.#previews.delete(runtime.live);
1775
+ }
1776
+ runtime.watched = true;
1777
+ }
1778
+ queueLiveEvent(sessionId, event) {
1779
+ const runtime = this.ports.state(sessionId);
1780
+ runtime.timelineReads.invalidateContent();
1781
+ this.updateSessionActivity(sessionId, event);
1782
+ this.ports.observeLive(sessionId, event);
1783
+ if (runtime.watched) this.ports.directEvent(sessionId);
1784
+ const initializing = runtime.live.initializingEvents;
1785
+ if (initializing !== void 0) {
1786
+ initializing.push(event);
1787
+ return;
1788
+ }
1789
+ this.forwardEvent(sessionId, event);
1790
+ }
1791
+ previewDelta(sessionId, event) {
1792
+ const key = this.ports.state(sessionId).live;
1793
+ if (event.type !== "assistant.message_delta" || event.agentId !== void 0 || event.data.parentToolCallId !== void 0) return;
1794
+ if (event.data.deltaContent.length === 0) return;
1795
+ let state = this.#previews.get(key);
1796
+ if (state === void 0) {
1797
+ state = { deltaContent: "" };
1798
+ this.#previews.set(key, state);
1799
+ }
1800
+ if (state.messageId !== event.data.messageId) {
1801
+ state.messageId = event.data.messageId;
1802
+ state.deltaContent = "";
1803
+ delete state.streaming;
1804
+ }
1805
+ state.deltaContent = (state.deltaContent + event.data.deltaContent).slice(0, 4096);
1806
+ const canonical = state.canonical?.final === false ? state.canonical : void 0;
1807
+ const previous = state.streaming ?? canonical;
1808
+ const revision = Date.parse(event.timestamp) - 1;
1809
+ if (previous !== void 0 && revision <= previous.revision) return;
1810
+ const item = {
1811
+ id: canonical?.id ?? assistantMessageId(event.data),
1812
+ kind: "assistant-message",
1813
+ actor: "assistant",
1814
+ turnId: previous?.turnId ?? event.id,
1815
+ revision,
1816
+ timestamp: previous?.timestamp ?? new Date(revision).toISOString(),
1817
+ sourceEventIds: [...previous?.sourceEventIds ?? [], event.id].slice(-1e3),
1818
+ content: (canonical === void 0 ? state.deltaContent : `${canonical.content}
1819
+
1820
+ ${state.deltaContent}`).slice(0, 4096),
1821
+ final: false
1822
+ };
1823
+ state.streaming = item;
1824
+ this.ports.callbacks()?.onAssistantPreview?.(sessionId, item);
1825
+ }
1826
+ forwardEvent(sessionId, event) {
1827
+ const runtime = this.ports.state(sessionId);
1828
+ const recent = runtime.live.recentEventIds;
1829
+ if (recent.ids.has(event.id)) {
1830
+ return;
1831
+ }
1832
+ runtime.timelineReads.invalidateContent();
1833
+ recent.ids.add(event.id);
1834
+ recent.order.push(event.id);
1835
+ while (recent.order.length > RECENT_EVENT_ID_LIMIT) {
1836
+ const expiredId = recent.order.shift();
1837
+ if (expiredId !== void 0) {
1838
+ recent.ids.delete(expiredId);
1839
+ }
1840
+ }
1841
+ this.previewDelta(sessionId, event);
1842
+ if (event.type === "session.context_cleared" || event.type === "session.snapshot_rewind") {
1843
+ runtime.live.normalizer = void 0;
1844
+ this.#previews.delete(runtime.live);
1845
+ runtime.toolLifecycles = new ConversationToolLifecycles();
1846
+ this.ports.callbacks()?.onTimelineInvalidated?.(sessionId, {
1847
+ reason: event.type === "session.context_cleared" ? "clear" : "rewind",
1848
+ sourceEventId: event.id,
1849
+ timestamp: event.timestamp
1850
+ });
1851
+ return;
1852
+ }
1853
+ if (event.type === "mcp.headers_refresh_required") {
1854
+ if (!runtime.watched) return;
1855
+ void this.ports.headers(sessionId, event.data).catch(
1856
+ (error) => {
1857
+ this.ports.callbacks()?.onSyncError(sessionId, error);
1858
+ }
1859
+ );
1860
+ return;
1861
+ }
1862
+ if (event.type === "session_limits_exhausted.requested") {
1863
+ if (!runtime.watched) return;
1864
+ void this.ports.limits(sessionId, event.data).catch(
1865
+ (error) => {
1866
+ this.ports.callbacks()?.onSyncError(sessionId, error);
1867
+ }
1868
+ );
1869
+ return;
1870
+ }
1871
+ let normalizer = runtime.live.normalizer;
1872
+ if (normalizer === void 0) {
1873
+ normalizer = new ConversationNormalizer((failure) => {
1874
+ this.ports.callbacks()?.onImageProjectionFailure?.(sessionId, failure);
1875
+ }, runtime.toolLifecycles, this.ports.inputProvenance(sessionId));
1876
+ runtime.live.normalizer = normalizer;
1877
+ }
1878
+ this.emitNormalizedItems(sessionId, event, normalizer.apply([event]));
1879
+ if (runtime.watched && event.type === "tool.execution_complete" && normalizer.pendingImageAssetIds().length > 0) {
1880
+ this.ports.resolveImages(sessionId, normalizer);
1881
+ }
1882
+ if (runtime.watched && (event.type === "session.plan_changed" || event.type === "session.todos_changed")) {
1883
+ this.ports.schedulePlan(sessionId, event);
1884
+ }
1885
+ }
1886
+ emitNormalizedItems(sessionId, event, items) {
1887
+ const runtime = this.ports.state(sessionId);
1888
+ for (const item of items) {
1889
+ if (item.kind === "assistant-message" && isContentfulAssistant(item)) {
1890
+ if (event.type === "assistant.message") {
1891
+ this.#previews.set(runtime.live, { canonical: item, deltaContent: "" });
1892
+ }
1893
+ if (!runtime.watched) this.ports.callbacks()?.onAssistantPreview?.(sessionId, item);
1894
+ } else if (!runtime.watched && item.kind === "human-message" && item.actor === "human") {
1895
+ this.ports.callbacks()?.onAssistantPreview?.(sessionId, item);
1896
+ }
1897
+ if (!runtime.watched) continue;
1898
+ if (item.kind === "plan-update" && (event.type === "session.plan_changed" || event.type === "session.todos_changed")) {
1899
+ continue;
1900
+ }
1901
+ this.ports.callbacks()?.onConversationItem(sessionId, item);
1902
+ }
1903
+ }
1904
+ updateSessionActivity(sessionId, event) {
1905
+ this.ports.changed();
1906
+ if (event.agentId !== void 0) {
1907
+ return;
1908
+ }
1909
+ const runtime = this.ports.state(sessionId);
1910
+ if (event.type === "session.resume" && event.data.sessionWasActive === true) {
1911
+ this.#clearTurnEndIdleTimer(sessionId);
1912
+ if (!runtime.activity.busy) {
1913
+ runtime.activity.busy = true;
1914
+ this.ports.callbacks()?.onSessionActivity?.(
1915
+ sessionId,
1916
+ "working",
1917
+ event.timestamp
1918
+ );
1919
+ }
1920
+ return;
1921
+ }
1922
+ if (event.type === "assistant.turn_start") {
1923
+ this.#clearTurnEndIdleTimer(sessionId);
1924
+ runtime.activity.activeRootTurn = true;
1925
+ if (!runtime.activity.busy) {
1926
+ runtime.activity.busy = true;
1927
+ this.ports.callbacks()?.onSessionActivity?.(
1928
+ sessionId,
1929
+ "working",
1930
+ event.timestamp
1931
+ );
1932
+ }
1933
+ return;
1934
+ }
1935
+ if (event.type === "assistant.turn_end") {
1936
+ runtime.activity.activeRootTurn = false;
1937
+ this.#scheduleTurnEndIdleFallback(sessionId, event.timestamp);
1938
+ return;
1939
+ }
1940
+ if (event.type === "session.idle" || event.type === "session.error" || event.type === "session.shutdown") {
1941
+ runtime.activity.activeRootTurn = false;
1942
+ this.markSessionIdle(sessionId, event.timestamp);
1943
+ }
1944
+ }
1945
+ #scheduleTurnEndIdleFallback(sessionId, occurredAt) {
1946
+ this.#clearTurnEndIdleTimer(sessionId);
1947
+ const runtime = this.ports.state(sessionId);
1948
+ const timer = setTimeout(() => {
1949
+ runtime.activity.turnEndIdleTimer = void 0;
1950
+ if (!runtime.activity.activeRootTurn) {
1951
+ this.markSessionIdle(sessionId, occurredAt);
1952
+ }
1953
+ }, TURN_END_IDLE_FALLBACK_MS);
1954
+ runtime.activity.turnEndIdleTimer = timer;
1955
+ }
1956
+ #clearTurnEndIdleTimer(sessionId) {
1957
+ this.ports.peek(sessionId)?.clearTurnEndIdleTimer();
1958
+ }
1959
+ markSessionIdle(sessionId, occurredAt) {
1960
+ this.#clearTurnEndIdleTimer(sessionId);
1961
+ const runtime = this.ports.state(sessionId);
1962
+ if (runtime.activity.busy) {
1963
+ runtime.activity.busy = false;
1964
+ this.ports.callbacks()?.onSessionActivity?.(sessionId, "idle", occurredAt);
1965
+ }
1966
+ }
1967
+ };
1968
+
1969
+ // src/git-remote-url.ts
1970
+ function sanitizeGitRemoteUrl(value) {
1971
+ const remote = value.trim();
1972
+ if (!remote) return void 0;
1973
+ try {
1974
+ const url = new URL(remote);
1975
+ if (!["http:", "https:", "ssh:", "git:"].includes(url.protocol) || !url.hostname) {
1976
+ return void 0;
1977
+ }
1978
+ url.username = "";
1979
+ url.password = "";
1980
+ url.search = "";
1981
+ url.hash = "";
1982
+ return url.toString().replace(/\/$/, "");
1983
+ } catch {
1984
+ if (/^[A-Za-z]:[\\/]/.test(remote)) return void 0;
1985
+ const scp = /^(?:([^@\s]+)@)?([^:\s]+):(.+)$/.exec(remote);
1986
+ if (!scp) return void 0;
1987
+ const username = scp[1];
1988
+ const host = scp[2];
1989
+ const path = scp[3];
1990
+ if (!host || !path) return void 0;
1991
+ const safeUser = username === "git" ? "git@" : "";
1992
+ return `${safeUser}${host}:${path.split(/[?#]/, 1)[0]}`;
1993
+ }
1994
+ }
1995
+
1996
+ // src/git-project-identity.ts
1997
+ import { execFile } from "node:child_process";
1998
+ import { createHash as createHash3 } from "node:crypto";
1999
+ import { realpath } from "node:fs/promises";
2000
+ import { isAbsolute, resolve } from "node:path";
2001
+ import { promisify } from "node:util";
2002
+ var execFileAsync = promisify(execFile);
2003
+ var NotGitDirectoryError = class extends Error {
2004
+ constructor(options) {
2005
+ super("The working directory is not a Git repository", options);
2006
+ }
2007
+ };
2008
+ function gitEnvironment() {
2009
+ const env = { ...process.env, LC_ALL: "C" };
2010
+ for (const key of [
2011
+ "GIT_DIR",
2012
+ "GIT_COMMON_DIR",
2013
+ "GIT_WORK_TREE",
2014
+ "GIT_INDEX_FILE",
2015
+ "GIT_PREFIX",
2016
+ "GIT_OBJECT_DIRECTORY",
2017
+ "GIT_ALTERNATE_OBJECT_DIRECTORIES",
2018
+ "GIT_CONFIG",
2019
+ "GIT_CONFIG_PARAMETERS",
2020
+ "GIT_CONFIG_COUNT"
2021
+ ]) {
2022
+ delete env[key];
2023
+ }
2024
+ return env;
2025
+ }
2026
+ async function git(args, env) {
2027
+ const { stdout } = await execFileAsync("git", args, {
2028
+ encoding: "utf8",
2029
+ windowsHide: true,
2030
+ timeout: 2e3,
2031
+ maxBuffer: 1024 * 1024,
2032
+ env
2033
+ });
2034
+ return stdout;
2035
+ }
2036
+ function absoluteGitPath(value) {
2037
+ if (!value || value.includes("\0") || !isAbsolute(value)) {
2038
+ throw new Error("Git did not return an absolute repository path");
2039
+ }
2040
+ return resolve(value);
2041
+ }
2042
+ async function resolveGitProjectIdentity(workingDirectory, env = gitEnvironment()) {
2043
+ if (!isAbsolute(workingDirectory) || workingDirectory.includes("\0")) {
2044
+ throw new Error("Git project inspection requires an absolute working directory");
2045
+ }
2046
+ let output;
2047
+ try {
2048
+ output = await git([
2049
+ "-C",
2050
+ workingDirectory,
2051
+ "rev-parse",
2052
+ "--path-format=absolute",
2053
+ "--show-toplevel",
2054
+ "--git-common-dir"
2055
+ ], env);
2056
+ } catch (error) {
2057
+ if (error instanceof Error && "code" in error && error.code === 128 && "stdout" in error && error.stdout === "" && "stderr" in error && typeof error.stderr === "string" && /^fatal: not a git repository \(or any of the parent directories\): \.git\r?\n$/.test(error.stderr)) {
2058
+ throw new NotGitDirectoryError({ cause: error });
2059
+ }
2060
+ throw error;
2061
+ }
2062
+ const roots = output.replace(/\r?\n$/, "").split(/\r?\n/);
2063
+ if (roots.length !== 2) throw new Error("Git returned ambiguous repository paths");
2064
+ const worktreeRoot = await realpath(absoluteGitPath(roots[0]));
2065
+ const commonGitDirectory = await realpath(absoluteGitPath(roots[1]));
2066
+ const worktrees = await git(["-C", workingDirectory, "worktree", "list", "--porcelain", "-z"], env);
2067
+ if (!worktrees.endsWith("\0\0")) throw new Error("Git returned an incomplete worktree list");
2068
+ const primary = worktrees.split("\0\0", 1)[0]?.split("\0");
2069
+ const first = primary?.[0];
2070
+ if (!first?.startsWith("worktree ")) throw new Error("Git did not identify its primary worktree");
2071
+ const mainWorktreeRoot = primary?.includes("bare") ? void 0 : absoluteGitPath(first.slice("worktree ".length));
2072
+ let origin;
2073
+ try {
2074
+ origin = await git([
2075
+ "--git-dir",
2076
+ commonGitDirectory,
2077
+ "config",
2078
+ "--local",
2079
+ "--includes",
2080
+ "--get",
2081
+ "remote.origin.url"
2082
+ ], env);
2083
+ } catch (error) {
2084
+ if (!(error instanceof Error && "code" in error && error.code === 1 && "stdout" in error && error.stdout === "" && "stderr" in error && error.stderr === "")) {
2085
+ throw error;
2086
+ }
2087
+ }
2088
+ const gitRemoteUrl = origin === void 0 ? void 0 : sanitizeGitRemoteUrl(origin);
2089
+ return {
2090
+ worktreeRoot,
2091
+ commonGitDirectory,
2092
+ localRepositoryKey: createHash3("sha256").update(commonGitDirectory).digest("base64url"),
2093
+ ...mainWorktreeRoot === void 0 ? {} : { mainWorktreeRoot },
2094
+ ...gitRemoteUrl === void 0 ? {} : { gitRemoteUrl }
2095
+ };
2096
+ }
2097
+
2098
+ // src/prompt-preparation-error.ts
2099
+ var PromptPreparationError = class extends Error {
2100
+ constructor(cause) {
2101
+ super(cause instanceof Error ? cause.message : "Prompt preparation failed", { cause });
2102
+ this.name = "PromptPreparationError";
2103
+ }
2104
+ };
2105
+
2106
+ // src/session-project-identity.ts
2107
+ import { realpath as realpath2, stat } from "node:fs/promises";
2108
+ import { isAbsolute as isAbsolute2 } from "node:path";
2109
+ var SESSION_PROJECT_IDENTITY_CONCURRENCY = 4;
2110
+ var errors = {
2111
+ SESSION_NOT_FOUND: "The session is unavailable",
2112
+ SESSION_ROOT_UNAVAILABLE: "The session working directory is unavailable",
2113
+ SESSION_ROOT_CHANGED: "The session working directory changed",
2114
+ PROJECT_IDENTITY_BUSY: "Too many project identity requests",
2115
+ PROJECT_IDENTITY_UNAVAILABLE: "The session project identity is unavailable"
2116
+ };
2117
+ var SessionProjectIdentityError = class extends Error {
2118
+ constructor(code, options) {
2119
+ super(errors[code], options);
2120
+ this.code = code;
2121
+ }
2122
+ code;
2123
+ };
2124
+ async function rootIdentity(workingDirectory) {
2125
+ const root = await realpath2(workingDirectory);
2126
+ const info = await stat(root);
2127
+ if (!info.isDirectory()) throw new SessionProjectIdentityError("SESSION_ROOT_UNAVAILABLE");
2128
+ return `${root}\0${info.dev}\0${info.ino}`;
2129
+ }
2130
+ var SessionProjectIdentityReader = class {
2131
+ constructor(metadata, inspect = resolveGitProjectIdentity) {
2132
+ this.metadata = metadata;
2133
+ this.inspect = inspect;
2134
+ }
2135
+ metadata;
2136
+ inspect;
2137
+ #active = 0;
2138
+ async get(sessionId) {
2139
+ if (this.#active >= SESSION_PROJECT_IDENTITY_CONCURRENCY) {
2140
+ throw new SessionProjectIdentityError("PROJECT_IDENTITY_BUSY");
2141
+ }
2142
+ this.#active++;
2143
+ try {
2144
+ const metadata = await this.metadata(sessionId);
2145
+ if (metadata === void 0) throw new SessionProjectIdentityError("SESSION_NOT_FOUND");
2146
+ const cwd = metadata.workingDirectory;
2147
+ if (!cwd || !isAbsolute2(cwd) || cwd.includes("\0")) {
2148
+ throw new SessionProjectIdentityError("SESSION_ROOT_UNAVAILABLE");
2149
+ }
2150
+ let initialRoot;
2151
+ try {
2152
+ initialRoot = await rootIdentity(cwd);
2153
+ } catch (cause) {
2154
+ throw new SessionProjectIdentityError("SESSION_ROOT_UNAVAILABLE", { cause });
2155
+ }
2156
+ let project;
2157
+ let inspectionError;
2158
+ try {
2159
+ const identity = await this.inspect(cwd);
2160
+ project = SessionGitProjectSchema.parse({
2161
+ localRepositoryKey: identity.localRepositoryKey,
2162
+ worktreeRoot: identity.worktreeRoot,
2163
+ ...identity.mainWorktreeRoot === void 0 ? {} : { mainWorktreeRoot: identity.mainWorktreeRoot },
2164
+ ...identity.gitRemoteUrl === void 0 ? {} : { gitRemoteUrl: identity.gitRemoteUrl }
2165
+ });
2166
+ } catch (error) {
2167
+ if (!(error instanceof NotGitDirectoryError)) {
2168
+ inspectionError = new SessionProjectIdentityError("PROJECT_IDENTITY_UNAVAILABLE", { cause: error });
2169
+ }
2170
+ }
2171
+ const latest = await this.metadata(sessionId);
2172
+ if (latest === void 0) throw new SessionProjectIdentityError("SESSION_NOT_FOUND");
2173
+ if (latest.workingDirectory !== cwd) throw new SessionProjectIdentityError("SESSION_ROOT_CHANGED");
2174
+ try {
2175
+ if (await rootIdentity(cwd) !== initialRoot) {
2176
+ throw new SessionProjectIdentityError("SESSION_ROOT_CHANGED");
2177
+ }
2178
+ } catch (cause) {
2179
+ throw new SessionProjectIdentityError("SESSION_ROOT_CHANGED", { cause });
2180
+ }
2181
+ if (inspectionError !== void 0) throw inspectionError;
2182
+ return project;
2183
+ } catch (cause) {
2184
+ if (cause instanceof SessionProjectIdentityError) throw cause;
2185
+ throw new SessionProjectIdentityError("PROJECT_IDENTITY_UNAVAILABLE", { cause });
2186
+ } finally {
2187
+ this.#active--;
2188
+ }
2189
+ }
2190
+ };
2191
+
2192
+ // src/session-workbench-runtime.ts
2193
+ import { createHash as createHash4 } from "node:crypto";
2194
+ import { z } from "zod";
2195
+ var WorkbenchRuntimeError = class extends SessionOperationError {
2196
+ constructor(code, message) {
2197
+ super(code, message);
2198
+ this.name = "WorkbenchRuntimeError";
2199
+ }
2200
+ };
2201
+ var WorkbenchOutcomeUncertainError = class extends Error {
2202
+ code = "stale-runtime";
2203
+ };
2204
+ var queueSchema = z.object({
2205
+ items: z.array(z.object({
2206
+ id: z.string().min(1).max(256),
2207
+ kind: z.enum(["message", "command"]),
2208
+ displayText: z.string().max(1e5),
2209
+ agentMode: z.enum(["interactive", "plan", "autopilot", "shell"])
2210
+ })).max(512),
2211
+ steeringMessages: z.array(z.string().max(1e5)).max(512)
2212
+ });
2213
+ var resultBase = { runtimeSettingsChanged: z.boolean().optional() };
2214
+ var invocationSchema = z.discriminatedUnion("kind", [
2215
+ z.object({ kind: z.literal("text"), text: z.string().max(64 * 1024), markdown: z.boolean().optional(), preserveAnsi: z.boolean().optional(), ...resultBase }),
2216
+ z.object({ kind: z.literal("completed"), message: z.string().max(64 * 1024).optional(), ...resultBase }),
2217
+ z.object({ kind: z.literal("agent-prompt"), prompt: z.string().min(1).max(64 * 1024), displayPrompt: z.string().max(64 * 1024), mode: z.enum(["interactive", "plan", "autopilot"]).optional(), notice: z.string().max(64 * 1024).optional(), ...resultBase }),
2218
+ z.object({ kind: z.literal("select-subcommand"), command: z.string().max(256), title: z.string().max(4096), options: z.array(z.object({ name: z.string().max(256), description: z.string().max(4096), group: z.string().max(256).optional() })).max(256), ...resultBase })
2219
+ ]);
2220
+ function nanoAiu(value) {
2221
+ if (!Number.isSafeInteger(value) || value < 0) {
2222
+ throw new WorkbenchRuntimeError("invalid-result", "Runtime nano-AIU value cannot be represented without precision loss");
2223
+ }
2224
+ return value.toString();
2225
+ }
2226
+ async function readWorkbenchMetrics(rpc) {
2227
+ if (rpc.usage === void 0 || rpc.model === void 0 || rpc.metadata?.contextInfo === void 0) {
2228
+ throw new WorkbenchRuntimeError("unsupported", "Runtime usage/context inspection is unavailable");
2229
+ }
2230
+ const [usage, model, attribution] = await Promise.all([
2231
+ rpc.usage.getMetrics(),
2232
+ rpc.model.getCurrent(),
2233
+ rpc.metadata.getContextAttribution?.()
2234
+ ]);
2235
+ const attributed = attribution?.contextAttribution;
2236
+ const modelId = attributed?.modelId ?? model.modelId;
2237
+ const { contextInfo } = await rpc.metadata.contextInfo({
2238
+ promptTokenLimit: attributed?.promptTokenLimit ?? 0,
2239
+ outputTokenLimit: attributed === void 0 || attributed === null ? 0 : Math.max(0, attributed.limit - attributed.promptTokenLimit),
2240
+ ...modelId === void 0 ? {} : { selectedModel: modelId }
2241
+ });
2242
+ return {
2243
+ ...modelId === void 0 ? {} : { modelId },
2244
+ totalPremiumRequestCost: usage.totalPremiumRequestCost,
2245
+ totalUserRequests: usage.totalUserRequests,
2246
+ ...usage.totalNanoAiu === void 0 ? {} : { totalNanoAiu: nanoAiu(usage.totalNanoAiu) },
2247
+ tokens: Object.entries(usage.tokenDetails ?? {}).flatMap(([kind, detail]) => detail === void 0 ? [] : [{ kind, count: detail.tokenCount }]),
2248
+ models: Object.entries(usage.modelMetrics).flatMap(([modelId2, metric]) => metric === void 0 ? [] : [{
2249
+ modelId: modelId2,
2250
+ inputTokens: metric.usage.inputTokens,
2251
+ outputTokens: metric.usage.outputTokens,
2252
+ cacheReadTokens: metric.usage.cacheReadTokens,
2253
+ cacheWriteTokens: metric.usage.cacheWriteTokens,
2254
+ requests: metric.requests.count,
2255
+ premiumRequestCost: metric.requests.cost,
2256
+ ...metric.totalNanoAiu === void 0 ? {} : { totalNanoAiu: nanoAiu(metric.totalNanoAiu) }
2257
+ }]),
2258
+ context: contextInfo ?? null
2259
+ };
2260
+ }
2261
+ function projectWorkbenchInsights(result) {
2262
+ return WorkbenchInsightsSchema.parse({
2263
+ sampledAt: result.sampledAt,
2264
+ generation: result.runtimeGeneration,
2265
+ ...result.modelId === void 0 ? {} : { model: result.modelId },
2266
+ totalUserRequests: result.totalUserRequests,
2267
+ premiumRequestCost: result.totalPremiumRequestCost,
2268
+ ...result.totalNanoAiu === void 0 ? {} : { nanoAiu: result.totalNanoAiu },
2269
+ tokens: result.tokens,
2270
+ models: result.models.map((model) => ({
2271
+ model: model.modelId,
2272
+ requests: model.requests,
2273
+ premiumRequestCost: model.premiumRequestCost,
2274
+ tokens: [
2275
+ { kind: "input", count: model.inputTokens },
2276
+ { kind: "output", count: model.outputTokens },
2277
+ { kind: "cache-read", count: model.cacheReadTokens },
2278
+ { kind: "cache-write", count: model.cacheWriteTokens }
2279
+ ]
2280
+ })),
2281
+ ...result.context === null ? {} : { context: {
2282
+ currentTokens: result.context.totalTokens,
2283
+ tokenLimit: result.context.promptTokenLimit,
2284
+ systemTokens: result.context.systemTokens,
2285
+ conversationTokens: result.context.conversationTokens,
2286
+ toolTokens: result.context.toolDefinitionsTokens
2287
+ } },
2288
+ canCompact: result.compactAllowed,
2289
+ ...result.compactDisabledReason === void 0 ? {} : { compactUnavailableReason: result.compactDisabledReason }
2290
+ });
2291
+ }
2292
+ var SessionWorkbenchRuntime = class {
2293
+ constructor(dependencies) {
2294
+ this.dependencies = dependencies;
2295
+ }
2296
+ dependencies;
2297
+ #activeQuestions = 0;
2298
+ #queueMutations = /* @__PURE__ */ new Map();
2299
+ #generation(runtime) {
2300
+ return `${runtime.workbench.identity}:${runtime.connection.generation}`;
2301
+ }
2302
+ async #session(sessionId) {
2303
+ this.dependencies.assertAvailable(sessionId);
2304
+ const session = await this.dependencies.getSession(sessionId);
2305
+ this.dependencies.assertAvailable(sessionId);
2306
+ const runtime = this.dependencies.getRuntime(sessionId);
2307
+ const generation = this.#generation(runtime);
2308
+ return { session, runtime, generation };
2309
+ }
2310
+ #assertGeneration(sessionId, generation) {
2311
+ this.dependencies.assertAvailable(sessionId);
2312
+ if (this.#generation(this.dependencies.getRuntime(sessionId)) !== generation) {
2313
+ throw new WorkbenchOutcomeUncertainError("Session runtime changed during the operation; do not replay mutations");
2314
+ }
2315
+ }
2316
+ async ask(sessionId, question) {
2317
+ z.string().trim().min(1).max(16 * 1024).parse(question);
2318
+ this.dependencies.assertAvailable(sessionId);
2319
+ const runtime = this.dependencies.getRuntime(sessionId);
2320
+ if (runtime.workbench.compacting || runtime.workbench.activeQuestions > 0 || this.#activeQuestions >= 2) {
2321
+ throw new WorkbenchRuntimeError("busy", "A side question or context compaction is already in progress");
2322
+ }
2323
+ runtime.workbench.activeQuestions += 1;
2324
+ this.#activeQuestions += 1;
2325
+ try {
2326
+ const { session, generation } = await this.#session(sessionId);
2327
+ if (session.rpc.ui.ephemeralQuery === void 0) {
2328
+ throw new WorkbenchRuntimeError("unsupported", "This runtime does not support side questions");
2329
+ }
2330
+ const result = z.object({ answer: z.string().max(64 * 1024) }).parse(
2331
+ await session.rpc.ui.ephemeralQuery({ question })
2332
+ );
2333
+ this.#assertGeneration(sessionId, generation);
2334
+ return WorkbenchAskResultSchema.parse({ answer: result.answer });
2335
+ } finally {
2336
+ runtime.workbench.activeQuestions -= 1;
2337
+ this.#activeQuestions -= 1;
2338
+ }
2339
+ }
2340
+ async queueSnapshot(sessionId) {
2341
+ const { session, generation } = await this.#session(sessionId);
2342
+ const queue = session.rpc.queue;
2343
+ if (queue === void 0) throw new WorkbenchRuntimeError("unsupported", "Runtime queue management is unavailable");
2344
+ const pending = queueSchema.parse(await queue.pendingItems());
2345
+ this.#assertGeneration(sessionId, generation);
2346
+ const groups = /* @__PURE__ */ new Map();
2347
+ for (const row of pending.items) {
2348
+ const existing = groups.get(row.id);
2349
+ if (existing !== void 0) {
2350
+ existing.displayTexts.push(row.displayText);
2351
+ existing.actions = existing.actions.filter((action) => action !== "edit");
2352
+ if (row.agentMode === "shell") existing.actions = existing.actions.filter((action) => action !== "send-now");
2353
+ } else {
2354
+ const actions = ["remove", "move"];
2355
+ if (row.kind === "message" && row.agentMode !== "shell") {
2356
+ actions.push("send-now");
2357
+ if (this.dependencies.supportsAttachmentPreservingQueueEdits()) actions.push("edit");
2358
+ }
2359
+ groups.set(row.id, { id: row.id, kind: row.kind, displayTexts: [row.displayText], actions });
2360
+ }
2361
+ }
2362
+ return {
2363
+ runtimeGeneration: generation,
2364
+ revision: createHash4("sha256").update(JSON.stringify(pending)).digest("hex"),
2365
+ sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
2366
+ items: [...groups.values()],
2367
+ steeringMessages: pending.steeringMessages
2368
+ };
2369
+ }
2370
+ async mutatePendingQueue(sessionId, input) {
2371
+ const previous = this.#queueMutations.get(sessionId);
2372
+ let release = () => {
2373
+ };
2374
+ const current2 = new Promise((resolve2) => {
2375
+ release = resolve2;
2376
+ });
2377
+ this.#queueMutations.set(sessionId, current2);
2378
+ await previous;
2379
+ try {
2380
+ const { session, runtime, generation } = await this.#session(sessionId);
2381
+ if (input.runtimeGeneration !== generation) throw new WorkbenchRuntimeError("stale-runtime", "Queue belongs to an earlier session runtime");
2382
+ const releaseMutation = runtime.admitMutation();
2383
+ try {
2384
+ const snapshot = await this.queueSnapshot(sessionId);
2385
+ if (snapshot.revision !== input.revision) return { outcome: "stale-snapshot", snapshot };
2386
+ const item = snapshot.items.find((entry) => entry.id === input.id);
2387
+ if (item === void 0) return { outcome: "not-found", snapshot };
2388
+ if (!item.actions.includes(input.action)) return { outcome: "not-editable", snapshot };
2389
+ const queue = session.rpc.queue;
2390
+ if (queue === void 0) throw new WorkbenchRuntimeError("unsupported", "Runtime queue management is unavailable");
2391
+ let outcome;
2392
+ switch (input.action) {
2393
+ case "edit":
2394
+ z.string().trim().min(1).max(1e5).parse(input.text);
2395
+ outcome = (await queue.updateText({ id: input.id, prompt: input.text, displayPrompt: input.text })).updated ? "updated" : "unchanged";
2396
+ break;
2397
+ case "remove":
2398
+ outcome = (await queue.removeAt({ id: input.id })).removed ? "removed" : "not-found";
2399
+ break;
2400
+ case "move":
2401
+ z.number().int().nonnegative().max(511).parse(input.toPosition);
2402
+ outcome = (await queue.moveItem({ id: input.id, toPosition: input.toPosition })).changed ? "moved" : "unchanged";
2403
+ break;
2404
+ case "send-now":
2405
+ outcome = (await queue.sendNow({ id: input.id })).steered ? "sent" : "not-steered";
2406
+ break;
2407
+ }
2408
+ this.#assertGeneration(sessionId, generation);
2409
+ return { outcome, snapshot: await this.queueSnapshot(sessionId) };
2410
+ } finally {
2411
+ releaseMutation();
2412
+ }
2413
+ } finally {
2414
+ release();
2415
+ if (this.#queueMutations.get(sessionId) === current2) this.#queueMutations.delete(sessionId);
2416
+ }
2417
+ }
2418
+ async listCommands(sessionId) {
2419
+ const { session, generation } = await this.#session(sessionId);
2420
+ if (session.rpc.commands === void 0) throw new WorkbenchRuntimeError("unsupported", "Runtime command discovery is unavailable");
2421
+ const { commands } = await session.rpc.commands.list({ includeBuiltins: true, includeSkills: true, includeClientCommands: true });
2422
+ if (commands.length > 256) throw new WorkbenchRuntimeError("invalid-result", "Runtime command catalog exceeds the supported size");
2423
+ const projected = commands.map((command) => {
2424
+ const name = command.name.toLowerCase();
2425
+ let action = command.kind === "skill" || command.kind === "builtin" && ["help", "version", "review", "diff", "skills"].includes(name) ? "invoke" : "unsupported";
2426
+ if (["ask", "btw"].includes(name)) action = "ask";
2427
+ if (["model", "mode", "plan", "autopilot"].includes(name)) action = "configure";
2428
+ if (["usage", "context"].includes(name)) action = "insights";
2429
+ if (name === "compact") action = "compact";
2430
+ const executable = action !== "unsupported" && (action !== "compact" || this.dependencies.supportsCoordinatedCompaction());
2431
+ return {
2432
+ name: command.name,
2433
+ aliases: command.aliases ?? [],
2434
+ description: command.description,
2435
+ kind: command.kind,
2436
+ ...command.input === void 0 ? {} : { inputHint: command.input.hint },
2437
+ inputRequired: command.input?.required === true,
2438
+ allowDuringAgentExecution: command.allowDuringAgentExecution,
2439
+ executable,
2440
+ action,
2441
+ ...executable ? {} : { disabledReason: action === "compact" ? "Runtime compaction coordination is unverified" : "This command has no supported workbench action" }
2442
+ };
2443
+ });
2444
+ for (const name of ["ask", "btw"]) {
2445
+ if (!projected.some((command) => command.name === name || command.aliases.includes(name))) {
2446
+ projected.push({ name, aliases: [], description: "Ask a side question", kind: "workbench", inputHint: "Question", inputRequired: true, allowDuringAgentExecution: true, executable: true, action: "ask" });
2447
+ }
2448
+ }
2449
+ this.#assertGeneration(sessionId, generation);
2450
+ return { runtimeGeneration: generation, commands: projected };
2451
+ }
2452
+ async invokeRuntimeCommand(sessionId, input) {
2453
+ const catalog = await this.listCommands(sessionId);
2454
+ const name = input.name.replace(/^\/+/u, "").toLowerCase();
2455
+ const command = catalog.commands.find((candidate) => candidate.name.toLowerCase() === name || candidate.aliases.some((alias) => alias.toLowerCase() === name));
2456
+ if (command === void 0) throw new WorkbenchRuntimeError("unknown-command", "Unknown slash command; choose explicitly to send it as text");
2457
+ if (!command.executable || command.action !== "invoke") throw new WorkbenchRuntimeError("unsupported", command.disabledReason ?? `Use the ${command.action} workbench action for this command`);
2458
+ if (command.inputRequired && !input.input?.trim()) throw new WorkbenchRuntimeError("unsupported", "This command requires input");
2459
+ const { session, runtime, generation } = await this.#session(sessionId);
2460
+ if (catalog.runtimeGeneration !== generation) throw new WorkbenchRuntimeError("stale-runtime", "Command catalog belongs to an earlier runtime");
2461
+ const invoke = session.rpc.commands?.invoke;
2462
+ const isProcessing = session.rpc.metadata.isProcessing;
2463
+ if (invoke === void 0 || isProcessing === void 0) throw new WorkbenchRuntimeError("unsupported", "Runtime command invocation or activity inspection is unavailable");
2464
+ const release = runtime.admitMutation();
2465
+ try {
2466
+ if (!command.allowDuringAgentExecution && (runtime.activity.busy || (await isProcessing()).processing)) {
2467
+ throw new WorkbenchRuntimeError("busy", "This command cannot run while the main task is active");
2468
+ }
2469
+ const result = invocationSchema.parse(await invoke({ name: command.name, ...input.input === void 0 ? {} : { input: input.input } }));
2470
+ this.#assertGeneration(sessionId, generation);
2471
+ return result;
2472
+ } finally {
2473
+ release();
2474
+ }
2475
+ }
2476
+ async #compactionDisabledReason(sessionId, session, runtime) {
2477
+ if (!this.dependencies.supportsCoordinatedCompaction()) return "Runtime compaction coordination is unverified";
2478
+ if (runtime.workbench.activeQuestions > 0) return "A side question is still running";
2479
+ if (runtime.workbench.activeMutations > 0 || runtime.connection.sendInProgressCount > 0 || runtime.configuration.transition !== void 0) return "A session mutation is in progress";
2480
+ if (runtime.activity.busy || runtime.activity.activeRootTurn || this.dependencies.hasPendingInteraction(sessionId)) return "The session has active work or a pending interaction";
2481
+ if (session.rpc.metadata.isProcessing === void 0 || session.rpc.permissions.pendingRequests === void 0 || session.rpc.queue === void 0 || session.rpc.history === void 0) return "Runtime idle-state inspection is unavailable";
2482
+ const [activity, pending, permissions] = await Promise.all([
2483
+ session.rpc.metadata.isProcessing(),
2484
+ session.rpc.queue.pendingItems(),
2485
+ session.rpc.permissions.pendingRequests()
2486
+ ]);
2487
+ if (activity.processing || pending.items.length > 0 || pending.steeringMessages.length > 0 || permissions.items.length > 0) return "The runtime has active work, queued items or a pending approval";
2488
+ if (runtime.activity.busy || runtime.activity.activeRootTurn || this.dependencies.hasPendingInteraction(sessionId)) return "Session activity changed while checking whether compaction is safe";
2489
+ return void 0;
2490
+ }
2491
+ async readInsights(sessionId) {
2492
+ const { session, runtime, generation } = await this.#session(sessionId);
2493
+ const metrics = await readWorkbenchMetrics(session.rpc);
2494
+ const reason = runtime.workbench.compacting ? "Context compaction is in progress" : await this.#compactionDisabledReason(sessionId, session, runtime);
2495
+ this.#assertGeneration(sessionId, generation);
2496
+ return {
2497
+ ...metrics,
2498
+ runtimeGeneration: generation,
2499
+ sampledAt: (/* @__PURE__ */ new Date()).toISOString(),
2500
+ compactAllowed: reason === void 0,
2501
+ ...reason === void 0 ? {} : { compactDisabledReason: reason }
2502
+ };
2503
+ }
2504
+ async compactContext(sessionId) {
2505
+ const { session, runtime, generation } = await this.#session(sessionId);
2506
+ if (runtime.workbench.compacting) throw new WorkbenchRuntimeError("busy", "Context compaction is already in progress");
2507
+ runtime.workbench.compacting = true;
2508
+ try {
2509
+ const reason = await this.#compactionDisabledReason(sessionId, session, runtime);
2510
+ if (reason !== void 0) throw new WorkbenchRuntimeError(this.dependencies.supportsCoordinatedCompaction() ? "busy" : "unsupported", reason);
2511
+ const history = session.rpc.history;
2512
+ if (history === void 0) throw new WorkbenchRuntimeError("unsupported", "Runtime context compaction is unavailable");
2513
+ this.#assertGeneration(sessionId, generation);
2514
+ const result = await history.compact();
2515
+ this.#assertGeneration(sessionId, generation);
2516
+ return {
2517
+ outcome: !result.success ? "failed" : !result.summaryContent?.trim() ? "no-summary" : result.tokensRemoved <= 0 ? "no-tokens-released" : "compacted",
2518
+ tokensRemoved: result.tokensRemoved,
2519
+ messagesRemoved: result.messagesRemoved,
2520
+ insights: await this.readInsights(sessionId)
2521
+ };
2522
+ } finally {
2523
+ runtime.workbench.compacting = false;
2524
+ }
2525
+ }
2526
+ #queueOutput(snapshot) {
2527
+ return WorkbenchQueueSnapshotSchema.parse({
2528
+ generation: snapshot.runtimeGeneration,
2529
+ revision: snapshot.revision,
2530
+ items: snapshot.items.map((item) => ({ id: item.id, kind: item.kind, texts: item.displayTexts, actions: item.actions })),
2531
+ steeringMessages: snapshot.steeringMessages
2532
+ });
2533
+ }
2534
+ async queue(sessionId) {
2535
+ return this.#queueOutput(await this.queueSnapshot(sessionId));
2536
+ }
2537
+ async mutateQueue(sessionId, input) {
2538
+ const action = input.action === "edit" ? { action: input.action, text: input.prompt } : input.action === "move" ? { action: input.action, toPosition: input.toPosition } : { action: input.action };
2539
+ const result = await this.mutatePendingQueue(sessionId, {
2540
+ runtimeGeneration: input.generation,
2541
+ revision: input.revision,
2542
+ id: input.id,
2543
+ ...action
2544
+ });
2545
+ if (["not-found", "not-steered", "stale-snapshot", "not-editable"].includes(result.outcome)) {
2546
+ throw new SessionOperationError(`QUEUE_${result.outcome.replaceAll("-", "_").toUpperCase()}`, `Queue action was not applied: ${result.outcome}; refresh the queue`);
2547
+ }
2548
+ return WorkbenchQueueMutationResultSchema.parse({
2549
+ changed: result.outcome !== "unchanged",
2550
+ snapshot: this.#queueOutput(result.snapshot)
2551
+ });
2552
+ }
2553
+ async commands(sessionId) {
2554
+ const catalog = await this.listCommands(sessionId);
2555
+ return catalog.commands.map((command) => WorkbenchCommandSchema.parse({
2556
+ name: command.name,
2557
+ description: command.description,
2558
+ ...command.inputHint === void 0 ? {} : { argumentHint: command.inputHint },
2559
+ kind: command.kind === "workbench" ? "client" : command.kind,
2560
+ allowDuringExecution: command.allowDuringAgentExecution,
2561
+ supported: command.executable,
2562
+ ...command.disabledReason === void 0 ? {} : { reason: command.disabledReason }
2563
+ }));
2564
+ }
2565
+ async invokeCommand(sessionId, name, input) {
2566
+ const catalog = await this.listCommands(sessionId);
2567
+ const canonical = name.replace(/^\/+/u, "").toLowerCase();
2568
+ const command = catalog.commands.find((entry) => entry.name.toLowerCase() === canonical || entry.aliases.some((alias) => alias.toLowerCase() === canonical));
2569
+ if (command === void 0) throw new WorkbenchRuntimeError("unknown-command", "Unknown slash command; choose explicitly to send it as text");
2570
+ if (!command.executable) throw new WorkbenchRuntimeError("unsupported", command.disabledReason ?? "This command is unavailable");
2571
+ if (command.action === "ask") return { kind: "text", text: (await this.ask(sessionId, input)).answer };
2572
+ if (command.action === "insights") return { kind: "text", text: JSON.stringify(await this.insights(sessionId), null, 2) };
2573
+ if (command.action === "compact") {
2574
+ const result = await this.compact(sessionId);
2575
+ return { kind: "text", text: result.success ? `Compacted ${result.tokensRemoved} tokens` : "Context compaction did not produce a smaller summary" };
2576
+ }
2577
+ if (command.action === "configure") {
2578
+ if (this.dependencies.configureSession === void 0) throw new WorkbenchRuntimeError("unsupported", "Session configuration is unavailable");
2579
+ const selected = input.trim();
2580
+ if (command.name === "model" && selected.length > 0) {
2581
+ await this.dependencies.configureSession(sessionId, { modelId: selected });
2582
+ } else {
2583
+ const mode = command.name === "plan" ? "plan" : command.name === "autopilot" ? "autopilot" : selected;
2584
+ const parsedMode = z.enum(["interactive", "plan", "autopilot"]).safeParse(mode);
2585
+ if (!parsedMode.success) throw new WorkbenchRuntimeError("unsupported", "Choose a model or mode using session configuration");
2586
+ await this.dependencies.configureSession(sessionId, { agentMode: parsedMode.data });
2587
+ }
2588
+ return { kind: "completed" };
2589
+ }
2590
+ const { runtime } = await this.#session(sessionId);
2591
+ const release = runtime.admitMutation();
2592
+ try {
2593
+ const result = await this.invokeRuntimeCommand(sessionId, { name, input });
2594
+ switch (result.kind) {
2595
+ case "text":
2596
+ return WorkbenchCommandResultSchema.parse({ kind: "text", text: result.text });
2597
+ case "completed":
2598
+ return result.message === void 0 ? { kind: "completed" } : WorkbenchCommandResultSchema.parse({ kind: "text", text: result.message });
2599
+ case "select-subcommand":
2600
+ return WorkbenchCommandResultSchema.parse({
2601
+ kind: "select-subcommand",
2602
+ choices: result.options.map((option) => ({ name: result.command, label: option.description || option.name, input: option.name }))
2603
+ });
2604
+ case "agent-prompt": {
2605
+ if (this.dependencies.sendCommandPrompt === void 0) {
2606
+ throw new WorkbenchRuntimeError("unsupported", "Agent-prompt commands require the main prompt delivery path");
2607
+ }
2608
+ const sent = await this.dependencies.sendCommandPrompt(sessionId, result);
2609
+ return WorkbenchCommandResultSchema.parse({ kind: "sent", messageId: sent.messageId });
2610
+ }
2611
+ }
2612
+ } finally {
2613
+ release();
2614
+ }
2615
+ }
2616
+ async insights(sessionId) {
2617
+ return projectWorkbenchInsights(await this.readInsights(sessionId));
2618
+ }
2619
+ async compact(sessionId) {
2620
+ const result = await this.compactContext(sessionId);
2621
+ return WorkbenchCompactResultSchema.parse({
2622
+ success: result.outcome === "compacted",
2623
+ tokensRemoved: result.tokensRemoved,
2624
+ messagesRemoved: result.messagesRemoved
2625
+ });
2626
+ }
2627
+ };
2628
+
2629
+ export {
2630
+ isClosedCopilotConnection,
2631
+ isDisposedCopilotSession,
2632
+ isSessionRestoreFailure,
2633
+ sessionRestoreFailure,
2634
+ diagnosticReference,
2635
+ diagnosticRequest,
2636
+ diagnosticError,
2637
+ recordOperationFailure,
2638
+ validateOperationResponse,
2639
+ measureOperationPhase,
2640
+ measureOperationStage,
2641
+ recordOperationTimelineRead,
2642
+ OperationDiagnostics,
2643
+ OperationMetrics,
2644
+ isDisplayableHumanMessage,
2645
+ ConversationNormalizer,
2646
+ SessionRuntime,
2647
+ SessionRuntimeRegistry,
2648
+ LiveTimelineProjection,
2649
+ sanitizeGitRemoteUrl,
2650
+ NotGitDirectoryError,
2651
+ resolveGitProjectIdentity,
2652
+ PromptPreparationError,
2653
+ SESSION_PROJECT_IDENTITY_CONCURRENCY,
2654
+ SessionProjectIdentityError,
2655
+ SessionProjectIdentityReader,
2656
+ readWorkbenchMetrics,
2657
+ projectWorkbenchInsights,
2658
+ SessionWorkbenchRuntime
2659
+ };