@adhdev/daemon-core 0.9.82-rc.16 → 0.9.82-rc.161

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 (209) 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 +19 -2
  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/mesh-coordinator.d.ts +72 -1
  13. package/dist/commands/router.d.ts +22 -0
  14. package/dist/config/chat-history.d.ts +7 -0
  15. package/dist/config/config.d.ts +5 -0
  16. package/dist/config/mesh-config.d.ts +71 -1
  17. package/dist/git/git-commands.d.ts +5 -1
  18. package/dist/index.d.ts +31 -6
  19. package/dist/index.js +17210 -3575
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +17127 -3551
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/installer.d.ts +1 -4
  24. package/dist/ipc/local-ipc-server.d.ts +91 -0
  25. package/dist/launch.d.ts +1 -1
  26. package/dist/logging/async-batch-writer.d.ts +10 -0
  27. package/dist/mesh/beads-db.d.ts +72 -0
  28. package/dist/mesh/contracts.d.ts +164 -0
  29. package/dist/mesh/coordinator-prompt.d.ts +30 -0
  30. package/dist/mesh/coordinator-registry.d.ts +59 -0
  31. package/dist/mesh/mesh-active-work.d.ts +90 -0
  32. package/dist/mesh/mesh-events.d.ts +77 -5
  33. package/dist/mesh/mesh-fast-forward.d.ts +39 -0
  34. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  35. package/dist/mesh/mesh-ledger.d.ts +58 -1
  36. package/dist/mesh/mesh-refine-status.d.ts +26 -0
  37. package/dist/mesh/mesh-work-queue.d.ts +44 -5
  38. package/dist/mesh/preview-freshness.d.ts +18 -0
  39. package/dist/mesh/refine-config.d.ts +193 -0
  40. package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
  41. package/dist/providers/approval-utils.d.ts +9 -0
  42. package/dist/providers/cli-provider-instance.d.ts +7 -2
  43. package/dist/providers/contracts.d.ts +98 -5
  44. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  45. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  46. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  47. package/dist/providers/native-history/dispatcher.d.ts +24 -0
  48. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  49. package/dist/providers/native-history/index.d.ts +13 -0
  50. package/dist/providers/provider-loader.d.ts +19 -1
  51. package/dist/providers/read-chat-contract.d.ts +29 -0
  52. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  53. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
  54. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  55. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +64 -0
  56. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  57. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  58. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  59. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  60. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  61. package/dist/providers/sdk/v1/index.d.ts +30 -0
  62. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  63. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  64. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  65. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  66. package/dist/providers/sdk/v1/types/common/index.d.ts +169 -0
  67. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  68. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  69. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  70. package/dist/providers/spec/adapter.d.ts +56 -0
  71. package/dist/providers/spec/cli-adapter.d.ts +76 -0
  72. package/dist/providers/spec/driver.d.ts +148 -0
  73. package/dist/providers/spec/evaluator.d.ts +47 -0
  74. package/dist/providers/spec/loader.d.ts +14 -0
  75. package/dist/providers/spec/native-history-executor.d.ts +39 -0
  76. package/dist/providers/spec/route.d.ts +4 -0
  77. package/dist/providers/spec/schema.gen.d.ts +507 -0
  78. package/dist/providers/spec/types.d.ts +211 -0
  79. package/dist/providers/transcript-v2.d.ts +176 -0
  80. package/dist/repo-mesh-types.d.ts +100 -1
  81. package/dist/sessions/registry.d.ts +3 -0
  82. package/dist/shared-types.d.ts +12 -0
  83. package/dist/status/reporter.d.ts +2 -0
  84. package/dist/status/snapshot.d.ts +1 -0
  85. package/dist/types.d.ts +5 -0
  86. package/package.json +7 -2
  87. package/src/boot/daemon-lifecycle.ts +17 -10
  88. package/src/boot/process-hardening.ts +89 -0
  89. package/src/chat/source-machine.ts +534 -0
  90. package/src/chat/source-resolver.ts +0 -0
  91. package/src/chat/subscription-updates.ts +14 -1
  92. package/src/cli-adapter-types.d.ts +1 -0
  93. package/src/cli-adapter-types.ts +18 -2
  94. package/src/cli-adapters/cli-script-runner.ts +421 -0
  95. package/src/cli-adapters/cli-state-engine.ts +1086 -0
  96. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  97. package/src/cli-adapters/provider-cli-adapter.ts +664 -1137
  98. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  99. package/src/cli-adapters/provider-cli-parse.ts +13 -0
  100. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  101. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  102. package/src/cli-adapters/provider-cli-shared.ts +57 -11
  103. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
  104. package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
  105. package/src/commands/chat-commands.ts +1597 -60
  106. package/src/commands/cli-manager.ts +195 -3
  107. package/src/commands/handler.ts +547 -2
  108. package/src/commands/mesh-coordinator.ts +231 -127
  109. package/src/commands/router.ts +3547 -502
  110. package/src/config/chat-history.ts +87 -24
  111. package/src/config/config.ts +12 -0
  112. package/src/config/mesh-config.ts +264 -2
  113. package/src/config/recent-activity.ts +8 -2
  114. package/src/daemon/dev-cli-debug.ts +10 -1
  115. package/src/detection/ide-detector.ts +26 -16
  116. package/src/git/git-commands.ts +17 -5
  117. package/src/git/git-worktree.ts +8 -1
  118. package/src/index.ts +91 -5
  119. package/src/installer.d.ts +1 -1
  120. package/src/installer.ts +8 -6
  121. package/src/ipc/local-ipc-server.ts +278 -0
  122. package/src/launch.d.ts +1 -1
  123. package/src/launch.ts +37 -28
  124. package/src/logging/async-batch-writer.ts +55 -0
  125. package/src/logging/logger.ts +2 -1
  126. package/src/mesh/beads-db.ts +479 -0
  127. package/src/mesh/contracts.ts +329 -0
  128. package/src/mesh/coordinator-prompt.ts +204 -30
  129. package/src/mesh/coordinator-registry.ts +121 -0
  130. package/src/mesh/mesh-active-work.ts +437 -0
  131. package/src/mesh/mesh-events.ts +820 -61
  132. package/src/mesh/mesh-fast-forward.ts +430 -0
  133. package/src/mesh/mesh-host-ownership.ts +73 -0
  134. package/src/mesh/mesh-ledger.ts +457 -104
  135. package/src/mesh/mesh-refine-status.ts +144 -0
  136. package/src/mesh/mesh-work-queue.ts +216 -158
  137. package/src/mesh/preview-freshness.ts +118 -0
  138. package/src/mesh/refine-config.ts +366 -0
  139. package/src/mesh/worktree-bootstrap-config.ts +247 -0
  140. package/src/providers/approval-utils.ts +39 -5
  141. package/src/providers/chat-message-normalization.ts +4 -11
  142. package/src/providers/cli-provider-instance.ts +388 -47
  143. package/src/providers/contracts.ts +105 -5
  144. package/src/providers/ide-provider-instance.ts +17 -3
  145. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  146. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  147. package/src/providers/native-history/codex-cli-transcript.ts +419 -0
  148. package/src/providers/native-history/dispatcher.ts +227 -0
  149. package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
  150. package/src/providers/native-history/index.ts +30 -0
  151. package/src/providers/provider-loader.ts +420 -31
  152. package/src/providers/provider-schema.ts +31 -13
  153. package/src/providers/read-chat-contract.ts +76 -16
  154. package/src/providers/sdk/README.md +49 -0
  155. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  156. package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
  157. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  158. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +245 -0
  159. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  160. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  161. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  162. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  163. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  164. package/src/providers/sdk/v1/index.ts +152 -0
  165. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  166. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  167. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  168. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +457 -0
  169. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  170. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  171. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  172. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  173. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  174. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  175. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  176. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  177. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +119 -0
  178. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  179. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  180. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  181. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  182. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  183. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  184. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  185. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  186. package/src/providers/sdk/v1/types/common/index.ts +210 -0
  187. package/src/providers/sdk/v1/validators/index.ts +19 -0
  188. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  189. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  190. package/src/providers/spec/adapter.ts +153 -0
  191. package/src/providers/spec/cli-adapter.ts +318 -0
  192. package/src/providers/spec/driver.ts +498 -0
  193. package/src/providers/spec/evaluator.ts +268 -0
  194. package/src/providers/spec/loader.ts +130 -0
  195. package/src/providers/spec/native-history-executor.ts +612 -0
  196. package/src/providers/spec/route.ts +51 -0
  197. package/src/providers/spec/schema.gen.ts +507 -0
  198. package/src/providers/spec/schema.json +210 -0
  199. package/src/providers/spec/types.ts +230 -0
  200. package/src/providers/transcript-v2.ts +567 -0
  201. package/src/providers/version-archive.ts +38 -20
  202. package/src/repo-mesh-types.ts +110 -1
  203. package/src/sessions/registry.ts +3 -0
  204. package/src/shared-types.ts +9 -0
  205. package/src/status/builders.ts +23 -6
  206. package/src/status/reporter.ts +15 -0
  207. package/src/status/snapshot.ts +35 -11
  208. package/src/system/host-memory.ts +29 -12
  209. 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,
@@ -207,7 +247,28 @@ export interface RepoMeshCoordinatorConfig {
207
247
  providerType?: string;
208
248
  /** Preferred node to run coordinator on (null = auto) */
209
249
  preferredNodeId?: string;
210
- /** Additional system prompt context for coordinator */
250
+ /**
251
+ * Full mesh-level override for the coordinator system prompt. When set,
252
+ * replaces the daemon's rendered default and any user-file override
253
+ * (~/.adhdev/coordinator-prompts/<cli>.md). The per-launch
254
+ * extraSystemPrompt still composes on top — it always lands last as
255
+ * Additional Context. Supports the same {{placeholders}} the daemon's
256
+ * default template uses ({{meshName}}, {{repo}}, {{nodes}}, …).
257
+ */
258
+ systemPromptOverride?: string;
259
+ /**
260
+ * Mesh-level append. Composes after whichever base prompt won
261
+ * (override → user-file override → daemon default). Use this when you
262
+ * want extra rules for THIS mesh but otherwise the standard prompt is
263
+ * fine. Stacks with the user-file append (`<cli>.append.md`) — both
264
+ * apply if both are set.
265
+ */
266
+ systemPromptAppend?: string;
267
+ /**
268
+ * @deprecated Use systemPromptAppend. Kept as a fallback alias so
269
+ * existing meshes.json files keep working without a migration step;
270
+ * the daemon prefers systemPromptAppend when both are present.
271
+ */
211
272
  systemPromptSuffix?: string;
212
273
  }
213
274
 
@@ -229,6 +290,7 @@ export interface LocalMeshEntry {
229
290
  defaultBranch?: string;
230
291
  policy: RepoMeshPolicy;
231
292
  coordinator: RepoMeshCoordinatorConfig;
293
+ meshHost?: RepoMeshHostMetadata;
232
294
  nodes: LocalMeshNodeEntry[];
233
295
  createdAt: string;
234
296
  updatedAt: string;
@@ -243,14 +305,38 @@ export interface LocalMeshNodeEntry {
243
305
  machineId?: string;
244
306
  userOverrides: Partial<RepoMeshNodeCapabilities>;
245
307
  policy: RepoMeshNodePolicy;
308
+ /**
309
+ * Per-node instruction surfaced in the coordinator prompt so the LLM
310
+ * knows what each node is for (e.g. "this is the staging mirror — run
311
+ * only smoke tests here", or "use opus on this node, sonnet elsewhere").
312
+ * Empty/missing: omitted silently from the rendered prompt, no rule
313
+ * line about it gets added. The coordinator forwards/honors it when
314
+ * delegating; we don't enforce it at the daemon level.
315
+ */
316
+ systemPrompt?: string;
246
317
  /** For single-machine mesh: same daemon, different worktree */
247
318
  isLocalWorktree?: boolean;
248
319
  /** Branch this worktree tracks (set when created via clone_mesh_node) */
249
320
  worktreeBranch?: string;
250
321
  /** Node ID this worktree was cloned from */
251
322
  clonedFromNodeId?: string;
323
+ /** Repo-local preparation result for ADHDev-created worktree nodes. */
324
+ worktreeBootstrap?: {
325
+ status: 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
326
+ required?: boolean;
327
+ configSource?: string;
328
+ configSourceType?: string;
329
+ startedAt?: string;
330
+ completedAt?: string;
331
+ lastCommand?: string;
332
+ exitCode?: number | null;
333
+ error?: string;
334
+ commandsRun?: Array<Record<string, unknown>>;
335
+ staleInputs?: string[];
336
+ };
252
337
  /** Optional associated/external repos configured as node metadata. */
253
338
  relatedRepos?: RepoMeshRelatedRepo[];
339
+ role?: RepoMeshDaemonRole;
254
340
  }
255
341
 
256
342
  // ─── Mesh Status (runtime, not persisted) ───────
@@ -261,6 +347,7 @@ export interface RepoMeshStatus {
261
347
  repoIdentity: string;
262
348
  defaultBranch?: string;
263
349
  refreshedAt: string;
350
+ meshHost?: RepoMeshHostStatus;
264
351
  nodes: RepoMeshNodeStatus[];
265
352
  queue?: RepoMeshQueueStatus;
266
353
  ledger?: RepoMeshLedgerStatus;
@@ -301,16 +388,28 @@ export interface RepoMeshNodeStatus {
301
388
  repoRoot?: string;
302
389
  daemonId?: string;
303
390
  machineId?: string;
391
+ role?: RepoMeshDaemonRole;
304
392
  machineStatus?: string;
305
393
  isLocalWorktree?: boolean;
306
394
  worktreeBranch?: string;
395
+ /** Mirrored from LocalMeshNodeEntry.systemPrompt for coordinator-prompt rendering. */
396
+ systemPrompt?: string;
307
397
  health: RepoMeshNodeHealth;
308
398
  git?: GitRepoStatus;
399
+ /**
400
+ * True when the selected coordinator has evidence that a peer git probe is still
401
+ * in flight or just timed out during initial mesh handshake, so callers should
402
+ * treat missing git data as pending instead of authoritative absence.
403
+ */
404
+ gitProbePending?: boolean;
309
405
  providers: string[];
310
406
  activeSessions: string[];
311
407
  activeSessionDetails?: RepoMeshSessionStatus[];
312
408
  providerPriority?: string[];
313
409
  launchReady?: boolean;
410
+ worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
411
+ launchBlockedReason?: string;
412
+ launchBlockedMessage?: string;
314
413
  lastSeenAt?: string;
315
414
  updatedAt?: string;
316
415
  connection?: RepoMeshPeerConnectionStatus;
@@ -405,3 +504,13 @@ export interface RepoMeshLedgerStatus {
405
504
  entries: RepoMeshLedgerEntryStatus[];
406
505
  summary: RepoMeshLedgerSummaryStatus;
407
506
  }
507
+
508
+ // ─── Async Job Lifecycle ─────────────────────────
509
+ // Shared base for all mesh async job types (refine jobs, bootstrap runs, etc.)
510
+ // Each concrete type adds its own status enum and domain-specific fields.
511
+
512
+ export interface MeshAsyncJobLifecycle {
513
+ startedAt?: string;
514
+ completedAt?: string;
515
+ error?: string;
516
+ }
@@ -8,6 +8,9 @@ export interface SessionRuntimeTarget {
8
8
  cdpManagerKey?: string;
9
9
  adapterKey?: string;
10
10
  instanceKey?: string;
11
+ /** Wall clock at register time. native-history readers use it as a
12
+ * cutoff so a fresh session can't show records from a prior one. */
13
+ spawnedAtMs?: number;
11
14
  }
12
15
 
13
16
  export class SessionRegistry {
@@ -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[];