@adhdev/daemon-core 0.9.82-rc.209 → 0.9.82-rc.210

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 (64) hide show
  1. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +2 -0
  2. package/dist/commands/router.d.ts +6 -0
  3. package/dist/git/git-commands.d.ts +2 -0
  4. package/dist/git/git-diff.d.ts +6 -0
  5. package/dist/index.d.ts +11 -5
  6. package/dist/index.js +5699 -3486
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +5679 -3483
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/mesh/coordinator-prompt.d.ts +6 -0
  11. package/dist/mesh/mesh-delivery-policy.d.ts +5 -0
  12. package/dist/mesh/mesh-events-coordinator.d.ts +151 -0
  13. package/dist/mesh/mesh-events-pending.d.ts +33 -0
  14. package/dist/mesh/mesh-events-stale.d.ts +40 -0
  15. package/dist/mesh/mesh-events-utils.d.ts +14 -0
  16. package/dist/mesh/mesh-events.d.ts +5 -198
  17. package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -3
  18. package/dist/mesh/mesh-ledger.d.ts +19 -0
  19. package/dist/mesh/mesh-missions.d.ts +58 -0
  20. package/dist/mesh/mesh-review-inbox.d.ts +90 -0
  21. package/dist/mesh/mesh-runtime-store.d.ts +175 -0
  22. package/dist/mesh/mesh-task-stats.d.ts +49 -0
  23. package/dist/mesh/mesh-work-queue.d.ts +82 -0
  24. package/dist/mesh/refine-config.d.ts +24 -2
  25. package/dist/mesh/worktree-bootstrap-config.d.ts +22 -0
  26. package/dist/providers/acp-provider-instance.d.ts +2 -0
  27. package/dist/providers/spec/driver.d.ts +8 -0
  28. package/dist/providers/spec/evaluator.d.ts +4 -5
  29. package/dist/providers/spec/loader.d.ts +1 -0
  30. package/dist/providers/spec/schema.gen.d.ts +1409 -6
  31. package/dist/providers/spec/types.d.ts +188 -175
  32. package/dist/repo-mesh-types.d.ts +1 -0
  33. package/package.json +1 -1
  34. package/src/boot/daemon-lifecycle.ts +3 -0
  35. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +20 -7
  36. package/src/commands/router.ts +594 -66
  37. package/src/git/git-commands.ts +5 -5
  38. package/src/git/git-diff.ts +53 -0
  39. package/src/index.ts +11 -5
  40. package/src/mesh/coordinator-prompt.ts +14 -1
  41. package/src/mesh/mesh-delivery-policy.ts +17 -0
  42. package/src/mesh/mesh-events-coordinator.ts +1404 -0
  43. package/src/mesh/mesh-events-pending.ts +371 -0
  44. package/src/mesh/mesh-events-stale.ts +283 -0
  45. package/src/mesh/mesh-events-utils.ts +161 -0
  46. package/src/mesh/mesh-events.ts +27 -2143
  47. package/src/mesh/mesh-ledger-reconciliation.ts +12 -5
  48. package/src/mesh/mesh-ledger.ts +134 -2
  49. package/src/mesh/mesh-missions.ts +151 -0
  50. package/src/mesh/mesh-review-inbox.ts +307 -0
  51. package/src/mesh/mesh-runtime-store.ts +539 -3
  52. package/src/mesh/mesh-task-stats.ts +154 -0
  53. package/src/mesh/mesh-work-queue.ts +233 -17
  54. package/src/mesh/refine-config.ts +42 -5
  55. package/src/mesh/worktree-bootstrap-config.ts +79 -0
  56. package/src/providers/acp-provider-instance.ts +15 -1
  57. package/src/providers/cli-provider-instance.ts +34 -13
  58. package/src/providers/spec/driver.ts +57 -29
  59. package/src/providers/spec/evaluator.ts +302 -112
  60. package/src/providers/spec/loader.ts +226 -37
  61. package/src/providers/spec/schema.gen.ts +450 -334
  62. package/src/providers/spec/schema.json +162 -75
  63. package/src/providers/spec/types.ts +234 -183
  64. package/src/repo-mesh-types.ts +1 -0
@@ -0,0 +1,307 @@
1
+ /**
2
+ * M4.0: Review inbox — derive review items from existing truth.
3
+ *
4
+ * No new store: review items are a query-time projection of
5
+ * - mesh_status node statuses (daemon-owned branchConvergence), and
6
+ * - the task ledger (completion evidence envelopes + refine job results).
7
+ *
8
+ * Scope (M4.0): host-daemon-local nodes only. Remote nodes are excluded and
9
+ * surfaced via remoteNodesExcluded/excludedRemoteNodeIds until the
10
+ * daemon↔daemon P2P relay path is live-verified (M4.2).
11
+ */
12
+
13
+ import type { MeshLedgerEntry } from './mesh-ledger.js';
14
+ import { buildMeshAsyncRefineJobs } from './mesh-refine-status.js';
15
+
16
+ export type MeshReviewInboxReason = 'merge_candidate' | 'refine_blocked_review';
17
+
18
+ export interface MeshReviewInboxConvergence {
19
+ status: string;
20
+ reason: string | null;
21
+ nextStep: string | null;
22
+ needsConvergence: boolean;
23
+ }
24
+
25
+ export interface MeshReviewInboxEvidence {
26
+ available: boolean;
27
+ /** Ledger kind of the entry the evidence came from. */
28
+ kind?: 'task_completed' | 'task_failed';
29
+ /** 'task_completion' = ordinary worker completion envelope; 'refine_job' = Refinery result. */
30
+ source?: 'task_completion' | 'refine_job';
31
+ timestamp?: string;
32
+ taskId?: string;
33
+ sessionId?: string;
34
+ /** M2-3 bootstrap stage from the refine validation gate: cached|ran|failed|skipped|legacy|not_configured. */
35
+ bootstrap?: Record<string, unknown> | null;
36
+ validation?: Record<string, unknown> | null;
37
+ checkpoint?: Record<string, unknown> | null;
38
+ worker?: {
39
+ status: string;
40
+ classification?: string;
41
+ changedFiles: string[];
42
+ changedFilesTruncated?: true;
43
+ validationResults: Array<Record<string, unknown>>;
44
+ errors: string[];
45
+ requiresUserAction: boolean;
46
+ } | null;
47
+ finalBranchConvergenceState?: Record<string, unknown>;
48
+ refineJob?: Record<string, unknown>;
49
+ }
50
+
51
+ export interface MeshReviewInboxDiffFile {
52
+ path: string;
53
+ status?: string;
54
+ insertions?: number;
55
+ deletions?: number;
56
+ binary?: boolean;
57
+ oldPath?: string;
58
+ }
59
+
60
+ export interface MeshReviewInboxDiffSummary {
61
+ baseRef: string;
62
+ files: MeshReviewInboxDiffFile[];
63
+ totalFiles: number;
64
+ totalInsertions: number;
65
+ totalDeletions: number;
66
+ truncated: boolean;
67
+ error?: string;
68
+ }
69
+
70
+ export interface MeshReviewInboxItem {
71
+ nodeId: string;
72
+ workspace: string | null;
73
+ branch: string | null;
74
+ defaultBranch: string | null;
75
+ isLocalWorktree: boolean;
76
+ reviewReason: MeshReviewInboxReason;
77
+ convergence: MeshReviewInboxConvergence;
78
+ evidence: MeshReviewInboxEvidence;
79
+ transcriptHandle: Record<string, unknown> | null;
80
+ /** Latest non-terminal Refinery job for this node (accepted/running), if any. */
81
+ activeRefineJob: Record<string, unknown> | null;
82
+ lastActivityAt: string | null;
83
+ /** Filled by the daemon command with a bounded base-branch diff probe; null when the workspace is not locally readable. */
84
+ diffSummary?: MeshReviewInboxDiffSummary | null;
85
+ }
86
+
87
+ export interface MeshReviewInboxDerivation {
88
+ items: MeshReviewInboxItem[];
89
+ remoteNodesExcluded: boolean;
90
+ excludedRemoteNodeIds: string[];
91
+ }
92
+
93
+ const MAX_CHANGED_FILES = 100;
94
+
95
+ function readString(value: unknown): string | null {
96
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
97
+ }
98
+
99
+ function readRecord(value: unknown): Record<string, unknown> | null {
100
+ return value && typeof value === 'object' && !Array.isArray(value)
101
+ ? value as Record<string, unknown>
102
+ : null;
103
+ }
104
+
105
+ function readStringArray(value: unknown, max: number): { values: string[]; truncated: boolean } {
106
+ if (!Array.isArray(value)) return { values: [], truncated: false };
107
+ const values = value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0);
108
+ return { values: values.slice(0, max), truncated: values.length > max };
109
+ }
110
+
111
+ function isLocalNodeStatus(node: Record<string, unknown>): boolean {
112
+ if (node.isLocalWorktree === true) return true;
113
+ const connection = readRecord(node.connection);
114
+ return readString(connection?.state) === 'self';
115
+ }
116
+
117
+ function readNodeConvergence(node: Record<string, unknown>): (MeshReviewInboxConvergence & { branch: string | null; defaultBranch: string | null }) | null {
118
+ const convergence = readRecord(node.branchConvergence);
119
+ const status = readString(convergence?.status);
120
+ if (!convergence || !status) return null;
121
+ return {
122
+ status,
123
+ reason: readString(convergence.reason),
124
+ nextStep: readString(convergence.nextStep),
125
+ needsConvergence: convergence.needsConvergence === true,
126
+ branch: readString(convergence.branch),
127
+ defaultBranch: readString(convergence.defaultBranch),
128
+ };
129
+ }
130
+
131
+ function isMergeCandidate(convergence: MeshReviewInboxConvergence): boolean {
132
+ if (convergence.status === 'pushed_feature_branch_needs_merge') return true;
133
+ // Clean non-default worktree branch — the local merge candidate shape
134
+ // (router classifies it cleanup_candidate with this reason).
135
+ return convergence.status === 'cleanup_candidate'
136
+ && convergence.reason === 'clean_non_default_worktree_branch';
137
+ }
138
+
139
+ function readWorkerArtifact(value: unknown): MeshReviewInboxEvidence['worker'] {
140
+ const worker = readRecord(value);
141
+ if (!worker) return null;
142
+ const changed = readStringArray(worker.changedFiles, MAX_CHANGED_FILES);
143
+ return {
144
+ status: readString(worker.status) ?? 'unknown',
145
+ ...(readString(worker.classification) ? { classification: readString(worker.classification)! } : {}),
146
+ changedFiles: changed.values,
147
+ ...(changed.truncated ? { changedFilesTruncated: true as const } : {}),
148
+ validationResults: Array.isArray(worker.validationResults)
149
+ ? worker.validationResults.map(item => readRecord(item)).filter((item): item is Record<string, unknown> => item !== null)
150
+ : [],
151
+ errors: readStringArray(worker.errors, 20).values,
152
+ requiresUserAction: worker.requiresUserAction === true,
153
+ };
154
+ }
155
+
156
+ function isTerminalLedgerKind(kind: string): kind is 'task_completed' | 'task_failed' {
157
+ return kind === 'task_completed' || kind === 'task_failed';
158
+ }
159
+
160
+ /**
161
+ * Latest terminal evidence for a node, normalized into the review card shape.
162
+ * Refine job entries carry the validation gate summary (incl. the M2-3
163
+ * bootstrap stage); ordinary completions carry the B-series evidence envelope.
164
+ */
165
+ function resolveNodeEvidence(nodeId: string, ledgerEntries: MeshLedgerEntry[]): {
166
+ evidence: MeshReviewInboxEvidence;
167
+ transcriptHandle: Record<string, unknown> | null;
168
+ } {
169
+ let evidence: MeshReviewInboxEvidence = { available: false };
170
+ let transcriptHandle: Record<string, unknown> | null = null;
171
+
172
+ for (let i = ledgerEntries.length - 1; i >= 0; i--) {
173
+ const entry = ledgerEntries[i];
174
+ if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
175
+ const payload = readRecord(entry.payload) ?? {};
176
+
177
+ if (!evidence.available) {
178
+ if (payload.source === 'refine_mesh_node_async_job') {
179
+ const result = readRecord(payload.result);
180
+ const validationSummary = readRecord(result?.validationSummary);
181
+ evidence = {
182
+ available: true,
183
+ kind: entry.kind,
184
+ source: 'refine_job',
185
+ timestamp: entry.timestamp,
186
+ ...(entry.sessionId ? { sessionId: entry.sessionId } : {}),
187
+ bootstrap: readRecord(validationSummary?.bootstrap),
188
+ validation: validationSummary
189
+ ? Object.fromEntries(Object.entries(validationSummary).filter(([key]) => key !== 'bootstrap'))
190
+ : null,
191
+ checkpoint: readRecord(result?.checkpoint),
192
+ worker: null,
193
+ ...(readRecord(payload.finalBranchConvergenceState) ?? readRecord(result?.finalBranchConvergenceState)
194
+ ? { finalBranchConvergenceState: (readRecord(payload.finalBranchConvergenceState) ?? readRecord(result?.finalBranchConvergenceState))! }
195
+ : {}),
196
+ ...(readRecord(payload.refineJob) ? { refineJob: readRecord(payload.refineJob)! } : {}),
197
+ };
198
+ } else {
199
+ const envelope = readRecord(payload.evidence);
200
+ evidence = {
201
+ available: true,
202
+ kind: entry.kind,
203
+ source: 'task_completion',
204
+ timestamp: entry.timestamp,
205
+ ...(readString(payload.taskId) ? { taskId: readString(payload.taskId)! } : {}),
206
+ ...(entry.sessionId ? { sessionId: entry.sessionId } : {}),
207
+ bootstrap: null,
208
+ validation: readRecord(envelope?.validation),
209
+ checkpoint: readRecord(envelope?.checkpoint),
210
+ worker: readWorkerArtifact(envelope?.workerResult ?? payload.workerResult),
211
+ };
212
+ }
213
+ }
214
+
215
+ if (!transcriptHandle) {
216
+ const envelope = readRecord(payload.evidence);
217
+ transcriptHandle = readRecord(envelope?.transcriptHandle);
218
+ }
219
+
220
+ if (evidence.available && transcriptHandle) break;
221
+ }
222
+
223
+ return { evidence, transcriptHandle };
224
+ }
225
+
226
+ /** Latest refine terminal result for a node that classified it blocked_review. */
227
+ function hasBlockedReviewRefineResult(nodeId: string, ledgerEntries: MeshLedgerEntry[]): boolean {
228
+ for (let i = ledgerEntries.length - 1; i >= 0; i--) {
229
+ const entry = ledgerEntries[i];
230
+ if (entry.nodeId !== nodeId || !isTerminalLedgerKind(entry.kind)) continue;
231
+ const payload = readRecord(entry.payload) ?? {};
232
+ if (payload.source !== 'refine_mesh_node_async_job') continue;
233
+ const result = readRecord(payload.result);
234
+ const finalState = readRecord(payload.finalBranchConvergenceState) ?? readRecord(result?.finalBranchConvergenceState);
235
+ // Only the most recent refine terminal counts — an older blocked_review
236
+ // superseded by a successful refine must not resurrect the card.
237
+ return readString(finalState?.status) === 'blocked_review'
238
+ || readString(result?.code) === 'blocked_review';
239
+ }
240
+ return false;
241
+ }
242
+
243
+ /**
244
+ * Derive review inbox items from mesh_status node statuses and a bounded
245
+ * ledger tail. Pure and read-only; the caller attaches diffSummary separately.
246
+ */
247
+ export function deriveMeshReviewInboxItems(args: {
248
+ nodes: Array<Record<string, unknown>>;
249
+ ledgerEntries: MeshLedgerEntry[];
250
+ }): MeshReviewInboxDerivation {
251
+ const items: MeshReviewInboxItem[] = [];
252
+ const excludedRemoteNodeIds: string[] = [];
253
+ const refineJobs = buildMeshAsyncRefineJobs({ ledgerEntries: args.ledgerEntries });
254
+
255
+ for (const node of args.nodes) {
256
+ const nodeId = readString(node.nodeId) ?? readString(node.id);
257
+ if (!nodeId) continue;
258
+
259
+ if (!isLocalNodeStatus(node)) {
260
+ excludedRemoteNodeIds.push(nodeId);
261
+ continue;
262
+ }
263
+
264
+ const convergence = readNodeConvergence(node);
265
+ if (!convergence) continue;
266
+ if (convergence.status === 'merged_to_main') continue;
267
+
268
+ let reviewReason: MeshReviewInboxReason | null = null;
269
+ if (hasBlockedReviewRefineResult(nodeId, args.ledgerEntries)) {
270
+ reviewReason = 'refine_blocked_review';
271
+ } else if (isMergeCandidate(convergence)) {
272
+ reviewReason = 'merge_candidate';
273
+ }
274
+ if (!reviewReason) continue;
275
+
276
+ const { evidence, transcriptHandle } = resolveNodeEvidence(nodeId, args.ledgerEntries);
277
+ const activeRefineJob = [...refineJobs].reverse().find(job =>
278
+ (job.nodeId === nodeId || job.targetNodeId === nodeId)
279
+ && (job.status === 'accepted' || job.status === 'running'),
280
+ ) ?? null;
281
+
282
+ items.push({
283
+ nodeId,
284
+ workspace: readString(node.workspace),
285
+ branch: convergence.branch ?? readString(node.worktreeBranch),
286
+ defaultBranch: convergence.defaultBranch,
287
+ isLocalWorktree: node.isLocalWorktree === true,
288
+ reviewReason,
289
+ convergence: {
290
+ status: convergence.status,
291
+ reason: convergence.reason,
292
+ nextStep: convergence.nextStep,
293
+ needsConvergence: convergence.needsConvergence,
294
+ },
295
+ evidence,
296
+ transcriptHandle,
297
+ activeRefineJob: activeRefineJob as Record<string, unknown> | null,
298
+ lastActivityAt: evidence.timestamp ?? null,
299
+ });
300
+ }
301
+
302
+ return {
303
+ items,
304
+ remoteNodesExcluded: excludedRemoteNodeIds.length > 0,
305
+ excludedRemoteNodeIds,
306
+ };
307
+ }