@adhdev/daemon-core 0.9.82-rc.173 → 0.9.82-rc.174

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.173",
3
+ "version": "0.9.82-rc.174",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -106,12 +106,21 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
106
106
  sourcePath = newestRecentFileAcrossGlob(resolved, filePat, windowMs, sessionFloor);
107
107
  } else {
108
108
  let stat: fs.Stats | null = null;
109
- try { stat = fs.statSync(resolved); } catch { return null; }
110
- if (stat.isFile()) {
109
+ try { stat = fs.statSync(resolved); } catch { /* fall through to date-walk fallback */ }
110
+ if (stat && stat.isFile()) {
111
111
  sourcePath = resolved;
112
- } else if (stat.isDirectory()) {
112
+ } else if (stat && stat.isDirectory()) {
113
113
  sourcePath = newestRecentFile(resolved, filePat, windowMs, sessionFloor);
114
114
  }
115
+ // Date-templated directories (e.g. ~/.codex/sessions/{yyyy}/{mm}/{dd})
116
+ // expand to today's UTC dir. A session started before UTC midnight
117
+ // keeps appending to its previous-day file, so today's dir never
118
+ // contains the live transcript. When the templated dir is empty
119
+ // or doesn't exist yet, walk the last 3 UTC days at the same depth
120
+ // and pick the newest matching rollout across all of them.
121
+ if (!sourcePath && hasDateTemplateSegment(src.path)) {
122
+ sourcePath = newestRecentFileAcrossDateWindow(src.path, input, filePat, windowMs, sessionFloor);
123
+ }
115
124
  }
116
125
  if (!sourcePath) return null;
117
126
 
@@ -367,6 +376,80 @@ function newestRecentFileAcrossGlob(template: string, pattern: RegExp, windowMs:
367
376
  return best ? best.p : null;
368
377
  }
369
378
 
379
+ function hasDateTemplateSegment(template: string): boolean {
380
+ return /\{yyyy\}|\{mm\}|\{dd\}/.test(template);
381
+ }
382
+
383
+ /**
384
+ * Walk the last N UTC days of a date-templated path (e.g.
385
+ * `~/.codex/sessions/{yyyy}/{mm}/{dd}`) and return the newest matching
386
+ * file across all of them. Codex sessions started just before UTC
387
+ * midnight keep writing to their original day's file even after the
388
+ * date rolls over, so today's expanded dir alone misses the live
389
+ * transcript.
390
+ */
391
+ function newestRecentFileAcrossDateWindow(
392
+ template: string,
393
+ input: NativeHistoryInput,
394
+ pattern: RegExp,
395
+ windowMs: number,
396
+ sessionFloorMs: number,
397
+ ): string | null {
398
+ const cutoff = Math.max(Date.now() - windowMs, sessionFloorMs);
399
+ let best: { p: string; mtime: number } | null = null;
400
+ for (let dayOffset = 0; dayOffset < 3; dayOffset += 1) {
401
+ const dayMs = Date.now() - dayOffset * 24 * 60 * 60 * 1000;
402
+ const dayInput: NativeHistoryInput = { ...input, sessionStartedAtMs: sessionFloorMs };
403
+ const resolved = expandPathForDate(template, dayInput, new Date(dayMs));
404
+ if (!resolved) continue;
405
+ let entries: fs.Dirent[];
406
+ try { entries = fs.readdirSync(resolved, { withFileTypes: true }); } catch { continue; }
407
+ for (const e of entries) {
408
+ if (!e.isFile() || !pattern.test(e.name)) continue;
409
+ const p = path.join(resolved, e.name);
410
+ const mtime = safeMtimeMs(p);
411
+ if (mtime < cutoff) continue;
412
+ if (!best || mtime > best.mtime) best = { p, mtime };
413
+ }
414
+ }
415
+ return best ? best.p : null;
416
+ }
417
+
418
+ function expandPathForDate(template: string, input: NativeHistoryInput, day: Date): string | null {
419
+ // Reuse expandPath logic but stamp {yyyy}/{mm}/{dd} from the given day.
420
+ if (!template) return null;
421
+ let out = template;
422
+ if (out.startsWith('~/') || out === '~') {
423
+ out = path.join(os.homedir(), out.slice(2));
424
+ }
425
+ out = out.replace(/\$\{([A-Z_][A-Z0-9_]*)(?::-(.*?))?\}/g, (_m, name, fallback) => {
426
+ const v = input.envOverrides?.[name] ?? process.env[name];
427
+ return v != null && v !== '' ? v : (fallback ?? '');
428
+ });
429
+ if (out.startsWith('~/')) out = path.join(os.homedir(), out.slice(2));
430
+ const workspaceRaw = input.workspace ?? '';
431
+ let workspaceResolved = workspaceRaw;
432
+ if (workspaceRaw) {
433
+ try { workspaceResolved = fs.realpathSync(workspaceRaw); } catch { /* keep raw */ }
434
+ }
435
+ const vars: Record<string, string> = {
436
+ cwd: workspaceResolved,
437
+ cwd_dashed: workspaceResolved.replace(/\//g, '-'),
438
+ session_id: input.providerSessionId || input.sessionId || input.historySessionId || '',
439
+ yyyy: String(day.getUTCFullYear()),
440
+ mm: String(day.getUTCMonth() + 1).padStart(2, '0'),
441
+ dd: String(day.getUTCDate()).padStart(2, '0'),
442
+ };
443
+ let missing = false;
444
+ out = out.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, (_m, name) => {
445
+ const v = vars[name] ?? '';
446
+ if (!v) missing = true;
447
+ return v;
448
+ });
449
+ if (missing) return null;
450
+ return out;
451
+ }
452
+
370
453
  function newestRecentFile(dir: string, pattern: RegExp, windowMs: number, sessionFloorMs = 0): string | null {
371
454
  let entries: fs.Dirent[];
372
455
  try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return null; }