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,53 @@
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/plannerGuard.ts — v4.6 Phase 2M.
10
+ *
11
+ * `/planner-guard on|off|status` — flip the keyword-based per-turn
12
+ * tool narrower (`moat/plannerGuard.ts`) live, no restart needed.
13
+ *
14
+ * Default: OFF. Smart models (GPT-5.5, Claude Sonnet 4.5+, Opus)
15
+ * select tools fine from the full catalog every turn, the way the
16
+ * reference multi-agent systems do. Per-turn narrowing was a
17
+ * v4.1-era workaround for smaller local models that got overwhelmed
18
+ * by 50+ tool schemas — opt in for that case via env (set
19
+ * `AIDEN_PLANNER_GUARD=1` at boot) or this slash command.
20
+ *
21
+ * Persists to `runtime_toggles.planner_guard` in config.yaml when
22
+ * a ConfigManager is wired (the normal REPL path). Env var
23
+ * `AIDEN_PLANNER_GUARD` always wins over both — see runtimeToggles.ts.
24
+ *
25
+ * Mirrors `/sandbox`, `/tce`, `/browser-depth`, `/suggestions`
26
+ * verbatim — same helpers, same output shape.
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.plannerGuard = void 0;
30
+ const _runtimeToggleHelpers_1 = require("./_runtimeToggleHelpers");
31
+ exports.plannerGuard = {
32
+ name: 'planner-guard',
33
+ description: 'Toggle keyword-based per-turn tool narrowing (default OFF, opt-in).',
34
+ category: 'system',
35
+ icon: '🧭',
36
+ handler: async (ctx) => {
37
+ const sub = (0, _runtimeToggleHelpers_1.parseSubcommand)(ctx.args[0]);
38
+ if (sub === 'on') {
39
+ await (0, _runtimeToggleHelpers_1.flip)('planner_guard', true, ctx);
40
+ return {};
41
+ }
42
+ if (sub === 'off') {
43
+ await (0, _runtimeToggleHelpers_1.flip)('planner_guard', false, ctx);
44
+ return {};
45
+ }
46
+ if (sub === 'status') {
47
+ (0, _runtimeToggleHelpers_1.printStatus)('planner_guard', ctx);
48
+ return {};
49
+ }
50
+ ctx.display.printError('Usage: /planner-guard on|off|status');
51
+ return {};
52
+ },
53
+ };
@@ -0,0 +1,122 @@
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/recovery.ts — v4.6 Phase 3b.
10
+ *
11
+ * `/recovery list [limit]` — top N recurring failure patterns
12
+ * `/recovery show <signature>` — details for one signature + reports
13
+ * `/recovery clear <signature>` — operator says "fixed; stop counting"
14
+ *
15
+ * Backed by the v7 `failure_signatures` + `recovery_reports` tables
16
+ * via the `RecoveryStore` singleton (initialised at REPL/daemon/MCP
17
+ * boot). All three sub-actions degrade cleanly when the store isn't
18
+ * initialised — print a non-fatal error and return.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.recovery = void 0;
22
+ const recoveryStore_1 = require("../../../core/v4/selfimprovement/recoveryStore");
23
+ /** Format a wall-clock ms timestamp as a compact UTC label. */
24
+ function formatTs(ms) {
25
+ return new Date(ms).toISOString().slice(0, 19) + 'Z';
26
+ }
27
+ /** Right-pad to width; truncate with ellipsis when too long. */
28
+ function pad(value, width) {
29
+ const s = String(value);
30
+ if (s.length === width)
31
+ return s;
32
+ if (s.length < width)
33
+ return s + ' '.repeat(width - s.length);
34
+ return s.slice(0, Math.max(0, width - 1)) + '…';
35
+ }
36
+ exports.recovery = {
37
+ name: 'recovery',
38
+ description: 'Inspect recurring failure patterns + recoveries (v4.6 Phase 3b).',
39
+ category: 'system',
40
+ icon: '🩹',
41
+ handler: async (ctx) => {
42
+ const action = (ctx.args[0] ?? 'list').toLowerCase();
43
+ const store = (0, recoveryStore_1.getRecoveryStore)();
44
+ if (!store) {
45
+ ctx.display.printError('recovery: recovery store not initialised — daemon DB unavailable?');
46
+ return {};
47
+ }
48
+ if (action === 'list') {
49
+ const limitArg = ctx.args[1];
50
+ const parsed = limitArg !== undefined ? Number.parseInt(limitArg, 10) : NaN;
51
+ const limit = Number.isFinite(parsed) && parsed > 0 ? parsed : 10;
52
+ const rows = store.listTopFailures(limit);
53
+ if (rows.length === 0) {
54
+ ctx.display.write('No recurring failures recorded yet.\n');
55
+ ctx.display.dim(' TCE writes failure signatures on classify; recoveries on failure→success transition.');
56
+ return {};
57
+ }
58
+ ctx.display.write(`${pad('signature', 50)} ${pad('occur', 6)} ${pad('recov', 6)} last_strategy\n`);
59
+ for (const r of rows) {
60
+ ctx.display.write(`${pad(r.signature, 50)} ${pad(r.occurrences, 6)} ${pad(r.recoveredCount, 6)} ${r.lastRecoveryStrategy ?? '-'}\n`);
61
+ }
62
+ ctx.display.write(`\n${rows.length} signature${rows.length === 1 ? '' : 's'} shown\n`);
63
+ return {};
64
+ }
65
+ if (action === 'show') {
66
+ const sig = ctx.args[1];
67
+ if (!sig) {
68
+ ctx.display.printError('Usage: /recovery show <signature>');
69
+ return {};
70
+ }
71
+ const row = store.getBySignature(sig);
72
+ if (!row) {
73
+ ctx.display.printError(`recovery: signature not found: ${sig}`);
74
+ return {};
75
+ }
76
+ ctx.display.write(`signature: ${row.signature}\n`);
77
+ ctx.display.write(`tool_name: ${row.toolName}\n`);
78
+ ctx.display.write(`failure_category: ${row.failureCategory}\n`);
79
+ ctx.display.write(`occurrences: ${row.occurrences}\n`);
80
+ ctx.display.write(`recovered_count: ${row.recoveredCount}\n`);
81
+ ctx.display.write(`first_seen: ${formatTs(row.firstSeenAt)}\n`);
82
+ ctx.display.write(`last_seen: ${formatTs(row.lastSeenAt)}\n`);
83
+ const reports = store.listReportsForSignature(row.id, 20);
84
+ if (reports.length === 0) {
85
+ ctx.display.dim(' (no recovery reports yet for this signature)');
86
+ return {};
87
+ }
88
+ ctx.display.write(`\nrecovery reports (${reports.length}, newest first):\n`);
89
+ for (const r of reports) {
90
+ ctx.display.write(` [${formatTs(r.createdAt)}] strategy=${r.successfulStrategy}`);
91
+ if (r.failedAttempts !== undefined) {
92
+ ctx.display.write(` failed=${r.failedAttempts}`);
93
+ }
94
+ if (r.sessionId)
95
+ ctx.display.write(` session=${r.sessionId}`);
96
+ ctx.display.write('\n');
97
+ if (r.notes)
98
+ ctx.display.dim(` ${r.notes}`);
99
+ if (r.verification)
100
+ ctx.display.dim(` verified: ${r.verification}`);
101
+ }
102
+ return {};
103
+ }
104
+ if (action === 'clear') {
105
+ const sig = ctx.args[1];
106
+ if (!sig) {
107
+ ctx.display.printError('Usage: /recovery clear <signature>');
108
+ return {};
109
+ }
110
+ const ok = store.clearSignature(sig);
111
+ if (ok) {
112
+ ctx.display.write(`recovery: cleared signature ${sig}\n`);
113
+ }
114
+ else {
115
+ ctx.display.printError(`recovery: signature not found: ${sig}`);
116
+ }
117
+ return {};
118
+ }
119
+ ctx.display.printError('Usage: /recovery list [limit] | show <signature> | clear <signature>');
120
+ return {};
121
+ },
122
+ };
@@ -0,0 +1,223 @@
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/runs.ts — v4.5 Phase 6: `aiden runs` command set.
10
+ *
11
+ * Surfaces the daemon's `runs` + `run_events` tables (Phase 1
12
+ * runStore) so operators can inspect daemon-fired turn history
13
+ * outside the REPL.
14
+ *
15
+ * Subcommands:
16
+ * list — recent runs; optional --limit / --source / --status filters
17
+ * show — full run row + all run_events for one runId
18
+ * interrupt — request cancellation of a running turn (Q-P6-4a:
19
+ * SIGUSR1 + marker file pattern)
20
+ * stats — aggregate counts by status + mean duration
21
+ *
22
+ * The `interrupt` path writes
23
+ * `~/.aiden/daemon/interrupt-<runId>.req` and signals the daemon
24
+ * via SIGUSR1. The dispatcher polls the marker directory on each
25
+ * lease-renew tick (~60s) and cancels runs whose marker exists.
26
+ * This matches the existing SIGUSR1 → exit 75 restart contract
27
+ * and keeps cancellation signal-driven rather than DB-poll.
28
+ */
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.runRunsSubcommand = runRunsSubcommand;
34
+ const node_fs_1 = __importDefault(require("node:fs"));
35
+ const node_path_1 = __importDefault(require("node:path"));
36
+ const daemon_1 = require("../../../core/v4/daemon");
37
+ const paths_1 = require("../../../core/v4/paths");
38
+ const noopOut = (s) => { process.stdout.write(s); };
39
+ const noopErr = (s) => { process.stderr.write(s); };
40
+ async function runRunsSubcommand(action, args, argv, opts = {}) {
41
+ const out = opts.writeOut ?? noopOut;
42
+ const err = opts.writeErr ?? noopErr;
43
+ const aidenRoot = (0, paths_1.resolveAidenRoot)();
44
+ const db = (0, daemon_1.openDaemonDb)((0, daemon_1.daemonDbPath)(aidenRoot));
45
+ const runStore = (0, daemon_1.createRunStore)({ db });
46
+ switch (action) {
47
+ case 'list': return cmdList(runStore, argv, out);
48
+ case 'show': return cmdShow(runStore, args[0], out, err);
49
+ case 'interrupt': return cmdInterrupt(args[0], aidenRoot, out, err);
50
+ case 'stats': return cmdStats(db, out);
51
+ default:
52
+ err(`Unknown runs action: ${action}\n`);
53
+ err('Actions: list, show <runId>, interrupt <runId>, stats\n');
54
+ return 2;
55
+ }
56
+ }
57
+ // ── list ──────────────────────────────────────────────────────────────────
58
+ function cmdList(runStore, argv, out) {
59
+ const allowedStatuses = new Set([
60
+ 'queued', 'running', 'completed', 'failed', 'cancelled', 'interrupted',
61
+ ]);
62
+ const status = argv.status && allowedStatuses.has(argv.status)
63
+ ? argv.status
64
+ : undefined;
65
+ // v4.6 Phase 2Q-B — default `topLevelOnly: true` hides children.
66
+ // `--include-children` flag (parsed by the CLI argv layer into
67
+ // `includeChildren: true`) flips the predicate to drop the IS NULL
68
+ // filter so child rows appear inline with parents.
69
+ const includeChildren = argv.includeChildren === true;
70
+ const rows = runStore.listRecent({
71
+ limit: argv.limit ?? 50,
72
+ status,
73
+ source: argv.source,
74
+ sessionIdPrefix: argv.trigger,
75
+ topLevelOnly: !includeChildren,
76
+ });
77
+ if (rows.length === 0) {
78
+ out('No runs match the filter.\n');
79
+ return 0;
80
+ }
81
+ out(`${'runId'.padEnd(6)} ${'status'.padEnd(11)} ${'finish'.padEnd(11)} ${'started'.padEnd(20)} sessionId\n`);
82
+ for (const r of rows) {
83
+ const started = new Date(r.startedAt).toISOString().slice(0, 19) + 'Z';
84
+ const finish = r.finishReason ?? '-';
85
+ // v4.6 Phase 2Q-B — child-count badge. Only relevant for the
86
+ // top-level view (when --include-children is OFF). Skipped on
87
+ // the flat view to avoid double-counting visual weight: in flat
88
+ // mode the children are already on screen as their own rows.
89
+ let badge = '';
90
+ if (!includeChildren) {
91
+ const { total, completed } = runStore.countChildren(r.id);
92
+ if (total > 0) {
93
+ badge = ` (${total} ${total === 1 ? 'child' : 'children'}, ${completed} OK)`;
94
+ }
95
+ }
96
+ out(`${String(r.id).padEnd(6)} ${r.status.padEnd(11)} ${finish.padEnd(11)} ${started.padEnd(20)} ${r.sessionId}${badge}\n`);
97
+ }
98
+ const hint = includeChildren
99
+ ? ' (parents + sub-agent children)'
100
+ : ' (top-level; use --include-children for sub-agents)';
101
+ out(`\n${rows.length} run${rows.length === 1 ? '' : 's'} shown${hint}\n`);
102
+ return 0;
103
+ }
104
+ // ── show ──────────────────────────────────────────────────────────────────
105
+ function cmdShow(runStore, rawId, out, err) {
106
+ if (!rawId) {
107
+ err('runs show: runId required\n');
108
+ return 2;
109
+ }
110
+ const runId = Number.parseInt(rawId, 10);
111
+ if (!Number.isFinite(runId) || runId <= 0) {
112
+ err(`runs show: invalid runId: ${rawId}\n`);
113
+ return 2;
114
+ }
115
+ const row = runStore.get(runId);
116
+ if (!row) {
117
+ err(`runs show: not found: ${runId}\n`);
118
+ return 1;
119
+ }
120
+ const events = runStore.listEvents(runId, 500);
121
+ out(JSON.stringify({
122
+ run: row,
123
+ events: events.map((e) => ({
124
+ ts: new Date(e.ts).toISOString(),
125
+ kind: e.kind,
126
+ payload: safeParse(e.payload),
127
+ })),
128
+ }, null, 2) + '\n');
129
+ return 0;
130
+ }
131
+ function safeParse(s) {
132
+ try {
133
+ return JSON.parse(s);
134
+ }
135
+ catch {
136
+ return s;
137
+ }
138
+ }
139
+ // ── interrupt ─────────────────────────────────────────────────────────────
140
+ function cmdInterrupt(rawId, aidenRoot, out, err) {
141
+ if (!rawId) {
142
+ err('runs interrupt: runId required\n');
143
+ return 2;
144
+ }
145
+ const runId = Number.parseInt(rawId, 10);
146
+ if (!Number.isFinite(runId) || runId <= 0) {
147
+ err(`runs interrupt: invalid runId: ${rawId}\n`);
148
+ return 2;
149
+ }
150
+ const markerDir = node_path_1.default.join(aidenRoot, 'daemon', 'interrupt');
151
+ try {
152
+ node_fs_1.default.mkdirSync(markerDir, { recursive: true });
153
+ }
154
+ catch (e) {
155
+ err(`runs interrupt: failed to create marker dir: ${e instanceof Error ? e.message : String(e)}\n`);
156
+ return 1;
157
+ }
158
+ const markerPath = node_path_1.default.join(markerDir, `${runId}.req`);
159
+ try {
160
+ node_fs_1.default.writeFileSync(markerPath, JSON.stringify({ runId, requestedAt: Date.now() }));
161
+ }
162
+ catch (e) {
163
+ err(`runs interrupt: failed to write marker: ${e instanceof Error ? e.message : String(e)}\n`);
164
+ return 1;
165
+ }
166
+ // Best-effort SIGUSR1 to the daemon. If the daemon isn't running,
167
+ // the marker is still there for whoever boots next — they'll find
168
+ // the run in `interrupted` state via the boot crash-recovery pass.
169
+ const lockPath = (0, daemon_1.daemonRuntimeLockPath)(aidenRoot);
170
+ let pid = null;
171
+ try {
172
+ if (node_fs_1.default.existsSync(lockPath)) {
173
+ const lines = node_fs_1.default.readFileSync(lockPath, 'utf-8').split(/\r?\n/);
174
+ // runtime.lock format: line 0 = instanceId, line 1 = pid, line 2 = port
175
+ const candidate = Number.parseInt(lines[1] ?? '', 10);
176
+ if (Number.isFinite(candidate))
177
+ pid = candidate;
178
+ }
179
+ }
180
+ catch { /* noop */ }
181
+ if (pid !== null && process.platform !== 'win32') {
182
+ try {
183
+ process.kill(pid, 'SIGUSR1');
184
+ }
185
+ catch { /* daemon may have died; marker still wins on next boot */ }
186
+ }
187
+ out(`runs interrupt: marker written at ${markerPath}\n`);
188
+ if (pid !== null && process.platform !== 'win32') {
189
+ out(`runs interrupt: SIGUSR1 sent to daemon pid=${pid}\n`);
190
+ }
191
+ else if (process.platform === 'win32') {
192
+ out('runs interrupt: SIGUSR1 not available on Windows — daemon picks up marker on next renew tick\n');
193
+ }
194
+ else {
195
+ out('runs interrupt: no live daemon found — marker will be honoured on next boot\n');
196
+ }
197
+ return 0;
198
+ }
199
+ // ── stats ─────────────────────────────────────────────────────────────────
200
+ function cmdStats(db, out) {
201
+ const counts = db.prepare(`SELECT status, COUNT(*) AS c FROM runs GROUP BY status`).all();
202
+ const completed = db.prepare(`SELECT AVG(completed_at - started_at) AS mean,
203
+ MIN(completed_at - started_at) AS min,
204
+ MAX(completed_at - started_at) AS max,
205
+ COUNT(*) AS n
206
+ FROM runs
207
+ WHERE status = 'completed' AND completed_at IS NOT NULL`).get();
208
+ out('Run status counts:\n');
209
+ if (counts.length === 0) {
210
+ out(' (no runs recorded)\n');
211
+ }
212
+ for (const r of counts) {
213
+ out(` ${r.status.padEnd(12)} ${r.c}\n`);
214
+ }
215
+ if (completed.n > 0 && completed.mean !== null) {
216
+ out('\nCompleted-run duration (ms):\n');
217
+ out(` mean ${Math.round(completed.mean)}\n`);
218
+ out(` min ${completed.min}\n`);
219
+ out(` max ${completed.max}\n`);
220
+ out(` n ${completed.n}\n`);
221
+ }
222
+ return 0;
223
+ }
@@ -0,0 +1,48 @@
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/sandbox.ts — v4.5 Phase 8a.
10
+ *
11
+ * `/sandbox on|off|status` — flip the v4.4 execution sandbox
12
+ * (filesystem allow/deny + docker session backend + dryRun
13
+ * preflight) without restart. Persists to config.yaml
14
+ * (runtime_toggles.sandbox). Env var AIDEN_SANDBOX always wins
15
+ * over both — see runtimeToggles.ts for precedence rules.
16
+ *
17
+ * Q-P8a-4(a): /sandbox off flips silently. User explicitly typed
18
+ * the command and the status output makes the flip visible. The
19
+ * sandbox denylist (fs.sensitive_path) remains in effect for
20
+ * unmistakably dangerous paths regardless of the toggle —
21
+ * disabling the sandbox does NOT remove the always-on denylist.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.sandbox = void 0;
25
+ const _runtimeToggleHelpers_1 = require("./_runtimeToggleHelpers");
26
+ exports.sandbox = {
27
+ name: 'sandbox',
28
+ description: 'Toggle the v4.4 execution sandbox (file ACLs + docker tools).',
29
+ category: 'system',
30
+ icon: '🛡',
31
+ handler: async (ctx) => {
32
+ const sub = (0, _runtimeToggleHelpers_1.parseSubcommand)(ctx.args[0]);
33
+ if (sub === 'on') {
34
+ await (0, _runtimeToggleHelpers_1.flip)('sandbox', true, ctx);
35
+ return {};
36
+ }
37
+ if (sub === 'off') {
38
+ await (0, _runtimeToggleHelpers_1.flip)('sandbox', false, ctx);
39
+ return {};
40
+ }
41
+ if (sub === 'status') {
42
+ (0, _runtimeToggleHelpers_1.printStatus)('sandbox', ctx);
43
+ return {};
44
+ }
45
+ ctx.display.printError('Usage: /sandbox on|off|status');
46
+ return {};
47
+ },
48
+ };
@@ -0,0 +1,93 @@
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/spawnPause.ts — v4.6 Phase 3A.
10
+ *
11
+ * `/spawn-pause on|off|status [reason...]` — operator kill-switch
12
+ * for sub-agent spawning. Backed by a file marker at
13
+ * `$aidenHome/spawn.paused` (see `core/v4/subagent/spawnPause.ts`)
14
+ * so REPL + daemon + MCP server all coordinate via the same state.
15
+ *
16
+ * /spawn-pause on — pause, no reason
17
+ * /spawn-pause on runaway-fanout — pause, reason="runaway-fanout"
18
+ * /spawn-pause on deploy window — pause, reason="deploy window"
19
+ * /spawn-pause off — resume
20
+ * /spawn-pause status — current state + reason + duration
21
+ *
22
+ * Unlike `/planner-guard`, `/sandbox`, etc., this command does NOT
23
+ * route through `runtimeToggles` — pause state is file-marker-
24
+ * backed (cross-process visibility) with first-class
25
+ * reason/pausedAt/pausedBy metadata that the boolean toggle surface
26
+ * can't carry. Mirrors plannerGuard.ts's command shape; diverges
27
+ * from `_runtimeToggleHelpers` because the storage backend is
28
+ * different.
29
+ *
30
+ * Hard contract: in-flight children are NEVER cancelled by this
31
+ * command. Pause affects only NEW spawns. Operators who want to
32
+ * stop in-flight runs use `aiden runs interrupt <runId>` (the
33
+ * existing per-run cancellation surface from v4.5 Phase 6).
34
+ */
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.spawnPause = void 0;
37
+ const spawnPause_1 = require("../../../core/v4/subagent/spawnPause");
38
+ /** Format a duration in ms as a compact `Xs` / `Xm` / `Xh` string. */
39
+ function formatDuration(ms) {
40
+ if (ms < 1000)
41
+ return `${ms}ms`;
42
+ if (ms < 60000)
43
+ return `${Math.round(ms / 1000)}s`;
44
+ if (ms < 3600000)
45
+ return `${Math.round(ms / 60000)}m`;
46
+ return `${Math.round(ms / 3600000)}h`;
47
+ }
48
+ exports.spawnPause = {
49
+ name: 'spawn-pause',
50
+ description: 'Pause/resume sub-agent spawning (in-flight children continue).',
51
+ category: 'system',
52
+ icon: '⏸',
53
+ handler: async (ctx) => {
54
+ const action = (ctx.args[0] ?? 'status').toLowerCase();
55
+ const reasonArg = ctx.args.slice(1).join(' ').trim() || null;
56
+ let state;
57
+ try {
58
+ state = (0, spawnPause_1.getSpawnPause)();
59
+ }
60
+ catch (e) {
61
+ ctx.display.printError('spawn-pause: not initialized — REPL boot did not wire the singleton.', e instanceof Error ? e.message : String(e));
62
+ return {};
63
+ }
64
+ if (action === 'on' || action === 'enable' || action === 'true' || action === '1') {
65
+ state.pause({ reason: reasonArg, pausedBy: 'repl' });
66
+ const s = state.status();
67
+ const reasonLine = s.reason ? ` reason: ${s.reason}\n` : '';
68
+ ctx.display.write(`spawn-pause: ON\n${reasonLine}`);
69
+ ctx.display.dim(' in-flight children continue. New spawn_sub_agent / subagent_fanout calls will reject.');
70
+ return {};
71
+ }
72
+ if (action === 'off' || action === 'disable' || action === 'false' || action === '0' || action === 'resume') {
73
+ state.resume();
74
+ ctx.display.write('spawn-pause: OFF (resumed)\n');
75
+ return {};
76
+ }
77
+ if (action === 'status' || action === '') {
78
+ const s = state.status();
79
+ if (!s.paused) {
80
+ ctx.display.write('spawn-pause: OFF\n');
81
+ return {};
82
+ }
83
+ const reasonLine = s.reason ? ` reason: ${s.reason}\n` : '';
84
+ const durationLine = s.durationMs !== undefined ? ` duration: ${formatDuration(s.durationMs)}\n` : '';
85
+ const pausedAtLine = s.pausedAt ? ` pausedAt: ${new Date(s.pausedAt).toISOString()}\n` : '';
86
+ const pausedByLine = s.pausedBy ? ` pausedBy: ${s.pausedBy}\n` : '';
87
+ ctx.display.write(`spawn-pause: ON\n${reasonLine}${durationLine}${pausedAtLine}${pausedByLine}`);
88
+ return {};
89
+ }
90
+ ctx.display.printError('Usage: /spawn-pause on [reason...] | off | status');
91
+ return {};
92
+ },
93
+ };
@@ -0,0 +1,68 @@
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/suggestions.ts — v4.5 Phase 8b.
10
+ *
11
+ * `/suggestions on|off|status` — flip the contextual capability
12
+ * suggestions surfaced by the suggestionEngine (Phase 8b). Reuses
13
+ * the Phase 8a `_runtimeToggleHelpers` since 'suggestions' is now
14
+ * a fourth ToggleKey on the runtimeToggles singleton.
15
+ *
16
+ * /suggestions on — re-enable tips (default).
17
+ * /suggestions off — silence tips for this REPL + persist to
18
+ * config.yaml (runtime_toggles.suggestions
19
+ * = false). Subsequent boots stay quiet
20
+ * until /suggestions on flips it back.
21
+ * /suggestions status — single-line state with source +
22
+ * fired-this-session count + budget remaining.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.suggestions = void 0;
26
+ const _runtimeToggleHelpers_1 = require("./_runtimeToggleHelpers");
27
+ const runtimeToggles_1 = require("../../../core/v4/runtimeToggles");
28
+ const suggestionEngine_1 = require("../../../core/v4/suggestionEngine");
29
+ exports.suggestions = {
30
+ name: 'suggestions',
31
+ description: 'Toggle contextual one-line capability tips.',
32
+ category: 'system',
33
+ icon: '💡',
34
+ handler: async (ctx) => {
35
+ const sub = (0, _runtimeToggleHelpers_1.parseSubcommand)(ctx.args[0]);
36
+ if (sub === 'on') {
37
+ await (0, _runtimeToggleHelpers_1.flip)('suggestions', true, ctx);
38
+ return {};
39
+ }
40
+ if (sub === 'off') {
41
+ await (0, _runtimeToggleHelpers_1.flip)('suggestions', false, ctx);
42
+ // Also session-dismiss so the in-process engine stops firing
43
+ // immediately, not just after the next REPL restart picks up
44
+ // the new config value.
45
+ try {
46
+ (0, suggestionEngine_1.getSuggestionEngine)().dismissAll();
47
+ }
48
+ catch { /* defensive */ }
49
+ return {};
50
+ }
51
+ if (sub === 'status') {
52
+ const tog = (0, runtimeToggles_1.getRuntimeToggles)().snapshot().suggestions;
53
+ const snap = (0, suggestionEngine_1.getSuggestionEngine)().snapshot();
54
+ const state = tog.value ? 'ON' : 'OFF';
55
+ const dismissTag = snap.dismissedSession && tog.value ? ' (dismissed this session)' : '';
56
+ ctx.display.write(`Suggestions: ${state} (source: ${tog.source})${dismissTag}\n`);
57
+ if (snap.firedSlots.length > 0) {
58
+ ctx.display.write(` fired this session: ${snap.firedSlots.join(', ')} · budget remaining: ${snap.budgetRemaining}\n`);
59
+ }
60
+ else {
61
+ ctx.display.write(` fired this session: (none) · budget remaining: ${snap.budgetRemaining}\n`);
62
+ }
63
+ return {};
64
+ }
65
+ ctx.display.printError('Usage: /suggestions on|off|status');
66
+ return {};
67
+ },
68
+ };
@@ -0,0 +1,41 @@
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/tce.ts — v4.5 Phase 8a.
10
+ *
11
+ * `/tce on|off|status` — flip the v4.2 Tool-Call Effort recovery
12
+ * pipeline (verifier + failure classifier + recovery report)
13
+ * without restart. Persists to config.yaml. Env var AIDEN_TCE
14
+ * always wins.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.tce = void 0;
18
+ const _runtimeToggleHelpers_1 = require("./_runtimeToggleHelpers");
19
+ exports.tce = {
20
+ name: 'tce',
21
+ description: 'Toggle the v4.2 Tool-Call Effort recovery pipeline.',
22
+ category: 'system',
23
+ icon: '🔁',
24
+ handler: async (ctx) => {
25
+ const sub = (0, _runtimeToggleHelpers_1.parseSubcommand)(ctx.args[0]);
26
+ if (sub === 'on') {
27
+ await (0, _runtimeToggleHelpers_1.flip)('tce', true, ctx);
28
+ return {};
29
+ }
30
+ if (sub === 'off') {
31
+ await (0, _runtimeToggleHelpers_1.flip)('tce', false, ctx);
32
+ return {};
33
+ }
34
+ if (sub === 'status') {
35
+ (0, _runtimeToggleHelpers_1.printStatus)('tce', ctx);
36
+ return {};
37
+ }
38
+ ctx.display.printError('Usage: /tce on|off|status');
39
+ return {};
40
+ },
41
+ };