@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,90 @@
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
+ import type { MeshLedgerEntry } from './mesh-ledger.js';
13
+ export type MeshReviewInboxReason = 'merge_candidate' | 'refine_blocked_review';
14
+ export interface MeshReviewInboxConvergence {
15
+ status: string;
16
+ reason: string | null;
17
+ nextStep: string | null;
18
+ needsConvergence: boolean;
19
+ }
20
+ export interface MeshReviewInboxEvidence {
21
+ available: boolean;
22
+ /** Ledger kind of the entry the evidence came from. */
23
+ kind?: 'task_completed' | 'task_failed';
24
+ /** 'task_completion' = ordinary worker completion envelope; 'refine_job' = Refinery result. */
25
+ source?: 'task_completion' | 'refine_job';
26
+ timestamp?: string;
27
+ taskId?: string;
28
+ sessionId?: string;
29
+ /** M2-3 bootstrap stage from the refine validation gate: cached|ran|failed|skipped|legacy|not_configured. */
30
+ bootstrap?: Record<string, unknown> | null;
31
+ validation?: Record<string, unknown> | null;
32
+ checkpoint?: Record<string, unknown> | null;
33
+ worker?: {
34
+ status: string;
35
+ classification?: string;
36
+ changedFiles: string[];
37
+ changedFilesTruncated?: true;
38
+ validationResults: Array<Record<string, unknown>>;
39
+ errors: string[];
40
+ requiresUserAction: boolean;
41
+ } | null;
42
+ finalBranchConvergenceState?: Record<string, unknown>;
43
+ refineJob?: Record<string, unknown>;
44
+ }
45
+ export interface MeshReviewInboxDiffFile {
46
+ path: string;
47
+ status?: string;
48
+ insertions?: number;
49
+ deletions?: number;
50
+ binary?: boolean;
51
+ oldPath?: string;
52
+ }
53
+ export interface MeshReviewInboxDiffSummary {
54
+ baseRef: string;
55
+ files: MeshReviewInboxDiffFile[];
56
+ totalFiles: number;
57
+ totalInsertions: number;
58
+ totalDeletions: number;
59
+ truncated: boolean;
60
+ error?: string;
61
+ }
62
+ export interface MeshReviewInboxItem {
63
+ nodeId: string;
64
+ workspace: string | null;
65
+ branch: string | null;
66
+ defaultBranch: string | null;
67
+ isLocalWorktree: boolean;
68
+ reviewReason: MeshReviewInboxReason;
69
+ convergence: MeshReviewInboxConvergence;
70
+ evidence: MeshReviewInboxEvidence;
71
+ transcriptHandle: Record<string, unknown> | null;
72
+ /** Latest non-terminal Refinery job for this node (accepted/running), if any. */
73
+ activeRefineJob: Record<string, unknown> | null;
74
+ lastActivityAt: string | null;
75
+ /** Filled by the daemon command with a bounded base-branch diff probe; null when the workspace is not locally readable. */
76
+ diffSummary?: MeshReviewInboxDiffSummary | null;
77
+ }
78
+ export interface MeshReviewInboxDerivation {
79
+ items: MeshReviewInboxItem[];
80
+ remoteNodesExcluded: boolean;
81
+ excludedRemoteNodeIds: string[];
82
+ }
83
+ /**
84
+ * Derive review inbox items from mesh_status node statuses and a bounded
85
+ * ledger tail. Pure and read-only; the caller attaches diffSummary separately.
86
+ */
87
+ export declare function deriveMeshReviewInboxItems(args: {
88
+ nodes: Array<Record<string, unknown>>;
89
+ ledgerEntries: MeshLedgerEntry[];
90
+ }): MeshReviewInboxDerivation;
@@ -146,4 +146,179 @@ export declare class MeshRuntimeStore {
146
146
  event: string;
147
147
  createdAt: string;
148
148
  }>;
149
+ /**
150
+ * Record a mesh tool call and check whether this mesh+tool combination is
151
+ * being called too rapidly (sliding window rate guard).
152
+ *
153
+ * Returns a rate-limit advisory string when the call rate is too high, null otherwise.
154
+ * windowMs: sliding window size in ms (default 10s)
155
+ * maxCalls: max allowed calls within the window (default 5)
156
+ */
157
+ recordMeshToolCall(opts: {
158
+ meshId: string;
159
+ tool: string;
160
+ sessionId?: string | null;
161
+ windowMs?: number;
162
+ maxCalls?: number;
163
+ }): {
164
+ rateLimitExceeded: boolean;
165
+ callsInWindow: number;
166
+ advisory: string | null;
167
+ };
168
+ /**
169
+ * Prune tool call log entries older than the given age in ms.
170
+ * Exposed for testing.
171
+ */
172
+ pruneToolCallLog(olderThanMs: number): void;
173
+ appendLedgerEntry(entry: {
174
+ id: string;
175
+ meshId: string;
176
+ timestamp: string;
177
+ kind: string;
178
+ nodeId?: string | null;
179
+ sessionId?: string | null;
180
+ providerType?: string | null;
181
+ payload?: unknown;
182
+ }): void;
183
+ readLedgerEntries(meshId: string, opts?: {
184
+ tail?: number;
185
+ since?: string;
186
+ kind?: string;
187
+ limit?: number;
188
+ }): Array<{
189
+ id: string;
190
+ meshId: string;
191
+ timestamp: string;
192
+ kind: string;
193
+ nodeId: string | null;
194
+ sessionId: string | null;
195
+ providerType: string | null;
196
+ payload: unknown;
197
+ }>;
198
+ /**
199
+ * G2 read cutover: read ledger entries in append order (oldest first),
200
+ * matching legacy JSONL file-order semantics. Ties on the same timestamp
201
+ * are broken by rowid (insertion order), preserving the positional
202
+ * guarantee that mesh-events relies on for same-millisecond entries.
203
+ */
204
+ readLedgerEntriesOrdered(meshId: string, opts?: {
205
+ since?: string;
206
+ kinds?: string[];
207
+ tail?: number;
208
+ }): Array<{
209
+ id: string;
210
+ meshId: string;
211
+ timestamp: string;
212
+ kind: string;
213
+ nodeId: string | null;
214
+ sessionId: string | null;
215
+ providerType: string | null;
216
+ payload: unknown;
217
+ }>;
218
+ /** Remove all ledger entries for a mesh (mesh deletion / test cleanup). */
219
+ clearLedgerForMesh(meshId: string): number;
220
+ /** G2: remove entries moved to the JSONL archive so the SQLite runtime set mirrors the active ledger. */
221
+ deleteLedgerEntries(meshId: string, ids: string[]): number;
222
+ hasLedgerEntry(meshId: string, id: string): boolean;
223
+ ledgerEntryCount(meshId: string): number;
224
+ importLedgerEntries(entries: Array<{
225
+ id: string;
226
+ meshId: string;
227
+ timestamp: string;
228
+ kind: string;
229
+ nodeId?: string | null;
230
+ sessionId?: string | null;
231
+ providerType?: string | null;
232
+ payload?: unknown;
233
+ }>): number;
234
+ /**
235
+ * G4: Read a bounded, cursor-addressable ledger slice directly from the SQLite
236
+ * mesh_event_ledger table. This is the P2P reconcile read path; JSONL files are
237
+ * retained as export/import/debug/legacy artifacts only.
238
+ *
239
+ * The return shape is structurally compatible with MeshLedgerSlice so callers
240
+ * in mesh-tools.ts can pass it directly to buildMeshLedgerReplicaEvidence.
241
+ */
242
+ readLedgerSlice(meshId: string, opts?: {
243
+ afterId?: string;
244
+ since?: string;
245
+ kind?: string;
246
+ limit?: number;
247
+ }): {
248
+ protocol: 'adhdev.mesh.ledger.slice.v1';
249
+ meshId: string;
250
+ entries: Array<{
251
+ id: string;
252
+ meshId: string;
253
+ timestamp: string;
254
+ kind: string;
255
+ nodeId: string | null;
256
+ sessionId: string | null;
257
+ providerType: string | null;
258
+ payload: unknown;
259
+ }>;
260
+ cursor: {
261
+ afterId: string | null;
262
+ nextAfterId: string | null;
263
+ limit: number;
264
+ hasMore: boolean;
265
+ };
266
+ sourceOfTruth: {
267
+ kind: 'local_sqlite';
268
+ table: 'mesh_event_ledger';
269
+ bounded: true;
270
+ maxLimit: number;
271
+ };
272
+ };
273
+ insertPendingEvent(event: {
274
+ id: string;
275
+ meshId: string;
276
+ coordinatorDaemonId?: string | null;
277
+ event: string;
278
+ payload?: unknown;
279
+ fingerprint?: string | null;
280
+ queuedAt: number;
281
+ }): boolean;
282
+ drainPendingEvents(meshId: string, coordinatorDaemonId?: string | null): Array<{
283
+ id: string;
284
+ event: string;
285
+ payload: unknown;
286
+ }>;
287
+ /** Non-destructive peek — returns undrained events without marking them drained. */
288
+ peekPendingEvents(meshId: string, coordinatorDaemonId?: string | null): Array<{
289
+ id: string;
290
+ event: string;
291
+ payload: unknown;
292
+ }>;
293
+ hasPendingEventFingerprint(meshId: string, fingerprint: string): boolean;
294
+ upsertMission(mission: {
295
+ id: string;
296
+ meshId: string;
297
+ title: string;
298
+ goal?: string;
299
+ status?: string;
300
+ }): void;
301
+ getMission(meshId: string, missionId: string): {
302
+ id: string;
303
+ meshId: string;
304
+ title: string;
305
+ goal: string;
306
+ status: string;
307
+ createdAt: string;
308
+ updatedAt: string;
309
+ } | null;
310
+ getMissions(meshId: string, statuses?: string[]): Array<{
311
+ id: string;
312
+ meshId: string;
313
+ title: string;
314
+ goal: string;
315
+ status: string;
316
+ createdAt: string;
317
+ updatedAt: string;
318
+ }>;
319
+ /** Remove all missions for a mesh — mesh deletion / test cleanup. */
320
+ clearMissionsForMesh(meshId: string): number;
321
+ /** Remove all pending-event rows (drained included) for a mesh — mesh deletion / test cleanup. */
322
+ clearPendingEventsForMesh(meshId: string): number;
323
+ pendingEventCount(meshId: string): number;
149
324
  }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * M7: Operational stats (time/attempts) derived from existing truth.
3
+ *
4
+ * No cost/token accounting — ADHDev observes PTY/CDP and cannot see API
5
+ * tokens (explicit non-goal). Everything here is derived at query time from
6
+ * the SQLite ledger and queue rows; there is no separate aggregate table.
7
+ * Tasks with missing ledger evidence report incompleteEvidence instead of
8
+ * estimated numbers.
9
+ */
10
+ export interface MeshTaskStats {
11
+ taskId: string;
12
+ status: string;
13
+ dispatchedAt: string | null;
14
+ terminalAt: string | null;
15
+ terminalKind: 'task_completed' | 'task_failed' | null;
16
+ /** dispatched → terminal wall clock; null when evidence is incomplete. */
17
+ durationMs: number | null;
18
+ /** Number of task_dispatched ledger entries observed for this task. */
19
+ dispatchCount: number;
20
+ /** Queue requeueCount (0 when the row is gone or never requeued). */
21
+ requeueCount: number;
22
+ /** True when dispatch or terminal ledger evidence is missing — numbers are withheld, never estimated. */
23
+ incompleteEvidence?: true;
24
+ }
25
+ export interface MeshMissionStats {
26
+ missionId: string;
27
+ taskCount: number;
28
+ completed: number;
29
+ failed: number;
30
+ /** Sum of per-task durations with complete evidence. */
31
+ totalDurationMs: number;
32
+ /** First dispatch → last terminal across the mission's tasks; null without complete endpoints. */
33
+ wallClockMs: number | null;
34
+ /** Total requeue attempts across the mission's tasks. */
35
+ retries: number;
36
+ /** Task ids whose ledger evidence was incomplete (excluded from sums). */
37
+ incompleteTaskIds: string[];
38
+ }
39
+ /**
40
+ * Compute per-task stats from ledger entries. Scans a bounded tail window —
41
+ * stats are an operational view of recent work, not a full historical report.
42
+ */
43
+ export declare function computeMeshTaskStats(meshId: string, opts?: {
44
+ taskIds?: string[];
45
+ missionId?: string;
46
+ tail?: number;
47
+ }): MeshTaskStats[];
48
+ /** Mission rollup — derived from per-task stats, no stored aggregates. */
49
+ export declare function computeMeshMissionStats(meshId: string, missionId: string): MeshMissionStats;
@@ -27,6 +27,20 @@ export interface MeshWorkQueueEntry {
27
27
  targetSessionId?: string;
28
28
  /** If specified, a node must expose all tags before it can claim the task. */
29
29
  requiredTags?: string[];
30
+ /**
31
+ * M1: ids of tasks that must reach 'completed' before this task is claimable.
32
+ * Forward references (ids not yet enqueued) are allowed for batch flows and
33
+ * simply keep the task waiting until the referenced task exists and completes.
34
+ */
35
+ dependsOn?: string[];
36
+ /** M1/M3: mission this task belongs to (joins mesh_missions). */
37
+ missionId?: string;
38
+ /**
39
+ * M1: why this task is held back (e.g. "dependency_failed:<taskId>").
40
+ * Only set by the system on dependency failure under the 'block' policy;
41
+ * waiting-on-dependency state is computed at view time, not stored.
42
+ */
43
+ blockedReason?: string;
30
44
  /** The node that actually claimed and is executing the task */
31
45
  assignedNodeId?: string;
32
46
  /** The session currently executing the task */
@@ -38,6 +52,8 @@ export interface MeshWorkQueueEntry {
38
52
  requeueReason?: string;
39
53
  requeuedAt?: string;
40
54
  requeueCount?: number;
55
+ /** Max automatic requeue attempts. When requeueCount reaches this, task is auto-failed. */
56
+ maxRetries?: number;
41
57
  /** Last automatic queue session spin-up attempt, for mesh_view_queue/debug visibility. */
42
58
  autoLaunch?: {
43
59
  status: 'skipped' | 'started' | 'failed' | 'completed';
@@ -59,8 +75,16 @@ export declare function normalizeMeshCapabilityTags(value: unknown): string[];
59
75
  export declare function buildMeshNodeCapabilityTags(node: {
60
76
  capabilities?: unknown;
61
77
  policy?: unknown;
78
+ isLocalWorktree?: unknown;
79
+ worktreeBranch?: unknown;
62
80
  } | undefined, providerType?: string): string[];
63
81
  export declare function nodeSatisfiesRequiredTags(requiredTags: unknown, capabilityTags: unknown): boolean;
82
+ /**
83
+ * M1: detect dependency cycles before enqueue. Walks the dependency graph of
84
+ * existing queue entries plus the new task's edges. Fail-closed: a cycle
85
+ * rejects the enqueue entirely. Synchronous and bounded by queue size.
86
+ */
87
+ export declare function assertNoDependencyCycle(meshId: string, newTaskId: string, dependsOn: string[]): void;
64
88
  /**
65
89
  * Add a new task to the mesh queue.
66
90
  */
@@ -69,6 +93,12 @@ export declare function enqueueTask(meshId: string, message: string, opts?: {
69
93
  targetSessionId?: string;
70
94
  taskMode?: MeshTaskMode | string;
71
95
  requiredTags?: string[];
96
+ /** M1: tasks that must complete before this one is claimable. */
97
+ dependsOn?: string[];
98
+ /** M1/M3: mission this task belongs to. */
99
+ missionId?: string;
100
+ /** Explicit task id for batch/template flows (M5). Random UUID when omitted. */
101
+ id?: string;
72
102
  } & MeshQueueMutationOptions): MeshWorkQueueEntry;
73
103
  /**
74
104
  * Get all tasks in the queue, optionally filtered by status.
@@ -81,6 +111,7 @@ export declare function getMeshQueueRevision(meshId: string): string;
81
111
  * Find the next pending task that this node is allowed to claim, and mark it as assigned.
82
112
  */
83
113
  export declare function claimNextTask(meshId: string, nodeId: string, sessionId: string, capabilityTags?: string[]): MeshWorkQueueEntry | null;
114
+ export type DependencyFailurePolicy = 'block' | 'cancel';
84
115
  /**
85
116
  * Update the status of a specific task.
86
117
  * Used when a session completes, fails, or stalls.
@@ -97,12 +128,30 @@ export declare function cancelTask(meshId: string, taskId: string, opts?: {
97
128
  * Return a queue task to pending for retry. By default, dead session targeting
98
129
  * and assigned ownership are cleared so stale assignments do not strand again.
99
130
  */
131
+ export type RequeueResult = {
132
+ status: 'requeued';
133
+ entry: MeshWorkQueueEntry;
134
+ } | {
135
+ status: 'failed_max_retries';
136
+ entry: MeshWorkQueueEntry;
137
+ maxRetries: number;
138
+ requeueCount: number;
139
+ } | {
140
+ status: 'not_found';
141
+ };
100
142
  export declare function requeueTask(meshId: string, taskId: string, opts?: {
101
143
  reason?: string;
102
144
  targetNodeId?: string;
103
145
  targetSessionId?: string;
104
146
  clearTargetNode?: boolean;
105
147
  clearTargetSession?: boolean;
148
+ /**
149
+ * Override the retry cap for this call. Use only for explicit operator actions.
150
+ * If true, the task is requeued even when requeueCount >= maxRetries.
151
+ */
152
+ force?: boolean;
153
+ /** Per-task retry cap override. Falls back to mesh policy maxTaskRetries (default 1). */
154
+ maxRetries?: number;
106
155
  } & MeshQueueMutationOptions): MeshWorkQueueEntry | null;
107
156
  /**
108
157
  * Update the status of the task currently assigned to a specific session.
@@ -110,6 +159,20 @@ export declare function requeueTask(meshId: string, taskId: string, opts?: {
110
159
  export declare function updateSessionTaskStatus(meshId: string, sessionId: string, status: MeshTaskStatus, opts?: {
111
160
  occurredAt?: string;
112
161
  }): MeshWorkQueueEntry | null;
162
+ /**
163
+ * M1-3: true when at least one pending task is waiting on the given task.
164
+ * Used by the completion event path to decide whether to wake the queue.
165
+ */
166
+ export declare function hasPendingDependents(meshId: string, taskId: string): boolean;
167
+ /**
168
+ * M1-4: view-time dependency state for a task — unmet dependency ids and
169
+ * whether the task is currently claimable from a dependency standpoint.
170
+ * Not stored (truth stays in task statuses).
171
+ */
172
+ export declare function describeTaskDependencyState(entry: Pick<MeshWorkQueueEntry, 'dependsOn' | 'blockedReason'>, statusById: Map<string, MeshTaskStatus | string>): {
173
+ waitingOn: string[];
174
+ dependenciesSatisfied: boolean;
175
+ };
113
176
  export interface MeshWorkQueueStats {
114
177
  total: number;
115
178
  active: number;
@@ -154,3 +217,22 @@ export declare function getActiveDirectDispatches(meshId: string): DirectDispatc
154
217
  export declare function updateDirectDispatchStatus(meshId: string, sessionId: string, status: 'acked' | 'completed' | 'failed' | 'stale'): void;
155
218
  export declare function cleanupTerminalDirectDispatches(olderThanMs?: number): void;
156
219
  export declare function markStaleDirectDispatches(meshId: string, olderThanMs?: number): void;
220
+ export type MeshToolCallRateResult = {
221
+ rateLimitExceeded: boolean;
222
+ callsInWindow: number;
223
+ advisory: string | null;
224
+ };
225
+ /**
226
+ * Record a coordinator tool call and return a rate-limit advisory when the
227
+ * call rate for that tool exceeds the allowed threshold.
228
+ *
229
+ * Defaults: 10-second sliding window, max 5 calls before advisory is raised.
230
+ * Returns { rateLimitExceeded: false } on any store error so callers are not blocked.
231
+ */
232
+ export declare function recordMeshToolCall(opts: {
233
+ meshId: string;
234
+ tool: string;
235
+ sessionId?: string | null;
236
+ windowMs?: number;
237
+ maxCalls?: number;
238
+ }): MeshToolCallRateResult;
@@ -23,8 +23,18 @@ export interface RepoMeshRefineConfig {
23
23
  validation?: {
24
24
  required?: boolean;
25
25
  /**
26
- * Optional dependency/bootstrap commands that Refinery runs before
27
- * validation commands. Refinery never infers installs on its own.
26
+ * M2-2 (v2): how Refinery sources its bootstrap stage.
27
+ * 'inherit' (default) consume the worktree_bootstrap config/state:
28
+ * skip when the node's bootstrap is 'ready' (staleInputs unchanged),
29
+ * run the worktree_bootstrap definition when stale/never-ran.
30
+ * 'skip' — no bootstrap stage at all (validation commands run as-is).
31
+ */
32
+ bootstrap?: 'inherit' | 'skip';
33
+ /**
34
+ * DEPRECATED (M2-2): define bootstrap once in
35
+ * .adhdev/worktree_bootstrap.json instead. Still honored when no
36
+ * worktree_bootstrap config exists, with a deprecation warning; when
37
+ * both exist the worktree_bootstrap config wins.
28
38
  */
29
39
  bootstrapCommands?: RepoMeshRefineValidationCommandConfig[];
30
40
  commands?: RepoMeshRefineValidationCommandConfig[];
@@ -51,9 +61,13 @@ export interface MeshRefineConfigLoadResult {
51
61
  export interface MeshRefineValidationPlan {
52
62
  source: string;
53
63
  sourceType: MeshRefineConfigLoadResult['sourceType'];
64
+ /** M2-2: how the bootstrap stage is sourced ('inherit' consumes worktree_bootstrap; 'skip' disables it). */
65
+ bootstrapMode: 'inherit' | 'skip';
54
66
  bootstrapCommands: MeshRefineValidationCommandPlan[];
55
67
  commands: MeshRefineValidationCommandPlan[];
56
68
  rejectedCommands: Array<Record<string, unknown>>;
69
+ /** M2-2: deprecation notices (e.g. validation.bootstrapCommands present). */
70
+ deprecationWarnings: string[];
57
71
  suggestions: RepoMeshRefineValidationCommandConfig[];
58
72
  suggestedConfig?: RepoMeshRefineConfig;
59
73
  unavailableReason?: string;
@@ -82,6 +96,11 @@ export declare const MESH_REFINE_CONFIG_SCHEMA: {
82
96
  readonly type: "boolean";
83
97
  readonly default: true;
84
98
  };
99
+ readonly bootstrap: {
100
+ readonly enum: readonly ["inherit", "skip"];
101
+ readonly default: "inherit";
102
+ readonly description: "M2-2 (v2): 'inherit' consumes the worktree_bootstrap config/state (skip when ready, rerun when stale); 'skip' disables the bootstrap stage entirely.";
103
+ };
85
104
  readonly commands: {
86
105
  readonly type: "array";
87
106
  readonly minItems: 1;
@@ -129,6 +148,7 @@ export declare const MESH_REFINE_CONFIG_SCHEMA: {
129
148
  readonly bootstrapCommands: {
130
149
  readonly type: "array";
131
150
  readonly maxItems: 4;
151
+ readonly description: "DEPRECATED: define bootstrap once in .adhdev/worktree_bootstrap.json. Honored only when no worktree_bootstrap config exists (with a deprecation warning); worktree_bootstrap wins when both exist.";
132
152
  readonly items: {
133
153
  readonly type: "object";
134
154
  readonly additionalProperties: false;
@@ -184,6 +204,8 @@ export declare function validateMeshRefineConfig(config: unknown, source?: strin
184
204
  bootstrapCommands: MeshRefineValidationCommandPlan[];
185
205
  commands: MeshRefineValidationCommandPlan[];
186
206
  rejectedCommands: Array<Record<string, unknown>>;
207
+ bootstrapMode: 'inherit' | 'skip';
208
+ deprecationWarnings: string[];
187
209
  };
188
210
  export declare function loadMeshRefineConfig(mesh: any, workspace: string): MeshRefineConfigLoadResult;
189
211
  export declare function suggestMeshRefineConfig(mesh: any, workspace: string): {
@@ -18,6 +18,15 @@ export interface WorktreeBootstrapState extends MeshAsyncJobLifecycle {
18
18
  exitCode?: number | null;
19
19
  commandsRun?: Array<Record<string, unknown>>;
20
20
  staleInputs?: string[];
21
+ /**
22
+ * M2-1: sha256 per staleInputs path recorded when the bootstrap reached
23
+ * 'ready'. evaluateWorktreeBootstrapState compares current file hashes
24
+ * against this to detect staleness (e.g. base merge changed a lockfile).
25
+ * Missing files hash to the literal 'absent'.
26
+ */
27
+ staleInputsDigest?: Record<string, string>;
28
+ /** M2-1: why an evaluated state resolved to stale (digest_mismatch | never_ran). */
29
+ staleReason?: string;
21
30
  }
22
31
  export interface WorktreeBootstrapConfigLoadResult {
23
32
  config?: RepoMeshWorktreeBootstrapConfig;
@@ -110,4 +119,17 @@ export declare function validateMeshWorktreeBootstrapConfig(config: unknown, sou
110
119
  rejectedCommands: Array<Record<string, unknown>>;
111
120
  };
112
121
  export declare function loadMeshWorktreeBootstrapConfig(mesh: any, workspace: string): WorktreeBootstrapConfigLoadResult;
122
+ /** M2-1: hash staleInputs files so 'ready' can be invalidated when they change. */
123
+ export declare function computeStaleInputsDigest(workspace: string, staleInputs: string[] | undefined): Record<string, string>;
124
+ /**
125
+ * M2-1: the official bootstrap state contract. Resolves the effective state
126
+ * for a node workspace from config presence + the persisted last-run state +
127
+ * a staleInputs digest comparison. Read-only — never runs commands.
128
+ *
129
+ * ready — last run succeeded and staleInputs are unchanged
130
+ * stale — never ran, or a staleInputs file changed since 'ready'
131
+ * running/failed — persisted lifecycle state passes through
132
+ * not_configured / disabled / (invalid → failed) — from config resolution
133
+ */
134
+ export declare function evaluateWorktreeBootstrapState(mesh: any, workspace: string, persisted?: WorktreeBootstrapState | null): WorktreeBootstrapState;
113
135
  export declare function runMeshWorktreeBootstrap(mesh: any, workspace: string): Promise<WorktreeBootstrapState>;
@@ -44,6 +44,8 @@ export declare class AcpProviderInstance implements ProviderInstance {
44
44
  private partialBlocks;
45
45
  /** Tool calls collected during current turn */
46
46
  private turnToolCalls;
47
+ /** Guard: prevent concurrent sendPrompt calls from racing on shared state */
48
+ private _sendPromptInFlight;
47
49
  private errorMessage;
48
50
  private errorReason;
49
51
  private stderrBuffer;
@@ -136,8 +136,16 @@ export declare class SpecDriver {
136
136
  private lastModalAt;
137
137
  /** The modal state snapshot held across busy blips. */
138
138
  private lastModalState;
139
+ /** Timestamp of when we last *exited* a modal state (approval/picker → idle
140
+ * or directly via completion_idle_after). Used to suppress completion_idle_after
141
+ * firings that were queued before the modal appeared and expire immediately
142
+ * after the modal is dismissed — without this the agent appears idle even
143
+ * though it is still generating. */
144
+ private lastModalExitAt;
139
145
  private completionIdleFirstSeenAt;
140
146
  private completionIdleKey;
147
+ /** Previous screen lines — passed to evaluate() for `changed` condition detection. */
148
+ private prevScreenLines;
141
149
  /** Timer that re-runs evaluate() once the hold window expires. Needed
142
150
  * because the PTY stops emitting once the agent finishes; without an
143
151
  * explicit wake-up there's nothing to trigger the busy → idle
@@ -45,11 +45,10 @@ export interface SpecEvaluation {
45
45
  trace: TraceEntry[];
46
46
  }
47
47
  export declare function evaluate(spec: CliSpec, screenText: string,
48
- /** Optional cursor position (0-based row and col). When supplied, states
49
- * with cursor_row_min/max or cursor_col_min/max predicates are filtered.
50
- * When omitted, cursor predicates are ignored and evaluation is text-only
51
- * (backward-compatible with all existing specs and call sites). */
48
+ /** Optional cursor position (0-based row and col). */
52
49
  cursor?: {
53
50
  row: number;
54
51
  col: number;
55
- }): SpecEvaluation;
52
+ },
53
+ /** Optional previous screen lines for `changed` condition detection. */
54
+ prevLines?: string[]): SpecEvaluation;
@@ -10,5 +10,6 @@ export interface SpecLoadError {
10
10
  sourcePath: string;
11
11
  }
12
12
  export declare function loadSpec(sourcePath: string): SpecLoadResult | SpecLoadError;
13
+ export declare function migrateV1toV3(raw: any): any;
13
14
  /** Convenience: look up a provider's spec.json next to its provider dir. */
14
15
  export declare function resolveSpecPath(providerDir: string): string;