adhdev 0.9.45 → 0.9.47

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
@@ -4145,29 +4145,6 @@ function listSavedHistorySessions(agentType, options = {}, historyBehavior) {
4145
4145
  return { sessions: [], hasMore: false };
4146
4146
  }
4147
4147
  }
4148
- function normalizeCanonicalHermesMessageContent(content) {
4149
- if (typeof content === "string") return content.trim();
4150
- if (content == null) return "";
4151
- try {
4152
- return JSON.stringify(content).trim();
4153
- } catch {
4154
- return String(content).trim();
4155
- }
4156
- }
4157
- function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
4158
- const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
4159
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
4160
- const stringTimestamp = typeof message.ts === "string" ? Date.parse(message.ts) : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
4161
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
4162
- return fallbackTs;
4163
- }
4164
- function extractTimestampValue(value) {
4165
- const numericTimestamp = Number(value || 0);
4166
- if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
4167
- const stringTimestamp = typeof value === "string" ? Date.parse(value) : NaN;
4168
- if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
4169
- return 0;
4170
- }
4171
4148
  function readExistingSessionStartRecord(agentType, historySessionId) {
4172
4149
  try {
4173
4150
  const dir = path7.join(HISTORY_DIR, agentType);
@@ -4213,569 +4190,77 @@ function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
4213
4190
  return false;
4214
4191
  }
4215
4192
  }
4216
- function buildHermesNativeHistoryRecords(historySessionId) {
4217
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4218
- if (!normalizedSessionId) return null;
4219
- try {
4220
- const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
4221
- if (!fs3.existsSync(sessionFilePath)) return null;
4222
- const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
4223
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
4224
- const records = [];
4225
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
4226
- for (const message of canonicalMessages) {
4227
- const role = String(message.role || "").trim();
4228
- const content = normalizeCanonicalHermesMessageContent(message.content);
4229
- if (!content) continue;
4230
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
4231
- fallbackTs = receivedAt + 1;
4232
- if (role === "user" || role === "assistant") {
4233
- records.push({
4234
- ts: new Date(receivedAt).toISOString(),
4235
- receivedAt,
4236
- role,
4237
- content,
4238
- kind: "standard",
4239
- agent: "hermes-cli",
4240
- historySessionId: normalizedSessionId
4241
- });
4242
- continue;
4243
- }
4244
- if (role === "tool") {
4245
- records.push({
4246
- ts: new Date(receivedAt).toISOString(),
4247
- receivedAt,
4248
- role: "assistant",
4249
- content,
4250
- kind: "tool",
4251
- senderName: "Tool",
4252
- agent: "hermes-cli",
4253
- historySessionId: normalizedSessionId
4254
- });
4255
- }
4256
- }
4257
- return records;
4258
- } catch {
4259
- return null;
4260
- }
4261
- }
4262
- function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
4263
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4264
- if (!normalizedSessionId) return false;
4265
- try {
4266
- const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
4267
- if (!fs3.existsSync(sessionFilePath)) return false;
4268
- const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
4269
- const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
4270
- const dir = path7.join(HISTORY_DIR, "hermes-cli");
4271
- fs3.mkdirSync(dir, { recursive: true });
4272
- const existingSessionStart = readExistingSessionStartRecord("hermes-cli", normalizedSessionId);
4273
- const records = [];
4274
- if (existingSessionStart) {
4275
- records.push({
4276
- ...existingSessionStart,
4277
- historySessionId: normalizedSessionId
4278
- });
4279
- }
4280
- let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
4281
- for (const message of canonicalMessages) {
4282
- const role = String(message.role || "").trim();
4283
- const content = normalizeCanonicalHermesMessageContent(message.content);
4284
- if (!content) continue;
4285
- const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
4286
- fallbackTs = receivedAt + 1;
4287
- if (role === "user") {
4288
- records.push({
4289
- ts: new Date(receivedAt).toISOString(),
4290
- receivedAt,
4291
- role: "user",
4292
- content,
4293
- kind: "standard",
4294
- agent: "hermes-cli",
4295
- historySessionId: normalizedSessionId
4296
- });
4297
- continue;
4298
- }
4299
- if (role === "assistant") {
4300
- records.push({
4301
- ts: new Date(receivedAt).toISOString(),
4302
- receivedAt,
4303
- role: "assistant",
4304
- content,
4305
- kind: "standard",
4306
- agent: "hermes-cli",
4307
- historySessionId: normalizedSessionId
4308
- });
4309
- continue;
4310
- }
4311
- if (role === "tool") {
4312
- records.push({
4313
- ts: new Date(receivedAt).toISOString(),
4314
- receivedAt,
4315
- role: "assistant",
4316
- content,
4317
- kind: "tool",
4318
- senderName: "Tool",
4319
- agent: "hermes-cli",
4320
- historySessionId: normalizedSessionId
4321
- });
4322
- }
4323
- }
4324
- return rewriteCanonicalSavedHistory("hermes-cli", normalizedSessionId, records);
4325
- } catch {
4326
- return false;
4327
- }
4328
- }
4329
- function resolveClaudeProjectTranscriptPath(historySessionId, workspace) {
4330
- const claudeProjectsDir = path7.join(os5.homedir(), ".claude", "projects");
4331
- if (!fs3.existsSync(claudeProjectsDir)) return null;
4332
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
4333
- if (normalizedWorkspace) {
4334
- const directPath = path7.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, "-"), `${historySessionId}.jsonl`);
4335
- if (fs3.existsSync(directPath)) return directPath;
4336
- }
4337
- const stack = [claudeProjectsDir];
4338
- while (stack.length > 0) {
4339
- const current = stack.pop();
4340
- if (!current) continue;
4341
- for (const entry of fs3.readdirSync(current, { withFileTypes: true })) {
4342
- const entryPath = path7.join(current, entry.name);
4343
- if (entry.isDirectory()) {
4344
- stack.push(entryPath);
4345
- continue;
4346
- }
4347
- if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
4348
- return entryPath;
4349
- }
4350
- }
4351
- }
4352
- return null;
4193
+ function getNativeHistoryScriptName(canonicalHistory, key) {
4194
+ const configured = canonicalHistory?.scripts?.[key];
4195
+ if (typeof configured === "string" && configured.trim()) return configured.trim();
4196
+ return key === "readSession" ? "readNativeHistory" : "listNativeHistory";
4353
4197
  }
4354
- function extractClaudeAssistantContentParts(content) {
4355
- if (typeof content === "string") {
4356
- const trimmed = content.trim();
4357
- return trimmed ? [{ content: trimmed, kind: "standard", role: "assistant" }] : [];
4358
- }
4359
- if (!Array.isArray(content)) return [];
4360
- const parts = [];
4361
- for (const block of content) {
4362
- if (!block || typeof block !== "object") continue;
4363
- const record2 = block;
4364
- const type = String(record2.type || "").trim();
4365
- if (type === "text") {
4366
- const text = String(record2.text || "").trim();
4367
- if (text) parts.push({ content: text, kind: "standard", role: "assistant" });
4368
- continue;
4369
- }
4370
- if (type === "tool_use") {
4371
- const name = String(record2.name || "").trim() || "Tool";
4372
- const input = record2.input && typeof record2.input === "object" ? record2.input : null;
4373
- const command = input ? String(input.command || "").trim() : "";
4374
- const summary = command ? `${name}: ${command}` : name;
4375
- if (summary) parts.push({ content: summary, kind: "tool", senderName: "Tool", role: "assistant" });
4376
- }
4377
- }
4378
- return parts;
4198
+ function getProviderNativeHistoryScript(scripts, canonicalHistory, key) {
4199
+ if (!canonicalHistory?.scripts) return null;
4200
+ const fn = scripts?.[getNativeHistoryScriptName(canonicalHistory, key)];
4201
+ return typeof fn === "function" ? fn : null;
4379
4202
  }
4380
- function extractClaudeUserContentParts(content) {
4381
- if (typeof content === "string") {
4382
- const trimmed = content.trim();
4383
- return trimmed ? [{ role: "user", content: trimmed, kind: "standard" }] : [];
4384
- }
4385
- if (!Array.isArray(content)) return [];
4386
- const parts = [];
4387
- for (const block of content) {
4388
- if (!block || typeof block !== "object") continue;
4389
- const record2 = block;
4390
- const type = String(record2.type || "").trim();
4391
- if (type === "text") {
4392
- const text = String(record2.text || "").trim();
4393
- if (text) parts.push({ role: "user", content: text, kind: "standard" });
4394
- continue;
4395
- }
4396
- if (type === "tool_result") {
4397
- const rawContent = record2.content;
4398
- const text = typeof rawContent === "string" ? rawContent.trim() : Array.isArray(rawContent) ? rawContent.map((entry) => {
4399
- if (typeof entry === "string") return entry.trim();
4400
- if (!entry || typeof entry !== "object") return "";
4401
- const nested = entry;
4402
- if (typeof nested.text === "string") return nested.text.trim();
4403
- if (typeof nested.content === "string") return nested.content.trim();
4404
- return "";
4405
- }).filter(Boolean).join("\n") : "";
4406
- if (text) parts.push({ role: "assistant", content: text, kind: "tool", senderName: "Tool" });
4407
- }
4408
- }
4409
- return parts;
4410
- }
4411
- function buildClaudeNativeHistoryRecords(historySessionId, workspace) {
4203
+ function normalizeProviderNativeHistoryRecords(agentType, historySessionId, records) {
4204
+ if (!Array.isArray(records)) return [];
4412
4205
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4413
- if (!normalizedSessionId) return null;
4414
- try {
4415
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
4416
- if (!transcriptPath) return null;
4417
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
4418
- const records = [];
4419
- let fallbackTs = Date.now();
4420
- for (const line of lines) {
4421
- let parsed = null;
4422
- try {
4423
- parsed = JSON.parse(line);
4424
- } catch {
4425
- parsed = null;
4426
- }
4427
- if (!parsed) continue;
4428
- const parsedSessionId = String(parsed.sessionId || "").trim();
4429
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
4430
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
4431
- fallbackTs = receivedAt + 1;
4432
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
4433
- if (records.length === 0 && parsedWorkspace) {
4434
- records.push({
4435
- ts: new Date(receivedAt).toISOString(),
4436
- receivedAt,
4437
- role: "system",
4438
- kind: "session_start",
4439
- content: parsedWorkspace,
4440
- agent: "claude-cli",
4441
- historySessionId: normalizedSessionId,
4442
- workspace: parsedWorkspace
4443
- });
4444
- }
4445
- const type = String(parsed.type || "").trim();
4446
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
4447
- if (type === "user" && message) {
4448
- for (const part of extractClaudeUserContentParts(message.content)) {
4449
- records.push({
4450
- ts: new Date(receivedAt).toISOString(),
4451
- receivedAt,
4452
- role: part.role,
4453
- content: part.content,
4454
- kind: part.kind,
4455
- senderName: part.senderName,
4456
- agent: "claude-cli",
4457
- historySessionId: normalizedSessionId
4458
- });
4459
- }
4460
- continue;
4461
- }
4462
- if (type === "assistant" && message) {
4463
- for (const part of extractClaudeAssistantContentParts(message.content)) {
4464
- records.push({
4465
- ts: new Date(receivedAt).toISOString(),
4466
- receivedAt,
4467
- role: "assistant",
4468
- content: part.content,
4469
- kind: part.kind,
4470
- senderName: part.senderName,
4471
- agent: "claude-cli",
4472
- historySessionId: normalizedSessionId
4473
- });
4474
- }
4475
- }
4476
- }
4477
- return records;
4478
- } catch {
4479
- return null;
4480
- }
4481
- }
4482
- function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
4483
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4484
- if (!normalizedSessionId) return false;
4485
- try {
4486
- const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
4487
- if (!transcriptPath) return false;
4488
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
4489
- const records = [];
4490
- const existingSessionStart = readExistingSessionStartRecord("claude-cli", normalizedSessionId);
4491
- if (existingSessionStart) {
4492
- records.push({
4493
- ...existingSessionStart,
4494
- historySessionId: normalizedSessionId
4495
- });
4496
- }
4497
- let fallbackTs = Date.now();
4498
- for (const line of lines) {
4499
- let parsed = null;
4500
- try {
4501
- parsed = JSON.parse(line);
4502
- } catch {
4503
- parsed = null;
4504
- }
4505
- if (!parsed) continue;
4506
- const parsedSessionId = String(parsed.sessionId || "").trim();
4507
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
4508
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
4509
- fallbackTs = receivedAt + 1;
4510
- const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
4511
- if (records.length === 0 && parsedWorkspace) {
4512
- records.push({
4513
- ts: new Date(receivedAt).toISOString(),
4514
- receivedAt,
4515
- role: "system",
4516
- kind: "session_start",
4517
- content: parsedWorkspace,
4518
- agent: "claude-cli",
4519
- historySessionId: normalizedSessionId,
4520
- workspace: parsedWorkspace
4521
- });
4522
- }
4523
- const type = String(parsed.type || "").trim();
4524
- const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
4525
- if (type === "user" && message) {
4526
- for (const part of extractClaudeUserContentParts(message.content)) {
4527
- records.push({
4528
- ts: new Date(receivedAt).toISOString(),
4529
- receivedAt,
4530
- role: part.role,
4531
- content: part.content,
4532
- kind: part.kind,
4533
- senderName: part.senderName,
4534
- agent: "claude-cli",
4535
- historySessionId: normalizedSessionId
4536
- });
4537
- }
4538
- continue;
4539
- }
4540
- if (type === "assistant" && message) {
4541
- for (const part of extractClaudeAssistantContentParts(message.content)) {
4542
- records.push({
4543
- ts: new Date(receivedAt).toISOString(),
4544
- receivedAt,
4545
- role: "assistant",
4546
- content: part.content,
4547
- kind: part.kind,
4548
- senderName: part.senderName,
4549
- agent: "claude-cli",
4550
- historySessionId: normalizedSessionId
4551
- });
4552
- }
4553
- }
4554
- }
4555
- return rewriteCanonicalSavedHistory("claude-cli", normalizedSessionId, records);
4556
- } catch {
4557
- return false;
4558
- }
4559
- }
4560
- function isUuidLikeSessionId(sessionId) {
4561
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
4562
- }
4563
- function readCodexSessionMeta(filePath) {
4564
- try {
4565
- const firstLine = fs3.readFileSync(filePath, "utf-8").split("\n").find(Boolean);
4566
- if (!firstLine) return null;
4567
- const parsed = JSON.parse(firstLine);
4568
- if (String(parsed.type || "") !== "session_meta") return null;
4569
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
4570
- return payload;
4571
- } catch {
4572
- return null;
4573
- }
4574
- }
4575
- function resolveCodexSessionTranscriptPath(historySessionId, workspace) {
4576
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4577
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
4578
- const sessionsDir = path7.join(os5.homedir(), ".codex", "sessions");
4579
- if (!fs3.existsSync(sessionsDir)) return null;
4580
- const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
4581
- const candidates = [];
4582
- const stack = [sessionsDir];
4583
- while (stack.length > 0) {
4584
- const current = stack.pop();
4585
- if (!current) continue;
4586
- let entries = [];
4587
- try {
4588
- entries = fs3.readdirSync(current, { withFileTypes: true });
4589
- } catch {
4590
- continue;
4591
- }
4592
- for (const entry of entries) {
4593
- const entryPath = path7.join(current, entry.name);
4594
- if (entry.isDirectory()) {
4595
- stack.push(entryPath);
4596
- continue;
4597
- }
4598
- if (!entry.isFile() || !entry.name.endsWith(".jsonl") || !entry.name.includes(normalizedSessionId)) continue;
4599
- const meta3 = readCodexSessionMeta(entryPath);
4600
- const metaSessionId = String(meta3?.id || "").trim();
4601
- if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
4602
- const metaWorkspace = String(meta3?.cwd || "").trim();
4603
- let mtimeMs = 0;
4604
- try {
4605
- mtimeMs = fs3.statSync(entryPath).mtimeMs;
4606
- } catch {
4607
- }
4608
- candidates.push({
4609
- path: entryPath,
4610
- mtimeMs,
4611
- workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
4612
- metaMatches: metaSessionId === normalizedSessionId
4613
- });
4614
- }
4615
- }
4616
- candidates.sort((a, b) => Number(b.workspaceMatches) - Number(a.workspaceMatches) || Number(b.metaMatches) - Number(a.metaMatches) || b.mtimeMs - a.mtimeMs);
4617
- return candidates[0]?.path || null;
4618
- }
4619
- function flattenCodexContent(content) {
4620
- if (typeof content === "string") return content.trim();
4621
- if (content == null) return "";
4622
- if (Array.isArray(content)) {
4623
- return content.map((entry) => flattenCodexContent(entry)).filter(Boolean).join("\n").trim();
4624
- }
4625
- if (typeof content === "object") {
4626
- const record2 = content;
4627
- if (typeof record2.text === "string") return record2.text.trim();
4628
- if (typeof record2.content === "string" || Array.isArray(record2.content)) return flattenCodexContent(record2.content);
4629
- if (typeof record2.output === "string") return record2.output.trim();
4630
- if (typeof record2.message === "string") return record2.message.trim();
4631
- }
4632
- return "";
4633
- }
4634
- function summarizeCodexToolCall(payload) {
4635
- const name = String(payload.name || payload.type || "tool").trim() || "tool";
4636
- const rawArguments = payload.arguments ?? payload.input;
4637
- let argumentValue = "";
4638
- if (typeof rawArguments === "string") {
4639
- const trimmed = rawArguments.trim();
4640
- try {
4641
- const parsed = JSON.parse(trimmed);
4642
- argumentValue = summarizeCodexToolArguments(parsed);
4643
- } catch {
4644
- argumentValue = trimmed;
4645
- }
4646
- } else {
4647
- argumentValue = summarizeCodexToolArguments(rawArguments);
4648
- }
4649
- return argumentValue ? `${name}: ${argumentValue}` : name;
4650
- }
4651
- function summarizeCodexToolArguments(value) {
4652
- if (typeof value === "string") return value.trim();
4653
- if (Array.isArray(value)) return value.map((entry) => String(entry)).join(" ").trim();
4654
- if (!value || typeof value !== "object") return "";
4655
- const record2 = value;
4656
- const direct = record2.command || record2.cmd || record2.query || record2.path || record2.prompt;
4657
- if (typeof direct === "string") return direct.trim();
4658
- if (Array.isArray(direct)) return direct.map((entry) => String(entry)).join(" ").trim();
4659
- try {
4660
- return JSON.stringify(record2).trim();
4661
- } catch {
4662
- return "";
4663
- }
4206
+ return records.map((record2) => sanitizeHistoryMessage(agentType, {
4207
+ ts: typeof record2?.ts === "string" ? record2.ts : new Date(Number(record2?.receivedAt) || Date.now()).toISOString(),
4208
+ receivedAt: Number(record2?.receivedAt) || Date.parse(record2?.ts || "") || Date.now(),
4209
+ role: record2?.role,
4210
+ content: String(record2?.content || ""),
4211
+ kind: record2?.kind || (record2?.role === "system" ? "session_start" : "standard"),
4212
+ senderName: record2?.senderName,
4213
+ agent: agentType,
4214
+ instanceId: record2?.instanceId,
4215
+ historySessionId: normalizeSavedHistorySessionId(record2?.historySessionId || normalizedSessionId),
4216
+ sessionTitle: record2?.sessionTitle,
4217
+ workspace: record2?.workspace
4218
+ })).filter(Boolean);
4219
+ }
4220
+ function callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, historySessionId, workspace) {
4221
+ const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, "readSession");
4222
+ if (!fn) return null;
4223
+ const result = fn({
4224
+ agentType,
4225
+ sessionId: historySessionId,
4226
+ historySessionId,
4227
+ workspace,
4228
+ format: canonicalHistory?.format,
4229
+ watchPath: canonicalHistory?.watchPath,
4230
+ args: { sessionId: historySessionId, historySessionId, workspace }
4231
+ });
4232
+ if (!result || typeof result !== "object") return null;
4233
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, result.messages || result.records);
4234
+ if (records.length === 0) return null;
4235
+ return {
4236
+ records,
4237
+ sourcePath: typeof result.sourcePath === "string" ? result.sourcePath : "",
4238
+ sourceMtimeMs: Number(result.sourceMtimeMs) || 0
4239
+ };
4664
4240
  }
4665
- function codexToolOutputContent(payload) {
4666
- const output = payload.output ?? payload.result ?? payload.content;
4667
- const text = flattenCodexContent(output);
4668
- if (text) return text;
4669
- if (output && typeof output === "object") {
4670
- try {
4671
- return JSON.stringify(output).trim();
4672
- } catch {
4673
- return "";
4674
- }
4675
- }
4676
- return "";
4241
+ function buildNativeHistoryReadResult(agentType, canonicalHistory, scripts, historySessionId, workspace) {
4242
+ const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
4243
+ if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
4244
+ return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
4677
4245
  }
4678
- function buildCodexNativeHistoryRecords(historySessionId, workspace) {
4246
+ function materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts) {
4679
4247
  const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4680
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return null;
4681
- try {
4682
- const transcriptPath = resolveCodexSessionTranscriptPath(normalizedSessionId, workspace);
4683
- if (!transcriptPath) return null;
4684
- const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
4685
- const records = [];
4686
- let fallbackTs = Date.now();
4687
- for (const line of lines) {
4688
- let parsed = null;
4689
- try {
4690
- parsed = JSON.parse(line);
4691
- } catch {
4692
- parsed = null;
4693
- }
4694
- if (!parsed) continue;
4695
- const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
4696
- fallbackTs = receivedAt + 1;
4697
- const type = String(parsed.type || "").trim();
4698
- const payload = parsed.payload && typeof parsed.payload === "object" ? parsed.payload : null;
4699
- if (!payload) continue;
4700
- if (type === "session_meta") {
4701
- const parsedSessionId = String(payload.id || "").trim();
4702
- if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
4703
- const parsedWorkspace = String(payload.cwd || workspace || "").trim();
4704
- if (records.length === 0 && parsedWorkspace) {
4705
- records.push({
4706
- ts: new Date(receivedAt).toISOString(),
4707
- receivedAt,
4708
- role: "system",
4709
- kind: "session_start",
4710
- content: parsedWorkspace,
4711
- agent: "codex-cli",
4712
- historySessionId: normalizedSessionId,
4713
- workspace: parsedWorkspace
4714
- });
4715
- }
4716
- continue;
4717
- }
4718
- if (type !== "response_item") continue;
4719
- const payloadType = String(payload.type || "").trim();
4720
- if (payloadType === "message") {
4721
- const role = String(payload.role || "").trim();
4722
- if (role !== "user" && role !== "assistant") continue;
4723
- const content = flattenCodexContent(payload.content);
4724
- if (!content) continue;
4725
- records.push({
4726
- ts: new Date(receivedAt).toISOString(),
4727
- receivedAt,
4728
- role,
4729
- content,
4730
- kind: "standard",
4731
- agent: "codex-cli",
4732
- historySessionId: normalizedSessionId
4733
- });
4734
- continue;
4735
- }
4736
- if (payloadType === "function_call" || payloadType === "custom_tool_call") {
4737
- const content = summarizeCodexToolCall(payload);
4738
- if (!content) continue;
4739
- records.push({
4740
- ts: new Date(receivedAt).toISOString(),
4741
- receivedAt,
4742
- role: "assistant",
4743
- content,
4744
- kind: "tool",
4745
- senderName: "Tool",
4746
- agent: "codex-cli",
4747
- historySessionId: normalizedSessionId
4748
- });
4749
- continue;
4750
- }
4751
- if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
4752
- const content = codexToolOutputContent(payload);
4753
- if (!content) continue;
4754
- records.push({
4755
- ts: new Date(receivedAt).toISOString(),
4756
- receivedAt,
4757
- role: "assistant",
4758
- content,
4759
- kind: "tool",
4760
- senderName: "Tool",
4761
- agent: "codex-cli",
4762
- historySessionId: normalizedSessionId
4763
- });
4764
- }
4765
- }
4766
- return records;
4767
- } catch {
4768
- return null;
4769
- }
4248
+ if (!normalizedSessionId) return false;
4249
+ const nativeResult = callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
4250
+ const nativeRecords = nativeResult?.records || [];
4251
+ if (nativeRecords.length === 0) return false;
4252
+ const normalizedRecords = nativeRecords.map((record2) => ({
4253
+ ...record2,
4254
+ agent: agentType,
4255
+ historySessionId: normalizedSessionId
4256
+ }));
4257
+ const existingSessionStart = readExistingSessionStartRecord(agentType, normalizedSessionId);
4258
+ const records = existingSessionStart && normalizedRecords[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId, agent: agentType }, ...normalizedRecords] : normalizedRecords;
4259
+ return rewriteCanonicalSavedHistory(agentType, normalizedSessionId, records);
4770
4260
  }
4771
- function rebuildCodexSavedHistoryFromNativeSession(historySessionId, workspace) {
4772
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
4773
- if (!normalizedSessionId || !isUuidLikeSessionId(normalizedSessionId)) return false;
4774
- const records = buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
4775
- if (!records || records.length === 0) return false;
4776
- const existingSessionStart = readExistingSessionStartRecord("codex-cli", normalizedSessionId);
4777
- const recordsToWrite = existingSessionStart && records[0]?.kind !== "session_start" ? [{ ...existingSessionStart, historySessionId: normalizedSessionId }, ...records] : records;
4778
- return rewriteCanonicalSavedHistory("codex-cli", normalizedSessionId, recordsToWrite);
4261
+ function materializeProviderNativeHistory(agentType, canonicalHistory, historySessionId, workspace, scripts) {
4262
+ if (!canonicalHistory || canonicalHistory.mode !== "materialized-mirror") return false;
4263
+ return materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts);
4779
4264
  }
4780
4265
  function isNativeSourceCanonicalHistory(canonicalHistory) {
4781
4266
  if (!canonicalHistory) return false;
@@ -4783,21 +4268,15 @@ function isNativeSourceCanonicalHistory(canonicalHistory) {
4783
4268
  if (canonicalHistory.mode === "materialized-mirror") return false;
4784
4269
  return true;
4785
4270
  }
4786
- function buildNativeHistoryRecords(canonicalHistory, historySessionId, workspace) {
4787
- const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || "");
4788
- if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
4789
- if (canonicalHistory.format === "hermes-json") return buildHermesNativeHistoryRecords(normalizedSessionId);
4790
- if (canonicalHistory.format === "claude-jsonl") return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
4791
- if (canonicalHistory.format === "codex-jsonl") return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
4792
- return null;
4793
- }
4794
4271
  function readProviderChatHistory(agentType, options = {}) {
4795
4272
  if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
4796
- const records = buildNativeHistoryRecords(options.canonicalHistory, options.historySessionId, options.workspace);
4797
- if (!records) return { messages: [], hasMore: false, source: "native-unavailable" };
4273
+ const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
4274
+ if (!nativeResult) return { messages: [], hasMore: false, source: "native-unavailable" };
4798
4275
  return {
4799
- ...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
4800
- source: "provider-native"
4276
+ ...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
4277
+ source: "provider-native",
4278
+ sourcePath: nativeResult.sourcePath,
4279
+ sourceMtimeMs: nativeResult.sourceMtimeMs
4801
4280
  };
4802
4281
  }
4803
4282
  return {
@@ -4830,68 +4309,64 @@ function buildNativeSessionSummary(agentType, historySessionId, records, sourceP
4830
4309
  sourceMtimeMs
4831
4310
  };
4832
4311
  }
4833
- function listFilesRecursive(root, predicate) {
4834
- if (!fs3.existsSync(root)) return [];
4835
- const results = [];
4836
- const stack = [root];
4837
- while (stack.length > 0) {
4838
- const current = stack.pop();
4839
- if (!current) continue;
4840
- let entries = [];
4841
- try {
4842
- entries = fs3.readdirSync(current, { withFileTypes: true });
4843
- } catch {
4844
- continue;
4845
- }
4846
- for (const entry of entries) {
4847
- const entryPath = path7.join(current, entry.name);
4848
- if (entry.isDirectory()) {
4849
- stack.push(entryPath);
4850
- continue;
4851
- }
4852
- if (predicate(entryPath, entry)) results.push(entryPath);
4853
- }
4854
- }
4855
- return results;
4312
+ function normalizeProviderNativeHistorySessionSummary(agentType, item) {
4313
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
4314
+ if (!historySessionId) return null;
4315
+ const sourcePath = typeof item?.sourcePath === "string" ? item.sourcePath : "";
4316
+ const sourceMtimeMs = Number(item?.sourceMtimeMs) || 0;
4317
+ const firstMessageAt = Number(item?.firstMessageAt) || sourceMtimeMs || Date.now();
4318
+ const lastMessageAt = Number(item?.lastMessageAt) || firstMessageAt;
4319
+ const messageCount = Math.max(0, Number(item?.messageCount) || 0);
4320
+ return {
4321
+ historySessionId,
4322
+ sessionTitle: typeof item?.sessionTitle === "string" ? item.sessionTitle : void 0,
4323
+ messageCount,
4324
+ firstMessageAt,
4325
+ lastMessageAt,
4326
+ preview: typeof item?.preview === "string" ? item.preview : void 0,
4327
+ workspace: typeof item?.workspace === "string" ? item.workspace : void 0,
4328
+ source: "provider-native",
4329
+ sourcePath,
4330
+ sourceMtimeMs
4331
+ };
4856
4332
  }
4857
- function collectNativeHistorySessionSummaries(agentType, canonicalHistory) {
4333
+ function collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
4334
+ const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, "listSessions");
4335
+ if (!fn) return null;
4336
+ const result = fn({
4337
+ agentType,
4338
+ format: canonicalHistory.format,
4339
+ watchPath: canonicalHistory.watchPath,
4340
+ args: {}
4341
+ });
4342
+ if (!result || typeof result !== "object") return [];
4343
+ const sessions = Array.isArray(result.sessions) ? result.sessions : [];
4858
4344
  const summaries = [];
4859
- if (canonicalHistory.format === "hermes-json") {
4860
- const root = path7.join(os5.homedir(), ".hermes", "sessions");
4861
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && /^session_.+\.json$/.test(entry.name))) {
4862
- const fileName = path7.basename(filePath);
4863
- const historySessionId = fileName.replace(/^session_/, "").replace(/\.json$/, "");
4864
- const records = buildHermesNativeHistoryRecords(historySessionId);
4865
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
4866
- if (summary) summaries.push(summary);
4867
- }
4868
- } else if (canonicalHistory.format === "claude-jsonl") {
4869
- const root = path7.join(os5.homedir(), ".claude", "projects");
4870
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
4871
- const historySessionId = path7.basename(filePath, ".jsonl");
4872
- const records = buildClaudeNativeHistoryRecords(historySessionId);
4873
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
4874
- if (summary) summaries.push(summary);
4875
- }
4876
- } else if (canonicalHistory.format === "codex-jsonl") {
4877
- const root = path7.join(os5.homedir(), ".codex", "sessions");
4878
- const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
4879
- for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith(".jsonl"))) {
4880
- const meta3 = readCodexSessionMeta(filePath);
4881
- const historySessionId = String(meta3?.id || path7.basename(filePath).match(uuidPattern)?.[1] || "").trim();
4345
+ for (const item of sessions) {
4346
+ if (Array.isArray(item?.messages || item?.records)) {
4347
+ const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || "");
4882
4348
  if (!historySessionId) continue;
4883
- const records = buildCodexNativeHistoryRecords(historySessionId, String(meta3?.cwd || "").trim() || void 0);
4884
- const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
4885
- if (summary) summaries.push(summary);
4349
+ const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, item.messages || item.records);
4350
+ const summary2 = buildNativeSessionSummary(agentType, historySessionId, records, typeof item?.sourcePath === "string" ? item.sourcePath : "");
4351
+ if (summary2) {
4352
+ if (Number(item?.sourceMtimeMs)) summary2.sourceMtimeMs = Number(item.sourceMtimeMs);
4353
+ summaries.push(summary2);
4354
+ }
4355
+ continue;
4886
4356
  }
4357
+ const summary = normalizeProviderNativeHistorySessionSummary(agentType, item);
4358
+ if (summary) summaries.push(summary);
4887
4359
  }
4888
4360
  return sortSavedHistorySessionSummaries(summaries);
4889
4361
  }
4362
+ function collectNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) {
4363
+ return collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) || [];
4364
+ }
4890
4365
  function listProviderHistorySessions(agentType, options = {}) {
4891
4366
  if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
4892
4367
  const offset = Math.max(0, options.offset || 0);
4893
4368
  const limit = Math.max(1, options.limit || 30);
4894
- const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory);
4369
+ const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory, options.scripts);
4895
4370
  return {
4896
4371
  sessions: summaries.slice(offset, offset + limit),
4897
4372
  hasMore: offset + limit < summaries.length,
@@ -8183,7 +7658,8 @@ async function handleChatHistory(h, args) {
8183
7658
  offset: offset || 0,
8184
7659
  limit: limit || 30,
8185
7660
  excludeRecentCount,
8186
- historyBehavior: provider?.historyBehavior
7661
+ historyBehavior: provider?.historyBehavior,
7662
+ scripts: provider?.scripts
8187
7663
  });
8188
7664
  return { success: true, ...result, agent: agentStr };
8189
7665
  } catch (e) {
@@ -14839,13 +14315,8 @@ var init_cli_provider_instance = __esm({
14839
14315
  historyWriter;
14840
14316
  runtimeMessages = [];
14841
14317
  lastPersistedHistoryMessages = [];
14842
- lastCanonicalHermesSyncMtimeMs = 0;
14843
- lastCanonicalHermesExistCheckAt = 0;
14844
- lastCanonicalHermesWatchPath = void 0;
14845
- lastCanonicalClaudeRebuildMtimeMs = 0;
14846
- lastCanonicalClaudeCheckAt = 0;
14847
- lastCanonicalCodexRebuildMtimeMs = 0;
14848
- lastCanonicalCodexCheckAt = 0;
14318
+ lastNativeSourceCanonicalCheckAt = 0;
14319
+ lastNativeSourceCanonicalCacheKey = void 0;
14849
14320
  cachedSqliteDb = null;
14850
14321
  cachedSqliteDbPath = null;
14851
14322
  cachedSqliteDbMissingUntil = 0;
@@ -15546,76 +15017,44 @@ ${effect.notification.body || ""}`.trim();
15546
15017
  const canonicalHistory = this.provider.canonicalHistory;
15547
15018
  if (!canonicalHistory) return false;
15548
15019
  if (isNativeSourceCanonicalHistory(canonicalHistory)) {
15020
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir].join("\0");
15021
+ const now = Date.now();
15022
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
15023
+ return true;
15024
+ }
15025
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
15026
+ this.lastNativeSourceCanonicalCheckAt = now;
15549
15027
  const restoredHistory = readProviderChatHistory(this.type, {
15550
15028
  canonicalHistory,
15551
15029
  historySessionId: this.providerSessionId,
15552
15030
  workspace: this.workingDir,
15553
15031
  offset: 0,
15554
15032
  limit: Number.MAX_SAFE_INTEGER,
15555
- historyBehavior: this.provider.historyBehavior
15033
+ historyBehavior: this.provider.historyBehavior,
15034
+ scripts: this.provider.scripts
15556
15035
  });
15557
- if (restoredHistory.source !== "provider-native") return false;
15558
- this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
15559
- role: message.role,
15560
- content: message.content,
15561
- kind: message.kind,
15562
- senderName: message.senderName,
15563
- receivedAt: message.receivedAt
15564
- }));
15036
+ if (restoredHistory.source === "provider-native") {
15037
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
15038
+ role: message.role,
15039
+ content: message.content,
15040
+ kind: message.kind,
15041
+ senderName: message.senderName,
15042
+ receivedAt: message.receivedAt
15043
+ }));
15044
+ }
15565
15045
  return true;
15566
15046
  }
15567
15047
  try {
15568
- let rebuilt = false;
15569
- if (canonicalHistory.format === "hermes-json") {
15570
- const watchPath = canonicalHistory.watchPath.replace(/^~/, os13.homedir()).replace("{{sessionId}}", this.providerSessionId);
15571
- const now = Date.now();
15572
- if (watchPath !== this.lastCanonicalHermesWatchPath || now - this.lastCanonicalHermesExistCheckAt >= 2e3) {
15573
- this.lastCanonicalHermesWatchPath = watchPath;
15574
- this.lastCanonicalHermesExistCheckAt = now;
15575
- if (!fs5.existsSync(watchPath)) return false;
15576
- } else if (this.lastCanonicalHermesSyncMtimeMs === 0) {
15577
- if (!fs5.existsSync(watchPath)) return false;
15578
- }
15579
- const stat4 = fs5.statSync(watchPath);
15580
- if (stat4.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
15581
- rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
15582
- if (rebuilt) this.lastCanonicalHermesSyncMtimeMs = stat4.mtimeMs;
15583
- } else if (canonicalHistory.format === "claude-jsonl") {
15584
- const now = Date.now();
15585
- if (now - this.lastCanonicalClaudeCheckAt < 2e3 && this.lastCanonicalClaudeRebuildMtimeMs !== 0) {
15586
- return true;
15587
- }
15588
- this.lastCanonicalClaudeCheckAt = now;
15589
- const claudeProjectsDir = path11.join(os13.homedir(), ".claude", "projects");
15590
- const workspaceSegment = typeof this.workingDir === "string" ? this.workingDir.replace(/[\\/]/g, "-").replace(/^-+/, "") : "";
15591
- const transcriptFile = path11.join(claudeProjectsDir, workspaceSegment, `${this.providerSessionId}.jsonl`);
15592
- let transcriptMtime = 0;
15593
- try {
15594
- transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
15595
- } catch {
15596
- }
15597
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalClaudeRebuildMtimeMs) return true;
15598
- rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
15599
- if (rebuilt) this.lastCanonicalClaudeRebuildMtimeMs = transcriptMtime || Date.now();
15600
- } else if (canonicalHistory.format === "codex-jsonl") {
15601
- const now = Date.now();
15602
- if (now - this.lastCanonicalCodexCheckAt < 2e3 && this.lastCanonicalCodexRebuildMtimeMs !== 0) {
15603
- return true;
15604
- }
15605
- this.lastCanonicalCodexCheckAt = now;
15606
- const transcriptFile = resolveCodexSessionTranscriptPath(this.providerSessionId, this.workingDir);
15607
- let transcriptMtime = 0;
15608
- if (transcriptFile) {
15609
- try {
15610
- transcriptMtime = fs5.statSync(transcriptFile).mtimeMs;
15611
- } catch {
15612
- }
15613
- }
15614
- if (transcriptMtime > 0 && transcriptMtime <= this.lastCanonicalCodexRebuildMtimeMs) return true;
15615
- rebuilt = rebuildCodexSavedHistoryFromNativeSession(this.providerSessionId, this.workingDir);
15616
- if (rebuilt) this.lastCanonicalCodexRebuildMtimeMs = transcriptMtime || Date.now();
15048
+ const cacheKey = [this.type, this.providerSessionId, this.workingDir, canonicalHistory.mode || "materialized-mirror"].join("\0");
15049
+ const now = Date.now();
15050
+ if (cacheKey === this.lastNativeSourceCanonicalCacheKey && now - this.lastNativeSourceCanonicalCheckAt < 2e3) {
15051
+ return true;
15052
+ }
15053
+ this.lastNativeSourceCanonicalCacheKey = cacheKey;
15054
+ this.lastNativeSourceCanonicalCheckAt = now;
15055
+ if (!materializeProviderNativeHistory(this.type, canonicalHistory, this.providerSessionId, this.workingDir, this.provider.scripts)) {
15056
+ return false;
15617
15057
  }
15618
- if (!rebuilt) return false;
15619
15058
  const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
15620
15059
  this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
15621
15060
  role: message.role,
@@ -15638,7 +15077,8 @@ ${effect.notification.body || ""}`.trim();
15638
15077
  workspace: this.workingDir,
15639
15078
  offset: 0,
15640
15079
  limit: Number.MAX_SAFE_INTEGER,
15641
- historyBehavior: this.provider.historyBehavior
15080
+ historyBehavior: this.provider.historyBehavior,
15081
+ scripts: this.provider.scripts
15642
15082
  }) : (() => {
15643
15083
  this.historyWriter.compactHistorySession(this.type, this.providerSessionId, this.provider.historyBehavior);
15644
15084
  return readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId, 0, this.provider.historyBehavior);
@@ -34175,10 +33615,10 @@ var init_readdirp = __esm({
34175
33615
  }
34176
33616
  async _formatEntry(dirent, path28) {
34177
33617
  let entry;
34178
- const basename10 = this._isDirent ? dirent.name : dirent;
33618
+ const basename9 = this._isDirent ? dirent.name : dirent;
34179
33619
  try {
34180
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path28, basename10));
34181
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename10 };
33620
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path28, basename9));
33621
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
34182
33622
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
34183
33623
  } catch (err) {
34184
33624
  this._onError(err);
@@ -34709,9 +34149,9 @@ var init_handler2 = __esm({
34709
34149
  _watchWithNodeFs(path28, listener) {
34710
34150
  const opts = this.fsw.options;
34711
34151
  const directory = sp.dirname(path28);
34712
- const basename10 = sp.basename(path28);
34152
+ const basename9 = sp.basename(path28);
34713
34153
  const parent = this.fsw._getWatchedDir(directory);
34714
- parent.add(basename10);
34154
+ parent.add(basename9);
34715
34155
  const absolutePath = sp.resolve(path28);
34716
34156
  const options = {
34717
34157
  persistent: opts.persistent
@@ -34721,7 +34161,7 @@ var init_handler2 = __esm({
34721
34161
  let closer;
34722
34162
  if (opts.usePolling) {
34723
34163
  const enableBin = opts.interval !== opts.binaryInterval;
34724
- options.interval = enableBin && isBinaryPath(basename10) ? opts.binaryInterval : opts.interval;
34164
+ options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
34725
34165
  closer = setFsWatchFileListener(path28, absolutePath, options, {
34726
34166
  listener,
34727
34167
  rawEmitter: this.fsw._emitRaw
@@ -34744,10 +34184,10 @@ var init_handler2 = __esm({
34744
34184
  return;
34745
34185
  }
34746
34186
  const dirname10 = sp.dirname(file2);
34747
- const basename10 = sp.basename(file2);
34187
+ const basename9 = sp.basename(file2);
34748
34188
  const parent = this.fsw._getWatchedDir(dirname10);
34749
34189
  let prevStats = stats;
34750
- if (parent.has(basename10))
34190
+ if (parent.has(basename9))
34751
34191
  return;
34752
34192
  const listener = async (path28, newStats) => {
34753
34193
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
@@ -34772,9 +34212,9 @@ var init_handler2 = __esm({
34772
34212
  prevStats = newStats2;
34773
34213
  }
34774
34214
  } catch (error48) {
34775
- this.fsw._remove(dirname10, basename10);
34215
+ this.fsw._remove(dirname10, basename9);
34776
34216
  }
34777
- } else if (parent.has(basename10)) {
34217
+ } else if (parent.has(basename9)) {
34778
34218
  const at = newStats.atimeMs;
34779
34219
  const mt = newStats.mtimeMs;
34780
34220
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -35787,6 +35227,7 @@ function validateProviderDefinition(raw) {
35787
35227
  warnings.push("Extension providers should have extensionId");
35788
35228
  }
35789
35229
  validateCapabilities(provider, controls, errors);
35230
+ validateCanonicalHistory(provider.canonicalHistory, errors);
35790
35231
  for (const control of controls) {
35791
35232
  validateControl(control, errors);
35792
35233
  }
@@ -35844,6 +35285,39 @@ function validateCapabilities(provider, controls, errors) {
35844
35285
  errors.push("providers declaring controls must set capabilities.controls.typedResults=true");
35845
35286
  }
35846
35287
  }
35288
+ function validateCanonicalHistory(raw, errors) {
35289
+ if (raw === void 0) return;
35290
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
35291
+ errors.push("canonicalHistory must be an object");
35292
+ return;
35293
+ }
35294
+ const canonicalHistory = raw;
35295
+ const format = canonicalHistory.format;
35296
+ if (format !== void 0 && (typeof format !== "string" || !format.trim())) {
35297
+ errors.push("canonicalHistory.format must be a non-empty string when provided");
35298
+ }
35299
+ const watchPath = canonicalHistory.watchPath;
35300
+ if (watchPath !== void 0 && (typeof watchPath !== "string" || !watchPath.trim())) {
35301
+ errors.push("canonicalHistory.watchPath must be a non-empty string when provided");
35302
+ }
35303
+ const mode = canonicalHistory.mode;
35304
+ if (mode !== void 0 && !["native-source", "materialized-mirror", "disabled"].includes(String(mode))) {
35305
+ errors.push("canonicalHistory.mode must be one of: native-source, materialized-mirror, disabled");
35306
+ }
35307
+ const scripts = canonicalHistory.scripts;
35308
+ if (scripts === void 0) return;
35309
+ if (!scripts || typeof scripts !== "object" || Array.isArray(scripts)) {
35310
+ errors.push("canonicalHistory.scripts must be an object");
35311
+ return;
35312
+ }
35313
+ const scriptConfig = scripts;
35314
+ for (const key of ["readSession", "listSessions"]) {
35315
+ const value = scriptConfig[key];
35316
+ if (typeof value !== "string" || !value.trim()) {
35317
+ errors.push(`canonicalHistory.scripts.${key} must be a non-empty string`);
35318
+ }
35319
+ }
35320
+ }
35847
35321
  function validateControl(control, errors) {
35848
35322
  if (!control || typeof control !== "object") {
35849
35323
  errors.push("controls: each control must be an object");
@@ -39124,12 +38598,13 @@ var init_router = __esm({
39124
38598
  const wantsAll = args?.all === true;
39125
38599
  const offset = wantsAll ? 0 : Math.max(0, Number(args?.offset) || 0);
39126
38600
  const limit = wantsAll ? Number.MAX_SAFE_INTEGER : Math.max(1, Math.min(100, Number(args?.limit) || 30));
39127
- const providerMeta = this.deps.providerLoader.getMeta(providerType);
38601
+ const providerMeta = this.deps.providerLoader.resolve?.(providerType) || this.deps.providerLoader.getMeta(providerType);
39128
38602
  const { sessions: historySessions, hasMore, source } = listProviderHistorySessions(providerType, {
39129
38603
  canonicalHistory: providerMeta?.canonicalHistory,
39130
38604
  offset,
39131
38605
  limit,
39132
- historyBehavior: providerMeta?.historyBehavior
38606
+ historyBehavior: providerMeta?.historyBehavior,
38607
+ scripts: providerMeta?.scripts
39133
38608
  });
39134
38609
  const state = loadState();
39135
38610
  const savedSessions = getSavedProviderSessions(state, { providerType, kind });
@@ -39155,7 +38630,10 @@ var init_router = __esm({
39155
38630
  messageCount: session.messageCount,
39156
38631
  firstMessageAt: session.firstMessageAt,
39157
38632
  lastMessageAt: session.lastMessageAt,
39158
- canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById
38633
+ canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById,
38634
+ historySource: session.source,
38635
+ sourcePath: session.sourcePath,
38636
+ sourceMtimeMs: session.sourceMtimeMs
39159
38637
  };
39160
38638
  }),
39161
38639
  hasMore,
@@ -47844,6 +47322,7 @@ var init_server_connection = __esm({
47844
47322
  iceServers = null;
47845
47323
  planLimits = null;
47846
47324
  compatBlocked = false;
47325
+ fatalAuthRejectedReason = null;
47847
47326
  get authTimeoutMs() {
47848
47327
  return Math.max(1, this.options.authTimeoutMs ?? 15e3);
47849
47328
  }
@@ -47960,6 +47439,7 @@ var init_server_connection = __esm({
47960
47439
  this.clearAuthTimeout();
47961
47440
  this.reconnectAttempts = 0;
47962
47441
  this.compatBlocked = false;
47442
+ this.fatalAuthRejectedReason = null;
47963
47443
  const payload = message.payload;
47964
47444
  this.userPlan = payload.plan || "free";
47965
47445
  this.iceServers = payload.iceServers || null;
@@ -47973,8 +47453,20 @@ var init_server_connection = __esm({
47973
47453
  this.startHeartbeat();
47974
47454
  } else if (message.type === "auth_error") {
47975
47455
  this.clearAuthTimeout();
47976
- LOG.error("Server", `Auth failed: ${message.payload.reason}`);
47456
+ const payload = message.payload;
47457
+ const reason = String(payload.reason || "Authentication failed");
47458
+ this.fatalAuthRejectedReason = reason;
47459
+ LOG.error("Server", `Auth failed: ${reason}`);
47460
+ if (reason === "machine_limit_exceeded") {
47461
+ LOG.error("Server", `[ServerConn] ${payload.message || "Machine limit exceeded. Remove an unused machine in the dashboard or upgrade your plan."}`);
47462
+ } else {
47463
+ LOG.error("Server", `[ServerConn] Saved machine credentials were rejected. Run 'adhdev setup --force' to re-authenticate, then restart the daemon.`);
47464
+ }
47977
47465
  this.setState("error");
47466
+ try {
47467
+ this.ws?.close(1e3, "Authentication rejected");
47468
+ } catch {
47469
+ }
47978
47470
  return;
47979
47471
  } else if (message.type === "version_mismatch") {
47980
47472
  const p = message.payload;
@@ -48007,6 +47499,18 @@ var init_server_connection = __esm({
48007
47499
  LOG.info("Server", `[ServerConn] WebSocket closed: ${code} ${reason}`);
48008
47500
  this.clearTimers();
48009
47501
  this.ws = null;
47502
+ if (code === 4001 || this.fatalAuthRejectedReason) {
47503
+ const authReason = this.fatalAuthRejectedReason;
47504
+ const reasonText = authReason ? `: ${authReason}` : "";
47505
+ LOG.error("Server", `[ServerConn] Authentication rejected${reasonText}. Not reconnecting with the same credentials.`);
47506
+ if (authReason === "machine_limit_exceeded") {
47507
+ LOG.error("Server", `[ServerConn] Remove an unused machine in the dashboard or upgrade your plan, then restart the daemon.`);
47508
+ } else {
47509
+ LOG.error("Server", `[ServerConn] Run 'adhdev setup --force', then 'adhdev daemon:restart'.`);
47510
+ }
47511
+ this.setState("disconnected");
47512
+ return;
47513
+ }
48010
47514
  if (code === 4011) {
48011
47515
  LOG.info("Server", `[ServerConn] \u26A0 Evicted by another machine. Not reconnecting.`);
48012
47516
  LOG.info("Server", `[ServerConn] This connection was released because another machine connected.`);
@@ -57410,7 +56914,7 @@ var init_adhdev_daemon = __esm({
57410
56914
  init_version();
57411
56915
  init_src();
57412
56916
  init_runtime_defaults();
57413
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.45" });
56917
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.47" });
57414
56918
  AdhdevDaemon = class _AdhdevDaemon {
57415
56919
  localHttpServer = null;
57416
56920
  localWss = null;