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.
- package/README.md +2 -1
- package/dist/{chunk-OFTTM3UD.js → chunk-PROMURPM.js} +2 -2
- package/dist/{chunk-JHX5OLL4.js → chunk-VAUC2W7I.js} +1303 -16
- package/dist/chunk-VAUC2W7I.js.map +1 -0
- package/dist/{dist-N2PQATP2.js → dist-6QTGTYZT.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/scan-refresh-worker.js +2 -2
- package/dist/search-index-worker.js +2 -2
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/SessionDetail-BnpvDwtH.js +54 -0
- package/dist/web/assets/{index-BgWoUD_4.js → index-99ZxpV2y.js} +2 -2
- package/dist/web/icon/agent/dsh.svg +3 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/chunk-JHX5OLL4.js.map +0 -1
- package/dist/web/assets/SessionDetail-lQz99ARH.js +0 -54
- /package/dist/{chunk-OFTTM3UD.js.map → chunk-PROMURPM.js.map} +0 -0
- /package/dist/{dist-N2PQATP2.js.map → dist-6QTGTYZT.js.map} +0 -0
|
@@ -121,7 +121,7 @@ function normalizeMessageParts(value) {
|
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
// ../core/dist/chunk-
|
|
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
|
|
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
|
|
8667
|
-
import { resolve as
|
|
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
|
|
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
|
|
9971
|
+
return existsSync13(path2);
|
|
8685
9972
|
},
|
|
8686
9973
|
readText(path2) {
|
|
8687
9974
|
try {
|
|
8688
|
-
return
|
|
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:
|
|
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
|
|
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
|
|
10336
|
+
function isRecord3(value) {
|
|
9050
10337
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
9051
10338
|
}
|
|
9052
10339
|
function isWorkerLogMessage(value) {
|
|
9053
|
-
if (!
|
|
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 &&
|
|
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
|
|
10573
|
+
return join18(homedir22(), ".cache", "codesesh");
|
|
9287
10574
|
}
|
|
9288
10575
|
function getCachePath2() {
|
|
9289
|
-
return
|
|
10576
|
+
return join18(getCacheDir2(), CACHE_FILENAME);
|
|
9290
10577
|
}
|
|
9291
10578
|
function getLegacyCachePath() {
|
|
9292
|
-
return
|
|
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-
|
|
16119
|
+
//# sourceMappingURL=chunk-VAUC2W7I.js.map
|