@adhdev/daemon-standalone 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
@@ -35992,29 +35992,6 @@ ${cleanBody}`;
35992
35992
  return { sessions: [], hasMore: false };
35993
35993
  }
35994
35994
  }
35995
- function normalizeCanonicalHermesMessageContent(content) {
35996
- if (typeof content === "string") return content.trim();
35997
- if (content == null) return "";
35998
- try {
35999
- return JSON.stringify(content).trim();
36000
- } catch {
36001
- return String(content).trim();
36002
- }
36003
- }
36004
- function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
36005
- const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
36006
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
36007
- const stringTimestamp = typeof message.ts === "string" ? Date.parse(message.ts) : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
36008
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
36009
- return fallbackTs;
36010
- }
36011
- function extractTimestampValue(value) {
36012
- const numericTimestamp = Number(value || 0);
36013
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
36014
- const stringTimestamp = typeof value === "string" ? Date.parse(value) : NaN;
36015
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
36016
- return 0;
36017
- }
36018
35995
  function readExistingSessionStartRecord(agentType, historySessionId) {
36019
35996
  try {
36020
35997
  const dir = path7.join(HISTORY_DIR, agentType);
@@ -36060,569 +36037,77 @@ ${cleanBody}`;
36060
36037
  return false;
36061
36038
  }
36062
36039
  }
36063
- function buildHermesNativeHistoryRecords(historySessionId) {
36064
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36065
- if (!normalizedSessionId) return null;
36066
- try {
36067
- const sessionFilePath = path7.join(os52.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
36068
- if (!fs32.existsSync(sessionFilePath)) return null;
36069
- const raw = JSON.parse(fs32.readFileSync(sessionFilePath, "utf-8"));
36070
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
36071
- const records = [];
36072
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
36073
- for (const message of canonicalMessages) {
36074
- const role = String(message.role || "").trim();
36075
- const content = normalizeCanonicalHermesMessageContent(message.content);
36076
- if (!content) continue;
36077
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
36078
- fallbackTs = receivedAt + 1;
36079
- if (role === "user" || role === "assistant") {
36080
- records.push({
36081
- ts: new Date(receivedAt).toISOString(),
36082
- receivedAt,
36083
- role,
36084
- content,
36085
- kind: "standard",
36086
- agent: "hermes-cli",
36087
- historySessionId: normalizedSessionId
36088
- });
36089
- continue;
36090
- }
36091
- if (role === "tool") {
36092
- records.push({
36093
- ts: new Date(receivedAt).toISOString(),
36094
- receivedAt,
36095
- role: "assistant",
36096
- content,
36097
- kind: "tool",
36098
- senderName: "Tool",
36099
- agent: "hermes-cli",
36100
- historySessionId: normalizedSessionId
36101
- });
36102
- }
36103
- }
36104
- return records;
36105
- } catch {
36106
- return null;
36107
- }
36040
+ function getNativeHistoryScriptName(canonicalHistory, key) {
36041
+ const configured = canonicalHistory?.scripts?.[key];
36042
+ if (typeof configured === "string" && configured.trim()) return configured.trim();
36043
+ return key === "readSession" ? "readNativeHistory" : "listNativeHistory";
36108
36044
  }
36109
- function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
36110
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36111
- if (!normalizedSessionId) return false;
36112
- try {
36113
- const sessionFilePath = path7.join(os52.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
36114
- if (!fs32.existsSync(sessionFilePath)) return false;
36115
- const raw = JSON.parse(fs32.readFileSync(sessionFilePath, "utf-8"));
36116
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
36117
- const dir = path7.join(HISTORY_DIR, "hermes-cli");
36118
- fs32.mkdirSync(dir, { recursive: true });
36119
- const existingSessionStart = readExistingSessionStartRecord("hermes-cli", normalizedSessionId);
36120
- const records = [];
36121
- if (existingSessionStart) {
36122
- records.push({
36123
- ...existingSessionStart,
36124
- historySessionId: normalizedSessionId
36125
- });
36126
- }
36127
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
36128
- for (const message of canonicalMessages) {
36129
- const role = String(message.role || "").trim();
36130
- const content = normalizeCanonicalHermesMessageContent(message.content);
36131
- if (!content) continue;
36132
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
36133
- fallbackTs = receivedAt + 1;
36134
- if (role === "user") {
36135
- records.push({
36136
- ts: new Date(receivedAt).toISOString(),
36137
- receivedAt,
36138
- role: "user",
36139
- content,
36140
- kind: "standard",
36141
- agent: "hermes-cli",
36142
- historySessionId: normalizedSessionId
36143
- });
36144
- continue;
36145
- }
36146
- if (role === "assistant") {
36147
- records.push({
36148
- ts: new Date(receivedAt).toISOString(),
36149
- receivedAt,
36150
- role: "assistant",
36151
- content,
36152
- kind: "standard",
36153
- agent: "hermes-cli",
36154
- historySessionId: normalizedSessionId
36155
- });
36156
- continue;
36157
- }
36158
- if (role === "tool") {
36159
- records.push({
36160
- ts: new Date(receivedAt).toISOString(),
36161
- receivedAt,
36162
- role: "assistant",
36163
- content,
36164
- kind: "tool",
36165
- senderName: "Tool",
36166
- agent: "hermes-cli",
36167
- historySessionId: normalizedSessionId
36168
- });
36169
- }
36170
- }
36171
- return rewriteCanonicalSavedHistory("hermes-cli", normalizedSessionId, records);
36172
- } catch {
36173
- return false;
36174
- }
36045
+ function getProviderNativeHistoryScript(scripts, canonicalHistory, key) {
36046
+ if (!canonicalHistory?.scripts) return null;
36047
+ const fn2 = scripts?.[getNativeHistoryScriptName(canonicalHistory, key)];
36048
+ return typeof fn2 === "function" ? fn2 : null;
36175
36049
  }
36176
- function resolveClaudeProjectTranscriptPath(historySessionId, workspace) {
36177
- const claudeProjectsDir = path7.join(os52.homedir(), ".claude", "projects");
36178
- if (!fs32.existsSync(claudeProjectsDir)) return null;
36179
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
36180
- if (normalizedWorkspace) {
36181
- const directPath = path7.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, "-"), `${historySessionId}.jsonl`);
36182
- if (fs32.existsSync(directPath)) return directPath;
36183
- }
36184
- const stack = [claudeProjectsDir];
36185
- while (stack.length > 0) {
36186
- const current = stack.pop();
36187
- if (!current) continue;
36188
- for (const entry of fs32.readdirSync(current, { withFileTypes: true })) {
36189
- const entryPath = path7.join(current, entry.name);
36190
- if (entry.isDirectory()) {
36191
- stack.push(entryPath);
36192
- continue;
36193
- }
36194
- if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
36195
- return entryPath;
36196
- }
36197
- }
36198
- }
36199
- return null;
36200
- }
36201
- function extractClaudeAssistantContentParts(content) {
36202
- if (typeof content === "string") {
36203
- const trimmed = content.trim();
36204
- return trimmed ? [{ content: trimmed, kind: "standard", role: "assistant" }] : [];
36205
- }
36206
- if (!Array.isArray(content)) return [];
36207
- const parts = [];
36208
- for (const block of content) {
36209
- if (!block || typeof block !== "object") continue;
36210
- const record2 = block;
36211
- const type = String(record2.type || "").trim();
36212
- if (type === "text") {
36213
- const text = String(record2.text || "").trim();
36214
- if (text) parts.push({ content: text, kind: "standard", role: "assistant" });
36215
- continue;
36216
- }
36217
- if (type === "tool_use") {
36218
- const name = String(record2.name || "").trim() || "Tool";
36219
- const input = record2.input && typeof record2.input === "object" ? record2.input : null;
36220
- const command = input ? String(input.command || "").trim() : "";
36221
- const summary = command ? `${name}: ${command}` : name;
36222
- if (summary) parts.push({ content: summary, kind: "tool", senderName: "Tool", role: "assistant" });
36223
- }
36224
- }
36225
- return parts;
36226
- }
36227
- function extractClaudeUserContentParts(content) {
36228
- if (typeof content === "string") {
36229
- const trimmed = content.trim();
36230
- return trimmed ? [{ role: "user", content: trimmed, kind: "standard" }] : [];
36231
- }
36232
- if (!Array.isArray(content)) return [];
36233
- const parts = [];
36234
- for (const block of content) {
36235
- if (!block || typeof block !== "object") continue;
36236
- const record2 = block;
36237
- const type = String(record2.type || "").trim();
36238
- if (type === "text") {
36239
- const text = String(record2.text || "").trim();
36240
- if (text) parts.push({ role: "user", content: text, kind: "standard" });
36241
- continue;
36242
- }
36243
- if (type === "tool_result") {
36244
- const rawContent = record2.content;
36245
- const text = typeof rawContent === "string" ? rawContent.trim() : Array.isArray(rawContent) ? rawContent.map((entry) => {
36246
- if (typeof entry === "string") return entry.trim();
36247
- if (!entry || typeof entry !== "object") return "";
36248
- const nested = entry;
36249
- if (typeof nested.text === "string") return nested.text.trim();
36250
- if (typeof nested.content === "string") return nested.content.trim();
36251
- return "";
36252
- }).filter(Boolean).join("\n") : "";
36253
- if (text) parts.push({ role: "assistant", content: text, kind: "tool", senderName: "Tool" });
36254
- }
36255
- }
36256
- return parts;
36257
- }
36258
- function buildClaudeNativeHistoryRecords(historySessionId, workspace) {
36259
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36260
- if (!normalizedSessionId) return null;
36261
- try {
36262
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
36263
- if (!transcriptPath) return null;
36264
- const lines = fs32.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
36265
- const records = [];
36266
- let fallbackTs = Date.now();
36267
- for (const line of lines) {
36268
- let parsed = null;
36269
- try {
36270
- parsed = JSON.parse(line);
36271
- } catch {
36272
- parsed = null;
36273
- }
36274
- if (!parsed) continue;
36275
- const parsedSessionId = String(parsed.sessionId || "").trim();
36276
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
36277
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
36278
- fallbackTs = receivedAt + 1;
36279
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
36280
- if (records.length === 0 && parsedWorkspace) {
36281
- records.push({
36282
- ts: new Date(receivedAt).toISOString(),
36283
- receivedAt,
36284
- role: "system",
36285
- kind: "session_start",
36286
- content: parsedWorkspace,
36287
- agent: "claude-cli",
36288
- historySessionId: normalizedSessionId,
36289
- workspace: parsedWorkspace
36290
- });
36291
- }
36292
- const type = String(parsed.type || "").trim();
36293
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
36294
- if (type === "user" && message) {
36295
- for (const part of extractClaudeUserContentParts(message.content)) {
36296
- records.push({
36297
- ts: new Date(receivedAt).toISOString(),
36298
- receivedAt,
36299
- role: part.role,
36300
- content: part.content,
36301
- kind: part.kind,
36302
- senderName: part.senderName,
36303
- agent: "claude-cli",
36304
- historySessionId: normalizedSessionId
36305
- });
36306
- }
36307
- continue;
36308
- }
36309
- if (type === "assistant" && message) {
36310
- for (const part of extractClaudeAssistantContentParts(message.content)) {
36311
- records.push({
36312
- ts: new Date(receivedAt).toISOString(),
36313
- receivedAt,
36314
- role: "assistant",
36315
- content: part.content,
36316
- kind: part.kind,
36317
- senderName: part.senderName,
36318
- agent: "claude-cli",
36319
- historySessionId: normalizedSessionId
36320
- });
36321
- }
36322
- }
36323
- }
36324
- return records;
36325
- } catch {
36326
- return null;
36327
- }
36328
- }
36329
- function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
36330
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36331
- if (!normalizedSessionId) return false;
36332
- try {
36333
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
36334
- if (!transcriptPath) return false;
36335
- const lines = fs32.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
36336
- const records = [];
36337
- const existingSessionStart = readExistingSessionStartRecord("claude-cli", normalizedSessionId);
36338
- if (existingSessionStart) {
36339
- records.push({
36340
- ...existingSessionStart,
36341
- historySessionId: normalizedSessionId
36342
- });
36343
- }
36344
- let fallbackTs = Date.now();
36345
- for (const line of lines) {
36346
- let parsed = null;
36347
- try {
36348
- parsed = JSON.parse(line);
36349
- } catch {
36350
- parsed = null;
36351
- }
36352
- if (!parsed) continue;
36353
- const parsedSessionId = String(parsed.sessionId || "").trim();
36354
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
36355
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
36356
- fallbackTs = receivedAt + 1;
36357
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
36358
- if (records.length === 0 && parsedWorkspace) {
36359
- records.push({
36360
- ts: new Date(receivedAt).toISOString(),
36361
- receivedAt,
36362
- role: "system",
36363
- kind: "session_start",
36364
- content: parsedWorkspace,
36365
- agent: "claude-cli",
36366
- historySessionId: normalizedSessionId,
36367
- workspace: parsedWorkspace
36368
- });
36369
- }
36370
- const type = String(parsed.type || "").trim();
36371
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
36372
- if (type === "user" && message) {
36373
- for (const part of extractClaudeUserContentParts(message.content)) {
36374
- records.push({
36375
- ts: new Date(receivedAt).toISOString(),
36376
- receivedAt,
36377
- role: part.role,
36378
- content: part.content,
36379
- kind: part.kind,
36380
- senderName: part.senderName,
36381
- agent: "claude-cli",
36382
- historySessionId: normalizedSessionId
36383
- });
36384
- }
36385
- continue;
36386
- }
36387
- if (type === "assistant" && message) {
36388
- for (const part of extractClaudeAssistantContentParts(message.content)) {
36389
- records.push({
36390
- ts: new Date(receivedAt).toISOString(),
36391
- receivedAt,
36392
- role: "assistant",
36393
- content: part.content,
36394
- kind: part.kind,
36395
- senderName: part.senderName,
36396
- agent: "claude-cli",
36397
- historySessionId: normalizedSessionId
36398
- });
36399
- }
36400
- }
36401
- }
36402
- return rewriteCanonicalSavedHistory("claude-cli", normalizedSessionId, records);
36403
- } catch {
36404
- return false;
36405
- }
36406
- }
36407
- function isUuidLikeSessionId(sessionId) {
36408
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
36409
- }
36410
- function readCodexSessionMeta(filePath) {
36411
- try {
36412
- const firstLine = fs32.readFileSync(filePath, "utf-8").split("\n").find(Boolean);
36413
- if (!firstLine) return null;
36414
- const parsed = JSON.parse(firstLine);
36415
- if (String(parsed.type || "") !== "session_meta") return null;
36416
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
36417
- return payload;
36418
- } catch {
36419
- return null;
36420
- }
36421
- }
36422
- function resolveCodexSessionTranscriptPath(historySessionId, workspace) {
36050
+ function normalizeProviderNativeHistoryRecords(agentType, historySessionId, records) {
36051
+ if (!Array.isArray(records)) return [];
36423
36052
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36424
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
36425
- const sessionsDir = path7.join(os52.homedir(), ".codex", "sessions");
36426
- if (!fs32.existsSync(sessionsDir)) return null;
36427
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
36428
- const candidates = [];
36429
- const stack = [sessionsDir];
36430
- while (stack.length > 0) {
36431
- const current = stack.pop();
36432
- if (!current) continue;
36433
- let entries = [];
36434
- try {
36435
- entries = fs32.readdirSync(current, { withFileTypes: true });
36436
- } catch {
36437
- continue;
36438
- }
36439
- for (const entry of entries) {
36440
- const entryPath = path7.join(current, entry.name);
36441
- if (entry.isDirectory()) {
36442
- stack.push(entryPath);
36443
- continue;
36444
- }
36445
- if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(normalizedSessionId)) continue;
36446
- const meta3 = readCodexSessionMeta(entryPath);
36447
- const metaSessionId = String(meta3?.id || "").trim();
36448
- if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
36449
- const metaWorkspace = String(meta3?.cwd || "").trim();
36450
- let mtimeMs = 0;
36451
- try {
36452
- mtimeMs = fs32.statSync(entryPath).mtimeMs;
36453
- } catch {
36454
- }
36455
- candidates.push({
36456
- path: entryPath,
36457
- mtimeMs,
36458
- workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
36459
- metaMatches: metaSessionId === normalizedSessionId
36460
- });
36461
- }
36462
- }
36463
- candidates.sort((a, b2) => Number(b2.workspaceMatches) - Number(a.workspaceMatches) || Number(b2.metaMatches) - Number(a.metaMatches) || b2.mtimeMs - a.mtimeMs);
36464
- return candidates[0]?.path || null;
36465
- }
36466
- function flattenCodexContent(content) {
36467
- if (typeof content === "string") return content.trim();
36468
- if (content == null) return "";
36469
- if (Array.isArray(content)) {
36470
- return content.map((entry) => flattenCodexContent(entry)).filter(Boolean).join("\n").trim();
36471
- }
36472
- if (typeof content === "object") {
36473
- const record2 = content;
36474
- if (typeof record2.text === "string") return record2.text.trim();
36475
- if (typeof record2.content === "string" || Array.isArray(record2.content)) return flattenCodexContent(record2.content);
36476
- if (typeof record2.output === "string") return record2.output.trim();
36477
- if (typeof record2.message === "string") return record2.message.trim();
36478
- }
36479
- return "";
36480
- }
36481
- function summarizeCodexToolCall(payload) {
36482
- const name = String(payload.name || payload.type || "tool").trim() || "tool";
36483
- const rawArguments = payload.arguments ?? payload.input;
36484
- let argumentValue = "";
36485
- if (typeof rawArguments === "string") {
36486
- const trimmed = rawArguments.trim();
36487
- try {
36488
- const parsed = JSON.parse(trimmed);
36489
- argumentValue = summarizeCodexToolArguments(parsed);
36490
- } catch {
36491
- argumentValue = trimmed;
36492
- }
36493
- } else {
36494
- argumentValue = summarizeCodexToolArguments(rawArguments);
36495
- }
36496
- return argumentValue ? `${name}: ${argumentValue}` : name;
36497
- }
36498
- function summarizeCodexToolArguments(value) {
36499
- if (typeof value === "string") return value.trim();
36500
- if (Array.isArray(value)) return value.map((entry) => String(entry)).join(" ").trim();
36501
- if (!value || typeof value !== "object") return "";
36502
- const record2 = value;
36503
- const direct = record2.command || record2.cmd || record2.query || record2.path || record2.prompt;
36504
- if (typeof direct === "string") return direct.trim();
36505
- if (Array.isArray(direct)) return direct.map((entry) => String(entry)).join(" ").trim();
36506
- try {
36507
- return JSON.stringify(record2).trim();
36508
- } catch {
36509
- return "";
36510
- }
36053
+ return records.map((record2) => sanitizeHistoryMessage(agentType, {
36054
+ ts: typeof record2?.ts === "string" ? record2.ts : new Date(Number(record2?.receivedAt) || Date.now()).toISOString(),
36055
+ receivedAt: Number(record2?.receivedAt) || Date.parse(record2?.ts || "") || Date.now(),
36056
+ role: record2?.role,
36057
+ content: String(record2?.content || ""),
36058
+ kind: record2?.kind || (record2?.role === "system" ? "session_start" : "standard"),
36059
+ senderName: record2?.senderName,
36060
+ agent: agentType,
36061
+ instanceId: record2?.instanceId,
36062
+ historySessionId: normalizeSavedHistorySessionId(record2?.historySessionId || normalizedSessionId),
36063
+ sessionTitle: record2?.sessionTitle,
36064
+ workspace: record2?.workspace
36065
+ })).filter(Boolean);
36066
+ }
36067
+ function callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, historySessionId, workspace) {
36068
+ const fn2 = getProviderNativeHistoryScript(scripts, canonicalHistory, "readSession");
36069
+ if (!fn2) return null;
36070
+ const result = fn2({
36071
+ agentType,
36072
+ sessionId: historySessionId,
36073
+ historySessionId,
36074
+ workspace,
36075
+ format: canonicalHistory?.format,
36076
+ watchPath: canonicalHistory?.watchPath,
36077
+ args: { sessionId: historySessionId, historySessionId, workspace }
36078
+ });
36079
+ if (!result || typeof result !== "object") return null;
36080
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, result.messages || result.records);
36081
+ if (records.length === 0) return null;
36082
+ return {
36083
+ records,
36084
+ sourcePath: typeof result.sourcePath === "string" ? result.sourcePath : "",
36085
+ sourceMtimeMs: Number(result.sourceMtimeMs) || 0
36086
+ };
36511
36087
  }
36512
- function codexToolOutputContent(payload) {
36513
- const output = payload.output ?? payload.result ?? payload.content;
36514
- const text = flattenCodexContent(output);
36515
- if (text) return text;
36516
- if (output && typeof output === "object") {
36517
- try {
36518
- return JSON.stringify(output).trim();
36519
- } catch {
36520
- return "";
36521
- }
36522
- }
36523
- return "";
36088
+ function buildNativeHistoryReadResult(agentType, canonicalHistory, scripts, historySessionId, workspace) {
36089
+ const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
36090
+ if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
36091
+ return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
36524
36092
  }
36525
- function buildCodexNativeHistoryRecords(historySessionId, workspace) {
36093
+ function materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts) {
36526
36094
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36527
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
36528
- try {
36529
- const transcriptPath = resolveCodexSessionTranscriptPath(normalizedSessionId, workspace);
36530
- if (!transcriptPath) return null;
36531
- const lines = fs32.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
36532
- const records = [];
36533
- let fallbackTs = Date.now();
36534
- for (const line of lines) {
36535
- let parsed = null;
36536
- try {
36537
- parsed = JSON.parse(line);
36538
- } catch {
36539
- parsed = null;
36540
- }
36541
- if (!parsed) continue;
36542
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
36543
- fallbackTs = receivedAt + 1;
36544
- const type = String(parsed.type || "").trim();
36545
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
36546
- if (!payload) continue;
36547
- if (type === "session_meta") {
36548
- const parsedSessionId = String(payload.id || "").trim();
36549
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
36550
- const parsedWorkspace = String(payload.cwd || workspace || "").trim();
36551
- if (records.length === 0 && parsedWorkspace) {
36552
- records.push({
36553
- ts: new Date(receivedAt).toISOString(),
36554
- receivedAt,
36555
- role: "system",
36556
- kind: "session_start",
36557
- content: parsedWorkspace,
36558
- agent: "codex-cli",
36559
- historySessionId: normalizedSessionId,
36560
- workspace: parsedWorkspace
36561
- });
36562
- }
36563
- continue;
36564
- }
36565
- if (type !== "response_item") continue;
36566
- const payloadType = String(payload.type || "").trim();
36567
- if (payloadType === "message") {
36568
- const role = String(payload.role || "").trim();
36569
- if (role !== "user" && role !== "assistant") continue;
36570
- const content = flattenCodexContent(payload.content);
36571
- if (!content) continue;
36572
- records.push({
36573
- ts: new Date(receivedAt).toISOString(),
36574
- receivedAt,
36575
- role,
36576
- content,
36577
- kind: "standard",
36578
- agent: "codex-cli",
36579
- historySessionId: normalizedSessionId
36580
- });
36581
- continue;
36582
- }
36583
- if (payloadType === "function_call" || payloadType === "custom_tool_call") {
36584
- const content = summarizeCodexToolCall(payload);
36585
- if (!content) continue;
36586
- records.push({
36587
- ts: new Date(receivedAt).toISOString(),
36588
- receivedAt,
36589
- role: "assistant",
36590
- content,
36591
- kind: "tool",
36592
- senderName: "Tool",
36593
- agent: "codex-cli",
36594
- historySessionId: normalizedSessionId
36595
- });
36596
- continue;
36597
- }
36598
- if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
36599
- const content = codexToolOutputContent(payload);
36600
- if (!content) continue;
36601
- records.push({
36602
- ts: new Date(receivedAt).toISOString(),
36603
- receivedAt,
36604
- role: "assistant",
36605
- content,
36606
- kind: "tool",
36607
- senderName: "Tool",
36608
- agent: "codex-cli",
36609
- historySessionId: normalizedSessionId
36610
- });
36611
- }
36612
- }
36613
- return records;
36614
- } catch {
36615
- return null;
36616
- }
36095
+ if (!normalizedSessionId) return false;
36096
+ const nativeResult = callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
36097
+ const nativeRecords = nativeResult?.records || [];
36098
+ if (nativeRecords.length === 0) return false;
36099
+ const normalizedRecords = nativeRecords.map((record2) => ({
36100
+ ...record2,
36101
+ agent: agentType,
36102
+ historySessionId: normalizedSessionId
36103
+ }));
36104
+ const existingSessionStart = readExistingSessionStartRecord(agentType, normalizedSessionId);
36105
+ const records = existingSessionStart && normalizedRecords[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId, agent: agentType }, ...normalizedRecords] : normalizedRecords;
36106
+ return rewriteCanonicalSavedHistory(agentType, normalizedSessionId, records);
36617
36107
  }
36618
- function rebuildCodexSavedHistoryFromNativeSession(historySessionId, workspace) {
36619
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
36620
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return false;
36621
- const records = buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
36622
- if (!records || records.length === 0) return false;
36623
- const existingSessionStart = readExistingSessionStartRecord("codex-cli", normalizedSessionId);
36624
- const recordsToWrite = existingSessionStart && records[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId }, ...records] : records;
36625
- return rewriteCanonicalSavedHistory("codex-cli", normalizedSessionId, recordsToWrite);
36108
+ function materializeProviderNativeHistory(agentType, canonicalHistory, historySessionId, workspace, scripts) {
36109
+ if (!canonicalHistory || canonicalHistory.mode !== "materialized-mirror") return false;
36110
+ return materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts);
36626
36111
  }
36627
36112
  function isNativeSourceCanonicalHistory(canonicalHistory) {
36628
36113
  if (!canonicalHistory) return false;
@@ -36630,21 +36115,15 @@ ${cleanBody}`;
36630
36115
  if (canonicalHistory.mode === "materialized-mirror") return false;
36631
36116
  return true;
36632
36117
  }
36633
- function buildNativeHistoryRecords(canonicalHistory, historySessionId, workspace) {
36634
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
36635
- if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
36636
- if (canonicalHistory.format === "hermes-json") return buildHermesNativeHistoryRecords(normalizedSessionId);
36637
- if (canonicalHistory.format === "claude-jsonl") return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
36638
- if (canonicalHistory.format === "codex-jsonl") return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
36639
- return null;
36640
- }
36641
36118
  function readProviderChatHistory(agentType, options = {}) {
36642
36119
  if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
36643
- const records = buildNativeHistoryRecords(options.canonicalHistory, options.historySessionId, options.workspace);
36644
- if (!records) return { messages: [], hasMore: false, source: "native-unavailable" };
36120
+ const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
36121
+ if (!nativeResult) return { messages: [], hasMore: false, source: "native-unavailable" };
36645
36122
  return {
36646
- ...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
36647
- source: "provider-native"
36123
+ ...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
36124
+ source: "provider-native",
36125
+ sourcePath: nativeResult.sourcePath,
36126
+ sourceMtimeMs: nativeResult.sourceMtimeMs
36648
36127
  };
36649
36128
  }
36650
36129
  return {
@@ -36677,68 +36156,64 @@ ${cleanBody}`;
36677
36156
  sourceMtimeMs
36678
36157
  };
36679
36158
  }
36680
- function listFilesRecursive(root, predicate) {
36681
- if (!fs32.existsSync(root)) return [];
36682
- const results = [];
36683
- const stack = [root];
36684
- while (stack.length > 0) {
36685
- const current = stack.pop();
36686
- if (!current) continue;
36687
- let entries = [];
36688
- try {
36689
- entries = fs32.readdirSync(current, { withFileTypes: true });
36690
- } catch {
36691
- continue;
36692
- }
36693
- for (const entry of entries) {
36694
- const entryPath = path7.join(current, entry.name);
36695
- if (entry.isDirectory()) {
36696
- stack.push(entryPath);
36697
- continue;
36698
- }
36699
- if (predicate(entryPath, entry)) results.push(entryPath);
36700
- }
36701
- }
36702
- return results;
36159
+ function normalizeProviderNativeHistorySessionSummary(agentType, item) {
36160
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
36161
+ if (!historySessionId) return null;
36162
+ const sourcePath = typeof item?.sourcePath === "string" ? item.sourcePath : "";
36163
+ const sourceMtimeMs = Number(item?.sourceMtimeMs) || 0;
36164
+ const firstMessageAt = Number(item?.firstMessageAt) || sourceMtimeMs || Date.now();
36165
+ const lastMessageAt = Number(item?.lastMessageAt) || firstMessageAt;
36166
+ const messageCount = Math.max(0, Number(item?.messageCount) || 0);
36167
+ return {
36168
+ historySessionId,
36169
+ sessionTitle: typeof item?.sessionTitle === "string" ? item.sessionTitle : void 0,
36170
+ messageCount,
36171
+ firstMessageAt,
36172
+ lastMessageAt,
36173
+ preview: typeof item?.preview === "string" ? item.preview : void 0,
36174
+ workspace: typeof item?.workspace === "string" ? item.workspace : void 0,
36175
+ source: "provider-native",
36176
+ sourcePath,
36177
+ sourceMtimeMs
36178
+ };
36703
36179
  }
36704
- function collectNativeHistorySessionSummaries(agentType, canonicalHistory) {
36180
+ function collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
36181
+ const fn2 = getProviderNativeHistoryScript(scripts, canonicalHistory, "listSessions");
36182
+ if (!fn2) return null;
36183
+ const result = fn2({
36184
+ agentType,
36185
+ format: canonicalHistory.format,
36186
+ watchPath: canonicalHistory.watchPath,
36187
+ args: {}
36188
+ });
36189
+ if (!result || typeof result !== "object") return [];
36190
+ const sessions = Array.isArray(result.sessions) ? result.sessions : [];
36705
36191
  const summaries = [];
36706
- if (canonicalHistory.format === "hermes-json") {
36707
- const root = path7.join(os52.homedir(), ".hermes", "sessions");
36708
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && /^session_.+\.json$/.test(entry.name))) {
36709
- const fileName = path7.basename(filePath);
36710
- const historySessionId = fileName.replace(/^session_/, "").replace(/\.json$/, "");
36711
- const records = buildHermesNativeHistoryRecords(historySessionId);
36712
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
36713
- if (summary) summaries.push(summary);
36714
- }
36715
- } else if (canonicalHistory.format === "claude-jsonl") {
36716
- const root = path7.join(os52.homedir(), ".claude", "projects");
36717
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
36718
- const historySessionId = path7.basename(filePath, ".jsonl");
36719
- const records = buildClaudeNativeHistoryRecords(historySessionId);
36720
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
36721
- if (summary) summaries.push(summary);
36722
- }
36723
- } else if (canonicalHistory.format === "codex-jsonl") {
36724
- const root = path7.join(os52.homedir(), ".codex", "sessions");
36725
- const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
36726
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
36727
- const meta3 = readCodexSessionMeta(filePath);
36728
- const historySessionId = String(meta3?.id || path7.basename(filePath).match(uuidPattern)?.[1] || "").trim();
36192
+ for (const item of sessions) {
36193
+ if (Array.isArray(item?.messages || item?.records)) {
36194
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
36729
36195
  if (!historySessionId) continue;
36730
- const records = buildCodexNativeHistoryRecords(historySessionId, String(meta3?.cwd || "").trim() || void 0);
36731
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
36732
- if (summary) summaries.push(summary);
36196
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, item.messages || item.records);
36197
+ const summary2 = buildNativeSessionSummary(agentType, historySessionId, records, typeof item?.sourcePath === "string" ? item.sourcePath : "");
36198
+ if (summary2) {
36199
+ if (Number(item?.sourceMtimeMs)) summary2.sourceMtimeMs = Number(item.sourceMtimeMs);
36200
+ summaries.push(summary2);
36201
+ }
36202
+ continue;
36733
36203
  }
36204
+ const summary = normalizeProviderNativeHistorySessionSummary(agentType, item);
36205
+ if (summary) summaries.push(summary);
36734
36206
  }
36735
36207
  return sortSavedHistorySessionSummaries(summaries);
36736
36208
  }
36209
+ function collectNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
36210
+ return collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) || [];
36211
+ }
36737
36212
  function listProviderHistorySessions(agentType, options = {}) {
36738
36213
  if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
36739
36214
  const offset = Math.max(0, options.offset || 0);
36740
36215
  const limit = Math.max(1, options.limit || 30);
36741
- const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory);
36216
+ const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory, options.scripts);
36742
36217
  return {
36743
36218
  sessions: summaries.slice(offset, offset + limit),
36744
36219
  hasMore: offset + limit < summaries.length,
@@ -39325,7 +38800,8 @@ ${effect.notification.body || ""}`.trim();
39325
38800
  offset: offset || 0,
39326
38801
  limit: limit || 30,
39327
38802
  excludeRecentCount,
39328
- historyBehavior: provider?.historyBehavior
38803
+ historyBehavior: provider?.historyBehavior,
38804
+ scripts: provider?.scripts
39329
38805
  });
39330
38806
  return { success: true, ...result, agent: agentStr };
39331
38807
  } catch (e) {
@@ -41777,13 +41253,8 @@ ${effect.notification.body || ""}`.trim();
41777
41253
  historyWriter;
41778
41254
  runtimeMessages = [];
41779
41255
  lastPersistedHistoryMessages = [];
41780
- lastCanonicalHermesSyncMtimeMs = 0;
41781
- lastCanonicalHermesExistCheckAt = 0;
41782
- lastCanonicalHermesWatchPath = void 0;
41783
- lastCanonicalClaudeRebuildMtimeMs = 0;
41784
- lastCanonicalClaudeCheckAt = 0;
41785
- lastCanonicalCodexRebuildMtimeMs = 0;
41786
- lastCanonicalCodexCheckAt = 0;
41256
+ lastNativeSourceCanonicalCheckAt = 0;
41257
+ lastNativeSourceCanonicalCacheKey = void 0;
41787
41258
  cachedSqliteDb = null;
41788
41259
  cachedSqliteDbPath = null;
41789
41260
  cachedSqliteDbMissingUntil = 0;
@@ -42484,76 +41955,44 @@ ${effect.notification.body || ""}`.trim();
42484
41955
  const canonicalHistory = this.provider.canonicalHistory;
42485
41956
  if (!canonicalHistory) return false;
42486
41957
  if (isNativeSourceCanonicalHistory(canonicalHistory)) {
41958
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir].join("\0");
41959
+ const now = Date.now();
41960
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
41961
+ return true;
41962
+ }
41963
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
41964
+ this.lastNativeSourceCanonicalCheckAt = now;
42487
41965
  const restoredHistory = readProviderChatHistory(this.type, {
42488
41966
  canonicalHistory,
42489
41967
  historySessionId: this.providerSessionId,
42490
41968
  workspace: this.workingDir,
42491
41969
  offset: 0,
42492
41970
  limit: Number.MAX_SAFE_INTEGER,
42493
- historyBehavior: this.provider.historyBehavior
41971
+ historyBehavior: this.provider.historyBehavior,
41972
+ scripts: this.provider.scripts
42494
41973
  });
42495
- if (restoredHistory.source !== "provider-native") return false;
42496
- this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
42497
- role: message.role,
42498
- content: message.content,
42499
- kind: message.kind,
42500
- senderName: message.senderName,
42501
- receivedAt: message.receivedAt
42502
- }));
41974
+ if (restoredHistory.source === "provider-native") {
41975
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
41976
+ role: message.role,
41977
+ content: message.content,
41978
+ kind: message.kind,
41979
+ senderName: message.senderName,
41980
+ receivedAt: message.receivedAt
41981
+ }));
41982
+ }
42503
41983
  return true;
42504
41984
  }
42505
41985
  try {
42506
- let rebuilt = false;
42507
- if (canonicalHistory.format === "hermes-json") {
42508
- const watchPath = canonicalHistory.watchPath.replace(/^~/, os11.homedir()).replace("{{sessionId}}", this.providerSessionId);
42509
- const now = Date.now();
42510
- if (watchPath !== this.lastCanonicalHermesWatchPath || now - this.lastCanonicalHermesExistCheckAt >= 2e3) {
42511
- this.lastCanonicalHermesWatchPath = watchPath;
42512
- this.lastCanonicalHermesExistCheckAt = now;
42513
- if (!fs52.existsSync(watchPath)) return false;
42514
- } else if (this.lastCanonicalHermesSyncMtimeMs === 0) {
42515
- if (!fs52.existsSync(watchPath)) return false;
42516
- }
42517
- const stat4 = fs52.statSync(watchPath);
42518
- if (stat4.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
42519
- rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
42520
- if (rebuilt) this.lastCanonicalHermesSyncMtimeMs = stat4.mtimeMs;
42521
- } else if (canonicalHistory.format === "claude-jsonl") {
42522
- const now = Date.now();
42523
- if (now - this.lastCanonicalClaudeCheckAt < 2e3 && this.lastCanonicalClaudeRebuildMtimeMs !== 0) {
42524
- return true;
42525
- }
42526
- this.lastCanonicalClaudeCheckAt = now;
42527
- const claudeProjectsDir = path11.join(os11.homedir(), ".claude", "projects");
42528
- const workspaceSegment = typeof this.workingDir === "string" ? this.workingDir.replace(/[\\/]/g, "-").replace(/^-+/, "") : "";
42529
- const transcriptFile = path11.join(claudeProjectsDir, workspaceSegment, `${this.providerSessionId}.jsonl`);
42530
- let transcriptMtime = 0;
42531
- try {
42532
- transcriptMtime = fs52.statSync(transcriptFile).mtimeMs;
42533
- } catch {
42534
- }
42535
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
42536
- rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
42537
- if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
42538
- } else if (canonicalHistory.format === "codex-jsonl") {
42539
- const now = Date.now();
42540
- if (now - this.lastCanonicalCodexCheckAt < 2e3 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
42541
- return true;
42542
- }
42543
- this.lastCanonicalCodexCheckAt = now;
42544
- const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
42545
- let transcriptMtime = 0;
42546
- if (transcriptFile) {
42547
- try {
42548
- transcriptMtime = fs52.statSync(transcriptFile).mtimeMs;
42549
- } catch {
42550
- }
42551
- }
42552
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
42553
- rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
42554
- if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
41986
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir, canonicalHistory.mode || "materialized-mirror"].join("\0");
41987
+ const now = Date.now();
41988
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
41989
+ return true;
41990
+ }
41991
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
41992
+ this.lastNativeSourceCanonicalCheckAt = now;
41993
+ if (!materializeProviderNativeHistory(this.type, canonicalHistory, this.providerSessionId, this.workingDir, this.provider.scripts)) {
41994
+ return false;
42555
41995
  }
42556
- if (!rebuilt) return false;
42557
41996
  const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
42558
41997
  this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
42559
41998
  role: message.role,
@@ -42576,7 +42015,8 @@ ${effect.notification.body || ""}`.trim();
42576
42015
  workspace: this.workingDir,
42577
42016
  offset: 0,
42578
42017
  limit: Number.MAX_SAFE_INTEGER,
42579
- historyBehavior: this.provider.historyBehavior
42018
+ historyBehavior: this.provider.historyBehavior,
42019
+ scripts: this.provider.scripts
42580
42020
  }) : (() => {
42581
42021
  this.historyWriter.compactHistorySession(this.type, this.providerSessionId, this.provider.historyBehavior);
42582
42022
  return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
@@ -44697,6 +44137,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
44697
44137
  warnings.push("Extension providers should have extensionId");
44698
44138
  }
44699
44139
  validateCapabilities(provider, controls, errors);
44140
+ validateCanonicalHistory(provider.canonicalHistory, errors);
44700
44141
  for (const control of controls) {
44701
44142
  validateControl(control, errors);
44702
44143
  }
@@ -44754,6 +44195,39 @@ Run 'adhdev doctor' for detailed diagnostics.`
44754
44195
  errors.push("providers declaring controls must set capabilities.controls.typedResults=true");
44755
44196
  }
44756
44197
  }
44198
+ function validateCanonicalHistory(raw, errors) {
44199
+ if (raw === void 0) return;
44200
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
44201
+ errors.push("canonicalHistory must be an object");
44202
+ return;
44203
+ }
44204
+ const canonicalHistory = raw;
44205
+ const format = canonicalHistory.format;
44206
+ if (format !== void 0 && (typeof format !== "string" || !format.trim())) {
44207
+ errors.push("canonicalHistory.format must be a non-empty string when provided");
44208
+ }
44209
+ const watchPath = canonicalHistory.watchPath;
44210
+ if (watchPath !== void 0 && (typeof watchPath !== "string" || !watchPath.trim())) {
44211
+ errors.push("canonicalHistory.watchPath must be a non-empty string when provided");
44212
+ }
44213
+ const mode = canonicalHistory.mode;
44214
+ if (mode !== void 0 && !["native-source", "materialized-mirror", "disabled"].includes(String(mode))) {
44215
+ errors.push("canonicalHistory.mode must be one of: native-source, materialized-mirror, disabled");
44216
+ }
44217
+ const scripts = canonicalHistory.scripts;
44218
+ if (scripts === void 0) return;
44219
+ if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) {
44220
+ errors.push("canonicalHistory.scripts must be an object");
44221
+ return;
44222
+ }
44223
+ const scriptConfig = scripts;
44224
+ for (const key of ["readSession", "listSessions"]) {
44225
+ const value = scriptConfig[key];
44226
+ if (typeof value !== "string" || !value.trim()) {
44227
+ errors.push(`canonicalHistory.scripts.${key} must be a non-empty string`);
44228
+ }
44229
+ }
44230
+ }
44757
44231
  function validateControl(control, errors) {
44758
44232
  if (!control || typeof control !== "object") {
44759
44233
  errors.push("controls: each control must be an object");
@@ -47879,7 +47353,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
47879
47353
  canonicalHistory: providerMeta?.canonicalHistory,
47880
47354
  offset,
47881
47355
  limit,
47882
- historyBehavior: providerMeta?.historyBehavior
47356
+ historyBehavior: providerMeta?.historyBehavior,
47357
+ scripts: providerMeta?.scripts
47883
47358
  });
47884
47359
  const state = loadState();
47885
47360
  const savedSessions = getSavedProviderSessions(state, { providerType, kind });
@@ -47905,7 +47380,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
47905
47380
  messageCount: session.messageCount,
47906
47381
  firstMessageAt: session.firstMessageAt,
47907
47382
  lastMessageAt: session.lastMessageAt,
47908
- canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById
47383
+ canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById,
47384
+ historySource: session.source,
47385
+ sourcePath: session.sourcePath,
47386
+ sourceMtimeMs: session.sourceMtimeMs
47909
47387
  };
47910
47388
  }),
47911
47389
  hasMore,