@adhdev/daemon-core 0.9.82-rc.136 → 0.9.82-rc.137

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 (39) hide show
  1. package/dist/cli-adapters/cli-script-runner.d.ts +45 -0
  2. package/dist/cli-adapters/cli-state-engine.d.ts +154 -0
  3. package/dist/cli-adapters/provider-cli-adapter.d.ts +73 -74
  4. package/dist/cli-adapters/provider-cli-shared.d.ts +4 -0
  5. package/dist/config/chat-history.d.ts +1 -0
  6. package/dist/index.d.ts +3 -3
  7. package/dist/index.js +2591 -1966
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +2594 -1974
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/mesh/beads-db.d.ts +54 -0
  12. package/dist/mesh/mesh-active-work.d.ts +7 -1
  13. package/dist/mesh/mesh-events.d.ts +10 -4
  14. package/dist/mesh/mesh-ledger.d.ts +21 -1
  15. package/dist/mesh/mesh-refine-status.d.ts +2 -3
  16. package/dist/mesh/mesh-work-queue.d.ts +17 -0
  17. package/dist/mesh/worktree-bootstrap-config.d.ts +2 -4
  18. package/dist/repo-mesh-types.d.ts +5 -0
  19. package/package.json +1 -1
  20. package/src/cli-adapters/cli-script-runner.ts +145 -0
  21. package/src/cli-adapters/cli-state-engine.ts +957 -0
  22. package/src/cli-adapters/provider-cli-adapter.d.ts +0 -1
  23. package/src/cli-adapters/provider-cli-adapter.ts +365 -1397
  24. package/src/cli-adapters/provider-cli-shared.ts +4 -0
  25. package/src/commands/chat-commands.ts +17 -1
  26. package/src/commands/router.ts +8 -0
  27. package/src/config/chat-history.ts +7 -3
  28. package/src/git/git-worktree.ts +8 -1
  29. package/src/index.ts +3 -2
  30. package/src/mesh/beads-db.ts +305 -2
  31. package/src/mesh/coordinator-prompt.ts +12 -17
  32. package/src/mesh/mesh-active-work.ts +162 -59
  33. package/src/mesh/mesh-events.ts +198 -53
  34. package/src/mesh/mesh-ledger.ts +321 -105
  35. package/src/mesh/mesh-refine-status.ts +2 -3
  36. package/src/mesh/mesh-work-queue.ts +116 -120
  37. package/src/mesh/worktree-bootstrap-config.ts +17 -4
  38. package/src/providers/provider-schema.ts +2 -0
  39. package/src/repo-mesh-types.ts +10 -0
@@ -2,17 +2,71 @@ import type { MeshTaskStatus, MeshWorkQueueEntry } from './mesh-work-queue.js';
2
2
  export declare class BeadsDB {
3
3
  private static instance;
4
4
  private readonly db;
5
+ private readonly dbPath;
5
6
  private readonly migratedMeshIds;
7
+ private fingerprintSweepCounter;
8
+ private walWriteCounter;
9
+ private static readonly WAL_CHECK_INTERVAL;
10
+ private static readonly WAL_MAX_BYTES;
6
11
  private constructor();
7
12
  static getInstance(): BeadsDB;
8
13
  static resetForTests(): void;
9
14
  close(): void;
10
15
  transaction<T>(fn: () => T): T;
11
16
  private migrate;
17
+ hasCompletionFingerprint(fingerprint: string): boolean;
18
+ recordCompletionFingerprint(fingerprint: string, ttlMs: number): void;
19
+ sweepExpiredFingerprints(): void;
20
+ private maybeCheckpointWal;
12
21
  private ensureLegacyQueueMigrated;
13
22
  getQueueEntries(meshId: string, statuses?: MeshTaskStatus[]): MeshWorkQueueEntry[];
14
23
  getQueueRevision(meshId: string): string;
15
24
  replaceQueue(meshId: string, queue: MeshWorkQueueEntry[]): void;
16
25
  deleteQueue(meshId: string): void;
26
+ insertQueueEntry(entry: MeshWorkQueueEntry): void;
27
+ updateQueueEntry(entry: MeshWorkQueueEntry): void;
28
+ findQueueEntryById(meshId: string, id: string): MeshWorkQueueEntry | null;
29
+ hasActiveAssignment(meshId: string, sessionId: string, nodeId: string): boolean;
30
+ claimNextQueueTask(meshId: string, nodeId: string, sessionId: string): MeshWorkQueueEntry | null;
31
+ getQueueStatsByStatus(meshId: string): {
32
+ status: string;
33
+ count: number;
34
+ }[];
35
+ getActiveAssignmentDetails(meshId: string): Array<{
36
+ id: string;
37
+ nodeId?: string;
38
+ sessionId?: string;
39
+ message: string;
40
+ }>;
41
+ findAssignedBySession(meshId: string, sessionId: string, occurredAtIso?: string): MeshWorkQueueEntry | null;
17
42
  private toRow;
43
+ insertDirectDispatch(entry: {
44
+ taskId: string;
45
+ meshId: string;
46
+ nodeId?: string;
47
+ sessionId?: string;
48
+ providerType?: string;
49
+ message: string;
50
+ taskMode?: string;
51
+ via: string;
52
+ dispatchedToIdleSession?: boolean;
53
+ dispatchedAt: string;
54
+ }): void;
55
+ getActiveDirectDispatches(meshId: string): Array<{
56
+ taskId: string;
57
+ meshId: string;
58
+ nodeId: string | null;
59
+ sessionId: string | null;
60
+ providerType: string | null;
61
+ message: string;
62
+ taskMode: string | null;
63
+ via: string;
64
+ status: string;
65
+ dispatchedToIdleSession: boolean;
66
+ dispatchedAt: string;
67
+ updatedAt: string;
68
+ }>;
69
+ updateDirectDispatchStatus(meshId: string, sessionId: string, status: 'acked' | 'completed' | 'failed' | 'stale'): void;
70
+ cleanupTerminalDirectDispatches(olderThanMs: number): void;
71
+ markStaleDirectDispatches(meshId: string, olderThanMs: number): void;
18
72
  }
@@ -1,5 +1,5 @@
1
1
  import type { MeshLedgerEntry } from './mesh-ledger.js';
2
- import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
2
+ import type { MeshWorkQueueEntry, DirectDispatchRecord } from './mesh-work-queue.js';
3
3
  export type MeshActiveWorkSource = 'queue' | 'direct';
4
4
  export type MeshActiveWorkStatus = 'pending' | 'assigned' | 'generating' | 'idle' | 'failed' | 'awaiting_approval';
5
5
  export interface MeshActiveWorkRecord {
@@ -64,6 +64,12 @@ export interface BuildMeshActiveWorkOptions {
64
64
  meshId: string;
65
65
  queue?: MeshWorkQueueEntry[];
66
66
  ledgerEntries?: MeshLedgerEntry[];
67
+ /**
68
+ * Active direct dispatches from BeadsDB. When provided, these are used instead of
69
+ * scanning ledger entries for direct dispatches — eliminates the O(n_ledger) scan.
70
+ * Falls back to ledger scanning when not provided.
71
+ */
72
+ directDispatches?: DirectDispatchRecord[];
67
73
  nodes?: any[];
68
74
  now?: number;
69
75
  /** Include terminal direct rows (idle/failed) for handoff/recent-work surfaces. Defaults false. */
@@ -8,14 +8,20 @@ export interface PendingMeshCoordinatorEvent {
8
8
  metadataEvent: Record<string, unknown>;
9
9
  coordinatorMessage?: string;
10
10
  queuedAt: number;
11
+ /**
12
+ * When set, this event is intended for a specific coordinator daemon.
13
+ * Coordinators on other daemons should ignore it during drain.
14
+ * Absent on legacy events — treated as broadcast to any coordinator.
15
+ */
16
+ targetCoordinatorDaemonId?: string;
11
17
  }
12
18
  export declare function queuePendingMeshCoordinatorEvent(event: PendingMeshCoordinatorEvent): boolean;
13
19
  /** Drain and return all pending coordinator events for meshId, removing them from disk. */
14
- export declare function drainPendingMeshCoordinatorEvents(meshId?: string): PendingMeshCoordinatorEvent[];
20
+ export declare function drainPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): PendingMeshCoordinatorEvent[];
15
21
  /** Peek at pending coordinator events without draining (non-destructive). */
16
- export declare function getPendingMeshCoordinatorEvents(meshId?: string): readonly PendingMeshCoordinatorEvent[];
17
- /** Explicitly clear all pending coordinator events for a mesh. */
18
- export declare function clearPendingMeshCoordinatorEvents(meshId?: string): void;
22
+ export declare function getPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): readonly PendingMeshCoordinatorEvent[];
23
+ /** Explicitly clear all pending coordinator events for a mesh (and coordinator if scoped). */
24
+ export declare function clearPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): void;
19
25
  export declare function tryAssignQueueTask(components: DaemonComponents, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
20
26
  /**
21
27
  * Triggers a queue check for all nodes in the mesh.
@@ -13,7 +13,7 @@
13
13
  * Safety: mode 0o600, atomic append via appendFileSync
14
14
  */
15
15
  import { EventEmitter } from 'events';
16
- export type MeshLedgerKind = 'task_dispatched' | 'task_completed' | 'task_failed' | 'task_stalled' | 'task_approval_needed' | 'session_launched' | 'session_auto_launch' | 'session_stopped' | 'checkpoint_created' | 'node_cloned' | 'node_joined' | 'node_removed' | 'coordinator_started' | 'recovery_attempted' | 'ledger_replicated' | 'ledger_reconciled' | 'direct_fast_forward';
16
+ export type MeshLedgerKind = 'task_dispatched' | 'task_completed' | 'task_failed' | 'task_stalled' | 'task_approval_needed' | 'p2p_dispatch_failed' | 'session_launched' | 'session_auto_launch' | 'session_stopped' | 'checkpoint_created' | 'node_cloned' | 'node_joined' | 'node_removed' | 'coordinator_started' | 'recovery_attempted' | 'ledger_replicated' | 'ledger_reconciled' | 'direct_fast_forward';
17
17
  export interface MeshLedgerEntry {
18
18
  id: string;
19
19
  meshId: string;
@@ -151,6 +151,26 @@ export interface AppendRemoteLedgerResult {
151
151
  }
152
152
  export declare const MAX_LEDGER_SLICE_LIMIT = 500;
153
153
  export declare function getLedgerDir(): string;
154
+ /**
155
+ * Footer to append to worker task messages so workers output structured results
156
+ * that the daemon parses via extractJsonObjectFromSummary / normalizeMeshWorkerResult.
157
+ *
158
+ * Usage: append buildWorkerTaskFooter() to the task message in mesh_send_task /
159
+ * mesh_enqueue_task. The coordinator prompt rules instruct coordinators to do this.
160
+ */
161
+ export declare function buildWorkerTaskFooter(): string;
162
+ /**
163
+ * Compact the active ledger file for a mesh by moving old terminal entries
164
+ * (task_completed, task_failed, task_stalled, recovery_attempted older than 7 days)
165
+ * to <meshId>.archive.jsonl, keeping the active file lean.
166
+ *
167
+ * Non-terminal entries (dispatch, sessions, node lifecycle) are always retained.
168
+ * Called automatically from appendLedgerEntry when the file exceeds COMPACT_THRESHOLD_BYTES.
169
+ */
170
+ export declare function compactLedger(meshId: string): {
171
+ archivedCount: number;
172
+ retainedCount: number;
173
+ };
154
174
  export declare function normalizeMeshWorkerResult(input?: Record<string, unknown>, source?: MeshWorkerResultArtifact['source']): MeshWorkerResultArtifact;
155
175
  export declare function buildTaskCompletionEvidence(opts: BuildTaskCompletionEvidenceOptions): MeshTaskCompletionEvidence;
156
176
  /**
@@ -1,7 +1,8 @@
1
1
  import type { MeshLedgerEntry } from './mesh-ledger.js';
2
2
  import type { PendingMeshCoordinatorEvent } from './mesh-events.js';
3
+ import type { MeshAsyncJobLifecycle } from '../repo-mesh-types.js';
3
4
  export type MeshAsyncRefineJobStatus = 'accepted' | 'running' | 'completed' | 'failed';
4
- export interface MeshAsyncRefineJobSummary {
5
+ export interface MeshAsyncRefineJobSummary extends MeshAsyncJobLifecycle {
5
6
  jobId: string;
6
7
  interactionId?: string;
7
8
  status: MeshAsyncRefineJobStatus;
@@ -12,8 +13,6 @@ export interface MeshAsyncRefineJobSummary {
12
13
  workspace?: string;
13
14
  branch?: string;
14
15
  into?: string;
15
- startedAt?: string;
16
- completedAt?: string;
17
16
  retryOfJobId?: string;
18
17
  lastEvent?: string;
19
18
  lastLedgerKind?: string;
@@ -1,4 +1,5 @@
1
1
  import type { RepoMeshDaemonRole } from '../repo-mesh-types.js';
2
+ import { BeadsDB } from './beads-db.js';
2
3
  export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
3
4
  export type MeshActiveTaskStatus = Extract<MeshTaskStatus, 'pending' | 'assigned'>;
4
5
  export type MeshHistoricalTaskStatus = Extract<MeshTaskStatus, 'completed' | 'failed' | 'cancelled'>;
@@ -127,3 +128,19 @@ export declare function getMeshQueueStats(meshId: string): MeshWorkQueueStats;
127
128
  export declare function __replaceMeshQueueForTests(meshId: string, queue: MeshWorkQueueEntry[]): void;
128
129
  export declare function __clearMeshQueueForTests(meshId: string): void;
129
130
  export declare function __resetBeadsDBForTests(): void;
131
+ export type DirectDispatchRecord = ReturnType<BeadsDB['getActiveDirectDispatches']>[number];
132
+ export declare function insertDirectDispatch(meshId: string, data: {
133
+ taskId: string;
134
+ nodeId?: string;
135
+ sessionId?: string;
136
+ providerType?: string;
137
+ message: string;
138
+ taskMode?: string;
139
+ via: string;
140
+ dispatchedToIdleSession?: boolean;
141
+ dispatchedAt: string;
142
+ }): void;
143
+ export declare function getActiveDirectDispatches(meshId: string): DirectDispatchRecord[];
144
+ export declare function updateDirectDispatchStatus(meshId: string, sessionId: string, status: 'acked' | 'completed' | 'failed' | 'stale'): void;
145
+ export declare function cleanupTerminalDirectDispatches(olderThanMs?: number): void;
146
+ export declare function markStaleDirectDispatches(meshId: string, olderThanMs?: number): void;
@@ -1,4 +1,5 @@
1
1
  import { type MeshRefineValidationCommandPlan, type RepoMeshRefineValidationCommandConfig } from './refine-config.js';
2
+ import type { MeshAsyncJobLifecycle } from '../repo-mesh-types.js';
2
3
  export type WorktreeBootstrapStatus = 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
3
4
  export interface RepoMeshWorktreeBootstrapConfig {
4
5
  version: 1;
@@ -8,16 +9,13 @@ export interface RepoMeshWorktreeBootstrapConfig {
8
9
  commands?: RepoMeshRefineValidationCommandConfig[];
9
10
  staleInputs?: string[];
10
11
  }
11
- export interface WorktreeBootstrapState {
12
+ export interface WorktreeBootstrapState extends MeshAsyncJobLifecycle {
12
13
  status: WorktreeBootstrapStatus;
13
14
  required: boolean;
14
15
  configSource?: string;
15
16
  configSourceType?: 'repo_file' | 'mesh_policy' | 'unavailable' | 'invalid';
16
- startedAt?: string;
17
- completedAt?: string;
18
17
  lastCommand?: string;
19
18
  exitCode?: number | null;
20
- error?: string;
21
19
  commandsRun?: Array<Record<string, unknown>>;
22
20
  staleInputs?: string[];
23
21
  }
@@ -408,3 +408,8 @@ export interface RepoMeshLedgerStatus {
408
408
  entries: RepoMeshLedgerEntryStatus[];
409
409
  summary: RepoMeshLedgerSummaryStatus;
410
410
  }
411
+ export interface MeshAsyncJobLifecycle {
412
+ startedAt?: string;
413
+ completedAt?: string;
414
+ error?: string;
415
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.136",
3
+ "version": "0.9.82-rc.137",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,145 @@
1
+ /**
2
+ * CliScriptRunner — isolated execution of provider CLI scripts
3
+ *
4
+ * Responsible solely for invoking provider-supplied JavaScript functions
5
+ * (detectStatus, parseApproval, parseSession, etc.) and managing the
6
+ * per-session script state created by createState().
7
+ *
8
+ * The runner is stateless with respect to PTY / buffer content — all
9
+ * input data is passed explicitly by the caller so that the adapter can
10
+ * remain a pure transport layer without embedding parsing logic.
11
+ */
12
+
13
+ import { LOG } from '../logging/logger.js';
14
+ import {
15
+ listCliScriptNames,
16
+ type CliApprovalInput,
17
+ type CliScripts,
18
+ type CliScriptInput,
19
+ type CliScreenSnapshot,
20
+ type CliStatusInput,
21
+ type ParsedSession,
22
+ } from './provider-cli-shared.js';
23
+
24
+ export class CliScriptRunner {
25
+ private scripts: CliScripts = {};
26
+ private scriptState: unknown = null;
27
+ private _parseErrorMessage: string | null = null;
28
+ private readonly cliType: string;
29
+
30
+ constructor(cliType: string) {
31
+ this.cliType = cliType;
32
+ }
33
+
34
+ // ─── Script lifecycle ─────────────────────────────
35
+
36
+ setScripts(scripts: CliScripts): void {
37
+ this.scripts = scripts;
38
+ this._parseErrorMessage = null;
39
+ this.scriptState = typeof scripts.createState === 'function'
40
+ ? (scripts.createState() ?? null)
41
+ : null;
42
+ }
43
+
44
+ /** Reset per-session state — called when the PTY process exits. */
45
+ resetSessionState(): void {
46
+ this.scriptState = null;
47
+ }
48
+
49
+ // ─── Script access (for reflection and test patching) ────────────────────
50
+
51
+ /** Returns the live scripts object. Direct property assignment on this object
52
+ * patches individual scripts without replacing others (used in tests). */
53
+ get cliScripts(): CliScripts { return this.scripts; }
54
+
55
+ // ─── Capability checks ────────────────────────────
56
+
57
+ hasDetectStatus(): boolean {
58
+ return typeof this.scripts.detectStatus === 'function';
59
+ }
60
+
61
+ hasParseSession(): boolean {
62
+ return typeof this.scripts.parseSession === 'function';
63
+ }
64
+
65
+ getScriptNames(): string[] {
66
+ return listCliScriptNames(this.scripts);
67
+ }
68
+
69
+ // ─── Error state ──────────────────────────────────
70
+
71
+ get parseErrorMessage(): string | null {
72
+ return this._parseErrorMessage;
73
+ }
74
+
75
+ clearParseError(): void {
76
+ this._parseErrorMessage = null;
77
+ }
78
+
79
+ // ─── Script invocation ────────────────────────────
80
+
81
+ detectStatus(input: CliStatusInput): string | null {
82
+ if (!this.scripts.detectStatus) return null;
83
+ try {
84
+ return this.invoke<string | null>(this.scripts.detectStatus, input);
85
+ } catch (e: any) {
86
+ LOG.warn('CLI', `[${this.cliType}] detectStatus error: ${e?.message || e}`);
87
+ return null;
88
+ }
89
+ }
90
+
91
+ parseApproval(
92
+ input: CliApprovalInput,
93
+ ): { message: string; buttons: string[] } | null {
94
+ if (!this.scripts.parseApproval) return null;
95
+ try {
96
+ return this.invoke<{ message: string; buttons: string[] } | null>(
97
+ this.scripts.parseApproval,
98
+ input,
99
+ );
100
+ } catch (e: any) {
101
+ LOG.warn('CLI', `[${this.cliType}] parseApproval error: ${e?.message || e}`);
102
+ return null;
103
+ }
104
+ }
105
+
106
+ parseSession(
107
+ input: CliScriptInput & { tail?: string; tailScreen?: CliScreenSnapshot },
108
+ ): ParsedSession | null {
109
+ if (!this.scripts.parseSession) {
110
+ this._parseErrorMessage = `${this.cliType} parseSession unavailable`;
111
+ return null;
112
+ }
113
+ try {
114
+ const result = this.invoke<ParsedSession | null>(this.scripts.parseSession, input);
115
+ this._parseErrorMessage = null;
116
+ return result && typeof result === 'object' ? result : null;
117
+ } catch (e: any) {
118
+ this._parseErrorMessage = e?.message || String(e);
119
+ LOG.warn('CLI', `[${this.cliType}] parseSession error: ${this._parseErrorMessage}`);
120
+ return null;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Invoke an arbitrary named script (e.g. setModel, openModelPicker).
126
+ * Throws if the script is not available.
127
+ */
128
+ invokeByName(name: string, input: any): any {
129
+ const fn = this.scripts[name];
130
+ if (typeof fn !== 'function') {
131
+ throw new Error(`CLI script '${name}' not available`);
132
+ }
133
+ return this.invoke(fn, input);
134
+ }
135
+
136
+ // ─── Internal ─────────────────────────────────────
137
+
138
+ private invoke<T>(fn: Function, input: any): T {
139
+ const hasStateFactory = typeof this.scripts.createState === 'function';
140
+ const expectsState = hasStateFactory || this.scriptState !== null || fn.length >= 2;
141
+ return expectsState
142
+ ? (fn as (state: unknown, input: any) => T)(this.scriptState, input)
143
+ : (fn as (input: any) => T)(input);
144
+ }
145
+ }