laohuang 0.9.2 → 0.9.3
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 +1732 -1696
- 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
7385
|
import { dirname as dirname13, join as join20 } 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
|
-
toolsFingerprint: fingerprintContextPart(tools)
|
|
8299
|
-
}).totalTokens + (input.reserveTokens ?? 0);
|
|
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
|
+
};
|
|
8300
7981
|
}
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
return { hardInputLimit, safetyTokens, autoTrigger, retainTokens };
|
|
8308
|
-
}
|
|
8309
|
-
|
|
8310
|
-
// ../../packages/attachment/attachment/dist/errors.js
|
|
8311
|
-
var AttachmentError = class extends Error {
|
|
8312
|
-
code;
|
|
8313
|
-
constructor(code, message, options) {
|
|
8314
|
-
super(message, options);
|
|
8315
|
-
this.name = "AttachmentError";
|
|
8316
|
-
this.code = code;
|
|
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;
|
|
8317
7988
|
}
|
|
8318
7989
|
};
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
if (!value || typeof value !== "object")
|
|
8332
|
-
throw new AttachmentError("INVALID_REFERENCE", "Invalid attachment content");
|
|
8333
|
-
const block = value;
|
|
8334
|
-
if (block.type !== "image" && block.type !== "file")
|
|
8335
|
-
throw new AttachmentError("INVALID_REFERENCE", "Invalid attachment kind");
|
|
8336
|
-
assertFileRef(block.ref);
|
|
8337
|
-
if (block.type === "image") {
|
|
8338
|
-
const ref = block.ref;
|
|
8339
|
-
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") {
|
|
8340
|
-
throw new AttachmentError("INVALID_REFERENCE", "Invalid image reference");
|
|
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 });
|
|
8341
8002
|
}
|
|
8342
|
-
|
|
8343
|
-
|
|
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;
|
|
8344
8014
|
}
|
|
8345
8015
|
}
|
|
8346
|
-
function
|
|
8347
|
-
const
|
|
8348
|
-
|
|
8349
|
-
|
|
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}`);
|
|
8350
8050
|
}
|
|
8351
8051
|
}
|
|
8352
|
-
function
|
|
8353
|
-
if (!
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
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");
|
|
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;
|
|
8362
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`);
|
|
8363
8080
|
}
|
|
8081
|
+
return value;
|
|
8364
8082
|
}
|
|
8365
8083
|
|
|
8366
|
-
// ../../packages/
|
|
8367
|
-
|
|
8368
|
-
};
|
|
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";
|
|
8369
8087
|
|
|
8370
|
-
// ../../packages/session/session-store/dist/session-
|
|
8371
|
-
function
|
|
8372
|
-
|
|
8373
|
-
|
|
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: [] });
|
|
8374
8093
|
}
|
|
8375
|
-
const
|
|
8376
|
-
|
|
8377
|
-
|
|
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
|
+
}
|
|
8378
8104
|
}
|
|
8379
|
-
|
|
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);
|
|
8380
8110
|
}
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8111
|
+
|
|
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: []
|
|
8391
8143
|
}
|
|
8392
|
-
|
|
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}`);
|
|
8393
8150
|
}
|
|
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 };
|
|
8394
8159
|
}
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
// ../../packages/session/session-store/dist/schema.js
|
|
8399
|
-
var ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
8400
|
-
"image_offload",
|
|
8401
|
-
"skill_context",
|
|
8402
|
-
"tool_definitions",
|
|
8403
|
-
"tool_catalog",
|
|
8404
|
-
"system_context",
|
|
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";
|
|
8160
|
+
continueLatest(projectRoot) {
|
|
8161
|
+
const latest = this.list(projectRoot).find((item) => item.status !== "corrupt");
|
|
8162
|
+
return latest === void 0 ? null : this.open(latest.sessionId);
|
|
8448
8163
|
}
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
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
|
+
});
|
|
8454
8170
|
}
|
|
8455
|
-
|
|
8456
|
-
|
|
8171
|
+
tree(projectRoot) {
|
|
8172
|
+
return buildSessionTree(this.list(projectRoot).filter((item) => item.status !== "corrupt"));
|
|
8457
8173
|
}
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
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
|
+
};
|
|
8468
8207
|
}
|
|
8469
|
-
|
|
8470
|
-
|
|
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 };
|
|
8471
8232
|
}
|
|
8472
|
-
|
|
8473
|
-
|
|
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);
|
|
8474
8239
|
}
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
if (forkedFrom["seq"] !== void 0) {
|
|
8479
|
-
requirePositiveInteger(forkedFrom, "seq");
|
|
8240
|
+
sessionPaths() {
|
|
8241
|
+
if (!existsSync2(this.#sessionsRoot)) {
|
|
8242
|
+
return [];
|
|
8480
8243
|
}
|
|
8481
|
-
|
|
8482
|
-
|
|
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
|
+
};
|
|
8294
|
+
}
|
|
8295
|
+
}
|
|
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;
|
|
8483
8300
|
}
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8301
|
+
}
|
|
8302
|
+
return replay.header.initialCwd;
|
|
8303
|
+
}
|
|
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;
|
|
8308
|
+
}
|
|
8309
|
+
}
|
|
8310
|
+
return "";
|
|
8311
|
+
}
|
|
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);
|
|
8326
|
+
}
|
|
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
|
+
});
|
|
8337
|
+
}
|
|
8338
|
+
}
|
|
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
|
+
}
|
|
8358
|
+
}
|
|
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
|
+
});
|
|
8487
8397
|
}
|
|
8488
8398
|
}
|
|
8489
|
-
return
|
|
8399
|
+
return items;
|
|
8490
8400
|
}
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8401
|
+
|
|
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);
|
|
8499
8409
|
}
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
const kind = stringValue(input, "kind");
|
|
8504
|
-
if (kind === "entry") {
|
|
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");
|
|
8512
|
-
}
|
|
8513
|
-
if (entryPayload.message !== void 0) {
|
|
8514
|
-
const message = objectOf(entryPayload.message, "message");
|
|
8515
|
-
if (message.attachments !== void 0) {
|
|
8516
|
-
if (message.role !== "user" && message.role !== "tool-result" || !Array.isArray(message.attachments))
|
|
8517
|
-
throw new SessionSchemaError("invalid message attachments");
|
|
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");
|
|
8522
|
-
}
|
|
8523
|
-
}
|
|
8524
|
-
if (entryType === "skill_context") {
|
|
8525
|
-
const payload = objectOf(input["payload"], "skill payload");
|
|
8526
|
-
const message = objectOf(payload["message"], "skill message");
|
|
8527
|
-
const metadata2 = objectOf(message["skillContext"], "skill metadata");
|
|
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");
|
|
8410
|
+
estimateTools(tools) {
|
|
8411
|
+
if (tools.length === 0) {
|
|
8412
|
+
return 0;
|
|
8530
8413
|
}
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
if (spec2["catalog"] !== void 0) {
|
|
8548
|
-
const catalog = objectOf(spec2["catalog"], "tool catalog metadata");
|
|
8549
|
-
for (const key of ["source", "originalName", "binding"])
|
|
8550
|
-
requireString(catalog, key);
|
|
8551
|
-
if (catalog["exposure"] !== "direct" && catalog["exposure"] !== "deferred")
|
|
8552
|
-
throw new SessionSchemaError("invalid exposure");
|
|
8553
|
-
}
|
|
8554
|
-
}
|
|
8555
|
-
} else {
|
|
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
|
-
}
|
|
8414
|
+
return this.estimateText(canonicalJson(tools.map(({ name, description, parameters }) => ({ name, description, parameters })))) + BLOCK_OVERHEAD;
|
|
8415
|
+
}
|
|
8416
|
+
estimateText(text3) {
|
|
8417
|
+
return Math.ceil(Buffer.byteLength(text3, "utf8") / TEXT_BYTES_PER_TOKEN);
|
|
8418
|
+
}
|
|
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
|
+
};
|
|
8565
8430
|
}
|
|
8566
|
-
|
|
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
|
+
};
|
|
8567
8438
|
}
|
|
8568
|
-
|
|
8569
|
-
const
|
|
8570
|
-
if (
|
|
8571
|
-
|
|
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;
|
|
8572
8445
|
}
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
const title = payload["title"];
|
|
8576
|
-
if (typeof title !== "string") {
|
|
8577
|
-
throw new SessionSchemaError("invalid session name record");
|
|
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");
|
|
8588
|
-
}
|
|
8446
|
+
if (message.role === "tool-result") {
|
|
8447
|
+
return MESSAGE_OVERHEAD + BLOCK_OVERHEAD + this.estimateText(message.content) + attachmentTokens;
|
|
8589
8448
|
}
|
|
8590
|
-
return
|
|
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);
|
|
8591
8455
|
}
|
|
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`);
|
|
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);
|
|
8597
8458
|
}
|
|
8598
|
-
|
|
8459
|
+
};
|
|
8460
|
+
function normalizeProviderUsage(usage) {
|
|
8461
|
+
return usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
|
|
8599
8462
|
}
|
|
8600
|
-
function
|
|
8601
|
-
|
|
8602
|
-
if (typeof value !== "string" || value === "") {
|
|
8603
|
-
throw new SessionSchemaError(`${key} must be a non-empty string`);
|
|
8604
|
-
}
|
|
8605
|
-
return value;
|
|
8463
|
+
function fingerprintContextPart(value) {
|
|
8464
|
+
return createHash2("sha256").update(canonicalJson(value)).digest("hex");
|
|
8606
8465
|
}
|
|
8607
|
-
function
|
|
8608
|
-
|
|
8609
|
-
return value;
|
|
8466
|
+
function canonicalJson(value) {
|
|
8467
|
+
return JSON.stringify(sortStable(value));
|
|
8610
8468
|
}
|
|
8611
|
-
function
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8469
|
+
function sortStable(value) {
|
|
8470
|
+
if (Array.isArray(value)) {
|
|
8471
|
+
return value.map(sortStable);
|
|
8472
|
+
}
|
|
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;
|
|
8615
8480
|
}
|
|
8616
8481
|
return value;
|
|
8617
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];
|
|
8489
|
+
}
|
|
8490
|
+
if (entry.entryType === "project_instructions" || entry.entryType === "user_message" || entry.entryType === "reminder") {
|
|
8491
|
+
return [entry.payload.message];
|
|
8492
|
+
}
|
|
8493
|
+
if (entry.entryType === "assistant_message") {
|
|
8494
|
+
return [entry.payload.message];
|
|
8495
|
+
}
|
|
8496
|
+
if (entry.entryType === "tool_result") {
|
|
8497
|
+
return [entry.payload.message];
|
|
8498
|
+
}
|
|
8499
|
+
return [];
|
|
8500
|
+
});
|
|
8501
|
+
}
|
|
8618
8502
|
|
|
8619
|
-
// ../../packages/session/session-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
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;
|
|
8633
|
-
}
|
|
8634
|
-
};
|
|
8635
|
-
function readSessionFile(path5) {
|
|
8636
|
-
const text3 = readFileSync(path5, "utf8");
|
|
8637
|
-
const endsWithNewline = text3.endsWith("\n");
|
|
8638
|
-
const rawLines = text3.split("\n");
|
|
8639
|
-
if (endsWithNewline) {
|
|
8640
|
-
rawLines.pop();
|
|
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);
|
|
8641
8509
|
}
|
|
8642
|
-
|
|
8643
|
-
|
|
8510
|
+
get tokens() {
|
|
8511
|
+
return this.#tokens;
|
|
8644
8512
|
}
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
const lineNumber = index + 1;
|
|
8651
|
-
const line2 = rawLines[index];
|
|
8652
|
-
if (line2.trim() === "") {
|
|
8653
|
-
continue;
|
|
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;
|
|
8654
8518
|
}
|
|
8655
|
-
|
|
8656
|
-
const
|
|
8657
|
-
if (
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
previousSeq = item.seq;
|
|
8661
|
-
items.push(item);
|
|
8662
|
-
} catch (error) {
|
|
8663
|
-
if (!endsWithNewline && index === rawLines.length - 1) {
|
|
8664
|
-
ignoredTornTail = true;
|
|
8665
|
-
break;
|
|
8519
|
+
if (entry.entryType === "assistant_message") {
|
|
8520
|
+
const usage = validUsageTokens(entry.payload.usage);
|
|
8521
|
+
if (usage !== null) {
|
|
8522
|
+
this.#tokens = usage;
|
|
8523
|
+
return;
|
|
8666
8524
|
}
|
|
8667
|
-
|
|
8525
|
+
}
|
|
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);
|
|
8668
8530
|
}
|
|
8669
8531
|
}
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
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;
|
|
8677
8546
|
}
|
|
8678
|
-
function
|
|
8679
|
-
|
|
8680
|
-
return
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
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);
|
|
8684
8559
|
}
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
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);
|
|
8688
8572
|
}
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
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
|
|
8703
8597
|
}
|
|
8704
|
-
}
|
|
8598
|
+
});
|
|
8705
8599
|
}
|
|
8706
|
-
|
|
8707
|
-
|
|
8600
|
+
return history;
|
|
8601
|
+
}
|
|
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;
|
|
8708
8611
|
}
|
|
8612
|
+
return this.append({
|
|
8613
|
+
entryType: "project_instructions",
|
|
8614
|
+
payload: {
|
|
8615
|
+
message: input.message,
|
|
8616
|
+
files: input.files,
|
|
8617
|
+
supersedesEntryIds: input.supersedesEntryIds ?? []
|
|
8618
|
+
}
|
|
8619
|
+
});
|
|
8709
8620
|
}
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
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}`);
|
|
8727
8640
|
}
|
|
8641
|
+
this.#staged.delete(token);
|
|
8642
|
+
return this.appendUser(staged);
|
|
8728
8643
|
}
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
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);
|
|
8644
|
+
discardStagedUser(token) {
|
|
8645
|
+
this.#staged.delete(token);
|
|
8646
|
+
}
|
|
8647
|
+
appendAssistant(input) {
|
|
8648
|
+
return this.append({
|
|
8649
|
+
entryType: "assistant_message",
|
|
8650
|
+
payload: input
|
|
8651
|
+
});
|
|
8652
|
+
}
|
|
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
|
|
8748
8660
|
}
|
|
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);
|
|
8661
|
+
}));
|
|
8764
8662
|
}
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
}
|
|
8771
|
-
function ensureSessionDirectory(sessionsRoot, projectKey) {
|
|
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";
|
|
8663
|
+
appendReminder(input) {
|
|
8664
|
+
return this.append({
|
|
8665
|
+
entryType: "reminder",
|
|
8666
|
+
payload: input
|
|
8667
|
+
});
|
|
8792
8668
|
}
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
attachments;
|
|
8796
|
-
header;
|
|
8797
|
-
path;
|
|
8798
|
-
#fd;
|
|
8799
|
-
#lockPath;
|
|
8800
|
-
#nextSeq;
|
|
8801
|
-
#closed = false;
|
|
8802
|
-
constructor(input) {
|
|
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`;
|
|
8669
|
+
appendToolDefinitions(input) {
|
|
8670
|
+
return this.append({ entryType: "tool_definitions", payload: input });
|
|
8809
8671
|
}
|
|
8810
|
-
|
|
8811
|
-
return this
|
|
8672
|
+
appendToolCatalog(input) {
|
|
8673
|
+
return this.append({ entryType: "tool_catalog", payload: input });
|
|
8812
8674
|
}
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
const entry = {
|
|
8816
|
-
...item,
|
|
8817
|
-
kind: "entry",
|
|
8818
|
-
entryType: input.entryType,
|
|
8819
|
-
payload: input.payload
|
|
8820
|
-
};
|
|
8821
|
-
const message = "message" in entry.payload ? entry.payload.message : void 0;
|
|
8822
|
-
const attachments = message && "attachments" in message ? message.attachments : void 0;
|
|
8823
|
-
if (attachments?.length) {
|
|
8824
|
-
for (const attachment of attachments)
|
|
8825
|
-
assertAttachmentContent(attachment);
|
|
8826
|
-
if (!this.attachments)
|
|
8827
|
-
throw new Error("Attachment persistence requires an attachment store");
|
|
8828
|
-
if (!("attachmentKey" in message) || !message.attachmentKey)
|
|
8829
|
-
throw new Error("Attachment message requires an occurrence key");
|
|
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;
|
|
8675
|
+
appendSkillContext(input) {
|
|
8676
|
+
return this.append({ entryType: "skill_context", payload: input });
|
|
8840
8677
|
}
|
|
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();
|
|
8851
|
-
}
|
|
8852
|
-
return record;
|
|
8678
|
+
appendCompaction(input) {
|
|
8679
|
+
return this.append({
|
|
8680
|
+
entryType: "compaction",
|
|
8681
|
+
payload: input
|
|
8682
|
+
});
|
|
8853
8683
|
}
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
fsyncSync(this.#fd);
|
|
8857
|
-
}
|
|
8684
|
+
entries() {
|
|
8685
|
+
return [...this.#entries];
|
|
8858
8686
|
}
|
|
8859
|
-
|
|
8860
|
-
if (
|
|
8687
|
+
offloadImages(keys2) {
|
|
8688
|
+
if (keys2.length === 0)
|
|
8861
8689
|
return;
|
|
8862
|
-
}
|
|
8863
|
-
this.flush();
|
|
8864
|
-
closeSync(this.#fd);
|
|
8865
|
-
this.#closed = true;
|
|
8866
|
-
if (existsSync(this.#lockPath)) {
|
|
8867
|
-
unlinkSync(this.#lockPath);
|
|
8868
|
-
}
|
|
8690
|
+
this.append({ entryType: "image_offload", payload: { keys: [...keys2] } });
|
|
8691
|
+
this.#journal.flush();
|
|
8869
8692
|
}
|
|
8870
|
-
|
|
8871
|
-
return
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
...input.taskId === void 0 ? {} : { taskId: input.taskId },
|
|
8878
|
-
...input.turnId === void 0 ? {} : { turnId: input.turnId },
|
|
8879
|
-
...input.copiedFrom === void 0 ? {} : { copiedFrom: input.copiedFrom }
|
|
8693
|
+
get contextTokens() {
|
|
8694
|
+
return this.#contextUsage.tokens;
|
|
8695
|
+
}
|
|
8696
|
+
onChange(listener) {
|
|
8697
|
+
this.#listeners.add(listener);
|
|
8698
|
+
return () => {
|
|
8699
|
+
this.#listeners.delete(listener);
|
|
8880
8700
|
};
|
|
8881
8701
|
}
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
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;
|
|
8888
8709
|
}
|
|
8889
8710
|
};
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
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;
|
|
8720
|
+
}
|
|
8721
|
+
};
|
|
8722
|
+
var ModelStreamCancelled = class extends ModelStreamError {
|
|
8723
|
+
constructor(message, options = {}) {
|
|
8724
|
+
super(message, options);
|
|
8725
|
+
this.name = "ModelStreamCancelled";
|
|
8726
|
+
}
|
|
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;
|
|
8744
|
+
}
|
|
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;
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
for (let index = requestedIndex - 1; index >= 0; index -= 1) {
|
|
8753
|
+
const candidate = REASONING_EFFORTS2[index];
|
|
8754
|
+
if (available.includes(candidate)) {
|
|
8755
|
+
return candidate;
|
|
8902
8756
|
}
|
|
8903
|
-
const replay = readSessionFile(path5);
|
|
8904
|
-
return new FileSessionJournal({
|
|
8905
|
-
header: replay.header,
|
|
8906
|
-
path: path5,
|
|
8907
|
-
nextSeq: replay.lastSeq + 1,
|
|
8908
|
-
fd,
|
|
8909
|
-
attachments: options.attachments
|
|
8910
|
-
});
|
|
8911
|
-
} catch (error) {
|
|
8912
|
-
releaseLock(lockPath);
|
|
8913
|
-
throw error;
|
|
8914
8757
|
}
|
|
8758
|
+
return "off";
|
|
8915
8759
|
}
|
|
8916
|
-
function
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
createdAt,
|
|
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 }
|
|
8934
|
-
};
|
|
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 };
|
|
8935
8767
|
}
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
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"];
|
|
8784
|
+
}
|
|
8785
|
+
}
|
|
8786
|
+
if (status === null || status === void 0) {
|
|
8787
|
+
status = record["status"];
|
|
8788
|
+
}
|
|
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";
|
|
8800
|
+
}
|
|
8801
|
+
if (status === 429 || record["name"] === "RateLimitError") {
|
|
8802
|
+
return "rate_limited";
|
|
8803
|
+
}
|
|
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";
|
|
8948
8810
|
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
}
|
|
8952
|
-
function isLiveLock(path5) {
|
|
8953
|
-
if (!existsSync(path5)) {
|
|
8954
|
-
return false;
|
|
8955
|
-
}
|
|
8956
|
-
try {
|
|
8957
|
-
const parsed = JSON.parse(readFileSync2(path5, "utf8"));
|
|
8958
|
-
if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid)) {
|
|
8959
|
-
return false;
|
|
8811
|
+
if (typeof status === "number" && status >= 500) {
|
|
8812
|
+
return "server";
|
|
8960
8813
|
}
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
return code === "EPERM";
|
|
8966
|
-
}
|
|
8967
|
-
}
|
|
8968
|
-
function releaseLock(path5) {
|
|
8969
|
-
if (existsSync(path5)) {
|
|
8970
|
-
unlinkSync(path5);
|
|
8814
|
+
if (record["name"] === "APIConnectionError" || NETWORK_PATTERN.test(message)) {
|
|
8815
|
+
return "retryable";
|
|
8816
|
+
}
|
|
8817
|
+
current = record["cause"];
|
|
8971
8818
|
}
|
|
8819
|
+
return "retryable";
|
|
8972
8820
|
}
|
|
8973
|
-
function
|
|
8974
|
-
|
|
8975
|
-
`);
|
|
8821
|
+
function modelErrorKind(error) {
|
|
8822
|
+
return error instanceof ModelError ? error.kind : classifyModelError(error);
|
|
8976
8823
|
}
|
|
8977
|
-
function
|
|
8978
|
-
if (
|
|
8979
|
-
|
|
8824
|
+
function portableModelMessage(message) {
|
|
8825
|
+
if (message.role !== "assistant") {
|
|
8826
|
+
return message;
|
|
8980
8827
|
}
|
|
8981
|
-
return
|
|
8828
|
+
return {
|
|
8829
|
+
role: "assistant",
|
|
8830
|
+
provider: message.provider,
|
|
8831
|
+
model: message.model,
|
|
8832
|
+
content: message.content
|
|
8833
|
+
};
|
|
8982
8834
|
}
|
|
8983
8835
|
|
|
8984
|
-
// ../../packages/
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
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;
|
|
8993
8845
|
}
|
|
8994
|
-
|
|
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);
|
|
8846
|
+
async complete(request) {
|
|
8847
|
+
if (request.cancelToken?.isCancelled()) {
|
|
8848
|
+
throw new ModelStreamCancelled(request.cancelToken.reason || "cancelled");
|
|
9003
8849
|
}
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
}
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
header: journal.header,
|
|
9039
|
-
items: [],
|
|
9040
|
-
lastSeq: 0,
|
|
9041
|
-
ignoredTornTail: false,
|
|
9042
|
-
openToolCalls: []
|
|
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);
|
|
9043
8884
|
}
|
|
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
8885
|
}
|
|
9051
|
-
|
|
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 };
|
|
9059
|
-
}
|
|
9060
|
-
continueLatest(projectRoot) {
|
|
9061
|
-
const latest = this.list(projectRoot).find((item) => item.status !== "corrupt");
|
|
9062
|
-
return latest === void 0 ? null : this.open(latest.sessionId);
|
|
8886
|
+
throw new ModelError("model retry loop exhausted", { kind: "protocol" });
|
|
9063
8887
|
}
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
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
|
|
9069
8900
|
});
|
|
8901
|
+
} else {
|
|
8902
|
+
handler?.("model_response_validating", {});
|
|
9070
8903
|
}
|
|
9071
|
-
|
|
9072
|
-
|
|
8904
|
+
}
|
|
8905
|
+
function normalizeRuntimeError(error, cancelToken) {
|
|
8906
|
+
if (error instanceof ModelStreamCancelled) {
|
|
8907
|
+
return error;
|
|
9073
8908
|
}
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
const target = parent.items.find((item) => item.kind === "entry" && item.entryType === "user_message" && item.id === options.entryId);
|
|
9077
|
-
if (target === void 0) {
|
|
9078
|
-
throw new Error("fork target must be a user_message entry");
|
|
9079
|
-
}
|
|
9080
|
-
const child = createSessionJournal({
|
|
9081
|
-
attachments: this.#attachments,
|
|
9082
|
-
sessionsRoot: this.#sessionsRoot,
|
|
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
|
|
9096
|
-
}
|
|
9097
|
-
});
|
|
9098
|
-
const throughSeq = options.mode === "before" ? target.seq - 1 : target.seq;
|
|
9099
|
-
copyEntries(child, parent.items.filter(isEntry).filter((item) => item.seq <= throughSeq));
|
|
9100
|
-
appendTitleSnapshot(child, sessionTitleAt(parent.items, throughSeq));
|
|
9101
|
-
child.close();
|
|
9102
|
-
return {
|
|
9103
|
-
sessionId: child.header.sessionId,
|
|
9104
|
-
path: child.path,
|
|
9105
|
-
editorText: options.mode === "before" ? target.payload.message.skillContext?.input ?? target.payload.message.content : ""
|
|
9106
|
-
};
|
|
8909
|
+
if (cancelToken?.isCancelled()) {
|
|
8910
|
+
return new ModelStreamCancelled(cancelToken.reason || "cancelled", { cause: error });
|
|
9107
8911
|
}
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
appVersion: this.#appVersion,
|
|
9117
|
-
provider: parent.header.provider,
|
|
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"
|
|
9126
|
-
}
|
|
8912
|
+
if (error instanceof ModelError) {
|
|
8913
|
+
return error;
|
|
8914
|
+
}
|
|
8915
|
+
if (error instanceof ModelStreamError) {
|
|
8916
|
+
return new ModelError(error.message, {
|
|
8917
|
+
kind: modelErrorKind(error),
|
|
8918
|
+
hadDelta: error.hadDelta,
|
|
8919
|
+
cause: error
|
|
9127
8920
|
});
|
|
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 };
|
|
9132
8921
|
}
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
8922
|
+
return new ModelError(errorMessage(error), {
|
|
8923
|
+
kind: modelErrorKind(error),
|
|
8924
|
+
cause: error
|
|
8925
|
+
});
|
|
8926
|
+
}
|
|
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";
|
|
8935
|
+
}
|
|
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");
|
|
8939
|
+
}
|
|
8940
|
+
return value;
|
|
8941
|
+
}
|
|
8942
|
+
function validateRetryDelays(value) {
|
|
8943
|
+
if (value.length === 0) {
|
|
8944
|
+
throw new RangeError("retryDelaysMs must not be empty");
|
|
8945
|
+
}
|
|
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");
|
|
9137
8949
|
}
|
|
9138
|
-
return readSessionFile(summary.path);
|
|
9139
8950
|
}
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
8951
|
+
return [...value];
|
|
8952
|
+
}
|
|
8953
|
+
async function defaultSleep(delayMs, cancelToken) {
|
|
8954
|
+
if (cancelToken?.isCancelled()) {
|
|
8955
|
+
throw new ModelStreamCancelled(cancelToken.reason || "cancelled");
|
|
8956
|
+
}
|
|
8957
|
+
const cancelled = await cancelToken?.wait(delayMs);
|
|
8958
|
+
if (cancelled) {
|
|
8959
|
+
throw new ModelStreamCancelled(cancelToken?.reason || "cancelled");
|
|
8960
|
+
}
|
|
8961
|
+
if (cancelToken === null) {
|
|
8962
|
+
await new Promise((resolve15) => setTimeout(resolve15, delayMs));
|
|
8963
|
+
}
|
|
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;
|
|
8976
|
+
}
|
|
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 });
|
|
9143
8988
|
}
|
|
9144
|
-
const
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
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 });
|
|
8992
|
+
}
|
|
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>`
|
|
9153
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 });
|
|
9154
9009
|
}
|
|
9010
|
+
}
|
|
9011
|
+
validateToolPairs(selected);
|
|
9012
|
+
const offloaded = new Set(sorted.flatMap((entry) => entry.entryType === "image_offload" ? entry.payload.keys : []));
|
|
9013
|
+
return {
|
|
9014
|
+
messages: selected.map((item) => projectImageOffloads(item.message, offloaded)),
|
|
9015
|
+
sourceEntryIds: selected.map((item) => item.entryId),
|
|
9016
|
+
activeCompactionId: activeCompaction?.id ?? null
|
|
9155
9017
|
};
|
|
9156
|
-
visit(this.#sessionsRoot);
|
|
9157
|
-
return paths.sort();
|
|
9158
9018
|
}
|
|
9159
9019
|
};
|
|
9160
|
-
function
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
cwd: cwdFrom(replay),
|
|
9172
|
-
provider: replay.header.provider,
|
|
9173
|
-
model: replay.header.model,
|
|
9174
|
-
origin: replay.header.origin,
|
|
9175
|
-
...replay.header.parentSessionId === void 0 ? {} : { parentSessionId: replay.header.parentSessionId },
|
|
9176
|
-
...title === null ? {} : { title },
|
|
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
|
-
};
|
|
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);
|
|
9029
|
+
}
|
|
9030
|
+
}
|
|
9194
9031
|
}
|
|
9032
|
+
return superseded;
|
|
9195
9033
|
}
|
|
9196
|
-
function
|
|
9197
|
-
for (
|
|
9198
|
-
|
|
9199
|
-
|
|
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;
|
|
9200
9039
|
}
|
|
9201
9040
|
}
|
|
9202
|
-
return
|
|
9041
|
+
return null;
|
|
9203
9042
|
}
|
|
9204
|
-
function
|
|
9205
|
-
for (
|
|
9206
|
-
|
|
9207
|
-
|
|
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;
|
|
9208
9048
|
}
|
|
9209
9049
|
}
|
|
9210
|
-
return
|
|
9050
|
+
return null;
|
|
9211
9051
|
}
|
|
9212
|
-
function
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
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;
|
|
9068
|
+
}
|
|
9069
|
+
if (entry.entryType === "tool_result") {
|
|
9070
|
+
return entry.payload.message;
|
|
9071
|
+
}
|
|
9072
|
+
return null;
|
|
9073
|
+
}
|
|
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");
|
|
9223
9081
|
}
|
|
9224
|
-
|
|
9225
|
-
|
|
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");
|
|
9100
|
+
}
|
|
9101
|
+
}
|
|
9102
|
+
if (expected.length > 0) {
|
|
9103
|
+
throw new ContextBuildError(ownerEntryId ?? "unknown", "missing tool result");
|
|
9104
|
+
}
|
|
9105
|
+
}
|
|
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;
|
|
9124
|
+
}
|
|
9226
9125
|
}
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
return item.kind === "entry";
|
|
9230
|
-
}
|
|
9231
|
-
function appendTitleSnapshot(journal, title) {
|
|
9232
|
-
if (title !== null) {
|
|
9233
|
-
journal.appendRecord({
|
|
9234
|
-
recordType: "session_name_changed",
|
|
9235
|
-
payload: { title }
|
|
9236
|
-
});
|
|
9126
|
+
if (retained.length === 0 && units.length > 0) {
|
|
9127
|
+
retained.push(units.at(-1));
|
|
9237
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
|
+
};
|
|
9238
9139
|
}
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
function projectTranscript(entries) {
|
|
9242
|
-
const items = [];
|
|
9243
|
-
const skillNames = /* @__PURE__ */ new Map();
|
|
9140
|
+
function serializeConversation(entries) {
|
|
9141
|
+
const lines2 = ["<conversation>"];
|
|
9244
9142
|
for (const entry of entries) {
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
} else if (entry.entryType === "assistant_message") {
|
|
9143
|
+
lines2.push(`<entry id="${entry.id}" seq="${entry.seq}" type="${entry.entryType}">`);
|
|
9144
|
+
if (entry.entryType === "assistant_message") {
|
|
9248
9145
|
for (const block of entry.payload.message.content) {
|
|
9249
|
-
if (block.type
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
if (args && typeof args === "object" && "name" in args && typeof args.name === "string") {
|
|
9254
|
-
skillNames.set(block.call.id, args.name);
|
|
9255
|
-
}
|
|
9256
|
-
} catch {
|
|
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}`);
|
|
9257
9150
|
}
|
|
9258
9151
|
}
|
|
9259
|
-
const text3 = entry.payload.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
9260
|
-
const reasoning = entry.payload.message.content.filter((block) => block.type === "reasoning").map((block) => block.text).join("");
|
|
9261
|
-
items.push({
|
|
9262
|
-
kind: "assistant",
|
|
9263
|
-
text: text3,
|
|
9264
|
-
...reasoning === "" ? {} : { reasoning }
|
|
9265
|
-
});
|
|
9266
9152
|
} else if (entry.entryType === "tool_result") {
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
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}`);
|
|
9277
9186
|
}
|
|
9278
|
-
|
|
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);
|
|
9279
9194
|
}
|
|
9195
|
+
units.push({ entries: grouped, messages: grouped.map(messageForEntry) });
|
|
9196
|
+
continue;
|
|
9280
9197
|
}
|
|
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
9198
|
}
|
|
9199
|
+
if (entry.entryType === "tool_result") {
|
|
9200
|
+
continue;
|
|
9201
|
+
}
|
|
9202
|
+
units.push({ entries: [entry], messages: [messageForEntry(entry)] });
|
|
9203
|
+
}
|
|
9204
|
+
return units;
|
|
9205
|
+
}
|
|
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;
|
|
9217
|
+
}
|
|
9218
|
+
throw new Error(`entry has no model message: ${entry.entryType}`);
|
|
9219
|
+
}
|
|
9220
|
+
|
|
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 };
|
|
9296
9256
|
}
|
|
9297
|
-
|
|
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;
|
|
9307
|
+
}
|
|
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);
|
|
9312
|
+
}
|
|
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
|