aiden-runtime 4.1.5 → 4.6.0

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 (181) hide show
  1. package/README.md +265 -847
  2. package/dist/api/server.js +32 -5
  3. package/dist/cli/v4/aidenCLI.js +536 -152
  4. package/dist/cli/v4/callbacks.js +170 -0
  5. package/dist/cli/v4/chatSession.js +245 -3
  6. package/dist/cli/v4/commands/_runtimeToggleHelpers.js +94 -0
  7. package/dist/cli/v4/commands/browserDepth.js +45 -0
  8. package/dist/cli/v4/commands/cron.js +264 -0
  9. package/dist/cli/v4/commands/daemon.js +541 -0
  10. package/dist/cli/v4/commands/daemonStatus.js +253 -0
  11. package/dist/cli/v4/commands/fanout.js +42 -59
  12. package/dist/cli/v4/commands/help.js +13 -0
  13. package/dist/cli/v4/commands/index.js +35 -1
  14. package/dist/cli/v4/commands/mcp.js +80 -54
  15. package/dist/cli/v4/commands/plannerGuard.js +53 -0
  16. package/dist/cli/v4/commands/recovery.js +122 -0
  17. package/dist/cli/v4/commands/runs.js +223 -0
  18. package/dist/cli/v4/commands/sandbox.js +48 -0
  19. package/dist/cli/v4/commands/spawnPause.js +93 -0
  20. package/dist/cli/v4/commands/suggestions.js +68 -0
  21. package/dist/cli/v4/commands/tce.js +41 -0
  22. package/dist/cli/v4/commands/trigger.js +378 -0
  23. package/dist/cli/v4/commands/update.js +95 -3
  24. package/dist/cli/v4/daemonAgentBuilder.js +145 -0
  25. package/dist/cli/v4/defaultSoul.js +1 -1
  26. package/dist/cli/v4/display/capabilityCard.js +26 -0
  27. package/dist/cli/v4/display.js +18 -8
  28. package/dist/cli/v4/replyRenderer.js +31 -23
  29. package/dist/cli/v4/updateBootPrompt.js +170 -0
  30. package/dist/core/playwrightBridge.js +129 -0
  31. package/dist/core/v4/aidenAgent.js +527 -5
  32. package/dist/core/v4/browserState.js +436 -0
  33. package/dist/core/v4/checkpoint.js +79 -0
  34. package/dist/core/v4/daemon/bootstrap.js +651 -0
  35. package/dist/core/v4/daemon/cleanShutdown.js +154 -0
  36. package/dist/core/v4/daemon/cron/cronBridge.js +126 -0
  37. package/dist/core/v4/daemon/cron/cronEmitter.js +173 -0
  38. package/dist/core/v4/daemon/cron/migration.js +199 -0
  39. package/dist/core/v4/daemon/cron/misfirePolicy.js +115 -0
  40. package/dist/core/v4/daemon/daemonConfig.js +90 -0
  41. package/dist/core/v4/daemon/db/connection.js +106 -0
  42. package/dist/core/v4/daemon/db/migrations.js +362 -0
  43. package/dist/core/v4/daemon/db/schema/v1.spec.js +18 -0
  44. package/dist/core/v4/daemon/dispatcher/agentRunner.js +98 -0
  45. package/dist/core/v4/daemon/dispatcher/budgetGate.js +127 -0
  46. package/dist/core/v4/daemon/dispatcher/daemonApproval.js +113 -0
  47. package/dist/core/v4/daemon/dispatcher/dailyBudgetTracker.js +120 -0
  48. package/dist/core/v4/daemon/dispatcher/dispatcher.js +389 -0
  49. package/dist/core/v4/daemon/dispatcher/fireRateLimiter.js +113 -0
  50. package/dist/core/v4/daemon/dispatcher/index.js +53 -0
  51. package/dist/core/v4/daemon/dispatcher/promptTemplate.js +95 -0
  52. package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +356 -0
  53. package/dist/core/v4/daemon/dispatcher/resolveModel.js +93 -0
  54. package/dist/core/v4/daemon/dispatcher/sessionId.js +93 -0
  55. package/dist/core/v4/daemon/drain.js +156 -0
  56. package/dist/core/v4/daemon/eventLoopLag.js +73 -0
  57. package/dist/core/v4/daemon/health.js +159 -0
  58. package/dist/core/v4/daemon/idempotencyStore.js +204 -0
  59. package/dist/core/v4/daemon/index.js +179 -0
  60. package/dist/core/v4/daemon/instanceTracker.js +99 -0
  61. package/dist/core/v4/daemon/resourceRegistry.js +150 -0
  62. package/dist/core/v4/daemon/restartCode.js +32 -0
  63. package/dist/core/v4/daemon/restartFailureCounter.js +77 -0
  64. package/dist/core/v4/daemon/runStore.js +144 -0
  65. package/dist/core/v4/daemon/runtimeLock.js +167 -0
  66. package/dist/core/v4/daemon/signals.js +50 -0
  67. package/dist/core/v4/daemon/supervisor.js +272 -0
  68. package/dist/core/v4/daemon/triggerBus.js +279 -0
  69. package/dist/core/v4/daemon/triggers/email/allowlist.js +70 -0
  70. package/dist/core/v4/daemon/triggers/email/automatedSender.js +78 -0
  71. package/dist/core/v4/daemon/triggers/email/bodyExtractor.js +0 -0
  72. package/dist/core/v4/daemon/triggers/email/emailSeenStore.js +99 -0
  73. package/dist/core/v4/daemon/triggers/email/emailSpec.js +107 -0
  74. package/dist/core/v4/daemon/triggers/email/imapConnection.js +211 -0
  75. package/dist/core/v4/daemon/triggers/email/index.js +332 -0
  76. package/dist/core/v4/daemon/triggers/email/seenUids.js +60 -0
  77. package/dist/core/v4/daemon/triggers/fileObservationsStore.js +93 -0
  78. package/dist/core/v4/daemon/triggers/fileWatcher.js +253 -0
  79. package/dist/core/v4/daemon/triggers/fileWatcherSpec.js +88 -0
  80. package/dist/core/v4/daemon/triggers/fsIdentity.js +42 -0
  81. package/dist/core/v4/daemon/triggers/globMatcher.js +100 -0
  82. package/dist/core/v4/daemon/triggers/reconcile.js +206 -0
  83. package/dist/core/v4/daemon/triggers/settleStat.js +81 -0
  84. package/dist/core/v4/daemon/triggers/webhook.js +376 -0
  85. package/dist/core/v4/daemon/triggers/webhookDeliveriesStore.js +109 -0
  86. package/dist/core/v4/daemon/triggers/webhookIdempotency.js +72 -0
  87. package/dist/core/v4/daemon/triggers/webhookRateLimit.js +56 -0
  88. package/dist/core/v4/daemon/triggers/webhookSpec.js +76 -0
  89. package/dist/core/v4/daemon/triggers/webhookVerifier.js +128 -0
  90. package/dist/core/v4/daemon/types.js +15 -0
  91. package/dist/core/v4/dockerSession.js +461 -0
  92. package/dist/core/v4/dryRun.js +117 -0
  93. package/dist/core/v4/failureClassifier.js +779 -0
  94. package/dist/core/v4/providerFallback.js +35 -2
  95. package/dist/core/v4/recoveryReport.js +449 -0
  96. package/dist/core/v4/runtimeToggles.js +214 -0
  97. package/dist/core/v4/sandboxConfig.js +285 -0
  98. package/dist/core/v4/sandboxFs.js +316 -0
  99. package/dist/core/v4/selfimprovement/recoveryStore.js +307 -0
  100. package/dist/core/v4/selfimprovement/signatureBuilder.js +158 -0
  101. package/dist/core/v4/subagent/childBuilder.js +391 -0
  102. package/dist/core/v4/subagent/fanout.js +75 -51
  103. package/dist/core/v4/subagent/spawnPause.js +191 -0
  104. package/dist/core/v4/subagent/spawnSubAgent.js +310 -0
  105. package/dist/core/v4/suggestionCatalog.js +41 -0
  106. package/dist/core/v4/suggestionEngine.js +210 -0
  107. package/dist/core/v4/toolRegistry.js +37 -3
  108. package/dist/core/v4/turnState.js +587 -0
  109. package/dist/core/v4/update/checkUpdate.js +63 -3
  110. package/dist/core/v4/update/installMethodDetect.js +115 -0
  111. package/dist/core/v4/update/registryClient.js +121 -0
  112. package/dist/core/v4/update/skipState.js +75 -0
  113. package/dist/core/v4/verifier.js +448 -0
  114. package/dist/core/version.js +1 -1
  115. package/dist/moat/plannerGuard.js +29 -0
  116. package/dist/providers/v4/anthropicAdapter.js +31 -3
  117. package/dist/providers/v4/chatCompletionsAdapter.js +26 -3
  118. package/dist/providers/v4/codexResponsesAdapter.js +25 -2
  119. package/dist/providers/v4/ollamaPromptToolsAdapter.js +57 -2
  120. package/dist/tools/v4/browser/_observer.js +224 -0
  121. package/dist/tools/v4/browser/browserBlocker.js +396 -0
  122. package/dist/tools/v4/browser/browserClick.js +18 -1
  123. package/dist/tools/v4/browser/browserClose.js +18 -1
  124. package/dist/tools/v4/browser/browserExtract.js +5 -1
  125. package/dist/tools/v4/browser/browserFill.js +17 -1
  126. package/dist/tools/v4/browser/browserGetUrl.js +5 -1
  127. package/dist/tools/v4/browser/browserNavigate.js +16 -1
  128. package/dist/tools/v4/browser/browserScreenshot.js +5 -1
  129. package/dist/tools/v4/browser/browserScroll.js +18 -1
  130. package/dist/tools/v4/browser/browserType.js +17 -1
  131. package/dist/tools/v4/browser/captchaCheck.js +5 -1
  132. package/dist/tools/v4/executeCode.js +1 -0
  133. package/dist/tools/v4/files/fileCopy.js +56 -2
  134. package/dist/tools/v4/files/fileDelete.js +38 -1
  135. package/dist/tools/v4/files/fileList.js +12 -1
  136. package/dist/tools/v4/files/fileMove.js +59 -2
  137. package/dist/tools/v4/files/filePatch.js +43 -1
  138. package/dist/tools/v4/files/fileRead.js +12 -1
  139. package/dist/tools/v4/files/fileWrite.js +41 -1
  140. package/dist/tools/v4/index.js +88 -61
  141. package/dist/tools/v4/memory/memoryAdd.js +14 -0
  142. package/dist/tools/v4/memory/memoryRemove.js +14 -0
  143. package/dist/tools/v4/memory/memoryReplace.js +15 -0
  144. package/dist/tools/v4/memory/sessionSummary.js +12 -0
  145. package/dist/tools/v4/process/processKill.js +19 -0
  146. package/dist/tools/v4/process/processList.js +1 -0
  147. package/dist/tools/v4/process/processLogRead.js +1 -0
  148. package/dist/tools/v4/process/processSpawn.js +13 -0
  149. package/dist/tools/v4/process/processWait.js +1 -0
  150. package/dist/tools/v4/sessions/recallSession.js +1 -0
  151. package/dist/tools/v4/sessions/sessionList.js +1 -0
  152. package/dist/tools/v4/sessions/sessionSearch.js +1 -0
  153. package/dist/tools/v4/skills/lookupToolSchema.js +7 -0
  154. package/dist/tools/v4/skills/skillManage.js +13 -0
  155. package/dist/tools/v4/skills/skillView.js +1 -0
  156. package/dist/tools/v4/skills/skillsList.js +1 -0
  157. package/dist/tools/v4/subagent/spawnSubAgentTool.js +334 -0
  158. package/dist/tools/v4/subagent/subagentFanout.js +54 -1
  159. package/dist/tools/v4/system/aidenSelfUpdate.js +16 -0
  160. package/dist/tools/v4/system/appClose.js +13 -0
  161. package/dist/tools/v4/system/appInput.js +13 -0
  162. package/dist/tools/v4/system/appLaunch.js +13 -0
  163. package/dist/tools/v4/system/clipboardRead.js +1 -0
  164. package/dist/tools/v4/system/clipboardWrite.js +14 -0
  165. package/dist/tools/v4/system/mediaKey.js +12 -0
  166. package/dist/tools/v4/system/mediaSessions.js +1 -0
  167. package/dist/tools/v4/system/mediaTransport.js +13 -0
  168. package/dist/tools/v4/system/naturalEvents.js +1 -0
  169. package/dist/tools/v4/system/nowPlaying.js +1 -0
  170. package/dist/tools/v4/system/osProcessList.js +1 -0
  171. package/dist/tools/v4/system/screenshot.js +1 -0
  172. package/dist/tools/v4/system/systemInfo.js +1 -0
  173. package/dist/tools/v4/system/volumeSet.js +17 -0
  174. package/dist/tools/v4/terminal/shellExec.js +81 -9
  175. package/dist/tools/v4/web/deepResearch.js +1 -0
  176. package/dist/tools/v4/web/openUrl.js +1 -0
  177. package/dist/tools/v4/web/webFetch.js +1 -0
  178. package/dist/tools/v4/web/webPage.js +1 -0
  179. package/dist/tools/v4/web/webSearch.js +1 -0
  180. package/dist/tools/v4/web/youtubeSearch.js +1 -0
  181. package/package.json +13 -3
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Shiva Deore (Taracod).
4
+ * Licensed under AGPL-3.0. See LICENSE for details.
5
+ *
6
+ * Aiden — local-first agent.
7
+ */
8
+ /**
9
+ * cli/v4/commands/daemonStatus.ts — v4.5 Phase 8a.
10
+ *
11
+ * `/daemon status` — read-only slash variant of `aiden daemon
12
+ * status` (the top-level Phase 4b CLI surface). Renders an inline
13
+ * summary inside the REPL without leaving the chat. Points users
14
+ * at `aiden daemon install|start|stop` for any mutation.
15
+ *
16
+ * Q-P8a-3(a) inline format — five lines max when running, two
17
+ * lines when disabled. Reads daemon.db directly so the slash
18
+ * command works regardless of whether the daemon is the same
19
+ * process as the REPL.
20
+ */
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.daemonStatus = void 0;
26
+ const node_fs_1 = __importDefault(require("node:fs"));
27
+ const node_os_1 = __importDefault(require("node:os"));
28
+ const node_path_1 = __importDefault(require("node:path"));
29
+ const daemon_1 = require("../../../core/v4/daemon");
30
+ const paths_1 = require("../../../core/v4/paths");
31
+ exports.daemonStatus = {
32
+ name: 'daemon',
33
+ description: 'Show v4.5 daemon status (read-only). Use `aiden daemon` for lifecycle.',
34
+ category: 'system',
35
+ icon: '⚙',
36
+ handler: async (ctx) => {
37
+ const sub = (ctx.args[0] ?? 'status').toLowerCase();
38
+ if (sub !== 'status') {
39
+ ctx.display.printError('Usage: /daemon status\n' +
40
+ 'For lifecycle commands (install / start / stop / restart / logs), use the top-level CLI:\n' +
41
+ ' aiden daemon install\n' +
42
+ ' aiden daemon start\n' +
43
+ ' aiden daemon stop\n' +
44
+ ' aiden daemon status\n' +
45
+ ' aiden daemon logs');
46
+ return {};
47
+ }
48
+ try {
49
+ const snapshot = readSnapshot();
50
+ printSnapshot(snapshot, ctx);
51
+ }
52
+ catch (e) {
53
+ ctx.display.warn(`/daemon status: failed to read state (${e instanceof Error ? e.message : String(e)})`);
54
+ }
55
+ return {};
56
+ },
57
+ };
58
+ // ── Snapshot collector ─────────────────────────────────────────────────────
59
+ function readSnapshot() {
60
+ const aidenRoot = (0, paths_1.resolveAidenRoot)();
61
+ const dbPath = (0, daemon_1.daemonDbPath)(aidenRoot);
62
+ const lockPath = (0, daemon_1.daemonRuntimeLockPath)(aidenRoot);
63
+ // ── Liveness via the in-process bootstrap handle first, then fall
64
+ // back to the runtime.lock PID check (covers the case where the
65
+ // daemon is another process and we're a REPL inspecting its db).
66
+ let running = false;
67
+ let instanceId = null;
68
+ let port = null;
69
+ let uptimeMs = null;
70
+ const handle = (0, daemon_1.getDaemonHandle)();
71
+ if (handle?.active && handle.instanceId) {
72
+ running = true;
73
+ instanceId = handle.instanceId;
74
+ port = (0, daemon_1.getDaemonConfig)().port;
75
+ if (handle.instanceTracker) {
76
+ // instanceTracker has a `getStartedAt` if exposed; otherwise
77
+ // derive from daemon_instances row below.
78
+ }
79
+ }
80
+ if (!running && node_fs_1.default.existsSync(lockPath)) {
81
+ try {
82
+ const lines = node_fs_1.default.readFileSync(lockPath, 'utf-8').split(/\r?\n/);
83
+ // runtime.lock format: [0]=instanceId, [1]=pid, [2]=port (Phase 1).
84
+ const lockedInstance = lines[0] ?? '';
85
+ const lockedPid = Number.parseInt(lines[1] ?? '', 10);
86
+ const lockedPort = Number.parseInt(lines[2] ?? '', 10);
87
+ if (Number.isFinite(lockedPid) && pidAlive(lockedPid)) {
88
+ running = true;
89
+ instanceId = lockedInstance || null;
90
+ if (Number.isFinite(lockedPort))
91
+ port = lockedPort;
92
+ }
93
+ }
94
+ catch { /* stale or unreadable */ }
95
+ }
96
+ if (!node_fs_1.default.existsSync(dbPath)) {
97
+ return {
98
+ running: false,
99
+ port: null,
100
+ instanceId: null,
101
+ uptimeMs: null,
102
+ triggerCounts: { file: 0, webhook: 0, email: 0, schedule: 0, manual: 0 },
103
+ recentRuns: [],
104
+ bus: { pending: 0, claimed: 0, deadLetter: 0 },
105
+ dailyBudget: null,
106
+ };
107
+ }
108
+ const db = (0, daemon_1.openDaemonDb)(dbPath);
109
+ // Uptime from the instance row when we have an instanceId.
110
+ if (running && instanceId) {
111
+ try {
112
+ const row = db
113
+ .prepare('SELECT started_at, last_heartbeat FROM daemon_instances WHERE instance_id = ?')
114
+ .get(instanceId);
115
+ if (row)
116
+ uptimeMs = Date.now() - row.started_at;
117
+ }
118
+ catch { /* noop */ }
119
+ }
120
+ // Trigger counts by source.
121
+ const triggerCounts = { file: 0, webhook: 0, email: 0, schedule: 0, manual: 0 };
122
+ try {
123
+ const rows = db.prepare(`SELECT source, COUNT(*) AS c FROM triggers WHERE enabled = 1 GROUP BY source`).all();
124
+ for (const r of rows) {
125
+ if (r.source in triggerCounts) {
126
+ triggerCounts[r.source] = r.c;
127
+ }
128
+ }
129
+ }
130
+ catch { /* triggers table missing — schema v1 not applied */ }
131
+ // Recent runs (last 3).
132
+ const recentRuns = (() => {
133
+ try {
134
+ const rows = db.prepare(`SELECT id, status, finish_reason, started_at, completed_at FROM runs
135
+ ORDER BY id DESC LIMIT 3`).all();
136
+ return rows.map((r) => ({
137
+ id: r.id,
138
+ status: r.status,
139
+ finishReason: r.finish_reason,
140
+ durationMs: r.completed_at !== null ? r.completed_at - r.started_at : null,
141
+ }));
142
+ }
143
+ catch {
144
+ return [];
145
+ }
146
+ })();
147
+ // Bus stats.
148
+ const bus = (() => {
149
+ try {
150
+ const rows = db.prepare(`SELECT status, COUNT(*) AS c FROM trigger_events GROUP BY status`).all();
151
+ const m = {};
152
+ for (const r of rows)
153
+ m[r.status] = r.c;
154
+ return {
155
+ pending: m.pending ?? 0,
156
+ claimed: m.claimed ?? 0,
157
+ deadLetter: m.dead_letter ?? 0,
158
+ };
159
+ }
160
+ catch {
161
+ return { pending: 0, claimed: 0, deadLetter: 0 };
162
+ }
163
+ })();
164
+ // Daily budget (piggybacked on idempotency_keys per Phase 7).
165
+ const dailyBudget = (() => {
166
+ const budgetRaw = process.env.AIDEN_DAEMON_DAILY_BUDGET;
167
+ if (!budgetRaw)
168
+ return null;
169
+ const budget = Number.parseInt(budgetRaw, 10);
170
+ if (!Number.isFinite(budget) || budget <= 0)
171
+ return null;
172
+ try {
173
+ const today = new Date().toISOString().slice(0, 10);
174
+ const row = db.prepare(`SELECT response_json FROM idempotency_keys WHERE scope = ? AND key = ?`).get('daemon_budget', today);
175
+ let used = 0;
176
+ if (row) {
177
+ try {
178
+ const parsed = JSON.parse(row.response_json);
179
+ used = typeof parsed.used === 'number' ? parsed.used : 0;
180
+ }
181
+ catch { /* noop */ }
182
+ }
183
+ return { used, budget, exhausted: used >= budget };
184
+ }
185
+ catch {
186
+ return { used: 0, budget, exhausted: false };
187
+ }
188
+ })();
189
+ return {
190
+ running,
191
+ port,
192
+ instanceId,
193
+ uptimeMs,
194
+ triggerCounts,
195
+ recentRuns,
196
+ bus,
197
+ dailyBudget,
198
+ };
199
+ }
200
+ // ── Renderer ───────────────────────────────────────────────────────────────
201
+ function printSnapshot(s, ctx) {
202
+ if (!s.running) {
203
+ ctx.display.write('Daemon: disabled.\n');
204
+ ctx.display.write('To enable: `aiden daemon install` (systemd / launchd) or `AIDEN_DAEMON=1` before `aiden`.\n');
205
+ return;
206
+ }
207
+ const uptime = s.uptimeMs !== null ? formatUptime(s.uptimeMs) : 'unknown';
208
+ const instanceShort = s.instanceId ? s.instanceId.slice(0, 8) : '?';
209
+ ctx.display.write(`Daemon: running (port ${s.port ?? '?'}, instance ${instanceShort}, uptime ${uptime})\n`);
210
+ const tc = s.triggerCounts;
211
+ ctx.display.write(`Triggers: ${tc.file} file · ${tc.webhook} webhook · ${tc.email} email · ${tc.schedule} schedule\n`);
212
+ if (s.recentRuns.length === 0) {
213
+ ctx.display.write('Recent runs (last 3): (none)\n');
214
+ }
215
+ else {
216
+ const parts = s.recentRuns.map((r) => {
217
+ const dur = r.durationMs !== null ? `(${(r.durationMs / 1000).toFixed(1)}s)` : '';
218
+ return `${r.status}${dur ? ' ' + dur : ''}`;
219
+ });
220
+ ctx.display.write(`Recent runs (last ${s.recentRuns.length}): ${parts.join(' · ')}\n`);
221
+ }
222
+ ctx.display.write(`Bus: ${s.bus.pending} pending · ${s.bus.claimed} claimed · ${s.bus.deadLetter} dead-letter\n`);
223
+ if (s.dailyBudget) {
224
+ const d = s.dailyBudget;
225
+ const exhaustedTag = d.exhausted ? ' EXHAUSTED' : '';
226
+ ctx.display.write(`Daily budget: ${d.used} / ${d.budget} tokens used (UTC ${new Date().toISOString().slice(0, 10)})${exhaustedTag}\n`);
227
+ }
228
+ }
229
+ function formatUptime(ms) {
230
+ const s = Math.floor(ms / 1000);
231
+ const days = Math.floor(s / 86400);
232
+ const hours = Math.floor((s % 86400) / 3600);
233
+ const mins = Math.floor((s % 3600) / 60);
234
+ if (days > 0)
235
+ return `${days}d ${hours}h`;
236
+ if (hours > 0)
237
+ return `${hours}h ${mins}m`;
238
+ return `${mins}m ${s % 60}s`;
239
+ }
240
+ function pidAlive(pid) {
241
+ try {
242
+ process.kill(pid, 0);
243
+ return true;
244
+ }
245
+ catch (e) {
246
+ // ESRCH = no such process; EPERM = process exists but we lack
247
+ // permission to signal it (still counts as alive).
248
+ return e.code === 'EPERM';
249
+ }
250
+ }
251
+ // Keep unused-import linter happy.
252
+ void node_os_1.default;
253
+ void node_path_1.default;
@@ -26,8 +26,6 @@
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  exports.parseFanoutArgs = parseFanoutArgs;
28
28
  exports.runFanoutCli = runFanoutCli;
29
- /* eslint-disable @typescript-eslint/no-explicit-any */
30
- const fanout_1 = require("../../../core/v4/subagent/fanout");
31
29
  function parseFanoutArgs(argv) {
32
30
  const args = { positional: [] };
33
31
  let i = 0;
@@ -172,65 +170,50 @@ async function runFanoutCli(argv, opts = {}) {
172
170
  return 1;
173
171
  }
174
172
  }
175
- // ── Dry-run path: stub providers, stub child runner, stub aggregator
176
- // exercises the Promise.all + abort + merge plumbing against the
177
- // built artifact without touching network or provider keys.
178
- const providers = [
179
- { providerId: 'stub-a', modelId: 'fake-model' },
180
- { providerId: 'stub-b', modelId: 'fake-model' },
181
- ];
182
- const stubAdapter = {
183
- apiMode: 'chat_completions',
184
- async call() {
185
- return {
186
- content: '[dry-run aggregator]',
187
- toolCalls: [],
188
- finishReason: 'stop',
189
- usage: { inputTokens: 0, outputTokens: 0 },
190
- };
191
- },
192
- };
193
- try {
194
- const result = await (0, fanout_1.runFanout)({
195
- mode: args.mode,
196
- query: args.query,
197
- tasks: args.mode === 'partition'
198
- ? Array.from({ length: args.n }, (_, i) => ({
199
- goal: `Task ${i + 1} from CLI: ${args.query}`,
200
- }))
201
- : undefined,
202
- n: args.n,
203
- merge: args.merge,
204
- providers,
205
- runChild: async ({ index, prompt, provider }) => {
206
- return `[dry-run child ${index} via ${provider.providerId}] echo: ${prompt}`;
207
- },
208
- aggregatorAdapter: stubAdapter,
209
- aggregatorModel: { providerId: 'stub-a', modelId: 'fake-model' },
210
- timeoutMs: args.timeoutMs,
173
+ // ── Dry-run path ─────────────────────────────────────────────
174
+ // v4.6 Phase 2Q `runFanout` now routes children through
175
+ // `spawnSubAgent`, which needs real `SpawnSubAgentDeps`
176
+ // (toolRegistry, parentProvider, runStore, …). Pre-2Q the dry-run
177
+ // exercised Promise.all + abort + merge dispatch via simple stubs;
178
+ // post-refactor the equivalent coverage now lives in
179
+ // `tests/v4/subagent/fanout.behavioral.test.ts` (Slice 5).
180
+ //
181
+ // Dry-run therefore emits a synthetic snapshot — same observable
182
+ // shape (mode/n/merge/per-child rows) so the runtime smoke can
183
+ // still assert "the CLI subcommand parses + runs to exit 0
184
+ // against the built artifact" without booting a runtime.
185
+ const stubProviders = ['stub-a', 'stub-b'];
186
+ const childRows = [];
187
+ for (let i = 0; i < args.n; i += 1) {
188
+ const providerId = stubProviders[i % stubProviders.length];
189
+ const prompt = args.mode === 'partition'
190
+ ? `Task ${i + 1} from CLI: ${args.query}`
191
+ : args.query;
192
+ childRows.push({
193
+ index: i,
194
+ providerId,
195
+ modelId: 'fake-model',
196
+ output: `[dry-run child ${i} via ${providerId}] echo: ${prompt}`,
197
+ elapsedMs: 0,
211
198
  });
212
- writeOut(`fanout dry-run\n`);
213
- writeOut(` mode: ${args.mode}\n`);
214
- writeOut(` n: ${args.n}\n`);
215
- writeOut(` merge: ${args.merge}\n`);
216
- writeOut(` query: ${args.query}\n`);
217
- writeOut(` succeeded: ${result.diagnostics.succeeded}/${args.n}\n`);
218
- writeOut(` totalMs: ${result.diagnostics.totalMs}\n`);
219
- writeOut(` providers: ${result.diagnostics.providerDistribution.join(', ')}\n`);
220
- writeOut(`\n--- merged ---\n`);
221
- if (args.merge === 'all') {
222
- for (const r of result.results) {
223
- writeOut(`\n[${r.index}] ${r.providerId}:${r.modelId} (${r.elapsedMs}ms)\n`);
224
- writeOut(`${r.output}\n`);
225
- }
226
- }
227
- else {
228
- writeOut(`${result.merged ?? '(no merged output)'}\n`);
199
+ }
200
+ writeOut(`fanout dry-run\n`);
201
+ writeOut(` mode: ${args.mode}\n`);
202
+ writeOut(` n: ${args.n}\n`);
203
+ writeOut(` merge: ${args.merge}\n`);
204
+ writeOut(` query: ${args.query}\n`);
205
+ writeOut(` succeeded: ${args.n}/${args.n}\n`);
206
+ writeOut(` totalMs: 0\n`);
207
+ writeOut(` providers: ${childRows.map((r) => r.providerId).join(', ')}\n`);
208
+ writeOut(`\n--- merged ---\n`);
209
+ if (args.merge === 'all') {
210
+ for (const r of childRows) {
211
+ writeOut(`\n[${r.index}] ${r.providerId}:${r.modelId} (${r.elapsedMs}ms)\n`);
212
+ writeOut(`${r.output}\n`);
229
213
  }
230
- return 0;
231
214
  }
232
- catch (err) {
233
- writeErr(`fanout failed: ${err.message}\n`);
234
- return 1;
215
+ else {
216
+ writeOut(`[dry-run aggregator]\n`);
235
217
  }
218
+ return 0;
236
219
  }
@@ -58,6 +58,19 @@ exports.SUBSECTION_MAP = {
58
58
  history: 'System',
59
59
  // Phase v4.1.2-update — npm self-update for the running install.
60
60
  update: 'System',
61
+ // v4.5 Phase 8a — subsystem live-flip slash commands.
62
+ sandbox: 'System',
63
+ tce: 'System',
64
+ 'browser-depth': 'System',
65
+ daemon: 'System',
66
+ // v4.5 Phase 8b — contextual capability suggestions.
67
+ suggestions: 'System',
68
+ // v4.6 Phase 2M — opt-in keyword-based tool narrower.
69
+ 'planner-guard': 'System',
70
+ // v4.6 Phase 3A — operator kill-switch for sub-agent spawning.
71
+ 'spawn-pause': 'System',
72
+ // v4.6 Phase 3b — self-improvement loop operator surface.
73
+ recovery: 'System',
61
74
  // ── Authentication ──
62
75
  auth: 'Authentication',
63
76
  // ── Help ──
@@ -12,7 +12,7 @@
12
12
  * and registers each on the global CommandRegistry at boot.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.allCommands = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
15
+ exports.allCommands = exports.recovery = exports.spawnPause = exports.plannerGuard = exports.suggestions = exports.daemonStatus = exports.browserDepth = exports.tce = exports.sandbox = exports.update = exports.reloadSoul = exports.history = exports.show = exports.status = exports.voice = exports.channel = exports.setup = exports.cron = exports.doctor = exports.license = exports.auth = exports.plugins = exports.streaming = exports.debugPrompt = exports.identity = exports.providers = exports.quit = exports.clear = exports.verbose = exports.reasoning = exports.reloadMcp = exports.skills = exports.skin = exports.yolo = exports.usage = exports.compress = exports.title = exports.save = exports.personality = exports.model = exports.tools = exports.help = void 0;
16
16
  const help_1 = require("./help");
17
17
  Object.defineProperty(exports, "help", { enumerable: true, get: function () { return help_1.help; } });
18
18
  const tools_1 = require("./tools");
@@ -79,6 +79,27 @@ const reloadSoul_1 = require("./reloadSoul");
79
79
  Object.defineProperty(exports, "reloadSoul", { enumerable: true, get: function () { return reloadSoul_1.reloadSoul; } });
80
80
  const update_1 = require("./update");
81
81
  Object.defineProperty(exports, "update", { enumerable: true, get: function () { return update_1.update; } });
82
+ // v4.5 Phase 8a — subsystem live-flip slash commands.
83
+ const sandbox_1 = require("./sandbox");
84
+ Object.defineProperty(exports, "sandbox", { enumerable: true, get: function () { return sandbox_1.sandbox; } });
85
+ const tce_1 = require("./tce");
86
+ Object.defineProperty(exports, "tce", { enumerable: true, get: function () { return tce_1.tce; } });
87
+ const browserDepth_1 = require("./browserDepth");
88
+ Object.defineProperty(exports, "browserDepth", { enumerable: true, get: function () { return browserDepth_1.browserDepth; } });
89
+ const daemonStatus_1 = require("./daemonStatus");
90
+ Object.defineProperty(exports, "daemonStatus", { enumerable: true, get: function () { return daemonStatus_1.daemonStatus; } });
91
+ // v4.5 Phase 8b — contextual capability suggestions toggle.
92
+ const suggestions_1 = require("./suggestions");
93
+ Object.defineProperty(exports, "suggestions", { enumerable: true, get: function () { return suggestions_1.suggestions; } });
94
+ // v4.6 Phase 2M — opt-in keyword-based tool narrower (default OFF).
95
+ const plannerGuard_1 = require("./plannerGuard");
96
+ Object.defineProperty(exports, "plannerGuard", { enumerable: true, get: function () { return plannerGuard_1.plannerGuard; } });
97
+ // v4.6 Phase 3A — operator kill-switch for sub-agent spawning.
98
+ const spawnPause_1 = require("./spawnPause");
99
+ Object.defineProperty(exports, "spawnPause", { enumerable: true, get: function () { return spawnPause_1.spawnPause; } });
100
+ // v4.6 Phase 3b — self-improvement loop operator surface.
101
+ const recovery_1 = require("./recovery");
102
+ Object.defineProperty(exports, "recovery", { enumerable: true, get: function () { return recovery_1.recovery; } });
82
103
  /** All built-in system commands, in canonical order. */
83
104
  exports.allCommands = [
84
105
  help_1.help,
@@ -115,6 +136,19 @@ exports.allCommands = [
115
136
  // probe + shared executeInstall executor (also wired into
116
137
  // aiden_self_update tool for natural-language requests).
117
138
  update_1.update,
139
+ // v4.5 Phase 8a — subsystem live-flip slash commands.
140
+ sandbox_1.sandbox,
141
+ tce_1.tce,
142
+ browserDepth_1.browserDepth,
143
+ daemonStatus_1.daemonStatus,
144
+ // v4.5 Phase 8b — contextual suggestions toggle.
145
+ suggestions_1.suggestions,
146
+ // v4.6 Phase 2M — opt-in keyword-based tool narrower (default OFF).
147
+ plannerGuard_1.plannerGuard,
148
+ // v4.6 Phase 3A — operator kill-switch for sub-agent spawning.
149
+ spawnPause_1.spawnPause,
150
+ // v4.6 Phase 3b — self-improvement loop operator surface.
151
+ recovery_1.recovery,
118
152
  clear_1.clear,
119
153
  quit_1.quit,
120
154
  ];
@@ -32,10 +32,15 @@
32
32
  * tools by default; opting in means the user accepted server-side
33
33
  * execution risk at config time.
34
34
  */
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.AIDEN_MCP_BUILD = void 0;
37
40
  exports.runMcpSubcommand = runMcpSubcommand;
38
41
  /* eslint-disable @typescript-eslint/no-explicit-any */
42
+ const node_os_1 = __importDefault(require("node:os"));
43
+ const node_crypto_1 = require("node:crypto");
39
44
  const paths_1 = require("../../../core/v4/paths");
40
45
  const toolRegistry_1 = require("../../../core/v4/toolRegistry");
41
46
  const skillLoader_1 = require("../../../core/v4/skillLoader");
@@ -46,12 +51,26 @@ const processRegistry_1 = require("../../../core/v4/processRegistry");
46
51
  const config_1 = require("../../../core/v4/config");
47
52
  const providerFallback_1 = require("../../../core/v4/providerFallback");
48
53
  const chatCompletionsAdapter_1 = require("../../../providers/v4/chatCompletionsAdapter");
49
- const aidenAgent_1 = require("../../../core/v4/aidenAgent");
50
54
  const factory_1 = require("../../../core/v4/logger/factory");
51
55
  const index_1 = require("../../../tools/v4/index");
52
56
  const envSources_1 = require("../envSources");
53
57
  const credentialResolver_1 = require("../../../providers/v4/credentialResolver");
54
58
  const runtimeResolver_1 = require("../../../providers/v4/runtimeResolver");
59
+ // v4.6 Phase 2R — MCP-mode subagent_fanout now routes children
60
+ // through the `spawnSubAgent` primitive (same as REPL after 2Q-A).
61
+ // The legacy `runChild` closure + RunChildArgs import are deleted
62
+ // per design doc §12.3 (2R cleanup). MCP needs its own daemon-db
63
+ // connection + instance id so child runs persist under
64
+ // `aiden runs list --include-children` for cross-runtime
65
+ // observability.
66
+ const daemon_1 = require("../../../core/v4/daemon");
67
+ const version_1 = require("../../../core/version");
68
+ // v4.6 Phase 3A — operator kill-switch. Initialised here so
69
+ // MCP-side `subagent_fanout` (and any future MCP-side
70
+ // `spawn_sub_agent` exposure) reads from the same marker file the
71
+ // REPL writes via /spawn-pause. Cross-process coordination is the
72
+ // whole point of the file-marker design.
73
+ const spawnPause_1 = require("../../../core/v4/subagent/spawnPause");
55
74
  const stdioServer_1 = require("../../../core/v4/mcp/server/stdioServer");
56
75
  Object.defineProperty(exports, "AIDEN_MCP_BUILD", { enumerable: true, get: function () { return stdioServer_1.AIDEN_MCP_BUILD; } });
57
76
  const diagnostics_1 = require("../../../core/v4/mcp/server/diagnostics");
@@ -206,6 +225,43 @@ async function wireSubagentFanout(opts) {
206
225
  }
207
226
  }
208
227
  const finalAdapter = wrapped;
228
+ // v4.6 Phase 2R — open a daemon-db connection + seed an MCP
229
+ // instance row so child sub-agent runs (spawned by
230
+ // subagent_fanout below) persist to the same `runs` table the
231
+ // REPL writes to. Operators can then see MCP-side fanout
232
+ // activity under `aiden runs list --include-children`. Same
233
+ // WAL-coexistence model as REPL — connection.ts caches per-path.
234
+ const mcpInstanceId = `mcp-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
235
+ const mcpDb = (0, daemon_1.openDaemonDb)((0, daemon_1.daemonDbPath)(opts.paths.root));
236
+ mcpDb.prepare(`INSERT OR IGNORE INTO daemon_instances
237
+ (instance_id, pid, hostname, started_at, last_heartbeat, version)
238
+ VALUES (?, ?, ?, ?, ?, ?)`).run(mcpInstanceId, process.pid, node_os_1.default.hostname(), Date.now(), Date.now(), version_1.VERSION);
239
+ const mcpRunStore = (0, daemon_1.createRunStore)({ db: mcpDb });
240
+ // v4.6 Phase 3b — self-improvement loop singleton against the
241
+ // same daemon.db. MCP-side spawn_sub_agent / subagent_fanout
242
+ // dispatches now record failure occurrences + recoveries into
243
+ // the shared ledger, so operators see MCP failures alongside
244
+ // REPL failures in `aiden /recovery list` from a future REPL
245
+ // session.
246
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
247
+ const { initRecoveryStore } = require('../../../core/v4/selfimprovement/recoveryStore');
248
+ initRecoveryStore({ db: mcpDb });
249
+ // v4.6 Phase 3A — wire the pause singleton against the same
250
+ // `paths.root` the REPL uses. The fanout handler's pause-check
251
+ // reads through `getSpawnPause()`, so initing here makes MCP
252
+ // mode respect the operator's /spawn-pause state without any
253
+ // additional plumbing.
254
+ const mcpPauseState = (0, spawnPause_1.initSpawnPause)({ aidenHome: opts.paths.root });
255
+ if (mcpPauseState.isPaused()) {
256
+ const s = mcpPauseState.status();
257
+ const reasonSuffix = s.reason ? ` (reason: ${s.reason})` : '';
258
+ opts.logger.warn(`MCP boot: subagent_fanout is PAUSED${reasonSuffix}. ` +
259
+ 'Operator must run /spawn-pause off in a REPL session to resume.', {
260
+ pausedAt: s.pausedAt ?? null,
261
+ pausedBy: s.pausedBy ?? null,
262
+ durationMs: s.durationMs ?? null,
263
+ });
264
+ }
209
265
  opts.registry.register((0, index_1.makeSubagentFanoutTool)({
210
266
  logger: opts.logger,
211
267
  resolveActiveModel: () => ({ providerId, modelId }),
@@ -224,70 +280,40 @@ async function wireSubagentFanout(opts) {
224
280
  }
225
281
  return [{ providerId, modelId }];
226
282
  },
227
- runChild: async (childOpts) => {
228
- const childCtx = {
283
+ // v4.6 Phase 2R — `spawnDeps` mirrors the REPL wiring in
284
+ // `cli/v4/aidenCLI.ts` (2Q-A). The MCP-mode parentToolContext
285
+ // is intentionally lean: no approvalEngine (a server has no
286
+ // human at the REPL to prompt — children's mutating tools are
287
+ // gated by AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE in childBuilder),
288
+ // no ssrfProtection/tirithScanner/memoryGuard (MCP's slim
289
+ // runtime never wired them — see header comment §9).
290
+ spawnDeps: {
291
+ toolRegistry: opts.registry,
292
+ parentToolContext: {
229
293
  cwd: process.cwd(),
230
294
  paths: opts.paths,
231
295
  sessions: opts.sessionManager,
232
296
  memory: opts.memoryManager,
233
297
  skillLoader: opts.skillLoader,
234
- // approvalEngine intentionally undefined — N children
235
- // contending for one stdin REPL would deadlock under MCP.
236
- };
237
- // Filter the child tool surface — read-only by default; opt-in
238
- // via AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE=1 (mirrors MCP env from
239
- // v4.1-mcp). Recursive fanout disallowed (depth=1).
240
- const allowDestructive = process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === '1' ||
241
- process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === 'true';
242
- const childToolNames = [];
243
- for (const name of opts.registry.list()) {
244
- const h = opts.registry.get(name);
245
- if (!h)
246
- continue;
247
- if (h.mutates && !allowDestructive)
248
- continue;
249
- if (name === 'subagent_fanout')
250
- continue;
251
- childToolNames.push(name);
252
- }
253
- const childExecutor = opts.registry.buildExecutor(childCtx);
254
- const childTools = childToolNames
255
- .map((n) => opts.registry.get(n)?.schema)
256
- .filter((s) => !!s);
257
- // Per-child cloned FallbackAdapter — own rate-limit state.
258
- const childProvider = finalAdapter instanceof providerFallback_1.FallbackAdapter
259
- ? finalAdapter.clone()
260
- : finalAdapter;
261
- const child = new aidenAgent_1.AidenAgent({
262
- provider: childProvider,
263
- tools: childTools,
264
- toolExecutor: childExecutor,
265
- maxTurns: childOpts.maxIterations,
266
- providerId: childOpts.provider.providerId,
267
- modelId: childOpts.provider.modelId,
268
- // No promptBuilder — children get a brief system prompt
269
- // (same lesson as v4.1-subagent.1: full SOUL.md makes
270
- // trivial fanouts spend 30s+ on verbose self-introductions).
271
- });
272
- if (childOpts.signal.aborted) {
273
- throw new Error('aborted before dispatch');
274
- }
275
- const roleLine = childOpts.role ? `Role: ${childOpts.role}. ` : '';
276
- const childSystemPrompt = `You are one of N parallel subagents. ${roleLine}` +
277
- `Answer the user's request concisely. Use available tools when ` +
278
- `the answer requires real-world information you don't have memorized.`;
279
- const history = [
280
- { role: 'system', content: childSystemPrompt },
281
- { role: 'user', content: childOpts.prompt },
282
- ];
283
- const result = await child.runConversation(history);
284
- return result.finalContent;
298
+ },
299
+ parentProvider: finalAdapter,
300
+ parentProviderId: providerId,
301
+ parentModelId: modelId,
302
+ runStore: mcpRunStore,
303
+ instanceId: mcpInstanceId,
304
+ logger: opts.logger,
285
305
  },
306
+ // No resolveParentRunId / resolveParentSessionId for MCP — the
307
+ // MCP server doesn't run a turn loop with its own `runs` row;
308
+ // each tool invocation arrives discrete from the client. Child
309
+ // rows therefore have NULL spawned_from_run_id, which is the
310
+ // honest representation (no MCP-side parent to link to).
286
311
  }));
287
312
  opts.logger.info('subagent_fanout: wired (replaces stub) [mcp serve]', {
288
313
  providerId,
289
314
  modelId,
290
315
  fallback: finalAdapter instanceof providerFallback_1.FallbackAdapter ? 'FallbackAdapter' : 'direct',
316
+ instanceId: mcpInstanceId,
291
317
  });
292
318
  }
293
319
  async function runMcpSubcommand(action, opts = {}) {