laohuang 0.9.2 → 0.9.4
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.
- package/dist/bin.js +1970 -1708
- package/dist/bin.js.map +4 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -7368,342 +7368,156 @@ var require_dist = __commonJS({
|
|
|
7368
7368
|
}
|
|
7369
7369
|
});
|
|
7370
7370
|
|
|
7371
|
+
// src/sqlite-warning-filter.ts
|
|
7372
|
+
var sqliteWarning = "SQLite is an experimental feature and might change at any time";
|
|
7373
|
+
process.emit = new Proxy(process.emit, {
|
|
7374
|
+
apply(target, receiver, args) {
|
|
7375
|
+
const [event, warning] = args;
|
|
7376
|
+
if (event === "warning" && warning instanceof Error && warning.name === "ExperimentalWarning" && warning.message === sqliteWarning && !("code" in warning)) {
|
|
7377
|
+
return false;
|
|
7378
|
+
}
|
|
7379
|
+
return Reflect.apply(target, receiver, args);
|
|
7380
|
+
}
|
|
7381
|
+
});
|
|
7382
|
+
|
|
7371
7383
|
// src/main.ts
|
|
7372
7384
|
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "node:fs";
|
|
7373
|
-
import { dirname as dirname13, join as
|
|
7385
|
+
import { dirname as dirname13, join as join21 } from "node:path";
|
|
7374
7386
|
import { fileURLToPath } from "node:url";
|
|
7375
7387
|
|
|
7376
7388
|
// ../../packages/session/session-context/dist/conversation-history.js
|
|
7377
|
-
import { randomUUID } from "node:crypto";
|
|
7389
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
7378
7390
|
|
|
7379
7391
|
// ../../packages/session/session-context/dist/token-estimator.js
|
|
7380
|
-
import { createHash } from "node:crypto";
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
var
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
if (tools.length === 0) {
|
|
7390
|
-
return 0;
|
|
7391
|
-
}
|
|
7392
|
-
return this.estimateText(canonicalJson(tools.map(({ name, description, parameters }) => ({ name, description, parameters })))) + BLOCK_OVERHEAD;
|
|
7393
|
-
}
|
|
7394
|
-
estimateText(text3) {
|
|
7395
|
-
return Math.ceil(Buffer.byteLength(text3, "utf8") / TEXT_BYTES_PER_TOKEN);
|
|
7396
|
-
}
|
|
7397
|
-
measure(input) {
|
|
7398
|
-
const anchor = input.anchor ?? null;
|
|
7399
|
-
if (anchor !== null && this.isValidAnchor(input, anchor)) {
|
|
7400
|
-
const trailingEntries = input.entries.filter((entry) => entry.seq > anchor.throughSeq);
|
|
7401
|
-
const trailingTokens = this.estimateMessages(messagesForEntries(trailingEntries));
|
|
7402
|
-
return {
|
|
7403
|
-
totalTokens: anchor.contextTokens + trailingTokens,
|
|
7404
|
-
anchorTokens: anchor.contextTokens,
|
|
7405
|
-
trailingTokens,
|
|
7406
|
-
source: "provider_usage"
|
|
7407
|
-
};
|
|
7408
|
-
}
|
|
7409
|
-
const totalTokens = this.estimateMessages(input.messages) + this.estimateTools(input.tools);
|
|
7410
|
-
return {
|
|
7411
|
-
totalTokens,
|
|
7412
|
-
anchorTokens: 0,
|
|
7413
|
-
trailingTokens: totalTokens,
|
|
7414
|
-
source: "estimated"
|
|
7415
|
-
};
|
|
7392
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
7393
|
+
|
|
7394
|
+
// ../../packages/attachment/attachment/dist/errors.js
|
|
7395
|
+
var AttachmentError = class extends Error {
|
|
7396
|
+
code;
|
|
7397
|
+
constructor(code, message, options) {
|
|
7398
|
+
super(message, options);
|
|
7399
|
+
this.name = "AttachmentError";
|
|
7400
|
+
this.code = code;
|
|
7416
7401
|
}
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7402
|
+
};
|
|
7403
|
+
|
|
7404
|
+
// ../../packages/attachment/attachment/dist/types.js
|
|
7405
|
+
var DEFAULT_IMAGE_LIMITS = Object.freeze({
|
|
7406
|
+
maxBytes: 20 * 1024 ** 2,
|
|
7407
|
+
maxImages: 20,
|
|
7408
|
+
maxBatchBytes: 200 * 1024 ** 2,
|
|
7409
|
+
maxPixels: 64e6,
|
|
7410
|
+
maxDimension: 8192
|
|
7411
|
+
});
|
|
7412
|
+
var RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
7413
|
+
var CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7414
|
+
function assertAttachmentContent(value) {
|
|
7415
|
+
if (!value || typeof value !== "object")
|
|
7416
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid attachment content");
|
|
7417
|
+
const block = value;
|
|
7418
|
+
if (block.type !== "image" && block.type !== "file")
|
|
7419
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid attachment kind");
|
|
7420
|
+
assertFileRef(block.ref);
|
|
7421
|
+
if (block.type === "image") {
|
|
7422
|
+
const ref = block.ref;
|
|
7423
|
+
if (!["image/png", "image/jpeg", "image/webp", "image/gif"].includes(String(ref.mimeType)) || !Number.isSafeInteger(ref.width) || Number(ref.width) < 1 || !Number.isSafeInteger(ref.height) || Number(ref.height) < 1 || typeof ref.animated !== "boolean") {
|
|
7424
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid image reference");
|
|
7426
7425
|
}
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
return total + BLOCK_OVERHEAD + this.estimateText(block.text);
|
|
7430
|
-
}
|
|
7431
|
-
return total + BLOCK_OVERHEAD + this.estimateText(`${block.call.name}${canonicalJson(block.call.arguments)}`);
|
|
7432
|
-
}, 0);
|
|
7433
|
-
}
|
|
7434
|
-
isValidAnchor(input, anchor) {
|
|
7435
|
-
return anchor.provider === input.provider && anchor.model === input.model && anchor.systemFingerprint === input.systemFingerprint && anchor.projectInstructionsFingerprint === input.projectInstructionsFingerprint && anchor.toolsFingerprint === input.toolsFingerprint && input.entries.some((entry) => entry.id === anchor.throughEntryId && entry.seq === anchor.throughSeq);
|
|
7426
|
+
if (block.view !== void 0)
|
|
7427
|
+
assertImageView(block.view);
|
|
7436
7428
|
}
|
|
7437
|
-
};
|
|
7438
|
-
function normalizeProviderUsage(usage) {
|
|
7439
|
-
return usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
|
|
7440
|
-
}
|
|
7441
|
-
function fingerprintContextPart(value) {
|
|
7442
|
-
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
7443
|
-
}
|
|
7444
|
-
function canonicalJson(value) {
|
|
7445
|
-
return JSON.stringify(sortStable(value));
|
|
7446
7429
|
}
|
|
7447
|
-
function
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
if (value !== null && typeof value === "object") {
|
|
7452
|
-
const record = value;
|
|
7453
|
-
const sorted = {};
|
|
7454
|
-
for (const key of Object.keys(record).sort()) {
|
|
7455
|
-
sorted[key] = sortStable(record[key]);
|
|
7456
|
-
}
|
|
7457
|
-
return sorted;
|
|
7430
|
+
function assertFileRef(value) {
|
|
7431
|
+
const ref = value;
|
|
7432
|
+
if (!ref || typeof ref !== "object" || typeof ref.id !== "string" || !/^sha256:[a-f0-9]{64}$/.test(ref.id) || !Number.isSafeInteger(ref.bytes) || Number(ref.bytes) < 0 || typeof ref.name !== "string") {
|
|
7433
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid attachment reference");
|
|
7458
7434
|
}
|
|
7459
|
-
return value;
|
|
7460
7435
|
}
|
|
7461
|
-
function
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
if (entry.entryType === "assistant_message") {
|
|
7472
|
-
return [entry.payload.message];
|
|
7473
|
-
}
|
|
7474
|
-
if (entry.entryType === "tool_result") {
|
|
7475
|
-
return [entry.payload.message];
|
|
7436
|
+
function assertImageView(value) {
|
|
7437
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
7438
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid image view");
|
|
7439
|
+
const view = value;
|
|
7440
|
+
if (view.fullResolution !== void 0 && typeof view.fullResolution !== "boolean")
|
|
7441
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid fullResolution");
|
|
7442
|
+
if (view.region !== void 0) {
|
|
7443
|
+
const r = view.region;
|
|
7444
|
+
if (!r || typeof r !== "object" || !["x", "y", "width", "height"].every((k) => Number.isSafeInteger(r[k])) || Number(r.x) < 0 || Number(r.y) < 0 || Number(r.width) < 1 || Number(r.height) < 1) {
|
|
7445
|
+
throw new AttachmentError("INVALID_REFERENCE", "Invalid crop region");
|
|
7476
7446
|
}
|
|
7477
|
-
|
|
7478
|
-
});
|
|
7447
|
+
}
|
|
7479
7448
|
}
|
|
7480
7449
|
|
|
7481
|
-
// ../../packages/
|
|
7482
|
-
var
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7450
|
+
// ../../packages/attachment/attachment/dist/attachment-store.js
|
|
7451
|
+
var AttachmentStore = class {
|
|
7452
|
+
};
|
|
7453
|
+
|
|
7454
|
+
// ../../packages/session/session-store/dist/session-metadata.js
|
|
7455
|
+
function normalizeSessionTitle(input) {
|
|
7456
|
+
if (new RegExp("\\p{Cc}|\\u2028|\\u2029", "u").test(input)) {
|
|
7457
|
+
throw new Error("Session name must be a single line without control characters.");
|
|
7487
7458
|
}
|
|
7488
|
-
|
|
7489
|
-
|
|
7459
|
+
const title = input.trim();
|
|
7460
|
+
if (title.length === 0) {
|
|
7461
|
+
throw new Error("Session name must not be empty.");
|
|
7490
7462
|
}
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7463
|
+
return title;
|
|
7464
|
+
}
|
|
7465
|
+
function sessionTitleAt(items, throughSeq = Infinity) {
|
|
7466
|
+
let title = null;
|
|
7467
|
+
for (const item of items) {
|
|
7468
|
+
if (item.seq > throughSeq) {
|
|
7469
|
+
break;
|
|
7496
7470
|
}
|
|
7497
|
-
if (
|
|
7498
|
-
const
|
|
7499
|
-
if (
|
|
7500
|
-
|
|
7501
|
-
return;
|
|
7471
|
+
if (item.kind === "record" && item.recordType === "session_name_changed") {
|
|
7472
|
+
const value = item.payload["title"];
|
|
7473
|
+
if (typeof value !== "string") {
|
|
7474
|
+
throw new Error("Invalid session name record.");
|
|
7502
7475
|
}
|
|
7503
|
-
|
|
7504
|
-
if (this.#tokens === null)
|
|
7505
|
-
return;
|
|
7506
|
-
if (entry.entryType === "user_message" || entry.entryType === "assistant_message" || entry.entryType === "tool_result" || entry.entryType === "reminder" || entry.entryType === "skill_context") {
|
|
7507
|
-
this.#tokens += estimateMessage(entry.payload.message);
|
|
7476
|
+
title = normalizeSessionTitle(value);
|
|
7508
7477
|
}
|
|
7509
7478
|
}
|
|
7510
|
-
|
|
7511
|
-
function validUsageTokens(usage) {
|
|
7512
|
-
if (usage === void 0 || usage === null)
|
|
7513
|
-
return null;
|
|
7514
|
-
const fields = [
|
|
7515
|
-
usage.inputTokens,
|
|
7516
|
-
usage.outputTokens,
|
|
7517
|
-
usage.cacheReadTokens ?? 0,
|
|
7518
|
-
usage.cacheWriteTokens ?? 0
|
|
7519
|
-
];
|
|
7520
|
-
if (!fields.every((value) => Number.isSafeInteger(value) && value >= 0))
|
|
7521
|
-
return null;
|
|
7522
|
-
const total = normalizeProviderUsage(usage);
|
|
7523
|
-
return Number.isSafeInteger(total) && total > 0 ? total : null;
|
|
7524
|
-
}
|
|
7525
|
-
function estimateMessage(message) {
|
|
7526
|
-
if (message.role === "system")
|
|
7527
|
-
return 0;
|
|
7528
|
-
if (message.role !== "assistant")
|
|
7529
|
-
return Math.ceil(message.content.length / 4);
|
|
7530
|
-
const chars = message.content.reduce((sum, block) => {
|
|
7531
|
-
if (block.type === "tool-call") {
|
|
7532
|
-
return sum + block.call.name.length + JSON.stringify(block.call.arguments).length;
|
|
7533
|
-
}
|
|
7534
|
-
return sum + block.text.length;
|
|
7535
|
-
}, 0);
|
|
7536
|
-
return Math.ceil(chars / 4);
|
|
7479
|
+
return title;
|
|
7537
7480
|
}
|
|
7538
7481
|
|
|
7539
|
-
// ../../packages/session/session-
|
|
7540
|
-
var
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
return history;
|
|
7579
|
-
}
|
|
7580
|
-
appendSystemContext(input) {
|
|
7581
|
-
return this.append({
|
|
7582
|
-
entryType: "system_context",
|
|
7583
|
-
payload: input
|
|
7584
|
-
});
|
|
7585
|
-
}
|
|
7586
|
-
appendProjectInstructions(input) {
|
|
7587
|
-
if (input.message.content === "") {
|
|
7588
|
-
return null;
|
|
7589
|
-
}
|
|
7590
|
-
return this.append({
|
|
7591
|
-
entryType: "project_instructions",
|
|
7592
|
-
payload: {
|
|
7593
|
-
message: input.message,
|
|
7594
|
-
files: input.files,
|
|
7595
|
-
supersedesEntryIds: input.supersedesEntryIds ?? []
|
|
7596
|
-
}
|
|
7597
|
-
});
|
|
7598
|
-
}
|
|
7599
|
-
appendUser(input) {
|
|
7600
|
-
return this.append({
|
|
7601
|
-
entryType: "user_message",
|
|
7602
|
-
payload: input
|
|
7603
|
-
});
|
|
7604
|
-
}
|
|
7605
|
-
stageUser(input) {
|
|
7606
|
-
const token = randomUUID().replaceAll("-", "");
|
|
7607
|
-
this.#staged.set(token, input);
|
|
7608
|
-
return {
|
|
7609
|
-
token,
|
|
7610
|
-
message: input.message,
|
|
7611
|
-
inputEventIds: input.inputEventIds
|
|
7612
|
-
};
|
|
7613
|
-
}
|
|
7614
|
-
commitStagedUser(token) {
|
|
7615
|
-
const staged = this.#staged.get(token);
|
|
7616
|
-
if (staged === void 0) {
|
|
7617
|
-
throw new Error(`unknown staged user message: ${token}`);
|
|
7618
|
-
}
|
|
7619
|
-
this.#staged.delete(token);
|
|
7620
|
-
return this.appendUser(staged);
|
|
7621
|
-
}
|
|
7622
|
-
discardStagedUser(token) {
|
|
7623
|
-
this.#staged.delete(token);
|
|
7624
|
-
}
|
|
7625
|
-
appendAssistant(input) {
|
|
7626
|
-
return this.append({
|
|
7627
|
-
entryType: "assistant_message",
|
|
7628
|
-
payload: input
|
|
7629
|
-
});
|
|
7630
|
-
}
|
|
7631
|
-
appendToolResults(input) {
|
|
7632
|
-
return input.messages.map((message) => this.append({
|
|
7633
|
-
entryType: "tool_result",
|
|
7634
|
-
payload: {
|
|
7635
|
-
message,
|
|
7636
|
-
requestId: input.requestId,
|
|
7637
|
-
recovered: input.recovered
|
|
7638
|
-
}
|
|
7639
|
-
}));
|
|
7640
|
-
}
|
|
7641
|
-
appendReminder(input) {
|
|
7642
|
-
return this.append({
|
|
7643
|
-
entryType: "reminder",
|
|
7644
|
-
payload: input
|
|
7645
|
-
});
|
|
7646
|
-
}
|
|
7647
|
-
appendToolDefinitions(input) {
|
|
7648
|
-
return this.append({ entryType: "tool_definitions", payload: input });
|
|
7649
|
-
}
|
|
7650
|
-
appendToolCatalog(input) {
|
|
7651
|
-
return this.append({ entryType: "tool_catalog", payload: input });
|
|
7652
|
-
}
|
|
7653
|
-
appendSkillContext(input) {
|
|
7654
|
-
return this.append({ entryType: "skill_context", payload: input });
|
|
7655
|
-
}
|
|
7656
|
-
appendCompaction(input) {
|
|
7657
|
-
return this.append({
|
|
7658
|
-
entryType: "compaction",
|
|
7659
|
-
payload: input
|
|
7660
|
-
});
|
|
7661
|
-
}
|
|
7662
|
-
entries() {
|
|
7663
|
-
return [...this.#entries];
|
|
7664
|
-
}
|
|
7665
|
-
offloadImages(keys2) {
|
|
7666
|
-
if (keys2.length === 0)
|
|
7667
|
-
return;
|
|
7668
|
-
this.append({ entryType: "image_offload", payload: { keys: [...keys2] } });
|
|
7669
|
-
this.#journal.flush();
|
|
7670
|
-
}
|
|
7671
|
-
get contextTokens() {
|
|
7672
|
-
return this.#contextUsage.tokens;
|
|
7673
|
-
}
|
|
7674
|
-
onChange(listener) {
|
|
7675
|
-
this.#listeners.add(listener);
|
|
7676
|
-
return () => {
|
|
7677
|
-
this.#listeners.delete(listener);
|
|
7678
|
-
};
|
|
7679
|
-
}
|
|
7680
|
-
append(input) {
|
|
7681
|
-
const entry = this.#journal.appendEntry(input);
|
|
7682
|
-
this.#entries.push(entry);
|
|
7683
|
-
this.#contextUsage.append(entry);
|
|
7684
|
-
for (const listener of this.#listeners)
|
|
7685
|
-
listener();
|
|
7686
|
-
return entry;
|
|
7687
|
-
}
|
|
7688
|
-
};
|
|
7689
|
-
|
|
7690
|
-
// ../../packages/llm/llm/dist/model-contracts.js
|
|
7691
|
-
var ModelStreamError = class extends Error {
|
|
7692
|
-
/** True when the stream had already delivered deltas before failing. */
|
|
7693
|
-
hadDelta;
|
|
7694
|
-
constructor(message, options = {}) {
|
|
7695
|
-
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
7696
|
-
this.name = "ModelStreamError";
|
|
7697
|
-
this.hadDelta = options.hadDelta ?? false;
|
|
7698
|
-
}
|
|
7699
|
-
};
|
|
7700
|
-
var ModelStreamCancelled = class extends ModelStreamError {
|
|
7701
|
-
constructor(message, options = {}) {
|
|
7702
|
-
super(message, options);
|
|
7703
|
-
this.name = "ModelStreamCancelled";
|
|
7704
|
-
}
|
|
7705
|
-
};
|
|
7706
|
-
var REASONING_EFFORTS = [
|
|
7482
|
+
// ../../packages/session/session-store/dist/schema.js
|
|
7483
|
+
var ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
7484
|
+
"context_reset",
|
|
7485
|
+
"image_offload",
|
|
7486
|
+
"skill_context",
|
|
7487
|
+
"tool_definitions",
|
|
7488
|
+
"tool_catalog",
|
|
7489
|
+
"system_context",
|
|
7490
|
+
"project_instructions",
|
|
7491
|
+
"user_message",
|
|
7492
|
+
"assistant_message",
|
|
7493
|
+
"tool_result",
|
|
7494
|
+
"reminder",
|
|
7495
|
+
"compaction"
|
|
7496
|
+
]);
|
|
7497
|
+
var RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
7498
|
+
"turn_started",
|
|
7499
|
+
"turn_finished",
|
|
7500
|
+
"model_request",
|
|
7501
|
+
"model_response",
|
|
7502
|
+
"tool_started",
|
|
7503
|
+
"tool_finished",
|
|
7504
|
+
"provider_changed",
|
|
7505
|
+
"model_changed",
|
|
7506
|
+
"reasoning_effort_changed",
|
|
7507
|
+
"cwd_changed",
|
|
7508
|
+
"usage",
|
|
7509
|
+
"error",
|
|
7510
|
+
"cancelled",
|
|
7511
|
+
"queue_enqueued",
|
|
7512
|
+
"queue_started",
|
|
7513
|
+
"queue_finished",
|
|
7514
|
+
"compaction_started",
|
|
7515
|
+
"compaction_finished",
|
|
7516
|
+
"session_name_changed",
|
|
7517
|
+
"session_closed"
|
|
7518
|
+
]);
|
|
7519
|
+
var ORIGINS = /* @__PURE__ */ new Set(["new", "fork", "clone", "import"]);
|
|
7520
|
+
var REASONING_EFFORTS = /* @__PURE__ */ new Set([
|
|
7707
7521
|
"off",
|
|
7708
7522
|
"minimal",
|
|
7709
7523
|
"low",
|
|
@@ -7711,1590 +7525,1812 @@ var REASONING_EFFORTS = [
|
|
|
7711
7525
|
"high",
|
|
7712
7526
|
"xhigh",
|
|
7713
7527
|
"max"
|
|
7714
|
-
];
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
const available = supported.length === 0 ? ["off"] : supported;
|
|
7720
|
-
if (available.includes(requested)) {
|
|
7721
|
-
return requested;
|
|
7528
|
+
]);
|
|
7529
|
+
var SessionSchemaError = class extends Error {
|
|
7530
|
+
constructor(message) {
|
|
7531
|
+
super(message);
|
|
7532
|
+
this.name = "SessionSchemaError";
|
|
7722
7533
|
}
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
}
|
|
7534
|
+
};
|
|
7535
|
+
function parseSessionHeader(value) {
|
|
7536
|
+
const input = objectOf(value, "session header");
|
|
7537
|
+
if (input["schemaVersion"] !== 1) {
|
|
7538
|
+
throw new SessionSchemaError("unsupported session schema version");
|
|
7729
7539
|
}
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
if (available.includes(candidate)) {
|
|
7733
|
-
return candidate;
|
|
7734
|
-
}
|
|
7540
|
+
if (input["type"] !== "session_header") {
|
|
7541
|
+
throw new SessionSchemaError("invalid session header type");
|
|
7735
7542
|
}
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
var ModelError = class extends ModelStreamError {
|
|
7747
|
-
kind;
|
|
7748
|
-
constructor(message, options) {
|
|
7749
|
-
super(message, { hadDelta: options.hadDelta ?? false, cause: options.cause });
|
|
7750
|
-
this.name = "ModelError";
|
|
7751
|
-
this.kind = options.kind;
|
|
7543
|
+
requireString(input, "sessionId");
|
|
7544
|
+
requireString(input, "createdAt");
|
|
7545
|
+
requireString(input, "initialCwd");
|
|
7546
|
+
requireString(input, "projectRoot");
|
|
7547
|
+
requireString(input, "projectKey");
|
|
7548
|
+
requireString(input, "appVersion");
|
|
7549
|
+
requireString(input, "provider");
|
|
7550
|
+
requireString(input, "model");
|
|
7551
|
+
if (!REASONING_EFFORTS.has(stringValue(input, "reasoningEffort"))) {
|
|
7552
|
+
throw new SessionSchemaError("invalid reasoning effort");
|
|
7752
7553
|
}
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
var NETWORK_PATTERN = /fetch failed|network|econnreset|econnrefused|etimedout|socket hang up|timed out/i;
|
|
7756
|
-
function readStatus(record) {
|
|
7757
|
-
let status = record["status_code"];
|
|
7758
|
-
if (status === null || status === void 0) {
|
|
7759
|
-
const response = record["response"];
|
|
7760
|
-
if (response !== null && typeof response === "object") {
|
|
7761
|
-
status = response["status_code"];
|
|
7762
|
-
}
|
|
7554
|
+
if (!ORIGINS.has(stringValue(input, "origin"))) {
|
|
7555
|
+
throw new SessionSchemaError("invalid session origin");
|
|
7763
7556
|
}
|
|
7764
|
-
if (
|
|
7765
|
-
|
|
7557
|
+
if (input["parentSessionId"] !== void 0) {
|
|
7558
|
+
requireString(input, "parentSessionId");
|
|
7766
7559
|
}
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
while (current !== null && current !== void 0 && (typeof current === "object" || typeof current === "function") && !seen.has(current)) {
|
|
7773
|
-
seen.add(current);
|
|
7774
|
-
const record = current;
|
|
7775
|
-
const status = readStatus(record);
|
|
7776
|
-
if (status === 401 || status === 403 || record["name"] === "AuthenticationError") {
|
|
7777
|
-
return "authentication";
|
|
7560
|
+
if (input["forkedFrom"] !== void 0) {
|
|
7561
|
+
const forkedFrom = objectOf(input["forkedFrom"], "forkedFrom");
|
|
7562
|
+
requireString(forkedFrom, "sessionId");
|
|
7563
|
+
if (forkedFrom["seq"] !== void 0) {
|
|
7564
|
+
requirePositiveInteger(forkedFrom, "seq");
|
|
7778
7565
|
}
|
|
7779
|
-
if (
|
|
7780
|
-
|
|
7566
|
+
if (forkedFrom["entryId"] !== void 0) {
|
|
7567
|
+
requireString(forkedFrom, "entryId");
|
|
7781
7568
|
}
|
|
7782
|
-
const
|
|
7783
|
-
if (
|
|
7784
|
-
|
|
7785
|
-
}
|
|
7786
|
-
if (CONTEXT_OVERFLOW_PATTERN.test(message)) {
|
|
7787
|
-
return "context_overflow";
|
|
7788
|
-
}
|
|
7789
|
-
if (typeof status === "number" && status >= 500) {
|
|
7790
|
-
return "server";
|
|
7791
|
-
}
|
|
7792
|
-
if (record["name"] === "APIConnectionError" || NETWORK_PATTERN.test(message)) {
|
|
7793
|
-
return "retryable";
|
|
7569
|
+
const mode = stringValue(forkedFrom, "mode");
|
|
7570
|
+
if (mode !== "before" && mode !== "at" && mode !== "clone") {
|
|
7571
|
+
throw new SessionSchemaError("invalid fork mode");
|
|
7794
7572
|
}
|
|
7795
|
-
current = record["cause"];
|
|
7796
7573
|
}
|
|
7797
|
-
return
|
|
7798
|
-
}
|
|
7799
|
-
function modelErrorKind(error) {
|
|
7800
|
-
return error instanceof ModelError ? error.kind : classifyModelError(error);
|
|
7574
|
+
return input;
|
|
7801
7575
|
}
|
|
7802
|
-
function
|
|
7803
|
-
|
|
7804
|
-
|
|
7576
|
+
function parseSessionItem(value, expectedSessionId) {
|
|
7577
|
+
const input = objectOf(value, "session item");
|
|
7578
|
+
if (input["schemaVersion"] !== 1) {
|
|
7579
|
+
throw new SessionSchemaError("unsupported session schema version");
|
|
7805
7580
|
}
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
model: message.model,
|
|
7810
|
-
content: message.content
|
|
7811
|
-
};
|
|
7812
|
-
}
|
|
7813
|
-
|
|
7814
|
-
// ../../packages/llm/llm/dist/model-runtime.js
|
|
7815
|
-
var ModelRuntime = class {
|
|
7816
|
-
adapter;
|
|
7817
|
-
retryDelaysMs;
|
|
7818
|
-
sleep;
|
|
7819
|
-
constructor(adapter, options = {}) {
|
|
7820
|
-
this.adapter = adapter;
|
|
7821
|
-
this.retryDelaysMs = validateRetryDelays(options.retryDelaysMs ?? [250, 1e3]);
|
|
7822
|
-
this.sleep = options.sleep ?? defaultSleep;
|
|
7581
|
+
const sessionId = requireString(input, "sessionId");
|
|
7582
|
+
if (expectedSessionId !== void 0 && sessionId !== expectedSessionId) {
|
|
7583
|
+
throw new SessionSchemaError("session id does not match header");
|
|
7823
7584
|
}
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7585
|
+
requirePositiveInteger(input, "seq");
|
|
7586
|
+
requireString(input, "id");
|
|
7587
|
+
requireString(input, "timestamp");
|
|
7588
|
+
const kind = stringValue(input, "kind");
|
|
7589
|
+
if (kind === "entry") {
|
|
7590
|
+
const entryType = stringValue(input, "entryType");
|
|
7591
|
+
if (!ENTRY_TYPES.has(entryType)) {
|
|
7592
|
+
throw new SessionSchemaError("invalid session entry type");
|
|
7827
7593
|
}
|
|
7828
|
-
const
|
|
7829
|
-
|
|
7830
|
-
const
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7594
|
+
const entryPayload = objectOf(input["payload"], "entry payload");
|
|
7595
|
+
if (entryType === "context_reset") {
|
|
7596
|
+
const boundary = entryPayload.resetThroughSeq;
|
|
7597
|
+
if (typeof boundary !== "number" || !Number.isSafeInteger(boundary) || boundary < 0 || boundary >= input.seq || entryPayload.reason !== "user_clear" || Object.keys(entryPayload).some((key) => key !== "resetThroughSeq" && key !== "reason")) {
|
|
7598
|
+
throw new SessionSchemaError("invalid legacy context reset");
|
|
7599
|
+
}
|
|
7600
|
+
}
|
|
7601
|
+
if (entryType === "image_offload" && (!Array.isArray(entryPayload.keys) || entryPayload.keys.some((key) => typeof key !== "string"))) {
|
|
7602
|
+
throw new SessionSchemaError("invalid image offload keys");
|
|
7603
|
+
}
|
|
7604
|
+
if (entryPayload.message !== void 0) {
|
|
7605
|
+
const message = objectOf(entryPayload.message, "message");
|
|
7606
|
+
if (message.attachments !== void 0) {
|
|
7607
|
+
if (message.role !== "user" && message.role !== "tool-result" || !Array.isArray(message.attachments))
|
|
7608
|
+
throw new SessionSchemaError("invalid message attachments");
|
|
7609
|
+
for (const attachment of message.attachments)
|
|
7610
|
+
assertAttachmentContent(attachment);
|
|
7611
|
+
if (typeof message.attachmentKey !== "string" || !message.attachmentKey)
|
|
7612
|
+
throw new SessionSchemaError("missing attachment occurrence key");
|
|
7613
|
+
}
|
|
7614
|
+
}
|
|
7615
|
+
if (entryType === "skill_context") {
|
|
7616
|
+
const payload = objectOf(input["payload"], "skill payload");
|
|
7617
|
+
const message = objectOf(payload["message"], "skill message");
|
|
7618
|
+
const metadata2 = objectOf(message["skillContext"], "skill metadata");
|
|
7619
|
+
if (message["role"] !== "user" || typeof message["content"] !== "string" || !["catalog", "activation", "invalidation"].includes(String(metadata2["kind"])) || typeof metadata2["key"] !== "string")
|
|
7620
|
+
throw new SessionSchemaError("invalid skill context");
|
|
7621
|
+
}
|
|
7622
|
+
if (entryType === "tool_definitions" || entryType === "tool_catalog") {
|
|
7623
|
+
const payload = objectOf(input["payload"], "tool context payload");
|
|
7624
|
+
const message = objectOf(payload["message"], "tool context message");
|
|
7625
|
+
if (typeof message["content"] !== "string")
|
|
7626
|
+
throw new SessionSchemaError("tool context content must be text");
|
|
7627
|
+
if (entryType === "tool_definitions") {
|
|
7628
|
+
if (message["role"] !== "system" || !Array.isArray(message["toolDefinitions"]))
|
|
7629
|
+
throw new SessionSchemaError("invalid tool definitions message");
|
|
7630
|
+
for (const raw of message["toolDefinitions"]) {
|
|
7631
|
+
const definition = objectOf(raw, "loaded tool");
|
|
7632
|
+
requireString(definition, "version");
|
|
7633
|
+
const spec2 = objectOf(definition["spec"], "tool spec");
|
|
7634
|
+
requireString(spec2, "name");
|
|
7635
|
+
if (typeof spec2["description"] !== "string" || !Array.isArray(spec2["promptGuidelines"]) || spec2["promptGuidelines"].some((value2) => typeof value2 !== "string"))
|
|
7636
|
+
throw new SessionSchemaError("invalid tool spec");
|
|
7637
|
+
objectOf(spec2["parameters"], "tool parameters");
|
|
7638
|
+
if (spec2["catalog"] !== void 0) {
|
|
7639
|
+
const catalog = objectOf(spec2["catalog"], "tool catalog metadata");
|
|
7640
|
+
for (const key of ["source", "originalName", "binding"])
|
|
7641
|
+
requireString(catalog, key);
|
|
7642
|
+
if (catalog["exposure"] !== "direct" && catalog["exposure"] !== "deferred")
|
|
7643
|
+
throw new SessionSchemaError("invalid exposure");
|
|
7644
|
+
}
|
|
7645
|
+
}
|
|
7646
|
+
} else {
|
|
7647
|
+
if (message["role"] !== "user")
|
|
7648
|
+
throw new SessionSchemaError("invalid catalog message role");
|
|
7649
|
+
const catalog = objectOf(message["toolCatalog"], "tool catalog");
|
|
7650
|
+
if (catalog["mode"] !== "full" && catalog["mode"] !== "deferred")
|
|
7651
|
+
throw new SessionSchemaError("invalid tool mode");
|
|
7652
|
+
for (const version of Object.values(objectOf(catalog["tools"], "tool versions")))
|
|
7653
|
+
if (typeof version !== "string")
|
|
7654
|
+
throw new SessionSchemaError("invalid tool version");
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
return input;
|
|
7658
|
+
}
|
|
7659
|
+
if (kind === "record") {
|
|
7660
|
+
const recordType = stringValue(input, "recordType");
|
|
7661
|
+
if (!RECORD_TYPES.has(recordType)) {
|
|
7662
|
+
throw new SessionSchemaError("invalid session record type");
|
|
7663
|
+
}
|
|
7664
|
+
const payload = objectOf(input["payload"], "record payload");
|
|
7665
|
+
if (recordType === "session_name_changed") {
|
|
7666
|
+
const title = payload["title"];
|
|
7667
|
+
if (typeof title !== "string") {
|
|
7668
|
+
throw new SessionSchemaError("invalid session name record");
|
|
7669
|
+
}
|
|
7847
7670
|
try {
|
|
7848
|
-
|
|
7671
|
+
if (normalizeSessionTitle(title) !== title) {
|
|
7672
|
+
throw new SessionSchemaError("session name must already be normalized");
|
|
7673
|
+
}
|
|
7849
7674
|
} catch (error) {
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
throw normalized;
|
|
7675
|
+
if (error instanceof SessionSchemaError) {
|
|
7676
|
+
throw error;
|
|
7853
7677
|
}
|
|
7854
|
-
|
|
7855
|
-
request.onDelta?.("model_retry_scheduled", {
|
|
7856
|
-
attempt: attempt + 1,
|
|
7857
|
-
max_attempts: maxAttempts,
|
|
7858
|
-
delay_ms: delayMs,
|
|
7859
|
-
error_kind: normalized.kind
|
|
7860
|
-
});
|
|
7861
|
-
await this.sleep(delayMs, request.cancelToken ?? null);
|
|
7678
|
+
throw new SessionSchemaError("invalid session name record");
|
|
7862
7679
|
}
|
|
7863
7680
|
}
|
|
7864
|
-
|
|
7865
|
-
}
|
|
7866
|
-
};
|
|
7867
|
-
function forwardEvent(event, handler) {
|
|
7868
|
-
if (event.type === "text-delta") {
|
|
7869
|
-
handler?.("model_text_delta", { text: event.text });
|
|
7870
|
-
} else if (event.type === "reasoning-delta") {
|
|
7871
|
-
handler?.("model_reasoning_delta", { text: event.text });
|
|
7872
|
-
} else if (event.type === "tool-call-delta") {
|
|
7873
|
-
handler?.("model_tool_call_delta", {
|
|
7874
|
-
index: event.index,
|
|
7875
|
-
id: event.id,
|
|
7876
|
-
name: event.name ?? null,
|
|
7877
|
-
arguments: event.argumentsDelta
|
|
7878
|
-
});
|
|
7879
|
-
} else {
|
|
7880
|
-
handler?.("model_response_validating", {});
|
|
7681
|
+
return input;
|
|
7881
7682
|
}
|
|
7683
|
+
throw new SessionSchemaError("invalid session item kind");
|
|
7882
7684
|
}
|
|
7883
|
-
function
|
|
7884
|
-
|
|
7885
|
-
return error;
|
|
7886
|
-
}
|
|
7887
|
-
if (cancelToken?.isCancelled()) {
|
|
7888
|
-
return new ModelStreamCancelled(cancelToken.reason || "cancelled", { cause: error });
|
|
7889
|
-
}
|
|
7890
|
-
if (error instanceof ModelError) {
|
|
7891
|
-
return error;
|
|
7892
|
-
}
|
|
7893
|
-
if (error instanceof ModelStreamError) {
|
|
7894
|
-
return new ModelError(error.message, {
|
|
7895
|
-
kind: modelErrorKind(error),
|
|
7896
|
-
hadDelta: error.hadDelta,
|
|
7897
|
-
cause: error
|
|
7898
|
-
});
|
|
7899
|
-
}
|
|
7900
|
-
return new ModelError(errorMessage(error), {
|
|
7901
|
-
kind: modelErrorKind(error),
|
|
7902
|
-
cause: error
|
|
7903
|
-
});
|
|
7685
|
+
function contextResetBoundary(entries) {
|
|
7686
|
+
return entries.reduce((boundary, entry) => entry.entryType === "context_reset" ? Math.max(boundary, entry.payload.resetThroughSeq) : boundary, 0);
|
|
7904
7687
|
}
|
|
7905
|
-
function
|
|
7906
|
-
if (
|
|
7907
|
-
|
|
7908
|
-
}
|
|
7909
|
-
if (error.hadDelta) {
|
|
7910
|
-
return false;
|
|
7688
|
+
function objectOf(value, label) {
|
|
7689
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
7690
|
+
throw new SessionSchemaError(`${label} must be an object`);
|
|
7911
7691
|
}
|
|
7912
|
-
return
|
|
7692
|
+
return value;
|
|
7913
7693
|
}
|
|
7914
|
-
function
|
|
7915
|
-
|
|
7916
|
-
|
|
7694
|
+
function requireString(input, key) {
|
|
7695
|
+
const value = input[key];
|
|
7696
|
+
if (typeof value !== "string" || value === "") {
|
|
7697
|
+
throw new SessionSchemaError(`${key} must be a non-empty string`);
|
|
7917
7698
|
}
|
|
7918
7699
|
return value;
|
|
7919
7700
|
}
|
|
7920
|
-
function
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
}
|
|
7924
|
-
for (const delay2 of value) {
|
|
7925
|
-
if (!Number.isInteger(delay2) || delay2 < 0 || !Number.isFinite(delay2)) {
|
|
7926
|
-
throw new RangeError("retryDelaysMs must contain finite non-negative integers");
|
|
7927
|
-
}
|
|
7928
|
-
}
|
|
7929
|
-
return [...value];
|
|
7701
|
+
function stringValue(input, key) {
|
|
7702
|
+
const value = requireString(input, key);
|
|
7703
|
+
return value;
|
|
7930
7704
|
}
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
const cancelled = await cancelToken?.wait(delayMs);
|
|
7936
|
-
if (cancelled) {
|
|
7937
|
-
throw new ModelStreamCancelled(cancelToken?.reason || "cancelled");
|
|
7938
|
-
}
|
|
7939
|
-
if (cancelToken === null) {
|
|
7940
|
-
await new Promise((resolve15) => setTimeout(resolve15, delayMs));
|
|
7705
|
+
function requirePositiveInteger(input, key) {
|
|
7706
|
+
const value = input[key];
|
|
7707
|
+
if (!Number.isInteger(value) || typeof value !== "number" || value < 1) {
|
|
7708
|
+
throw new SessionSchemaError(`${key} must be a positive integer`);
|
|
7941
7709
|
}
|
|
7942
|
-
|
|
7943
|
-
function errorMessage(error) {
|
|
7944
|
-
return error instanceof Error ? error.message : String(error);
|
|
7710
|
+
return value;
|
|
7945
7711
|
}
|
|
7946
7712
|
|
|
7947
|
-
// ../../packages/session/session-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7713
|
+
// ../../packages/session/session-store/dist/attachment-references.js
|
|
7714
|
+
import { lstatSync, readdirSync } from "node:fs";
|
|
7715
|
+
import { join } from "node:path";
|
|
7716
|
+
|
|
7717
|
+
// ../../packages/session/session-store/dist/session-reader.js
|
|
7718
|
+
import { readFileSync } from "node:fs";
|
|
7719
|
+
var SessionCorruptError = class extends Error {
|
|
7720
|
+
path;
|
|
7721
|
+
line;
|
|
7722
|
+
constructor(path5, line2, message, options = {}) {
|
|
7723
|
+
super(`${path5}:${line2}: ${message}`, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
7724
|
+
this.name = "SessionCorruptError";
|
|
7725
|
+
this.path = path5;
|
|
7726
|
+
this.line = line2;
|
|
7954
7727
|
}
|
|
7955
7728
|
};
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
const system = latestActive(sorted, "system_context", superseded);
|
|
7963
|
-
if (system !== null) {
|
|
7964
|
-
selected.push({ entryId: system.id, message: system.payload.message });
|
|
7965
|
-
}
|
|
7966
|
-
for (const entry of sorted) {
|
|
7967
|
-
if (entry.entryType === "project_instructions" && !superseded.has(entry.id)) {
|
|
7968
|
-
selected.push({ entryId: entry.id, message: entry.payload.message });
|
|
7969
|
-
}
|
|
7970
|
-
}
|
|
7971
|
-
if (activeCompaction !== null) {
|
|
7972
|
-
selected.push({
|
|
7973
|
-
entryId: activeCompaction.id,
|
|
7974
|
-
message: {
|
|
7975
|
-
role: "user",
|
|
7976
|
-
content: `<conversation_summary>
|
|
7977
|
-
${activeCompaction.payload.summary}
|
|
7978
|
-
</conversation_summary>`
|
|
7979
|
-
}
|
|
7980
|
-
});
|
|
7981
|
-
}
|
|
7982
|
-
for (const entry of conversationTail(sorted, activeCompaction)) {
|
|
7983
|
-
const message = modelMessageForEntry(entry, input.currentProvider, input.currentModel);
|
|
7984
|
-
if (message !== null) {
|
|
7985
|
-
selected.push({ entryId: entry.id, message });
|
|
7986
|
-
}
|
|
7987
|
-
}
|
|
7988
|
-
validateToolPairs(selected);
|
|
7989
|
-
const offloaded = new Set(sorted.flatMap((entry) => entry.entryType === "image_offload" ? entry.payload.keys : []));
|
|
7990
|
-
return {
|
|
7991
|
-
messages: selected.map((item) => projectImageOffloads(item.message, offloaded)),
|
|
7992
|
-
sourceEntryIds: selected.map((item) => item.entryId),
|
|
7993
|
-
activeCompactionId: activeCompaction?.id ?? null
|
|
7994
|
-
};
|
|
7729
|
+
function readSessionFile(path5) {
|
|
7730
|
+
const text3 = readFileSync(path5, "utf8");
|
|
7731
|
+
const endsWithNewline = text3.endsWith("\n");
|
|
7732
|
+
const rawLines = text3.split("\n");
|
|
7733
|
+
if (endsWithNewline) {
|
|
7734
|
+
rawLines.pop();
|
|
7995
7735
|
}
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
7736
|
+
if (rawLines.length === 0 || rawLines[0] === "") {
|
|
7737
|
+
throw new SessionCorruptError(path5, 1, "missing session header");
|
|
7738
|
+
}
|
|
7739
|
+
const header = parseLine(path5, 1, rawLines[0], parseSessionHeader);
|
|
7740
|
+
const items = [];
|
|
7741
|
+
let ignoredTornTail = false;
|
|
7742
|
+
let previousSeq = 0;
|
|
7743
|
+
for (let index = 1; index < rawLines.length; index += 1) {
|
|
7744
|
+
const lineNumber = index + 1;
|
|
7745
|
+
const line2 = rawLines[index];
|
|
7746
|
+
if (line2.trim() === "") {
|
|
7747
|
+
continue;
|
|
8002
7748
|
}
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
7749
|
+
try {
|
|
7750
|
+
const item = parseSessionItem(JSON.parse(line2), header.sessionId);
|
|
7751
|
+
if (item.seq !== previousSeq + 1) {
|
|
7752
|
+
throw new SessionSchemaError("session item seq must be contiguous");
|
|
8006
7753
|
}
|
|
7754
|
+
previousSeq = item.seq;
|
|
7755
|
+
items.push(item);
|
|
7756
|
+
} catch (error) {
|
|
7757
|
+
if (!endsWithNewline && index === rawLines.length - 1) {
|
|
7758
|
+
ignoredTornTail = true;
|
|
7759
|
+
break;
|
|
7760
|
+
}
|
|
7761
|
+
throw corrupt(path5, lineNumber, error);
|
|
8007
7762
|
}
|
|
8008
7763
|
}
|
|
8009
|
-
return
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
}
|
|
8017
|
-
}
|
|
8018
|
-
return null;
|
|
7764
|
+
return {
|
|
7765
|
+
header,
|
|
7766
|
+
items,
|
|
7767
|
+
lastSeq: previousSeq,
|
|
7768
|
+
ignoredTornTail,
|
|
7769
|
+
openToolCalls: findOpenToolCalls(items)
|
|
7770
|
+
};
|
|
8019
7771
|
}
|
|
8020
|
-
function
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
}
|
|
7772
|
+
function parseLine(path5, line2, raw, parse3) {
|
|
7773
|
+
try {
|
|
7774
|
+
return parse3(JSON.parse(raw));
|
|
7775
|
+
} catch (error) {
|
|
7776
|
+
throw corrupt(path5, line2, error);
|
|
8026
7777
|
}
|
|
8027
|
-
return null;
|
|
8028
|
-
}
|
|
8029
|
-
function conversationTail(entries, activeCompaction) {
|
|
8030
|
-
const boundary = activeCompaction?.payload.retainedFromSeq ?? 1;
|
|
8031
|
-
return entries.filter((entry) => entry.seq >= boundary && entry.entryType !== "system_context" && entry.entryType !== "project_instructions" && entry.entryType !== "compaction");
|
|
8032
7778
|
}
|
|
8033
|
-
function
|
|
8034
|
-
if (
|
|
8035
|
-
return
|
|
8036
|
-
if (entry.entryType === "user_message" || entry.entryType === "reminder") {
|
|
8037
|
-
return entry.payload.message;
|
|
8038
|
-
}
|
|
8039
|
-
if (entry.entryType === "assistant_message") {
|
|
8040
|
-
const message = entry.payload.message;
|
|
8041
|
-
if (message.provider !== currentProvider || message.model !== currentModel) {
|
|
8042
|
-
return portableModelMessage(message);
|
|
8043
|
-
}
|
|
8044
|
-
return message;
|
|
8045
|
-
}
|
|
8046
|
-
if (entry.entryType === "tool_result") {
|
|
8047
|
-
return entry.payload.message;
|
|
7779
|
+
function corrupt(path5, line2, error) {
|
|
7780
|
+
if (error instanceof SessionCorruptError) {
|
|
7781
|
+
return error;
|
|
8048
7782
|
}
|
|
8049
|
-
return
|
|
7783
|
+
return new SessionCorruptError(path5, line2, error instanceof Error ? error.message : "invalid session JSONL", { cause: error });
|
|
8050
7784
|
}
|
|
8051
|
-
function
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
7785
|
+
function findOpenToolCalls(items) {
|
|
7786
|
+
const calls = /* @__PURE__ */ new Map();
|
|
7787
|
+
for (const item of items) {
|
|
7788
|
+
if (item.kind === "entry" && item.entryType === "context_reset") {
|
|
7789
|
+
for (const [id, call] of calls) {
|
|
7790
|
+
if (call.seq <= item.payload.resetThroughSeq)
|
|
7791
|
+
calls.delete(id);
|
|
8058
7792
|
}
|
|
8059
|
-
expected = item.message.content.filter((block) => block.type === "tool-call").map((block) => block.call.id);
|
|
8060
|
-
ownerEntryId = expected.length === 0 ? null : item.entryId;
|
|
8061
|
-
continue;
|
|
8062
7793
|
}
|
|
8063
|
-
if (item.
|
|
8064
|
-
const
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
7794
|
+
if (item.kind === "entry" && item.entryType === "assistant_message") {
|
|
7795
|
+
for (const block of item.payload.message.content) {
|
|
7796
|
+
if (block.type === "tool-call") {
|
|
7797
|
+
calls.set(block.call.id, {
|
|
7798
|
+
entryId: item.id,
|
|
7799
|
+
seq: item.seq,
|
|
7800
|
+
toolCallId: block.call.id,
|
|
7801
|
+
toolName: block.call.name
|
|
7802
|
+
});
|
|
7803
|
+
}
|
|
8070
7804
|
}
|
|
8071
|
-
expected = rest;
|
|
8072
|
-
ownerEntryId = expected.length === 0 ? null : ownerEntryId;
|
|
8073
|
-
continue;
|
|
8074
7805
|
}
|
|
8075
|
-
if (
|
|
8076
|
-
|
|
7806
|
+
if (item.kind === "entry" && item.entryType === "tool_result") {
|
|
7807
|
+
calls.delete(item.payload.message.toolCallId);
|
|
8077
7808
|
}
|
|
8078
7809
|
}
|
|
8079
|
-
|
|
8080
|
-
throw new ContextBuildError(ownerEntryId ?? "unknown", "missing tool result");
|
|
8081
|
-
}
|
|
7810
|
+
return [...calls.values()];
|
|
8082
7811
|
}
|
|
8083
7812
|
|
|
8084
|
-
// ../../packages/session/session-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
break;
|
|
7813
|
+
// ../../packages/session/session-store/dist/attachment-references.js
|
|
7814
|
+
function attachmentReferences(entries) {
|
|
7815
|
+
const result = /* @__PURE__ */ new Set();
|
|
7816
|
+
for (const entry of entries) {
|
|
7817
|
+
if (entry.entryType === "compaction" || entry.entryType === "image_offload" || entry.entryType === "context_reset")
|
|
7818
|
+
continue;
|
|
7819
|
+
if (!("message" in entry.payload) || !entry.payload.message || typeof entry.payload.message !== "object")
|
|
7820
|
+
throw new Error("Incomplete session message during attachment scan");
|
|
7821
|
+
const message = entry.payload.message;
|
|
7822
|
+
if (!("attachments" in message))
|
|
7823
|
+
continue;
|
|
7824
|
+
for (const block of message.attachments ?? []) {
|
|
7825
|
+
assertAttachmentContent(block);
|
|
7826
|
+
result.add(block.ref.id);
|
|
8099
7827
|
}
|
|
8100
7828
|
}
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
const
|
|
8105
|
-
const
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
7829
|
+
return result;
|
|
7830
|
+
}
|
|
7831
|
+
function scanAttachmentReferences(sessionsRoot) {
|
|
7832
|
+
const result = /* @__PURE__ */ new Set();
|
|
7833
|
+
const visit = (directory) => {
|
|
7834
|
+
for (const file of readdirSync(directory, { withFileTypes: true })) {
|
|
7835
|
+
const path5 = join(directory, file.name);
|
|
7836
|
+
if (file.isSymbolicLink())
|
|
7837
|
+
throw new Error(`Cannot scan symbolic session path: ${path5}`);
|
|
7838
|
+
if (file.isDirectory())
|
|
7839
|
+
visit(path5);
|
|
7840
|
+
else if (file.name.endsWith(".jsonl")) {
|
|
7841
|
+
if (!lstatSync(path5).isFile())
|
|
7842
|
+
throw new Error(`Invalid session file: ${path5}`);
|
|
7843
|
+
const replay = readSessionFile(path5);
|
|
7844
|
+
if (replay.ignoredTornTail)
|
|
7845
|
+
throw new Error(`Incomplete session journal: ${path5}`);
|
|
7846
|
+
for (const id of attachmentReferences(replay.items.filter((item) => item.kind === "entry")))
|
|
7847
|
+
result.add(id);
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
8113
7850
|
};
|
|
7851
|
+
visit(sessionsRoot);
|
|
7852
|
+
return result;
|
|
8114
7853
|
}
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
}
|
|
8126
|
-
}
|
|
8127
|
-
} else if (entry.entryType === "tool_result") {
|
|
8128
|
-
lines2.push(`[tool-result ${entry.payload.message.toolCallId} ${entry.payload.message.toolName}] ${entry.payload.message.content}`);
|
|
8129
|
-
} else if (entry.entryType === "tool_definitions") {
|
|
8130
|
-
lines2.push(`[loaded tool definitions] ${(entry.payload.message.toolDefinitions ?? []).map((tool) => tool.spec.name).join(", ")}`);
|
|
8131
|
-
} else if (entry.entryType === "user_message" || entry.entryType === "skill_context" || entry.entryType === "reminder" || entry.entryType === "project_instructions") {
|
|
8132
|
-
lines2.push(`[${entry.payload.message.role}] ${entry.payload.message.content}`);
|
|
8133
|
-
}
|
|
8134
|
-
if ("message" in entry.payload && "attachments" in entry.payload.message) {
|
|
8135
|
-
for (const block of entry.payload.message.attachments ?? [])
|
|
8136
|
-
lines2.push(`[attachment metadata only; not visual interpretation] ${JSON.stringify(block)}`);
|
|
8137
|
-
}
|
|
8138
|
-
lines2.push("</entry>");
|
|
7854
|
+
|
|
7855
|
+
// ../../packages/session/session-store/dist/session-paths.js
|
|
7856
|
+
import { createHash } from "node:crypto";
|
|
7857
|
+
import { mkdirSync, realpathSync } from "node:fs";
|
|
7858
|
+
import { basename, join as join2, resolve } from "node:path";
|
|
7859
|
+
function canonicalProjectRoot(projectRoot) {
|
|
7860
|
+
try {
|
|
7861
|
+
return realpathSync.native(projectRoot);
|
|
7862
|
+
} catch {
|
|
7863
|
+
return resolve(projectRoot);
|
|
8139
7864
|
}
|
|
8140
|
-
lines2.push("</conversation>");
|
|
8141
|
-
return lines2.join("\n");
|
|
8142
7865
|
}
|
|
8143
|
-
function
|
|
8144
|
-
const
|
|
8145
|
-
const
|
|
8146
|
-
|
|
8147
|
-
const entry = entries[index];
|
|
8148
|
-
if (groupedDefinitions.has(entry.id))
|
|
8149
|
-
continue;
|
|
8150
|
-
if (entry.entryType === "image_offload" || entry.entryType === "system_context" || entry.entryType === "project_instructions" || entry.entryType === "compaction") {
|
|
8151
|
-
continue;
|
|
8152
|
-
}
|
|
8153
|
-
if (entry.entryType === "assistant_message") {
|
|
8154
|
-
const toolCallIds = entry.payload.message.content.filter((block) => block.type === "tool-call").map((block) => block.call.id);
|
|
8155
|
-
if (toolCallIds.length > 0) {
|
|
8156
|
-
const grouped = [entry];
|
|
8157
|
-
for (const id of toolCallIds) {
|
|
8158
|
-
const resultIndex = entries.findIndex((candidate) => candidate.entryType === "tool_result" && candidate.payload.message.toolCallId === id);
|
|
8159
|
-
if (resultIndex < 0) {
|
|
8160
|
-
throw new Error(`cannot compact incomplete tool call: ${id}`);
|
|
8161
|
-
}
|
|
8162
|
-
grouped.push(entries[resultIndex]);
|
|
8163
|
-
}
|
|
8164
|
-
const lastResultIndex = entries.findIndex((candidate) => candidate.id === grouped.at(-1).id);
|
|
8165
|
-
const definitions = entries[lastResultIndex + 1];
|
|
8166
|
-
if (definitions?.entryType === "tool_definitions") {
|
|
8167
|
-
grouped.push(definitions);
|
|
8168
|
-
groupedDefinitions.add(definitions.id);
|
|
8169
|
-
}
|
|
8170
|
-
units.push({ entries: grouped, messages: grouped.map(messageForEntry) });
|
|
8171
|
-
continue;
|
|
8172
|
-
}
|
|
8173
|
-
}
|
|
8174
|
-
if (entry.entryType === "tool_result") {
|
|
8175
|
-
continue;
|
|
8176
|
-
}
|
|
8177
|
-
units.push({ entries: [entry], messages: [messageForEntry(entry)] });
|
|
8178
|
-
}
|
|
8179
|
-
return units;
|
|
7866
|
+
function projectKeyForRoot(projectRoot) {
|
|
7867
|
+
const canonical = canonicalProjectRoot(projectRoot);
|
|
7868
|
+
const digest = createHash("sha256").update(canonical).digest("hex").slice(0, 12);
|
|
7869
|
+
return `${sanitizePathPart(basename(canonical) || "project")}-${digest}`;
|
|
8180
7870
|
}
|
|
8181
|
-
function
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
throw new Error(`entry has no model message: ${entry.entryType}`);
|
|
7871
|
+
function ensureSessionDirectory(sessionsRoot, projectKey) {
|
|
7872
|
+
const directory = join2(sessionsRoot, projectKey);
|
|
7873
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
7874
|
+
return directory;
|
|
7875
|
+
}
|
|
7876
|
+
function sessionPathForHeader(sessionsRoot, projectKey, createdAt, sessionId) {
|
|
7877
|
+
const safeCreatedAt = createdAt.replaceAll(":", "").replaceAll(".", "");
|
|
7878
|
+
return join2(ensureSessionDirectory(sessionsRoot, projectKey), `${safeCreatedAt}_${sessionId}.jsonl`);
|
|
7879
|
+
}
|
|
7880
|
+
function sanitizePathPart(value) {
|
|
7881
|
+
const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7882
|
+
return sanitized || "project";
|
|
8194
7883
|
}
|
|
8195
7884
|
|
|
8196
|
-
// ../../packages/session/session-
|
|
8197
|
-
|
|
7885
|
+
// ../../packages/session/session-store/dist/session-journal.js
|
|
7886
|
+
import { closeSync, existsSync, fsyncSync, openSync, readFileSync as readFileSync2, unlinkSync, writeSync } from "node:fs";
|
|
7887
|
+
import { randomUUID } from "node:crypto";
|
|
7888
|
+
var SessionJournalLockError = class extends Error {
|
|
8198
7889
|
constructor(message) {
|
|
8199
7890
|
super(message);
|
|
8200
|
-
this.name = "
|
|
7891
|
+
this.name = "SessionJournalLockError";
|
|
8201
7892
|
}
|
|
8202
7893
|
};
|
|
8203
|
-
var
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
#
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
this
|
|
7894
|
+
var FileSessionJournal = class {
|
|
7895
|
+
attachments;
|
|
7896
|
+
header;
|
|
7897
|
+
path;
|
|
7898
|
+
#fd;
|
|
7899
|
+
#lockPath;
|
|
7900
|
+
#nextSeq;
|
|
7901
|
+
#closed = false;
|
|
7902
|
+
constructor(input) {
|
|
7903
|
+
this.attachments = input.attachments;
|
|
7904
|
+
this.header = input.header;
|
|
7905
|
+
this.path = input.path;
|
|
7906
|
+
this.#nextSeq = input.nextSeq;
|
|
7907
|
+
this.#fd = input.fd;
|
|
7908
|
+
this.#lockPath = `${input.path}.lock`;
|
|
8213
7909
|
}
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
7910
|
+
get nextSeq() {
|
|
7911
|
+
return this.#nextSeq;
|
|
7912
|
+
}
|
|
7913
|
+
appendEntry(input) {
|
|
7914
|
+
const item = this.makeBase(input);
|
|
7915
|
+
const entry = {
|
|
7916
|
+
...item,
|
|
7917
|
+
kind: "entry",
|
|
7918
|
+
entryType: input.entryType,
|
|
7919
|
+
payload: input.payload
|
|
7920
|
+
};
|
|
7921
|
+
const message = "message" in entry.payload ? entry.payload.message : void 0;
|
|
7922
|
+
const attachments = message && "attachments" in message ? message.attachments : void 0;
|
|
7923
|
+
if (attachments?.length) {
|
|
7924
|
+
for (const attachment of attachments)
|
|
7925
|
+
assertAttachmentContent(attachment);
|
|
7926
|
+
if (!this.attachments)
|
|
7927
|
+
throw new Error("Attachment persistence requires an attachment store");
|
|
7928
|
+
if (!("attachmentKey" in message) || !message.attachmentKey)
|
|
7929
|
+
throw new Error("Attachment message requires an occurrence key");
|
|
7930
|
+
this.attachments.commit(attachments.map((block) => block.ref), () => {
|
|
7931
|
+
this.writeItem(entry);
|
|
7932
|
+
this.flush();
|
|
7933
|
+
});
|
|
7934
|
+
} else
|
|
7935
|
+
this.writeItem(entry);
|
|
7936
|
+
if (entry.entryType === "compaction") {
|
|
7937
|
+
this.flush();
|
|
8222
7938
|
}
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
7939
|
+
return entry;
|
|
7940
|
+
}
|
|
7941
|
+
appendRecord(input) {
|
|
7942
|
+
const record = {
|
|
7943
|
+
...this.makeBase(input),
|
|
7944
|
+
kind: "record",
|
|
7945
|
+
recordType: input.recordType,
|
|
7946
|
+
payload: input.payload ?? {}
|
|
7947
|
+
};
|
|
7948
|
+
this.writeItem(record);
|
|
7949
|
+
if (record.recordType === "turn_finished" || record.recordType === "compaction_finished" || record.recordType === "session_closed") {
|
|
7950
|
+
this.flush();
|
|
8229
7951
|
}
|
|
8230
|
-
return
|
|
7952
|
+
return record;
|
|
8231
7953
|
}
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
protectedFromSeq: input.protectedFromSeq,
|
|
8236
|
-
entries: input.entries,
|
|
8237
|
-
// A search preflight reserves the pending call/result/definition unit.
|
|
8238
|
-
// Keep the latest completed unit and summarize older units to make room.
|
|
8239
|
-
retainTokens: input.reserveTokens === void 0 ? calculated.retainTokens : 1,
|
|
8240
|
-
estimator: this.#estimator
|
|
8241
|
-
});
|
|
8242
|
-
if (input.trigger === "manual" && plan.summarizedEntries.length === 0) {
|
|
8243
|
-
throw new Error("No messages to compact in current history.");
|
|
7954
|
+
flush() {
|
|
7955
|
+
if (!this.#closed) {
|
|
7956
|
+
fsyncSync(this.#fd);
|
|
8244
7957
|
}
|
|
8245
|
-
const summarizedFromSeq = plan.summarizedEntries[0]?.seq ?? 1;
|
|
8246
|
-
const summarizedThroughSeq = plan.summarizedEntries.at(-1)?.seq ?? Math.max(0, summarizedFromSeq - 1);
|
|
8247
|
-
const activeCompaction = [...input.entries].reverse().find((entry) => entry.entryType === "compaction");
|
|
8248
|
-
const messagesBefore = this.#builder.build(input).messages;
|
|
8249
|
-
const tokensBefore = this.#estimator.estimateMessages(messagesBefore) + this.#estimator.estimateTools(input.projectTools?.(messagesBefore) ?? input.tools);
|
|
8250
|
-
const summary = plan.summarizedEntries.length === 0 ? {
|
|
8251
|
-
summary: "No prior conversation needed compaction.",
|
|
8252
|
-
inputTokens: 0,
|
|
8253
|
-
outputTokens: 0
|
|
8254
|
-
} : await this.#summarize({
|
|
8255
|
-
serialized: serializeConversation(plan.summarizedEntries),
|
|
8256
|
-
maxSummaryTokens: input.policy.maxSummaryTokens
|
|
8257
|
-
});
|
|
8258
|
-
const payload = {
|
|
8259
|
-
summary: summary.summary,
|
|
8260
|
-
summarizedFromSeq,
|
|
8261
|
-
summarizedThroughSeq,
|
|
8262
|
-
retainedFromSeq: plan.retainedFromSeq,
|
|
8263
|
-
...activeCompaction === void 0 ? {} : { supersedesCompactionId: activeCompaction.id },
|
|
8264
|
-
tokensBefore,
|
|
8265
|
-
retainedTokens: calculated.retainTokens,
|
|
8266
|
-
summaryInputTokens: summary.inputTokens,
|
|
8267
|
-
summaryOutputTokens: summary.outputTokens,
|
|
8268
|
-
provider: input.currentProvider,
|
|
8269
|
-
model: input.currentModel,
|
|
8270
|
-
trigger: input.trigger
|
|
8271
|
-
};
|
|
8272
|
-
return { entry: this.#appendCompaction(payload) };
|
|
8273
7958
|
}
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
7959
|
+
close() {
|
|
7960
|
+
if (this.#closed) {
|
|
7961
|
+
return;
|
|
7962
|
+
}
|
|
7963
|
+
this.flush();
|
|
7964
|
+
closeSync(this.#fd);
|
|
7965
|
+
this.#closed = true;
|
|
7966
|
+
if (existsSync(this.#lockPath)) {
|
|
7967
|
+
unlinkSync(this.#lockPath);
|
|
8282
7968
|
}
|
|
8283
|
-
const compaction = await this.compact({ ...input, trigger: "provider_overflow" });
|
|
8284
|
-
prepared = await this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
|
|
8285
|
-
return await send(prepared);
|
|
8286
7969
|
}
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
7970
|
+
makeBase(input) {
|
|
7971
|
+
return {
|
|
7972
|
+
schemaVersion: 1,
|
|
7973
|
+
sessionId: this.header.sessionId,
|
|
7974
|
+
seq: this.#nextSeq,
|
|
7975
|
+
id: randomUUID().replaceAll("-", ""),
|
|
7976
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7977
|
+
...input.taskId === void 0 ? {} : { taskId: input.taskId },
|
|
7978
|
+
...input.turnId === void 0 ? {} : { turnId: input.turnId },
|
|
7979
|
+
...input.copiedFrom === void 0 ? {} : { copiedFrom: input.copiedFrom }
|
|
7980
|
+
};
|
|
7981
|
+
}
|
|
7982
|
+
writeItem(item) {
|
|
7983
|
+
if (this.#closed) {
|
|
7984
|
+
throw new Error("session journal is closed");
|
|
7985
|
+
}
|
|
7986
|
+
writeLine(this.#fd, item);
|
|
7987
|
+
this.#nextSeq += 1;
|
|
8300
7988
|
}
|
|
8301
7989
|
};
|
|
8302
|
-
function
|
|
8303
|
-
const
|
|
8304
|
-
const
|
|
8305
|
-
const
|
|
8306
|
-
const
|
|
8307
|
-
|
|
7990
|
+
function createSessionJournal(options) {
|
|
7991
|
+
const header = options.header ?? makeHeader(options);
|
|
7992
|
+
const path5 = options.path ?? sessionPathForHeader(options.sessionsRoot, header.projectKey, header.createdAt, header.sessionId);
|
|
7993
|
+
const existing = existsSync(path5);
|
|
7994
|
+
const lockPath = `${path5}.lock`;
|
|
7995
|
+
acquireLock(lockPath, header.sessionId);
|
|
7996
|
+
try {
|
|
7997
|
+
const fd = openSync(path5, existing ? "a" : "wx", 384);
|
|
7998
|
+
if (!existing) {
|
|
7999
|
+
writeLine(fd, header);
|
|
8000
|
+
fsyncSync(fd);
|
|
8001
|
+
return new FileSessionJournal({ header, path: path5, nextSeq: 1, fd, attachments: options.attachments });
|
|
8002
|
+
}
|
|
8003
|
+
const replay = readSessionFile(path5);
|
|
8004
|
+
return new FileSessionJournal({
|
|
8005
|
+
header: replay.header,
|
|
8006
|
+
path: path5,
|
|
8007
|
+
nextSeq: replay.lastSeq + 1,
|
|
8008
|
+
fd,
|
|
8009
|
+
attachments: options.attachments
|
|
8010
|
+
});
|
|
8011
|
+
} catch (error) {
|
|
8012
|
+
releaseLock(lockPath);
|
|
8013
|
+
throw error;
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
function makeHeader(options) {
|
|
8017
|
+
const projectRoot = canonicalProjectRoot(required(options.projectRoot, "projectRoot"));
|
|
8018
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8019
|
+
return {
|
|
8020
|
+
schemaVersion: 1,
|
|
8021
|
+
type: "session_header",
|
|
8022
|
+
sessionId: randomUUID().replaceAll("-", ""),
|
|
8023
|
+
createdAt,
|
|
8024
|
+
initialCwd: required(options.initialCwd, "initialCwd"),
|
|
8025
|
+
projectRoot,
|
|
8026
|
+
projectKey: projectKeyForRoot(projectRoot),
|
|
8027
|
+
appVersion: required(options.appVersion, "appVersion"),
|
|
8028
|
+
provider: required(options.provider, "provider"),
|
|
8029
|
+
model: required(options.model, "model"),
|
|
8030
|
+
reasoningEffort: options.reasoningEffort ?? "high",
|
|
8031
|
+
origin: options.origin ?? "new",
|
|
8032
|
+
...options.parentSessionId === void 0 ? {} : { parentSessionId: options.parentSessionId },
|
|
8033
|
+
...options.forkedFrom === void 0 ? {} : { forkedFrom: options.forkedFrom }
|
|
8034
|
+
};
|
|
8035
|
+
}
|
|
8036
|
+
function acquireLock(path5, sessionId) {
|
|
8037
|
+
try {
|
|
8038
|
+
const fd = openSync(path5, "wx", 384);
|
|
8039
|
+
writeLine(fd, { pid: process.pid, sessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8040
|
+
closeSync(fd);
|
|
8041
|
+
} catch (error) {
|
|
8042
|
+
if (!isLiveLock(path5)) {
|
|
8043
|
+
releaseLock(path5);
|
|
8044
|
+
const fd = openSync(path5, "wx", 384);
|
|
8045
|
+
writeLine(fd, { pid: process.pid, sessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8046
|
+
closeSync(fd);
|
|
8047
|
+
return;
|
|
8048
|
+
}
|
|
8049
|
+
throw new SessionJournalLockError(`session journal is locked: ${path5}`);
|
|
8050
|
+
}
|
|
8051
|
+
}
|
|
8052
|
+
function isLiveLock(path5) {
|
|
8053
|
+
if (!existsSync(path5)) {
|
|
8054
|
+
return false;
|
|
8055
|
+
}
|
|
8056
|
+
try {
|
|
8057
|
+
const parsed = JSON.parse(readFileSync2(path5, "utf8"));
|
|
8058
|
+
if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid)) {
|
|
8059
|
+
return false;
|
|
8060
|
+
}
|
|
8061
|
+
process.kill(parsed.pid, 0);
|
|
8062
|
+
return true;
|
|
8063
|
+
} catch (error) {
|
|
8064
|
+
const code = error.code;
|
|
8065
|
+
return code === "EPERM";
|
|
8066
|
+
}
|
|
8067
|
+
}
|
|
8068
|
+
function releaseLock(path5) {
|
|
8069
|
+
if (existsSync(path5)) {
|
|
8070
|
+
unlinkSync(path5);
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
function writeLine(fd, value) {
|
|
8074
|
+
writeSync(fd, `${JSON.stringify(value)}
|
|
8075
|
+
`);
|
|
8076
|
+
}
|
|
8077
|
+
function required(value, label) {
|
|
8078
|
+
if (value === void 0 || value === "") {
|
|
8079
|
+
throw new Error(`${label} is required`);
|
|
8080
|
+
}
|
|
8081
|
+
return value;
|
|
8308
8082
|
}
|
|
8309
8083
|
|
|
8310
|
-
// ../../packages/
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8084
|
+
// ../../packages/session/session-store/dist/session-manager.js
|
|
8085
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync } from "node:fs";
|
|
8086
|
+
import { join as join3 } from "node:path";
|
|
8087
|
+
|
|
8088
|
+
// ../../packages/session/session-store/dist/session-tree.js
|
|
8089
|
+
function buildSessionTree(sessions) {
|
|
8090
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
8091
|
+
for (const session of sessions) {
|
|
8092
|
+
nodes.set(session.sessionId, { session, children: [] });
|
|
8317
8093
|
}
|
|
8318
|
-
|
|
8094
|
+
const roots = [];
|
|
8095
|
+
for (const session of sessions) {
|
|
8096
|
+
const node = nodes.get(session.sessionId);
|
|
8097
|
+
const parentId = session.parentSessionId;
|
|
8098
|
+
const parent = parentId === void 0 ? void 0 : nodes.get(parentId);
|
|
8099
|
+
if (parent === void 0) {
|
|
8100
|
+
roots.push(node);
|
|
8101
|
+
} else {
|
|
8102
|
+
parent.children.push(node);
|
|
8103
|
+
}
|
|
8104
|
+
}
|
|
8105
|
+
const sortNodes = (items) => items.sort((left, right) => right.session.createdAt.localeCompare(left.session.createdAt)).map((item) => ({
|
|
8106
|
+
session: item.session,
|
|
8107
|
+
children: sortNodes(item.children)
|
|
8108
|
+
}));
|
|
8109
|
+
return sortNodes(roots);
|
|
8110
|
+
}
|
|
8319
8111
|
|
|
8320
|
-
// ../../packages/
|
|
8321
|
-
var
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8112
|
+
// ../../packages/session/session-store/dist/session-manager.js
|
|
8113
|
+
var SessionManager = class {
|
|
8114
|
+
#attachments;
|
|
8115
|
+
#sessionsRoot;
|
|
8116
|
+
#appVersion;
|
|
8117
|
+
constructor(options) {
|
|
8118
|
+
this.#attachments = options.attachments;
|
|
8119
|
+
this.#sessionsRoot = options.sessionsRoot;
|
|
8120
|
+
this.#appVersion = options.appVersion;
|
|
8121
|
+
}
|
|
8122
|
+
create(options) {
|
|
8123
|
+
const journal = createSessionJournal({
|
|
8124
|
+
attachments: this.#attachments,
|
|
8125
|
+
sessionsRoot: this.#sessionsRoot,
|
|
8126
|
+
projectRoot: options.projectRoot,
|
|
8127
|
+
initialCwd: options.initialCwd,
|
|
8128
|
+
appVersion: this.#appVersion,
|
|
8129
|
+
provider: options.provider,
|
|
8130
|
+
model: options.model,
|
|
8131
|
+
reasoningEffort: options.reasoningEffort,
|
|
8132
|
+
origin: "new"
|
|
8133
|
+
});
|
|
8134
|
+
return {
|
|
8135
|
+
header: journal.header,
|
|
8136
|
+
journal,
|
|
8137
|
+
replay: {
|
|
8138
|
+
header: journal.header,
|
|
8139
|
+
items: [],
|
|
8140
|
+
lastSeq: 0,
|
|
8141
|
+
ignoredTornTail: false,
|
|
8142
|
+
openToolCalls: []
|
|
8143
|
+
}
|
|
8144
|
+
};
|
|
8145
|
+
}
|
|
8146
|
+
open(sessionId) {
|
|
8147
|
+
const summary = this.list().find((item) => item.sessionId === sessionId);
|
|
8148
|
+
if (summary === void 0 || summary.status === "corrupt") {
|
|
8149
|
+
throw new Error(`unknown session: ${sessionId}`);
|
|
8341
8150
|
}
|
|
8342
|
-
|
|
8343
|
-
|
|
8151
|
+
const replay = readSessionFile(summary.path);
|
|
8152
|
+
const journal = createSessionJournal({
|
|
8153
|
+
attachments: this.#attachments,
|
|
8154
|
+
sessionsRoot: this.#sessionsRoot,
|
|
8155
|
+
header: replay.header,
|
|
8156
|
+
path: summary.path
|
|
8157
|
+
});
|
|
8158
|
+
return { header: replay.header, journal, replay };
|
|
8159
|
+
}
|
|
8160
|
+
continueLatest(projectRoot) {
|
|
8161
|
+
const latest = this.list(projectRoot).find((item) => item.status !== "corrupt");
|
|
8162
|
+
return latest === void 0 ? null : this.open(latest.sessionId);
|
|
8163
|
+
}
|
|
8164
|
+
list(projectRoot) {
|
|
8165
|
+
const canonical = projectRoot === void 0 ? null : canonicalProjectRoot(projectRoot);
|
|
8166
|
+
return this.sessionPaths().map((path5) => summaryForPath(path5)).filter((summary) => canonical === null || summary.projectRoot === canonical).sort((left, right) => {
|
|
8167
|
+
const updated = right.updatedAt.localeCompare(left.updatedAt);
|
|
8168
|
+
return updated === 0 ? right.createdAt.localeCompare(left.createdAt) : updated;
|
|
8169
|
+
});
|
|
8170
|
+
}
|
|
8171
|
+
tree(projectRoot) {
|
|
8172
|
+
return buildSessionTree(this.list(projectRoot).filter((item) => item.status !== "corrupt"));
|
|
8173
|
+
}
|
|
8174
|
+
fork(options) {
|
|
8175
|
+
const parent = this.parentReplay(options.parentSessionId);
|
|
8176
|
+
const target = parent.items.find((item) => item.kind === "entry" && item.entryType === "user_message" && item.id === options.entryId);
|
|
8177
|
+
if (target === void 0) {
|
|
8178
|
+
throw new Error("fork target must be a user_message entry");
|
|
8179
|
+
}
|
|
8180
|
+
const child = createSessionJournal({
|
|
8181
|
+
attachments: this.#attachments,
|
|
8182
|
+
sessionsRoot: this.#sessionsRoot,
|
|
8183
|
+
projectRoot: parent.header.projectRoot,
|
|
8184
|
+
initialCwd: parent.header.initialCwd,
|
|
8185
|
+
appVersion: this.#appVersion,
|
|
8186
|
+
provider: parent.header.provider,
|
|
8187
|
+
model: parent.header.model,
|
|
8188
|
+
reasoningEffort: parent.header.reasoningEffort,
|
|
8189
|
+
origin: "fork",
|
|
8190
|
+
parentSessionId: parent.header.sessionId,
|
|
8191
|
+
forkedFrom: {
|
|
8192
|
+
sessionId: parent.header.sessionId,
|
|
8193
|
+
seq: target.seq,
|
|
8194
|
+
entryId: target.id,
|
|
8195
|
+
mode: options.mode
|
|
8196
|
+
}
|
|
8197
|
+
});
|
|
8198
|
+
const throughSeq = options.mode === "before" ? target.seq - 1 : target.seq;
|
|
8199
|
+
copyEntries(child, parent.items.filter(isEntry).filter((item) => item.seq <= throughSeq));
|
|
8200
|
+
appendTitleSnapshot(child, sessionTitleAt(parent.items, throughSeq));
|
|
8201
|
+
child.close();
|
|
8202
|
+
return {
|
|
8203
|
+
sessionId: child.header.sessionId,
|
|
8204
|
+
path: child.path,
|
|
8205
|
+
editorText: options.mode === "before" ? target.payload.message.skillContext?.input ?? target.payload.message.content : ""
|
|
8206
|
+
};
|
|
8207
|
+
}
|
|
8208
|
+
clone(options) {
|
|
8209
|
+
const parent = this.parentReplay(options.parentSessionId);
|
|
8210
|
+
const lastEntry = [...parent.items].reverse().find((item) => item.kind === "entry");
|
|
8211
|
+
const child = createSessionJournal({
|
|
8212
|
+
attachments: this.#attachments,
|
|
8213
|
+
sessionsRoot: this.#sessionsRoot,
|
|
8214
|
+
projectRoot: parent.header.projectRoot,
|
|
8215
|
+
initialCwd: parent.header.initialCwd,
|
|
8216
|
+
appVersion: this.#appVersion,
|
|
8217
|
+
provider: parent.header.provider,
|
|
8218
|
+
model: parent.header.model,
|
|
8219
|
+
reasoningEffort: parent.header.reasoningEffort,
|
|
8220
|
+
origin: "clone",
|
|
8221
|
+
parentSessionId: parent.header.sessionId,
|
|
8222
|
+
forkedFrom: {
|
|
8223
|
+
sessionId: parent.header.sessionId,
|
|
8224
|
+
...lastEntry === void 0 ? {} : { seq: lastEntry.seq, entryId: lastEntry.id },
|
|
8225
|
+
mode: "clone"
|
|
8226
|
+
}
|
|
8227
|
+
});
|
|
8228
|
+
copyEntries(child, parent.items.filter(isEntry));
|
|
8229
|
+
appendTitleSnapshot(child, sessionTitleAt(parent.items));
|
|
8230
|
+
child.close();
|
|
8231
|
+
return { sessionId: child.header.sessionId, path: child.path };
|
|
8232
|
+
}
|
|
8233
|
+
parentReplay(sessionId) {
|
|
8234
|
+
const summary = this.list().find((item) => item.sessionId === sessionId);
|
|
8235
|
+
if (summary === void 0 || summary.status === "corrupt") {
|
|
8236
|
+
throw new Error(`unknown session: ${sessionId}`);
|
|
8237
|
+
}
|
|
8238
|
+
return readSessionFile(summary.path);
|
|
8239
|
+
}
|
|
8240
|
+
sessionPaths() {
|
|
8241
|
+
if (!existsSync2(this.#sessionsRoot)) {
|
|
8242
|
+
return [];
|
|
8243
|
+
}
|
|
8244
|
+
const paths = [];
|
|
8245
|
+
const visit = (directory) => {
|
|
8246
|
+
for (const entry of readdirSync2(directory)) {
|
|
8247
|
+
const absolute = join3(directory, entry);
|
|
8248
|
+
const stat6 = statSync(absolute);
|
|
8249
|
+
if (stat6.isDirectory()) {
|
|
8250
|
+
visit(absolute);
|
|
8251
|
+
} else if (absolute.endsWith(".jsonl")) {
|
|
8252
|
+
paths.push(absolute);
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
};
|
|
8256
|
+
visit(this.#sessionsRoot);
|
|
8257
|
+
return paths.sort();
|
|
8258
|
+
}
|
|
8259
|
+
};
|
|
8260
|
+
function summaryForPath(path5) {
|
|
8261
|
+
try {
|
|
8262
|
+
const replay = readSessionFile(path5);
|
|
8263
|
+
const last = replay.items.at(-1);
|
|
8264
|
+
const title = sessionTitleAt(replay.items);
|
|
8265
|
+
return {
|
|
8266
|
+
sessionId: replay.header.sessionId,
|
|
8267
|
+
path: path5,
|
|
8268
|
+
createdAt: replay.header.createdAt,
|
|
8269
|
+
updatedAt: last?.timestamp ?? replay.header.createdAt,
|
|
8270
|
+
projectRoot: replay.header.projectRoot,
|
|
8271
|
+
cwd: cwdFrom(replay),
|
|
8272
|
+
provider: replay.header.provider,
|
|
8273
|
+
model: replay.header.model,
|
|
8274
|
+
origin: replay.header.origin,
|
|
8275
|
+
...replay.header.parentSessionId === void 0 ? {} : { parentSessionId: replay.header.parentSessionId },
|
|
8276
|
+
...title === null ? {} : { title },
|
|
8277
|
+
lastUserText: lastUserText(replay.items),
|
|
8278
|
+
status: last?.kind === "record" && last.recordType === "session_closed" ? "closed" : existsSync2(`${path5}.lock`) ? "open" : "interrupted"
|
|
8279
|
+
};
|
|
8280
|
+
} catch {
|
|
8281
|
+
return {
|
|
8282
|
+
sessionId: path5,
|
|
8283
|
+
path: path5,
|
|
8284
|
+
createdAt: "",
|
|
8285
|
+
updatedAt: "",
|
|
8286
|
+
projectRoot: "",
|
|
8287
|
+
cwd: "",
|
|
8288
|
+
provider: "",
|
|
8289
|
+
model: "",
|
|
8290
|
+
origin: "import",
|
|
8291
|
+
lastUserText: "",
|
|
8292
|
+
status: "corrupt"
|
|
8293
|
+
};
|
|
8344
8294
|
}
|
|
8345
8295
|
}
|
|
8346
|
-
function
|
|
8347
|
-
const
|
|
8348
|
-
|
|
8349
|
-
|
|
8296
|
+
function cwdFrom(replay) {
|
|
8297
|
+
for (const item of [...replay.items].reverse()) {
|
|
8298
|
+
if (item.kind === "entry" && item.entryType === "system_context") {
|
|
8299
|
+
return item.payload.cwd;
|
|
8300
|
+
}
|
|
8350
8301
|
}
|
|
8302
|
+
return replay.header.initialCwd;
|
|
8351
8303
|
}
|
|
8352
|
-
function
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
if (view.fullResolution !== void 0 && typeof view.fullResolution !== "boolean")
|
|
8357
|
-
throw new AttachmentError("INVALID_REFERENCE", "Invalid fullResolution");
|
|
8358
|
-
if (view.region !== void 0) {
|
|
8359
|
-
const r = view.region;
|
|
8360
|
-
if (!r || typeof r !== "object" || !["x", "y", "width", "height"].every((k) => Number.isSafeInteger(r[k])) || Number(r.x) < 0 || Number(r.y) < 0 || Number(r.width) < 1 || Number(r.height) < 1) {
|
|
8361
|
-
throw new AttachmentError("INVALID_REFERENCE", "Invalid crop region");
|
|
8304
|
+
function lastUserText(items) {
|
|
8305
|
+
for (const item of [...items].reverse()) {
|
|
8306
|
+
if (item.kind === "entry" && item.entryType === "user_message") {
|
|
8307
|
+
return item.payload.message.skillContext?.input ?? item.payload.message.content;
|
|
8362
8308
|
}
|
|
8363
8309
|
}
|
|
8310
|
+
return "";
|
|
8364
8311
|
}
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8312
|
+
function copyEntries(journal, entries) {
|
|
8313
|
+
for (const entry of entries) {
|
|
8314
|
+
const input = {
|
|
8315
|
+
entryType: entry.entryType,
|
|
8316
|
+
payload: entry.entryType === "context_reset" ? { ...entry.payload, resetThroughSeq: entries.filter((candidate) => candidate.seq <= entry.payload.resetThroughSeq).length } : entry.payload,
|
|
8317
|
+
...entry.taskId === void 0 ? {} : { taskId: entry.taskId },
|
|
8318
|
+
...entry.turnId === void 0 ? {} : { turnId: entry.turnId },
|
|
8319
|
+
copiedFrom: {
|
|
8320
|
+
sessionId: entry.sessionId,
|
|
8321
|
+
itemId: entry.id,
|
|
8322
|
+
seq: entry.seq
|
|
8323
|
+
}
|
|
8324
|
+
};
|
|
8325
|
+
journal.appendEntry(input);
|
|
8374
8326
|
}
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8327
|
+
}
|
|
8328
|
+
function isEntry(item) {
|
|
8329
|
+
return item.kind === "entry";
|
|
8330
|
+
}
|
|
8331
|
+
function appendTitleSnapshot(journal, title) {
|
|
8332
|
+
if (title !== null) {
|
|
8333
|
+
journal.appendRecord({
|
|
8334
|
+
recordType: "session_name_changed",
|
|
8335
|
+
payload: { title }
|
|
8336
|
+
});
|
|
8378
8337
|
}
|
|
8379
|
-
return title;
|
|
8380
8338
|
}
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
if (
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8339
|
+
|
|
8340
|
+
// ../../packages/session/session-store/dist/transcript-projector.js
|
|
8341
|
+
function projectTranscript(entries) {
|
|
8342
|
+
const items = [];
|
|
8343
|
+
const skillNames = /* @__PURE__ */ new Map();
|
|
8344
|
+
for (const entry of entries) {
|
|
8345
|
+
if (entry.entryType === "user_message") {
|
|
8346
|
+
items.push({ kind: "user", text: entry.payload.message.skillContext?.input ?? entry.payload.message.content });
|
|
8347
|
+
} else if (entry.entryType === "assistant_message") {
|
|
8348
|
+
for (const block of entry.payload.message.content) {
|
|
8349
|
+
if (block.type !== "tool-call" || block.call.name !== "skill")
|
|
8350
|
+
continue;
|
|
8351
|
+
try {
|
|
8352
|
+
const args = JSON.parse(block.call.arguments);
|
|
8353
|
+
if (args && typeof args === "object" && "name" in args && typeof args.name === "string") {
|
|
8354
|
+
skillNames.set(block.call.id, args.name);
|
|
8355
|
+
}
|
|
8356
|
+
} catch {
|
|
8357
|
+
}
|
|
8391
8358
|
}
|
|
8392
|
-
|
|
8359
|
+
const text3 = entry.payload.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
8360
|
+
const reasoning = entry.payload.message.content.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
8361
|
+
items.push({
|
|
8362
|
+
kind: "assistant",
|
|
8363
|
+
text: text3,
|
|
8364
|
+
...reasoning === "" ? {} : { reasoning }
|
|
8365
|
+
});
|
|
8366
|
+
} else if (entry.entryType === "tool_result") {
|
|
8367
|
+
const message = entry.payload.message;
|
|
8368
|
+
let result = message.content;
|
|
8369
|
+
if (message.toolName === "skill") {
|
|
8370
|
+
try {
|
|
8371
|
+
const value = JSON.parse(result);
|
|
8372
|
+
if (value && typeof value === "object") {
|
|
8373
|
+
if ("content" in value && typeof value.content === "string")
|
|
8374
|
+
result = value.content;
|
|
8375
|
+
else if ("error" in value && typeof value.error === "string")
|
|
8376
|
+
result = value.error;
|
|
8377
|
+
}
|
|
8378
|
+
} catch {
|
|
8379
|
+
}
|
|
8380
|
+
}
|
|
8381
|
+
items.push({
|
|
8382
|
+
kind: "tool",
|
|
8383
|
+
callId: entry.payload.message.toolCallId,
|
|
8384
|
+
name: entry.payload.message.toolName,
|
|
8385
|
+
subject: message.toolName === "skill" ? skillNames.get(message.toolCallId) ?? "" : message.toolCallId,
|
|
8386
|
+
result,
|
|
8387
|
+
isError: entry.payload.message.isError
|
|
8388
|
+
});
|
|
8389
|
+
} else if (entry.entryType === "context_reset") {
|
|
8390
|
+
items.push({ kind: "notice", text: "Conversation context was cleared.", tone: "info" });
|
|
8391
|
+
} else if (entry.entryType === "compaction") {
|
|
8392
|
+
items.push({
|
|
8393
|
+
kind: "notice",
|
|
8394
|
+
text: "Conversation context was compacted.",
|
|
8395
|
+
tone: "info"
|
|
8396
|
+
});
|
|
8393
8397
|
}
|
|
8394
8398
|
}
|
|
8395
|
-
return
|
|
8399
|
+
return items;
|
|
8396
8400
|
}
|
|
8397
8401
|
|
|
8398
|
-
// ../../packages/session/session-
|
|
8399
|
-
var
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
"project_instructions",
|
|
8406
|
-
"user_message",
|
|
8407
|
-
"assistant_message",
|
|
8408
|
-
"tool_result",
|
|
8409
|
-
"reminder",
|
|
8410
|
-
"compaction"
|
|
8411
|
-
]);
|
|
8412
|
-
var RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
8413
|
-
"turn_started",
|
|
8414
|
-
"turn_finished",
|
|
8415
|
-
"model_request",
|
|
8416
|
-
"model_response",
|
|
8417
|
-
"tool_started",
|
|
8418
|
-
"tool_finished",
|
|
8419
|
-
"provider_changed",
|
|
8420
|
-
"model_changed",
|
|
8421
|
-
"reasoning_effort_changed",
|
|
8422
|
-
"cwd_changed",
|
|
8423
|
-
"usage",
|
|
8424
|
-
"error",
|
|
8425
|
-
"cancelled",
|
|
8426
|
-
"queue_enqueued",
|
|
8427
|
-
"queue_started",
|
|
8428
|
-
"queue_finished",
|
|
8429
|
-
"compaction_started",
|
|
8430
|
-
"compaction_finished",
|
|
8431
|
-
"session_name_changed",
|
|
8432
|
-
"session_closed"
|
|
8433
|
-
]);
|
|
8434
|
-
var ORIGINS = /* @__PURE__ */ new Set(["new", "fork", "clone", "import"]);
|
|
8435
|
-
var REASONING_EFFORTS2 = /* @__PURE__ */ new Set([
|
|
8436
|
-
"off",
|
|
8437
|
-
"minimal",
|
|
8438
|
-
"low",
|
|
8439
|
-
"medium",
|
|
8440
|
-
"high",
|
|
8441
|
-
"xhigh",
|
|
8442
|
-
"max"
|
|
8443
|
-
]);
|
|
8444
|
-
var SessionSchemaError = class extends Error {
|
|
8445
|
-
constructor(message) {
|
|
8446
|
-
super(message);
|
|
8447
|
-
this.name = "SessionSchemaError";
|
|
8402
|
+
// ../../packages/session/session-context/dist/token-estimator.js
|
|
8403
|
+
var TEXT_BYTES_PER_TOKEN = 4;
|
|
8404
|
+
var BLOCK_OVERHEAD = 4;
|
|
8405
|
+
var MESSAGE_OVERHEAD = 4;
|
|
8406
|
+
var DefaultTokenEstimator = class {
|
|
8407
|
+
estimateMessages(messages) {
|
|
8408
|
+
return messages.reduce((total, message) => total + this.estimateMessage(message), 0);
|
|
8448
8409
|
}
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8410
|
+
estimateTools(tools) {
|
|
8411
|
+
if (tools.length === 0) {
|
|
8412
|
+
return 0;
|
|
8413
|
+
}
|
|
8414
|
+
return this.estimateText(canonicalJson(tools.map(({ name, description, parameters }) => ({ name, description, parameters })))) + BLOCK_OVERHEAD;
|
|
8454
8415
|
}
|
|
8455
|
-
|
|
8456
|
-
|
|
8416
|
+
estimateText(text3) {
|
|
8417
|
+
return Math.ceil(Buffer.byteLength(text3, "utf8") / TEXT_BYTES_PER_TOKEN);
|
|
8457
8418
|
}
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8419
|
+
measure(input) {
|
|
8420
|
+
const anchor = input.anchor ?? null;
|
|
8421
|
+
if (anchor !== null && this.isValidAnchor(input, anchor)) {
|
|
8422
|
+
const trailingEntries = input.entries.filter((entry) => entry.seq > anchor.throughSeq);
|
|
8423
|
+
const trailingTokens = this.estimateMessages(messagesForEntries(trailingEntries));
|
|
8424
|
+
return {
|
|
8425
|
+
totalTokens: anchor.contextTokens + trailingTokens,
|
|
8426
|
+
anchorTokens: anchor.contextTokens,
|
|
8427
|
+
trailingTokens,
|
|
8428
|
+
source: "provider_usage"
|
|
8429
|
+
};
|
|
8430
|
+
}
|
|
8431
|
+
const totalTokens = this.estimateMessages(input.messages) + this.estimateTools(input.tools);
|
|
8432
|
+
return {
|
|
8433
|
+
totalTokens,
|
|
8434
|
+
anchorTokens: 0,
|
|
8435
|
+
trailingTokens: totalTokens,
|
|
8436
|
+
source: "estimated"
|
|
8437
|
+
};
|
|
8468
8438
|
}
|
|
8469
|
-
|
|
8470
|
-
|
|
8439
|
+
estimateMessage(message) {
|
|
8440
|
+
const attachmentTokens = message.role === "user" || message.role === "tool-result" ? (message.attachments ?? []).reduce((sum, block, index) => sum + (block.type === "image" && !message.omittedImageIndexes?.includes(index) ? 85 + 170 * Math.ceil(block.ref.width / 512) * Math.ceil(block.ref.height / 512) : 32), 0) : 0;
|
|
8441
|
+
if (message.role === "system" && message.toolDefinitions !== void 0)
|
|
8442
|
+
return 0;
|
|
8443
|
+
if (message.role === "system" || message.role === "user") {
|
|
8444
|
+
return MESSAGE_OVERHEAD + BLOCK_OVERHEAD + this.estimateText(message.content) + attachmentTokens;
|
|
8445
|
+
}
|
|
8446
|
+
if (message.role === "tool-result") {
|
|
8447
|
+
return MESSAGE_OVERHEAD + BLOCK_OVERHEAD + this.estimateText(message.content) + attachmentTokens;
|
|
8448
|
+
}
|
|
8449
|
+
return MESSAGE_OVERHEAD + message.content.reduce((total, block) => {
|
|
8450
|
+
if (block.type === "text" || block.type === "reasoning") {
|
|
8451
|
+
return total + BLOCK_OVERHEAD + this.estimateText(block.text);
|
|
8452
|
+
}
|
|
8453
|
+
return total + BLOCK_OVERHEAD + this.estimateText(`${block.call.name}${canonicalJson(block.call.arguments)}`);
|
|
8454
|
+
}, 0);
|
|
8455
|
+
}
|
|
8456
|
+
isValidAnchor(input, anchor) {
|
|
8457
|
+
return anchor.provider === input.provider && anchor.throughSeq > contextResetBoundary(input.entries) && anchor.model === input.model && anchor.systemFingerprint === input.systemFingerprint && anchor.projectInstructionsFingerprint === input.projectInstructionsFingerprint && anchor.toolsFingerprint === input.toolsFingerprint && input.entries.some((entry) => entry.id === anchor.throughEntryId && entry.seq === anchor.throughSeq);
|
|
8458
|
+
}
|
|
8459
|
+
};
|
|
8460
|
+
function normalizeProviderUsage(usage) {
|
|
8461
|
+
return usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
|
|
8462
|
+
}
|
|
8463
|
+
function fingerprintContextPart(value) {
|
|
8464
|
+
return createHash2("sha256").update(canonicalJson(value)).digest("hex");
|
|
8465
|
+
}
|
|
8466
|
+
function canonicalJson(value) {
|
|
8467
|
+
return JSON.stringify(sortStable(value));
|
|
8468
|
+
}
|
|
8469
|
+
function sortStable(value) {
|
|
8470
|
+
if (Array.isArray(value)) {
|
|
8471
|
+
return value.map(sortStable);
|
|
8471
8472
|
}
|
|
8472
|
-
if (
|
|
8473
|
-
|
|
8473
|
+
if (value !== null && typeof value === "object") {
|
|
8474
|
+
const record = value;
|
|
8475
|
+
const sorted = {};
|
|
8476
|
+
for (const key of Object.keys(record).sort()) {
|
|
8477
|
+
sorted[key] = sortStable(record[key]);
|
|
8478
|
+
}
|
|
8479
|
+
return sorted;
|
|
8474
8480
|
}
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8481
|
+
return value;
|
|
8482
|
+
}
|
|
8483
|
+
function messagesForEntries(entries) {
|
|
8484
|
+
return entries.flatMap((entry) => {
|
|
8485
|
+
if (entry.entryType === "tool_definitions" || entry.entryType === "tool_catalog" || entry.entryType === "skill_context")
|
|
8486
|
+
return [entry.payload.message];
|
|
8487
|
+
if (entry.entryType === "system_context") {
|
|
8488
|
+
return [entry.payload.message];
|
|
8480
8489
|
}
|
|
8481
|
-
if (
|
|
8482
|
-
|
|
8490
|
+
if (entry.entryType === "project_instructions" || entry.entryType === "user_message" || entry.entryType === "reminder") {
|
|
8491
|
+
return [entry.payload.message];
|
|
8483
8492
|
}
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
throw new SessionSchemaError("invalid fork mode");
|
|
8493
|
+
if (entry.entryType === "assistant_message") {
|
|
8494
|
+
return [entry.payload.message];
|
|
8487
8495
|
}
|
|
8488
|
-
|
|
8489
|
-
|
|
8496
|
+
if (entry.entryType === "tool_result") {
|
|
8497
|
+
return [entry.payload.message];
|
|
8498
|
+
}
|
|
8499
|
+
return [];
|
|
8500
|
+
});
|
|
8490
8501
|
}
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8502
|
+
|
|
8503
|
+
// ../../packages/session/session-context/dist/context-usage.js
|
|
8504
|
+
var ContextUsage = class {
|
|
8505
|
+
#tokens = 0;
|
|
8506
|
+
constructor(entries = []) {
|
|
8507
|
+
for (const entry of entries)
|
|
8508
|
+
this.append(entry);
|
|
8495
8509
|
}
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
throw new SessionSchemaError("session id does not match header");
|
|
8510
|
+
get tokens() {
|
|
8511
|
+
return this.#tokens;
|
|
8499
8512
|
}
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
const entryType = stringValue(input, "entryType");
|
|
8506
|
-
if (!ENTRY_TYPES.has(entryType)) {
|
|
8507
|
-
throw new SessionSchemaError("invalid session entry type");
|
|
8508
|
-
}
|
|
8509
|
-
const entryPayload = objectOf(input["payload"], "entry payload");
|
|
8510
|
-
if (entryType === "image_offload" && (!Array.isArray(entryPayload.keys) || entryPayload.keys.some((key) => typeof key !== "string"))) {
|
|
8511
|
-
throw new SessionSchemaError("invalid image offload keys");
|
|
8513
|
+
/** Histories are append-only and ordered; process each committed entry once. */
|
|
8514
|
+
append(entry) {
|
|
8515
|
+
if (entry.entryType === "context_reset" || entry.entryType === "compaction" || entry.entryType === "image_offload") {
|
|
8516
|
+
this.#tokens = null;
|
|
8517
|
+
return;
|
|
8512
8518
|
}
|
|
8513
|
-
if (
|
|
8514
|
-
const
|
|
8515
|
-
if (
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
for (const attachment of message.attachments)
|
|
8519
|
-
assertAttachmentContent(attachment);
|
|
8520
|
-
if (typeof message.attachmentKey !== "string" || !message.attachmentKey)
|
|
8521
|
-
throw new SessionSchemaError("missing attachment occurrence key");
|
|
8519
|
+
if (entry.entryType === "assistant_message") {
|
|
8520
|
+
const usage = validUsageTokens(entry.payload.usage);
|
|
8521
|
+
if (usage !== null) {
|
|
8522
|
+
this.#tokens = usage;
|
|
8523
|
+
return;
|
|
8522
8524
|
}
|
|
8523
8525
|
}
|
|
8524
|
-
if (
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
if (message["role"] !== "user" || typeof message["content"] !== "string" || !["catalog", "activation", "invalidation"].includes(String(metadata2["kind"])) || typeof metadata2["key"] !== "string")
|
|
8529
|
-
throw new SessionSchemaError("invalid skill context");
|
|
8526
|
+
if (this.#tokens === null)
|
|
8527
|
+
return;
|
|
8528
|
+
if (entry.entryType === "user_message" || entry.entryType === "assistant_message" || entry.entryType === "tool_result" || entry.entryType === "reminder" || entry.entryType === "skill_context") {
|
|
8529
|
+
this.#tokens += estimateMessage(entry.payload.message);
|
|
8530
8530
|
}
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8531
|
+
}
|
|
8532
|
+
};
|
|
8533
|
+
function validUsageTokens(usage) {
|
|
8534
|
+
if (usage === void 0 || usage === null)
|
|
8535
|
+
return null;
|
|
8536
|
+
const fields = [
|
|
8537
|
+
usage.inputTokens,
|
|
8538
|
+
usage.outputTokens,
|
|
8539
|
+
usage.cacheReadTokens ?? 0,
|
|
8540
|
+
usage.cacheWriteTokens ?? 0
|
|
8541
|
+
];
|
|
8542
|
+
if (!fields.every((value) => Number.isSafeInteger(value) && value >= 0))
|
|
8543
|
+
return null;
|
|
8544
|
+
const total = normalizeProviderUsage(usage);
|
|
8545
|
+
return Number.isSafeInteger(total) && total > 0 ? total : null;
|
|
8546
|
+
}
|
|
8547
|
+
function estimateMessage(message) {
|
|
8548
|
+
if (message.role === "system")
|
|
8549
|
+
return 0;
|
|
8550
|
+
if (message.role !== "assistant")
|
|
8551
|
+
return Math.ceil(message.content.length / 4);
|
|
8552
|
+
const chars = message.content.reduce((sum, block) => {
|
|
8553
|
+
if (block.type === "tool-call") {
|
|
8554
|
+
return sum + block.call.name.length + JSON.stringify(block.call.arguments).length;
|
|
8555
|
+
}
|
|
8556
|
+
return sum + block.text.length;
|
|
8557
|
+
}, 0);
|
|
8558
|
+
return Math.ceil(chars / 4);
|
|
8559
|
+
}
|
|
8560
|
+
|
|
8561
|
+
// ../../packages/session/session-context/dist/conversation-history.js
|
|
8562
|
+
var ConversationHistory = class _ConversationHistory {
|
|
8563
|
+
#journal;
|
|
8564
|
+
#entries;
|
|
8565
|
+
#staged = /* @__PURE__ */ new Map();
|
|
8566
|
+
#contextUsage;
|
|
8567
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
8568
|
+
constructor(entries, journal) {
|
|
8569
|
+
this.#entries = [...entries];
|
|
8570
|
+
this.#journal = journal;
|
|
8571
|
+
this.#contextUsage = new ContextUsage(entries);
|
|
8572
|
+
}
|
|
8573
|
+
static fromReplay(replay, journal) {
|
|
8574
|
+
const history = new _ConversationHistory(replay.items.filter((item) => item.kind === "entry"), journal);
|
|
8575
|
+
for (const open6 of replay.openToolCalls) {
|
|
8576
|
+
const entry = history.appendToolResults({
|
|
8577
|
+
requestId: "recovered",
|
|
8578
|
+
recovered: true,
|
|
8579
|
+
messages: [{
|
|
8580
|
+
role: "tool-result",
|
|
8581
|
+
toolCallId: open6.toolCallId,
|
|
8582
|
+
toolName: open6.toolName,
|
|
8583
|
+
content: JSON.stringify({
|
|
8584
|
+
ok: false,
|
|
8585
|
+
status: "interrupted",
|
|
8586
|
+
error: "Tool execution was interrupted before a result was recorded."
|
|
8587
|
+
}),
|
|
8588
|
+
isError: true
|
|
8589
|
+
}]
|
|
8590
|
+
})[0];
|
|
8591
|
+
journal.appendRecord({
|
|
8592
|
+
recordType: "error",
|
|
8593
|
+
payload: {
|
|
8594
|
+
type: "interrupted_tool_call",
|
|
8595
|
+
toolCallId: open6.toolCallId,
|
|
8596
|
+
repairedEntryId: entry?.id ?? null
|
|
8554
8597
|
}
|
|
8555
|
-
}
|
|
8556
|
-
if (message["role"] !== "user")
|
|
8557
|
-
throw new SessionSchemaError("invalid catalog message role");
|
|
8558
|
-
const catalog = objectOf(message["toolCatalog"], "tool catalog");
|
|
8559
|
-
if (catalog["mode"] !== "full" && catalog["mode"] !== "deferred")
|
|
8560
|
-
throw new SessionSchemaError("invalid tool mode");
|
|
8561
|
-
for (const version of Object.values(objectOf(catalog["tools"], "tool versions")))
|
|
8562
|
-
if (typeof version !== "string")
|
|
8563
|
-
throw new SessionSchemaError("invalid tool version");
|
|
8564
|
-
}
|
|
8598
|
+
});
|
|
8565
8599
|
}
|
|
8566
|
-
return
|
|
8600
|
+
return history;
|
|
8567
8601
|
}
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8602
|
+
appendSystemContext(input) {
|
|
8603
|
+
return this.append({
|
|
8604
|
+
entryType: "system_context",
|
|
8605
|
+
payload: input
|
|
8606
|
+
});
|
|
8607
|
+
}
|
|
8608
|
+
appendProjectInstructions(input) {
|
|
8609
|
+
if (input.message.content === "") {
|
|
8610
|
+
return null;
|
|
8572
8611
|
}
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
try {
|
|
8580
|
-
if (normalizeSessionTitle(title) !== title) {
|
|
8581
|
-
throw new SessionSchemaError("session name must already be normalized");
|
|
8582
|
-
}
|
|
8583
|
-
} catch (error) {
|
|
8584
|
-
if (error instanceof SessionSchemaError) {
|
|
8585
|
-
throw error;
|
|
8586
|
-
}
|
|
8587
|
-
throw new SessionSchemaError("invalid session name record");
|
|
8612
|
+
return this.append({
|
|
8613
|
+
entryType: "project_instructions",
|
|
8614
|
+
payload: {
|
|
8615
|
+
message: input.message,
|
|
8616
|
+
files: input.files,
|
|
8617
|
+
supersedesEntryIds: input.supersedesEntryIds ?? []
|
|
8588
8618
|
}
|
|
8619
|
+
});
|
|
8620
|
+
}
|
|
8621
|
+
appendUser(input) {
|
|
8622
|
+
return this.append({
|
|
8623
|
+
entryType: "user_message",
|
|
8624
|
+
payload: input
|
|
8625
|
+
});
|
|
8626
|
+
}
|
|
8627
|
+
stageUser(input) {
|
|
8628
|
+
const token = randomUUID2().replaceAll("-", "");
|
|
8629
|
+
this.#staged.set(token, input);
|
|
8630
|
+
return {
|
|
8631
|
+
token,
|
|
8632
|
+
message: input.message,
|
|
8633
|
+
inputEventIds: input.inputEventIds
|
|
8634
|
+
};
|
|
8635
|
+
}
|
|
8636
|
+
commitStagedUser(token) {
|
|
8637
|
+
const staged = this.#staged.get(token);
|
|
8638
|
+
if (staged === void 0) {
|
|
8639
|
+
throw new Error(`unknown staged user message: ${token}`);
|
|
8589
8640
|
}
|
|
8590
|
-
|
|
8641
|
+
this.#staged.delete(token);
|
|
8642
|
+
return this.appendUser(staged);
|
|
8591
8643
|
}
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
function objectOf(value, label) {
|
|
8595
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8596
|
-
throw new SessionSchemaError(`${label} must be an object`);
|
|
8644
|
+
discardStagedUser(token) {
|
|
8645
|
+
this.#staged.delete(token);
|
|
8597
8646
|
}
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
throw new SessionSchemaError(`${key} must be a non-empty string`);
|
|
8647
|
+
appendAssistant(input) {
|
|
8648
|
+
return this.append({
|
|
8649
|
+
entryType: "assistant_message",
|
|
8650
|
+
payload: input
|
|
8651
|
+
});
|
|
8604
8652
|
}
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
throw new SessionSchemaError(`${key} must be a positive integer`);
|
|
8653
|
+
appendToolResults(input) {
|
|
8654
|
+
return input.messages.map((message) => this.append({
|
|
8655
|
+
entryType: "tool_result",
|
|
8656
|
+
payload: {
|
|
8657
|
+
message,
|
|
8658
|
+
requestId: input.requestId,
|
|
8659
|
+
recovered: input.recovered
|
|
8660
|
+
}
|
|
8661
|
+
}));
|
|
8615
8662
|
}
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
import { join } from "node:path";
|
|
8622
|
-
|
|
8623
|
-
// ../../packages/session/session-store/dist/session-reader.js
|
|
8624
|
-
import { readFileSync } from "node:fs";
|
|
8625
|
-
var SessionCorruptError = class extends Error {
|
|
8626
|
-
path;
|
|
8627
|
-
line;
|
|
8628
|
-
constructor(path5, line2, message, options = {}) {
|
|
8629
|
-
super(`${path5}:${line2}: ${message}`, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
8630
|
-
this.name = "SessionCorruptError";
|
|
8631
|
-
this.path = path5;
|
|
8632
|
-
this.line = line2;
|
|
8663
|
+
appendReminder(input) {
|
|
8664
|
+
return this.append({
|
|
8665
|
+
entryType: "reminder",
|
|
8666
|
+
payload: input
|
|
8667
|
+
});
|
|
8633
8668
|
}
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
const text3 = readFileSync(path5, "utf8");
|
|
8637
|
-
const endsWithNewline = text3.endsWith("\n");
|
|
8638
|
-
const rawLines = text3.split("\n");
|
|
8639
|
-
if (endsWithNewline) {
|
|
8640
|
-
rawLines.pop();
|
|
8669
|
+
appendToolDefinitions(input) {
|
|
8670
|
+
return this.append({ entryType: "tool_definitions", payload: input });
|
|
8641
8671
|
}
|
|
8642
|
-
|
|
8643
|
-
|
|
8672
|
+
appendToolCatalog(input) {
|
|
8673
|
+
return this.append({ entryType: "tool_catalog", payload: input });
|
|
8644
8674
|
}
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
let ignoredTornTail = false;
|
|
8648
|
-
let previousSeq = 0;
|
|
8649
|
-
for (let index = 1; index < rawLines.length; index += 1) {
|
|
8650
|
-
const lineNumber = index + 1;
|
|
8651
|
-
const line2 = rawLines[index];
|
|
8652
|
-
if (line2.trim() === "") {
|
|
8653
|
-
continue;
|
|
8654
|
-
}
|
|
8655
|
-
try {
|
|
8656
|
-
const item = parseSessionItem(JSON.parse(line2), header.sessionId);
|
|
8657
|
-
if (item.seq !== previousSeq + 1) {
|
|
8658
|
-
throw new SessionSchemaError("session item seq must be contiguous");
|
|
8659
|
-
}
|
|
8660
|
-
previousSeq = item.seq;
|
|
8661
|
-
items.push(item);
|
|
8662
|
-
} catch (error) {
|
|
8663
|
-
if (!endsWithNewline && index === rawLines.length - 1) {
|
|
8664
|
-
ignoredTornTail = true;
|
|
8665
|
-
break;
|
|
8666
|
-
}
|
|
8667
|
-
throw corrupt(path5, lineNumber, error);
|
|
8668
|
-
}
|
|
8675
|
+
appendSkillContext(input) {
|
|
8676
|
+
return this.append({ entryType: "skill_context", payload: input });
|
|
8669
8677
|
}
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
openToolCalls: findOpenToolCalls(items)
|
|
8676
|
-
};
|
|
8677
|
-
}
|
|
8678
|
-
function parseLine(path5, line2, raw, parse3) {
|
|
8679
|
-
try {
|
|
8680
|
-
return parse3(JSON.parse(raw));
|
|
8681
|
-
} catch (error) {
|
|
8682
|
-
throw corrupt(path5, line2, error);
|
|
8678
|
+
appendCompaction(input) {
|
|
8679
|
+
return this.append({
|
|
8680
|
+
entryType: "compaction",
|
|
8681
|
+
payload: input
|
|
8682
|
+
});
|
|
8683
8683
|
}
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
if (error instanceof SessionCorruptError) {
|
|
8687
|
-
return error;
|
|
8684
|
+
entries() {
|
|
8685
|
+
return [...this.#entries];
|
|
8688
8686
|
}
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
if (item.kind === "entry" && item.entryType === "assistant_message") {
|
|
8695
|
-
for (const block of item.payload.message.content) {
|
|
8696
|
-
if (block.type === "tool-call") {
|
|
8697
|
-
calls.set(block.call.id, {
|
|
8698
|
-
entryId: item.id,
|
|
8699
|
-
seq: item.seq,
|
|
8700
|
-
toolCallId: block.call.id,
|
|
8701
|
-
toolName: block.call.name
|
|
8702
|
-
});
|
|
8703
|
-
}
|
|
8704
|
-
}
|
|
8705
|
-
}
|
|
8706
|
-
if (item.kind === "entry" && item.entryType === "tool_result") {
|
|
8707
|
-
calls.delete(item.payload.message.toolCallId);
|
|
8708
|
-
}
|
|
8687
|
+
offloadImages(keys2) {
|
|
8688
|
+
if (keys2.length === 0)
|
|
8689
|
+
return;
|
|
8690
|
+
this.append({ entryType: "image_offload", payload: { keys: [...keys2] } });
|
|
8691
|
+
this.#journal.flush();
|
|
8709
8692
|
}
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
// ../../packages/session/session-store/dist/attachment-references.js
|
|
8714
|
-
function attachmentReferences(entries) {
|
|
8715
|
-
const result = /* @__PURE__ */ new Set();
|
|
8716
|
-
for (const entry of entries) {
|
|
8717
|
-
if (entry.entryType === "compaction" || entry.entryType === "image_offload")
|
|
8718
|
-
continue;
|
|
8719
|
-
if (!("message" in entry.payload) || !entry.payload.message || typeof entry.payload.message !== "object")
|
|
8720
|
-
throw new Error("Incomplete session message during attachment scan");
|
|
8721
|
-
const message = entry.payload.message;
|
|
8722
|
-
if (!("attachments" in message))
|
|
8723
|
-
continue;
|
|
8724
|
-
for (const block of message.attachments ?? []) {
|
|
8725
|
-
assertAttachmentContent(block);
|
|
8726
|
-
result.add(block.ref.id);
|
|
8727
|
-
}
|
|
8693
|
+
get contextTokens() {
|
|
8694
|
+
return this.#contextUsage.tokens;
|
|
8728
8695
|
}
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
for (const file of readdirSync(directory, { withFileTypes: true })) {
|
|
8735
|
-
const path5 = join(directory, file.name);
|
|
8736
|
-
if (file.isSymbolicLink())
|
|
8737
|
-
throw new Error(`Cannot scan symbolic session path: ${path5}`);
|
|
8738
|
-
if (file.isDirectory())
|
|
8739
|
-
visit(path5);
|
|
8740
|
-
else if (file.name.endsWith(".jsonl")) {
|
|
8741
|
-
if (!lstatSync(path5).isFile())
|
|
8742
|
-
throw new Error(`Invalid session file: ${path5}`);
|
|
8743
|
-
const replay = readSessionFile(path5);
|
|
8744
|
-
if (replay.ignoredTornTail)
|
|
8745
|
-
throw new Error(`Incomplete session journal: ${path5}`);
|
|
8746
|
-
for (const id of attachmentReferences(replay.items.filter((item) => item.kind === "entry")))
|
|
8747
|
-
result.add(id);
|
|
8748
|
-
}
|
|
8749
|
-
}
|
|
8750
|
-
};
|
|
8751
|
-
visit(sessionsRoot);
|
|
8752
|
-
return result;
|
|
8753
|
-
}
|
|
8754
|
-
|
|
8755
|
-
// ../../packages/session/session-store/dist/session-paths.js
|
|
8756
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
8757
|
-
import { mkdirSync, realpathSync } from "node:fs";
|
|
8758
|
-
import { basename, join as join2, resolve } from "node:path";
|
|
8759
|
-
function canonicalProjectRoot(projectRoot) {
|
|
8760
|
-
try {
|
|
8761
|
-
return realpathSync.native(projectRoot);
|
|
8762
|
-
} catch {
|
|
8763
|
-
return resolve(projectRoot);
|
|
8696
|
+
onChange(listener) {
|
|
8697
|
+
this.#listeners.add(listener);
|
|
8698
|
+
return () => {
|
|
8699
|
+
this.#listeners.delete(listener);
|
|
8700
|
+
};
|
|
8764
8701
|
}
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
const directory = join2(sessionsRoot, projectKey);
|
|
8773
|
-
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
8774
|
-
return directory;
|
|
8775
|
-
}
|
|
8776
|
-
function sessionPathForHeader(sessionsRoot, projectKey, createdAt, sessionId) {
|
|
8777
|
-
const safeCreatedAt = createdAt.replaceAll(":", "").replaceAll(".", "");
|
|
8778
|
-
return join2(ensureSessionDirectory(sessionsRoot, projectKey), `${safeCreatedAt}_${sessionId}.jsonl`);
|
|
8779
|
-
}
|
|
8780
|
-
function sanitizePathPart(value) {
|
|
8781
|
-
const sanitized = value.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8782
|
-
return sanitized || "project";
|
|
8783
|
-
}
|
|
8784
|
-
|
|
8785
|
-
// ../../packages/session/session-store/dist/session-journal.js
|
|
8786
|
-
import { closeSync, existsSync, fsyncSync, openSync, readFileSync as readFileSync2, unlinkSync, writeSync } from "node:fs";
|
|
8787
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
8788
|
-
var SessionJournalLockError = class extends Error {
|
|
8789
|
-
constructor(message) {
|
|
8790
|
-
super(message);
|
|
8791
|
-
this.name = "SessionJournalLockError";
|
|
8702
|
+
append(input) {
|
|
8703
|
+
const entry = this.#journal.appendEntry(input);
|
|
8704
|
+
this.#entries.push(entry);
|
|
8705
|
+
this.#contextUsage.append(entry);
|
|
8706
|
+
for (const listener of this.#listeners)
|
|
8707
|
+
listener();
|
|
8708
|
+
return entry;
|
|
8792
8709
|
}
|
|
8793
8710
|
};
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
this.attachments = input.attachments;
|
|
8804
|
-
this.header = input.header;
|
|
8805
|
-
this.path = input.path;
|
|
8806
|
-
this.#nextSeq = input.nextSeq;
|
|
8807
|
-
this.#fd = input.fd;
|
|
8808
|
-
this.#lockPath = `${input.path}.lock`;
|
|
8711
|
+
|
|
8712
|
+
// ../../packages/llm/llm/dist/model-contracts.js
|
|
8713
|
+
var ModelStreamError = class extends Error {
|
|
8714
|
+
/** True when the stream had already delivered deltas before failing. */
|
|
8715
|
+
hadDelta;
|
|
8716
|
+
constructor(message, options = {}) {
|
|
8717
|
+
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
8718
|
+
this.name = "ModelStreamError";
|
|
8719
|
+
this.hadDelta = options.hadDelta ?? false;
|
|
8809
8720
|
}
|
|
8810
|
-
|
|
8811
|
-
|
|
8721
|
+
};
|
|
8722
|
+
var ModelStreamCancelled = class extends ModelStreamError {
|
|
8723
|
+
constructor(message, options = {}) {
|
|
8724
|
+
super(message, options);
|
|
8725
|
+
this.name = "ModelStreamCancelled";
|
|
8812
8726
|
}
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
this.attachments.commit(attachments.map((block) => block.ref), () => {
|
|
8831
|
-
this.writeItem(entry);
|
|
8832
|
-
this.flush();
|
|
8833
|
-
});
|
|
8834
|
-
} else
|
|
8835
|
-
this.writeItem(entry);
|
|
8836
|
-
if (entry.entryType === "compaction") {
|
|
8837
|
-
this.flush();
|
|
8838
|
-
}
|
|
8839
|
-
return entry;
|
|
8727
|
+
};
|
|
8728
|
+
var REASONING_EFFORTS2 = [
|
|
8729
|
+
"off",
|
|
8730
|
+
"minimal",
|
|
8731
|
+
"low",
|
|
8732
|
+
"medium",
|
|
8733
|
+
"high",
|
|
8734
|
+
"xhigh",
|
|
8735
|
+
"max"
|
|
8736
|
+
];
|
|
8737
|
+
function isReasoningEffort(value) {
|
|
8738
|
+
return REASONING_EFFORTS2.includes(value);
|
|
8739
|
+
}
|
|
8740
|
+
function clampReasoningEffort(supported, requested) {
|
|
8741
|
+
const available = supported.length === 0 ? ["off"] : supported;
|
|
8742
|
+
if (available.includes(requested)) {
|
|
8743
|
+
return requested;
|
|
8840
8744
|
}
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
payload: input.payload ?? {}
|
|
8847
|
-
};
|
|
8848
|
-
this.writeItem(record);
|
|
8849
|
-
if (record.recordType === "turn_finished" || record.recordType === "compaction_finished" || record.recordType === "session_closed") {
|
|
8850
|
-
this.flush();
|
|
8745
|
+
const requestedIndex = REASONING_EFFORTS2.indexOf(requested);
|
|
8746
|
+
for (let index = requestedIndex; index < REASONING_EFFORTS2.length; index += 1) {
|
|
8747
|
+
const candidate = REASONING_EFFORTS2[index];
|
|
8748
|
+
if (available.includes(candidate)) {
|
|
8749
|
+
return candidate;
|
|
8851
8750
|
}
|
|
8852
|
-
return record;
|
|
8853
8751
|
}
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8752
|
+
for (let index = requestedIndex - 1; index >= 0; index -= 1) {
|
|
8753
|
+
const candidate = REASONING_EFFORTS2[index];
|
|
8754
|
+
if (available.includes(candidate)) {
|
|
8755
|
+
return candidate;
|
|
8857
8756
|
}
|
|
8858
8757
|
}
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8758
|
+
return "off";
|
|
8759
|
+
}
|
|
8760
|
+
function projectImageOffloads(message, keys2) {
|
|
8761
|
+
if (message.role !== "user" && message.role !== "tool-result" || !message.attachments?.length)
|
|
8762
|
+
return message;
|
|
8763
|
+
const omittedImageIndexes = message.attachments.flatMap((block, index) => block.type === "image" && keys2.has(`${message.attachmentKey}:${index}`) ? [index] : []);
|
|
8764
|
+
if (!omittedImageIndexes.length)
|
|
8765
|
+
return message;
|
|
8766
|
+
return { ...message, omittedImageIndexes };
|
|
8767
|
+
}
|
|
8768
|
+
var ModelError = class extends ModelStreamError {
|
|
8769
|
+
kind;
|
|
8770
|
+
constructor(message, options) {
|
|
8771
|
+
super(message, { hadDelta: options.hadDelta ?? false, cause: options.cause });
|
|
8772
|
+
this.name = "ModelError";
|
|
8773
|
+
this.kind = options.kind;
|
|
8774
|
+
}
|
|
8775
|
+
};
|
|
8776
|
+
var CONTEXT_OVERFLOW_PATTERN = /context length|maximum context|context window|too many tokens|reduce the length/i;
|
|
8777
|
+
var NETWORK_PATTERN = /fetch failed|network|econnreset|econnrefused|etimedout|socket hang up|timed out/i;
|
|
8778
|
+
function readStatus(record) {
|
|
8779
|
+
let status = record["status_code"];
|
|
8780
|
+
if (status === null || status === void 0) {
|
|
8781
|
+
const response = record["response"];
|
|
8782
|
+
if (response !== null && typeof response === "object") {
|
|
8783
|
+
status = response["status_code"];
|
|
8868
8784
|
}
|
|
8869
8785
|
}
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
schemaVersion: 1,
|
|
8873
|
-
sessionId: this.header.sessionId,
|
|
8874
|
-
seq: this.#nextSeq,
|
|
8875
|
-
id: randomUUID2().replaceAll("-", ""),
|
|
8876
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8877
|
-
...input.taskId === void 0 ? {} : { taskId: input.taskId },
|
|
8878
|
-
...input.turnId === void 0 ? {} : { turnId: input.turnId },
|
|
8879
|
-
...input.copiedFrom === void 0 ? {} : { copiedFrom: input.copiedFrom }
|
|
8880
|
-
};
|
|
8786
|
+
if (status === null || status === void 0) {
|
|
8787
|
+
status = record["status"];
|
|
8881
8788
|
}
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8789
|
+
return status;
|
|
8790
|
+
}
|
|
8791
|
+
function classifyModelError(error) {
|
|
8792
|
+
let current = error;
|
|
8793
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8794
|
+
while (current !== null && current !== void 0 && (typeof current === "object" || typeof current === "function") && !seen.has(current)) {
|
|
8795
|
+
seen.add(current);
|
|
8796
|
+
const record = current;
|
|
8797
|
+
const status = readStatus(record);
|
|
8798
|
+
if (status === 401 || status === 403 || record["name"] === "AuthenticationError") {
|
|
8799
|
+
return "authentication";
|
|
8885
8800
|
}
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
}
|
|
8889
|
-
};
|
|
8890
|
-
function createSessionJournal(options) {
|
|
8891
|
-
const header = options.header ?? makeHeader(options);
|
|
8892
|
-
const path5 = options.path ?? sessionPathForHeader(options.sessionsRoot, header.projectKey, header.createdAt, header.sessionId);
|
|
8893
|
-
const existing = existsSync(path5);
|
|
8894
|
-
const lockPath = `${path5}.lock`;
|
|
8895
|
-
acquireLock(lockPath, header.sessionId);
|
|
8896
|
-
try {
|
|
8897
|
-
const fd = openSync(path5, existing ? "a" : "wx", 384);
|
|
8898
|
-
if (!existing) {
|
|
8899
|
-
writeLine(fd, header);
|
|
8900
|
-
fsyncSync(fd);
|
|
8901
|
-
return new FileSessionJournal({ header, path: path5, nextSeq: 1, fd, attachments: options.attachments });
|
|
8801
|
+
if (status === 429 || record["name"] === "RateLimitError") {
|
|
8802
|
+
return "rate_limited";
|
|
8902
8803
|
}
|
|
8903
|
-
const
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8804
|
+
const message = typeof record["message"] === "string" ? record["message"] : "";
|
|
8805
|
+
if (status === 408 || status === 504 || record["name"] === "TimeoutError" || /\btimeout\b|\btimed out\b/i.test(message)) {
|
|
8806
|
+
return "timeout";
|
|
8807
|
+
}
|
|
8808
|
+
if (CONTEXT_OVERFLOW_PATTERN.test(message)) {
|
|
8809
|
+
return "context_overflow";
|
|
8810
|
+
}
|
|
8811
|
+
if (typeof status === "number" && status >= 500) {
|
|
8812
|
+
return "server";
|
|
8813
|
+
}
|
|
8814
|
+
if (record["name"] === "APIConnectionError" || NETWORK_PATTERN.test(message)) {
|
|
8815
|
+
return "retryable";
|
|
8816
|
+
}
|
|
8817
|
+
current = record["cause"];
|
|
8914
8818
|
}
|
|
8819
|
+
return "retryable";
|
|
8915
8820
|
}
|
|
8916
|
-
function
|
|
8917
|
-
|
|
8918
|
-
|
|
8821
|
+
function modelErrorKind(error) {
|
|
8822
|
+
return error instanceof ModelError ? error.kind : classifyModelError(error);
|
|
8823
|
+
}
|
|
8824
|
+
function portableModelMessage(message) {
|
|
8825
|
+
if (message.role !== "assistant") {
|
|
8826
|
+
return message;
|
|
8827
|
+
}
|
|
8919
8828
|
return {
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
initialCwd: required(options.initialCwd, "initialCwd"),
|
|
8925
|
-
projectRoot,
|
|
8926
|
-
projectKey: projectKeyForRoot(projectRoot),
|
|
8927
|
-
appVersion: required(options.appVersion, "appVersion"),
|
|
8928
|
-
provider: required(options.provider, "provider"),
|
|
8929
|
-
model: required(options.model, "model"),
|
|
8930
|
-
reasoningEffort: options.reasoningEffort ?? "high",
|
|
8931
|
-
origin: options.origin ?? "new",
|
|
8932
|
-
...options.parentSessionId === void 0 ? {} : { parentSessionId: options.parentSessionId },
|
|
8933
|
-
...options.forkedFrom === void 0 ? {} : { forkedFrom: options.forkedFrom }
|
|
8829
|
+
role: "assistant",
|
|
8830
|
+
provider: message.provider,
|
|
8831
|
+
model: message.model,
|
|
8832
|
+
content: message.content
|
|
8934
8833
|
};
|
|
8935
8834
|
}
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8835
|
+
|
|
8836
|
+
// ../../packages/llm/llm/dist/model-runtime.js
|
|
8837
|
+
var ModelRuntime = class {
|
|
8838
|
+
adapter;
|
|
8839
|
+
retryDelaysMs;
|
|
8840
|
+
sleep;
|
|
8841
|
+
constructor(adapter, options = {}) {
|
|
8842
|
+
this.adapter = adapter;
|
|
8843
|
+
this.retryDelaysMs = validateRetryDelays(options.retryDelaysMs ?? [250, 1e3]);
|
|
8844
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
8845
|
+
}
|
|
8846
|
+
async complete(request) {
|
|
8847
|
+
if (request.cancelToken?.isCancelled()) {
|
|
8848
|
+
throw new ModelStreamCancelled(request.cancelToken.reason || "cancelled");
|
|
8849
|
+
}
|
|
8850
|
+
const maxAttempts = validateMaxAttempts(request.maxAttempts ?? 3);
|
|
8851
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
8852
|
+
const adapterRequest = {
|
|
8853
|
+
...request.imageContext === void 0 ? {} : { imageContext: request.imageContext },
|
|
8854
|
+
provider: request.provider,
|
|
8855
|
+
model: request.model,
|
|
8856
|
+
...request.baseUrl === void 0 ? {} : { baseUrl: request.baseUrl },
|
|
8857
|
+
messages: request.messages,
|
|
8858
|
+
tools: request.tools,
|
|
8859
|
+
...request.reasoningEffort === void 0 ? {} : { reasoningEffort: request.reasoningEffort },
|
|
8860
|
+
...request.temperature === void 0 ? {} : { temperature: request.temperature },
|
|
8861
|
+
...request.maxOutputTokens === void 0 ? {} : { maxOutputTokens: request.maxOutputTokens },
|
|
8862
|
+
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
8863
|
+
...request.requestId === void 0 ? {} : { requestId: request.requestId },
|
|
8864
|
+
...request.cancelToken == null ? {} : { cancelToken: request.cancelToken },
|
|
8865
|
+
...request.isRequestActive == null ? {} : { isRequestActive: request.isRequestActive },
|
|
8866
|
+
...request.onDelta == null ? {} : { onEvent: (event) => forwardEvent(event, request.onDelta) },
|
|
8867
|
+
...request.onRequestOpened == null ? {} : { onRequestOpened: request.onRequestOpened }
|
|
8868
|
+
};
|
|
8869
|
+
try {
|
|
8870
|
+
return await this.adapter.runAttempt(adapterRequest);
|
|
8871
|
+
} catch (error) {
|
|
8872
|
+
const normalized = normalizeRuntimeError(error, request.cancelToken ?? null);
|
|
8873
|
+
if (!shouldRetry(normalized, attempt, maxAttempts)) {
|
|
8874
|
+
throw normalized;
|
|
8875
|
+
}
|
|
8876
|
+
const delayMs = this.retryDelaysMs[Math.min(attempt - 1, this.retryDelaysMs.length - 1)];
|
|
8877
|
+
request.onDelta?.("model_retry_scheduled", {
|
|
8878
|
+
attempt: attempt + 1,
|
|
8879
|
+
max_attempts: maxAttempts,
|
|
8880
|
+
delay_ms: delayMs,
|
|
8881
|
+
error_kind: normalized.kind
|
|
8882
|
+
});
|
|
8883
|
+
await this.sleep(delayMs, request.cancelToken ?? null);
|
|
8884
|
+
}
|
|
8948
8885
|
}
|
|
8949
|
-
throw new
|
|
8886
|
+
throw new ModelError("model retry loop exhausted", { kind: "protocol" });
|
|
8887
|
+
}
|
|
8888
|
+
};
|
|
8889
|
+
function forwardEvent(event, handler) {
|
|
8890
|
+
if (event.type === "text-delta") {
|
|
8891
|
+
handler?.("model_text_delta", { text: event.text });
|
|
8892
|
+
} else if (event.type === "reasoning-delta") {
|
|
8893
|
+
handler?.("model_reasoning_delta", { text: event.text });
|
|
8894
|
+
} else if (event.type === "tool-call-delta") {
|
|
8895
|
+
handler?.("model_tool_call_delta", {
|
|
8896
|
+
index: event.index,
|
|
8897
|
+
id: event.id,
|
|
8898
|
+
name: event.name ?? null,
|
|
8899
|
+
arguments: event.argumentsDelta
|
|
8900
|
+
});
|
|
8901
|
+
} else {
|
|
8902
|
+
handler?.("model_response_validating", {});
|
|
8950
8903
|
}
|
|
8951
8904
|
}
|
|
8952
|
-
function
|
|
8953
|
-
if (
|
|
8954
|
-
return
|
|
8905
|
+
function normalizeRuntimeError(error, cancelToken) {
|
|
8906
|
+
if (error instanceof ModelStreamCancelled) {
|
|
8907
|
+
return error;
|
|
8955
8908
|
}
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid)) {
|
|
8959
|
-
return false;
|
|
8960
|
-
}
|
|
8961
|
-
process.kill(parsed.pid, 0);
|
|
8962
|
-
return true;
|
|
8963
|
-
} catch (error) {
|
|
8964
|
-
const code = error.code;
|
|
8965
|
-
return code === "EPERM";
|
|
8909
|
+
if (cancelToken?.isCancelled()) {
|
|
8910
|
+
return new ModelStreamCancelled(cancelToken.reason || "cancelled", { cause: error });
|
|
8966
8911
|
}
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
if (existsSync(path5)) {
|
|
8970
|
-
unlinkSync(path5);
|
|
8912
|
+
if (error instanceof ModelError) {
|
|
8913
|
+
return error;
|
|
8971
8914
|
}
|
|
8915
|
+
if (error instanceof ModelStreamError) {
|
|
8916
|
+
return new ModelError(error.message, {
|
|
8917
|
+
kind: modelErrorKind(error),
|
|
8918
|
+
hadDelta: error.hadDelta,
|
|
8919
|
+
cause: error
|
|
8920
|
+
});
|
|
8921
|
+
}
|
|
8922
|
+
return new ModelError(errorMessage(error), {
|
|
8923
|
+
kind: modelErrorKind(error),
|
|
8924
|
+
cause: error
|
|
8925
|
+
});
|
|
8972
8926
|
}
|
|
8973
|
-
function
|
|
8974
|
-
|
|
8975
|
-
|
|
8927
|
+
function shouldRetry(error, attempt, maxAttempts) {
|
|
8928
|
+
if (!(error instanceof ModelError) || attempt >= maxAttempts) {
|
|
8929
|
+
return false;
|
|
8930
|
+
}
|
|
8931
|
+
if (error.hadDelta) {
|
|
8932
|
+
return false;
|
|
8933
|
+
}
|
|
8934
|
+
return error.kind === "timeout" || error.kind === "rate_limited" || error.kind === "server" || error.kind === "retryable";
|
|
8976
8935
|
}
|
|
8977
|
-
function
|
|
8978
|
-
if (value
|
|
8979
|
-
throw new
|
|
8936
|
+
function validateMaxAttempts(value) {
|
|
8937
|
+
if (!Number.isInteger(value) || value < 1 || value > 10) {
|
|
8938
|
+
throw new RangeError("maxAttempts must be an integer from 1 through 10");
|
|
8980
8939
|
}
|
|
8981
8940
|
return value;
|
|
8982
8941
|
}
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
import { join as join3 } from "node:path";
|
|
8987
|
-
|
|
8988
|
-
// ../../packages/session/session-store/dist/session-tree.js
|
|
8989
|
-
function buildSessionTree(sessions) {
|
|
8990
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
8991
|
-
for (const session of sessions) {
|
|
8992
|
-
nodes.set(session.sessionId, { session, children: [] });
|
|
8942
|
+
function validateRetryDelays(value) {
|
|
8943
|
+
if (value.length === 0) {
|
|
8944
|
+
throw new RangeError("retryDelaysMs must not be empty");
|
|
8993
8945
|
}
|
|
8994
|
-
const
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
const parentId = session.parentSessionId;
|
|
8998
|
-
const parent = parentId === void 0 ? void 0 : nodes.get(parentId);
|
|
8999
|
-
if (parent === void 0) {
|
|
9000
|
-
roots.push(node);
|
|
9001
|
-
} else {
|
|
9002
|
-
parent.children.push(node);
|
|
8946
|
+
for (const delay2 of value) {
|
|
8947
|
+
if (!Number.isInteger(delay2) || delay2 < 0 || !Number.isFinite(delay2)) {
|
|
8948
|
+
throw new RangeError("retryDelaysMs must contain finite non-negative integers");
|
|
9003
8949
|
}
|
|
9004
8950
|
}
|
|
9005
|
-
|
|
9006
|
-
session: item.session,
|
|
9007
|
-
children: sortNodes(item.children)
|
|
9008
|
-
}));
|
|
9009
|
-
return sortNodes(roots);
|
|
8951
|
+
return [...value];
|
|
9010
8952
|
}
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
#attachments;
|
|
9015
|
-
#sessionsRoot;
|
|
9016
|
-
#appVersion;
|
|
9017
|
-
constructor(options) {
|
|
9018
|
-
this.#attachments = options.attachments;
|
|
9019
|
-
this.#sessionsRoot = options.sessionsRoot;
|
|
9020
|
-
this.#appVersion = options.appVersion;
|
|
9021
|
-
}
|
|
9022
|
-
create(options) {
|
|
9023
|
-
const journal = createSessionJournal({
|
|
9024
|
-
attachments: this.#attachments,
|
|
9025
|
-
sessionsRoot: this.#sessionsRoot,
|
|
9026
|
-
projectRoot: options.projectRoot,
|
|
9027
|
-
initialCwd: options.initialCwd,
|
|
9028
|
-
appVersion: this.#appVersion,
|
|
9029
|
-
provider: options.provider,
|
|
9030
|
-
model: options.model,
|
|
9031
|
-
reasoningEffort: options.reasoningEffort,
|
|
9032
|
-
origin: "new"
|
|
9033
|
-
});
|
|
9034
|
-
return {
|
|
9035
|
-
header: journal.header,
|
|
9036
|
-
journal,
|
|
9037
|
-
replay: {
|
|
9038
|
-
header: journal.header,
|
|
9039
|
-
items: [],
|
|
9040
|
-
lastSeq: 0,
|
|
9041
|
-
ignoredTornTail: false,
|
|
9042
|
-
openToolCalls: []
|
|
9043
|
-
}
|
|
9044
|
-
};
|
|
9045
|
-
}
|
|
9046
|
-
open(sessionId) {
|
|
9047
|
-
const summary = this.list().find((item) => item.sessionId === sessionId);
|
|
9048
|
-
if (summary === void 0 || summary.status === "corrupt") {
|
|
9049
|
-
throw new Error(`unknown session: ${sessionId}`);
|
|
9050
|
-
}
|
|
9051
|
-
const replay = readSessionFile(summary.path);
|
|
9052
|
-
const journal = createSessionJournal({
|
|
9053
|
-
attachments: this.#attachments,
|
|
9054
|
-
sessionsRoot: this.#sessionsRoot,
|
|
9055
|
-
header: replay.header,
|
|
9056
|
-
path: summary.path
|
|
9057
|
-
});
|
|
9058
|
-
return { header: replay.header, journal, replay };
|
|
8953
|
+
async function defaultSleep(delayMs, cancelToken) {
|
|
8954
|
+
if (cancelToken?.isCancelled()) {
|
|
8955
|
+
throw new ModelStreamCancelled(cancelToken.reason || "cancelled");
|
|
9059
8956
|
}
|
|
9060
|
-
|
|
9061
|
-
|
|
9062
|
-
|
|
8957
|
+
const cancelled = await cancelToken?.wait(delayMs);
|
|
8958
|
+
if (cancelled) {
|
|
8959
|
+
throw new ModelStreamCancelled(cancelToken?.reason || "cancelled");
|
|
9063
8960
|
}
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
return this.sessionPaths().map((path5) => summaryForPath(path5)).filter((summary) => canonical === null || summary.projectRoot === canonical).sort((left, right) => {
|
|
9067
|
-
const updated = right.updatedAt.localeCompare(left.updatedAt);
|
|
9068
|
-
return updated === 0 ? right.createdAt.localeCompare(left.createdAt) : updated;
|
|
9069
|
-
});
|
|
8961
|
+
if (cancelToken === null) {
|
|
8962
|
+
await new Promise((resolve15) => setTimeout(resolve15, delayMs));
|
|
9070
8963
|
}
|
|
9071
|
-
|
|
9072
|
-
|
|
8964
|
+
}
|
|
8965
|
+
function errorMessage(error) {
|
|
8966
|
+
return error instanceof Error ? error.message : String(error);
|
|
8967
|
+
}
|
|
8968
|
+
|
|
8969
|
+
// ../../packages/session/session-context/dist/context-builder.js
|
|
8970
|
+
var ContextBuildError = class extends Error {
|
|
8971
|
+
entryId;
|
|
8972
|
+
constructor(entryId, message) {
|
|
8973
|
+
super(`${entryId}: ${message}`);
|
|
8974
|
+
this.name = "ContextBuildError";
|
|
8975
|
+
this.entryId = entryId;
|
|
9073
8976
|
}
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
8977
|
+
};
|
|
8978
|
+
var ContextBuilder = class {
|
|
8979
|
+
build(input) {
|
|
8980
|
+
const sorted = [...input.entries].sort((left, right) => left.seq - right.seq);
|
|
8981
|
+
const superseded = supersededEntryIds(sorted);
|
|
8982
|
+
const boundary = contextResetBoundary(sorted);
|
|
8983
|
+
const activeCompaction = latestCompaction(sorted.filter((entry) => entry.seq > boundary));
|
|
8984
|
+
const selected = [];
|
|
8985
|
+
const system = latestActive(sorted, "system_context", superseded);
|
|
8986
|
+
if (system !== null) {
|
|
8987
|
+
selected.push({ entryId: system.id, message: system.payload.message });
|
|
9079
8988
|
}
|
|
9080
|
-
const
|
|
9081
|
-
|
|
9082
|
-
|
|
9083
|
-
projectRoot: parent.header.projectRoot,
|
|
9084
|
-
initialCwd: parent.header.initialCwd,
|
|
9085
|
-
appVersion: this.#appVersion,
|
|
9086
|
-
provider: parent.header.provider,
|
|
9087
|
-
model: parent.header.model,
|
|
9088
|
-
reasoningEffort: parent.header.reasoningEffort,
|
|
9089
|
-
origin: "fork",
|
|
9090
|
-
parentSessionId: parent.header.sessionId,
|
|
9091
|
-
forkedFrom: {
|
|
9092
|
-
sessionId: parent.header.sessionId,
|
|
9093
|
-
seq: target.seq,
|
|
9094
|
-
entryId: target.id,
|
|
9095
|
-
mode: options.mode
|
|
8989
|
+
for (const entry of sorted) {
|
|
8990
|
+
if (entry.entryType === "project_instructions" && !superseded.has(entry.id)) {
|
|
8991
|
+
selected.push({ entryId: entry.id, message: entry.payload.message });
|
|
9096
8992
|
}
|
|
9097
|
-
}
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
8993
|
+
}
|
|
8994
|
+
if (activeCompaction !== null) {
|
|
8995
|
+
selected.push({
|
|
8996
|
+
entryId: activeCompaction.id,
|
|
8997
|
+
message: {
|
|
8998
|
+
role: "user",
|
|
8999
|
+
content: `<conversation_summary>
|
|
9000
|
+
${activeCompaction.payload.summary}
|
|
9001
|
+
</conversation_summary>`
|
|
9002
|
+
}
|
|
9003
|
+
});
|
|
9004
|
+
}
|
|
9005
|
+
for (const entry of conversationTail(sorted, activeCompaction).filter((entry2) => entry2.seq > boundary)) {
|
|
9006
|
+
const message = modelMessageForEntry(entry, input.currentProvider, input.currentModel);
|
|
9007
|
+
if (message !== null) {
|
|
9008
|
+
selected.push({ entryId: entry.id, message });
|
|
9009
|
+
}
|
|
9010
|
+
}
|
|
9011
|
+
validateToolPairs(selected);
|
|
9012
|
+
const offloaded = new Set(sorted.flatMap((entry) => entry.entryType === "image_offload" ? entry.payload.keys : []));
|
|
9102
9013
|
return {
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9014
|
+
messages: selected.map((item) => projectImageOffloads(item.message, offloaded)),
|
|
9015
|
+
sourceEntryIds: selected.map((item) => item.entryId),
|
|
9016
|
+
activeCompactionId: activeCompaction?.id ?? null
|
|
9106
9017
|
};
|
|
9107
9018
|
}
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
model: parent.header.model,
|
|
9119
|
-
reasoningEffort: parent.header.reasoningEffort,
|
|
9120
|
-
origin: "clone",
|
|
9121
|
-
parentSessionId: parent.header.sessionId,
|
|
9122
|
-
forkedFrom: {
|
|
9123
|
-
sessionId: parent.header.sessionId,
|
|
9124
|
-
...lastEntry === void 0 ? {} : { seq: lastEntry.seq, entryId: lastEntry.id },
|
|
9125
|
-
mode: "clone"
|
|
9019
|
+
};
|
|
9020
|
+
function supersededEntryIds(entries) {
|
|
9021
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
9022
|
+
for (const entry of entries) {
|
|
9023
|
+
if (entry.entryType === "system_context" && entry.payload.supersedesEntryId !== void 0) {
|
|
9024
|
+
superseded.add(entry.payload.supersedesEntryId);
|
|
9025
|
+
}
|
|
9026
|
+
if (entry.entryType === "project_instructions") {
|
|
9027
|
+
for (const id of entry.payload.supersedesEntryIds) {
|
|
9028
|
+
superseded.add(id);
|
|
9126
9029
|
}
|
|
9127
|
-
}
|
|
9128
|
-
copyEntries(child, parent.items.filter(isEntry));
|
|
9129
|
-
appendTitleSnapshot(child, sessionTitleAt(parent.items));
|
|
9130
|
-
child.close();
|
|
9131
|
-
return { sessionId: child.header.sessionId, path: child.path };
|
|
9030
|
+
}
|
|
9132
9031
|
}
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9032
|
+
return superseded;
|
|
9033
|
+
}
|
|
9034
|
+
function latestActive(entries, entryType, superseded) {
|
|
9035
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
9036
|
+
const entry = entries[index];
|
|
9037
|
+
if (entry.entryType === entryType && !superseded.has(entry.id)) {
|
|
9038
|
+
return entry;
|
|
9137
9039
|
}
|
|
9138
|
-
return readSessionFile(summary.path);
|
|
9139
9040
|
}
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9041
|
+
return null;
|
|
9042
|
+
}
|
|
9043
|
+
function latestCompaction(entries) {
|
|
9044
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
9045
|
+
const entry = entries[index];
|
|
9046
|
+
if (entry.entryType === "compaction") {
|
|
9047
|
+
return entry;
|
|
9143
9048
|
}
|
|
9144
|
-
const paths = [];
|
|
9145
|
-
const visit = (directory) => {
|
|
9146
|
-
for (const entry of readdirSync2(directory)) {
|
|
9147
|
-
const absolute = join3(directory, entry);
|
|
9148
|
-
const stat6 = statSync(absolute);
|
|
9149
|
-
if (stat6.isDirectory()) {
|
|
9150
|
-
visit(absolute);
|
|
9151
|
-
} else if (absolute.endsWith(".jsonl")) {
|
|
9152
|
-
paths.push(absolute);
|
|
9153
|
-
}
|
|
9154
|
-
}
|
|
9155
|
-
};
|
|
9156
|
-
visit(this.#sessionsRoot);
|
|
9157
|
-
return paths.sort();
|
|
9158
9049
|
}
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
lastUserText: lastUserText(replay.items),
|
|
9178
|
-
status: last?.kind === "record" && last.recordType === "session_closed" ? "closed" : existsSync2(`${path5}.lock`) ? "open" : "interrupted"
|
|
9179
|
-
};
|
|
9180
|
-
} catch {
|
|
9181
|
-
return {
|
|
9182
|
-
sessionId: path5,
|
|
9183
|
-
path: path5,
|
|
9184
|
-
createdAt: "",
|
|
9185
|
-
updatedAt: "",
|
|
9186
|
-
projectRoot: "",
|
|
9187
|
-
cwd: "",
|
|
9188
|
-
provider: "",
|
|
9189
|
-
model: "",
|
|
9190
|
-
origin: "import",
|
|
9191
|
-
lastUserText: "",
|
|
9192
|
-
status: "corrupt"
|
|
9193
|
-
};
|
|
9050
|
+
return null;
|
|
9051
|
+
}
|
|
9052
|
+
function conversationTail(entries, activeCompaction) {
|
|
9053
|
+
const boundary = activeCompaction?.payload.retainedFromSeq ?? 1;
|
|
9054
|
+
return entries.filter((entry) => entry.seq >= boundary && entry.entryType !== "system_context" && entry.entryType !== "project_instructions" && entry.entryType !== "compaction");
|
|
9055
|
+
}
|
|
9056
|
+
function modelMessageForEntry(entry, currentProvider, currentModel) {
|
|
9057
|
+
if (entry.entryType === "tool_definitions" || entry.entryType === "tool_catalog" || entry.entryType === "skill_context")
|
|
9058
|
+
return entry.payload.message;
|
|
9059
|
+
if (entry.entryType === "user_message" || entry.entryType === "reminder") {
|
|
9060
|
+
return entry.payload.message;
|
|
9061
|
+
}
|
|
9062
|
+
if (entry.entryType === "assistant_message") {
|
|
9063
|
+
const message = entry.payload.message;
|
|
9064
|
+
if (message.provider !== currentProvider || message.model !== currentModel) {
|
|
9065
|
+
return portableModelMessage(message);
|
|
9066
|
+
}
|
|
9067
|
+
return message;
|
|
9194
9068
|
}
|
|
9069
|
+
if (entry.entryType === "tool_result") {
|
|
9070
|
+
return entry.payload.message;
|
|
9071
|
+
}
|
|
9072
|
+
return null;
|
|
9195
9073
|
}
|
|
9196
|
-
function
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9074
|
+
function validateToolPairs(selected) {
|
|
9075
|
+
let expected = [];
|
|
9076
|
+
let ownerEntryId = null;
|
|
9077
|
+
for (const item of selected) {
|
|
9078
|
+
if (item.message.role === "assistant") {
|
|
9079
|
+
if (expected.length > 0) {
|
|
9080
|
+
throw new ContextBuildError(ownerEntryId ?? item.entryId, "missing tool result");
|
|
9081
|
+
}
|
|
9082
|
+
expected = item.message.content.filter((block) => block.type === "tool-call").map((block) => block.call.id);
|
|
9083
|
+
ownerEntryId = expected.length === 0 ? null : item.entryId;
|
|
9084
|
+
continue;
|
|
9085
|
+
}
|
|
9086
|
+
if (item.message.role === "tool-result") {
|
|
9087
|
+
const [next, ...rest] = expected;
|
|
9088
|
+
if (next === void 0) {
|
|
9089
|
+
throw new ContextBuildError(item.entryId, "orphan tool result");
|
|
9090
|
+
}
|
|
9091
|
+
if (item.message.toolCallId !== next) {
|
|
9092
|
+
throw new ContextBuildError(item.entryId, "tool result order does not match tool call order");
|
|
9093
|
+
}
|
|
9094
|
+
expected = rest;
|
|
9095
|
+
ownerEntryId = expected.length === 0 ? null : ownerEntryId;
|
|
9096
|
+
continue;
|
|
9097
|
+
}
|
|
9098
|
+
if (expected.length > 0) {
|
|
9099
|
+
throw new ContextBuildError(ownerEntryId ?? item.entryId, "missing tool result");
|
|
9200
9100
|
}
|
|
9201
9101
|
}
|
|
9202
|
-
|
|
9102
|
+
if (expected.length > 0) {
|
|
9103
|
+
throw new ContextBuildError(ownerEntryId ?? "unknown", "missing tool result");
|
|
9104
|
+
}
|
|
9203
9105
|
}
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9106
|
+
|
|
9107
|
+
// ../../packages/session/session-context/dist/summary-prompt.js
|
|
9108
|
+
var COMPACTION_SYSTEM_PROMPT = "Summarize the conversation for continuation. Preserve user goals, explicit constraints, completed work, modified files, architectural decisions, tool outcomes, failures, current TODOs, and concrete state needed to continue. Do not include credentials. Do not invent results.";
|
|
9109
|
+
|
|
9110
|
+
// ../../packages/session/session-context/dist/compaction.js
|
|
9111
|
+
function selectCompactionPlan(input) {
|
|
9112
|
+
const boundary = contextResetBoundary(input.entries);
|
|
9113
|
+
input = { ...input, entries: input.entries.filter((entry) => entry.seq > boundary) };
|
|
9114
|
+
const units = semanticUnits(input.entries);
|
|
9115
|
+
const retained = [];
|
|
9116
|
+
let tokens = 0;
|
|
9117
|
+
for (let index = units.length - 1; index >= 0; index -= 1) {
|
|
9118
|
+
const unit = units[index];
|
|
9119
|
+
const unitTokens = input.estimator.estimateMessages(unit.messages);
|
|
9120
|
+
retained.unshift(unit);
|
|
9121
|
+
tokens += unitTokens;
|
|
9122
|
+
if (tokens >= input.retainTokens) {
|
|
9123
|
+
break;
|
|
9208
9124
|
}
|
|
9209
9125
|
}
|
|
9210
|
-
|
|
9126
|
+
if (retained.length === 0 && units.length > 0) {
|
|
9127
|
+
retained.push(units.at(-1));
|
|
9128
|
+
}
|
|
9129
|
+
const currentTurn = input.protectedFromSeq ?? [...input.entries].reverse().find((entry) => entry.entryType === "user_message")?.seq ?? 0;
|
|
9130
|
+
const hasCurrentImages = input.entries.some((entry) => entry.seq >= currentTurn && "message" in entry.payload && "attachments" in entry.payload.message && entry.payload.message.attachments?.some((block) => block.type === "image"));
|
|
9131
|
+
const retainedFirst = Math.min(retained[0]?.entries[0]?.seq ?? Number.MAX_SAFE_INTEGER, hasCurrentImages ? currentTurn : Number.MAX_SAFE_INTEGER);
|
|
9132
|
+
const retainedUnits = units.filter((unit) => unit.entries[0].seq >= retainedFirst);
|
|
9133
|
+
const summarized = units.filter((unit) => unit.entries[0].seq < retainedFirst);
|
|
9134
|
+
return {
|
|
9135
|
+
summarizedEntries: summarized.flatMap((unit) => unit.entries),
|
|
9136
|
+
retainedEntries: retainedUnits.flatMap((unit) => unit.entries),
|
|
9137
|
+
retainedFromSeq: retainedUnits[0]?.entries[0]?.seq ?? 1
|
|
9138
|
+
};
|
|
9211
9139
|
}
|
|
9212
|
-
function
|
|
9140
|
+
function serializeConversation(entries) {
|
|
9141
|
+
const lines2 = ["<conversation>"];
|
|
9213
9142
|
for (const entry of entries) {
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9143
|
+
lines2.push(`<entry id="${entry.id}" seq="${entry.seq}" type="${entry.entryType}">`);
|
|
9144
|
+
if (entry.entryType === "assistant_message") {
|
|
9145
|
+
for (const block of entry.payload.message.content) {
|
|
9146
|
+
if (block.type === "tool-call") {
|
|
9147
|
+
lines2.push(`[assistant tool-call ${block.call.id} ${block.call.name}] ${JSON.stringify(block.call.arguments)}`);
|
|
9148
|
+
} else {
|
|
9149
|
+
lines2.push(`[assistant ${block.type}] ${block.text}`);
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
} else if (entry.entryType === "tool_result") {
|
|
9153
|
+
lines2.push(`[tool-result ${entry.payload.message.toolCallId} ${entry.payload.message.toolName}] ${entry.payload.message.content}`);
|
|
9154
|
+
} else if (entry.entryType === "tool_definitions") {
|
|
9155
|
+
lines2.push(`[loaded tool definitions] ${(entry.payload.message.toolDefinitions ?? []).map((tool) => tool.spec.name).join(", ")}`);
|
|
9156
|
+
} else if (entry.entryType === "user_message" || entry.entryType === "skill_context" || entry.entryType === "reminder" || entry.entryType === "project_instructions") {
|
|
9157
|
+
lines2.push(`[${entry.payload.message.role}] ${entry.payload.message.content}`);
|
|
9158
|
+
}
|
|
9159
|
+
if ("message" in entry.payload && "attachments" in entry.payload.message) {
|
|
9160
|
+
for (const block of entry.payload.message.attachments ?? [])
|
|
9161
|
+
lines2.push(`[attachment metadata only; not visual interpretation] ${JSON.stringify(block)}`);
|
|
9162
|
+
}
|
|
9163
|
+
lines2.push("</entry>");
|
|
9164
|
+
}
|
|
9165
|
+
lines2.push("</conversation>");
|
|
9166
|
+
return lines2.join("\n");
|
|
9167
|
+
}
|
|
9168
|
+
function semanticUnits(entries) {
|
|
9169
|
+
const units = [];
|
|
9170
|
+
const groupedDefinitions = /* @__PURE__ */ new Set();
|
|
9171
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
9172
|
+
const entry = entries[index];
|
|
9173
|
+
if (groupedDefinitions.has(entry.id))
|
|
9174
|
+
continue;
|
|
9175
|
+
if (entry.entryType === "context_reset" || entry.entryType === "image_offload" || entry.entryType === "system_context" || entry.entryType === "project_instructions" || entry.entryType === "compaction") {
|
|
9176
|
+
continue;
|
|
9177
|
+
}
|
|
9178
|
+
if (entry.entryType === "assistant_message") {
|
|
9179
|
+
const toolCallIds = entry.payload.message.content.filter((block) => block.type === "tool-call").map((block) => block.call.id);
|
|
9180
|
+
if (toolCallIds.length > 0) {
|
|
9181
|
+
const grouped = [entry];
|
|
9182
|
+
for (const id of toolCallIds) {
|
|
9183
|
+
const resultIndex = entries.findIndex((candidate) => candidate.entryType === "tool_result" && candidate.payload.message.toolCallId === id);
|
|
9184
|
+
if (resultIndex < 0) {
|
|
9185
|
+
throw new Error(`cannot compact incomplete tool call: ${id}`);
|
|
9186
|
+
}
|
|
9187
|
+
grouped.push(entries[resultIndex]);
|
|
9188
|
+
}
|
|
9189
|
+
const lastResultIndex = entries.findIndex((candidate) => candidate.id === grouped.at(-1).id);
|
|
9190
|
+
const definitions = entries[lastResultIndex + 1];
|
|
9191
|
+
if (definitions?.entryType === "tool_definitions") {
|
|
9192
|
+
grouped.push(definitions);
|
|
9193
|
+
groupedDefinitions.add(definitions.id);
|
|
9194
|
+
}
|
|
9195
|
+
units.push({ entries: grouped, messages: grouped.map(messageForEntry) });
|
|
9196
|
+
continue;
|
|
9223
9197
|
}
|
|
9224
|
-
}
|
|
9225
|
-
|
|
9198
|
+
}
|
|
9199
|
+
if (entry.entryType === "tool_result") {
|
|
9200
|
+
continue;
|
|
9201
|
+
}
|
|
9202
|
+
units.push({ entries: [entry], messages: [messageForEntry(entry)] });
|
|
9226
9203
|
}
|
|
9204
|
+
return units;
|
|
9227
9205
|
}
|
|
9228
|
-
function
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9206
|
+
function messageForEntry(entry) {
|
|
9207
|
+
if (entry.entryType === "tool_definitions" || entry.entryType === "tool_catalog" || entry.entryType === "skill_context")
|
|
9208
|
+
return entry.payload.message;
|
|
9209
|
+
if (entry.entryType === "assistant_message") {
|
|
9210
|
+
return entry.payload.message;
|
|
9211
|
+
}
|
|
9212
|
+
if (entry.entryType === "tool_result") {
|
|
9213
|
+
return entry.payload.message;
|
|
9214
|
+
}
|
|
9215
|
+
if (entry.entryType === "user_message" || entry.entryType === "reminder" || entry.entryType === "project_instructions") {
|
|
9216
|
+
return entry.payload.message;
|
|
9237
9217
|
}
|
|
9218
|
+
throw new Error(`entry has no model message: ${entry.entryType}`);
|
|
9238
9219
|
}
|
|
9239
9220
|
|
|
9240
|
-
// ../../packages/session/session-
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
}
|
|
9266
|
-
}
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9221
|
+
// ../../packages/session/session-context/dist/context-governor.js
|
|
9222
|
+
var ContextBudgetError = class extends Error {
|
|
9223
|
+
constructor(message) {
|
|
9224
|
+
super(message);
|
|
9225
|
+
this.name = "ContextBudgetError";
|
|
9226
|
+
}
|
|
9227
|
+
};
|
|
9228
|
+
var ContextGovernor = class {
|
|
9229
|
+
#builder;
|
|
9230
|
+
#estimator;
|
|
9231
|
+
#summarize;
|
|
9232
|
+
#appendCompaction;
|
|
9233
|
+
constructor(options) {
|
|
9234
|
+
this.#builder = options.builder ?? new ContextBuilder();
|
|
9235
|
+
this.#estimator = options.estimator ?? new DefaultTokenEstimator();
|
|
9236
|
+
this.#summarize = options.summarize;
|
|
9237
|
+
this.#appendCompaction = options.appendCompaction;
|
|
9238
|
+
}
|
|
9239
|
+
async prepare(input) {
|
|
9240
|
+
let built = this.#builder.build(input);
|
|
9241
|
+
let tokens = this.measure(input, built);
|
|
9242
|
+
const calculated = calculateModelBudget({ budget: input.budget, policy: input.policy });
|
|
9243
|
+
if (!input.policy.auto || tokens <= calculated.autoTrigger) {
|
|
9244
|
+
if (tokens > calculated.hardInputLimit)
|
|
9245
|
+
throw new ContextBudgetError("context exceeds hard input limit");
|
|
9246
|
+
return { built, messages: built.messages, compacted: false, tokens };
|
|
9247
|
+
}
|
|
9248
|
+
const result = await this.compact({ ...input, trigger: "automatic" });
|
|
9249
|
+
const entries = [...input.entries, result.entry];
|
|
9250
|
+
built = this.#builder.build({ ...input, entries });
|
|
9251
|
+
tokens = this.measure({ ...input, entries }, built);
|
|
9252
|
+
if (tokens > calculated.hardInputLimit) {
|
|
9253
|
+
throw new ContextBudgetError("context remains over hard input limit after compaction");
|
|
9254
|
+
}
|
|
9255
|
+
return { built, messages: built.messages, compacted: true, tokens };
|
|
9256
|
+
}
|
|
9257
|
+
async compact(input) {
|
|
9258
|
+
const calculated = calculateModelBudget({ budget: input.budget, policy: input.policy });
|
|
9259
|
+
const plan = selectCompactionPlan({
|
|
9260
|
+
protectedFromSeq: input.protectedFromSeq,
|
|
9261
|
+
entries: input.entries,
|
|
9262
|
+
// A search preflight reserves the pending call/result/definition unit.
|
|
9263
|
+
// Keep the latest completed unit and summarize older units to make room.
|
|
9264
|
+
retainTokens: input.reserveTokens === void 0 ? calculated.retainTokens : 1,
|
|
9265
|
+
estimator: this.#estimator
|
|
9266
|
+
});
|
|
9267
|
+
if (input.trigger === "manual" && plan.summarizedEntries.length === 0) {
|
|
9268
|
+
throw new Error("No messages to compact in current history.");
|
|
9269
|
+
}
|
|
9270
|
+
const summarizedFromSeq = plan.summarizedEntries[0]?.seq ?? 1;
|
|
9271
|
+
const summarizedThroughSeq = plan.summarizedEntries.at(-1)?.seq ?? Math.max(0, summarizedFromSeq - 1);
|
|
9272
|
+
const built = this.#builder.build(input);
|
|
9273
|
+
const activeCompaction = input.entries.find((entry) => entry.id === built.activeCompactionId);
|
|
9274
|
+
const messagesBefore = built.messages;
|
|
9275
|
+
const tokensBefore = this.#estimator.estimateMessages(messagesBefore) + this.#estimator.estimateTools(input.projectTools?.(messagesBefore) ?? input.tools);
|
|
9276
|
+
const summary = plan.summarizedEntries.length === 0 ? {
|
|
9277
|
+
summary: "No prior conversation needed compaction.",
|
|
9278
|
+
inputTokens: 0,
|
|
9279
|
+
outputTokens: 0
|
|
9280
|
+
} : await this.#summarize({
|
|
9281
|
+
serialized: serializeConversation(plan.summarizedEntries),
|
|
9282
|
+
maxSummaryTokens: input.policy.maxSummaryTokens
|
|
9283
|
+
});
|
|
9284
|
+
const payload = {
|
|
9285
|
+
summary: summary.summary,
|
|
9286
|
+
summarizedFromSeq,
|
|
9287
|
+
summarizedThroughSeq,
|
|
9288
|
+
retainedFromSeq: plan.retainedFromSeq,
|
|
9289
|
+
...activeCompaction === void 0 ? {} : { supersedesCompactionId: activeCompaction.id },
|
|
9290
|
+
tokensBefore,
|
|
9291
|
+
retainedTokens: calculated.retainTokens,
|
|
9292
|
+
summaryInputTokens: summary.inputTokens,
|
|
9293
|
+
summaryOutputTokens: summary.outputTokens,
|
|
9294
|
+
provider: input.currentProvider,
|
|
9295
|
+
model: input.currentModel,
|
|
9296
|
+
trigger: input.trigger
|
|
9297
|
+
};
|
|
9298
|
+
return { entry: this.#appendCompaction(payload) };
|
|
9299
|
+
}
|
|
9300
|
+
async completeWithOverflowRecovery(input, send) {
|
|
9301
|
+
let prepared = await this.prepare(input);
|
|
9302
|
+
try {
|
|
9303
|
+
return await send(prepared);
|
|
9304
|
+
} catch (error) {
|
|
9305
|
+
if (!(error instanceof ModelError) || modelErrorKind(error) !== "context_overflow" || error.hadDelta) {
|
|
9306
|
+
throw error;
|
|
9280
9307
|
}
|
|
9281
|
-
items.push({
|
|
9282
|
-
kind: "tool",
|
|
9283
|
-
callId: entry.payload.message.toolCallId,
|
|
9284
|
-
name: entry.payload.message.toolName,
|
|
9285
|
-
subject: message.toolName === "skill" ? skillNames.get(message.toolCallId) ?? "" : message.toolCallId,
|
|
9286
|
-
result,
|
|
9287
|
-
isError: entry.payload.message.isError
|
|
9288
|
-
});
|
|
9289
|
-
} else if (entry.entryType === "compaction") {
|
|
9290
|
-
items.push({
|
|
9291
|
-
kind: "notice",
|
|
9292
|
-
text: "Conversation context was compacted.",
|
|
9293
|
-
tone: "info"
|
|
9294
|
-
});
|
|
9295
9308
|
}
|
|
9309
|
+
const compaction = await this.compact({ ...input, trigger: "provider_overflow" });
|
|
9310
|
+
prepared = await this.prepare({ ...input, entries: [...input.entries, compaction.entry] });
|
|
9311
|
+
return await send(prepared);
|
|
9296
9312
|
}
|
|
9297
|
-
|
|
9313
|
+
measure(input, built) {
|
|
9314
|
+
const tools = input.projectTools?.(built.messages) ?? input.tools;
|
|
9315
|
+
return this.#estimator.measure({
|
|
9316
|
+
messages: built.messages,
|
|
9317
|
+
tools,
|
|
9318
|
+
entries: input.entries,
|
|
9319
|
+
anchor: input.anchor ?? null,
|
|
9320
|
+
provider: input.currentProvider,
|
|
9321
|
+
model: input.currentModel,
|
|
9322
|
+
systemFingerprint: fingerprintContextPart(built.messages.find((message) => message.role === "system") ?? null),
|
|
9323
|
+
projectInstructionsFingerprint: fingerprintContextPart(built.messages.filter((message, index) => index > 0 && message.role === "user")),
|
|
9324
|
+
toolsFingerprint: fingerprintContextPart(tools)
|
|
9325
|
+
}).totalTokens + (input.reserveTokens ?? 0);
|
|
9326
|
+
}
|
|
9327
|
+
};
|
|
9328
|
+
function calculateModelBudget(input) {
|
|
9329
|
+
const hardInputLimit = input.budget.contextWindow - input.budget.maxOutputTokens;
|
|
9330
|
+
const safetyTokens = Math.ceil(input.budget.contextWindow * input.policy.safetyRatio);
|
|
9331
|
+
const autoTrigger = Math.min(Math.floor(input.budget.contextWindow * input.policy.thresholdRatio), hardInputLimit - safetyTokens);
|
|
9332
|
+
const retainTokens = input.policy.retainTokens ?? Math.floor(input.budget.contextWindow * input.policy.retainRatio);
|
|
9333
|
+
return { hardInputLimit, safetyTokens, autoTrigger, retainTokens };
|
|
9298
9334
|
}
|
|
9299
9335
|
|
|
9300
9336
|
// ../../packages/llm/llm-pi-ai/dist/adapter.js
|
|
@@ -23486,7 +23522,8 @@ var CONTROL_KEYS = /* @__PURE__ */ new Map([
|
|
|
23486
23522
|
[12, makeKeyInput("ctrl_l", { ctrl: true })],
|
|
23487
23523
|
[15, makeKeyInput("character", { text: "o", ctrl: true })],
|
|
23488
23524
|
[19, makeKeyInput("character", { text: "s", ctrl: true })],
|
|
23489
|
-
[20, makeKeyInput("character", { text: "t", ctrl: true })]
|
|
23525
|
+
[20, makeKeyInput("character", { text: "t", ctrl: true })],
|
|
23526
|
+
[22, makeKeyInput("character", { text: "v", ctrl: true })]
|
|
23490
23527
|
]);
|
|
23491
23528
|
var ESCAPE_INCOMPLETE = { status: "incomplete" };
|
|
23492
23529
|
var ESCAPE_CONSUMED = { status: "consumed" };
|
|
@@ -23633,6 +23670,10 @@ var RawInputDecoder = class {
|
|
|
23633
23670
|
this.buffer = this.buffer.subarray(3);
|
|
23634
23671
|
return kind !== void 0 ? { status: "action", action: inputAction(kind) } : ESCAPE_CONSUMED;
|
|
23635
23672
|
}
|
|
23673
|
+
if (second === 118) {
|
|
23674
|
+
this.buffer = this.buffer.subarray(2);
|
|
23675
|
+
return { status: "action", action: inputAction(InputActionKind.Key, "", makeKeyInput("character", { text: "v", alt: true })) };
|
|
23676
|
+
}
|
|
23636
23677
|
this.buffer = this.buffer.subarray(1);
|
|
23637
23678
|
return ESCAPE_CONSUMED;
|
|
23638
23679
|
}
|
|
@@ -23920,7 +23961,7 @@ function decodeSpecialEscapeAction(sequence) {
|
|
|
23920
23961
|
return kind !== void 0 ? inputAction(kind) : null;
|
|
23921
23962
|
}
|
|
23922
23963
|
function decodeModifiedControlKey(sequence) {
|
|
23923
|
-
const kitty = /^\x1b\[(13|57414|9|117)(?::\d*)?(?::\d+)?;(\d+)(?::(\d+))?u$/.exec(sequence);
|
|
23964
|
+
const kitty = /^\x1b\[(13|57414|9|117|118)(?::\d*)?(?::\d+)?;(\d+)(?::(\d+))?u$/.exec(sequence);
|
|
23924
23965
|
if (kitty !== null) {
|
|
23925
23966
|
const code = kitty[1];
|
|
23926
23967
|
const modifier2 = Number.parseInt(kitty[2], 10) - 1;
|
|
@@ -23930,6 +23971,9 @@ function decodeModifiedControlKey(sequence) {
|
|
|
23930
23971
|
if (!isKittyPressEvent(kitty[3])) {
|
|
23931
23972
|
return null;
|
|
23932
23973
|
}
|
|
23974
|
+
if (code === "118" && (matchesKittyModifiers(modifier2, 4) || matchesKittyModifiers(modifier2, 2))) {
|
|
23975
|
+
return inputAction(InputActionKind.Key, "", makeKeyInput("character", { text: "v", ctrl: matchesKittyModifiers(modifier2, 4), alt: matchesKittyModifiers(modifier2, 2) }));
|
|
23976
|
+
}
|
|
23933
23977
|
if ((code === "13" || code === "57414") && matchesKittyModifiers(modifier2, 2)) {
|
|
23934
23978
|
return inputAction(InputActionKind.Key, "", makeKeyInput("enter", { alt: true }));
|
|
23935
23979
|
}
|
|
@@ -23944,6 +23988,9 @@ function decodeModifiedControlKey(sequence) {
|
|
|
23944
23988
|
}
|
|
23945
23989
|
const modifier = Number.parseInt(modifyOtherKeys[1], 10) - 1;
|
|
23946
23990
|
const codepoint = Number.parseInt(modifyOtherKeys[2], 10);
|
|
23991
|
+
if (codepoint === 118 && (modifier === 4 || modifier === 2)) {
|
|
23992
|
+
return inputAction(InputActionKind.Key, "", makeKeyInput("character", { text: "v", ctrl: modifier === 4, alt: modifier === 2 }));
|
|
23993
|
+
}
|
|
23947
23994
|
if (codepoint === 117 && modifier === 4) {
|
|
23948
23995
|
return inputAction(InputActionKind.DeleteToLineStart);
|
|
23949
23996
|
}
|
|
@@ -24294,6 +24341,7 @@ var TerminalInputDecoder = class {
|
|
|
24294
24341
|
|
|
24295
24342
|
// ../../packages/terminal/tui/dist/keybindings/default-keybindings.js
|
|
24296
24343
|
var DEFAULT_KEYBINDINGS = [
|
|
24344
|
+
{ context: "editor", key: process.platform === "win32" ? "alt+v" : "ctrl+v", action: "paste_clipboard" },
|
|
24297
24345
|
{ context: "terminal", key: "ctrl+o", action: "toggle_tool_output" },
|
|
24298
24346
|
{ context: "terminal", key: "ctrl+l", action: "select_model" },
|
|
24299
24347
|
{ context: "terminal", key: "ctrl+t", action: "toggle_thinking" },
|
|
@@ -27462,6 +27510,7 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27462
27510
|
#renderTimer = null;
|
|
27463
27511
|
#lastRenderAt = 0;
|
|
27464
27512
|
#disposeResize = null;
|
|
27513
|
+
#clipboard = null;
|
|
27465
27514
|
writeError = null;
|
|
27466
27515
|
constructor(ui, driver, editor, decoderFactory) {
|
|
27467
27516
|
this.#ui = ui;
|
|
@@ -27564,6 +27613,7 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27564
27613
|
return this.#viewHost.render(context);
|
|
27565
27614
|
}
|
|
27566
27615
|
requestExit() {
|
|
27616
|
+
this.invalidateClipboardPaste();
|
|
27567
27617
|
this.#exitRequested = true;
|
|
27568
27618
|
this.#exitResolve?.();
|
|
27569
27619
|
}
|
|
@@ -27572,6 +27622,7 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27572
27622
|
return;
|
|
27573
27623
|
}
|
|
27574
27624
|
this.#closed = true;
|
|
27625
|
+
this.invalidateClipboardPaste();
|
|
27575
27626
|
if (this.#renderTimer !== null) {
|
|
27576
27627
|
clearTimeout(this.#renderTimer);
|
|
27577
27628
|
this.#renderTimer = null;
|
|
@@ -27778,14 +27829,29 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27778
27829
|
}
|
|
27779
27830
|
changed = true;
|
|
27780
27831
|
} else if (item.type === "open_selection") {
|
|
27832
|
+
this.invalidateClipboardPaste();
|
|
27781
27833
|
this.#viewHost.openSelection(item.request).then(item.resolve);
|
|
27782
27834
|
changed = true;
|
|
27783
27835
|
} else if (item.type === "open_prompt") {
|
|
27836
|
+
this.invalidateClipboardPaste();
|
|
27784
27837
|
this.#viewHost.openPrompt(item.request).then(item.resolve);
|
|
27785
27838
|
changed = true;
|
|
27786
27839
|
} else if (item.type === "close_view") {
|
|
27787
27840
|
this.#viewHost.closeActive(item.value);
|
|
27788
27841
|
changed = true;
|
|
27842
|
+
} else if (item.type === "clipboard") {
|
|
27843
|
+
if (this.#clipboard === item.controller)
|
|
27844
|
+
this.#clipboard = null;
|
|
27845
|
+
if (item.controller.signal.aborted) {
|
|
27846
|
+
this.#appendNotice("Clipboard paste cancelled because input changed; paste again.");
|
|
27847
|
+
} else if (item.error) {
|
|
27848
|
+
this.#appendNotice(item.error);
|
|
27849
|
+
} else if (item.text) {
|
|
27850
|
+
this.#applyEditorAction(inputAction(InputActionKind.Insert, item.text));
|
|
27851
|
+
} else {
|
|
27852
|
+
this.#appendNotice("Clipboard has no image or text.");
|
|
27853
|
+
}
|
|
27854
|
+
changed = true;
|
|
27789
27855
|
} else if (item.event instanceof LocalMessage) {
|
|
27790
27856
|
const message = item.event;
|
|
27791
27857
|
this.#ui.appendTranscript(createNoticeBlock(this.#ui.newBlockId(), message.text, message.tone));
|
|
@@ -27820,6 +27886,7 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27820
27886
|
#applyActions(actions) {
|
|
27821
27887
|
for (const action of actions) {
|
|
27822
27888
|
if (action.kind === InputActionKind.Newline) {
|
|
27889
|
+
this.invalidateClipboardPaste();
|
|
27823
27890
|
if (this.#viewHost.activeId() !== null) {
|
|
27824
27891
|
this.#viewHost.handleInput({ type: "text", text: "\n" });
|
|
27825
27892
|
this.#needsRender = true;
|
|
@@ -27832,6 +27899,12 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27832
27899
|
}
|
|
27833
27900
|
}
|
|
27834
27901
|
#applyInputEvent(event) {
|
|
27902
|
+
const clipboardAction = event.type === "key" && this.#ui.keybindings.resolve(event.key, ["editor"]) === "paste_clipboard";
|
|
27903
|
+
if (clipboardAction && this.#viewHost.activeId() === null) {
|
|
27904
|
+
this.#pasteClipboard();
|
|
27905
|
+
return;
|
|
27906
|
+
}
|
|
27907
|
+
this.invalidateClipboardPaste();
|
|
27835
27908
|
if (this.#viewHost.handleInput(event)) {
|
|
27836
27909
|
this.#needsRender = true;
|
|
27837
27910
|
return;
|
|
@@ -27851,6 +27924,8 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27851
27924
|
}
|
|
27852
27925
|
}
|
|
27853
27926
|
#applyKeyAction(action) {
|
|
27927
|
+
if (action === "paste_clipboard")
|
|
27928
|
+
return;
|
|
27854
27929
|
const capability = ACTION_CAPABILITIES[action];
|
|
27855
27930
|
if (capability !== void 0 && !this.#ui.capabilities[capability]) {
|
|
27856
27931
|
this.#appendNotice(unavailableActionNotice(action));
|
|
@@ -27960,6 +28035,7 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27960
28035
|
return true;
|
|
27961
28036
|
}
|
|
27962
28037
|
#applyEof() {
|
|
28038
|
+
this.invalidateClipboardPaste();
|
|
27963
28039
|
const effect = this.#editor.apply(inputAction(InputActionKind.Eof), { runtimeActive: this.#ui.isRunning() });
|
|
27964
28040
|
this.#applyEffect(effect);
|
|
27965
28041
|
this.#needsRender = true;
|
|
@@ -27982,6 +28058,27 @@ var InteractiveTerminalLoop = class _InteractiveTerminalLoop {
|
|
|
27982
28058
|
#appendNotice(text3) {
|
|
27983
28059
|
this.#ui.appendTranscript(createNoticeBlock(this.#ui.newBlockId(), text3, "warning"));
|
|
27984
28060
|
}
|
|
28061
|
+
invalidateClipboardPaste() {
|
|
28062
|
+
this.#clipboard?.abort();
|
|
28063
|
+
}
|
|
28064
|
+
#pasteClipboard() {
|
|
28065
|
+
if (this.#clipboard)
|
|
28066
|
+
return;
|
|
28067
|
+
const read = this.#ui.clipboardReader;
|
|
28068
|
+
if (!read) {
|
|
28069
|
+
this.#appendNotice("Clipboard paste is unavailable in this host.");
|
|
28070
|
+
return;
|
|
28071
|
+
}
|
|
28072
|
+
const controller = new AbortController();
|
|
28073
|
+
this.#clipboard = controller;
|
|
28074
|
+
const deliver = (text3, error) => {
|
|
28075
|
+
if (this.#closed || this.#exitRequested)
|
|
28076
|
+
return;
|
|
28077
|
+
this.#work.push({ type: "clipboard", controller, text: text3, error });
|
|
28078
|
+
this.#scheduleWakeup();
|
|
28079
|
+
};
|
|
28080
|
+
void Promise.resolve().then(() => read(controller.signal)).then((text3) => deliver(text3), (error) => deliver(null, error instanceof Error ? error.message : "Clipboard paste failed."));
|
|
28081
|
+
}
|
|
27985
28082
|
#refreshCompletions() {
|
|
27986
28083
|
const registry = this.#ui.commandRegistry;
|
|
27987
28084
|
if (registry !== null) {
|
|
@@ -28040,6 +28137,7 @@ function normalizePositiveInteger(value) {
|
|
|
28040
28137
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
28041
28138
|
}
|
|
28042
28139
|
var TerminalUI = class {
|
|
28140
|
+
clipboardReader;
|
|
28043
28141
|
theme;
|
|
28044
28142
|
state;
|
|
28045
28143
|
reducer;
|
|
@@ -28070,6 +28168,7 @@ var TerminalUI = class {
|
|
|
28070
28168
|
#pendingDisplayDrops = 0;
|
|
28071
28169
|
#keyActionCallback;
|
|
28072
28170
|
constructor(options = {}) {
|
|
28171
|
+
this.clipboardReader = options.clipboardReader;
|
|
28073
28172
|
this.theme = resolveTerminalTheme(options.theme);
|
|
28074
28173
|
this.projectRoot = options.projectRoot ?? null;
|
|
28075
28174
|
this.provider = options.provider ?? null;
|
|
@@ -28188,6 +28287,8 @@ var TerminalUI = class {
|
|
|
28188
28287
|
Object.assign(this.capabilities, capabilities);
|
|
28189
28288
|
}
|
|
28190
28289
|
setSessionId(sessionId) {
|
|
28290
|
+
if (sessionId !== this.#sessionId)
|
|
28291
|
+
this.#loop?.invalidateClipboardPaste();
|
|
28191
28292
|
if (sessionId !== this.#sessionId)
|
|
28192
28293
|
this.state.contextTokens = 0;
|
|
28193
28294
|
this.#sessionId = sessionId;
|
|
@@ -28207,6 +28308,7 @@ var TerminalUI = class {
|
|
|
28207
28308
|
this.#loop?.requestRender();
|
|
28208
28309
|
}
|
|
28209
28310
|
setComposerText(text3) {
|
|
28311
|
+
this.#loop?.invalidateClipboardPaste();
|
|
28210
28312
|
const editor = this.#loop?.editor;
|
|
28211
28313
|
if (editor === void 0) {
|
|
28212
28314
|
return;
|
|
@@ -39394,6 +39496,160 @@ async function copyText(text3, options) {
|
|
|
39394
39496
|
return terminalCopy(text3, options, failures);
|
|
39395
39497
|
}
|
|
39396
39498
|
|
|
39499
|
+
// src/clipboard-paste.ts
|
|
39500
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
39501
|
+
import { mkdtemp as mkdtemp2, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
39502
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
39503
|
+
import { join as join20 } from "node:path";
|
|
39504
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
39505
|
+
import sharp3 from "sharp";
|
|
39506
|
+
var MAX_OUTPUT = 30 * 1024 * 1024;
|
|
39507
|
+
var MAX_TEXT = 1024 * 1024;
|
|
39508
|
+
var MAC_CLIPBOARD_SCRIPT = `
|
|
39509
|
+
ObjC.import('AppKit');
|
|
39510
|
+
function run() {
|
|
39511
|
+
var pb = $.NSPasteboard.generalPasteboard;
|
|
39512
|
+
var data = pb.dataForType($.NSPasteboardTypePNG);
|
|
39513
|
+
if (!data.js) {
|
|
39514
|
+
var tiff = pb.dataForType($.NSPasteboardTypeTIFF);
|
|
39515
|
+
if (tiff.js) {
|
|
39516
|
+
var bitmap = $.NSBitmapImageRep.imageRepWithData(tiff);
|
|
39517
|
+
if (!bitmap.js) throw new Error('Cannot decode clipboard image');
|
|
39518
|
+
if (bitmap.pixelsWide * bitmap.pixelsHigh > 64000000) throw new Error('Clipboard image exceeds pixel limit');
|
|
39519
|
+
data = bitmap.representationUsingTypeProperties($.NSPNGFileType, $({}));
|
|
39520
|
+
if (!data.js) throw new Error('Cannot encode clipboard image');
|
|
39521
|
+
}
|
|
39522
|
+
}
|
|
39523
|
+
if (data.js) {
|
|
39524
|
+
if (data.length > 20971520) throw new Error('Clipboard image exceeds 20 MiB');
|
|
39525
|
+
return JSON.stringify({kind:'image', base64:ObjC.unwrap(data.base64EncodedStringWithOptions(0))});
|
|
39526
|
+
}
|
|
39527
|
+
var text = pb.stringForType($.NSPasteboardTypeString);
|
|
39528
|
+
return JSON.stringify(text.js ? {kind:'text', text:ObjC.unwrap(text)} : {kind:'empty'});
|
|
39529
|
+
}`;
|
|
39530
|
+
var WINDOWS_CLIPBOARD_SCRIPT = `
|
|
39531
|
+
$ErrorActionPreference = 'Stop'
|
|
39532
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
39533
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
39534
|
+
Add-Type -AssemblyName System.Drawing
|
|
39535
|
+
$image = [System.Windows.Forms.Clipboard]::GetImage()
|
|
39536
|
+
if ($null -ne $image) {
|
|
39537
|
+
$stream = [System.IO.MemoryStream]::new()
|
|
39538
|
+
try {
|
|
39539
|
+
if ([long]$image.Width * $image.Height -gt 64000000) { throw 'Clipboard image exceeds pixel limit' }
|
|
39540
|
+
$image.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
39541
|
+
if ($stream.Length -gt 20971520) { throw 'Clipboard image exceeds 20 MiB' }
|
|
39542
|
+
@{kind='image'; base64=[Convert]::ToBase64String($stream.ToArray())} | ConvertTo-Json -Compress
|
|
39543
|
+
} finally { $stream.Dispose(); $image.Dispose() }
|
|
39544
|
+
} elseif ([System.Windows.Forms.Clipboard]::ContainsText()) {
|
|
39545
|
+
@{kind='text'; text=[System.Windows.Forms.Clipboard]::GetText()} | ConvertTo-Json -Compress
|
|
39546
|
+
} else { @{kind='empty'} | ConvertTo-Json -Compress }
|
|
39547
|
+
`;
|
|
39548
|
+
function clipboardReadCommand(platform) {
|
|
39549
|
+
if (platform === "darwin") return { command: "/usr/bin/osascript", args: ["-l", "JavaScript", "-e", MAC_CLIPBOARD_SCRIPT] };
|
|
39550
|
+
if (platform === "win32") return { command: "powershell.exe", args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-STA", "-EncodedCommand", Buffer.from(WINDOWS_CLIPBOARD_SCRIPT, "utf16le").toString("base64")] };
|
|
39551
|
+
throw new Error("Image clipboard paste requires native macOS or Windows; use a local image path on this platform.");
|
|
39552
|
+
}
|
|
39553
|
+
function createClipboardReadRunner(env, timeoutMs = 5e3) {
|
|
39554
|
+
return (command, args, signal) => new Promise((resolve15, reject) => {
|
|
39555
|
+
signal.throwIfAborted();
|
|
39556
|
+
const child = spawn4(command, [...args], { env: { ...env }, shell: false, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
39557
|
+
const chunks = [];
|
|
39558
|
+
let bytes = 0;
|
|
39559
|
+
let settled = false;
|
|
39560
|
+
const finish = (error) => {
|
|
39561
|
+
if (settled) return;
|
|
39562
|
+
settled = true;
|
|
39563
|
+
clearTimeout(timer);
|
|
39564
|
+
signal.removeEventListener("abort", abort);
|
|
39565
|
+
if (error) {
|
|
39566
|
+
child.kill("SIGKILL");
|
|
39567
|
+
reject(error);
|
|
39568
|
+
} else resolve15(Buffer.concat(chunks).toString("utf8"));
|
|
39569
|
+
};
|
|
39570
|
+
const abort = () => finish(new Error("Clipboard paste cancelled."));
|
|
39571
|
+
const timer = setTimeout(() => finish(new Error("Clipboard read timed out.")), timeoutMs);
|
|
39572
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
39573
|
+
child.stdout.on("data", (chunk) => {
|
|
39574
|
+
bytes += chunk.length;
|
|
39575
|
+
if (bytes > MAX_OUTPUT) finish(new Error("Clipboard content exceeds the size limit."));
|
|
39576
|
+
else if (!settled) chunks.push(chunk);
|
|
39577
|
+
});
|
|
39578
|
+
child.stderr.resume();
|
|
39579
|
+
child.once("error", () => finish(new Error("Cannot start the system clipboard reader.")));
|
|
39580
|
+
child.once("close", (code) => finish(code === 0 ? void 0 : new Error("System clipboard read failed; check clipboard access and try again.")));
|
|
39581
|
+
if (signal.aborted) abort();
|
|
39582
|
+
});
|
|
39583
|
+
}
|
|
39584
|
+
function createClipboardPaste(options) {
|
|
39585
|
+
const run = options.run ?? createClipboardReadRunner(options.env);
|
|
39586
|
+
let directory;
|
|
39587
|
+
let closed = false;
|
|
39588
|
+
let pending;
|
|
39589
|
+
const read = async (signal) => {
|
|
39590
|
+
signal.throwIfAborted();
|
|
39591
|
+
if (closed) throw new Error("Clipboard reader is closed.");
|
|
39592
|
+
if (options.env.SSH_CONNECTION || options.env.SSH_TTY) throw new Error("Remote clipboard reading is unavailable over SSH; use a remote image path.");
|
|
39593
|
+
const command = clipboardReadCommand(options.platform);
|
|
39594
|
+
const raw = await run(command.command, command.args, signal);
|
|
39595
|
+
signal.throwIfAborted();
|
|
39596
|
+
let value;
|
|
39597
|
+
try {
|
|
39598
|
+
value = JSON.parse(raw.replace(/^\uFEFF/, ""));
|
|
39599
|
+
} catch {
|
|
39600
|
+
throw new Error("Invalid response from clipboard reader.");
|
|
39601
|
+
}
|
|
39602
|
+
if (!value || typeof value !== "object" || !("kind" in value)) throw new Error("Invalid clipboard content.");
|
|
39603
|
+
if (value.kind === "empty") return null;
|
|
39604
|
+
if (value.kind === "text" && "text" in value && typeof value.text === "string") {
|
|
39605
|
+
if (Buffer.byteLength(value.text) > MAX_TEXT) throw new Error("Clipboard text exceeds 1 MiB.");
|
|
39606
|
+
return value.text.replace(/\r\n?/g, "\n").replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
|
|
39607
|
+
}
|
|
39608
|
+
if (value.kind !== "image" || !("base64" in value) || typeof value.base64 !== "string" || value.base64.length > Math.ceil(DEFAULT_IMAGE_LIMITS.maxBytes / 3) * 4 || value.base64.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(value.base64)) {
|
|
39609
|
+
throw new Error("Invalid or oversized clipboard image.");
|
|
39610
|
+
}
|
|
39611
|
+
const data = Buffer.from(value.base64, "base64");
|
|
39612
|
+
if (data.toString("base64") !== value.base64) throw new Error("Invalid clipboard image encoding.");
|
|
39613
|
+
if (!data.length || data.length > DEFAULT_IMAGE_LIMITS.maxBytes) throw new Error("Clipboard image exceeds size limits.");
|
|
39614
|
+
const image = sharp3(data, { limitInputPixels: DEFAULT_IMAGE_LIMITS.maxPixels });
|
|
39615
|
+
try {
|
|
39616
|
+
const meta = await image.metadata();
|
|
39617
|
+
if (meta.format !== "png" || !meta.width || !meta.height || Math.max(meta.width, meta.height) > DEFAULT_IMAGE_LIMITS.maxDimension) {
|
|
39618
|
+
throw new Error("Invalid image");
|
|
39619
|
+
}
|
|
39620
|
+
await image.stats();
|
|
39621
|
+
} catch {
|
|
39622
|
+
throw new Error("Clipboard image is invalid or exceeds image limits.");
|
|
39623
|
+
}
|
|
39624
|
+
signal.throwIfAborted();
|
|
39625
|
+
directory ??= await mkdtemp2(join20(options.tempRoot ?? tmpdir2(), "laohuang-clipboard-"));
|
|
39626
|
+
const path5 = join20(directory, `${randomUUID18()}.png`);
|
|
39627
|
+
try {
|
|
39628
|
+
await writeFile5(path5, data, { flag: "wx", mode: 384 });
|
|
39629
|
+
signal.throwIfAborted();
|
|
39630
|
+
return ` ${JSON.stringify(path5)} `;
|
|
39631
|
+
} catch (error) {
|
|
39632
|
+
await rm2(path5, { force: true });
|
|
39633
|
+
throw error;
|
|
39634
|
+
}
|
|
39635
|
+
};
|
|
39636
|
+
return {
|
|
39637
|
+
read(signal) {
|
|
39638
|
+
if (pending) return Promise.reject(new Error("Clipboard paste already in progress."));
|
|
39639
|
+
pending = read(signal).finally(() => {
|
|
39640
|
+
pending = void 0;
|
|
39641
|
+
});
|
|
39642
|
+
return pending;
|
|
39643
|
+
},
|
|
39644
|
+
async close() {
|
|
39645
|
+
closed = true;
|
|
39646
|
+
await pending?.catch(() => {
|
|
39647
|
+
});
|
|
39648
|
+
if (directory) await rm2(directory, { recursive: true, force: true });
|
|
39649
|
+
}
|
|
39650
|
+
};
|
|
39651
|
+
}
|
|
39652
|
+
|
|
39397
39653
|
// src/main.ts
|
|
39398
39654
|
var VERSION = readPackageVersion();
|
|
39399
39655
|
function readPackageVersion() {
|
|
@@ -39499,13 +39755,13 @@ async function main(argv, options = {}) {
|
|
|
39499
39755
|
const configPath = options.configPath ?? defaultConfigPath(environ);
|
|
39500
39756
|
const manager = new ConfigManager(configPath);
|
|
39501
39757
|
const credentials = new CredentialStore(
|
|
39502
|
-
options.credentialsPath ??
|
|
39758
|
+
options.credentialsPath ?? join21(dirname13(configPath), "credentials.json")
|
|
39503
39759
|
);
|
|
39504
39760
|
const modelCatalogStore = new ModelCatalogStore(
|
|
39505
|
-
options.modelsPath ??
|
|
39761
|
+
options.modelsPath ?? join21(dirname13(configPath), "models.json")
|
|
39506
39762
|
);
|
|
39507
39763
|
const customModels = new CustomModelsStore(
|
|
39508
|
-
options.customModelsPath ??
|
|
39764
|
+
options.customModelsPath ?? join21(dirname13(configPath), "custom-models.json")
|
|
39509
39765
|
);
|
|
39510
39766
|
let modelPlatform;
|
|
39511
39767
|
let attachmentRuntime;
|
|
@@ -39683,10 +39939,11 @@ async function main(argv, options = {}) {
|
|
|
39683
39939
|
return 2;
|
|
39684
39940
|
}
|
|
39685
39941
|
let terminalUi = null;
|
|
39942
|
+
const clipboardPaste = createClipboardPaste({ platform: process.platform, env: environ });
|
|
39686
39943
|
let terminalDriver = null;
|
|
39687
39944
|
let selectedModel = modelPlatform.catalog.getModel(config.provider, config.model);
|
|
39688
39945
|
attachmentRuntime = createAttachmentRuntime({
|
|
39689
|
-
root:
|
|
39946
|
+
root: join21(getHomeDirectory({ env: environ }), ".laohuang", "attachments"),
|
|
39690
39947
|
sessionsRoot: defaultSessionsRoot(environ),
|
|
39691
39948
|
onError: (error) => writeStderr(`Attachment cleanup failed: ${errorMessage11(error)}`)
|
|
39692
39949
|
});
|
|
@@ -39728,6 +39985,7 @@ async function main(argv, options = {}) {
|
|
|
39728
39985
|
version: VERSION,
|
|
39729
39986
|
theme: args.theme,
|
|
39730
39987
|
driver: terminalDriver,
|
|
39988
|
+
clipboardReader: (signal) => clipboardPaste.read(signal),
|
|
39731
39989
|
capabilities: { reasoning: selectedModel?.reasoning ?? false },
|
|
39732
39990
|
contextWindow: selectedModel?.contextWindow
|
|
39733
39991
|
});
|
|
@@ -39999,7 +40257,11 @@ Submit an empty line to retry, or enter a replacement.`);
|
|
|
39999
40257
|
if (composedForCleanup) await composedForCleanup.close();
|
|
40000
40258
|
else await sessionController.close();
|
|
40001
40259
|
} finally {
|
|
40002
|
-
|
|
40260
|
+
try {
|
|
40261
|
+
await clipboardPaste.close();
|
|
40262
|
+
} finally {
|
|
40263
|
+
attachmentRuntime.close();
|
|
40264
|
+
}
|
|
40003
40265
|
}
|
|
40004
40266
|
}
|
|
40005
40267
|
}
|
|
@@ -40007,7 +40269,7 @@ Submit an empty line to retry, or enter a replacement.`);
|
|
|
40007
40269
|
}
|
|
40008
40270
|
}
|
|
40009
40271
|
function defaultSessionsRoot(environ) {
|
|
40010
|
-
return
|
|
40272
|
+
return join21(getHomeDirectory({ env: environ }), ".laohuang", "sessions");
|
|
40011
40273
|
}
|
|
40012
40274
|
async function runInitialModelSelection(options) {
|
|
40013
40275
|
let providerName = options.providerName;
|