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/callbacks.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.CliCallbacks = void 0;
|
|
22
|
+
exports.mapBlockerToCard = mapBlockerToCard;
|
|
22
23
|
exports.renderApprovalBox = renderApprovalBox;
|
|
23
24
|
const box_1 = require("./box");
|
|
24
25
|
async function defaultPrompts() {
|
|
@@ -45,6 +46,68 @@ const DECISION_CHOICES = [
|
|
|
45
46
|
{ name: 'Deny', value: 'deny' },
|
|
46
47
|
];
|
|
47
48
|
const KNOWN_TIERS = new Set(['safe', 'caution', 'dangerous']);
|
|
49
|
+
// ── v4.3 Phase 3 — manual-blocker card mapping ────────────────────────────
|
|
50
|
+
/** Best-effort hostname extraction; falls back to the raw URL. */
|
|
51
|
+
function blockerHostname(url) {
|
|
52
|
+
try {
|
|
53
|
+
return new URL(url).hostname || url;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Map a BlockerSurface to the existing CapabilityCardData chrome.
|
|
61
|
+
* Pure helper — same shape per `BlockerKind`, parameterised by the
|
|
62
|
+
* blocker's URL + optional subtype for the body text. The renderer
|
|
63
|
+
* (`display.capabilityCard`) handles all rendering chrome; this
|
|
64
|
+
* function only fills the slots semantically.
|
|
65
|
+
*
|
|
66
|
+
* Public for unit tests; chatSession + callbacks consume via the
|
|
67
|
+
* `renderBlockerCardIfPresent` method below.
|
|
68
|
+
*/
|
|
69
|
+
function mapBlockerToCard(blocker) {
|
|
70
|
+
const host = blockerHostname(blocker.url);
|
|
71
|
+
const labels = {
|
|
72
|
+
captcha: {
|
|
73
|
+
title: `CAPTCHA challenge at ${host}`,
|
|
74
|
+
canStill: ['Solve the challenge in the browser tab', 'Cancel this task'],
|
|
75
|
+
cannot: ['Continue automatically without your action'],
|
|
76
|
+
fix: `I'll wait — solve the ${blocker.subtype ?? 'CAPTCHA'} challenge and tell me when ready.`,
|
|
77
|
+
},
|
|
78
|
+
login: {
|
|
79
|
+
title: `Sign-in required at ${host}`,
|
|
80
|
+
canStill: ['Sign in via the browser tab', 'Cancel this task'],
|
|
81
|
+
cannot: ['Continue without authentication'],
|
|
82
|
+
fix: `I'll wait — sign in and let me know when done.`,
|
|
83
|
+
},
|
|
84
|
+
'2fa': {
|
|
85
|
+
title: `Two-factor code required at ${host}`,
|
|
86
|
+
canStill: ['Enter the 2FA code in the browser tab', 'Cancel this task'],
|
|
87
|
+
cannot: ['Continue without the verification code'],
|
|
88
|
+
fix: `I'll wait — enter your code and tell me when complete.`,
|
|
89
|
+
},
|
|
90
|
+
verification: {
|
|
91
|
+
title: `Identity verification at ${host}`,
|
|
92
|
+
canStill: ['Complete the verification in the browser', 'Cancel this task'],
|
|
93
|
+
cannot: ['Continue without verification'],
|
|
94
|
+
fix: `I'll wait — finish the verification and tell me when done.`,
|
|
95
|
+
},
|
|
96
|
+
consent: {
|
|
97
|
+
title: `Consent banner at ${host}`,
|
|
98
|
+
canStill: ['Dismiss the banner in the browser', 'Continue if banner is dismissable'],
|
|
99
|
+
cannot: ['Reliably interact with content while the banner blocks it'],
|
|
100
|
+
fix: `Dismiss the cookie or privacy banner and I'll retry.`,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const t = labels[blocker.kind];
|
|
104
|
+
return {
|
|
105
|
+
title: `🛑 ${t.title}`,
|
|
106
|
+
canStill: t.canStill,
|
|
107
|
+
cannotReliably: t.cannot,
|
|
108
|
+
fix: t.fix,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
48
111
|
function parseRiskTier(content) {
|
|
49
112
|
const head = content.trim().toLowerCase().split(/\s+/)[0] ?? '';
|
|
50
113
|
// Strip punctuation that might bleed in from JSON: "safe.", "caution,"...
|
|
@@ -161,6 +224,17 @@ class CliCallbacks {
|
|
|
161
224
|
catch { /* defensive */ }
|
|
162
225
|
return;
|
|
163
226
|
}
|
|
227
|
+
// v4.3 Phase 3 — render a structured "agent needs human help"
|
|
228
|
+
// card when the browser observer detected a manual blocker
|
|
229
|
+
// (CAPTCHA / login / 2FA / verification / consent). Renders for
|
|
230
|
+
// ALL trail-row outcomes below — the blocker is independent of
|
|
231
|
+
// the tool's own success/fail signal. Inline placement gives
|
|
232
|
+
// the user immediate awareness; the model's next reply will
|
|
233
|
+
// explain in prose. Defensive: missing fields silently skip.
|
|
234
|
+
try {
|
|
235
|
+
this.renderBlockerCardIfPresent(result);
|
|
236
|
+
}
|
|
237
|
+
catch { /* defensive */ }
|
|
164
238
|
// v4.1.4 reply-quality polish — Part 1.6. Helper used by ALL
|
|
165
239
|
// outcome branches below so the activity indicator gets re-armed
|
|
166
240
|
// for the gap that follows this tool (next tool, or final reply).
|
|
@@ -353,12 +427,84 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
353
427
|
const label = files.length > 0 ? files.join(', ') : 'none';
|
|
354
428
|
this.display.dim(`[memory] refreshed system prompt (${label})`);
|
|
355
429
|
};
|
|
430
|
+
/**
|
|
431
|
+
* v4.1.6 Polish 2 — post-turn skill-proposal handler.
|
|
432
|
+
*
|
|
433
|
+
* chatSession calls this AFTER `agentTurn` has rendered the agent's
|
|
434
|
+
* reply on screen. Internally:
|
|
435
|
+
* 1. Reuses `promptSkillProposal` (the existing inquirer modal)
|
|
436
|
+
* to ask the user "Save this as a reusable skill? Yes/No".
|
|
437
|
+
* 2. If accepted AND a SkillTeacher reference is wired, calls
|
|
438
|
+
* `skillTeacher.handleProposal({...skipPrompt})` to build
|
|
439
|
+
* the markdown + persist via skillManager.
|
|
440
|
+
* 3. Returns the result so chatSession can surface a confirmation
|
|
441
|
+
* line if needed.
|
|
442
|
+
*
|
|
443
|
+
* Decoupled from the agent's `runConversation` so the inquirer no
|
|
444
|
+
* longer fires mid-turn (the v4.1.5 visual-smoke regression).
|
|
445
|
+
*
|
|
446
|
+
* Defensive — exceptions in any step return `{created: false,
|
|
447
|
+
* reason: 'error'}` so a misbehaving prompt or save can't break
|
|
448
|
+
* the chat loop. The inquirer modal itself catches input-stream
|
|
449
|
+
* exceptions via the existing try/catch in promptSkillProposal.
|
|
450
|
+
*/
|
|
451
|
+
this.handleSkillProposal = async (proposal) => {
|
|
452
|
+
// Step 1: ask the user (reuses existing modal).
|
|
453
|
+
let accept;
|
|
454
|
+
try {
|
|
455
|
+
accept = await this.promptSkillProposal(proposal);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return { created: false, reason: 'prompt_error' };
|
|
459
|
+
}
|
|
460
|
+
if (!accept) {
|
|
461
|
+
return { created: false, reason: 'declined' };
|
|
462
|
+
}
|
|
463
|
+
// Step 2: persist via SkillTeacher when available. Test harnesses
|
|
464
|
+
// that don't wire a teacher just get the prompt without the save.
|
|
465
|
+
if (!this.skillTeacher) {
|
|
466
|
+
return { created: false, reason: 'no_skill_teacher_wired' };
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
// Pass `promptUser: undefined` to bypass the modal in
|
|
470
|
+
// `handleProposal` (we already showed it above). The teacher
|
|
471
|
+
// sees tier === 'tier_3_propose' WITHOUT a prompt callback,
|
|
472
|
+
// which it interprets as `no_prompt_callback` and skips the
|
|
473
|
+
// save — so we need to call the create branch directly.
|
|
474
|
+
//
|
|
475
|
+
// SkillTeacher's tier-4 (auto) branch creates without
|
|
476
|
+
// prompting; we reuse that path by passing a stub that always
|
|
477
|
+
// returns true (user already accepted above).
|
|
478
|
+
const result = await this.skillTeacher.handleProposal(proposal, {
|
|
479
|
+
promptUser: async () => true,
|
|
480
|
+
});
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
return {
|
|
485
|
+
created: false,
|
|
486
|
+
reason: `create_failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
};
|
|
356
490
|
this.display = opts.display;
|
|
357
491
|
this.auxiliaryClient = opts.auxiliaryClient;
|
|
358
492
|
this.verboseMode = opts.verboseMode ?? 'normal';
|
|
359
493
|
this.promptsPromise = opts.promptModule
|
|
360
494
|
? Promise.resolve(opts.promptModule)
|
|
361
495
|
: defaultPrompts();
|
|
496
|
+
this.skillTeacher = opts.skillTeacher;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* v4.1.6 Polish 2 — late-wire the SkillTeacher reference. aidenCLI
|
|
500
|
+
* constructs CliCallbacks early (the approval engine needs it
|
|
501
|
+
* stitched in), but SkillTeacher is built later in the boot
|
|
502
|
+
* sequence after skillLoader / skillManager are ready. Call this
|
|
503
|
+
* once after both exist so `handleSkillProposal` can persist
|
|
504
|
+
* accepted proposals.
|
|
505
|
+
*/
|
|
506
|
+
setSkillTeacher(teacher) {
|
|
507
|
+
this.skillTeacher = teacher;
|
|
362
508
|
}
|
|
363
509
|
/** Update verbose mode at runtime (wired to /verbose). */
|
|
364
510
|
setVerboseMode(mode) {
|
|
@@ -415,6 +561,30 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
415
561
|
this.toolTraceBeforeHook = opts.before;
|
|
416
562
|
this.toolTraceAfterHook = opts.after;
|
|
417
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* v4.3 Phase 3 — render a manual-blocker card when the browser
|
|
566
|
+
* observer detected a CAPTCHA / login / 2FA / verification /
|
|
567
|
+
* consent surface on the page. Reuses the existing capabilityCard
|
|
568
|
+
* chrome via a `mapBlockerToCard` semantic mapping — no new layout
|
|
569
|
+
* code, no new dedicated card component.
|
|
570
|
+
*
|
|
571
|
+
* Defensive: silently skips when the field shape doesn't match
|
|
572
|
+
* (no browserState, no blocker, unrecognised kind). Never throws
|
|
573
|
+
* — caller wraps in try/catch defensively.
|
|
574
|
+
*
|
|
575
|
+
* The blocker info is structural data emitted by the observer
|
|
576
|
+
* HOC (`tools/v4/browser/_observer.ts`); the renderer reads it
|
|
577
|
+
* from `result.result.browserState.blocker` after every browser
|
|
578
|
+
* tool call. Inline placement gives users immediate awareness
|
|
579
|
+
* before the model's next reply lands.
|
|
580
|
+
*/
|
|
581
|
+
renderBlockerCardIfPresent(result) {
|
|
582
|
+
const inner = (result?.result ?? null);
|
|
583
|
+
const blocker = inner?.browserState?.blocker;
|
|
584
|
+
if (!blocker)
|
|
585
|
+
return;
|
|
586
|
+
this.display.capabilityCard(mapBlockerToCard(blocker));
|
|
587
|
+
}
|
|
418
588
|
}
|
|
419
589
|
exports.CliCallbacks = CliCallbacks;
|
|
420
590
|
// Tier-3.1 (v4.1-tier3.1): replaced 🟢/🟡/🔴 emoji badges with
|
|
@@ -872,6 +872,21 @@ class ChatSession {
|
|
|
872
872
|
}
|
|
873
873
|
}
|
|
874
874
|
async runAgentTurn(userInput) {
|
|
875
|
+
// v4.5 Phase 8b — daemon-scheduling intent check on the user's
|
|
876
|
+
// initial message. Classifies regex hits like "every day at",
|
|
877
|
+
// "watch this folder", "when an email arrives" — and queues a
|
|
878
|
+
// tip to render at the END of the agent's response (so it
|
|
879
|
+
// doesn't crowd the agent's actual reply). Engine handles
|
|
880
|
+
// budget + dismissal.
|
|
881
|
+
let _deferredTip = null;
|
|
882
|
+
try {
|
|
883
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
884
|
+
const { getSuggestionEngine } = require('../../core/v4/suggestionEngine');
|
|
885
|
+
const t = getSuggestionEngine().checkInitialMessage(userInput);
|
|
886
|
+
if (t)
|
|
887
|
+
_deferredTip = t;
|
|
888
|
+
}
|
|
889
|
+
catch { /* defensive — never block a turn on a suggestion */ }
|
|
875
890
|
// Phase 30.2.1 — explore mode: short-circuit BEFORE building the
|
|
876
891
|
// turn-status spinner / agent call. The wizard skipped, so there's
|
|
877
892
|
// no real provider to talk to. Print a friendly redirect to /setup
|
|
@@ -907,6 +922,43 @@ class ChatSession {
|
|
|
907
922
|
const baseHistory = newHistory.length > 0
|
|
908
923
|
? [...this.history, ...newHistory, userMsg]
|
|
909
924
|
: [...this.history, userMsg];
|
|
925
|
+
// v4.6 Phase 2Q-B — REPL parent-run row (best-effort).
|
|
926
|
+
//
|
|
927
|
+
// Insert a `runs` row tagged with this REPL session BEFORE the
|
|
928
|
+
// agent loop dispatches. Capture the row id into the shared
|
|
929
|
+
// `replParentRunRef` so any `spawn_sub_agent` / `subagent_fanout`
|
|
930
|
+
// child this turn produces can link back via
|
|
931
|
+
// `spawned_from_run_id`. The ref is cleared in the catch /
|
|
932
|
+
// success paths below regardless of outcome.
|
|
933
|
+
//
|
|
934
|
+
// Defensive: a runStore write failure (locked DB, schema drift,
|
|
935
|
+
// etc.) must NOT crash the REPL — every persistence call here is
|
|
936
|
+
// wrapped in try/catch and reduces to a logged warning. The
|
|
937
|
+
// user-facing turn still runs.
|
|
938
|
+
let replRunId = null;
|
|
939
|
+
const replRunStore = this.opts.replRunStore;
|
|
940
|
+
const replInstanceId = this.opts.replInstanceId;
|
|
941
|
+
const replParentRunRef = this.opts.replParentRunRef;
|
|
942
|
+
if (replRunStore && replInstanceId && this.sessionId) {
|
|
943
|
+
try {
|
|
944
|
+
replRunId = replRunStore.create({
|
|
945
|
+
sessionId: this.sessionId,
|
|
946
|
+
instanceId: replInstanceId,
|
|
947
|
+
status: 'running',
|
|
948
|
+
startedAt: turnStartedAt,
|
|
949
|
+
});
|
|
950
|
+
if (replParentRunRef) {
|
|
951
|
+
replParentRunRef.runId = replRunId;
|
|
952
|
+
replParentRunRef.sessionId = this.sessionId;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
catch (err) {
|
|
956
|
+
// Logged once per turn; the user's chat is not interrupted.
|
|
957
|
+
// eslint-disable-next-line no-console
|
|
958
|
+
console.warn('[runs] failed to write REPL parent-run row:', err instanceof Error ? err.message : String(err));
|
|
959
|
+
replRunId = null;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
910
962
|
// Phase 16c: streaming gated on display.streaming config.
|
|
911
963
|
// v4.1.4 Part 1.6: PRODUCTION DEFAULT FLIPPED FROM FALSE TO TRUE.
|
|
912
964
|
// Streaming delivers the activity indicator, tool-row live tick,
|
|
@@ -1164,6 +1216,35 @@ class ChatSession {
|
|
|
1164
1216
|
this.history = result.messages;
|
|
1165
1217
|
this.totalUsage.inputTokens += result.totalUsage.inputTokens;
|
|
1166
1218
|
this.totalUsage.outputTokens += result.totalUsage.outputTokens;
|
|
1219
|
+
// v4.6 Phase 2Q-B — finalize the REPL parent-run row on success.
|
|
1220
|
+
// `finishReason` from the agent loop maps directly into our DB
|
|
1221
|
+
// status: `stop` → completed; `interrupted` / `tool_loop` →
|
|
1222
|
+
// surface as 'interrupted' so it's visible in `runs list`;
|
|
1223
|
+
// `budget_exhausted` / `error` → failed. Wrapped in try/catch
|
|
1224
|
+
// so even a runStore write failure here can't crash the REPL.
|
|
1225
|
+
if (replRunStore && replRunId !== null) {
|
|
1226
|
+
try {
|
|
1227
|
+
const dbStatus = result.finishReason === 'stop' ? 'completed' :
|
|
1228
|
+
result.finishReason === 'interrupted' ? 'interrupted' :
|
|
1229
|
+
result.finishReason === 'tool_loop' ? 'interrupted' :
|
|
1230
|
+
'failed';
|
|
1231
|
+
replRunStore.setStatus(replRunId, dbStatus, {
|
|
1232
|
+
finishReason: result.finishReason,
|
|
1233
|
+
completedAt: Date.now(),
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
catch (err) {
|
|
1237
|
+
// eslint-disable-next-line no-console
|
|
1238
|
+
console.warn('[runs] failed to finalize REPL parent-run row:', err instanceof Error ? err.message : String(err));
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
// Clear the shared ref so a subsequent turn (or stray
|
|
1242
|
+
// spawn/fanout dispatched between turns from a slash command
|
|
1243
|
+
// handler) doesn't see a stale parent id.
|
|
1244
|
+
if (replParentRunRef) {
|
|
1245
|
+
replParentRunRef.runId = null;
|
|
1246
|
+
replParentRunRef.sessionId = null;
|
|
1247
|
+
}
|
|
1167
1248
|
// Phase 16d: surface inline confirmations for verified memory writes.
|
|
1168
1249
|
// We MUST gate on verified=true (the post-write read flag from
|
|
1169
1250
|
// MemoryGuard) — HonestyEnforcement uses the same flag to catch
|
|
@@ -1178,9 +1259,25 @@ class ChatSession {
|
|
|
1178
1259
|
if (result.toolCallTrace && result.toolCallTrace.length > 0) {
|
|
1179
1260
|
this.sessionToolTrace.push(...result.toolCallTrace);
|
|
1180
1261
|
}
|
|
1181
|
-
//
|
|
1182
|
-
//
|
|
1183
|
-
|
|
1262
|
+
// v4.1.6 spike (TCE) — tool-loop terminal surface. When the
|
|
1263
|
+
// agent ended the turn via the recovery controller's surface
|
|
1264
|
+
// stage, render a structured-failure card instead of the
|
|
1265
|
+
// (empty) reply. Same chrome as auth / platform capability
|
|
1266
|
+
// cards — fits the established Aiden UX language for
|
|
1267
|
+
// "the action you wanted didn't happen, here's why and what
|
|
1268
|
+
// you can do." Surface BEFORE the tool→reply separator path
|
|
1269
|
+
// below because there's no agent reply to introduce.
|
|
1270
|
+
if (result.finishReason === 'tool_loop' && result.toolLoopCard) {
|
|
1271
|
+
// Emit the muted rule so the card visually separates from
|
|
1272
|
+
// the tool trail above it.
|
|
1273
|
+
emitToolReplySeparator();
|
|
1274
|
+
this.opts.display.capabilityCard(result.toolLoopCard);
|
|
1275
|
+
}
|
|
1276
|
+
else if (result.finalContent && !streamingActive) {
|
|
1277
|
+
// When streaming was active and emitted the final content
|
|
1278
|
+
// already, skip the markdown re-render — we'd otherwise
|
|
1279
|
+
// duplicate text.
|
|
1280
|
+
//
|
|
1184
1281
|
// v4.1.5 Issue O — non-streaming reply path. Emit the muted
|
|
1185
1282
|
// rule between the tool trail and the agent header before
|
|
1186
1283
|
// the one-shot reply lands. Idempotent + tool-gated by
|
|
@@ -1188,6 +1285,30 @@ class ChatSession {
|
|
|
1188
1285
|
emitToolReplySeparator();
|
|
1189
1286
|
this.opts.display.write(this.opts.display.agentTurn(result.finalContent));
|
|
1190
1287
|
}
|
|
1288
|
+
// v4.1.6 Polish 2 — post-render skill-proposal handler.
|
|
1289
|
+
// The agent loop now SKIPS the inquirer prompt when a
|
|
1290
|
+
// prompt callback is wired, surfacing the SkillProposal
|
|
1291
|
+
// here instead. We fire the prompt AFTER the agent reply
|
|
1292
|
+
// has rendered so the user sees the answer before being
|
|
1293
|
+
// asked "save this as a reusable skill?" — fixing the
|
|
1294
|
+
// v4.1.5 visual-smoke regression where the prompt fired
|
|
1295
|
+
// mid-turn and clobbered the reply.
|
|
1296
|
+
//
|
|
1297
|
+
// Wrapped in try/catch so a buggy proposal flow never
|
|
1298
|
+
// breaks the chat loop. A successful save surfaces a
|
|
1299
|
+
// dim confirmation line that fits the established
|
|
1300
|
+
// memory-confirmation chrome.
|
|
1301
|
+
if (result.skillProposal && this.opts.callbacks?.handleSkillProposal) {
|
|
1302
|
+
try {
|
|
1303
|
+
const saveResult = await this.opts.callbacks.handleSkillProposal(result.skillProposal);
|
|
1304
|
+
if (saveResult?.created && saveResult.skillName) {
|
|
1305
|
+
this.opts.display.dim(` ✓ Saved as skill: ${saveResult.skillName}`);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
catch {
|
|
1309
|
+
/* defensive — never let proposal flow break the chat loop */
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1191
1312
|
if (this.sessionId) {
|
|
1192
1313
|
// Only persist the new tail of messages — what got added this turn.
|
|
1193
1314
|
const newSlice = this.history.slice(turnStart);
|
|
@@ -1195,6 +1316,20 @@ class ChatSession {
|
|
|
1195
1316
|
}
|
|
1196
1317
|
this.setStatusState({ kind: 'ready' });
|
|
1197
1318
|
this.lastTurnElapsedMs = Date.now() - turnStartedAt;
|
|
1319
|
+
// v4.5 Phase 8b — surface a deferred daemon-scheduling tip
|
|
1320
|
+
// queued at turn start. Renders AFTER the agent's response per
|
|
1321
|
+
// Q-P8b-3(b) — the user reads the answer first, then sees the
|
|
1322
|
+
// ambient capability hint.
|
|
1323
|
+
if (_deferredTip) {
|
|
1324
|
+
try {
|
|
1325
|
+
this.opts.display.dim(_deferredTip.message);
|
|
1326
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1327
|
+
const { getSuggestionEngine } = require('../../core/v4/suggestionEngine');
|
|
1328
|
+
getSuggestionEngine().recordFired(_deferredTip.slot);
|
|
1329
|
+
}
|
|
1330
|
+
catch { /* defensive */ }
|
|
1331
|
+
_deferredTip = null;
|
|
1332
|
+
}
|
|
1198
1333
|
// Tier-3.1a: dim full-width rule between the agent reply and the
|
|
1199
1334
|
// post-turn status footer.
|
|
1200
1335
|
this.opts.display.write(` ${this.opts.display.rule()}\n`);
|
|
@@ -1220,6 +1355,26 @@ class ChatSession {
|
|
|
1220
1355
|
progressBar?.hide();
|
|
1221
1356
|
if (streamingActive)
|
|
1222
1357
|
this.opts.display.streamComplete();
|
|
1358
|
+
// v4.6 Phase 2Q-B — finalize REPL parent-run row on error.
|
|
1359
|
+
// Visible in `aiden runs list` as a failed top-level row so
|
|
1360
|
+
// operators can correlate a chat error with whatever children
|
|
1361
|
+
// it had already kicked off this turn.
|
|
1362
|
+
if (replRunStore && replRunId !== null) {
|
|
1363
|
+
try {
|
|
1364
|
+
replRunStore.setStatus(replRunId, 'failed', {
|
|
1365
|
+
finishReason: 'error',
|
|
1366
|
+
completedAt: Date.now(),
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
catch (e2) {
|
|
1370
|
+
// eslint-disable-next-line no-console
|
|
1371
|
+
console.warn('[runs] failed to mark REPL parent-run failed:', e2 instanceof Error ? e2.message : String(e2));
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
if (replParentRunRef) {
|
|
1375
|
+
replParentRunRef.runId = null;
|
|
1376
|
+
replParentRunRef.sessionId = null;
|
|
1377
|
+
}
|
|
1223
1378
|
const msg = err?.message ?? String(err);
|
|
1224
1379
|
// v4.1.3-prebump: classify the error so the suggestion below
|
|
1225
1380
|
// points at the actual fix instead of the generic "/model or
|
|
@@ -1349,6 +1504,30 @@ class ChatSession {
|
|
|
1349
1504
|
providerOk: !this.opts.unconfigured,
|
|
1350
1505
|
version: version_1.VERSION,
|
|
1351
1506
|
}) + '\n');
|
|
1507
|
+
// v4.6 Phase 3A — operator kill-switch indicator. Lands ABOVE
|
|
1508
|
+
// the blank-line + provider-source annotation so an operator
|
|
1509
|
+
// who paused in a prior session sees the state immediately on
|
|
1510
|
+
// boot, alongside the standard status pills. Single dim
|
|
1511
|
+
// warning line; no special chrome — the message itself is the
|
|
1512
|
+
// visual signal.
|
|
1513
|
+
try {
|
|
1514
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1515
|
+
const { getSpawnPause } = require('../../core/v4/subagent/spawnPause');
|
|
1516
|
+
const s = getSpawnPause().status();
|
|
1517
|
+
if (s.paused) {
|
|
1518
|
+
const reasonSuffix = s.reason ? ` · ${s.reason}` : '';
|
|
1519
|
+
const durationSuffix = typeof s.durationMs === 'number'
|
|
1520
|
+
? ` · ${formatDuration(s.durationMs)}`
|
|
1521
|
+
: '';
|
|
1522
|
+
display.warn(`spawn-pause: ON${reasonSuffix}${durationSuffix} — use /spawn-pause off to resume`);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
// Singleton not initialised (test stubs, etc.) — silently skip.
|
|
1527
|
+
}
|
|
1528
|
+
// v4.5 TUI polish — blank line so the status pills row doesn't
|
|
1529
|
+
// crowd the muted source annotation right beneath it.
|
|
1530
|
+
display.write('\n');
|
|
1352
1531
|
// v4.1.3-prebump: dim source annotation under the pills row so the
|
|
1353
1532
|
// user can see WHY this provider/model was chosen — closes the
|
|
1354
1533
|
// information gap that made Case 3 (persisted-config) look like a
|
|
@@ -1408,10 +1587,73 @@ class ChatSession {
|
|
|
1408
1587
|
}
|
|
1409
1588
|
// Scroll footer (parchment at ≥80 cols, single-line credits below).
|
|
1410
1589
|
display.write(display.scrollFooter() + '\n');
|
|
1590
|
+
// v4.5 update system — boxed three-option prompt rendered AFTER
|
|
1591
|
+
// the boot card / status pills (Q-U5b less-intrusive position),
|
|
1592
|
+
// BEFORE the bottomPromptHint. Fires only when:
|
|
1593
|
+
// - update check came back with `updateAvailable && !skipped`
|
|
1594
|
+
// - stdin is a TTY (non-interactive boots short-circuit to 'later')
|
|
1595
|
+
// 5-second timeout defaults to 'later' so a user away from
|
|
1596
|
+
// keyboard isn't held up. Skip-on-'n' writes the version to the
|
|
1597
|
+
// .update_check.json cache so subsequent boots stay quiet until
|
|
1598
|
+
// a newer release ships.
|
|
1599
|
+
try {
|
|
1600
|
+
await this.maybeShowBootUpdatePrompt();
|
|
1601
|
+
}
|
|
1602
|
+
catch { /* never let the update prompt crash boot */ }
|
|
1411
1603
|
// Bottom prompt hint — final line of the boot card.
|
|
1412
1604
|
display.write('\n');
|
|
1413
1605
|
display.write(display.bottomPromptHint() + '\n');
|
|
1414
1606
|
}
|
|
1607
|
+
/**
|
|
1608
|
+
* v4.5 update system — orchestrates the boot prompt. Lazy-imports
|
|
1609
|
+
* the update modules so non-boot code paths (e.g. test harness
|
|
1610
|
+
* sessions constructed without paths wired) don't pay the cost.
|
|
1611
|
+
*/
|
|
1612
|
+
async maybeShowBootUpdatePrompt() {
|
|
1613
|
+
if (!this.opts.paths)
|
|
1614
|
+
return;
|
|
1615
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1616
|
+
const cu = require('../../core/v4/update/checkUpdate');
|
|
1617
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1618
|
+
const md = require('../../core/v4/update/installMethodDetect');
|
|
1619
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1620
|
+
const ss = require('../../core/v4/update/skipState');
|
|
1621
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1622
|
+
const bp = require('./updateBootPrompt');
|
|
1623
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1624
|
+
const ei = require('../../core/v4/update/executeInstall');
|
|
1625
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1626
|
+
const ver = require('../../core/version');
|
|
1627
|
+
const status = await cu.checkForUpdate({ paths: this.opts.paths, installedVersion: ver.VERSION });
|
|
1628
|
+
if (!status.updateAvailable || !status.latest || status.skipped)
|
|
1629
|
+
return;
|
|
1630
|
+
const method = md.detectInstallMethod();
|
|
1631
|
+
const choice = await bp.showBootUpdatePrompt({
|
|
1632
|
+
status, method,
|
|
1633
|
+
display: { write: (s) => this.opts.display.write(s), dim: (s) => this.opts.display.dim(s) },
|
|
1634
|
+
});
|
|
1635
|
+
if (choice === 'install') {
|
|
1636
|
+
if (method.inProcessInstallSupported) {
|
|
1637
|
+
this.opts.display.write(`Installing aiden-runtime ${status.latest}…\n`);
|
|
1638
|
+
const result = await ei.executeInstall({ packageSpec: `aiden-runtime@${status.latest}` });
|
|
1639
|
+
if (result.success) {
|
|
1640
|
+
this.opts.display.write(` ✓ aiden-runtime ${result.installedVersion ?? status.latest} installed.\n`);
|
|
1641
|
+
this.opts.display.dim('Restart Aiden to apply: type /quit then re-run `aiden`.');
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
this.opts.display.warn(result.error ?? 'Install failed (no error message).');
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
this.opts.display.write(`To update, run:\n ${method.updateCommand(status.latest)}\n`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
else if (choice === 'skip') {
|
|
1652
|
+
await cu.updateCacheFile(this.opts.paths, (current) => ss.applySkip(current, status.latest));
|
|
1653
|
+
this.opts.display.dim(` skipped ${status.latest}. Boot prompt resumes when a newer version ships.`);
|
|
1654
|
+
}
|
|
1655
|
+
// 'later' = no-op; prompt fires again next session.
|
|
1656
|
+
}
|
|
1415
1657
|
/** Phase 22 Task 4: state transitions for the right-most segment. */
|
|
1416
1658
|
setStatusState(state) {
|
|
1417
1659
|
this.statusState = state;
|
|
@@ -0,0 +1,94 @@
|
|
|
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/_runtimeToggleHelpers.ts — v4.5 Phase 8a.
|
|
10
|
+
*
|
|
11
|
+
* Shared `flip` + `printStatus` helpers used by /sandbox, /tce, and
|
|
12
|
+
* /browser-depth. Each subsystem command is a tiny wrapper over
|
|
13
|
+
* these — same on/off/status surface, same status format.
|
|
14
|
+
*
|
|
15
|
+
* The persist path goes through `ConfigManager.set()` + `save()` so
|
|
16
|
+
* the runtime_toggles section is written verbatim to config.yaml.
|
|
17
|
+
* The runtimeToggles singleton's `set()` then fires onChange
|
|
18
|
+
* callbacks for cached consumers (sandboxConfig's singleton).
|
|
19
|
+
*
|
|
20
|
+
* Test seam: `flip` and `printStatus` accept a `ctx` shaped like
|
|
21
|
+
* SlashCommandContext (just `display`, optionally `config`). Tests
|
|
22
|
+
* pass a minimal stub.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.flip = flip;
|
|
26
|
+
exports.printStatus = printStatus;
|
|
27
|
+
exports.parseSubcommand = parseSubcommand;
|
|
28
|
+
const runtimeToggles_1 = require("../../../core/v4/runtimeToggles");
|
|
29
|
+
const LABEL = {
|
|
30
|
+
sandbox: 'Sandbox',
|
|
31
|
+
tce: 'TCE',
|
|
32
|
+
browser_depth: 'Browser depth',
|
|
33
|
+
suggestions: 'Suggestions',
|
|
34
|
+
planner_guard: 'Planner-Guard',
|
|
35
|
+
};
|
|
36
|
+
const CONFIG_DOTTED = {
|
|
37
|
+
sandbox: 'runtime_toggles.sandbox',
|
|
38
|
+
tce: 'runtime_toggles.tce',
|
|
39
|
+
browser_depth: 'runtime_toggles.browser_depth',
|
|
40
|
+
suggestions: 'runtime_toggles.suggestions',
|
|
41
|
+
planner_guard: 'runtime_toggles.planner_guard',
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Apply a toggle change. When `ctx.config` is wired, persists to
|
|
45
|
+
* config.yaml. Otherwise the flip is in-process only (current
|
|
46
|
+
* session sees the new value; next process boot doesn't).
|
|
47
|
+
*
|
|
48
|
+
* Returns nothing — prints status via ctx.display.
|
|
49
|
+
*/
|
|
50
|
+
async function flip(key, value, ctx) {
|
|
51
|
+
const rt = (0, runtimeToggles_1.getRuntimeToggles)();
|
|
52
|
+
// Persist when a ConfigManager is wired.
|
|
53
|
+
if (ctx.config) {
|
|
54
|
+
try {
|
|
55
|
+
ctx.config.set(CONFIG_DOTTED[key], value);
|
|
56
|
+
await ctx.config.save();
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
ctx.display.warn(`[${key}] config.yaml save failed (${e instanceof Error ? e.message : String(e)}); ` +
|
|
60
|
+
`flip applies to this session only.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
await rt.set(key, value, { persist: false });
|
|
64
|
+
printStatus(key, ctx);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Print the current state of one toggle. One-line output per
|
|
68
|
+
* Q-P8a-2(a):
|
|
69
|
+
*
|
|
70
|
+
* `Sandbox: ON (source: config)`
|
|
71
|
+
*
|
|
72
|
+
* `source` reveals which precedence layer provided the value —
|
|
73
|
+
* critical for debugging "why is it ON when my .env says 0".
|
|
74
|
+
*/
|
|
75
|
+
function printStatus(key, ctx) {
|
|
76
|
+
const snap = (0, runtimeToggles_1.getRuntimeToggles)().snapshot()[key];
|
|
77
|
+
const label = LABEL[key];
|
|
78
|
+
const state = snap.value ? 'ON' : 'OFF';
|
|
79
|
+
ctx.display.write(`${label}: ${state} (source: ${snap.source})\n`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Parse the on/off/status subcommand. Returns null when the input
|
|
83
|
+
* is unrecognised (caller prints usage).
|
|
84
|
+
*/
|
|
85
|
+
function parseSubcommand(raw) {
|
|
86
|
+
const s = (raw ?? 'status').toLowerCase();
|
|
87
|
+
if (s === 'on' || s === 'enable' || s === '1' || s === 'true')
|
|
88
|
+
return 'on';
|
|
89
|
+
if (s === 'off' || s === 'disable' || s === '0' || s === 'false')
|
|
90
|
+
return 'off';
|
|
91
|
+
if (s === 'status' || s === '' || s === undefined)
|
|
92
|
+
return 'status';
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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/browserDepth.ts — v4.5 Phase 8a.
|
|
10
|
+
*
|
|
11
|
+
* `/browser-depth on|off|status` — flip the v4.3 state-aware
|
|
12
|
+
* browser observer (URL/DOM/iframe-tree capture, stale-ref retry,
|
|
13
|
+
* manual-blocker detection) without restart. Persists to
|
|
14
|
+
* config.yaml. Env var AIDEN_BROWSER_DEPTH always wins.
|
|
15
|
+
*
|
|
16
|
+
* Q-P8a-5(a): named `/browser-depth` to mirror the env var
|
|
17
|
+
* exactly. Reserves `/browser` for future browser-navigation
|
|
18
|
+
* commands so the namespace stays unambiguous.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.browserDepth = void 0;
|
|
22
|
+
const _runtimeToggleHelpers_1 = require("./_runtimeToggleHelpers");
|
|
23
|
+
exports.browserDepth = {
|
|
24
|
+
name: 'browser-depth',
|
|
25
|
+
description: 'Toggle the v4.3 state-aware browser observer.',
|
|
26
|
+
category: 'system',
|
|
27
|
+
icon: '🌐',
|
|
28
|
+
handler: async (ctx) => {
|
|
29
|
+
const sub = (0, _runtimeToggleHelpers_1.parseSubcommand)(ctx.args[0]);
|
|
30
|
+
if (sub === 'on') {
|
|
31
|
+
await (0, _runtimeToggleHelpers_1.flip)('browser_depth', true, ctx);
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
if (sub === 'off') {
|
|
35
|
+
await (0, _runtimeToggleHelpers_1.flip)('browser_depth', false, ctx);
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
if (sub === 'status') {
|
|
39
|
+
(0, _runtimeToggleHelpers_1.printStatus)('browser_depth', ctx);
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
ctx.display.printError('Usage: /browser-depth on|off|status');
|
|
43
|
+
return {};
|
|
44
|
+
},
|
|
45
|
+
};
|