@adhdev/daemon-core 0.9.82-rc.186 → 0.9.82-rc.188

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.
Files changed (55) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +1 -0
  2. package/dist/commands/cli-manager.d.ts +2 -1
  3. package/dist/commands/mesh-coordinator.d.ts +13 -0
  4. package/dist/commands/router.d.ts +5 -1
  5. package/dist/config/chat-history.d.ts +1 -0
  6. package/dist/git/git-commands.d.ts +2 -0
  7. package/dist/git/git-types.d.ts +2 -0
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.js +15461 -14552
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +14411 -13502
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/mesh/beads-db.d.ts +1 -0
  14. package/dist/mesh/mesh-events.d.ts +46 -1
  15. package/dist/mesh/mesh-work-queue.d.ts +1 -0
  16. package/dist/providers/cli-provider-instance.d.ts +4 -0
  17. package/dist/providers/contracts.d.ts +32 -1
  18. package/dist/providers/native-history/dispatcher.d.ts +2 -0
  19. package/dist/providers/sdk/v1/types/common/index.d.ts +35 -1
  20. package/dist/providers/spec/cli-adapter.d.ts +1 -0
  21. package/dist/providers/spec/driver.d.ts +6 -1
  22. package/dist/providers/spec/native-history-executor.d.ts +2 -0
  23. package/dist/providers/spec/schema.gen.d.ts +22 -0
  24. package/dist/providers/spec/types.d.ts +10 -0
  25. package/dist/repo-mesh-types.d.ts +6 -0
  26. package/package.json +1 -1
  27. package/src/boot/daemon-lifecycle.ts +2 -0
  28. package/src/commands/chat-commands.ts +206 -18
  29. package/src/commands/cli-manager.ts +56 -14
  30. package/src/commands/mesh-coordinator.ts +110 -5
  31. package/src/commands/router.ts +146 -21
  32. package/src/config/chat-history.ts +4 -0
  33. package/src/git/git-commands.ts +20 -2
  34. package/src/git/git-status.ts +35 -6
  35. package/src/git/git-types.ts +2 -0
  36. package/src/index.ts +2 -2
  37. package/src/mesh/beads-db.ts +4 -0
  38. package/src/mesh/mesh-events.ts +264 -4
  39. package/src/mesh/mesh-work-queue.ts +4 -0
  40. package/src/providers/cli-provider-instance.ts +122 -13
  41. package/src/providers/contracts.d.ts +55 -0
  42. package/src/providers/contracts.ts +36 -1
  43. package/src/providers/native-history/dispatcher.ts +126 -17
  44. package/src/providers/provider-loader.ts +4 -7
  45. package/src/providers/provider-schema.ts +56 -1
  46. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +46 -0
  47. package/src/providers/sdk/v1/types/common/index.ts +19 -0
  48. package/src/providers/spec/cli-adapter.ts +32 -5
  49. package/src/providers/spec/driver.ts +68 -1
  50. package/src/providers/spec/evaluator.ts +11 -1
  51. package/src/providers/spec/native-history-executor.ts +93 -27
  52. package/src/providers/spec/schema.gen.ts +12 -1
  53. package/src/providers/spec/schema.json +21 -1
  54. package/src/providers/spec/types.ts +10 -0
  55. package/src/repo-mesh-types.ts +6 -0
@@ -58,6 +58,7 @@ export interface NativeHistoryMessage {
58
58
  content: string;
59
59
  receivedAt: number;
60
60
  kind?: string;
61
+ workspace?: string;
61
62
  }
62
63
 
63
64
  export interface NativeHistoryResult {
@@ -66,6 +67,7 @@ export interface NativeHistoryResult {
66
67
  sourcePath: string;
67
68
  sourceMtimeMs: number;
68
69
  nativeHistoryCoverage?: 'full' | 'partial' | 'best-effort';
70
+ workspace?: string;
69
71
  }
70
72
 
71
73
  const UUID_RE = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
@@ -87,6 +89,7 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
87
89
 
88
90
  const windowMs = typeof src.recent_window_ms === 'number' ? src.recent_window_ms : 5 * 60_000;
89
91
  const filePat = src.file_pattern ? globToRegex(src.file_pattern) : /.*\.jsonl$/;
92
+ const requestedSessionId = readRequestedSessionId(input);
90
93
 
91
94
  // path can be:
92
95
  // - a concrete file → used as-is
@@ -112,7 +115,8 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
112
115
  const workspaceHint = typeof input.workspace === 'string' && input.workspace.trim() ? input.workspace.trim() : '';
113
116
  let sourcePath: string | null = null;
114
117
  if (resolved.includes('*')) {
115
- sourcePath = pickSessionBoundFileAcrossGlob(resolved, filePat, windowMs, sessionFloor, workspaceHint)
118
+ sourcePath = pickExactSessionFileAcrossGlob(resolved, filePat, requestedSessionId)
119
+ || pickSessionBoundFileAcrossGlob(resolved, filePat, windowMs, sessionFloor, workspaceHint)
116
120
  || newestRecentFileAcrossGlob(resolved, filePat, windowMs, sessionFloor);
117
121
  } else {
118
122
  let stat: fs.Stats | null = null;
@@ -120,18 +124,18 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
120
124
  if (stat && stat.isFile()) {
121
125
  sourcePath = resolved;
122
126
  } else if (stat && stat.isDirectory()) {
123
- sourcePath = pickSessionBoundFile(resolved, filePat, windowMs, sessionFloor, workspaceHint)
124
- || newestRecentFile(resolved, filePat, windowMs, sessionFloor);
127
+ sourcePath = pickExactSessionFile(resolved, filePat, requestedSessionId)
128
+ || (requestedSessionId ? null : pickSessionBoundFile(resolved, filePat, windowMs, sessionFloor, workspaceHint))
129
+ || (requestedSessionId ? null : newestRecentFile(resolved, filePat, windowMs, sessionFloor));
125
130
  }
126
131
  // Date-templated directories (e.g. ~/.codex/sessions/{yyyy}/{mm}/{dd})
127
- // expand to today's UTC dir. A session started before UTC midnight
128
- // keeps appending to its previous-day file, so today's dir never
129
- // contains the live transcript. When the templated dir is empty
130
- // or doesn't exist yet, walk the last 3 UTC days at the same depth
131
- // and pick the newest matching rollout across all of them.
132
+ // can drift from the provider's chosen calendar day because CLIs
133
+ // disagree on local-vs-UTC date buckets. Search nearby date dirs
134
+ // before falling back to non-exact matching.
132
135
  if (!sourcePath && hasDateTemplateSegment(src.path)) {
133
- sourcePath = pickSessionBoundFileAcrossDateWindow(src.path, input, filePat, windowMs, sessionFloor, workspaceHint)
134
- || newestRecentFileAcrossDateWindow(src.path, input, filePat, windowMs, sessionFloor);
136
+ sourcePath = pickExactSessionFileAcrossDateWindow(src.path, input, filePat, requestedSessionId)
137
+ || (requestedSessionId ? null : pickSessionBoundFileAcrossDateWindow(src.path, input, filePat, windowMs, sessionFloor, workspaceHint))
138
+ || (requestedSessionId ? null : newestRecentFileAcrossDateWindow(src.path, input, filePat, windowMs, sessionFloor));
135
139
  }
136
140
  }
137
141
  if (!sourcePath) return null;
@@ -139,6 +143,7 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
139
143
  const mtime = safeMtimeMs(sourcePath);
140
144
  const lines = readJsonlLines(sourcePath);
141
145
  if (lines.length === 0) return null;
146
+ const transcriptWorkspace = readSessionMetaWorkspace(lines);
142
147
 
143
148
  // session id: filename uuid or extracted from first record
144
149
  let providerSessionId: string | undefined;
@@ -150,7 +155,7 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
150
155
  if (m) providerSessionId = m[1];
151
156
  }
152
157
 
153
- const requested = input.providerSessionId || '';
158
+ const requested = requestedSessionId || '';
154
159
  if (requested && providerSessionId && providerSessionId !== requested) return null;
155
160
 
156
161
  const filter = src.message_filter ? compileWhere(src.message_filter.where) : null;
@@ -159,7 +164,10 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
159
164
  const rec = lines[i];
160
165
  if (filter && !filter(rec)) continue;
161
166
  const msg = projectMessage(rec, src.message_map, i, lines.length, mtime);
162
- if (msg) messages.push(msg);
167
+ if (msg) {
168
+ if (transcriptWorkspace) msg.workspace = transcriptWorkspace;
169
+ messages.push(msg);
170
+ }
163
171
  }
164
172
  if (messages.length === 0) return null;
165
173
 
@@ -169,9 +177,19 @@ function executeJsonl(src: NativeHistoryJsonlSource, input: NativeHistoryInput):
169
177
  sourcePath,
170
178
  sourceMtimeMs: mtime,
171
179
  nativeHistoryCoverage: 'full',
180
+ workspace: transcriptWorkspace,
172
181
  };
173
182
  }
174
183
 
184
+ function readSessionMetaWorkspace(lines: any[]): string | undefined {
185
+ for (const record of lines.slice(0, 5)) {
186
+ if (String(record?.type ?? '') !== 'session_meta') continue;
187
+ const cwd = typeof record?.payload?.cwd === 'string' ? record.payload.cwd.trim() : '';
188
+ if (cwd) return cwd;
189
+ }
190
+ return undefined;
191
+ }
192
+
175
193
  function readJsonlLines(p: string): any[] {
176
194
  let text: string;
177
195
  try { text = fs.readFileSync(p, 'utf8'); } catch { return []; }
@@ -292,9 +310,9 @@ function expandPath(template: string, input: NativeHistoryInput): string | null
292
310
  cwd: workspaceResolved,
293
311
  cwd_dashed: workspaceResolved.replace(/\//g, '-'),
294
312
  session_id: input.providerSessionId || input.sessionId || input.historySessionId || '',
295
- yyyy: String(now.getUTCFullYear()),
296
- mm: String(now.getUTCMonth() + 1).padStart(2, '0'),
297
- dd: String(now.getUTCDate()).padStart(2, '0'),
313
+ yyyy: String(now.getFullYear()),
314
+ mm: String(now.getMonth() + 1).padStart(2, '0'),
315
+ dd: String(now.getDate()).padStart(2, '0'),
298
316
  };
299
317
  // Replace {var}. If a referenced variable is empty (e.g. session_id
300
318
  // before the agent has allocated one), return null so the caller
@@ -393,12 +411,10 @@ function hasDateTemplateSegment(template: string): boolean {
393
411
  }
394
412
 
395
413
  /**
396
- * Walk the last N UTC days of a date-templated path (e.g.
414
+ * Walk nearby local calendar days of a date-templated path (e.g.
397
415
  * `~/.codex/sessions/{yyyy}/{mm}/{dd}`) and return the newest matching
398
- * file across all of them. Codex sessions started just before UTC
399
- * midnight keep writing to their original day's file even after the
400
- * date rolls over, so today's expanded dir alone misses the live
401
- * transcript.
416
+ * file across all of them. Providers differ on local-vs-UTC date buckets,
417
+ * so today's expanded dir alone can miss a live transcript.
402
418
  */
403
419
  function newestRecentFileAcrossDateWindow(
404
420
  template: string,
@@ -409,8 +425,8 @@ function newestRecentFileAcrossDateWindow(
409
425
  ): string | null {
410
426
  const cutoff = Math.max(Date.now() - windowMs, sessionFloorMs);
411
427
  let best: { p: string; mtime: number } | null = null;
412
- for (let dayOffset = 0; dayOffset < 3; dayOffset += 1) {
413
- const dayMs = Date.now() - dayOffset * 24 * 60 * 60 * 1000;
428
+ for (const dayOffset of [0, -1, 1, -2, 2]) {
429
+ const dayMs = Date.now() + dayOffset * 24 * 60 * 60 * 1000;
414
430
  const dayInput: NativeHistoryInput = { ...input, sessionStartedAtMs: sessionFloorMs };
415
431
  const resolved = expandPathForDate(template, dayInput, new Date(dayMs));
416
432
  if (!resolved) continue;
@@ -448,9 +464,9 @@ function expandPathForDate(template: string, input: NativeHistoryInput, day: Dat
448
464
  cwd: workspaceResolved,
449
465
  cwd_dashed: workspaceResolved.replace(/\//g, '-'),
450
466
  session_id: input.providerSessionId || input.sessionId || input.historySessionId || '',
451
- yyyy: String(day.getUTCFullYear()),
452
- mm: String(day.getUTCMonth() + 1).padStart(2, '0'),
453
- dd: String(day.getUTCDate()).padStart(2, '0'),
467
+ yyyy: String(day.getFullYear()),
468
+ mm: String(day.getMonth() + 1).padStart(2, '0'),
469
+ dd: String(day.getDate()).padStart(2, '0'),
454
470
  };
455
471
  let missing = false;
456
472
  out = out.replace(/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g, (_m, name) => {
@@ -481,6 +497,56 @@ function safeMtimeMs(p: string): number {
481
497
  try { return Math.floor(fs.statSync(p).mtimeMs); } catch { return 0; }
482
498
  }
483
499
 
500
+ function readRequestedSessionId(input: NativeHistoryInput): string {
501
+ const raw = input.providerSessionId || input.sessionId || input.historySessionId || '';
502
+ const value = typeof raw === 'string' ? raw.trim() : '';
503
+ return UUID_RE.test(value) ? value : '';
504
+ }
505
+
506
+ function filenameUuid(filePath: string): string {
507
+ const match = path.basename(filePath).match(UUID_RE);
508
+ return match?.[1] || '';
509
+ }
510
+
511
+ function pickExactSessionFile(dir: string, pattern: RegExp, requestedSessionId: string): string | null {
512
+ if (!requestedSessionId) return null;
513
+ const files = listMatchingFiles(dir, pattern)
514
+ .filter(p => filenameUuid(p).toLowerCase() === requestedSessionId.toLowerCase())
515
+ .sort((a, b) => safeMtimeMs(b) - safeMtimeMs(a));
516
+ return files[0] || null;
517
+ }
518
+
519
+ function pickExactSessionFileAcrossGlob(template: string, pattern: RegExp, requestedSessionId: string): string | null {
520
+ if (!requestedSessionId) return null;
521
+ const dirs = expandDirGlob(template);
522
+ const matches: string[] = [];
523
+ for (const d of dirs) {
524
+ const found = pickExactSessionFile(d, pattern, requestedSessionId);
525
+ if (found) matches.push(found);
526
+ }
527
+ matches.sort((a, b) => safeMtimeMs(b) - safeMtimeMs(a));
528
+ return matches[0] || null;
529
+ }
530
+
531
+ function pickExactSessionFileAcrossDateWindow(
532
+ template: string,
533
+ input: NativeHistoryInput,
534
+ pattern: RegExp,
535
+ requestedSessionId: string,
536
+ ): string | null {
537
+ if (!requestedSessionId) return null;
538
+ const matches: string[] = [];
539
+ for (const dayOffset of [0, -1, 1, -2, 2]) {
540
+ const dayMs = Date.now() + dayOffset * 24 * 60 * 60 * 1000;
541
+ const resolved = expandPathForDate(template, input, new Date(dayMs));
542
+ if (!resolved) continue;
543
+ const found = pickExactSessionFile(resolved, pattern, requestedSessionId);
544
+ if (found) matches.push(found);
545
+ }
546
+ matches.sort((a, b) => safeMtimeMs(b) - safeMtimeMs(a));
547
+ return matches[0] || null;
548
+ }
549
+
484
550
  // ────────────────────────────────────────────────────────────────────────────
485
551
  // Per-session rollout binding
486
552
  //
@@ -624,8 +690,8 @@ function pickSessionBoundFileAcrossDateWindow(
624
690
  if (!sessionFloorMs || !workspaceHint) return null;
625
691
  const cutoff = Math.max(Date.now() - windowMs, sessionFloorMs - SPAWN_BIND_GRACE_MS);
626
692
  const files: string[] = [];
627
- for (let dayOffset = 0; dayOffset < 3; dayOffset += 1) {
628
- const dayMs = sessionFloorMs - dayOffset * 24 * 60 * 60 * 1000;
693
+ for (const dayOffset of [0, -1, 1, -2, 2]) {
694
+ const dayMs = sessionFloorMs + dayOffset * 24 * 60 * 60 * 1000;
629
695
  const dayInput: NativeHistoryInput = { ...input, sessionStartedAtMs: sessionFloorMs };
630
696
  const resolved = expandPathForDate(template, dayInput, new Date(dayMs));
631
697
  if (!resolved) continue;
@@ -130,7 +130,18 @@ export const SCHEMA = {
130
130
  "additionalProperties": false,
131
131
  "properties": {
132
132
  "busy_hold_ms": { "type": "integer", "minimum": 0 },
133
- "startup_grace_ms": { "type": "integer", "minimum": 0 }
133
+ "startup_grace_ms": { "type": "integer", "minimum": 0 },
134
+ "completion_idle_after": {
135
+ "type": "object",
136
+ "additionalProperties": false,
137
+ "required": ["regex", "hold_ms"],
138
+ "properties": {
139
+ "section": { "type": "string", "minLength": 1 },
140
+ "regex": { "type": "string", "minLength": 1 },
141
+ "flags": { "type": "string" },
142
+ "hold_ms": { "type": "integer", "minimum": 0 }
143
+ }
144
+ }
134
145
  }
135
146
  }
136
147
  },
@@ -59,7 +59,27 @@
59
59
  "default": [],
60
60
  "items": { "$ref": "#/definitions/delegateTrigger" }
61
61
  },
62
- "native_history": { "$ref": "#/definitions/nativeHistory" }
62
+ "native_history": { "$ref": "#/definitions/nativeHistory" },
63
+ "cli_version_range": { "type": "string", "minLength": 1 },
64
+ "debounce": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "properties": {
68
+ "busy_hold_ms": { "type": "integer", "minimum": 0 },
69
+ "startup_grace_ms": { "type": "integer", "minimum": 0 },
70
+ "completion_idle_after": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["regex", "hold_ms"],
74
+ "properties": {
75
+ "section": { "type": "string", "minLength": 1 },
76
+ "regex": { "type": "string", "minLength": 1 },
77
+ "flags": { "type": "string" },
78
+ "hold_ms": { "type": "integer", "minimum": 0 }
79
+ }
80
+ }
81
+ }
82
+ }
63
83
  },
64
84
  "definitions": {
65
85
  "size": {
@@ -227,5 +227,15 @@ export interface CliSpec {
227
227
  * once the window passes and an idle state has actually been
228
228
  * observed. */
229
229
  startup_grace_ms?: number;
230
+ /** Treat a provider-specific completion marker as idle after it has
231
+ * remained visible for hold_ms. This handles TUIs that leave their
232
+ * last spinner glyph next to a completed timer, causing the normal
233
+ * busy regex to keep matching after the turn is done. */
234
+ completion_idle_after?: {
235
+ section?: string;
236
+ regex: string;
237
+ flags?: string;
238
+ hold_ms: number;
239
+ };
230
240
  };
231
241
  }
@@ -359,11 +359,17 @@ export interface RepoMeshSessionStatus {
359
359
  sessionId: string;
360
360
  providerType?: string;
361
361
  state?: string;
362
+ chatStatus?: string;
362
363
  lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
363
364
  surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
364
365
  recoveryState?: string | null;
365
366
  workspace?: string | null;
366
367
  title?: string | null;
368
+ role?: string | null;
369
+ isSelfCoordinator?: boolean;
370
+ statusNote?: string | null;
371
+ createdAt?: string | null;
372
+ startedAt?: string | null;
367
373
  lastActivityAt?: string | null;
368
374
  isCached?: boolean;
369
375
  }