@adhdev/daemon-core 0.9.82-rc.7 → 0.9.82-rc.70

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 (72) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +2 -0
  2. package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
  3. package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
  4. package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
  5. package/dist/commands/router.d.ts +24 -0
  6. package/dist/config/mesh-config.d.ts +66 -1
  7. package/dist/git/git-commands.d.ts +1 -0
  8. package/dist/git/git-status.d.ts +5 -0
  9. package/dist/git/git-types.d.ts +10 -0
  10. package/dist/index.d.ts +13 -6
  11. package/dist/index.js +4619 -1143
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +4582 -1128
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/installer.d.ts +1 -4
  16. package/dist/launch.d.ts +1 -1
  17. package/dist/logging/async-batch-writer.d.ts +10 -0
  18. package/dist/mesh/beads-db.d.ts +18 -0
  19. package/dist/mesh/mesh-active-work.d.ts +48 -0
  20. package/dist/mesh/mesh-events.d.ts +28 -5
  21. package/dist/mesh/mesh-fast-forward.d.ts +39 -0
  22. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  23. package/dist/mesh/mesh-ledger.d.ts +38 -1
  24. package/dist/mesh/mesh-work-queue.d.ts +27 -5
  25. package/dist/mesh/refine-config.d.ts +119 -0
  26. package/dist/providers/chat-message-normalization.d.ts +1 -0
  27. package/dist/providers/cli-provider-instance.d.ts +1 -0
  28. package/dist/repo-mesh-types.d.ts +160 -0
  29. package/dist/status/reporter.d.ts +2 -0
  30. package/package.json +3 -1
  31. package/src/boot/daemon-lifecycle.ts +4 -0
  32. package/src/cli-adapters/provider-cli-adapter.ts +91 -3
  33. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  34. package/src/cli-adapters/provider-cli-parse.ts +4 -0
  35. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  36. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  37. package/src/cli-adapters/provider-cli-shared.ts +20 -10
  38. package/src/commands/handler.ts +8 -1
  39. package/src/commands/mesh-coordinator.ts +13 -143
  40. package/src/commands/router.ts +2452 -409
  41. package/src/config/chat-history.ts +9 -7
  42. package/src/config/mesh-config.ts +244 -1
  43. package/src/daemon/dev-cli-debug.ts +10 -1
  44. package/src/detection/ide-detector.ts +26 -16
  45. package/src/git/git-commands.ts +3 -3
  46. package/src/git/git-status.ts +97 -6
  47. package/src/git/git-summary.ts +3 -0
  48. package/src/git/git-types.ts +11 -0
  49. package/src/index.ts +39 -5
  50. package/src/installer.d.ts +1 -1
  51. package/src/installer.ts +8 -6
  52. package/src/launch.d.ts +1 -1
  53. package/src/launch.ts +37 -28
  54. package/src/logging/async-batch-writer.ts +55 -0
  55. package/src/logging/logger.ts +2 -1
  56. package/src/mesh/beads-db.ts +176 -0
  57. package/src/mesh/coordinator-prompt.ts +4 -2
  58. package/src/mesh/mesh-active-work.ts +205 -0
  59. package/src/mesh/mesh-events.ts +291 -38
  60. package/src/mesh/mesh-fast-forward.ts +430 -0
  61. package/src/mesh/mesh-host-ownership.ts +73 -0
  62. package/src/mesh/mesh-ledger.ts +138 -1
  63. package/src/mesh/mesh-work-queue.ts +199 -137
  64. package/src/mesh/refine-config.ts +306 -0
  65. package/src/providers/chat-message-normalization.ts +3 -1
  66. package/src/providers/cli-provider-instance.ts +66 -1
  67. package/src/providers/ide-provider-instance.ts +17 -3
  68. package/src/providers/provider-loader.ts +10 -4
  69. package/src/providers/version-archive.ts +38 -20
  70. package/src/repo-mesh-types.ts +174 -0
  71. package/src/status/reporter.ts +15 -0
  72. package/src/system/host-memory.ts +29 -12
@@ -23,11 +23,42 @@ export interface RepoMesh {
23
23
  defaultBranch?: string;
24
24
  policy: RepoMeshPolicy;
25
25
  coordinator: RepoMeshCoordinatorConfig;
26
+ meshHost?: RepoMeshHostMetadata;
26
27
  projectContext: ProjectContextSnapshot;
27
28
  nodes: RepoMeshNode[];
28
29
  status: 'active' | 'archived' | 'deleted';
29
30
  }
30
31
 
32
+ export type RepoMeshDaemonRole = 'host' | 'member';
33
+
34
+ export interface RepoMeshHostPairingMetadata {
35
+ status: 'not_configured' | 'pairing' | 'paired' | 'rejected' | 'revoked';
36
+ tokenId?: string;
37
+ joinedAt?: string;
38
+ lastPairedAt?: string;
39
+ lastRejectedAt?: string;
40
+ expiresAt?: string;
41
+ }
42
+
43
+ export interface RepoMeshHostMetadata {
44
+ /** Local daemon role for this mesh. Missing metadata defaults to host for standalone compatibility. */
45
+ role: RepoMeshDaemonRole;
46
+ /** Daemon that owns mesh truth/status/git/queue/session/ledger/coordinator ownership. */
47
+ hostDaemonId?: string;
48
+ /** Mesh node that represents the host daemon, when known. */
49
+ hostNodeId?: string;
50
+ /** Future standalone manual pairing endpoint entered by member daemons. */
51
+ hostAddress?: string;
52
+ /** Redacted pairing state only; raw join tokens must not be persisted here. */
53
+ pairing?: RepoMeshHostPairingMetadata;
54
+ }
55
+
56
+ export interface RepoMeshHostStatus extends RepoMeshHostMetadata {
57
+ canOwnCoordinator: boolean;
58
+ canOwnQueue: boolean;
59
+ defaulted: boolean;
60
+ }
61
+
31
62
  export interface RepoMeshNode {
32
63
  id: string;
33
64
  daemonId: string;
@@ -42,6 +73,7 @@ export interface RepoMeshNode {
42
73
  effectiveCapabilities: RepoMeshNodeCapabilities;
43
74
  policy: RepoMeshNodePolicy;
44
75
  health: RepoMeshNodeHealth;
76
+ role?: RepoMeshDaemonRole;
45
77
  status: 'enabled' | 'disabled' | 'removed';
46
78
  }
47
79
 
@@ -229,6 +261,7 @@ export interface LocalMeshEntry {
229
261
  defaultBranch?: string;
230
262
  policy: RepoMeshPolicy;
231
263
  coordinator: RepoMeshCoordinatorConfig;
264
+ meshHost?: RepoMeshHostMetadata;
232
265
  nodes: LocalMeshNodeEntry[];
233
266
  createdAt: string;
234
267
  updatedAt: string;
@@ -251,6 +284,7 @@ export interface LocalMeshNodeEntry {
251
284
  clonedFromNodeId?: string;
252
285
  /** Optional associated/external repos configured as node metadata. */
253
286
  relatedRepos?: RepoMeshRelatedRepo[];
287
+ role?: RepoMeshDaemonRole;
254
288
  }
255
289
 
256
290
  // ─── Mesh Status (runtime, not persisted) ───────
@@ -259,17 +293,157 @@ export interface RepoMeshStatus {
259
293
  meshId: string;
260
294
  meshName: string;
261
295
  repoIdentity: string;
296
+ defaultBranch?: string;
262
297
  refreshedAt: string;
298
+ meshHost?: RepoMeshHostStatus;
263
299
  nodes: RepoMeshNodeStatus[];
300
+ queue?: RepoMeshQueueStatus;
301
+ ledger?: RepoMeshLedgerStatus;
302
+ }
303
+
304
+ export interface RepoMeshSessionStatus {
305
+ sessionId: string;
306
+ providerType?: string;
307
+ state?: string;
308
+ lifecycle?: 'starting' | 'running' | 'stopping' | 'stopped' | 'failed' | 'interrupted';
309
+ surfaceKind?: 'live_runtime' | 'recovery_snapshot' | 'inactive_record';
310
+ recoveryState?: string | null;
311
+ workspace?: string | null;
312
+ title?: string | null;
313
+ lastActivityAt?: string | null;
314
+ isCached?: boolean;
315
+ }
316
+
317
+ export type RepoMeshPeerConnectionState = 'self' | 'connected' | 'connecting' | 'disconnected' | 'failed' | 'closed' | 'unknown';
318
+ export type RepoMeshPeerConnectionTransport = 'local' | 'direct' | 'relay' | 'unknown';
319
+
320
+ export interface RepoMeshPeerConnectionStatus {
321
+ perspective: 'selected_coordinator';
322
+ source: 'mesh_peer_status' | 'not_reported';
323
+ state: RepoMeshPeerConnectionState;
324
+ transport: RepoMeshPeerConnectionTransport;
325
+ reported: boolean;
326
+ reason?: string;
327
+ lastStateChangeAt?: string;
328
+ lastConnectedAt?: string;
329
+ lastCommandAt?: string;
264
330
  }
265
331
 
266
332
  export interface RepoMeshNodeStatus {
267
333
  nodeId: string;
268
334
  machineLabel: string;
269
335
  workspace: string;
336
+ repoRoot?: string;
337
+ daemonId?: string;
338
+ machineId?: string;
339
+ role?: RepoMeshDaemonRole;
340
+ machineStatus?: string;
341
+ isLocalWorktree?: boolean;
342
+ worktreeBranch?: string;
270
343
  health: RepoMeshNodeHealth;
271
344
  git?: GitRepoStatus;
345
+ /**
346
+ * True when the selected coordinator has evidence that a peer git probe is still
347
+ * in flight or just timed out during initial mesh handshake, so callers should
348
+ * treat missing git data as pending instead of authoritative absence.
349
+ */
350
+ gitProbePending?: boolean;
272
351
  providers: string[];
273
352
  activeSessions: string[];
353
+ activeSessionDetails?: RepoMeshSessionStatus[];
354
+ providerPriority?: string[];
355
+ launchReady?: boolean;
356
+ lastSeenAt?: string;
357
+ updatedAt?: string;
358
+ connection?: RepoMeshPeerConnectionStatus;
274
359
  error?: string;
275
360
  }
361
+
362
+ export type RepoMeshQueueTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
363
+
364
+ export interface RepoMeshQueueTask {
365
+ id: string;
366
+ meshId: string;
367
+ message: string;
368
+ status: RepoMeshQueueTaskStatus;
369
+ targetNodeId?: string;
370
+ targetSessionId?: string;
371
+ assignedNodeId?: string;
372
+ assignedSessionId?: string;
373
+ cancelReason?: string;
374
+ cancelledAt?: string;
375
+ requeueReason?: string;
376
+ requeuedAt?: string;
377
+ requeueCount?: number;
378
+ autoLaunch?: {
379
+ status: 'skipped' | 'started' | 'failed' | 'completed';
380
+ reason?: string;
381
+ nodeId?: string;
382
+ providerType?: string;
383
+ sessionId?: string;
384
+ updatedAt: string;
385
+ };
386
+ dispatchTimestamp?: string;
387
+ createdAt: string;
388
+ updatedAt: string;
389
+ }
390
+
391
+ export interface RepoMeshQueueSummary {
392
+ total: number;
393
+ active: number;
394
+ historical: number;
395
+ pending: number;
396
+ assigned: number;
397
+ completed: number;
398
+ failed: number;
399
+ cancelled: number;
400
+ activeCounts: {
401
+ pending: number;
402
+ assigned: number;
403
+ };
404
+ historicalCounts: {
405
+ completed: number;
406
+ failed: number;
407
+ cancelled: number;
408
+ };
409
+ activeAssignments: Array<{
410
+ id: string;
411
+ nodeId?: string;
412
+ sessionId?: string;
413
+ message: string;
414
+ }>;
415
+ }
416
+
417
+ export interface RepoMeshQueueStatus {
418
+ tasks: RepoMeshQueueTask[];
419
+ summary: RepoMeshQueueSummary;
420
+ }
421
+
422
+ export interface RepoMeshLedgerEntryStatus {
423
+ id: string;
424
+ meshId: string;
425
+ timestamp: string;
426
+ kind: string;
427
+ nodeId?: string;
428
+ sessionId?: string;
429
+ providerType?: string;
430
+ payload: Record<string, unknown>;
431
+ }
432
+
433
+ export interface RepoMeshLedgerSummaryStatus {
434
+ meshId: string;
435
+ totalEntries: number;
436
+ taskDispatched: number;
437
+ taskCompleted: number;
438
+ taskFailed: number;
439
+ taskStalled: number;
440
+ sessionLaunched: number;
441
+ checkpointCreated: number;
442
+ lastActivityAt: string | null;
443
+ recentFailures: number;
444
+ }
445
+
446
+ export interface RepoMeshLedgerStatus {
447
+ entries: RepoMeshLedgerEntryStatus[];
448
+ summary: RepoMeshLedgerSummaryStatus;
449
+ }
@@ -51,6 +51,8 @@ export class DaemonStatusReporter {
51
51
  private lastStatusSentAt = 0;
52
52
  private statusPendingThrottle = false;
53
53
  private lastP2PStatusHash = '';
54
+ private lastP2PStatusSentAt: number = 0;
55
+ private p2pDebounceTimer: ReturnType<typeof setTimeout> | null = null;
54
56
  private lastServerStatusHash = '';
55
57
  private lastStatusSummary = '';
56
58
 
@@ -355,7 +357,20 @@ export class DaemonStatusReporter {
355
357
  : { ...hashTarget, sessions };
356
358
  const h = this.simpleHash(JSON.stringify(hashPayload));
357
359
  if (h !== this.lastP2PStatusHash) {
360
+ const now = Date.now();
361
+ // Rate limit: max 1 per 500ms
362
+ if (this.lastP2PStatusSentAt && now - this.lastP2PStatusSentAt < 500) {
363
+ if (!this.p2pDebounceTimer) {
364
+ this.p2pDebounceTimer = setTimeout(() => {
365
+ this.p2pDebounceTimer = null;
366
+ this.sendUnifiedStatusReport({ reason: 'p2p_debounce' });
367
+ }, 500);
368
+ }
369
+ return false; // Dropped for now, but will trigger later
370
+ }
371
+
358
372
  this.lastP2PStatusHash = h;
373
+ this.lastP2PStatusSentAt = now;
359
374
  this.deps.p2p?.sendStatus(payload);
360
375
  return true;
361
376
  }
@@ -11,7 +11,10 @@
11
11
  */
12
12
 
13
13
  import * as os from 'os';
14
- import { execSync } from 'child_process';
14
+ import { exec } from 'child_process';
15
+ import { promisify } from 'util';
16
+
17
+ const execAsync = promisify(exec);
15
18
 
16
19
  export interface HostMemorySnapshot {
17
20
  totalMem: number;
@@ -21,19 +24,22 @@ export interface HostMemorySnapshot {
21
24
  availableMem: number;
22
25
  }
23
26
 
24
- function parseDarwinAvailableBytes(totalMem: number): number | null {
25
- if (os.platform() !== 'darwin') return null;
27
+ let cachedDarwinAvail: number | null = null;
28
+ let darwinMemoryInterval: NodeJS.Timeout | null = null;
29
+
30
+ async function updateDarwinMemoryCache() {
31
+ if (os.platform() !== 'darwin') return;
26
32
  try {
27
- const out = execSync('vm_stat', {
33
+ const { stdout } = await execAsync('vm_stat', {
28
34
  encoding: 'utf-8',
29
35
  timeout: 4000,
30
36
  maxBuffer: 256 * 1024,
31
37
  });
32
- const pageSizeMatch = out.match(/page size of (\d+)\s*bytes/i);
38
+ const pageSizeMatch = stdout.match(/page size of (\d+)\s*bytes/i);
33
39
  const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : 4096;
34
40
 
35
41
  const counts: Record<string, number> = {};
36
- for (const line of out.split('\n')) {
42
+ for (const line of stdout.split('\n')) {
37
43
  const m = line.match(/^\s*Pages\s+([^:]+):\s+([\d,]+)\s*\.?/);
38
44
  if (!m) continue;
39
45
  const key = m[1].trim().toLowerCase().replace(/\s+/g, '_');
@@ -49,17 +55,28 @@ function parseDarwinAvailableBytes(totalMem: number): number | null {
49
55
 
50
56
  const availPages = free + inactive + speculative + purgeable + fileBacked;
51
57
  const bytes = availPages * pageSize;
52
- if (!Number.isFinite(bytes) || bytes < 0) return null;
53
- return Math.min(bytes, totalMem);
58
+ cachedDarwinAvail = Number.isFinite(bytes) && bytes >= 0 ? Math.min(bytes, os.totalmem()) : null;
54
59
  } catch {
55
- return null;
60
+ // silently fallback
56
61
  }
57
62
  }
58
63
 
59
64
  export function getHostMemorySnapshot(): HostMemorySnapshot {
65
+ if (os.platform() === 'darwin' && !darwinMemoryInterval) {
66
+ updateDarwinMemoryCache();
67
+ darwinMemoryInterval = setInterval(updateDarwinMemoryCache, 3000);
68
+ darwinMemoryInterval.unref();
69
+ }
70
+
60
71
  const totalMem = os.totalmem();
61
72
  const freeMem = os.freemem();
62
- const darwinAvail = parseDarwinAvailableBytes(totalMem);
63
- const availableMem = darwinAvail != null ? darwinAvail : freeMem;
64
- return { totalMem, freeMem, availableMem };
73
+ const availableMem = os.platform() === 'darwin'
74
+ ? (cachedDarwinAvail ?? freeMem)
75
+ : freeMem;
76
+
77
+ return {
78
+ totalMem,
79
+ freeMem,
80
+ availableMem,
81
+ };
65
82
  }