@adhdev/daemon-core 0.9.82-rc.15 → 0.9.82-rc.152

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 (182) hide show
  1. package/dist/boot/process-hardening.d.ts +50 -0
  2. package/dist/chat/source-machine.d.ts +166 -0
  3. package/dist/chat/source-resolver.d.ts +104 -0
  4. package/dist/chat/subscription-updates.d.ts +1 -0
  5. package/dist/cli-adapter-types.d.ts +5 -1
  6. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  7. package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
  8. package/dist/cli-adapters/provider-cli-adapter.d.ts +104 -63
  9. package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
  10. package/dist/cli-adapters/provider-cli-shared.d.ts +27 -0
  11. package/dist/commands/handler.d.ts +66 -0
  12. package/dist/commands/router.d.ts +22 -0
  13. package/dist/config/chat-history.d.ts +5 -0
  14. package/dist/config/config.d.ts +5 -0
  15. package/dist/config/mesh-config.d.ts +68 -1
  16. package/dist/git/git-commands.d.ts +5 -1
  17. package/dist/index.d.ts +20 -6
  18. package/dist/index.js +13153 -3031
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +13099 -3023
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/installer.d.ts +1 -4
  23. package/dist/ipc/local-ipc-server.d.ts +91 -0
  24. package/dist/launch.d.ts +1 -1
  25. package/dist/logging/async-batch-writer.d.ts +10 -0
  26. package/dist/mesh/beads-db.d.ts +72 -0
  27. package/dist/mesh/contracts.d.ts +164 -0
  28. package/dist/mesh/coordinator-registry.d.ts +25 -0
  29. package/dist/mesh/mesh-active-work.d.ts +90 -0
  30. package/dist/mesh/mesh-events.d.ts +77 -5
  31. package/dist/mesh/mesh-fast-forward.d.ts +39 -0
  32. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  33. package/dist/mesh/mesh-ledger.d.ts +58 -1
  34. package/dist/mesh/mesh-refine-status.d.ts +26 -0
  35. package/dist/mesh/mesh-work-queue.d.ts +44 -5
  36. package/dist/mesh/preview-freshness.d.ts +18 -0
  37. package/dist/mesh/refine-config.d.ts +193 -0
  38. package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
  39. package/dist/providers/approval-utils.d.ts +9 -0
  40. package/dist/providers/cli-provider-instance.d.ts +6 -1
  41. package/dist/providers/contracts.d.ts +50 -5
  42. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  43. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  44. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  45. package/dist/providers/native-history/index.d.ts +11 -0
  46. package/dist/providers/provider-loader.d.ts +19 -1
  47. package/dist/providers/read-chat-contract.d.ts +29 -0
  48. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  49. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
  50. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  51. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +64 -0
  52. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  53. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  54. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  55. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  56. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  57. package/dist/providers/sdk/v1/index.d.ts +30 -0
  58. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  59. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  60. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  61. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  62. package/dist/providers/sdk/v1/types/common/index.d.ts +169 -0
  63. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  64. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  65. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  66. package/dist/providers/transcript-v2.d.ts +176 -0
  67. package/dist/repo-mesh-types.d.ts +67 -0
  68. package/dist/shared-types.d.ts +12 -0
  69. package/dist/status/reporter.d.ts +2 -0
  70. package/dist/status/snapshot.d.ts +1 -0
  71. package/dist/types.d.ts +5 -0
  72. package/package.json +6 -2
  73. package/src/boot/daemon-lifecycle.ts +17 -10
  74. package/src/boot/process-hardening.ts +89 -0
  75. package/src/chat/source-machine.ts +534 -0
  76. package/src/chat/source-resolver.ts +0 -0
  77. package/src/chat/subscription-updates.ts +14 -1
  78. package/src/cli-adapter-types.d.ts +1 -0
  79. package/src/cli-adapter-types.ts +3 -1
  80. package/src/cli-adapters/cli-script-runner.ts +421 -0
  81. package/src/cli-adapters/cli-state-engine.ts +1086 -0
  82. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  83. package/src/cli-adapters/provider-cli-adapter.ts +664 -1137
  84. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  85. package/src/cli-adapters/provider-cli-parse.ts +13 -0
  86. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  87. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  88. package/src/cli-adapters/provider-cli-shared.ts +57 -11
  89. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
  90. package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
  91. package/src/commands/chat-commands.ts +1451 -52
  92. package/src/commands/cli-manager.ts +184 -3
  93. package/src/commands/handler.ts +547 -2
  94. package/src/commands/mesh-coordinator.ts +13 -143
  95. package/src/commands/router.ts +3273 -428
  96. package/src/config/chat-history.ts +79 -24
  97. package/src/config/config.ts +12 -0
  98. package/src/config/mesh-config.ts +249 -2
  99. package/src/config/recent-activity.ts +8 -2
  100. package/src/daemon/dev-cli-debug.ts +10 -1
  101. package/src/detection/ide-detector.ts +26 -16
  102. package/src/git/git-commands.ts +17 -5
  103. package/src/git/git-worktree.ts +8 -1
  104. package/src/index.ts +72 -5
  105. package/src/installer.d.ts +1 -1
  106. package/src/installer.ts +8 -6
  107. package/src/ipc/local-ipc-server.ts +278 -0
  108. package/src/launch.d.ts +1 -1
  109. package/src/launch.ts +37 -28
  110. package/src/logging/async-batch-writer.ts +55 -0
  111. package/src/logging/logger.ts +2 -1
  112. package/src/mesh/beads-db.ts +479 -0
  113. package/src/mesh/contracts.ts +329 -0
  114. package/src/mesh/coordinator-prompt.ts +40 -22
  115. package/src/mesh/coordinator-registry.ts +75 -0
  116. package/src/mesh/mesh-active-work.ts +437 -0
  117. package/src/mesh/mesh-events.ts +820 -61
  118. package/src/mesh/mesh-fast-forward.ts +430 -0
  119. package/src/mesh/mesh-host-ownership.ts +73 -0
  120. package/src/mesh/mesh-ledger.ts +457 -104
  121. package/src/mesh/mesh-refine-status.ts +144 -0
  122. package/src/mesh/mesh-work-queue.ts +216 -158
  123. package/src/mesh/preview-freshness.ts +118 -0
  124. package/src/mesh/refine-config.ts +366 -0
  125. package/src/mesh/worktree-bootstrap-config.ts +247 -0
  126. package/src/providers/approval-utils.ts +39 -5
  127. package/src/providers/chat-message-normalization.ts +4 -11
  128. package/src/providers/cli-provider-instance.ts +380 -44
  129. package/src/providers/contracts.ts +52 -5
  130. package/src/providers/ide-provider-instance.ts +17 -3
  131. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  132. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  133. package/src/providers/native-history/codex-cli-transcript.ts +419 -0
  134. package/src/providers/native-history/index.ts +23 -0
  135. package/src/providers/provider-loader.ts +294 -28
  136. package/src/providers/provider-schema.ts +31 -13
  137. package/src/providers/read-chat-contract.ts +76 -16
  138. package/src/providers/sdk/README.md +49 -0
  139. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  140. package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
  141. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  142. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +245 -0
  143. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  144. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  145. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  146. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  147. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  148. package/src/providers/sdk/v1/index.ts +152 -0
  149. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  150. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  151. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  152. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +444 -0
  153. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  154. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  155. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  156. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  157. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  158. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  159. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  160. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  161. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +119 -0
  162. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  163. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  164. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  165. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  166. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  167. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  168. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  169. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  170. package/src/providers/sdk/v1/types/common/index.ts +210 -0
  171. package/src/providers/sdk/v1/validators/index.ts +19 -0
  172. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  173. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  174. package/src/providers/transcript-v2.ts +567 -0
  175. package/src/providers/version-archive.ts +38 -20
  176. package/src/repo-mesh-types.ts +77 -0
  177. package/src/shared-types.ts +9 -0
  178. package/src/status/builders.ts +23 -6
  179. package/src/status/reporter.ts +15 -0
  180. package/src/status/snapshot.ts +35 -11
  181. package/src/system/host-memory.ts +29 -12
  182. package/src/types.ts +5 -0
@@ -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
 
@@ -62,6 +94,13 @@ export interface RepoMeshPolicy {
62
94
  requirePreTaskCheckpoint: boolean;
63
95
  requirePostTaskCheckpoint: boolean;
64
96
  requireApprovalForPush: boolean;
97
+ /**
98
+ * Narrow Refinery opt-in: when validation and patch-equivalence have passed,
99
+ * allow Refinery to publish submodule gitlink commits to each submodule's
100
+ * configured remote main branch with a non-force push, then verify reachability.
101
+ * Defaults to false; root branch pushes/merges are not affected.
102
+ */
103
+ allowAutoPublishSubmoduleMainCommits?: boolean;
65
104
  requireApprovalForDestructiveGit: boolean;
66
105
  dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
67
106
  maxParallelTasks: number;
@@ -126,6 +165,7 @@ export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
126
165
  requirePreTaskCheckpoint: false,
127
166
  requirePostTaskCheckpoint: true,
128
167
  requireApprovalForPush: true,
168
+ allowAutoPublishSubmoduleMainCommits: false,
129
169
  requireApprovalForDestructiveGit: true,
130
170
  dirtyWorkspaceBehavior: 'warn',
131
171
  maxParallelTasks: 2,
@@ -229,6 +269,7 @@ export interface LocalMeshEntry {
229
269
  defaultBranch?: string;
230
270
  policy: RepoMeshPolicy;
231
271
  coordinator: RepoMeshCoordinatorConfig;
272
+ meshHost?: RepoMeshHostMetadata;
232
273
  nodes: LocalMeshNodeEntry[];
233
274
  createdAt: string;
234
275
  updatedAt: string;
@@ -249,8 +290,23 @@ export interface LocalMeshNodeEntry {
249
290
  worktreeBranch?: string;
250
291
  /** Node ID this worktree was cloned from */
251
292
  clonedFromNodeId?: string;
293
+ /** Repo-local preparation result for ADHDev-created worktree nodes. */
294
+ worktreeBootstrap?: {
295
+ status: 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
296
+ required?: boolean;
297
+ configSource?: string;
298
+ configSourceType?: string;
299
+ startedAt?: string;
300
+ completedAt?: string;
301
+ lastCommand?: string;
302
+ exitCode?: number | null;
303
+ error?: string;
304
+ commandsRun?: Array<Record<string, unknown>>;
305
+ staleInputs?: string[];
306
+ };
252
307
  /** Optional associated/external repos configured as node metadata. */
253
308
  relatedRepos?: RepoMeshRelatedRepo[];
309
+ role?: RepoMeshDaemonRole;
254
310
  }
255
311
 
256
312
  // ─── Mesh Status (runtime, not persisted) ───────
@@ -261,6 +317,7 @@ export interface RepoMeshStatus {
261
317
  repoIdentity: string;
262
318
  defaultBranch?: string;
263
319
  refreshedAt: string;
320
+ meshHost?: RepoMeshHostStatus;
264
321
  nodes: RepoMeshNodeStatus[];
265
322
  queue?: RepoMeshQueueStatus;
266
323
  ledger?: RepoMeshLedgerStatus;
@@ -301,16 +358,26 @@ export interface RepoMeshNodeStatus {
301
358
  repoRoot?: string;
302
359
  daemonId?: string;
303
360
  machineId?: string;
361
+ role?: RepoMeshDaemonRole;
304
362
  machineStatus?: string;
305
363
  isLocalWorktree?: boolean;
306
364
  worktreeBranch?: string;
307
365
  health: RepoMeshNodeHealth;
308
366
  git?: GitRepoStatus;
367
+ /**
368
+ * True when the selected coordinator has evidence that a peer git probe is still
369
+ * in flight or just timed out during initial mesh handshake, so callers should
370
+ * treat missing git data as pending instead of authoritative absence.
371
+ */
372
+ gitProbePending?: boolean;
309
373
  providers: string[];
310
374
  activeSessions: string[];
311
375
  activeSessionDetails?: RepoMeshSessionStatus[];
312
376
  providerPriority?: string[];
313
377
  launchReady?: boolean;
378
+ worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
379
+ launchBlockedReason?: string;
380
+ launchBlockedMessage?: string;
314
381
  lastSeenAt?: string;
315
382
  updatedAt?: string;
316
383
  connection?: RepoMeshPeerConnectionStatus;
@@ -405,3 +472,13 @@ export interface RepoMeshLedgerStatus {
405
472
  entries: RepoMeshLedgerEntryStatus[];
406
473
  summary: RepoMeshLedgerSummaryStatus;
407
474
  }
475
+
476
+ // ─── Async Job Lifecycle ─────────────────────────
477
+ // Shared base for all mesh async job types (refine jobs, bootstrap runs, etc.)
478
+ // Each concrete type adds its own status enum and domain-specific fields.
479
+
480
+ export interface MeshAsyncJobLifecycle {
481
+ startedAt?: string;
482
+ completedAt?: string;
483
+ error?: string;
484
+ }
@@ -110,6 +110,13 @@ export interface ReadChatSyncResult {
110
110
  status: string;
111
111
  title?: string;
112
112
  activeModal?: { message: string; buttons: string[] } | null;
113
+ /**
114
+ * Chat source provenance from ChatSourceMachine (A2). Carries the
115
+ * selected source, transition cause, lock state, and legacy
116
+ * fallbackReason — opaque to the daemon-core, consumed by web-core
117
+ * for the source debug badge and SourceTimeline (A3).
118
+ */
119
+ messageSource?: Record<string, unknown>;
113
120
  }
114
121
 
115
122
  export interface ProviderSummaryItem {
@@ -389,6 +396,8 @@ export interface SessionEntry {
389
396
  seenCompletionMarker?: string;
390
397
  surfaceHidden?: boolean;
391
398
  settings?: Record<string, any>;
399
+ /** Set when this session is acting as a mesh coordinator for the given mesh. */
400
+ coordinator?: { meshId: string; role: 'coordinator' };
392
401
  meshQueueStats?: {
393
402
  total?: number;
394
403
  active?: number;
@@ -25,6 +25,7 @@ import {
25
25
  type NormalizeActiveChatOptions,
26
26
  } from './normalize.js';
27
27
  import { getMeshQueueStats } from '../mesh/mesh-work-queue.js';
28
+ import { getCoordinatorForSession } from '../mesh/coordinator-registry.js';
28
29
  import { normalizeProviderStateControlValues } from '../providers/provider-patch-state.js';
29
30
  import { normalizeProviderSummaryMetadata } from '../providers/summary-metadata.js';
30
31
  import {
@@ -164,7 +165,7 @@ const ACP_SESSION_CAPABILITIES: SessionCapability[] = [
164
165
  'set_thought_level',
165
166
  ];
166
167
 
167
- function buildIdeWorkspaceSession(
168
+ function buildWorkspaceSession(
168
169
  state: IdeProviderState,
169
170
  cdpManagers: Map<string, DaemonCdpManager>,
170
171
  options: SessionEntryBuildOptions,
@@ -179,7 +180,10 @@ function buildIdeWorkspaceSession(
179
180
  const git = getGitSummaryForWorkspace(workspace, options);
180
181
  const title = activeChat?.title || state.name;
181
182
  const meshCoordinatorFor = state.settings?.meshCoordinatorFor as string | undefined;
182
- const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : undefined;
183
+ const registryEntry = state.instanceId ? getCoordinatorForSession(state.instanceId) : undefined;
184
+ const effectiveMeshId = meshCoordinatorFor || registryEntry?.meshId;
185
+ const coordinator = effectiveMeshId ? { meshId: effectiveMeshId, role: 'coordinator' as const } : undefined;
186
+ const meshQueueStats = effectiveMeshId ? getMeshQueueStats(effectiveMeshId) : undefined;
183
187
  return {
184
188
  id: state.instanceId || state.type,
185
189
  parentId: null,
@@ -203,6 +207,7 @@ function buildIdeWorkspaceSession(
203
207
  errorReason: state.errorReason,
204
208
  lastUpdated: state.lastUpdated,
205
209
  settings: state.settings,
210
+ ...(coordinator && { coordinator }),
206
211
  ...(meshQueueStats && { meshQueueStats }),
207
212
  };
208
213
  }
@@ -221,7 +226,10 @@ function buildExtensionAgentSession(
221
226
  const workspace = parent.workspace || null;
222
227
  const git = getGitSummaryForWorkspace(workspace, options);
223
228
  const meshCoordinatorFor = ext.settings?.meshCoordinatorFor as string | undefined;
224
- const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : undefined;
229
+ const registryEntry = ext.instanceId ? getCoordinatorForSession(ext.instanceId) : undefined;
230
+ const effectiveMeshId = meshCoordinatorFor || registryEntry?.meshId;
231
+ const coordinator = effectiveMeshId ? { meshId: effectiveMeshId, role: 'coordinator' as const } : undefined;
232
+ const meshQueueStats = effectiveMeshId ? getMeshQueueStats(effectiveMeshId) : undefined;
225
233
  return {
226
234
  id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
227
235
  parentId: parent.instanceId || parent.type,
@@ -245,6 +253,7 @@ function buildExtensionAgentSession(
245
253
  errorReason: ext.errorReason,
246
254
  lastUpdated: ext.lastUpdated,
247
255
  settings: ext.settings,
256
+ ...(coordinator && { coordinator }),
248
257
  ...(meshQueueStats && { meshQueueStats }),
249
258
  };
250
259
  }
@@ -287,7 +296,10 @@ function buildCliSession(state: CliProviderState, options: SessionEntryBuildOpti
287
296
  const workspace = state.workspace || null;
288
297
  const git = getGitSummaryForWorkspace(workspace, options);
289
298
  const meshCoordinatorFor = state.settings?.meshCoordinatorFor as string | undefined;
290
- const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : undefined;
299
+ const registryEntry = state.instanceId ? getCoordinatorForSession(state.instanceId) : undefined;
300
+ const effectiveMeshId = meshCoordinatorFor || registryEntry?.meshId;
301
+ const coordinator = effectiveMeshId ? { meshId: effectiveMeshId, role: 'coordinator' as const } : undefined;
302
+ const meshQueueStats = effectiveMeshId ? getMeshQueueStats(effectiveMeshId) : undefined;
291
303
  return {
292
304
  id: state.instanceId,
293
305
  parentId: null,
@@ -327,6 +339,7 @@ function buildCliSession(state: CliProviderState, options: SessionEntryBuildOpti
327
339
  errorReason: state.errorReason,
328
340
  lastUpdated: state.lastUpdated,
329
341
  settings: state.settings,
342
+ ...(coordinator && { coordinator }),
330
343
  ...(meshQueueStats && { meshQueueStats }),
331
344
  };
332
345
  }
@@ -341,7 +354,10 @@ function buildAcpSession(state: AcpProviderState, options: SessionEntryBuildOpti
341
354
  const workspace = state.workspace || null;
342
355
  const git = getGitSummaryForWorkspace(workspace, options);
343
356
  const meshCoordinatorFor = state.settings?.meshCoordinatorFor as string | undefined;
344
- const meshQueueStats = meshCoordinatorFor ? getMeshQueueStats(meshCoordinatorFor) : undefined;
357
+ const registryEntry = state.instanceId ? getCoordinatorForSession(state.instanceId) : undefined;
358
+ const effectiveMeshId = meshCoordinatorFor || registryEntry?.meshId;
359
+ const coordinator = effectiveMeshId ? { meshId: effectiveMeshId, role: 'coordinator' as const } : undefined;
360
+ const meshQueueStats = effectiveMeshId ? getMeshQueueStats(effectiveMeshId) : undefined;
345
361
  return {
346
362
  id: state.instanceId,
347
363
  parentId: null,
@@ -364,6 +380,7 @@ function buildAcpSession(state: AcpProviderState, options: SessionEntryBuildOpti
364
380
  errorReason: state.errorReason,
365
381
  lastUpdated: state.lastUpdated,
366
382
  settings: state.settings,
383
+ ...(coordinator && { coordinator }),
367
384
  ...(meshQueueStats && { meshQueueStats }),
368
385
  };
369
386
  }
@@ -380,7 +397,7 @@ export function buildSessionEntries(
380
397
  const acpStates = allStates.filter((s): s is AcpProviderState => s.category === 'acp');
381
398
 
382
399
  for (const state of ideStates) {
383
- sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
400
+ sessions.push(buildWorkspaceSession(state, cdpManagers, options));
384
401
  for (const ext of state.extensions as ExtensionProviderState[]) {
385
402
  if (!shouldIncludeExtensionSession(ext)) continue;
386
403
  sessions.push(buildExtensionAgentSession(state, ext, options));
@@ -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
  }
@@ -297,18 +297,30 @@ export function getSessionCompletionMarker(session: {
297
297
  receivedAt?: number | string;
298
298
  timestamp?: number | string;
299
299
  _turnKey?: string;
300
+ kind?: string;
300
301
  }> | null
301
302
  } | null
302
303
  }) {
303
- const lastMessage = session.activeChat?.messages?.at?.(-1);
304
- if (!lastMessage) return '';
305
- const role = typeof lastMessage.role === 'string' ? lastMessage.role : '';
306
- if (role === 'user' || role === 'human' || role === 'system') return '';
307
- if (typeof lastMessage._turnKey === 'string' && lastMessage._turnKey) return `turn:${lastMessage._turnKey}`;
308
- if (typeof lastMessage.id === 'string' && lastMessage.id) return `id:${lastMessage.id}`;
309
- if (typeof lastMessage.index === 'number' && Number.isFinite(lastMessage.index)) return `idx:${lastMessage.index}`;
310
- const timestamp = getMessageEventTime(lastMessage);
311
- return timestamp > 0 ? `ts:${timestamp}` : '';
304
+ const messages = session.activeChat?.messages;
305
+ if (!Array.isArray(messages) || messages.length === 0) return '';
306
+ // Walk backwards until we find an assistant message that's not a tool call,
307
+ // or a user/human message (which ends the turn). System messages and tool
308
+ // messages don't gate "task completion" they can't be acknowledged
309
+ // independently and rotate too aggressively for marker-based bell clearing.
310
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
311
+ const m = messages[i];
312
+ const role = typeof m?.role === 'string' ? m.role : '';
313
+ const kind = typeof m?.kind === 'string' ? m.kind : '';
314
+ if (role === 'user' || role === 'human') return '';
315
+ if (role === 'system') continue;
316
+ if (kind === 'tool') continue;
317
+ if (typeof m._turnKey === 'string' && m._turnKey) return `turn:${m._turnKey}`;
318
+ if (typeof m.id === 'string' && m.id) return `id:${m.id}`;
319
+ if (typeof m.index === 'number' && Number.isFinite(m.index)) return `idx:${m.index}`;
320
+ const timestamp = getMessageEventTime(m);
321
+ return timestamp > 0 ? `ts:${timestamp}` : '';
322
+ }
323
+ return '';
312
324
  }
313
325
 
314
326
  function getSessionLastUsedAt(session: {
@@ -347,9 +359,21 @@ function getUnreadState(
347
359
  if (status === 'generating' || status === 'starting') {
348
360
  return { unread: false, inboxBucket: 'working' };
349
361
  }
362
+ // Read-state resolution:
363
+ // - When provider supplies a `completionMarker`, prefer marker-equality.
364
+ // But if `seenCompletionMarker` hasn't been recorded yet (legacy/transition
365
+ // case), fall back to timestamp comparison so a stale marker doesn't keep
366
+ // the bell lit after the user already viewed the conversation.
367
+ // - When there is no completionMarker, use timestamp + role guard.
368
+ // - Trailing `assistant.tool` turns no longer count as unread on their own
369
+ // — the human can't acknowledge a tool call separately from the answer,
370
+ // so they'd be stuck with a perpetually unread badge.
371
+ const ignorableTrailingRoles = lastRole === 'user' || lastRole === 'human' || lastRole === 'system' || lastRole === 'tool';
350
372
  const unread = completionMarker
351
- ? completionMarker !== seenCompletionMarker
352
- : hasContentChange && lastUsedAt > lastSeenAt && lastRole !== 'user' && lastRole !== 'human' && lastRole !== 'system';
373
+ ? (seenCompletionMarker
374
+ ? completionMarker !== seenCompletionMarker
375
+ : hasContentChange && lastUsedAt > lastSeenAt && !ignorableTrailingRoles)
376
+ : hasContentChange && lastUsedAt > lastSeenAt && !ignorableTrailingRoles;
353
377
  return { unread, inboxBucket: unread ? 'task_complete' : 'idle' };
354
378
  }
355
379
 
@@ -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
  }
package/src/types.ts CHANGED
@@ -43,6 +43,11 @@ export interface ChatMessage {
43
43
  index?: number;
44
44
  timestamp?: number;
45
45
  receivedAt?: number;
46
+ /** (A2.3) Monotonic sequence number per (session, source). Producer-asserted
47
+ * when emitted by v2-aware providers (transcript v2 schema); otherwise
48
+ * derived by the daemon from receivedAt/timestamp/index. ChatSourceMachine
49
+ * uses this as the native peak watermark for regression detection. */
50
+ sequence?: number;
46
51
  _turnKey?: string;
47
52
  /** Tool calls associated with this message */
48
53
  toolCalls?: ToolCallInfo[];