@adhdev/daemon-core 0.9.45 → 0.9.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8160,29 +8160,6 @@ function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
8160
8160
  return { sessions: [], hasMore: false };
8161
8161
  }
8162
8162
  }
8163
- function normalizeCanonicalHermesMessageContent(content) {
8164
- if (typeof content === "string") return content.trim();
8165
- if (content == null) return "";
8166
- try {
8167
- return JSON.stringify(content).trim();
8168
- } catch {
8169
- return String(content).trim();
8170
- }
8171
- }
8172
- function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
8173
- const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
8174
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
8175
- const stringTimestamp = typeof message.ts === "string" ? Date.parse(message.ts) : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
8176
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
8177
- return fallbackTs;
8178
- }
8179
- function extractTimestampValue(value) {
8180
- const numericTimestamp = Number(value || 0);
8181
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
8182
- const stringTimestamp = typeof value === "string" ? Date.parse(value) : NaN;
8183
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
8184
- return 0;
8185
- }
8186
8163
  function readExistingSessionStartRecord(agentType, historySessionId) {
8187
8164
  try {
8188
8165
  const dir = path7.join(HISTORY_DIR, agentType);
@@ -8228,569 +8205,77 @@ function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
8228
8205
  return false;
8229
8206
  }
8230
8207
  }
8231
- function buildHermesNativeHistoryRecords(historySessionId) {
8232
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8233
- if (!normalizedSessionId) return null;
8234
- try {
8235
- const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
8236
- if (!fs3.existsSync(sessionFilePath)) return null;
8237
- const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
8238
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
8239
- const records = [];
8240
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
8241
- for (const message of canonicalMessages) {
8242
- const role = String(message.role || "").trim();
8243
- const content = normalizeCanonicalHermesMessageContent(message.content);
8244
- if (!content) continue;
8245
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
8246
- fallbackTs = receivedAt + 1;
8247
- if (role === "user" || role === "assistant") {
8248
- records.push({
8249
- ts: new Date(receivedAt).toISOString(),
8250
- receivedAt,
8251
- role,
8252
- content,
8253
- kind: "standard",
8254
- agent: "hermes-cli",
8255
- historySessionId: normalizedSessionId
8256
- });
8257
- continue;
8258
- }
8259
- if (role === "tool") {
8260
- records.push({
8261
- ts: new Date(receivedAt).toISOString(),
8262
- receivedAt,
8263
- role: "assistant",
8264
- content,
8265
- kind: "tool",
8266
- senderName: "Tool",
8267
- agent: "hermes-cli",
8268
- historySessionId: normalizedSessionId
8269
- });
8270
- }
8271
- }
8272
- return records;
8273
- } catch {
8274
- return null;
8275
- }
8276
- }
8277
- function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
8278
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8279
- if (!normalizedSessionId) return false;
8280
- try {
8281
- const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
8282
- if (!fs3.existsSync(sessionFilePath)) return false;
8283
- const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
8284
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
8285
- const dir = path7.join(HISTORY_DIR, "hermes-cli");
8286
- fs3.mkdirSync(dir, { recursive: true });
8287
- const existingSessionStart = readExistingSessionStartRecord("hermes-cli", normalizedSessionId);
8288
- const records = [];
8289
- if (existingSessionStart) {
8290
- records.push({
8291
- ...existingSessionStart,
8292
- historySessionId: normalizedSessionId
8293
- });
8294
- }
8295
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
8296
- for (const message of canonicalMessages) {
8297
- const role = String(message.role || "").trim();
8298
- const content = normalizeCanonicalHermesMessageContent(message.content);
8299
- if (!content) continue;
8300
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
8301
- fallbackTs = receivedAt + 1;
8302
- if (role === "user") {
8303
- records.push({
8304
- ts: new Date(receivedAt).toISOString(),
8305
- receivedAt,
8306
- role: "user",
8307
- content,
8308
- kind: "standard",
8309
- agent: "hermes-cli",
8310
- historySessionId: normalizedSessionId
8311
- });
8312
- continue;
8313
- }
8314
- if (role === "assistant") {
8315
- records.push({
8316
- ts: new Date(receivedAt).toISOString(),
8317
- receivedAt,
8318
- role: "assistant",
8319
- content,
8320
- kind: "standard",
8321
- agent: "hermes-cli",
8322
- historySessionId: normalizedSessionId
8323
- });
8324
- continue;
8325
- }
8326
- if (role === "tool") {
8327
- records.push({
8328
- ts: new Date(receivedAt).toISOString(),
8329
- receivedAt,
8330
- role: "assistant",
8331
- content,
8332
- kind: "tool",
8333
- senderName: "Tool",
8334
- agent: "hermes-cli",
8335
- historySessionId: normalizedSessionId
8336
- });
8337
- }
8338
- }
8339
- return rewriteCanonicalSavedHistory("hermes-cli", normalizedSessionId, records);
8340
- } catch {
8341
- return false;
8342
- }
8343
- }
8344
- function resolveClaudeProjectTranscriptPath(historySessionId, workspace) {
8345
- const claudeProjectsDir = path7.join(os5.homedir(), ".claude", "projects");
8346
- if (!fs3.existsSync(claudeProjectsDir)) return null;
8347
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
8348
- if (normalizedWorkspace) {
8349
- const directPath = path7.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, "-"), `${historySessionId}.jsonl`);
8350
- if (fs3.existsSync(directPath)) return directPath;
8351
- }
8352
- const stack = [claudeProjectsDir];
8353
- while (stack.length > 0) {
8354
- const current = stack.pop();
8355
- if (!current) continue;
8356
- for (const entry of fs3.readdirSync(current, { withFileTypes: true })) {
8357
- const entryPath = path7.join(current, entry.name);
8358
- if (entry.isDirectory()) {
8359
- stack.push(entryPath);
8360
- continue;
8361
- }
8362
- if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
8363
- return entryPath;
8364
- }
8365
- }
8366
- }
8367
- return null;
8368
- }
8369
- function extractClaudeAssistantContentParts(content) {
8370
- if (typeof content === "string") {
8371
- const trimmed = content.trim();
8372
- return trimmed ? [{ content: trimmed, kind: "standard", role: "assistant" }] : [];
8373
- }
8374
- if (!Array.isArray(content)) return [];
8375
- const parts = [];
8376
- for (const block of content) {
8377
- if (!block || typeof block !== "object") continue;
8378
- const record = block;
8379
- const type = String(record.type || "").trim();
8380
- if (type === "text") {
8381
- const text = String(record.text || "").trim();
8382
- if (text) parts.push({ content: text, kind: "standard", role: "assistant" });
8383
- continue;
8384
- }
8385
- if (type === "tool_use") {
8386
- const name = String(record.name || "").trim() || "Tool";
8387
- const input = record.input && typeof record.input === "object" ? record.input : null;
8388
- const command = input ? String(input.command || "").trim() : "";
8389
- const summary = command ? `${name}: ${command}` : name;
8390
- if (summary) parts.push({ content: summary, kind: "tool", senderName: "Tool", role: "assistant" });
8391
- }
8392
- }
8393
- return parts;
8394
- }
8395
- function extractClaudeUserContentParts(content) {
8396
- if (typeof content === "string") {
8397
- const trimmed = content.trim();
8398
- return trimmed ? [{ role: "user", content: trimmed, kind: "standard" }] : [];
8399
- }
8400
- if (!Array.isArray(content)) return [];
8401
- const parts = [];
8402
- for (const block of content) {
8403
- if (!block || typeof block !== "object") continue;
8404
- const record = block;
8405
- const type = String(record.type || "").trim();
8406
- if (type === "text") {
8407
- const text = String(record.text || "").trim();
8408
- if (text) parts.push({ role: "user", content: text, kind: "standard" });
8409
- continue;
8410
- }
8411
- if (type === "tool_result") {
8412
- const rawContent = record.content;
8413
- const text = typeof rawContent === "string" ? rawContent.trim() : Array.isArray(rawContent) ? rawContent.map((entry) => {
8414
- if (typeof entry === "string") return entry.trim();
8415
- if (!entry || typeof entry !== "object") return "";
8416
- const nested = entry;
8417
- if (typeof nested.text === "string") return nested.text.trim();
8418
- if (typeof nested.content === "string") return nested.content.trim();
8419
- return "";
8420
- }).filter(Boolean).join("\n") : "";
8421
- if (text) parts.push({ role: "assistant", content: text, kind: "tool", senderName: "Tool" });
8422
- }
8423
- }
8424
- return parts;
8425
- }
8426
- function buildClaudeNativeHistoryRecords(historySessionId, workspace) {
8427
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8428
- if (!normalizedSessionId) return null;
8429
- try {
8430
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
8431
- if (!transcriptPath) return null;
8432
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
8433
- const records = [];
8434
- let fallbackTs = Date.now();
8435
- for (const line of lines) {
8436
- let parsed = null;
8437
- try {
8438
- parsed = JSON.parse(line);
8439
- } catch {
8440
- parsed = null;
8441
- }
8442
- if (!parsed) continue;
8443
- const parsedSessionId = String(parsed.sessionId || "").trim();
8444
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
8445
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
8446
- fallbackTs = receivedAt + 1;
8447
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
8448
- if (records.length === 0 && parsedWorkspace) {
8449
- records.push({
8450
- ts: new Date(receivedAt).toISOString(),
8451
- receivedAt,
8452
- role: "system",
8453
- kind: "session_start",
8454
- content: parsedWorkspace,
8455
- agent: "claude-cli",
8456
- historySessionId: normalizedSessionId,
8457
- workspace: parsedWorkspace
8458
- });
8459
- }
8460
- const type = String(parsed.type || "").trim();
8461
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
8462
- if (type === "user" && message) {
8463
- for (const part of extractClaudeUserContentParts(message.content)) {
8464
- records.push({
8465
- ts: new Date(receivedAt).toISOString(),
8466
- receivedAt,
8467
- role: part.role,
8468
- content: part.content,
8469
- kind: part.kind,
8470
- senderName: part.senderName,
8471
- agent: "claude-cli",
8472
- historySessionId: normalizedSessionId
8473
- });
8474
- }
8475
- continue;
8476
- }
8477
- if (type === "assistant" && message) {
8478
- for (const part of extractClaudeAssistantContentParts(message.content)) {
8479
- records.push({
8480
- ts: new Date(receivedAt).toISOString(),
8481
- receivedAt,
8482
- role: "assistant",
8483
- content: part.content,
8484
- kind: part.kind,
8485
- senderName: part.senderName,
8486
- agent: "claude-cli",
8487
- historySessionId: normalizedSessionId
8488
- });
8489
- }
8490
- }
8491
- }
8492
- return records;
8493
- } catch {
8494
- return null;
8495
- }
8496
- }
8497
- function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
8498
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8499
- if (!normalizedSessionId) return false;
8500
- try {
8501
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
8502
- if (!transcriptPath) return false;
8503
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
8504
- const records = [];
8505
- const existingSessionStart = readExistingSessionStartRecord("claude-cli", normalizedSessionId);
8506
- if (existingSessionStart) {
8507
- records.push({
8508
- ...existingSessionStart,
8509
- historySessionId: normalizedSessionId
8510
- });
8511
- }
8512
- let fallbackTs = Date.now();
8513
- for (const line of lines) {
8514
- let parsed = null;
8515
- try {
8516
- parsed = JSON.parse(line);
8517
- } catch {
8518
- parsed = null;
8519
- }
8520
- if (!parsed) continue;
8521
- const parsedSessionId = String(parsed.sessionId || "").trim();
8522
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
8523
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
8524
- fallbackTs = receivedAt + 1;
8525
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
8526
- if (records.length === 0 && parsedWorkspace) {
8527
- records.push({
8528
- ts: new Date(receivedAt).toISOString(),
8529
- receivedAt,
8530
- role: "system",
8531
- kind: "session_start",
8532
- content: parsedWorkspace,
8533
- agent: "claude-cli",
8534
- historySessionId: normalizedSessionId,
8535
- workspace: parsedWorkspace
8536
- });
8537
- }
8538
- const type = String(parsed.type || "").trim();
8539
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
8540
- if (type === "user" && message) {
8541
- for (const part of extractClaudeUserContentParts(message.content)) {
8542
- records.push({
8543
- ts: new Date(receivedAt).toISOString(),
8544
- receivedAt,
8545
- role: part.role,
8546
- content: part.content,
8547
- kind: part.kind,
8548
- senderName: part.senderName,
8549
- agent: "claude-cli",
8550
- historySessionId: normalizedSessionId
8551
- });
8552
- }
8553
- continue;
8554
- }
8555
- if (type === "assistant" && message) {
8556
- for (const part of extractClaudeAssistantContentParts(message.content)) {
8557
- records.push({
8558
- ts: new Date(receivedAt).toISOString(),
8559
- receivedAt,
8560
- role: "assistant",
8561
- content: part.content,
8562
- kind: part.kind,
8563
- senderName: part.senderName,
8564
- agent: "claude-cli",
8565
- historySessionId: normalizedSessionId
8566
- });
8567
- }
8568
- }
8569
- }
8570
- return rewriteCanonicalSavedHistory("claude-cli", normalizedSessionId, records);
8571
- } catch {
8572
- return false;
8573
- }
8574
- }
8575
- function isUuidLikeSessionId(sessionId) {
8576
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
8208
+ function getNativeHistoryScriptName(canonicalHistory, key) {
8209
+ const configured = canonicalHistory?.scripts?.[key];
8210
+ if (typeof configured === "string" && configured.trim()) return configured.trim();
8211
+ return key === "readSession" ? "readNativeHistory" : "listNativeHistory";
8577
8212
  }
8578
- function readCodexSessionMeta(filePath) {
8579
- try {
8580
- const firstLine = fs3.readFileSync(filePath, "utf-8").split("\n").find(Boolean);
8581
- if (!firstLine) return null;
8582
- const parsed = JSON.parse(firstLine);
8583
- if (String(parsed.type || "") !== "session_meta") return null;
8584
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
8585
- return payload;
8586
- } catch {
8587
- return null;
8588
- }
8213
+ function getProviderNativeHistoryScript(scripts, canonicalHistory, key) {
8214
+ if (!canonicalHistory?.scripts) return null;
8215
+ const fn = scripts?.[getNativeHistoryScriptName(canonicalHistory, key)];
8216
+ return typeof fn === "function" ? fn : null;
8589
8217
  }
8590
- function resolveCodexSessionTranscriptPath(historySessionId, workspace) {
8218
+ function normalizeProviderNativeHistoryRecords(agentType, historySessionId, records) {
8219
+ if (!Array.isArray(records)) return [];
8591
8220
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8592
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
8593
- const sessionsDir = path7.join(os5.homedir(), ".codex", "sessions");
8594
- if (!fs3.existsSync(sessionsDir)) return null;
8595
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
8596
- const candidates = [];
8597
- const stack = [sessionsDir];
8598
- while (stack.length > 0) {
8599
- const current = stack.pop();
8600
- if (!current) continue;
8601
- let entries = [];
8602
- try {
8603
- entries = fs3.readdirSync(current, { withFileTypes: true });
8604
- } catch {
8605
- continue;
8606
- }
8607
- for (const entry of entries) {
8608
- const entryPath = path7.join(current, entry.name);
8609
- if (entry.isDirectory()) {
8610
- stack.push(entryPath);
8611
- continue;
8612
- }
8613
- if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(normalizedSessionId)) continue;
8614
- const meta = readCodexSessionMeta(entryPath);
8615
- const metaSessionId = String(meta?.id || "").trim();
8616
- if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
8617
- const metaWorkspace = String(meta?.cwd || "").trim();
8618
- let mtimeMs = 0;
8619
- try {
8620
- mtimeMs = fs3.statSync(entryPath).mtimeMs;
8621
- } catch {
8622
- }
8623
- candidates.push({
8624
- path: entryPath,
8625
- mtimeMs,
8626
- workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
8627
- metaMatches: metaSessionId === normalizedSessionId
8628
- });
8629
- }
8630
- }
8631
- candidates.sort((a, b) => Number(b.workspaceMatches) - Number(a.workspaceMatches) || Number(b.metaMatches) - Number(a.metaMatches) || b.mtimeMs - a.mtimeMs);
8632
- return candidates[0]?.path || null;
8633
- }
8634
- function flattenCodexContent(content) {
8635
- if (typeof content === "string") return content.trim();
8636
- if (content == null) return "";
8637
- if (Array.isArray(content)) {
8638
- return content.map((entry) => flattenCodexContent(entry)).filter(Boolean).join("\n").trim();
8639
- }
8640
- if (typeof content === "object") {
8641
- const record = content;
8642
- if (typeof record.text === "string") return record.text.trim();
8643
- if (typeof record.content === "string" || Array.isArray(record.content)) return flattenCodexContent(record.content);
8644
- if (typeof record.output === "string") return record.output.trim();
8645
- if (typeof record.message === "string") return record.message.trim();
8646
- }
8647
- return "";
8648
- }
8649
- function summarizeCodexToolCall(payload) {
8650
- const name = String(payload.name || payload.type || "tool").trim() || "tool";
8651
- const rawArguments = payload.arguments ?? payload.input;
8652
- let argumentValue = "";
8653
- if (typeof rawArguments === "string") {
8654
- const trimmed = rawArguments.trim();
8655
- try {
8656
- const parsed = JSON.parse(trimmed);
8657
- argumentValue = summarizeCodexToolArguments(parsed);
8658
- } catch {
8659
- argumentValue = trimmed;
8660
- }
8661
- } else {
8662
- argumentValue = summarizeCodexToolArguments(rawArguments);
8663
- }
8664
- return argumentValue ? `${name}: ${argumentValue}` : name;
8665
- }
8666
- function summarizeCodexToolArguments(value) {
8667
- if (typeof value === "string") return value.trim();
8668
- if (Array.isArray(value)) return value.map((entry) => String(entry)).join(" ").trim();
8669
- if (!value || typeof value !== "object") return "";
8670
- const record = value;
8671
- const direct = record.command || record.cmd || record.query || record.path || record.prompt;
8672
- if (typeof direct === "string") return direct.trim();
8673
- if (Array.isArray(direct)) return direct.map((entry) => String(entry)).join(" ").trim();
8674
- try {
8675
- return JSON.stringify(record).trim();
8676
- } catch {
8677
- return "";
8678
- }
8221
+ return records.map((record) => sanitizeHistoryMessage(agentType, {
8222
+ ts: typeof record?.ts === "string" ? record.ts : new Date(Number(record?.receivedAt) || Date.now()).toISOString(),
8223
+ receivedAt: Number(record?.receivedAt) || Date.parse(record?.ts || "") || Date.now(),
8224
+ role: record?.role,
8225
+ content: String(record?.content || ""),
8226
+ kind: record?.kind || (record?.role === "system" ? "session_start" : "standard"),
8227
+ senderName: record?.senderName,
8228
+ agent: agentType,
8229
+ instanceId: record?.instanceId,
8230
+ historySessionId: normalizeSavedHistorySessionId(record?.historySessionId || normalizedSessionId),
8231
+ sessionTitle: record?.sessionTitle,
8232
+ workspace: record?.workspace
8233
+ })).filter(Boolean);
8234
+ }
8235
+ function callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, historySessionId, workspace) {
8236
+ const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, "readSession");
8237
+ if (!fn) return null;
8238
+ const result = fn({
8239
+ agentType,
8240
+ sessionId: historySessionId,
8241
+ historySessionId,
8242
+ workspace,
8243
+ format: canonicalHistory?.format,
8244
+ watchPath: canonicalHistory?.watchPath,
8245
+ args: { sessionId: historySessionId, historySessionId, workspace }
8246
+ });
8247
+ if (!result || typeof result !== "object") return null;
8248
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, result.messages || result.records);
8249
+ if (records.length === 0) return null;
8250
+ return {
8251
+ records,
8252
+ sourcePath: typeof result.sourcePath === "string" ? result.sourcePath : "",
8253
+ sourceMtimeMs: Number(result.sourceMtimeMs) || 0
8254
+ };
8679
8255
  }
8680
- function codexToolOutputContent(payload) {
8681
- const output = payload.output ?? payload.result ?? payload.content;
8682
- const text = flattenCodexContent(output);
8683
- if (text) return text;
8684
- if (output && typeof output === "object") {
8685
- try {
8686
- return JSON.stringify(output).trim();
8687
- } catch {
8688
- return "";
8689
- }
8690
- }
8691
- return "";
8256
+ function buildNativeHistoryReadResult(agentType, canonicalHistory, scripts, historySessionId, workspace) {
8257
+ const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
8258
+ if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
8259
+ return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
8692
8260
  }
8693
- function buildCodexNativeHistoryRecords(historySessionId, workspace) {
8261
+ function materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts) {
8694
8262
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8695
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
8696
- try {
8697
- const transcriptPath = resolveCodexSessionTranscriptPath(normalizedSessionId, workspace);
8698
- if (!transcriptPath) return null;
8699
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
8700
- const records = [];
8701
- let fallbackTs = Date.now();
8702
- for (const line of lines) {
8703
- let parsed = null;
8704
- try {
8705
- parsed = JSON.parse(line);
8706
- } catch {
8707
- parsed = null;
8708
- }
8709
- if (!parsed) continue;
8710
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
8711
- fallbackTs = receivedAt + 1;
8712
- const type = String(parsed.type || "").trim();
8713
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
8714
- if (!payload) continue;
8715
- if (type === "session_meta") {
8716
- const parsedSessionId = String(payload.id || "").trim();
8717
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
8718
- const parsedWorkspace = String(payload.cwd || workspace || "").trim();
8719
- if (records.length === 0 && parsedWorkspace) {
8720
- records.push({
8721
- ts: new Date(receivedAt).toISOString(),
8722
- receivedAt,
8723
- role: "system",
8724
- kind: "session_start",
8725
- content: parsedWorkspace,
8726
- agent: "codex-cli",
8727
- historySessionId: normalizedSessionId,
8728
- workspace: parsedWorkspace
8729
- });
8730
- }
8731
- continue;
8732
- }
8733
- if (type !== "response_item") continue;
8734
- const payloadType = String(payload.type || "").trim();
8735
- if (payloadType === "message") {
8736
- const role = String(payload.role || "").trim();
8737
- if (role !== "user" && role !== "assistant") continue;
8738
- const content = flattenCodexContent(payload.content);
8739
- if (!content) continue;
8740
- records.push({
8741
- ts: new Date(receivedAt).toISOString(),
8742
- receivedAt,
8743
- role,
8744
- content,
8745
- kind: "standard",
8746
- agent: "codex-cli",
8747
- historySessionId: normalizedSessionId
8748
- });
8749
- continue;
8750
- }
8751
- if (payloadType === "function_call" || payloadType === "custom_tool_call") {
8752
- const content = summarizeCodexToolCall(payload);
8753
- if (!content) continue;
8754
- records.push({
8755
- ts: new Date(receivedAt).toISOString(),
8756
- receivedAt,
8757
- role: "assistant",
8758
- content,
8759
- kind: "tool",
8760
- senderName: "Tool",
8761
- agent: "codex-cli",
8762
- historySessionId: normalizedSessionId
8763
- });
8764
- continue;
8765
- }
8766
- if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
8767
- const content = codexToolOutputContent(payload);
8768
- if (!content) continue;
8769
- records.push({
8770
- ts: new Date(receivedAt).toISOString(),
8771
- receivedAt,
8772
- role: "assistant",
8773
- content,
8774
- kind: "tool",
8775
- senderName: "Tool",
8776
- agent: "codex-cli",
8777
- historySessionId: normalizedSessionId
8778
- });
8779
- }
8780
- }
8781
- return records;
8782
- } catch {
8783
- return null;
8784
- }
8263
+ if (!normalizedSessionId) return false;
8264
+ const nativeResult = callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
8265
+ const nativeRecords = nativeResult?.records || [];
8266
+ if (nativeRecords.length === 0) return false;
8267
+ const normalizedRecords = nativeRecords.map((record) => ({
8268
+ ...record,
8269
+ agent: agentType,
8270
+ historySessionId: normalizedSessionId
8271
+ }));
8272
+ const existingSessionStart = readExistingSessionStartRecord(agentType, normalizedSessionId);
8273
+ const records = existingSessionStart && normalizedRecords[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId, agent: agentType }, ...normalizedRecords] : normalizedRecords;
8274
+ return rewriteCanonicalSavedHistory(agentType, normalizedSessionId, records);
8785
8275
  }
8786
- function rebuildCodexSavedHistoryFromNativeSession(historySessionId, workspace) {
8787
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
8788
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return false;
8789
- const records = buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
8790
- if (!records || records.length === 0) return false;
8791
- const existingSessionStart = readExistingSessionStartRecord("codex-cli", normalizedSessionId);
8792
- const recordsToWrite = existingSessionStart && records[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId }, ...records] : records;
8793
- return rewriteCanonicalSavedHistory("codex-cli", normalizedSessionId, recordsToWrite);
8276
+ function materializeProviderNativeHistory(agentType, canonicalHistory, historySessionId, workspace, scripts) {
8277
+ if (!canonicalHistory || canonicalHistory.mode !== "materialized-mirror") return false;
8278
+ return materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts);
8794
8279
  }
8795
8280
  function isNativeSourceCanonicalHistory(canonicalHistory) {
8796
8281
  if (!canonicalHistory) return false;
@@ -8798,21 +8283,15 @@ function isNativeSourceCanonicalHistory(canonicalHistory) {
8798
8283
  if (canonicalHistory.mode === "materialized-mirror") return false;
8799
8284
  return true;
8800
8285
  }
8801
- function buildNativeHistoryRecords(canonicalHistory, historySessionId, workspace) {
8802
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
8803
- if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
8804
- if (canonicalHistory.format === "hermes-json") return buildHermesNativeHistoryRecords(normalizedSessionId);
8805
- if (canonicalHistory.format === "claude-jsonl") return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
8806
- if (canonicalHistory.format === "codex-jsonl") return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
8807
- return null;
8808
- }
8809
8286
  function readProviderChatHistory(agentType, options = {}) {
8810
8287
  if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
8811
- const records = buildNativeHistoryRecords(options.canonicalHistory, options.historySessionId, options.workspace);
8812
- if (!records) return { messages: [], hasMore: false, source: "native-unavailable" };
8288
+ const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
8289
+ if (!nativeResult) return { messages: [], hasMore: false, source: "native-unavailable" };
8813
8290
  return {
8814
- ...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
8815
- source: "provider-native"
8291
+ ...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
8292
+ source: "provider-native",
8293
+ sourcePath: nativeResult.sourcePath,
8294
+ sourceMtimeMs: nativeResult.sourceMtimeMs
8816
8295
  };
8817
8296
  }
8818
8297
  return {
@@ -8845,68 +8324,64 @@ function buildNativeSessionSummary(agentType, historySessionId, records, sourceP
8845
8324
  sourceMtimeMs
8846
8325
  };
8847
8326
  }
8848
- function listFilesRecursive(root, predicate) {
8849
- if (!fs3.existsSync(root)) return [];
8850
- const results = [];
8851
- const stack = [root];
8852
- while (stack.length > 0) {
8853
- const current = stack.pop();
8854
- if (!current) continue;
8855
- let entries = [];
8856
- try {
8857
- entries = fs3.readdirSync(current, { withFileTypes: true });
8858
- } catch {
8859
- continue;
8860
- }
8861
- for (const entry of entries) {
8862
- const entryPath = path7.join(current, entry.name);
8863
- if (entry.isDirectory()) {
8864
- stack.push(entryPath);
8865
- continue;
8866
- }
8867
- if (predicate(entryPath, entry)) results.push(entryPath);
8868
- }
8869
- }
8870
- return results;
8327
+ function normalizeProviderNativeHistorySessionSummary(agentType, item) {
8328
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
8329
+ if (!historySessionId) return null;
8330
+ const sourcePath = typeof item?.sourcePath === "string" ? item.sourcePath : "";
8331
+ const sourceMtimeMs = Number(item?.sourceMtimeMs) || 0;
8332
+ const firstMessageAt = Number(item?.firstMessageAt) || sourceMtimeMs || Date.now();
8333
+ const lastMessageAt = Number(item?.lastMessageAt) || firstMessageAt;
8334
+ const messageCount = Math.max(0, Number(item?.messageCount) || 0);
8335
+ return {
8336
+ historySessionId,
8337
+ sessionTitle: typeof item?.sessionTitle === "string" ? item.sessionTitle : void 0,
8338
+ messageCount,
8339
+ firstMessageAt,
8340
+ lastMessageAt,
8341
+ preview: typeof item?.preview === "string" ? item.preview : void 0,
8342
+ workspace: typeof item?.workspace === "string" ? item.workspace : void 0,
8343
+ source: "provider-native",
8344
+ sourcePath,
8345
+ sourceMtimeMs
8346
+ };
8871
8347
  }
8872
- function collectNativeHistorySessionSummaries(agentType, canonicalHistory) {
8348
+ function collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
8349
+ const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, "listSessions");
8350
+ if (!fn) return null;
8351
+ const result = fn({
8352
+ agentType,
8353
+ format: canonicalHistory.format,
8354
+ watchPath: canonicalHistory.watchPath,
8355
+ args: {}
8356
+ });
8357
+ if (!result || typeof result !== "object") return [];
8358
+ const sessions = Array.isArray(result.sessions) ? result.sessions : [];
8873
8359
  const summaries = [];
8874
- if (canonicalHistory.format === "hermes-json") {
8875
- const root = path7.join(os5.homedir(), ".hermes", "sessions");
8876
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && /^session_.+\.json$/.test(entry.name))) {
8877
- const fileName = path7.basename(filePath);
8878
- const historySessionId = fileName.replace(/^session_/, "").replace(/\.json$/, "");
8879
- const records = buildHermesNativeHistoryRecords(historySessionId);
8880
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
8881
- if (summary) summaries.push(summary);
8882
- }
8883
- } else if (canonicalHistory.format === "claude-jsonl") {
8884
- const root = path7.join(os5.homedir(), ".claude", "projects");
8885
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
8886
- const historySessionId = path7.basename(filePath, ".jsonl");
8887
- const records = buildClaudeNativeHistoryRecords(historySessionId);
8888
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
8889
- if (summary) summaries.push(summary);
8890
- }
8891
- } else if (canonicalHistory.format === "codex-jsonl") {
8892
- const root = path7.join(os5.homedir(), ".codex", "sessions");
8893
- const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
8894
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
8895
- const meta = readCodexSessionMeta(filePath);
8896
- const historySessionId = String(meta?.id || path7.basename(filePath).match(uuidPattern)?.[1] || "").trim();
8360
+ for (const item of sessions) {
8361
+ if (Array.isArray(item?.messages || item?.records)) {
8362
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
8897
8363
  if (!historySessionId) continue;
8898
- const records = buildCodexNativeHistoryRecords(historySessionId, String(meta?.cwd || "").trim() || void 0);
8899
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
8900
- if (summary) summaries.push(summary);
8364
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, item.messages || item.records);
8365
+ const summary2 = buildNativeSessionSummary(agentType, historySessionId, records, typeof item?.sourcePath === "string" ? item.sourcePath : "");
8366
+ if (summary2) {
8367
+ if (Number(item?.sourceMtimeMs)) summary2.sourceMtimeMs = Number(item.sourceMtimeMs);
8368
+ summaries.push(summary2);
8369
+ }
8370
+ continue;
8901
8371
  }
8372
+ const summary = normalizeProviderNativeHistorySessionSummary(agentType, item);
8373
+ if (summary) summaries.push(summary);
8902
8374
  }
8903
8375
  return sortSavedHistorySessionSummaries(summaries);
8904
8376
  }
8377
+ function collectNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
8378
+ return collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) || [];
8379
+ }
8905
8380
  function listProviderHistorySessions(agentType, options = {}) {
8906
8381
  if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
8907
8382
  const offset = Math.max(0, options.offset || 0);
8908
8383
  const limit = Math.max(1, options.limit || 30);
8909
- const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory);
8384
+ const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory, options.scripts);
8910
8385
  return {
8911
8386
  sessions: summaries.slice(offset, offset + limit),
8912
8387
  hasMore: offset + limit < summaries.length,
@@ -11539,7 +11014,8 @@ async function handleChatHistory(h, args) {
11539
11014
  offset: offset || 0,
11540
11015
  limit: limit || 30,
11541
11016
  excludeRecentCount,
11542
- historyBehavior: provider?.historyBehavior
11017
+ historyBehavior: provider?.historyBehavior,
11018
+ scripts: provider?.scripts
11543
11019
  });
11544
11020
  return { success: true, ...result, agent: agentStr };
11545
11021
  } catch (e) {
@@ -14013,13 +13489,8 @@ var CliProviderInstance = class {
14013
13489
  historyWriter;
14014
13490
  runtimeMessages = [];
14015
13491
  lastPersistedHistoryMessages = [];
14016
- lastCanonicalHermesSyncMtimeMs = 0;
14017
- lastCanonicalHermesExistCheckAt = 0;
14018
- lastCanonicalHermesWatchPath = void 0;
14019
- lastCanonicalClaudeRebuildMtimeMs = 0;
14020
- lastCanonicalClaudeCheckAt = 0;
14021
- lastCanonicalCodexRebuildMtimeMs = 0;
14022
- lastCanonicalCodexCheckAt = 0;
13492
+ lastNativeSourceCanonicalCheckAt = 0;
13493
+ lastNativeSourceCanonicalCacheKey = void 0;
14023
13494
  cachedSqliteDb = null;
14024
13495
  cachedSqliteDbPath = null;
14025
13496
  cachedSqliteDbMissingUntil = 0;
@@ -14720,76 +14191,44 @@ ${effect.notification.body || ""}`.trim();
14720
14191
  const canonicalHistory = this.provider.canonicalHistory;
14721
14192
  if (!canonicalHistory) return false;
14722
14193
  if (isNativeSourceCanonicalHistory(canonicalHistory)) {
14194
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir].join("\0");
14195
+ const now = Date.now();
14196
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
14197
+ return true;
14198
+ }
14199
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
14200
+ this.lastNativeSourceCanonicalCheckAt = now;
14723
14201
  const restoredHistory = readProviderChatHistory(this.type, {
14724
14202
  canonicalHistory,
14725
14203
  historySessionId: this.providerSessionId,
14726
14204
  workspace: this.workingDir,
14727
14205
  offset: 0,
14728
14206
  limit: Number.MAX_SAFE_INTEGER,
14729
- historyBehavior: this.provider.historyBehavior
14207
+ historyBehavior: this.provider.historyBehavior,
14208
+ scripts: this.provider.scripts
14730
14209
  });
14731
- if (restoredHistory.source !== "provider-native") return false;
14732
- this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
14733
- role: message.role,
14734
- content: message.content,
14735
- kind: message.kind,
14736
- senderName: message.senderName,
14737
- receivedAt: message.receivedAt
14738
- }));
14210
+ if (restoredHistory.source === "provider-native") {
14211
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
14212
+ role: message.role,
14213
+ content: message.content,
14214
+ kind: message.kind,
14215
+ senderName: message.senderName,
14216
+ receivedAt: message.receivedAt
14217
+ }));
14218
+ }
14739
14219
  return true;
14740
14220
  }
14741
14221
  try {
14742
- let rebuilt = false;
14743
- if (canonicalHistory.format === "hermes-json") {
14744
- const watchPath = canonicalHistory.watchPath.replace(/^~/, os11.homedir()).replace("{{sessionId}}", this.providerSessionId);
14745
- const now = Date.now();
14746
- if (watchPath !== this.lastCanonicalHermesWatchPath || now - this.lastCanonicalHermesExistCheckAt >= 2e3) {
14747
- this.lastCanonicalHermesWatchPath = watchPath;
14748
- this.lastCanonicalHermesExistCheckAt = now;
14749
- if (!fs5.existsSync(watchPath)) return false;
14750
- } else if (this.lastCanonicalHermesSyncMtimeMs === 0) {
14751
- if (!fs5.existsSync(watchPath)) return false;
14752
- }
14753
- const stat = fs5.statSync(watchPath);
14754
- if (stat.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
14755
- rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
14756
- if (rebuilt) this.lastCanonicalHermesSyncMtimeMs = stat.mtimeMs;
14757
- } else if (canonicalHistory.format === "claude-jsonl") {
14758
- const now = Date.now();
14759
- if (now - this.lastCanonicalClaudeCheckAt < 2e3 && this.lastCanonicalClaudeRebuildMtimeMs !== 0) {
14760
- return true;
14761
- }
14762
- this.lastCanonicalClaudeCheckAt = now;
14763
- const claudeProjectsDir = path11.join(os11.homedir(), ".claude", "projects");
14764
- const workspaceSegment = typeof this.workingDir === "string" ? this.workingDir.replace(/[\\/]/g, "-").replace(/^-+/, "") : "";
14765
- const transcriptFile = path11.join(claudeProjectsDir, workspaceSegment, `${this.providerSessionId}.jsonl`);
14766
- let transcriptMtime = 0;
14767
- try {
14768
- transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
14769
- } catch {
14770
- }
14771
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
14772
- rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
14773
- if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
14774
- } else if (canonicalHistory.format === "codex-jsonl") {
14775
- const now = Date.now();
14776
- if (now - this.lastCanonicalCodexCheckAt < 2e3 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
14777
- return true;
14778
- }
14779
- this.lastCanonicalCodexCheckAt = now;
14780
- const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
14781
- let transcriptMtime = 0;
14782
- if (transcriptFile) {
14783
- try {
14784
- transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
14785
- } catch {
14786
- }
14787
- }
14788
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
14789
- rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
14790
- if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
14222
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir, canonicalHistory.mode || "materialized-mirror"].join("\0");
14223
+ const now = Date.now();
14224
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
14225
+ return true;
14226
+ }
14227
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
14228
+ this.lastNativeSourceCanonicalCheckAt = now;
14229
+ if (!materializeProviderNativeHistory(this.type, canonicalHistory, this.providerSessionId, this.workingDir, this.provider.scripts)) {
14230
+ return false;
14791
14231
  }
14792
- if (!rebuilt) return false;
14793
14232
  const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
14794
14233
  this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
14795
14234
  role: message.role,
@@ -14812,7 +14251,8 @@ ${effect.notification.body || ""}`.trim();
14812
14251
  workspace: this.workingDir,
14813
14252
  offset: 0,
14814
14253
  limit: Number.MAX_SAFE_INTEGER,
14815
- historyBehavior: this.provider.historyBehavior
14254
+ historyBehavior: this.provider.historyBehavior,
14255
+ scripts: this.provider.scripts
14816
14256
  }) : (() => {
14817
14257
  this.historyWriter.compactHistorySession(this.type, this.providerSessionId, this.provider.historyBehavior);
14818
14258
  return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
@@ -16947,6 +16387,7 @@ function validateProviderDefinition(raw) {
16947
16387
  warnings.push("Extension providers should have extensionId");
16948
16388
  }
16949
16389
  validateCapabilities(provider, controls, errors);
16390
+ validateCanonicalHistory(provider.canonicalHistory, errors);
16950
16391
  for (const control of controls) {
16951
16392
  validateControl(control, errors);
16952
16393
  }
@@ -17004,6 +16445,39 @@ function validateCapabilities(provider, controls, errors) {
17004
16445
  errors.push("providers declaring controls must set capabilities.controls.typedResults=true");
17005
16446
  }
17006
16447
  }
16448
+ function validateCanonicalHistory(raw, errors) {
16449
+ if (raw === void 0) return;
16450
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
16451
+ errors.push("canonicalHistory must be an object");
16452
+ return;
16453
+ }
16454
+ const canonicalHistory = raw;
16455
+ const format = canonicalHistory.format;
16456
+ if (format !== void 0 && (typeof format !== "string" || !format.trim())) {
16457
+ errors.push("canonicalHistory.format must be a non-empty string when provided");
16458
+ }
16459
+ const watchPath = canonicalHistory.watchPath;
16460
+ if (watchPath !== void 0 && (typeof watchPath !== "string" || !watchPath.trim())) {
16461
+ errors.push("canonicalHistory.watchPath must be a non-empty string when provided");
16462
+ }
16463
+ const mode = canonicalHistory.mode;
16464
+ if (mode !== void 0 && !["native-source", "materialized-mirror", "disabled"].includes(String(mode))) {
16465
+ errors.push("canonicalHistory.mode must be one of: native-source, materialized-mirror, disabled");
16466
+ }
16467
+ const scripts = canonicalHistory.scripts;
16468
+ if (scripts === void 0) return;
16469
+ if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) {
16470
+ errors.push("canonicalHistory.scripts must be an object");
16471
+ return;
16472
+ }
16473
+ const scriptConfig = scripts;
16474
+ for (const key of ["readSession", "listSessions"]) {
16475
+ const value = scriptConfig[key];
16476
+ if (typeof value !== "string" || !value.trim()) {
16477
+ errors.push(`canonicalHistory.scripts.${key} must be a non-empty string`);
16478
+ }
16479
+ }
16480
+ }
17007
16481
  function validateControl(control, errors) {
17008
16482
  if (!control || typeof control !== "object") {
17009
16483
  errors.push("controls: each control must be an object");
@@ -20147,7 +19621,8 @@ var DaemonCommandRouter = class {
20147
19621
  canonicalHistory: providerMeta?.canonicalHistory,
20148
19622
  offset,
20149
19623
  limit,
20150
- historyBehavior: providerMeta?.historyBehavior
19624
+ historyBehavior: providerMeta?.historyBehavior,
19625
+ scripts: providerMeta?.scripts
20151
19626
  });
20152
19627
  const state = loadState();
20153
19628
  const savedSessions = getSavedProviderSessions(state, { providerType, kind });
@@ -20173,7 +19648,10 @@ var DaemonCommandRouter = class {
20173
19648
  messageCount: session.messageCount,
20174
19649
  firstMessageAt: session.firstMessageAt,
20175
19650
  lastMessageAt: session.lastMessageAt,
20176
- canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById
19651
+ canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById,
19652
+ historySource: session.source,
19653
+ sourcePath: session.sourcePath,
19654
+ sourceMtimeMs: session.sourceMtimeMs
20177
19655
  };
20178
19656
  }),
20179
19657
  hasMore,