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.
- package/README.md +265 -847
- package/dist/api/server.js +32 -5
- package/dist/cli/v4/aidenCLI.js +536 -152
- package/dist/cli/v4/callbacks.js +170 -0
- package/dist/cli/v4/chatSession.js +245 -3
- package/dist/cli/v4/commands/_runtimeToggleHelpers.js +94 -0
- package/dist/cli/v4/commands/browserDepth.js +45 -0
- package/dist/cli/v4/commands/cron.js +264 -0
- package/dist/cli/v4/commands/daemon.js +541 -0
- package/dist/cli/v4/commands/daemonStatus.js +253 -0
- package/dist/cli/v4/commands/fanout.js +42 -59
- package/dist/cli/v4/commands/help.js +13 -0
- package/dist/cli/v4/commands/index.js +35 -1
- package/dist/cli/v4/commands/mcp.js +80 -54
- package/dist/cli/v4/commands/plannerGuard.js +53 -0
- package/dist/cli/v4/commands/recovery.js +122 -0
- package/dist/cli/v4/commands/runs.js +223 -0
- package/dist/cli/v4/commands/sandbox.js +48 -0
- package/dist/cli/v4/commands/spawnPause.js +93 -0
- package/dist/cli/v4/commands/suggestions.js +68 -0
- package/dist/cli/v4/commands/tce.js +41 -0
- package/dist/cli/v4/commands/trigger.js +378 -0
- package/dist/cli/v4/commands/update.js +95 -3
- package/dist/cli/v4/daemonAgentBuilder.js +145 -0
- package/dist/cli/v4/defaultSoul.js +1 -1
- package/dist/cli/v4/display/capabilityCard.js +26 -0
- package/dist/cli/v4/display.js +18 -8
- package/dist/cli/v4/replyRenderer.js +31 -23
- package/dist/cli/v4/updateBootPrompt.js +170 -0
- package/dist/core/playwrightBridge.js +129 -0
- package/dist/core/v4/aidenAgent.js +527 -5
- package/dist/core/v4/browserState.js +436 -0
- package/dist/core/v4/checkpoint.js +79 -0
- package/dist/core/v4/daemon/bootstrap.js +651 -0
- package/dist/core/v4/daemon/cleanShutdown.js +154 -0
- package/dist/core/v4/daemon/cron/cronBridge.js +126 -0
- package/dist/core/v4/daemon/cron/cronEmitter.js +173 -0
- package/dist/core/v4/daemon/cron/migration.js +199 -0
- package/dist/core/v4/daemon/cron/misfirePolicy.js +115 -0
- package/dist/core/v4/daemon/daemonConfig.js +90 -0
- package/dist/core/v4/daemon/db/connection.js +106 -0
- package/dist/core/v4/daemon/db/migrations.js +362 -0
- package/dist/core/v4/daemon/db/schema/v1.spec.js +18 -0
- package/dist/core/v4/daemon/dispatcher/agentRunner.js +98 -0
- package/dist/core/v4/daemon/dispatcher/budgetGate.js +127 -0
- package/dist/core/v4/daemon/dispatcher/daemonApproval.js +113 -0
- package/dist/core/v4/daemon/dispatcher/dailyBudgetTracker.js +120 -0
- package/dist/core/v4/daemon/dispatcher/dispatcher.js +389 -0
- package/dist/core/v4/daemon/dispatcher/fireRateLimiter.js +113 -0
- package/dist/core/v4/daemon/dispatcher/index.js +53 -0
- package/dist/core/v4/daemon/dispatcher/promptTemplate.js +95 -0
- package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +356 -0
- package/dist/core/v4/daemon/dispatcher/resolveModel.js +93 -0
- package/dist/core/v4/daemon/dispatcher/sessionId.js +93 -0
- package/dist/core/v4/daemon/drain.js +156 -0
- package/dist/core/v4/daemon/eventLoopLag.js +73 -0
- package/dist/core/v4/daemon/health.js +159 -0
- package/dist/core/v4/daemon/idempotencyStore.js +204 -0
- package/dist/core/v4/daemon/index.js +179 -0
- package/dist/core/v4/daemon/instanceTracker.js +99 -0
- package/dist/core/v4/daemon/resourceRegistry.js +150 -0
- package/dist/core/v4/daemon/restartCode.js +32 -0
- package/dist/core/v4/daemon/restartFailureCounter.js +77 -0
- package/dist/core/v4/daemon/runStore.js +144 -0
- package/dist/core/v4/daemon/runtimeLock.js +167 -0
- package/dist/core/v4/daemon/signals.js +50 -0
- package/dist/core/v4/daemon/supervisor.js +272 -0
- package/dist/core/v4/daemon/triggerBus.js +279 -0
- package/dist/core/v4/daemon/triggers/email/allowlist.js +70 -0
- package/dist/core/v4/daemon/triggers/email/automatedSender.js +78 -0
- package/dist/core/v4/daemon/triggers/email/bodyExtractor.js +0 -0
- package/dist/core/v4/daemon/triggers/email/emailSeenStore.js +99 -0
- package/dist/core/v4/daemon/triggers/email/emailSpec.js +107 -0
- package/dist/core/v4/daemon/triggers/email/imapConnection.js +211 -0
- package/dist/core/v4/daemon/triggers/email/index.js +332 -0
- package/dist/core/v4/daemon/triggers/email/seenUids.js +60 -0
- package/dist/core/v4/daemon/triggers/fileObservationsStore.js +93 -0
- package/dist/core/v4/daemon/triggers/fileWatcher.js +253 -0
- package/dist/core/v4/daemon/triggers/fileWatcherSpec.js +88 -0
- package/dist/core/v4/daemon/triggers/fsIdentity.js +42 -0
- package/dist/core/v4/daemon/triggers/globMatcher.js +100 -0
- package/dist/core/v4/daemon/triggers/reconcile.js +206 -0
- package/dist/core/v4/daemon/triggers/settleStat.js +81 -0
- package/dist/core/v4/daemon/triggers/webhook.js +376 -0
- package/dist/core/v4/daemon/triggers/webhookDeliveriesStore.js +109 -0
- package/dist/core/v4/daemon/triggers/webhookIdempotency.js +72 -0
- package/dist/core/v4/daemon/triggers/webhookRateLimit.js +56 -0
- package/dist/core/v4/daemon/triggers/webhookSpec.js +76 -0
- package/dist/core/v4/daemon/triggers/webhookVerifier.js +128 -0
- package/dist/core/v4/daemon/types.js +15 -0
- package/dist/core/v4/dockerSession.js +461 -0
- package/dist/core/v4/dryRun.js +117 -0
- package/dist/core/v4/failureClassifier.js +779 -0
- package/dist/core/v4/providerFallback.js +35 -2
- package/dist/core/v4/recoveryReport.js +449 -0
- package/dist/core/v4/runtimeToggles.js +214 -0
- package/dist/core/v4/sandboxConfig.js +285 -0
- package/dist/core/v4/sandboxFs.js +316 -0
- package/dist/core/v4/selfimprovement/recoveryStore.js +307 -0
- package/dist/core/v4/selfimprovement/signatureBuilder.js +158 -0
- package/dist/core/v4/subagent/childBuilder.js +391 -0
- package/dist/core/v4/subagent/fanout.js +75 -51
- package/dist/core/v4/subagent/spawnPause.js +191 -0
- package/dist/core/v4/subagent/spawnSubAgent.js +310 -0
- package/dist/core/v4/suggestionCatalog.js +41 -0
- package/dist/core/v4/suggestionEngine.js +210 -0
- package/dist/core/v4/toolRegistry.js +37 -3
- package/dist/core/v4/turnState.js +587 -0
- package/dist/core/v4/update/checkUpdate.js +63 -3
- package/dist/core/v4/update/installMethodDetect.js +115 -0
- package/dist/core/v4/update/registryClient.js +121 -0
- package/dist/core/v4/update/skipState.js +75 -0
- package/dist/core/v4/verifier.js +448 -0
- package/dist/core/version.js +1 -1
- package/dist/moat/plannerGuard.js +29 -0
- package/dist/providers/v4/anthropicAdapter.js +31 -3
- package/dist/providers/v4/chatCompletionsAdapter.js +26 -3
- package/dist/providers/v4/codexResponsesAdapter.js +25 -2
- package/dist/providers/v4/ollamaPromptToolsAdapter.js +57 -2
- package/dist/tools/v4/browser/_observer.js +224 -0
- package/dist/tools/v4/browser/browserBlocker.js +396 -0
- package/dist/tools/v4/browser/browserClick.js +18 -1
- package/dist/tools/v4/browser/browserClose.js +18 -1
- package/dist/tools/v4/browser/browserExtract.js +5 -1
- package/dist/tools/v4/browser/browserFill.js +17 -1
- package/dist/tools/v4/browser/browserGetUrl.js +5 -1
- package/dist/tools/v4/browser/browserNavigate.js +16 -1
- package/dist/tools/v4/browser/browserScreenshot.js +5 -1
- package/dist/tools/v4/browser/browserScroll.js +18 -1
- package/dist/tools/v4/browser/browserType.js +17 -1
- package/dist/tools/v4/browser/captchaCheck.js +5 -1
- package/dist/tools/v4/executeCode.js +1 -0
- package/dist/tools/v4/files/fileCopy.js +56 -2
- package/dist/tools/v4/files/fileDelete.js +38 -1
- package/dist/tools/v4/files/fileList.js +12 -1
- package/dist/tools/v4/files/fileMove.js +59 -2
- package/dist/tools/v4/files/filePatch.js +43 -1
- package/dist/tools/v4/files/fileRead.js +12 -1
- package/dist/tools/v4/files/fileWrite.js +41 -1
- package/dist/tools/v4/index.js +88 -61
- package/dist/tools/v4/memory/memoryAdd.js +14 -0
- package/dist/tools/v4/memory/memoryRemove.js +14 -0
- package/dist/tools/v4/memory/memoryReplace.js +15 -0
- package/dist/tools/v4/memory/sessionSummary.js +12 -0
- package/dist/tools/v4/process/processKill.js +19 -0
- package/dist/tools/v4/process/processList.js +1 -0
- package/dist/tools/v4/process/processLogRead.js +1 -0
- package/dist/tools/v4/process/processSpawn.js +13 -0
- package/dist/tools/v4/process/processWait.js +1 -0
- package/dist/tools/v4/sessions/recallSession.js +1 -0
- package/dist/tools/v4/sessions/sessionList.js +1 -0
- package/dist/tools/v4/sessions/sessionSearch.js +1 -0
- package/dist/tools/v4/skills/lookupToolSchema.js +7 -0
- package/dist/tools/v4/skills/skillManage.js +13 -0
- package/dist/tools/v4/skills/skillView.js +1 -0
- package/dist/tools/v4/skills/skillsList.js +1 -0
- package/dist/tools/v4/subagent/spawnSubAgentTool.js +334 -0
- package/dist/tools/v4/subagent/subagentFanout.js +54 -1
- package/dist/tools/v4/system/aidenSelfUpdate.js +16 -0
- package/dist/tools/v4/system/appClose.js +13 -0
- package/dist/tools/v4/system/appInput.js +13 -0
- package/dist/tools/v4/system/appLaunch.js +13 -0
- package/dist/tools/v4/system/clipboardRead.js +1 -0
- package/dist/tools/v4/system/clipboardWrite.js +14 -0
- package/dist/tools/v4/system/mediaKey.js +12 -0
- package/dist/tools/v4/system/mediaSessions.js +1 -0
- package/dist/tools/v4/system/mediaTransport.js +13 -0
- package/dist/tools/v4/system/naturalEvents.js +1 -0
- package/dist/tools/v4/system/nowPlaying.js +1 -0
- package/dist/tools/v4/system/osProcessList.js +1 -0
- package/dist/tools/v4/system/screenshot.js +1 -0
- package/dist/tools/v4/system/systemInfo.js +1 -0
- package/dist/tools/v4/system/volumeSet.js +17 -0
- package/dist/tools/v4/terminal/shellExec.js +81 -9
- package/dist/tools/v4/web/deepResearch.js +1 -0
- package/dist/tools/v4/web/openUrl.js +1 -0
- package/dist/tools/v4/web/webFetch.js +1 -0
- package/dist/tools/v4/web/webPage.js +1 -0
- package/dist/tools/v4/web/webSearch.js +1 -0
- package/dist/tools/v4/web/youtubeSearch.js +1 -0
- package/package.json +13 -3
package/dist/cli/v4/aidenCLI.js
CHANGED
|
@@ -97,6 +97,13 @@ const sessionManager_1 = require("../../core/v4/sessionManager");
|
|
|
97
97
|
const toolRegistry_1 = require("../../core/v4/toolRegistry");
|
|
98
98
|
const skillLoader_1 = require("../../core/v4/skillLoader");
|
|
99
99
|
const index_1 = require("../../tools/v4/index");
|
|
100
|
+
// v4.6 Phase 1 — spawn_sub_agent: always-on runStore + LLM-callable tool.
|
|
101
|
+
const node_crypto_1 = require("node:crypto");
|
|
102
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
103
|
+
const daemonConfig_1 = require("../../core/v4/daemon/daemonConfig");
|
|
104
|
+
const connection_1 = require("../../core/v4/daemon/db/connection");
|
|
105
|
+
const runStore_1 = require("../../core/v4/daemon/runStore");
|
|
106
|
+
const spawnSubAgentTool_1 = require("../../tools/v4/subagent/spawnSubAgentTool");
|
|
100
107
|
const skillCommands_1 = require("../../core/v4/skillCommands");
|
|
101
108
|
const aidenAgent_1 = require("../../core/v4/aidenAgent");
|
|
102
109
|
const promptBuilder_1 = require("../../core/v4/promptBuilder");
|
|
@@ -207,7 +214,35 @@ function buildAgentFallbackSlots(primaryAdapter, primaryProviderId, primaryModel
|
|
|
207
214
|
// same Groq account at slots 0 and 1).
|
|
208
215
|
return [primarySlot, ...defaults];
|
|
209
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Commander 5.x option-value collector. Use with `.option('--flag <v>',
|
|
219
|
+
* '...', collectArray, [])` so repeated `--flag a --flag b` produces
|
|
220
|
+
* `['a','b']` instead of `'b'`. Commander 5 does NOT support the
|
|
221
|
+
* `<x...>` variadic syntax on options (only positional args).
|
|
222
|
+
*/
|
|
223
|
+
function collectArray(value, previous) {
|
|
224
|
+
if (!Array.isArray(previous))
|
|
225
|
+
return [value];
|
|
226
|
+
return previous.concat([value]);
|
|
227
|
+
}
|
|
210
228
|
async function main(argv, opts = {}) {
|
|
229
|
+
// v4.5 Phase 1 — daemon foundation bootstrap is deferred until the
|
|
230
|
+
// REPL action handler fires (see the default `.action()` below).
|
|
231
|
+
// Subcommands like `trigger add`, `trigger list`, `config get`,
|
|
232
|
+
// `--version`, `--help`, `doctor`, etc. do NOT need the daemon
|
|
233
|
+
// foundation. Booting it for every CLI invocation:
|
|
234
|
+
// 1. Wastes work (HTTP server up + tear down for a one-shot
|
|
235
|
+
// DB-write subcommand).
|
|
236
|
+
// 2. Creates phantom "crash recovery" warnings: the daemon-
|
|
237
|
+
// foundation install/teardown cycle for each subcommand
|
|
238
|
+
// doesn't shut down gracefully (process.exit happens after
|
|
239
|
+
// the action), so the NEXT invocation's evaluateBootState
|
|
240
|
+
// sees a stale daemon_instances row with a missed heartbeat
|
|
241
|
+
// and writes a crash_reports row.
|
|
242
|
+
// 3. Adds latency on cold paths (CLI feels slow when AIDEN_DAEMON=1).
|
|
243
|
+
// The interactive REPL action calls `bootstrapDaemon()` explicitly,
|
|
244
|
+
// which is the only entry point that genuinely benefits from the
|
|
245
|
+
// foundation (long-running session = useful daemon).
|
|
211
246
|
const program = new commander_1.Command();
|
|
212
247
|
program
|
|
213
248
|
.name('aiden')
|
|
@@ -234,6 +269,36 @@ async function main(argv, opts = {}) {
|
|
|
234
269
|
process.stderr.write(`Run 'aiden --help' for available commands.\n`);
|
|
235
270
|
process.exit(2);
|
|
236
271
|
}
|
|
272
|
+
// v4.5 Phase 1 — daemon foundation bootstrap. Lazy-imported so
|
|
273
|
+
// the daemon module's side-effect cost stays at zero when
|
|
274
|
+
// AIDEN_DAEMON is off (better-sqlite3 native binding, chokidar,
|
|
275
|
+
// express, etc. all stay unloaded). Only fires for the REPL
|
|
276
|
+
// path — subcommands (trigger add/list/show/remove/enable/
|
|
277
|
+
// disable/test, config, --version, --help, doctor, …) do NOT
|
|
278
|
+
// touch the daemon foundation.
|
|
279
|
+
// v4.5 Phase 7c — two-phase bootstrap. The daemon FOUNDATION
|
|
280
|
+
// (file watchers, webhook routes, email triggers, cron
|
|
281
|
+
// emitter, dispatcher with placeholder runner, HTTP server)
|
|
282
|
+
// comes up HERE — before buildAgentRuntime, before the setup
|
|
283
|
+
// wizard. This restores the pre-7b guarantee that daemon
|
|
284
|
+
// foundation boots regardless of REPL configuration state
|
|
285
|
+
// (matters for systemd/launchd units booted on fresh machines
|
|
286
|
+
// and for any environment without a provider configured yet).
|
|
287
|
+
//
|
|
288
|
+
// The REAL agent runner gets installed later inside
|
|
289
|
+
// runInteractiveChat() once buildAgentRuntime returns — see
|
|
290
|
+
// `installDaemonAgentBuilder` call there.
|
|
291
|
+
try {
|
|
292
|
+
if (process.env.AIDEN_DAEMON === '1') {
|
|
293
|
+
const { bootstrapDaemonFoundation } = await Promise.resolve().then(() => __importStar(require('../../core/v4/daemon/bootstrap')));
|
|
294
|
+
bootstrapDaemonFoundation();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (e) {
|
|
298
|
+
// Fail-loud but non-fatal — daemon foundation init failure
|
|
299
|
+
// must not block the user from opening a REPL.
|
|
300
|
+
console.error('[daemon] foundation bootstrap failed: ' + (e instanceof Error ? e.message : String(e)));
|
|
301
|
+
}
|
|
237
302
|
// Tier-3.1: surface --no-ui as an env var so downstream modules
|
|
238
303
|
// (which import uiBuild.ts) see the flag without threading it
|
|
239
304
|
// through every call site.
|
|
@@ -318,6 +383,140 @@ async function main(argv, opts = {}) {
|
|
|
318
383
|
}
|
|
319
384
|
await runSkillsSubcommand(action, arg, opts);
|
|
320
385
|
});
|
|
386
|
+
// v4.5 Phase 2 — file watcher trigger management.
|
|
387
|
+
program
|
|
388
|
+
.command('trigger <action> [args...]')
|
|
389
|
+
.description('Manage daemon triggers. Actions: add, list, show, remove, enable, disable, test.')
|
|
390
|
+
// Commander 5.x stores option values directly on the Command
|
|
391
|
+
// instance, so `--name` would clobber Command.prototype.name().
|
|
392
|
+
// Use `--label` instead; we map to the internal `name` arg below.
|
|
393
|
+
.option('--label <label>', 'Trigger label/name (for add).')
|
|
394
|
+
// Commander 5.x does NOT support `<x...>` variadic syntax on
|
|
395
|
+
// OPTIONS (only on positional args) — repeating overwrites
|
|
396
|
+
// instead of appending. Use an explicit collector so multiple
|
|
397
|
+
// --path / --include / --exclude / --event flags accumulate.
|
|
398
|
+
.option('--path <path>', 'Path to watch (for add file). Repeatable.', collectArray, [])
|
|
399
|
+
.option('--include <glob>', 'Include glob pattern. Repeatable.', collectArray, [])
|
|
400
|
+
.option('--exclude <glob>', 'Exclude glob pattern. Repeatable.', collectArray, [])
|
|
401
|
+
.option('--event <type>', 'Event type: add|change|unlink. Repeatable.', collectArray, [])
|
|
402
|
+
.option('--debounce-ms <n>', 'Per-path debounce in ms (default 750).', (v) => Number.parseInt(v, 10))
|
|
403
|
+
.option('--settle-ms <n>', 'Stable-stat settle in ms (default 1000).', (v) => Number.parseInt(v, 10))
|
|
404
|
+
.option('--max-settle-ms <n>', 'Max settle time before giving up (default 30000).', (v) => Number.parseInt(v, 10))
|
|
405
|
+
.option('--max-queue-depth <n>', 'Max per-watcher queue depth (default 100).', (v) => Number.parseInt(v, 10))
|
|
406
|
+
.option('--no-ignore-temp', 'Disable default ignore for editor temps + .git + node_modules.')
|
|
407
|
+
.option('--content-hash', 'Compute sha256 per change (opt-in; slower).')
|
|
408
|
+
.option('--reconcile <policy>', 'skip_existing | process_new_since_last_seen | full_rescan (default skip_existing).')
|
|
409
|
+
.option('--polling', 'Force polling mode (network FS / WSL bind mount).')
|
|
410
|
+
.option('--prompt-template <text>', 'Phase 5 — agent prompt template.')
|
|
411
|
+
.option('--disabled', 'Create trigger in disabled state.')
|
|
412
|
+
// v4.5 Phase 3 — webhook-specific options.
|
|
413
|
+
.option('--hmac <format>', 'Webhook HMAC format: github | gitlab | generic (default generic).')
|
|
414
|
+
.option('--secret <s>', 'Webhook secret. Auto-generated when omitted.')
|
|
415
|
+
.option('--rate-limit <n>', 'Webhook requests/min (default 30).', (v) => Number.parseInt(v, 10))
|
|
416
|
+
.option('--max-body-bytes <n>', 'Webhook max body cap (default 1048576).', (v) => Number.parseInt(v, 10))
|
|
417
|
+
.option('--idempotency-ttl-ms <n>', 'Webhook idempotency TTL (default 3600000).', (v) => Number.parseInt(v, 10))
|
|
418
|
+
.option('--deliver-only', 'Phase 3 stub: accept + log; Phase 5 will dispatch via channel.')
|
|
419
|
+
// v4.5 Phase 4a — email IMAP options.
|
|
420
|
+
.option('--host <host>', 'IMAP host (for add email).')
|
|
421
|
+
.option('--port <n>', 'IMAP port (default 993).', (v) => Number.parseInt(v, 10))
|
|
422
|
+
.option('--user <addr>', 'IMAP user / email address (for add email).')
|
|
423
|
+
.option('--password <pwd>', 'IMAP password (for add email).')
|
|
424
|
+
.option('--no-tls', 'Disable TLS (for add email; default is TLS on).')
|
|
425
|
+
.option('--mailbox <name>', 'IMAP mailbox (default INBOX).')
|
|
426
|
+
.option('--poll-ms <n>', 'Email poll interval ms (default 15000).', (v) => Number.parseInt(v, 10))
|
|
427
|
+
.option('--allow-sender <addr-or-glob>', 'Allowed sender pattern (repeatable, REQUIRED for email).', collectArray, [])
|
|
428
|
+
.option('--allow-subject <regex>', 'Allowed subject regex (repeatable).', collectArray, [])
|
|
429
|
+
.option('--attachment-policy <policy>', 'skip | inline-text | save-to-tmp (default skip).')
|
|
430
|
+
.option('--no-validate', 'Skip IMAP pre-flight connectivity check.')
|
|
431
|
+
.action(async (action, posArgs, cmd) => {
|
|
432
|
+
const { runTriggerSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/trigger')));
|
|
433
|
+
const cliOpts = cmd.opts();
|
|
434
|
+
const argv = {
|
|
435
|
+
name: cliOpts.label,
|
|
436
|
+
paths: cliOpts.path,
|
|
437
|
+
include: cliOpts.include,
|
|
438
|
+
exclude: cliOpts.exclude,
|
|
439
|
+
events: cliOpts.event,
|
|
440
|
+
debounceMs: cliOpts.debounceMs,
|
|
441
|
+
settleMs: cliOpts.settleMs,
|
|
442
|
+
maxSettleMs: cliOpts.maxSettleMs,
|
|
443
|
+
maxQueueDepth: cliOpts.maxQueueDepth,
|
|
444
|
+
noIgnoreTemp: cliOpts.ignoreTemp === false,
|
|
445
|
+
contentHash: cliOpts.contentHash === true,
|
|
446
|
+
reconcile: cliOpts.reconcile,
|
|
447
|
+
polling: cliOpts.polling === true,
|
|
448
|
+
promptTemplate: cliOpts.promptTemplate,
|
|
449
|
+
disabled: cliOpts.disabled === true,
|
|
450
|
+
// v4.5 Phase 3 — webhook options.
|
|
451
|
+
hmac: cliOpts.hmac,
|
|
452
|
+
secret: cliOpts.secret,
|
|
453
|
+
rateLimit: cliOpts.rateLimit,
|
|
454
|
+
maxBodyBytes: cliOpts.maxBodyBytes,
|
|
455
|
+
idempotencyTtlMs: cliOpts.idempotencyTtlMs,
|
|
456
|
+
deliverOnly: cliOpts.deliverOnly === true,
|
|
457
|
+
// v4.5 Phase 4a — email options.
|
|
458
|
+
host: cliOpts.host,
|
|
459
|
+
port: cliOpts.port,
|
|
460
|
+
user: cliOpts.user,
|
|
461
|
+
password: cliOpts.password,
|
|
462
|
+
noTls: cliOpts.tls === false,
|
|
463
|
+
mailbox: cliOpts.mailbox,
|
|
464
|
+
pollMs: cliOpts.pollMs,
|
|
465
|
+
allowSenders: cliOpts.allowSender ?? [],
|
|
466
|
+
allowSubjects: cliOpts.allowSubject ?? [],
|
|
467
|
+
attachmentPolicy: cliOpts.attachmentPolicy,
|
|
468
|
+
noValidate: cliOpts.validate === false,
|
|
469
|
+
};
|
|
470
|
+
const code = await runTriggerSubcommand(action, posArgs ?? [], argv, {
|
|
471
|
+
writeOut: opts.writeOut,
|
|
472
|
+
});
|
|
473
|
+
process.exit(code);
|
|
474
|
+
});
|
|
475
|
+
// v4.5 Phase 4b — daemon supervisor commands.
|
|
476
|
+
program
|
|
477
|
+
.command('daemon <action> [args...]')
|
|
478
|
+
.description('Manage the v4.5 daemon. Actions: install, uninstall, start, stop, restart, status, logs.')
|
|
479
|
+
.action(async (action, posArgs) => {
|
|
480
|
+
const { runDaemonSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/daemon')));
|
|
481
|
+
const code = await runDaemonSubcommand(action, posArgs ?? [], {
|
|
482
|
+
writeOut: opts.writeOut,
|
|
483
|
+
});
|
|
484
|
+
process.exit(code);
|
|
485
|
+
});
|
|
486
|
+
// v4.5 Phase 6 — `aiden cron` top-level surface (mirrors slash command).
|
|
487
|
+
program
|
|
488
|
+
.command('cron <action> [args...]')
|
|
489
|
+
.description('Scheduled jobs. Actions: add, list, show, remove, enable, disable, run, logs.')
|
|
490
|
+
.option('--label <name>', 'job label (alphanumeric/dash/underscore)')
|
|
491
|
+
.option('--schedule <expr>', 'cron expr ("0 9 * * *") / interval ("every 5m") / ISO timestamp')
|
|
492
|
+
.option('--command <cmd>', 'shell command to run')
|
|
493
|
+
.option('--timezone <tz>', 'IANA timezone (default UTC)')
|
|
494
|
+
.option('--misfire-policy <policy>', 'skip_stale | run_once_if_late | catch_up_with_limit | manual_review')
|
|
495
|
+
.option('--prompt-template <tpl>', 'render template instead of running raw command (daemon mode)')
|
|
496
|
+
.option('--deliver-only', 'daemon skips the agent loop on fire')
|
|
497
|
+
.action(async (action, posArgs, cmdObj) => {
|
|
498
|
+
const { runCronSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/cron')));
|
|
499
|
+
const code = await runCronSubcommand(action, posArgs ?? [], cmdObj, {
|
|
500
|
+
writeOut: opts.writeOut,
|
|
501
|
+
});
|
|
502
|
+
process.exit(code);
|
|
503
|
+
});
|
|
504
|
+
// v4.5 Phase 6 — `aiden runs` surface (daemon run history).
|
|
505
|
+
program
|
|
506
|
+
.command('runs <action> [args...]')
|
|
507
|
+
.description('Daemon runs. Actions: list, show <id>, interrupt <id>, stats.')
|
|
508
|
+
.option('--limit <n>', 'list: max rows (default 50)', (v) => Number.parseInt(v, 10))
|
|
509
|
+
.option('--source <src>', 'list: filter by trigger source (file/webhook/email/schedule/manual)')
|
|
510
|
+
.option('--status <s>', 'list: filter by status (queued/running/completed/failed/cancelled/interrupted)')
|
|
511
|
+
.option('--trigger <prefix>', 'list: sessionId prefix (e.g. "trigger:file:<id>:")')
|
|
512
|
+
.option('--include-children', 'list: include sub-agent children (default: top-level only, with per-parent badge)')
|
|
513
|
+
.action(async (action, posArgs, cmdObj) => {
|
|
514
|
+
const { runRunsSubcommand } = await Promise.resolve().then(() => __importStar(require('./commands/runs')));
|
|
515
|
+
const code = await runRunsSubcommand(action, posArgs ?? [], cmdObj, {
|
|
516
|
+
writeOut: opts.writeOut,
|
|
517
|
+
});
|
|
518
|
+
process.exit(code);
|
|
519
|
+
});
|
|
321
520
|
program
|
|
322
521
|
.command('mcp <action>')
|
|
323
522
|
.description('MCP server mode (Phase v4.1-mcp). Actions: serve, status, tools.')
|
|
@@ -432,6 +631,60 @@ async function main(argv, opts = {}) {
|
|
|
432
631
|
async function buildAgentRuntime(cliOpts, opts) {
|
|
433
632
|
const paths = opts.pathsOverride ?? (0, paths_1.resolveAidenPaths)();
|
|
434
633
|
await (0, paths_1.ensureAidenDirsExist)(paths);
|
|
634
|
+
// ── v4.6 Phase 1 — always-on runStore for spawn_sub_agent ──────────────
|
|
635
|
+
//
|
|
636
|
+
// The spawn_sub_agent primitive persists each child run to the runs
|
|
637
|
+
// table via spawned_from_run_id FK. The REPL needs a runStore handle
|
|
638
|
+
// regardless of whether AIDEN_DAEMON=1 or not. SQLite WAL mode
|
|
639
|
+
// (enabled in openDaemonDb) allows REPL + daemon to coexist on the
|
|
640
|
+
// same file without lock contention. The connection.ts module caches
|
|
641
|
+
// per-path, so when daemon foundation has already opened the DB this
|
|
642
|
+
// call returns the same handle. Migration runner is idempotent on
|
|
643
|
+
// already-current schemas.
|
|
644
|
+
const replInstanceId = `repl-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
645
|
+
const replDb = (0, connection_1.openDaemonDb)((0, daemonConfig_1.daemonDbPath)(paths.root));
|
|
646
|
+
// Seed the REPL's daemon_instances row so the FK on runs.instance_id
|
|
647
|
+
// is satisfied. Idempotent under INSERT OR IGNORE — multiple REPL
|
|
648
|
+
// launches reusing the same instance_id (rare with random UUID
|
|
649
|
+
// suffix) silently no-op.
|
|
650
|
+
replDb.prepare(`INSERT OR IGNORE INTO daemon_instances
|
|
651
|
+
(instance_id, pid, hostname, started_at, last_heartbeat, version)
|
|
652
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(replInstanceId, process.pid, node_os_1.default.hostname(), Date.now(), Date.now(), version_1.VERSION);
|
|
653
|
+
const replRunStore = (0, runStore_1.createRunStore)({ db: replDb });
|
|
654
|
+
// v4.6 Phase 3A — operator kill-switch for sub-agent spawning.
|
|
655
|
+
// Initialised as early as possible so any subsequent tool wiring
|
|
656
|
+
// sees the singleton. The marker file lives at
|
|
657
|
+
// `<paths.root>/spawn.paused` and is shared across REPL + daemon
|
|
658
|
+
// + MCP for cross-process coordination. The startup probe (warn
|
|
659
|
+
// operator that pause is active from a prior session) fires
|
|
660
|
+
// later, once `bootLogger` is available — see the
|
|
661
|
+
// `spawnPauseBootStatus` block below.
|
|
662
|
+
const { initSpawnPause } = await Promise.resolve().then(() => __importStar(require('../../core/v4/subagent/spawnPause')));
|
|
663
|
+
const spawnPauseState = initSpawnPause({ aidenHome: paths.root });
|
|
664
|
+
// v4.6 Phase 3b — self-improvement loop. Initialise the durable
|
|
665
|
+
// failure-ledger / recovery-report store against the same
|
|
666
|
+
// daemon.db handle the runStore uses. WAL coexistence: REPL +
|
|
667
|
+
// daemon + MCP all share the same connection-cached handle, so
|
|
668
|
+
// any writes from one runtime are visible to the others. The
|
|
669
|
+
// TCE write-through path inside the agent loop reads through the
|
|
670
|
+
// module-level singleton; initialising here makes spawnSubAgent
|
|
671
|
+
// and daemon-fired turns observe the same persistence.
|
|
672
|
+
const { initRecoveryStore } = await Promise.resolve().then(() => __importStar(require('../../core/v4/selfimprovement/recoveryStore')));
|
|
673
|
+
initRecoveryStore({ db: replDb });
|
|
674
|
+
const spawnPauseBootStatus = spawnPauseState.isPaused()
|
|
675
|
+
? spawnPauseState.status()
|
|
676
|
+
: null;
|
|
677
|
+
// v4.6 Phase 2Q-B — mutable holder for the REPL's current parent
|
|
678
|
+
// run id. ChatSession's `runAgentTurn` writes a row before each
|
|
679
|
+
// turn dispatches and stores the id here (and clears it on
|
|
680
|
+
// completion). The spawn / fanout tool factories below read it
|
|
681
|
+
// through `resolveParentRunId` / `resolveParentSessionId`
|
|
682
|
+
// callbacks so any child spawned mid-turn is linked back to the
|
|
683
|
+
// live REPL parent row via `runs.spawned_from_run_id`.
|
|
684
|
+
const replParentRunRef = {
|
|
685
|
+
runId: null,
|
|
686
|
+
sessionId: null,
|
|
687
|
+
};
|
|
435
688
|
// Phase 16c.2: load `paths.envFile` (the aiden-managed `.env` that
|
|
436
689
|
// `setupWizard.ts::upsertEnvVar` writes to) into `process.env` BEFORE
|
|
437
690
|
// any provider resolution. The bug: setup wrote keys to this file but
|
|
@@ -475,6 +728,21 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
475
728
|
}
|
|
476
729
|
const config = new config_1.ConfigManager(paths);
|
|
477
730
|
await config.load();
|
|
731
|
+
// v4.5 Phase 8a — initialise the runtimeToggles singleton with a
|
|
732
|
+
// ConfigManager seam so /sandbox /tce /browser-depth flips persist
|
|
733
|
+
// to config.yaml. Core modules (sandboxConfig, turnState,
|
|
734
|
+
// browserState) consult this singleton; precedence is
|
|
735
|
+
// env > config.yaml > default per Q-P8a-1(a).
|
|
736
|
+
{
|
|
737
|
+
const { initRuntimeToggles } = await Promise.resolve().then(() => __importStar(require('../../core/v4/runtimeToggles')));
|
|
738
|
+
initRuntimeToggles({
|
|
739
|
+
configRead: (key) => config.getValue(key),
|
|
740
|
+
configWriteAndSave: async (key, value) => {
|
|
741
|
+
config.set(key, value);
|
|
742
|
+
await config.save();
|
|
743
|
+
},
|
|
744
|
+
});
|
|
745
|
+
}
|
|
478
746
|
// Phase 30.2 — fresh-user UX. Detection extends the old
|
|
479
747
|
// `isFreshInstall`-only gate so we cover three new failure modes:
|
|
480
748
|
// 1. fresh user with no env / no OAuth / no config → wizard fires
|
|
@@ -504,42 +772,63 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
504
772
|
// consumed when building the adapter.
|
|
505
773
|
let exploreMode = false;
|
|
506
774
|
if (wizardNeeded) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
process.stdout.write('config.yaml is empty — let\'s pick a provider that matches.\n');
|
|
523
|
-
}
|
|
524
|
-
process.stdout.write('Launching setup wizard…\n\n');
|
|
525
|
-
const result = await (0, setupWizard_1.runSetupWizard)({ paths });
|
|
526
|
-
// Phase 30.2.1: three exit states.
|
|
527
|
-
if (result.status === 'exited') {
|
|
528
|
-
// Recovery option [5] — clean exit, no REPL.
|
|
529
|
-
process.exit(0);
|
|
530
|
-
}
|
|
531
|
-
if (result.status === 'skipped') {
|
|
532
|
-
// Recovery option [4] "explore mode" OR Ctrl+C cancellation.
|
|
533
|
-
// Boot continues into the REPL with a NullAdapter; chat is
|
|
534
|
-
// intercepted by ChatSession, slash commands work normally.
|
|
535
|
-
// Flagged here and consumed below where the adapter is built.
|
|
775
|
+
// v4.5 Phase 7c — TTY guard. The wizard uses inquirer prompts
|
|
776
|
+
// which block on stdin. When stdin is NOT a TTY (systemd unit
|
|
777
|
+
// start, launchd run, piped invocation, CI), there's no user
|
|
778
|
+
// to answer, so the wizard would hang forever — which in turn
|
|
779
|
+
// blocks the rest of buildAgentRuntime AND any post-build
|
|
780
|
+
// daemon wiring. Bail into exploreMode instead so the REPL
|
|
781
|
+
// boots with a NullAdapter; the daemon foundation (started at
|
|
782
|
+
// top of REPL action handler) keeps serving trigger rails with
|
|
783
|
+
// the placeholder runner. The operator runs `aiden` interactively
|
|
784
|
+
// once to finish configuration; from then on the real-agent
|
|
785
|
+
// runner installs on every subsequent boot.
|
|
786
|
+
if (!process.stdin.isTTY) {
|
|
787
|
+
process.stderr.write('[setup] stdin is not a TTY — skipping interactive wizard. ' +
|
|
788
|
+
'Booting in explore mode with NullAdapter. ' +
|
|
789
|
+
'Run `aiden` from a terminal once to configure a provider.\n');
|
|
536
790
|
exploreMode = true;
|
|
791
|
+
// Skip the wizard block entirely; fall through to adapter
|
|
792
|
+
// build with exploreMode=true so a NullAdapter is used.
|
|
537
793
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
794
|
+
else {
|
|
795
|
+
if (!detection.hasAnyProvider) {
|
|
796
|
+
// Truly empty: no env, no OAuth, no Ollama, no inline config.
|
|
797
|
+
process.stdout.write(`\n${(0, providerDetection_1.summarizeDetection)(detection)}\n`);
|
|
798
|
+
}
|
|
799
|
+
else if (configuredProviderBroken) {
|
|
800
|
+
// Config points at a provider we can't credential-resolve.
|
|
801
|
+
process.stdout.write(`\nConfigured provider '${detection.configProvider}' has no usable credentials ` +
|
|
802
|
+
`at ${node_path_1.default.join(paths.root, 'auth', `${detection.configProvider}.json`)}.\n`);
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
// Detected something (env / oauth / ollama) but config.yaml is
|
|
806
|
+
// missing or empty — DEFAULT_CONFIG would route to anthropic and
|
|
807
|
+
// the resolver would fail. Surface the detection so the user
|
|
808
|
+
// sees what we found, then walk them through proper setup.
|
|
809
|
+
process.stdout.write(`\n${(0, providerDetection_1.summarizeDetection)(detection)}\n`);
|
|
810
|
+
process.stdout.write('config.yaml is empty — let\'s pick a provider that matches.\n');
|
|
811
|
+
}
|
|
812
|
+
process.stdout.write('Launching setup wizard…\n\n');
|
|
813
|
+
const result = await (0, setupWizard_1.runSetupWizard)({ paths });
|
|
814
|
+
// Phase 30.2.1: three exit states.
|
|
815
|
+
if (result.status === 'exited') {
|
|
816
|
+
// Recovery option [5] — clean exit, no REPL.
|
|
817
|
+
process.exit(0);
|
|
818
|
+
}
|
|
819
|
+
if (result.status === 'skipped') {
|
|
820
|
+
// Recovery option [4] "explore mode" OR Ctrl+C cancellation.
|
|
821
|
+
// Boot continues into the REPL with a NullAdapter; chat is
|
|
822
|
+
// intercepted by ChatSession, slash commands work normally.
|
|
823
|
+
// Flagged here and consumed below where the adapter is built.
|
|
824
|
+
exploreMode = true;
|
|
825
|
+
}
|
|
826
|
+
// 'configured' (or 'skipped' — we still want the env/.env reload
|
|
827
|
+
// for slash commands like /providers that read fresh state) →
|
|
828
|
+
// re-load both so the resolver sees what the wizard wrote.
|
|
829
|
+
(0, envSources_1.loadAidenEnvFile)(paths.envFile);
|
|
830
|
+
await config.load();
|
|
831
|
+
} // end of TTY branch
|
|
543
832
|
}
|
|
544
833
|
// Phase v4.1.2-bug1: boot model selection now consults the priority-
|
|
545
834
|
// list auto-picker (cli/v4/providerBootSelector.ts) instead of
|
|
@@ -726,35 +1015,28 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
726
1015
|
else
|
|
727
1016
|
display.dim(ln.text);
|
|
728
1017
|
}
|
|
1018
|
+
// v4.5 TUI polish — blank line so the plugin boot card breathes
|
|
1019
|
+
// before the AIDEN banner stamps in below.
|
|
1020
|
+
display.write('\n');
|
|
729
1021
|
// Phase 16g: surface the SOUL.md upgrade notice once on boot (only
|
|
730
1022
|
// when set — for users with edited SOUL.md that would have been
|
|
731
1023
|
// silently overwritten by the upgrade).
|
|
732
1024
|
if (soulNotice) {
|
|
733
1025
|
display.dim(`[soul] ${soulNotice}`);
|
|
734
1026
|
}
|
|
735
|
-
//
|
|
736
|
-
//
|
|
737
|
-
//
|
|
738
|
-
//
|
|
1027
|
+
// v4.5 update system — the old setImmediate dim/warn one-liner
|
|
1028
|
+
// here was superseded by the interactive boot prompt rendered
|
|
1029
|
+
// later inside `chatSession.run()::maybeShowBootUpdatePrompt`.
|
|
1030
|
+
// Single surface for update notification: the boxed prompt.
|
|
1031
|
+
// Pre-warm the registry probe so the cache is fresh by the time
|
|
1032
|
+
// the prompt asks — same non-blocking pattern, same opt-out
|
|
1033
|
+
// semantics, no user-visible output from this call.
|
|
739
1034
|
setImmediate(async () => {
|
|
740
1035
|
try {
|
|
741
|
-
const { checkForUpdate
|
|
1036
|
+
const { checkForUpdate } = await Promise.resolve().then(() => __importStar(require('../../core/v4/update/checkUpdate')));
|
|
742
1037
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
743
1038
|
const pkg = require('../../package.json');
|
|
744
|
-
|
|
745
|
-
paths,
|
|
746
|
-
installedVersion: pkg.version,
|
|
747
|
-
});
|
|
748
|
-
const line = formatUpdateLine(status);
|
|
749
|
-
if (line) {
|
|
750
|
-
// Phase 20 Task 6: louder surfacing on first-ever boot when the
|
|
751
|
-
// installed package is already behind. Subsequent boots stay
|
|
752
|
-
// low-key (dim) — users have seen the line before.
|
|
753
|
-
if (status.firstRun && status.updateAvailable)
|
|
754
|
-
display.warn(line);
|
|
755
|
-
else
|
|
756
|
-
display.dim(line);
|
|
757
|
-
}
|
|
1039
|
+
await checkForUpdate({ paths, installedVersion: pkg.version });
|
|
758
1040
|
}
|
|
759
1041
|
catch {
|
|
760
1042
|
/* silent — update check is best-effort */
|
|
@@ -902,6 +1184,11 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
902
1184
|
const recallSessionHealth = new subsystemHealth_1.SubsystemHealthTracker('recall-session');
|
|
903
1185
|
subsystemHealthRegistry.register('recall-session', () => recallSessionHealth.snapshot());
|
|
904
1186
|
const skillTeacher = new skillTeacher_1.SkillTeacher(skillLoader, skillManageProxy, skillTeacherTier, undefined, (name) => toolRegistry.get(name), skillTeacherHealth);
|
|
1187
|
+
// v4.1.6 Polish 2 — late-wire the SkillTeacher reference so the
|
|
1188
|
+
// post-render `handleSkillProposal` flow can persist accepted
|
|
1189
|
+
// proposals. CliCallbacks was constructed earlier (line ~906) —
|
|
1190
|
+
// before SkillTeacher existed — so we set it now.
|
|
1191
|
+
callbacks.setSkillTeacher(skillTeacher);
|
|
905
1192
|
// ── Tool executor with full Phase 9 + 10 context ─────────────────────
|
|
906
1193
|
const toolExecutor = toolRegistry.buildExecutor({
|
|
907
1194
|
cwd: process.cwd(),
|
|
@@ -921,6 +1208,14 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
921
1208
|
return typeof v === 'boolean' ? v : undefined;
|
|
922
1209
|
};
|
|
923
1210
|
const resolveToolset = (name) => toolRegistry.get(name)?.toolset;
|
|
1211
|
+
// v4.2 Phase 4 — checkpoint/restore mutability resolver. The agent's
|
|
1212
|
+
// Phase 4 hook calls this before dispatching each tool to decide
|
|
1213
|
+
// whether to flag the live checkpoint as having mutated state. Same
|
|
1214
|
+
// registry source as resolveToolset; closure captures the live
|
|
1215
|
+
// registry reference so newly-registered tools are seen. Unknown
|
|
1216
|
+
// tools return undefined → agent treats them as non-mutating (no
|
|
1217
|
+
// checkpoint flag); plugin authors must declare `mutates` honestly.
|
|
1218
|
+
const resolveMutates = (name) => toolRegistry.get(name)?.mutates;
|
|
924
1219
|
// ── Phase 16b.4: assemble system-prompt context ─────────────────────
|
|
925
1220
|
// PromptBuilder needs SOUL.md (read at build time from `paths.soulMd`),
|
|
926
1221
|
// a frozen MemorySnapshot (loaded once at boot — same lifecycle as
|
|
@@ -1005,7 +1300,11 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1005
1300
|
// ── Build agent with all moat layers attached ────────────────────────
|
|
1006
1301
|
const agent = new aidenAgent_1.AidenAgent({
|
|
1007
1302
|
provider: adapter,
|
|
1008
|
-
tools
|
|
1303
|
+
// v4.6 Phase 1 — 'repl' context filter excludes tools tagged
|
|
1304
|
+
// daemon-only (none today) and INCLUDES tools tagged repl-only
|
|
1305
|
+
// (e.g. spawn_sub_agent, registered after this line). Tools with
|
|
1306
|
+
// no `contexts` field default to visible in both contexts.
|
|
1307
|
+
tools: toolRegistry.getSchemas(undefined, 'repl'),
|
|
1009
1308
|
toolExecutor,
|
|
1010
1309
|
maxTurns: config.getValue('agent.max_turns', 90),
|
|
1011
1310
|
auxiliaryClient,
|
|
@@ -1034,6 +1333,22 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1034
1333
|
skillOutcomeTracker.onTool(call, phase, result);
|
|
1035
1334
|
}
|
|
1036
1335
|
catch { /* telemetry must not break the turn */ }
|
|
1336
|
+
// v4.5 Phase 8b — pre-tool-call contextual suggestion. Fires
|
|
1337
|
+
// when the call would benefit from a v4.4/v4.5 subsystem the
|
|
1338
|
+
// user currently has off. Engine handles dismissal + budget;
|
|
1339
|
+
// we just render the tip and tell the engine it was shown.
|
|
1340
|
+
if (phase === 'before') {
|
|
1341
|
+
try {
|
|
1342
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1343
|
+
const { getSuggestionEngine } = require('../../core/v4/suggestionEngine');
|
|
1344
|
+
const tip = getSuggestionEngine().checkToolCall(call);
|
|
1345
|
+
if (tip) {
|
|
1346
|
+
display.dim(tip.message);
|
|
1347
|
+
getSuggestionEngine().recordFired(tip.slot);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
catch { /* never let a suggestion crash a tool call */ }
|
|
1351
|
+
}
|
|
1037
1352
|
callbacks.onToolCall?.(call, phase, result);
|
|
1038
1353
|
},
|
|
1039
1354
|
onCompression: callbacks.onCompression,
|
|
@@ -1042,6 +1357,7 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1042
1357
|
skillTeacherCallbacks: { promptUser: callbacks.promptSkillProposal },
|
|
1043
1358
|
resolveVerifiedFlag,
|
|
1044
1359
|
resolveToolset,
|
|
1360
|
+
resolveMutates,
|
|
1045
1361
|
providerId,
|
|
1046
1362
|
modelId,
|
|
1047
1363
|
// Phase 16b.4: wire PromptBuilder so SOUL.md actually reaches the LLM.
|
|
@@ -1113,6 +1429,28 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1113
1429
|
memoryManager.onMutation((file) => {
|
|
1114
1430
|
agent.markMemoryDirty(file === 'user' ? 'user' : 'memory');
|
|
1115
1431
|
});
|
|
1432
|
+
// v4.5 Phase 7b — daemon agent builder. Captures the references
|
|
1433
|
+
// above so the dispatcher can construct a fresh AidenAgent per
|
|
1434
|
+
// daemon-claimed trigger. Strategy B (closure capture) — REPL
|
|
1435
|
+
// construction stays untouched; we just expose the builder on
|
|
1436
|
+
// the returned AgentRuntime so runInteractiveChat can pass it to
|
|
1437
|
+
// bootstrapDaemon().
|
|
1438
|
+
const { buildDaemonAgentBuilder } = await Promise.resolve().then(() => __importStar(require('./daemonAgentBuilder')));
|
|
1439
|
+
const daemonAgentBuilder = buildDaemonAgentBuilder({
|
|
1440
|
+
paths,
|
|
1441
|
+
resolver,
|
|
1442
|
+
fallbackAdapter: adapter,
|
|
1443
|
+
toolRegistry,
|
|
1444
|
+
toolExecutor,
|
|
1445
|
+
auxiliaryClient,
|
|
1446
|
+
promptBuilder,
|
|
1447
|
+
promptBuilderOptions,
|
|
1448
|
+
memoryManager,
|
|
1449
|
+
resolveVerifiedFlag,
|
|
1450
|
+
resolveToolset,
|
|
1451
|
+
resolveMutates,
|
|
1452
|
+
maxTurns: config.getValue('agent.max_turns', 90),
|
|
1453
|
+
});
|
|
1116
1454
|
// Phase v4.1.2 alive-core: SOUL.md file watcher. Best-effort —
|
|
1117
1455
|
// some filesystems (network mounts, certain WSL configs) don't
|
|
1118
1456
|
// support fs.watch reliably. We try to attach; if it fails, the
|
|
@@ -1154,6 +1492,21 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1154
1492
|
// Wire the gateway singleton's logger BEFORE registering its processor
|
|
1155
1493
|
// so register / unregister channel events are scoped correctly.
|
|
1156
1494
|
gateway_1.gateway.attachLogger(bootLogger.child('gateway'));
|
|
1495
|
+
// v4.6 Phase 3A — startup probe for the spawn-pause kill-switch.
|
|
1496
|
+
// The state was initialised early (line ~740) before tool wiring.
|
|
1497
|
+
// Now that bootLogger exists, emit a visible warning so an
|
|
1498
|
+
// operator who forgot they paused in a prior session learns
|
|
1499
|
+
// immediately rather than puzzling at silent rejected fanouts.
|
|
1500
|
+
if (spawnPauseBootStatus) {
|
|
1501
|
+
const s = spawnPauseBootStatus;
|
|
1502
|
+
const reasonSuffix = s.reason ? ` (reason: ${s.reason})` : '';
|
|
1503
|
+
bootLogger.warn(`spawn_sub_agent / subagent_fanout are PAUSED${reasonSuffix}. ` +
|
|
1504
|
+
'Run /spawn-pause off to resume.', {
|
|
1505
|
+
pausedAt: s.pausedAt ?? null,
|
|
1506
|
+
pausedBy: s.pausedBy ?? null,
|
|
1507
|
+
durationMs: s.durationMs ?? null,
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1157
1510
|
// ── Phase v4.1-subagent.1 — replace subagent_fanout stub with wired version
|
|
1158
1511
|
//
|
|
1159
1512
|
// tools/v4/index.ts registers a stub at boot so the schema is visible
|
|
@@ -1170,10 +1523,45 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1170
1523
|
// shared subsystems (registry, skillLoader, paths, memoryManager,
|
|
1171
1524
|
// promptBuilder, promptBuilderOptions) are read-only and pass by
|
|
1172
1525
|
// reference.
|
|
1526
|
+
// v4.6 Phase 2Q-A — `runFanout` routes each child through
|
|
1527
|
+
// `spawnSubAgent`. `spawnDeps` mirrors the deps the
|
|
1528
|
+
// `makeSpawnSubAgentTool` factory accepts (see registration below
|
|
1529
|
+
// this block). The legacy per-call `runChild` closure that lived
|
|
1530
|
+
// here pre-2R has been deleted; the primitive owns child
|
|
1531
|
+
// construction now.
|
|
1173
1532
|
toolRegistry.register((0, index_1.makeSubagentFanoutTool)({
|
|
1174
1533
|
logger: bootLogger.child('subagent'),
|
|
1175
1534
|
resolveActiveModel: () => ({ providerId, modelId }),
|
|
1176
1535
|
aggregatorAdapter: adapter,
|
|
1536
|
+
spawnDeps: {
|
|
1537
|
+
toolRegistry,
|
|
1538
|
+
parentToolContext: {
|
|
1539
|
+
cwd: process.cwd(),
|
|
1540
|
+
paths,
|
|
1541
|
+
sessions: sessionManager,
|
|
1542
|
+
memory: memoryManager,
|
|
1543
|
+
memoryGuard,
|
|
1544
|
+
ssrfProtection,
|
|
1545
|
+
tirithScanner,
|
|
1546
|
+
skillLoader,
|
|
1547
|
+
},
|
|
1548
|
+
parentProvider: adapter,
|
|
1549
|
+
parentProviderId: providerId,
|
|
1550
|
+
parentModelId: modelId,
|
|
1551
|
+
resolveVerifiedFlag,
|
|
1552
|
+
resolveToolset,
|
|
1553
|
+
resolveMutates,
|
|
1554
|
+
runStore: replRunStore,
|
|
1555
|
+
instanceId: replInstanceId,
|
|
1556
|
+
logger: bootLogger.child('subagent'),
|
|
1557
|
+
},
|
|
1558
|
+
// v4.6 Phase 2Q-B — REPL parent-run wiring. Reads the shared
|
|
1559
|
+
// `replParentRunRef` mutated by `ChatSession.runAgentTurn` so
|
|
1560
|
+
// fanout children get `spawned_from_run_id` populated. Returns
|
|
1561
|
+
// undefined between turns (ref cleared post-turn), matching the
|
|
1562
|
+
// pre-2Q-B behaviour for slash-command-triggered spawns.
|
|
1563
|
+
resolveParentRunId: () => replParentRunRef.runId ?? undefined,
|
|
1564
|
+
resolveParentSessionId: () => replParentRunRef.sessionId ?? undefined,
|
|
1177
1565
|
resolveProviders: () => {
|
|
1178
1566
|
// When the parent uses FallbackAdapter, expose every key-present
|
|
1179
1567
|
// slot's (providerId, modelId) so rotation can spread children
|
|
@@ -1194,110 +1582,68 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1194
1582
|
}
|
|
1195
1583
|
return [{ providerId, modelId }];
|
|
1196
1584
|
},
|
|
1197
|
-
runChild: async (childOpts) => {
|
|
1198
|
-
// Per-child context: paths / skillLoader / memoryManager / processes
|
|
1199
|
-
// are SAFE to share (read-only or per-call by design). The approval
|
|
1200
|
-
// engine is intentionally OMITTED — N children competing for one
|
|
1201
|
-
// stdin REPL would deadlock.
|
|
1202
|
-
const childCtx = {
|
|
1203
|
-
cwd: process.cwd(),
|
|
1204
|
-
paths,
|
|
1205
|
-
sessions: sessionManager,
|
|
1206
|
-
memory: memoryManager,
|
|
1207
|
-
skillLoader,
|
|
1208
|
-
// approvalEngine, ssrfProtection, tirithScanner, memoryGuard:
|
|
1209
|
-
// SSRF + Tirith would be safe to share but adding them now
|
|
1210
|
-
// expands the per-child surface; keep lean for v4.1-subagent.1
|
|
1211
|
-
// and revisit when fanout actually exercises network or shell
|
|
1212
|
-
// tools (gated by ALLOW_DESTRUCTIVE).
|
|
1213
|
-
};
|
|
1214
|
-
// Filter the tool surface. Default-safe: read-only tools only.
|
|
1215
|
-
// AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE=1 mirrors the MCP env from
|
|
1216
|
-
// v4.1-mcp — predictable, env-driven.
|
|
1217
|
-
const allowDestructive = process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === '1' ||
|
|
1218
|
-
process.env.AIDEN_SUBAGENT_ALLOW_DESTRUCTIVE === 'true';
|
|
1219
|
-
const childToolNames = [];
|
|
1220
|
-
for (const name of toolRegistry.list()) {
|
|
1221
|
-
const h = toolRegistry.get(name);
|
|
1222
|
-
if (!h)
|
|
1223
|
-
continue;
|
|
1224
|
-
if (h.mutates && !allowDestructive)
|
|
1225
|
-
continue;
|
|
1226
|
-
// Avoid recursive fanout this phase — children cannot spawn
|
|
1227
|
-
// their own children. Recursion was capped at depth 1 by
|
|
1228
|
-
// default in prior multi-agent systems for the same reason;
|
|
1229
|
-
// v3 starved nested spawns.
|
|
1230
|
-
if (name === 'subagent_fanout')
|
|
1231
|
-
continue;
|
|
1232
|
-
childToolNames.push(name);
|
|
1233
|
-
}
|
|
1234
|
-
const childExecutor = toolRegistry.buildExecutor(childCtx);
|
|
1235
|
-
const childTools = childToolNames
|
|
1236
|
-
.map((n) => toolRegistry.get(n)?.schema)
|
|
1237
|
-
.filter((s) => !!s);
|
|
1238
|
-
// Provider isolation: clone the FallbackAdapter so per-child
|
|
1239
|
-
// rate-limit state doesn't pollute the parent or siblings.
|
|
1240
|
-
// Non-Fallback adapters are stateless by spec (providers/v4/
|
|
1241
|
-
// types.ts:190) so direct reuse is safe.
|
|
1242
|
-
const childProvider = adapter instanceof providerFallback_1.FallbackAdapter
|
|
1243
|
-
? adapter.clone()
|
|
1244
|
-
: adapter;
|
|
1245
|
-
// Build per-child AidenAgent. Skip the moat layers (PlannerGuard,
|
|
1246
|
-
// HonestyEnforcement, SkillTeacher, SkillEnforcementTracker) —
|
|
1247
|
-
// they're parent-loop concerns and add cost without value at the
|
|
1248
|
-
// child scale. Skip promptBuilder too: children get a SHORT
|
|
1249
|
-
// system prompt (brief identity + role) instead of the parent's
|
|
1250
|
-
// full SOUL.md + 72-skills inventory + memory snapshot. The
|
|
1251
|
-
// tradeoff is deliberate — children answer the GOAL, not "be
|
|
1252
|
-
// Aiden". With the full prompt, trivial queries take 30s+ for
|
|
1253
|
-
// children to generate verbose self-introductions; the lean
|
|
1254
|
-
// child prompt brings n=2 trivial fanouts under 12s. Parent
|
|
1255
|
-
// should pass any context children genuinely need via the
|
|
1256
|
-
// `query` / `tasks[].context` argument.
|
|
1257
|
-
const child = new aidenAgent_1.AidenAgent({
|
|
1258
|
-
provider: childProvider,
|
|
1259
|
-
tools: childTools,
|
|
1260
|
-
toolExecutor: childExecutor,
|
|
1261
|
-
maxTurns: childOpts.maxIterations,
|
|
1262
|
-
providerId: childOpts.provider.providerId,
|
|
1263
|
-
modelId: childOpts.provider.modelId,
|
|
1264
|
-
// No promptBuilder — childSystemPrompt prepended manually below.
|
|
1265
|
-
// No fallback strategy — child failures bubble up to the
|
|
1266
|
-
// orchestrator, which surfaces them in the result envelope.
|
|
1267
|
-
});
|
|
1268
|
-
// Honour the abort signal — if the parent aborts mid-call (or the
|
|
1269
|
-
// per-child timeout fires), short-circuit before dispatching to
|
|
1270
|
-
// the provider. AidenAgent doesn't take an AbortSignal directly;
|
|
1271
|
-
// the AbortController plumbing through fetch is the
|
|
1272
|
-
// v4.1-subagent.2 / v4.2 hardening pass. Pre-check here for the
|
|
1273
|
-
// synchronous path.
|
|
1274
|
-
if (childOpts.signal.aborted) {
|
|
1275
|
-
throw new Error('aborted before dispatch');
|
|
1276
|
-
}
|
|
1277
|
-
// Brief, role-aware system prompt — drops 5KB+ of Aiden identity
|
|
1278
|
-
// boilerplate that would otherwise inflate every child to 30s+
|
|
1279
|
-
// wall-clock for a trivial query. The parent agent retains the
|
|
1280
|
-
// full prompt when it's the orchestrator; children answer the
|
|
1281
|
-
// goal directly.
|
|
1282
|
-
const roleLine = childOpts.role
|
|
1283
|
-
? `Role: ${childOpts.role}. `
|
|
1284
|
-
: '';
|
|
1285
|
-
const childSystemPrompt = `You are one of ${childOpts.index >= 0 ? 'N' : '?'} parallel subagents. ` +
|
|
1286
|
-
`${roleLine}Answer the user's request concisely. Use available tools when ` +
|
|
1287
|
-
`the answer requires real-world information you don't have memorized.`;
|
|
1288
|
-
const history = [
|
|
1289
|
-
{ role: 'system', content: childSystemPrompt },
|
|
1290
|
-
{ role: 'user', content: childOpts.prompt },
|
|
1291
|
-
];
|
|
1292
|
-
const result = await child.runConversation(history);
|
|
1293
|
-
return result.finalContent;
|
|
1294
|
-
},
|
|
1295
1585
|
}));
|
|
1296
1586
|
bootLogger.child('subagent').info('subagent_fanout: wired (replaces stub)', {
|
|
1297
1587
|
providerId,
|
|
1298
1588
|
modelId,
|
|
1299
1589
|
fallback: adapter instanceof providerFallback_1.FallbackAdapter ? 'FallbackAdapter' : 'direct',
|
|
1300
1590
|
});
|
|
1591
|
+
// ── v4.6 Phase 1 — register spawn_sub_agent (REPL only) ────────────────
|
|
1592
|
+
//
|
|
1593
|
+
// The new single-child synchronous primitive. Coexists with
|
|
1594
|
+
// subagent_fanout (Q9 — additive in Phase 1; Phase 2 will refactor
|
|
1595
|
+
// fanout to call this primitive N times).
|
|
1596
|
+
//
|
|
1597
|
+
// Wired here, AFTER `agent` and `toolExecutor` are in scope, because
|
|
1598
|
+
// the child builder needs the parent agent's reference (to read
|
|
1599
|
+
// `getCurrentSignal()` at dispatch time per the agent-instance signal
|
|
1600
|
+
// pattern) and the parent's tool context for ssrf/tirith/memory/etc.
|
|
1601
|
+
//
|
|
1602
|
+
// Deliberately NOT registered in cli/v4/daemonAgentBuilder.ts —
|
|
1603
|
+
// daemon-fired agents don't expose spawn_sub_agent in their tool
|
|
1604
|
+
// catalog (Q6 lock).
|
|
1605
|
+
toolRegistry.register((0, spawnSubAgentTool_1.makeSpawnSubAgentTool)({
|
|
1606
|
+
parentAgent: agent,
|
|
1607
|
+
toolRegistry,
|
|
1608
|
+
parentToolContext: {
|
|
1609
|
+
cwd: process.cwd(),
|
|
1610
|
+
paths,
|
|
1611
|
+
sessions: sessionManager,
|
|
1612
|
+
memory: memoryManager,
|
|
1613
|
+
memoryGuard,
|
|
1614
|
+
// approvalEngine intentionally OMITTED — the child builder
|
|
1615
|
+
// constructs its own auto-deny ApprovalEngine. Listing it here
|
|
1616
|
+
// would be ignored (childBuilder overrides via spread), but
|
|
1617
|
+
// keeping it out makes the intent explicit.
|
|
1618
|
+
ssrfProtection,
|
|
1619
|
+
tirithScanner,
|
|
1620
|
+
skillLoader,
|
|
1621
|
+
},
|
|
1622
|
+
parentProvider: adapter,
|
|
1623
|
+
parentProviderId: providerId,
|
|
1624
|
+
parentModelId: modelId,
|
|
1625
|
+
resolveVerifiedFlag,
|
|
1626
|
+
resolveToolset,
|
|
1627
|
+
resolveMutates,
|
|
1628
|
+
runStore: replRunStore,
|
|
1629
|
+
instanceId: replInstanceId,
|
|
1630
|
+
// v4.6 Phase 2Q-B — REPL parent-run wiring. Reads the same
|
|
1631
|
+
// shared `replParentRunRef` the fanout factory above uses;
|
|
1632
|
+
// ChatSession.runAgentTurn populates it before each turn
|
|
1633
|
+
// dispatches. Returns undefined between turns so spawns from
|
|
1634
|
+
// slash-command handlers stay top-level (consistent with
|
|
1635
|
+
// pre-2Q-B observable behaviour).
|
|
1636
|
+
resolveParentRunId: () => replParentRunRef.runId ?? undefined,
|
|
1637
|
+
resolveParentSessionId: () => replParentRunRef.sessionId ?? undefined,
|
|
1638
|
+
// v4.6 Phase 1 observability — info-level traces for spec at
|
|
1639
|
+
// invocation, child-tools count, completion, and per-tool-call
|
|
1640
|
+
// run_events on the child's runs row.
|
|
1641
|
+
logger: bootLogger.child('subagent'),
|
|
1642
|
+
}));
|
|
1643
|
+
bootLogger.child('subagent').info('spawn_sub_agent: wired (REPL only)', {
|
|
1644
|
+
instanceId: replInstanceId,
|
|
1645
|
+
dbPath: (0, daemonConfig_1.daemonDbPath)(paths.root),
|
|
1646
|
+
});
|
|
1301
1647
|
// ── Phase v4.1-2.1: gateway message processor ────────────────────
|
|
1302
1648
|
//
|
|
1303
1649
|
// Channel adapters call `gateway.routeMessage(...)` for every inbound
|
|
@@ -1489,10 +1835,40 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1489
1835
|
pluginLoader,
|
|
1490
1836
|
exploreMode,
|
|
1491
1837
|
channelManager,
|
|
1838
|
+
daemonAgentBuilder,
|
|
1839
|
+
// v4.6 Phase 2Q-B — REPL parent-run wiring.
|
|
1840
|
+
replRunStore,
|
|
1841
|
+
replInstanceId,
|
|
1842
|
+
replParentRunRef,
|
|
1492
1843
|
};
|
|
1493
1844
|
}
|
|
1494
1845
|
async function runInteractiveChat(cliOpts, opts) {
|
|
1495
1846
|
const runtime = await buildAgentRuntime(cliOpts, opts);
|
|
1847
|
+
// v4.5 Phase 7c — install the REAL agent runner now that the
|
|
1848
|
+
// REPL agent is built. The daemon foundation already came up
|
|
1849
|
+
// earlier (top of REPL action handler) with the Phase 5a
|
|
1850
|
+
// placeholder runner serving claims. This call atomically swaps
|
|
1851
|
+
// the dispatcher's runner from placeholder → real; the next
|
|
1852
|
+
// claim uses `runtime.daemonAgentBuilder` to construct a real
|
|
1853
|
+
// AidenAgent per fire.
|
|
1854
|
+
try {
|
|
1855
|
+
if (process.env.AIDEN_DAEMON === '1') {
|
|
1856
|
+
const { getDaemonHandle, installDaemonAgentBuilder } = await Promise.resolve().then(() => __importStar(require('../../core/v4/daemon/bootstrap')));
|
|
1857
|
+
const handle = getDaemonHandle();
|
|
1858
|
+
if (handle && handle.active) {
|
|
1859
|
+
const ok = installDaemonAgentBuilder(handle, runtime.daemonAgentBuilder, { provider: runtime.providerId, model: runtime.modelId });
|
|
1860
|
+
if (!ok) {
|
|
1861
|
+
console.warn('[daemon] real-runner install returned false — placeholder still active');
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
catch (e) {
|
|
1867
|
+
// Fail-loud but non-fatal — install failure leaves the
|
|
1868
|
+
// placeholder runner active; the REPL still works and the
|
|
1869
|
+
// daemon's rails still serve triggers.
|
|
1870
|
+
console.error('[daemon] install agent builder failed: ' + (e instanceof Error ? e.message : String(e)));
|
|
1871
|
+
}
|
|
1496
1872
|
const sessionOpts = {
|
|
1497
1873
|
agent: runtime.agent,
|
|
1498
1874
|
display: runtime.display,
|
|
@@ -1530,6 +1906,14 @@ async function runInteractiveChat(cliOpts, opts) {
|
|
|
1530
1906
|
// when /quit fires the auto-summary path.
|
|
1531
1907
|
memoryManager: runtime.memoryManager,
|
|
1532
1908
|
memoryGuard: runtime.memoryGuard,
|
|
1909
|
+
// v4.6 Phase 2Q-B — REPL parent-run wiring. ChatSession.runAgentTurn
|
|
1910
|
+
// writes a runs row per turn and stores its id into
|
|
1911
|
+
// `replParentRunRef`; the spawn / fanout factories above read the
|
|
1912
|
+
// ref via their `resolveParentRunId` / `resolveParentSessionId`
|
|
1913
|
+
// callbacks so children get `spawned_from_run_id` populated.
|
|
1914
|
+
replRunStore: runtime.replRunStore,
|
|
1915
|
+
replInstanceId: runtime.replInstanceId,
|
|
1916
|
+
replParentRunRef: runtime.replParentRunRef,
|
|
1533
1917
|
};
|
|
1534
1918
|
if (cliOpts.tui) {
|
|
1535
1919
|
await (0, aidenTUI_1.runTuiMode)({
|