codesesh 1.0.1 → 1.0.2

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.
@@ -121,7 +121,7 @@ function normalizeMessageParts(value) {
121
121
  });
122
122
  }
123
123
 
124
- // ../core/dist/chunk-ML3TMMX3.mjs
124
+ // ../core/dist/chunk-LE5L4UBM.mjs
125
125
  import { chmodSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
126
126
  import { basename, dirname, join } from "path";
127
127
  import { createHash } from "crypto";
@@ -159,6 +159,15 @@ import { homedir as homedir4, platform as platform3 } from "os";
159
159
  import { join as join13 } from "path";
160
160
  import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as statSync8 } from "fs";
161
161
  import { dirname as dirname6, join as join14 } from "path";
162
+ import { existsSync as existsSync12, readdirSync as readdirSync4, realpathSync, statSync as statSync10 } from "fs";
163
+ import { basename as basename9, join as join17 } from "path";
164
+ import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync8, readSync as readSync2, statSync as statSync9 } from "fs";
165
+ import { homedir as homedir5 } from "os";
166
+ import { join as join15, resolve as resolve2 } from "path";
167
+ import * as zlib from "zlib";
168
+ import { createHash as createHash3 } from "crypto";
169
+ import { readFileSync as readFileSync9 } from "fs";
170
+ import { join as join16 } from "path";
162
171
  var registrations = [];
163
172
  function registerAgent(reg) {
164
173
  registrations.push(reg);
@@ -8054,6 +8063,1274 @@ var GrokAgent = class extends FileSystemSessionSource {
8054
8063
  ]);
8055
8064
  }
8056
8065
  };
8066
+ var DSH_FORMAT_VERSION = 0;
8067
+ var PROJECT_SLUG_MAX_LENGTH = 251;
8068
+ var STABLE_READ_ATTEMPTS = 3;
8069
+ var HEADER_PREFIX_BYTES = 64 * 1024;
8070
+ var ZSTD_MAGIC = 4247762216;
8071
+ var KNOWN_DSH_EVENT_TYPES = /* @__PURE__ */ new Set([
8072
+ "agent-preset/selected",
8073
+ "agent/inbox/spliced",
8074
+ "approval/asked",
8075
+ "approval/decided",
8076
+ "approval/policy",
8077
+ "assistant/chunk",
8078
+ "assistant/message",
8079
+ "command/done",
8080
+ "command/run",
8081
+ "compaction/end",
8082
+ "compaction/prune",
8083
+ "compaction/start",
8084
+ "compaction/summary",
8085
+ "feedback/record",
8086
+ "goal/change",
8087
+ "hook/invoked",
8088
+ "hook/result",
8089
+ "llm/retry",
8090
+ "llm/retry-started",
8091
+ "permission/preset",
8092
+ "plan/mode",
8093
+ "request/context",
8094
+ "request/header",
8095
+ "sandbox/mode",
8096
+ "schedule/change",
8097
+ "session/end-seed",
8098
+ "session/title",
8099
+ "session/title-llm-request",
8100
+ "step/end",
8101
+ "step/start",
8102
+ "subagent/descriptor",
8103
+ "todo/write",
8104
+ "tool-workflow/agent-end",
8105
+ "tool-workflow/agent-start",
8106
+ "tool-workflow/run-end",
8107
+ "tool-workflow/run-start",
8108
+ "tool/call",
8109
+ "tool/code-dispatch",
8110
+ "tool/code-dispatch-start",
8111
+ "tool/result",
8112
+ "turn/end",
8113
+ "turn/start",
8114
+ "user/message",
8115
+ "web/deepseek-search-llm-request"
8116
+ ]);
8117
+ var TEXT_CHUNK_ROWS = /* @__PURE__ */ new Set(["text-chunks", "reasoning-chunks"]);
8118
+ var TOOL_CALL_CHUNK_ROW = "tool-call-chunks";
8119
+ var DshSessionLogError = class extends Error {
8120
+ constructor(message, options) {
8121
+ super(message, options);
8122
+ this.name = "DshSessionLogError";
8123
+ }
8124
+ };
8125
+ function expandHomePath(path2) {
8126
+ if (path2 === "~") return homedir5();
8127
+ if (path2.startsWith("~/") || path2.startsWith("~\\")) return join15(homedir5(), path2.slice(2));
8128
+ return path2;
8129
+ }
8130
+ function resolveDshDataRoot() {
8131
+ const fromEnv = process.env["DSH_HOME"];
8132
+ const selected = fromEnv !== void 0 && fromEnv.trim().length > 0 ? fromEnv : join15(homedir5(), ".dsh");
8133
+ return resolve2(expandHomePath(selected));
8134
+ }
8135
+ function dshSessionsRoot(dataRoot) {
8136
+ return join15(dataRoot, "sessions");
8137
+ }
8138
+ function dshAttachmentsRoot(dataRoot) {
8139
+ return join15(dataRoot, "attachments", "v1");
8140
+ }
8141
+ function dshLogFileName(encoding) {
8142
+ return encoding === "zstd" ? "session.jsonl.zstd" : "session.jsonl";
8143
+ }
8144
+ function isSafeSegmentChar(ch) {
8145
+ return /^[A-Za-z0-9._-]$/.test(ch);
8146
+ }
8147
+ function escapeCodeUnit(code) {
8148
+ return `~${code.toString(16).toUpperCase().padStart(4, "0")}`;
8149
+ }
8150
+ function dshEncodeSegment(raw) {
8151
+ if (raw.length === 0) throw new DshSessionLogError("cannot encode an empty path segment");
8152
+ if (raw === ".") return "~002E";
8153
+ if (raw === "..") return "~002E~002E";
8154
+ let out = "";
8155
+ for (let index = 0; index < raw.length; index += 1) {
8156
+ const code = raw.charCodeAt(index);
8157
+ const ch = String.fromCharCode(code);
8158
+ out += ch !== "~" && isSafeSegmentChar(ch) ? ch : escapeCodeUnit(code);
8159
+ }
8160
+ return out;
8161
+ }
8162
+ function dshProjectKey(cwd) {
8163
+ if (cwd.length === 0) throw new DshSessionLogError("cannot encode an empty project path");
8164
+ let readable = "";
8165
+ let separatorRun = false;
8166
+ for (let index = 0; index < cwd.length; index += 1) {
8167
+ const code = cwd.charCodeAt(index);
8168
+ const ch = String.fromCharCode(code);
8169
+ if (ch === "/" || ch === "\\" || ch === ":") {
8170
+ if (!separatorRun) readable += "-";
8171
+ separatorRun = true;
8172
+ continue;
8173
+ }
8174
+ readable += ch !== "~" && isSafeSegmentChar(ch) ? ch : escapeCodeUnit(code);
8175
+ separatorRun = false;
8176
+ }
8177
+ const slug = readable.replace(/^-+/, "") || "root";
8178
+ return `--${slug.slice(0, PROJECT_SLUG_MAX_LENGTH)}--`;
8179
+ }
8180
+ function dshLogPath(sessionsRoot, cwd, id, encoding) {
8181
+ const project = cwd === void 0 ? "_no-cwd" : dshProjectKey(cwd);
8182
+ return join15(sessionsRoot, project, dshEncodeSegment(id), dshLogFileName(encoding));
8183
+ }
8184
+ function sameFileIdentity(before, after) {
8185
+ return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeNs === after.mtimeNs && before.ctimeNs === after.ctimeNs;
8186
+ }
8187
+ function readDshFileSnapshot(sourcePath) {
8188
+ for (let attempt = 0; attempt < STABLE_READ_ATTEMPTS; attempt += 1) {
8189
+ const before = statSync9(sourcePath, { bigint: true });
8190
+ const buffer = readFileSync8(sourcePath);
8191
+ const after = statSync9(sourcePath, { bigint: true });
8192
+ if (sameFileIdentity(before, after)) return { buffer, stats: after };
8193
+ }
8194
+ throw new DshSessionLogError(
8195
+ `DSH session log ${JSON.stringify(sourcePath)} changed during every read attempt`
8196
+ );
8197
+ }
8198
+ function dshFileIdentity(stats) {
8199
+ return [stats.dev, stats.ino, stats.size, stats.mtimeNs, stats.ctimeNs].map(String);
8200
+ }
8201
+ function readFilePrefix(sourcePath, maxBytes) {
8202
+ const handle = openSync2(sourcePath, "r");
8203
+ try {
8204
+ const buffer = Buffer.allocUnsafe(maxBytes);
8205
+ return buffer.subarray(0, readSync2(handle, buffer, 0, maxBytes, 0));
8206
+ } finally {
8207
+ closeSync2(handle);
8208
+ }
8209
+ }
8210
+ function resolveZstdDecoder(sourcePath) {
8211
+ const decoder = zlib.zstdDecompressSync;
8212
+ if (typeof decoder !== "function") {
8213
+ throw new DshSessionLogError(
8214
+ `DSH session log ${JSON.stringify(sourcePath)} is Zstandard-compressed, but this Node runtime (${process.version}) has no native Zstandard decoder; DSH itself requires Node ^22.19.0 || >=24.0.0`
8215
+ );
8216
+ }
8217
+ return decoder;
8218
+ }
8219
+ function scanZstdFrames(buffer, sourcePath, maxFrames = Number.POSITIVE_INFINITY) {
8220
+ const frames = [];
8221
+ let offset = 0;
8222
+ const corrupt2 = (reason) => {
8223
+ throw new DshSessionLogError(
8224
+ `corrupt Zstandard session log ${JSON.stringify(sourcePath)}: ${reason}`
8225
+ );
8226
+ };
8227
+ while (offset < buffer.length) {
8228
+ const start = offset;
8229
+ if (buffer.length - offset < 4) return { frames, tornStart: start };
8230
+ if (buffer.readUInt32LE(offset) !== ZSTD_MAGIC)
8231
+ corrupt2(`invalid frame magic at byte ${offset}`);
8232
+ offset += 4;
8233
+ if (offset === buffer.length) return { frames, tornStart: start };
8234
+ const descriptor = buffer.readUInt8(offset);
8235
+ offset += 1;
8236
+ if ((descriptor & 24) !== 0) corrupt2(`reserved frame-header bit at byte ${offset - 1}`);
8237
+ const contentSizeFlag = descriptor >>> 6;
8238
+ const singleSegment = (descriptor & 32) !== 0;
8239
+ const hasChecksum = (descriptor & 4) !== 0;
8240
+ const dictionaryFlag = descriptor & 3;
8241
+ const dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;
8242
+ const contentSizeBytes = contentSizeFlag === 0 ? singleSegment ? 1 : 0 : 1 << contentSizeFlag;
8243
+ const remainingHeaderBytes = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;
8244
+ if (buffer.length - offset < remainingHeaderBytes) return { frames, tornStart: start };
8245
+ offset += remainingHeaderBytes;
8246
+ for (; ; ) {
8247
+ if (buffer.length - offset < 3) return { frames, tornStart: start };
8248
+ const blockHeader = buffer.readUIntLE(offset, 3);
8249
+ offset += 3;
8250
+ const lastBlock = (blockHeader & 1) !== 0;
8251
+ const blockType = blockHeader >>> 1 & 3;
8252
+ const blockSize = blockHeader >>> 3;
8253
+ if (blockType === 3) corrupt2(`reserved block type at byte ${offset - 3}`);
8254
+ const payloadBytes = blockType === 1 ? 1 : blockSize;
8255
+ if (buffer.length - offset < payloadBytes) return { frames, tornStart: start };
8256
+ offset += payloadBytes;
8257
+ if (lastBlock) break;
8258
+ }
8259
+ if (hasChecksum) {
8260
+ if (buffer.length - offset < 4) return { frames, tornStart: start };
8261
+ offset += 4;
8262
+ }
8263
+ frames.push({ start, end: offset });
8264
+ if (frames.length === maxFrames) return { frames };
8265
+ }
8266
+ return { frames };
8267
+ }
8268
+ function decodeFrame(decoder, buffer, frame, sourcePath) {
8269
+ try {
8270
+ return decoder(buffer.subarray(frame.start, frame.end)).toString("utf8");
8271
+ } catch (error) {
8272
+ throw new DshSessionLogError(
8273
+ `corrupt Zstandard session log ${JSON.stringify(sourcePath)}: frame at byte ${frame.start} failed validation`,
8274
+ { cause: error }
8275
+ );
8276
+ }
8277
+ }
8278
+ function frameRecords(plaintext, sourcePath, frameStart) {
8279
+ if (!plaintext.endsWith("\n")) {
8280
+ throw new DshSessionLogError(
8281
+ `corrupt Zstandard session log ${JSON.stringify(sourcePath)}: complete frame at byte ${frameStart} ends mid-record`
8282
+ );
8283
+ }
8284
+ return plaintext.slice(0, -1).split("\n");
8285
+ }
8286
+ function warnTornTail(sourcePath, detail) {
8287
+ getCoreDiagnostics()?.warn("dsh.torn_session_tail", { source_path: sourcePath, ...detail });
8288
+ }
8289
+ function readCompressedRecords(buffer, sourcePath, headerOnly) {
8290
+ const decoder = resolveZstdDecoder(sourcePath);
8291
+ const scan = scanZstdFrames(buffer, sourcePath, headerOnly ? 1 : Number.POSITIVE_INFINITY);
8292
+ const first = scan.frames[0];
8293
+ if (!first) {
8294
+ throw new DshSessionLogError(
8295
+ `DSH session log ${JSON.stringify(sourcePath)} has no complete header frame`
8296
+ );
8297
+ }
8298
+ const headerPlaintext = decodeFrame(decoder, buffer, first, sourcePath);
8299
+ if (headerPlaintext.indexOf("\n") !== headerPlaintext.length - 1) {
8300
+ throw new DshSessionLogError(
8301
+ `corrupt Zstandard session log ${JSON.stringify(sourcePath)}: header frame does not hold exactly one record`
8302
+ );
8303
+ }
8304
+ const records = [headerPlaintext.slice(0, -1)];
8305
+ if (headerOnly) return records;
8306
+ for (const frame of scan.frames.slice(1)) {
8307
+ records.push(
8308
+ ...frameRecords(decodeFrame(decoder, buffer, frame, sourcePath), sourcePath, frame.start)
8309
+ );
8310
+ }
8311
+ if (scan.tornStart !== void 0) {
8312
+ warnTornTail(sourcePath, { encoding: "zstd", torn_start: scan.tornStart });
8313
+ }
8314
+ return records;
8315
+ }
8316
+ function readPlaintextRecords(buffer, sourcePath, headerOnly) {
8317
+ const text = buffer.toString("utf8");
8318
+ const newline = text.indexOf("\n");
8319
+ if (newline === -1) {
8320
+ throw new DshSessionLogError(
8321
+ `DSH session log ${JSON.stringify(sourcePath)} has no complete header record`
8322
+ );
8323
+ }
8324
+ if (headerOnly) return [text.slice(0, newline)];
8325
+ const complete = text.endsWith("\n");
8326
+ const records = (complete ? text.slice(0, -1) : text).split("\n");
8327
+ if (!complete) {
8328
+ records.pop();
8329
+ warnTornTail(sourcePath, {
8330
+ encoding: "none",
8331
+ torn_bytes: text.length - text.lastIndexOf("\n") - 1
8332
+ });
8333
+ }
8334
+ return records;
8335
+ }
8336
+ function readRecords(buffer, sourcePath, encoding, headerOnly) {
8337
+ return encoding === "zstd" ? readCompressedRecords(buffer, sourcePath, headerOnly) : readPlaintextRecords(buffer, sourcePath, headerOnly);
8338
+ }
8339
+ function isRecord(value) {
8340
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8341
+ }
8342
+ function isCount(value) {
8343
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
8344
+ }
8345
+ function optionalString2(value, field, sourcePath) {
8346
+ if (value === void 0) return void 0;
8347
+ if (typeof value !== "string") {
8348
+ throw new DshSessionLogError(
8349
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: header ${field} must be a string`
8350
+ );
8351
+ }
8352
+ return value;
8353
+ }
8354
+ function parseJsonRecord2(line, what, sourcePath) {
8355
+ let parsed;
8356
+ try {
8357
+ parsed = JSON.parse(line);
8358
+ } catch (error) {
8359
+ throw new DshSessionLogError(
8360
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: ${what} is not valid JSON`,
8361
+ { cause: error }
8362
+ );
8363
+ }
8364
+ if (!isRecord(parsed)) {
8365
+ throw new DshSessionLogError(
8366
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: ${what} is not an object`
8367
+ );
8368
+ }
8369
+ return parsed;
8370
+ }
8371
+ function parseHeaderRecord(line, sourcePath) {
8372
+ const parsed = parseJsonRecord2(line, "header line", sourcePath);
8373
+ const version = parsed["version"];
8374
+ if (typeof version !== "number" || version !== DSH_FORMAT_VERSION) {
8375
+ throw new DshSessionLogError(
8376
+ `unsupported DSH session format version ${JSON.stringify(version)} in ${JSON.stringify(sourcePath)}; this build reads version ${DSH_FORMAT_VERSION}`
8377
+ );
8378
+ }
8379
+ const invalid = (reason) => {
8380
+ throw new DshSessionLogError(
8381
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: ${reason}`
8382
+ );
8383
+ };
8384
+ if (parsed["type"] !== "session") invalid("first record is not a session header");
8385
+ const id = parsed["id"];
8386
+ if (typeof id !== "string" || id.length === 0) invalid("header id must be a non-empty string");
8387
+ const createdAt = parsed["createdAt"];
8388
+ if (!isCount(createdAt)) invalid("header createdAt must be a non-negative safe integer");
8389
+ const delegationDepth = parsed["delegationDepth"];
8390
+ if (!isCount(delegationDepth)) {
8391
+ invalid("header delegationDepth must be a non-negative safe integer");
8392
+ }
8393
+ const seedLength = parsed["seedLength"];
8394
+ if (seedLength !== void 0 && !isCount(seedLength)) {
8395
+ invalid("header seedLength must be a non-negative safe integer");
8396
+ }
8397
+ const origin = parsed["origin"];
8398
+ if (origin !== void 0 && origin !== "subagent") invalid("header origin must be 'subagent'");
8399
+ return {
8400
+ id,
8401
+ createdAt,
8402
+ cwd: optionalString2(parsed["cwd"], "cwd", sourcePath),
8403
+ parentSession: optionalString2(parsed["parentSession"], "parentSession", sourcePath),
8404
+ seedLength,
8405
+ origin,
8406
+ delegationDepth,
8407
+ agentPreset: optionalString2(parsed["agentPreset"], "agentPreset", sourcePath)
8408
+ };
8409
+ }
8410
+ function chunkRunMembers(data, payloadKey, tag, sourcePath) {
8411
+ const malformed = (reason) => {
8412
+ throw new DshSessionLogError(
8413
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: malformed ${tag} storage row: ${reason}`
8414
+ );
8415
+ };
8416
+ if (typeof data["turn"] !== "number" || typeof data["step"] !== "number" || typeof data["index"] !== "number") {
8417
+ malformed("turn/step/index must be numbers");
8418
+ }
8419
+ const payload = data[payloadKey];
8420
+ if (!Array.isArray(payload) || payload.length === 0 || payload.some((entry) => typeof entry !== "string")) {
8421
+ malformed(`${payloadKey} must be a non-empty string array`);
8422
+ }
8423
+ const gaps = data["dt"];
8424
+ if (!Array.isArray(gaps) || gaps.some((gap) => !Number.isSafeInteger(gap))) {
8425
+ malformed("dt must be an array of safe integers");
8426
+ }
8427
+ const members = payload;
8428
+ if (gaps.length !== members.length - 1) {
8429
+ malformed(`dt length ${gaps.length} does not match ${members.length} members`);
8430
+ }
8431
+ return { members, gaps };
8432
+ }
8433
+ function expandChunkRow(row, tag, sourcePath) {
8434
+ const malformed = (reason) => {
8435
+ throw new DshSessionLogError(
8436
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: malformed ${tag} storage row: ${reason}`
8437
+ );
8438
+ };
8439
+ const seq0 = row["seq0"];
8440
+ const time0 = row["time0"];
8441
+ if (!isCount(seq0)) malformed("seq0 must be a non-negative safe integer");
8442
+ if (!Number.isSafeInteger(time0)) malformed("time0 must be a safe integer");
8443
+ const data = row["data"];
8444
+ if (!isRecord(data)) malformed("data must be an object");
8445
+ const payload = data;
8446
+ const isToolCall = tag === TOOL_CALL_CHUNK_ROW;
8447
+ const { members, gaps } = chunkRunMembers(
8448
+ payload,
8449
+ isToolCall ? "args" : "texts",
8450
+ tag,
8451
+ sourcePath
8452
+ );
8453
+ const callId = payload["id"];
8454
+ const callName = payload["name"];
8455
+ if (isToolCall) {
8456
+ if (typeof callId !== "string") malformed("id must be a string");
8457
+ if (callName !== void 0 && typeof callName !== "string") malformed("name must be a string");
8458
+ }
8459
+ if (!Number.isSafeInteger(seq0 + members.length - 1)) {
8460
+ malformed("member seqs must stay safe integers");
8461
+ }
8462
+ const turn = payload["turn"];
8463
+ const step = payload["step"];
8464
+ const index = payload["index"];
8465
+ const deltaType = tag === "text-chunks" ? "text-delta" : tag === "reasoning-chunks" ? "reasoning-delta" : "tool-call-delta";
8466
+ const events = [];
8467
+ let time = time0;
8468
+ for (let member = 0; member < members.length; member += 1) {
8469
+ if (member > 0) {
8470
+ time += gaps[member - 1];
8471
+ if (!Number.isSafeInteger(time)) malformed("member times must stay safe integers");
8472
+ }
8473
+ const chunk = isToolCall ? {
8474
+ type: deltaType,
8475
+ index,
8476
+ id: callId,
8477
+ ...callName !== void 0 ? { name: callName } : {},
8478
+ argumentsDelta: members[member]
8479
+ } : { type: deltaType, index, text: members[member] };
8480
+ events.push({
8481
+ type: "assistant/chunk",
8482
+ seq: seq0 + member,
8483
+ time,
8484
+ data: { turn, step, chunk }
8485
+ });
8486
+ }
8487
+ return events;
8488
+ }
8489
+ function decodeStorageRecord(line, sourcePath) {
8490
+ const parsed = parseJsonRecord2(line, "event record", sourcePath);
8491
+ const tag = parsed["type"];
8492
+ if (typeof tag === "string" && (TEXT_CHUNK_ROWS.has(tag) || tag === TOOL_CALL_CHUNK_ROW)) {
8493
+ return expandChunkRow(parsed, tag, sourcePath);
8494
+ }
8495
+ if (typeof tag !== "string" || tag.length === 0) {
8496
+ throw new DshSessionLogError(
8497
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: event record has no type`
8498
+ );
8499
+ }
8500
+ if (!KNOWN_DSH_EVENT_TYPES.has(tag) && parsed["ignorable"] !== true) {
8501
+ throw new DshSessionLogError(
8502
+ `DSH session log ${JSON.stringify(sourcePath)} contains unsupported required event ${JSON.stringify(tag)}; upgrade CodeSesh to read it`
8503
+ );
8504
+ }
8505
+ if (!isCount(parsed["seq"]) || !Number.isSafeInteger(parsed["time"])) {
8506
+ throw new DshSessionLogError(
8507
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: event ${JSON.stringify(tag)} has an invalid seq/time`
8508
+ );
8509
+ }
8510
+ return [parsed];
8511
+ }
8512
+ function readDshSessionHeader(sourcePath, encoding) {
8513
+ const prefix = readFilePrefix(sourcePath, HEADER_PREFIX_BYTES);
8514
+ const line = firstRecordOf(prefix, sourcePath, encoding, prefix.length === HEADER_PREFIX_BYTES) ?? firstRecordOf(readFileSync8(sourcePath), sourcePath, encoding, false);
8515
+ return parseHeaderRecord(line, sourcePath);
8516
+ }
8517
+ function firstRecordOf(buffer, sourcePath, encoding, mayBeTruncated) {
8518
+ if (mayBeTruncated) {
8519
+ const complete = encoding === "zstd" ? scanZstdFrames(buffer, sourcePath, 1).frames.length > 0 : buffer.includes(10);
8520
+ if (!complete) return null;
8521
+ }
8522
+ return readRecords(buffer, sourcePath, encoding, true)[0];
8523
+ }
8524
+ function readDshSessionLog(sourcePath, encoding) {
8525
+ const { buffer } = readDshFileSnapshot(sourcePath);
8526
+ const records = readRecords(buffer, sourcePath, encoding, false);
8527
+ const header = parseHeaderRecord(records[0], sourcePath);
8528
+ const events = [];
8529
+ for (let line = 1; line < records.length; line += 1) {
8530
+ for (const event of decodeStorageRecord(records[line], sourcePath)) {
8531
+ if (event.seq !== events.length) {
8532
+ throw new DshSessionLogError(
8533
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: seq ${event.seq} at record ${line} breaks the contiguous prefix (expected ${events.length})`
8534
+ );
8535
+ }
8536
+ events.push(event);
8537
+ }
8538
+ }
8539
+ if (header.seedLength !== void 0 && header.seedLength > events.length) {
8540
+ throw new DshSessionLogError(
8541
+ `corrupt DSH session log ${JSON.stringify(sourcePath)}: seedLength ${header.seedLength} exceeds ${events.length} events`
8542
+ );
8543
+ }
8544
+ return { header, events };
8545
+ }
8546
+ var AGENT_NAME = "dsh";
8547
+ var IMAGE_MEDIA_TYPES = /* @__PURE__ */ new Set([
8548
+ "image/png",
8549
+ "image/jpeg",
8550
+ "image/webp",
8551
+ "image/gif"
8552
+ ]);
8553
+ var ATTACHMENT_ID_PATTERN = /^sha256:([a-f0-9]{64})$/;
8554
+ var IMAGE_UNAVAILABLE_TEXT = "Image attachment unavailable";
8555
+ function isRecord2(value) {
8556
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8557
+ }
8558
+ function asRecord2(value) {
8559
+ return isRecord2(value) ? value : {};
8560
+ }
8561
+ function asText(value) {
8562
+ return typeof value === "string" ? value : "";
8563
+ }
8564
+ function corrupt(context, reason) {
8565
+ throw new DshSessionLogError(
8566
+ `corrupt DSH session log ${JSON.stringify(context.sourcePath)}: ${reason}`
8567
+ );
8568
+ }
8569
+ function warn(context, event, detail) {
8570
+ getCoreDiagnostics()?.warn(event, {
8571
+ agent: AGENT_NAME,
8572
+ session_id: context.sessionId,
8573
+ ...detail
8574
+ });
8575
+ }
8576
+ var SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
8577
+ "user/message",
8578
+ "assistant/message",
8579
+ "tool/result"
8580
+ ]);
8581
+ function isAppendOrigin(event, context) {
8582
+ if (!SURFACE_EVENT_TYPES.has(event.type)) return false;
8583
+ const op = event.surfaceOp;
8584
+ if (op === "append") return true;
8585
+ if (op === void 0) {
8586
+ corrupt(
8587
+ context,
8588
+ `surface event ${JSON.stringify(event.type)} at seq ${event.seq} has no surfaceOp`
8589
+ );
8590
+ }
8591
+ const replace = asRecord2(op);
8592
+ if (replace["op"] !== "replace" || !Number.isSafeInteger(replace["start"]) || !Number.isSafeInteger(replace["end"])) {
8593
+ corrupt(
8594
+ context,
8595
+ `surface event ${JSON.stringify(event.type)} at seq ${event.seq} has an invalid surfaceOp`
8596
+ );
8597
+ }
8598
+ return false;
8599
+ }
8600
+ function readImageBlock(block, timeMs, context) {
8601
+ const attachment = asRecord2(block["attachment"]);
8602
+ const attachmentId = asText(attachment["attachmentId"]);
8603
+ const mediaType = asText(attachment["mediaType"]);
8604
+ const digest = ATTACHMENT_ID_PATTERN.exec(attachmentId)?.[1];
8605
+ const reject = (reason) => {
8606
+ warn(context, "dsh.attachment_unreadable", { attachment_id: attachmentId, reason });
8607
+ return null;
8608
+ };
8609
+ if (!digest) return reject("invalid attachment reference");
8610
+ if (!IMAGE_MEDIA_TYPES.has(mediaType)) return reject("unsupported media type");
8611
+ let data;
8612
+ try {
8613
+ data = readFileSync9(join16(context.attachmentsRoot, "objects", digest.slice(0, 2), digest));
8614
+ } catch {
8615
+ return reject("attachment object is missing");
8616
+ }
8617
+ const declaredBytes = attachment["bytes"];
8618
+ if (typeof declaredBytes === "number" && data.byteLength !== declaredBytes) {
8619
+ return reject("attachment byte length does not match its reference");
8620
+ }
8621
+ if (createHash3("sha256").update(data).digest("hex") !== digest) {
8622
+ return reject("attachment failed integrity verification");
8623
+ }
8624
+ return {
8625
+ type: "image",
8626
+ data: data.toString("base64"),
8627
+ mime_type: mediaType,
8628
+ time_created: timeMs
8629
+ };
8630
+ }
8631
+ function convertContentBlocks(content, timeMs, context, canonicalCallIds) {
8632
+ if (!Array.isArray(content)) return [];
8633
+ const parts = [];
8634
+ let droppedImages = 0;
8635
+ for (const raw of content) {
8636
+ const block = asRecord2(raw);
8637
+ switch (block["type"]) {
8638
+ case "text":
8639
+ case "reasoning": {
8640
+ const text = cleanInternalText(asText(block["text"]));
8641
+ if (text) parts.push({ type: block["type"], text, time_created: timeMs });
8642
+ break;
8643
+ }
8644
+ case "image": {
8645
+ const image = readImageBlock(block, timeMs, context);
8646
+ if (image) parts.push(image);
8647
+ else droppedImages += 1;
8648
+ break;
8649
+ }
8650
+ case "tool-call": {
8651
+ const callId = asText(block["id"]);
8652
+ if (callId && canonicalCallIds.has(callId)) break;
8653
+ parts.push(buildToolPart2(callId, asText(block["name"]), block["arguments"], timeMs));
8654
+ break;
8655
+ }
8656
+ default:
8657
+ break;
8658
+ }
8659
+ }
8660
+ if (parts.length === 0 && droppedImages > 0) {
8661
+ parts.push({ type: "text", text: IMAGE_UNAVAILABLE_TEXT, time_created: timeMs });
8662
+ }
8663
+ return parts;
8664
+ }
8665
+ function parseToolArguments(raw) {
8666
+ if (typeof raw !== "string") return raw ?? {};
8667
+ try {
8668
+ return JSON.parse(raw);
8669
+ } catch {
8670
+ return raw;
8671
+ }
8672
+ }
8673
+ function buildToolPart2(callId, name, rawArguments, timeMs) {
8674
+ const tool = name.trim() || "tool";
8675
+ return {
8676
+ type: "tool",
8677
+ tool,
8678
+ title: tool.toLowerCase(),
8679
+ callID: callId || void 0,
8680
+ time_created: timeMs,
8681
+ state: { status: "running", input: parseToolArguments(rawArguments) }
8682
+ };
8683
+ }
8684
+ function nonNegative(value) {
8685
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
8686
+ }
8687
+ function readUsage(value) {
8688
+ if (!isRecord2(value)) return null;
8689
+ return {
8690
+ inputTokens: nonNegative(value["inputTokens"]),
8691
+ outputTokens: nonNegative(value["outputTokens"]),
8692
+ cacheReadTokens: nonNegative(value["cacheReadTokens"]),
8693
+ cacheWriteTokens: nonNegative(value["cacheWriteTokens"]),
8694
+ reasoningTokens: nonNegative(value["reasoningTokens"])
8695
+ };
8696
+ }
8697
+ function toMessageTokens(usage) {
8698
+ const cacheRead = nonNegative(usage.cacheReadTokens);
8699
+ const cacheCreate = nonNegative(usage.cacheWriteTokens);
8700
+ const totalInput = nonNegative(usage.inputTokens) + cacheRead + cacheCreate;
8701
+ const output = nonNegative(usage.outputTokens);
8702
+ const reasoning = Math.min(nonNegative(usage.reasoningTokens), output);
8703
+ return {
8704
+ tokens: {
8705
+ input: totalInput,
8706
+ output: output - reasoning,
8707
+ reasoning: reasoning || void 0,
8708
+ cache_read: cacheRead || void 0,
8709
+ cache_create: cacheCreate || void 0
8710
+ },
8711
+ totals: { input: totalInput, output, cacheRead, cacheCreate, cost: 0 }
8712
+ };
8713
+ }
8714
+ var DshProjector = class {
8715
+ constructor(context, seedLength, canonicalCallIds) {
8716
+ this.context = context;
8717
+ this.seedLength = seedLength;
8718
+ this.canonicalCallIds = canonicalCallIds;
8719
+ }
8720
+ context;
8721
+ seedLength;
8722
+ canonicalCallIds;
8723
+ builder = new TranscriptBuilder({ messageDefaults: "sparse" });
8724
+ totals = {
8725
+ input: 0,
8726
+ output: 0,
8727
+ cacheRead: 0,
8728
+ cacheCreate: 0,
8729
+ cost: 0
8730
+ };
8731
+ modelUsage = {};
8732
+ settledSteps = /* @__PURE__ */ new Set();
8733
+ pending = null;
8734
+ provider = null;
8735
+ model = null;
8736
+ ownTitle = null;
8737
+ inheritedTitle = null;
8738
+ subagentLabel = null;
8739
+ firstHumanText = null;
8740
+ updatedAt = 0;
8741
+ project(events) {
8742
+ for (const event of events) {
8743
+ const own = event.seq >= this.seedLength;
8744
+ if (own) this.updatedAt = Math.max(this.updatedAt, event.time);
8745
+ this.consume(event, own);
8746
+ }
8747
+ this.flushPendingStep();
8748
+ const result = this.builder.finish({
8749
+ message_count: 0,
8750
+ total_input_tokens: this.totals.input,
8751
+ total_output_tokens: this.totals.output,
8752
+ total_cache_read_tokens: this.totals.cacheRead || void 0,
8753
+ total_cache_create_tokens: this.totals.cacheCreate || void 0,
8754
+ total_cost: this.totals.cost,
8755
+ cost_source: this.totals.cost > 0 ? "estimated" : void 0
8756
+ });
8757
+ return {
8758
+ messages: result.messages,
8759
+ stats: result.stats,
8760
+ modelUsage: this.modelUsage,
8761
+ title: this.resolveTitle(),
8762
+ updatedAt: this.updatedAt
8763
+ };
8764
+ }
8765
+ consume(event, own) {
8766
+ switch (event.type) {
8767
+ case "user/message":
8768
+ this.consumeUserMessage(event, own);
8769
+ return;
8770
+ case "assistant/message":
8771
+ this.consumeAssistantMessage(event, own);
8772
+ return;
8773
+ case "tool/call":
8774
+ if (own) this.consumeToolCall(event);
8775
+ return;
8776
+ case "tool/result":
8777
+ if (isAppendOrigin(event, this.context) && own) this.consumeToolResult(event);
8778
+ return;
8779
+ case "assistant/chunk":
8780
+ if (own) this.consumeChunk(event);
8781
+ return;
8782
+ case "request/context":
8783
+ this.consumeRequestContext(event);
8784
+ return;
8785
+ case "session/title":
8786
+ this.consumeTitle(event, own);
8787
+ return;
8788
+ case "subagent/descriptor":
8789
+ if (own) this.consumeSubagentDescriptor(event);
8790
+ return;
8791
+ default:
8792
+ return;
8793
+ }
8794
+ }
8795
+ consumeUserMessage(event, own) {
8796
+ const appendOrigin = isAppendOrigin(event, this.context);
8797
+ const data = asRecord2(event.data);
8798
+ if (!appendOrigin || !own || asRecord2(data["source"])["kind"] !== "user") return;
8799
+ const parts = convertContentBlocks(
8800
+ data["content"],
8801
+ event.time,
8802
+ this.context,
8803
+ this.canonicalCallIds
8804
+ );
8805
+ if (parts.length === 0) return;
8806
+ this.builder.appendMessage({
8807
+ id: asText(data["id"]) || `dsh-user-${event.seq}`,
8808
+ role: "user",
8809
+ timestampMs: event.time,
8810
+ parts
8811
+ });
8812
+ this.firstHumanText ??= firstTextBlock(data["content"]);
8813
+ }
8814
+ consumeAssistantMessage(event, own) {
8815
+ const appendOrigin = isAppendOrigin(event, this.context);
8816
+ const data = asRecord2(event.data);
8817
+ const turn = data["turn"];
8818
+ const step = data["step"];
8819
+ if (typeof turn === "number" && typeof step === "number") {
8820
+ this.settleStep(turn, step);
8821
+ }
8822
+ if (!appendOrigin || !own) return;
8823
+ const message = asRecord2(data["message"]);
8824
+ const source = asRecord2(message["source"]);
8825
+ const provider = asText(source["provider"]) || null;
8826
+ const model = asText(source["model"]) || null;
8827
+ if (provider) this.provider = provider;
8828
+ if (model) this.model = model;
8829
+ const parts = convertContentBlocks(
8830
+ message["content"],
8831
+ event.time,
8832
+ this.context,
8833
+ this.canonicalCallIds
8834
+ );
8835
+ const usage = readUsage(data["usage"]);
8836
+ this.appendAssistant({
8837
+ id: asText(message["id"]) || `dsh-assistant-${event.seq}`,
8838
+ timeMs: event.time,
8839
+ parts,
8840
+ provider,
8841
+ model,
8842
+ usage
8843
+ });
8844
+ }
8845
+ consumeToolCall(event) {
8846
+ const data = asRecord2(event.data);
8847
+ const callId = asText(data["callId"]);
8848
+ const part = buildToolPart2(callId, asText(data["name"]), data["arguments"], event.time);
8849
+ this.builder.appendToolCall(
8850
+ part,
8851
+ { id: callId, timestampMs: event.time, agent: AGENT_NAME },
8852
+ { target: "current" }
8853
+ );
8854
+ }
8855
+ consumeToolResult(event) {
8856
+ const data = asRecord2(event.data);
8857
+ const message = asRecord2(data["message"]);
8858
+ const source = asRecord2(message["source"]);
8859
+ if (source["kind"] !== "tool") {
8860
+ corrupt(this.context, `tool/result at seq ${event.seq} is not sourced from a tool call`);
8861
+ }
8862
+ const callId = asText(source["callId"]);
8863
+ const content = Array.isArray(message["content"]) ? message["content"] : [];
8864
+ const block = asRecord2(content[0]);
8865
+ if (content.length !== 1 || block["type"] !== "tool-result") {
8866
+ corrupt(
8867
+ this.context,
8868
+ `tool/result at seq ${event.seq} must carry exactly one tool-result block`
8869
+ );
8870
+ }
8871
+ if (asText(block["toolCallId"]) !== callId) {
8872
+ corrupt(this.context, `tool/result at seq ${event.seq} disagrees with its call identity`);
8873
+ }
8874
+ const failed = block["isError"] === true || isRecord2(data["error"]);
8875
+ const resolved = this.builder.resolveToolCall(callId, {
8876
+ output: convertContentBlocks(
8877
+ block["content"],
8878
+ event.time,
8879
+ this.context,
8880
+ this.canonicalCallIds
8881
+ ),
8882
+ status: failed ? "error" : "completed",
8883
+ metadata: data["meta"],
8884
+ consume: true
8885
+ });
8886
+ if (!resolved) {
8887
+ warn(this.context, "dsh.orphan_tool_result", { call_id: callId, seq: event.seq });
8888
+ }
8889
+ }
8890
+ consumeChunk(event) {
8891
+ const data = asRecord2(event.data);
8892
+ const turn = data["turn"];
8893
+ const step = data["step"];
8894
+ if (typeof turn !== "number" || typeof step !== "number") return;
8895
+ if (this.settledSteps.has(stepKey(turn, step))) return;
8896
+ const pending2 = this.openStep(turn, step, event.time);
8897
+ pending2.lastTime = event.time;
8898
+ applyChunk(pending2, asRecord2(data["chunk"]));
8899
+ }
8900
+ consumeRequestContext(event) {
8901
+ const data = asRecord2(event.data);
8902
+ const provider = asText(data["provider"]);
8903
+ const model = asText(data["model"]);
8904
+ if (provider) this.provider = provider;
8905
+ if (model) this.model = model;
8906
+ }
8907
+ consumeTitle(event, own) {
8908
+ const title = normalizeTitleText(asText(asRecord2(event.data)["title"]));
8909
+ if (!title) return;
8910
+ if (own) this.ownTitle = title;
8911
+ else this.inheritedTitle = title;
8912
+ }
8913
+ consumeSubagentDescriptor(event) {
8914
+ const label = normalizeTitleText(asText(asRecord2(event.data)["label"]));
8915
+ if (label) this.subagentLabel ??= label;
8916
+ }
8917
+ /** A settled message supersedes the streaming deltas of the same step. */
8918
+ settleStep(turn, step) {
8919
+ this.settledSteps.add(stepKey(turn, step));
8920
+ if (this.pending?.turn === turn && this.pending.step === step) this.pending = null;
8921
+ }
8922
+ openStep(turn, step, timeMs) {
8923
+ if (this.pending && (this.pending.turn !== turn || this.pending.step !== step)) {
8924
+ this.flushPendingStep();
8925
+ }
8926
+ this.pending ??= {
8927
+ turn,
8928
+ step,
8929
+ firstTime: timeMs,
8930
+ lastTime: timeMs,
8931
+ blocks: /* @__PURE__ */ new Map(),
8932
+ usage: null
8933
+ };
8934
+ return this.pending;
8935
+ }
8936
+ /** Rebuild the assistant message of a step the log was cut short in. */
8937
+ flushPendingStep() {
8938
+ const pending2 = this.pending;
8939
+ this.pending = null;
8940
+ if (!pending2) return;
8941
+ const parts = [];
8942
+ for (const index of [...pending2.blocks.keys()].sort((a, b) => a - b)) {
8943
+ const block = pending2.blocks.get(index);
8944
+ if (block.type === "tool-call") {
8945
+ if (this.canonicalCallIds.has(block.id)) continue;
8946
+ parts.push(buildToolPart2(block.id, block.name, block.args, pending2.lastTime));
8947
+ continue;
8948
+ }
8949
+ const text = cleanInternalText(block.text);
8950
+ if (text) parts.push({ type: block.type, text, time_created: pending2.firstTime });
8951
+ }
8952
+ if (parts.length === 0) return;
8953
+ this.appendAssistant({
8954
+ id: `dsh-step-${pending2.turn}-${pending2.step}`,
8955
+ timeMs: pending2.firstTime,
8956
+ parts,
8957
+ provider: this.provider,
8958
+ model: this.model,
8959
+ usage: pending2.usage
8960
+ });
8961
+ }
8962
+ appendAssistant(input) {
8963
+ const mapped = input.usage ? toMessageTokens(input.usage) : null;
8964
+ const cost = mapped ? estimateTokenCost(input.model, mapped.tokens) ?? 0 : 0;
8965
+ if (mapped) {
8966
+ this.totals.input += mapped.totals.input;
8967
+ this.totals.output += mapped.totals.output;
8968
+ this.totals.cacheRead += mapped.totals.cacheRead;
8969
+ this.totals.cacheCreate += mapped.totals.cacheCreate;
8970
+ this.totals.cost += cost;
8971
+ if (input.model) {
8972
+ const billed = mapped.totals.input + mapped.totals.output;
8973
+ this.modelUsage[input.model] = (this.modelUsage[input.model] ?? 0) + billed;
8974
+ }
8975
+ }
8976
+ if (input.parts.length === 0) return;
8977
+ this.builder.appendMessage({
8978
+ id: input.id,
8979
+ role: "assistant",
8980
+ agent: AGENT_NAME,
8981
+ timestampMs: input.timeMs,
8982
+ parts: input.parts,
8983
+ provider: input.provider,
8984
+ model: input.model,
8985
+ tokens: mapped?.tokens,
8986
+ cost: cost || void 0,
8987
+ costSource: cost > 0 ? "estimated" : void 0
8988
+ });
8989
+ }
8990
+ resolveTitle() {
8991
+ return this.ownTitle ?? this.subagentLabel ?? this.firstHumanText ?? this.inheritedTitle;
8992
+ }
8993
+ };
8994
+ function stepKey(turn, step) {
8995
+ return `${turn}:${step}`;
8996
+ }
8997
+ function firstTextBlock(content) {
8998
+ if (!Array.isArray(content)) return null;
8999
+ for (const raw of content) {
9000
+ const block = asRecord2(raw);
9001
+ if (block["type"] !== "text") continue;
9002
+ const text = cleanInternalText(asText(block["text"]));
9003
+ if (text) return text;
9004
+ }
9005
+ return null;
9006
+ }
9007
+ function applyChunk(pending2, chunk) {
9008
+ const index = chunk["index"];
9009
+ if (typeof index !== "number") {
9010
+ if (chunk["type"] === "usage") pending2.usage = readUsage(chunk["usage"]);
9011
+ return;
9012
+ }
9013
+ switch (chunk["type"]) {
9014
+ case "text-delta":
9015
+ case "reasoning-delta": {
9016
+ const type = chunk["type"] === "text-delta" ? "text" : "reasoning";
9017
+ const current = pending2.blocks.get(index);
9018
+ const text = current && current.type === type ? current.text : "";
9019
+ pending2.blocks.set(index, { type, text: text + asText(chunk["text"]) });
9020
+ return;
9021
+ }
9022
+ case "tool-call-delta": {
9023
+ const current = pending2.blocks.get(index);
9024
+ const args = current && current.type === "tool-call" ? current.args : "";
9025
+ const name = current && current.type === "tool-call" ? current.name : "";
9026
+ pending2.blocks.set(index, {
9027
+ type: "tool-call",
9028
+ id: asText(chunk["id"]),
9029
+ name: asText(chunk["name"]) || name,
9030
+ args: args + asText(chunk["argumentsDelta"])
9031
+ });
9032
+ return;
9033
+ }
9034
+ case "block-end": {
9035
+ const block = asRecord2(chunk["block"]);
9036
+ if (block["type"] === "text" || block["type"] === "reasoning") {
9037
+ pending2.blocks.set(index, { type: block["type"], text: asText(block["text"]) });
9038
+ } else if (block["type"] === "tool-call") {
9039
+ pending2.blocks.set(index, {
9040
+ type: "tool-call",
9041
+ id: asText(block["id"]),
9042
+ name: asText(block["name"]),
9043
+ args: asText(block["arguments"])
9044
+ });
9045
+ }
9046
+ return;
9047
+ }
9048
+ default:
9049
+ return;
9050
+ }
9051
+ }
9052
+ function collectCanonicalCallIds(events, context) {
9053
+ const ids = /* @__PURE__ */ new Set();
9054
+ for (const event of events) {
9055
+ if (event.type !== "tool/call") continue;
9056
+ const callId = asText(asRecord2(event.data)["callId"]);
9057
+ if (!callId) corrupt(context, `tool/call at seq ${event.seq} has no call id`);
9058
+ if (ids.has(callId)) corrupt(context, `duplicate tool call id ${JSON.stringify(callId)}`);
9059
+ ids.add(callId);
9060
+ }
9061
+ return ids;
9062
+ }
9063
+ function projectDshSession(options) {
9064
+ const context = {
9065
+ sessionId: options.header.id,
9066
+ sourcePath: options.sourcePath,
9067
+ attachmentsRoot: options.attachmentsRoot
9068
+ };
9069
+ const projector = new DshProjector(
9070
+ context,
9071
+ options.header.seedLength ?? 0,
9072
+ collectCanonicalCallIds(options.events, context)
9073
+ );
9074
+ const projection = projector.project(options.events);
9075
+ return {
9076
+ ...projection,
9077
+ updatedAt: projection.updatedAt || options.header.createdAt
9078
+ };
9079
+ }
9080
+ var PARSER_REVISION = "dsh-parser-v1";
9081
+ var ENCODINGS = ["zstd", "none"];
9082
+ function encodingOfPath(sourcePath) {
9083
+ return basename9(sourcePath) === dshLogFileName("zstd") ? "zstd" : "none";
9084
+ }
9085
+ function sameFile(left, right) {
9086
+ if (left === right) return true;
9087
+ try {
9088
+ return realpathSync(left) === realpathSync(right);
9089
+ } catch {
9090
+ return false;
9091
+ }
9092
+ }
9093
+ var DshAgent = class extends FileSystemSessionSource {
9094
+ name = "dsh";
9095
+ displayName = "DSH";
9096
+ getSessionWatchPlan() {
9097
+ const dataRoot = resolveDshDataRoot();
9098
+ return {
9099
+ status: "supported",
9100
+ targets: [{ root: dataRoot, path: dshSessionsRoot(dataRoot) }]
9101
+ };
9102
+ }
9103
+ isAvailable() {
9104
+ try {
9105
+ return this.listArtifacts().length > 0;
9106
+ } catch {
9107
+ return true;
9108
+ }
9109
+ }
9110
+ listSessionSources(options) {
9111
+ const sessionsRoot = dshSessionsRoot(resolveDshDataRoot());
9112
+ const refs = [];
9113
+ const seenIds = /* @__PURE__ */ new Map();
9114
+ for (const artifact of this.listArtifacts()) {
9115
+ const stats = this.statArtifact(artifact.sourcePath);
9116
+ if (!stats) continue;
9117
+ if (!matchesScanWindow(Number(stats.mtimeMs), options)) continue;
9118
+ const header = this.enumerationStep(
9119
+ "reading session headers",
9120
+ artifact.sourcePath,
9121
+ () => readDshSessionHeader(artifact.sourcePath, artifact.encoding)
9122
+ );
9123
+ this.assertStoredIdentity(sessionsRoot, artifact, header);
9124
+ const previous = seenIds.get(header.id);
9125
+ if (previous) {
9126
+ throw new SessionScanError(this.name, "enumerating session sources", {
9127
+ cause: new DshSessionLogError(
9128
+ `duplicate DSH session id ${JSON.stringify(header.id)} in ${JSON.stringify(previous)} and ${JSON.stringify(artifact.sourcePath)}`
9129
+ ),
9130
+ sourcePath: artifact.sourcePath
9131
+ });
9132
+ }
9133
+ seenIds.set(header.id, artifact.sourcePath);
9134
+ refs.push({
9135
+ sessionId: header.id,
9136
+ sourcePath: artifact.sourcePath,
9137
+ fingerprint: JSON.stringify([
9138
+ PARSER_REVISION,
9139
+ artifact.encoding,
9140
+ ...dshFileIdentity(stats)
9141
+ ])
9142
+ });
9143
+ }
9144
+ return refs;
9145
+ }
9146
+ /** The scan window is applied during enumeration, so parsing needs no options. */
9147
+ scanSessionSource(sourcePath) {
9148
+ return getParsedSession(
9149
+ this.scanSessionSourceResult({ sessionId: "", sourcePath, fingerprint: "" })
9150
+ );
9151
+ }
9152
+ scanSessionSourceResult(source) {
9153
+ const encoding = encodingOfPath(source.sourcePath);
9154
+ const { header, projection } = this.parseSession(source.sourcePath, encoding);
9155
+ if (projection.messages.length === 0) {
9156
+ return filteredSession("no visible messages");
9157
+ }
9158
+ const head = {
9159
+ id: header.id,
9160
+ slug: `${this.name}/${header.id}`,
9161
+ title: this.resolveTitle(header, projection),
9162
+ directory: header.cwd ?? "",
9163
+ ...header.parentSession ? { parent_reference: { agentName: this.name, sessionId: header.parentSession } } : {},
9164
+ time_created: header.createdAt,
9165
+ time_updated: projection.updatedAt,
9166
+ stats: projection.stats,
9167
+ ...Object.keys(projection.modelUsage).length > 0 ? { model_usage: projection.modelUsage } : {}
9168
+ };
9169
+ this.sessionMetaMap.set(head.id, {
9170
+ id: head.id,
9171
+ sourcePath: source.sourcePath,
9172
+ sourceFingerprint: JSON.stringify([
9173
+ PARSER_REVISION,
9174
+ encoding,
9175
+ ...dshFileIdentity(statSync10(source.sourcePath, { bigint: true }))
9176
+ ]),
9177
+ sourceMtimeMs: statSync10(source.sourcePath).mtimeMs,
9178
+ encoding,
9179
+ directory: head.directory,
9180
+ title: head.title,
9181
+ createdAt: head.time_created,
9182
+ updatedAt: projection.updatedAt,
9183
+ messageCount: projection.messages.length,
9184
+ parentSessionId: header.parentSession ?? null
9185
+ });
9186
+ return parsedSession(head);
9187
+ }
9188
+ getSessionData(sessionId) {
9189
+ const meta = this.sessionMetaMap.get(sessionId);
9190
+ if (!meta) throw new Error(`Session not found: ${sessionId}`);
9191
+ const { header, projection } = this.parseSession(meta.sourcePath, meta.encoding);
9192
+ return {
9193
+ reference: { agentName: this.name, sessionId: header.id },
9194
+ id: header.id,
9195
+ title: this.resolveTitle(header, projection),
9196
+ slug: `${this.name}/${header.id}`,
9197
+ directory: header.cwd ?? "",
9198
+ ...header.parentSession ? { parent_reference: { agentName: this.name, sessionId: header.parentSession } } : {},
9199
+ version: "0",
9200
+ time_created: header.createdAt,
9201
+ time_updated: projection.updatedAt,
9202
+ stats: projection.stats,
9203
+ messages: projection.messages
9204
+ };
9205
+ }
9206
+ /**
9207
+ * A cached head with no visible messages was materialized from context-only
9208
+ * events; dropping it keeps blank sessions out of the list after a restart.
9209
+ */
9210
+ filterCachedSessions(sessions) {
9211
+ return sessions.filter((session) => session.stats.message_count > 0);
9212
+ }
9213
+ parseSession(sourcePath, encoding) {
9214
+ const dataRoot = resolveDshDataRoot();
9215
+ const { header, events } = readDshSessionLog(sourcePath, encoding);
9216
+ return {
9217
+ header,
9218
+ projection: projectDshSession({
9219
+ header,
9220
+ events,
9221
+ sourcePath,
9222
+ attachmentsRoot: dshAttachmentsRoot(dataRoot)
9223
+ })
9224
+ };
9225
+ }
9226
+ resolveTitle(header, projection) {
9227
+ return resolveSessionTitle(projection.title, null, basenameTitle(header.cwd ?? null));
9228
+ }
9229
+ /**
9230
+ * Walk `sessions/<project>/<session>/` and reject the layouts DSH itself
9231
+ * refuses: a flat pre-directory artifact, and a root or session directory
9232
+ * holding both physical encodings (which one is current is unknowable).
9233
+ */
9234
+ listArtifacts() {
9235
+ const sessionsRoot = dshSessionsRoot(resolveDshDataRoot());
9236
+ const artifacts = [];
9237
+ let rootEncoding = null;
9238
+ for (const project of this.listDirectories(sessionsRoot, true)) {
9239
+ this.rejectLegacyLayout(project);
9240
+ for (const sessionDir of this.listDirectories(project, false)) {
9241
+ const present = ENCODINGS.filter(
9242
+ (encoding2) => existsSync12(join17(sessionDir, dshLogFileName(encoding2)))
9243
+ );
9244
+ if (present.length === 0) continue;
9245
+ if (present.length > 1) {
9246
+ this.rejectEnumeration(
9247
+ sessionDir,
9248
+ `session directory ${JSON.stringify(sessionDir)} holds both physical encodings`
9249
+ );
9250
+ }
9251
+ const encoding = present[0];
9252
+ if (rootEncoding !== null && rootEncoding !== encoding) {
9253
+ this.rejectEnumeration(
9254
+ sessionDir,
9255
+ `session root ${JSON.stringify(sessionsRoot)} mixes ${rootEncoding} and ${encoding} artifacts`
9256
+ );
9257
+ }
9258
+ rootEncoding = encoding;
9259
+ artifacts.push({ sourcePath: join17(sessionDir, dshLogFileName(encoding)), encoding });
9260
+ }
9261
+ }
9262
+ return artifacts;
9263
+ }
9264
+ listDirectories(directory, tolerateMissing) {
9265
+ try {
9266
+ return readdirSync4(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join17(directory, entry.name));
9267
+ } catch (error) {
9268
+ if (tolerateMissing && isMissingDirectory(error)) return [];
9269
+ throw new SessionScanError(this.name, "enumerating session sources", {
9270
+ cause: error,
9271
+ sourcePath: directory
9272
+ });
9273
+ }
9274
+ }
9275
+ rejectLegacyLayout(project) {
9276
+ const legacy = readdirSync4(project, { withFileTypes: true }).find(
9277
+ (entry) => entry.isFile() && (entry.name.endsWith(".jsonl") || entry.name.endsWith(".jsonl.zstd"))
9278
+ );
9279
+ if (!legacy) return;
9280
+ this.rejectEnumeration(
9281
+ join17(project, legacy.name),
9282
+ `session artifact ${JSON.stringify(join17(project, legacy.name))} uses the unsupported flat-file layout`
9283
+ );
9284
+ }
9285
+ /** The header's id and cwd must name exactly the artifact they were read from. */
9286
+ assertStoredIdentity(sessionsRoot, artifact, header) {
9287
+ let expected;
9288
+ try {
9289
+ expected = dshLogPath(sessionsRoot, header.cwd, header.id, artifact.encoding);
9290
+ } catch (error) {
9291
+ this.rejectEnumeration(
9292
+ artifact.sourcePath,
9293
+ `header id ${JSON.stringify(header.id)} cannot name a storage path`,
9294
+ error
9295
+ );
9296
+ }
9297
+ if (!sameFile(artifact.sourcePath, expected)) {
9298
+ this.rejectEnumeration(
9299
+ artifact.sourcePath,
9300
+ `header identifies ${JSON.stringify(expected)}, not ${JSON.stringify(artifact.sourcePath)}`
9301
+ );
9302
+ }
9303
+ }
9304
+ enumerationStep(stage, sourcePath, read) {
9305
+ try {
9306
+ return read();
9307
+ } catch (error) {
9308
+ throw new SessionScanError(this.name, stage, { cause: error, sourcePath });
9309
+ }
9310
+ }
9311
+ statArtifact(sourcePath) {
9312
+ try {
9313
+ return statSync10(sourcePath, { bigint: true });
9314
+ } catch (error) {
9315
+ if (isMissingDirectory(error)) return null;
9316
+ throw new SessionScanError(this.name, "reading session source metadata", {
9317
+ cause: error,
9318
+ sourcePath
9319
+ });
9320
+ }
9321
+ }
9322
+ rejectEnumeration(sourcePath, reason, cause) {
9323
+ throw new SessionScanError(this.name, "enumerating session sources", {
9324
+ cause: new DshSessionLogError(reason, cause ? { cause } : void 0),
9325
+ sourcePath
9326
+ });
9327
+ }
9328
+ };
9329
+ function isMissingDirectory(error) {
9330
+ if (typeof error !== "object" || error === null || !("code" in error)) return false;
9331
+ const code = error.code;
9332
+ return code === "ENOENT" || code === "ENOTDIR";
9333
+ }
8057
9334
  registerAgent({
8058
9335
  icon: "/icon/agent/claudecode.svg",
8059
9336
  iconColored: true,
@@ -8111,6 +9388,16 @@ registerAgent({
8111
9388
  toolStrategy: "custom",
8112
9389
  create: () => new PiAgent()
8113
9390
  });
9391
+ registerAgent({
9392
+ icon: "/icon/agent/dsh.svg",
9393
+ iconColored: true,
9394
+ resolveDataRoot: resolveDshDataRoot,
9395
+ // The DSH launcher hands inner arguments to a profile, so no single resume
9396
+ // command holds across installations.
9397
+ resumeCommandPrefix: null,
9398
+ toolStrategy: "custom",
9399
+ create: () => new DshAgent()
9400
+ });
8114
9401
  registerAgent({
8115
9402
  icon: "/icon/agent/cursor.svg",
8116
9403
  resolveDataRoot: resolveCursorDataRoot,
@@ -8659,15 +9946,15 @@ var SAMPLE_DASHBOARD_DATA = {
8659
9946
  // ../core/dist/index.mjs
8660
9947
  import { availableParallelism } from "os";
8661
9948
  import { Worker } from "worker_threads";
8662
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
9949
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
8663
9950
  import { spawnSync } from "child_process";
8664
9951
  import * as os from "os";
8665
9952
  import * as path from "path";
8666
- import { createHash as createHash3 } from "crypto";
8667
- import { resolve as resolve2, sep } from "path";
9953
+ import { createHash as createHash4 } from "crypto";
9954
+ import { resolve as resolve3, sep } from "path";
8668
9955
  import { existsSync as existsSync32, rmSync as rmSync2, unlinkSync } from "fs";
8669
9956
  import { existsSync as existsSync22 } from "fs";
8670
- import { join as join15 } from "path";
9957
+ import { join as join18 } from "path";
8671
9958
  import { homedir as homedir22 } from "os";
8672
9959
  import { createHash as createHash22 } from "crypto";
8673
9960
  import { randomUUID } from "crypto";
@@ -8681,11 +9968,11 @@ function fallbackDisplayName(input) {
8681
9968
  }
8682
9969
  var realFs = {
8683
9970
  exists(path2) {
8684
- return existsSync12(path2);
9971
+ return existsSync13(path2);
8685
9972
  },
8686
9973
  readText(path2) {
8687
9974
  try {
8688
- return readFileSync8(path2, "utf8");
9975
+ return readFileSync10(path2, "utf8");
8689
9976
  } catch {
8690
9977
  return null;
8691
9978
  }
@@ -8830,7 +10117,7 @@ function projectIdentityProjection(identity, resolverRevision, inputs) {
8830
10117
  return {
8831
10118
  identity,
8832
10119
  resolverRevision,
8833
- inputSignature: createHash3("sha256").update(JSON.stringify(inputs)).digest("hex")
10120
+ inputSignature: createHash4("sha256").update(JSON.stringify(inputs)).digest("hex")
8834
10121
  };
8835
10122
  }
8836
10123
  function loose() {
@@ -8955,7 +10242,7 @@ function isPathScopeMatch(queryPath, sessionPath) {
8955
10242
  return session === queryPath || session.startsWith(queryPath + "/") || queryPath.startsWith(session + "/");
8956
10243
  }
8957
10244
  function normalizeScopePath(path2) {
8958
- return resolve2(path2).replaceAll(sep, "/");
10245
+ return resolve3(path2).replaceAll(sep, "/");
8959
10246
  }
8960
10247
  var SMART_TAG_CLASSIFIER_REVISION = "smart-tags-v1";
8961
10248
  var TAG_ORDER = [
@@ -9046,12 +10333,12 @@ function hasEditedDocPath(value) {
9046
10333
  return Object.values(record).some(hasEditedDocPath);
9047
10334
  }
9048
10335
  var WORKER_LOG_MESSAGE_TYPE = "codesesh.worker-log";
9049
- function isRecord(value) {
10336
+ function isRecord3(value) {
9050
10337
  return value != null && typeof value === "object" && !Array.isArray(value);
9051
10338
  }
9052
10339
  function isWorkerLogMessage(value) {
9053
- if (!isRecord(value) || value.type !== WORKER_LOG_MESSAGE_TYPE) return false;
9054
- return typeof value.ts === "string" && value.ts.length > 0 && (value.level === "debug" || value.level === "info" || value.level === "warn" || value.level === "error") && typeof value.event === "string" && value.event.length > 0 && Number.isSafeInteger(value.pid) && Number(value.pid) > 0 && Number.isSafeInteger(value.threadId) && Number(value.threadId) >= 0 && isRecord(value.data);
10340
+ if (!isRecord3(value) || value.type !== WORKER_LOG_MESSAGE_TYPE) return false;
10341
+ return typeof value.ts === "string" && value.ts.length > 0 && (value.level === "debug" || value.level === "info" || value.level === "warn" || value.level === "error") && typeof value.event === "string" && value.event.length > 0 && Number.isSafeInteger(value.pid) && Number(value.pid) > 0 && Number.isSafeInteger(value.threadId) && Number(value.threadId) >= 0 && isRecord3(value.data);
9055
10342
  }
9056
10343
  var CODEX_PATCH_TYPES = /* @__PURE__ */ new Set([
9057
10344
  "edit_file",
@@ -9283,13 +10570,13 @@ function setSchemaEnsuredPath(path2) {
9283
10570
  schemaEnsuredPath = path2;
9284
10571
  }
9285
10572
  function getCacheDir2() {
9286
- return join15(homedir22(), ".cache", "codesesh");
10573
+ return join18(homedir22(), ".cache", "codesesh");
9287
10574
  }
9288
10575
  function getCachePath2() {
9289
- return join15(getCacheDir2(), CACHE_FILENAME);
10576
+ return join18(getCacheDir2(), CACHE_FILENAME);
9290
10577
  }
9291
10578
  function getLegacyCachePath() {
9292
- return join15(getCacheDir2(), LEGACY_CACHE_FILENAME);
10579
+ return join18(getCacheDir2(), LEGACY_CACHE_FILENAME);
9293
10580
  }
9294
10581
  function hasCacheStorage() {
9295
10582
  return existsSync22(getCachePath2());
@@ -14829,4 +16116,4 @@ export {
14829
16116
  executeSessionSearch,
14830
16117
  filterSessionSearchCandidates
14831
16118
  };
14832
- //# sourceMappingURL=chunk-JHX5OLL4.js.map
16119
+ //# sourceMappingURL=chunk-VAUC2W7I.js.map