aiden-runtime 4.1.5 → 4.5.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 +250 -847
- package/dist/api/server.js +32 -5
- package/dist/cli/v4/aidenCLI.js +351 -53
- package/dist/cli/v4/callbacks.js +170 -0
- package/dist/cli/v4/chatSession.js +138 -3
- package/dist/cli/v4/commands/_runtimeToggleHelpers.js +92 -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/help.js +7 -0
- package/dist/cli/v4/commands/index.js +20 -1
- package/dist/cli/v4/commands/runs.js +203 -0
- package/dist/cli/v4/commands/sandbox.js +48 -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 +142 -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 +308 -4
- package/dist/core/v4/browserState.js +436 -0
- package/dist/core/v4/checkpoint.js +79 -0
- package/dist/core/v4/daemon/bootstrap.js +604 -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 +296 -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 +114 -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/recoveryReport.js +449 -0
- package/dist/core/v4/runtimeToggles.js +187 -0
- package/dist/core/v4/sandboxConfig.js +285 -0
- package/dist/core/v4/sandboxFs.js +316 -0
- package/dist/core/v4/suggestionCatalog.js +41 -0
- package/dist/core/v4/suggestionEngine.js +210 -0
- package/dist/core/v4/toolRegistry.js +18 -0
- 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/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 +71 -58
- 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 +2 -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/subagentFanout.js +1 -0
- 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 +7 -1
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
|
|
@@ -1178,9 +1193,25 @@ class ChatSession {
|
|
|
1178
1193
|
if (result.toolCallTrace && result.toolCallTrace.length > 0) {
|
|
1179
1194
|
this.sessionToolTrace.push(...result.toolCallTrace);
|
|
1180
1195
|
}
|
|
1181
|
-
//
|
|
1182
|
-
//
|
|
1183
|
-
|
|
1196
|
+
// v4.1.6 spike (TCE) — tool-loop terminal surface. When the
|
|
1197
|
+
// agent ended the turn via the recovery controller's surface
|
|
1198
|
+
// stage, render a structured-failure card instead of the
|
|
1199
|
+
// (empty) reply. Same chrome as auth / platform capability
|
|
1200
|
+
// cards — fits the established Aiden UX language for
|
|
1201
|
+
// "the action you wanted didn't happen, here's why and what
|
|
1202
|
+
// you can do." Surface BEFORE the tool→reply separator path
|
|
1203
|
+
// below because there's no agent reply to introduce.
|
|
1204
|
+
if (result.finishReason === 'tool_loop' && result.toolLoopCard) {
|
|
1205
|
+
// Emit the muted rule so the card visually separates from
|
|
1206
|
+
// the tool trail above it.
|
|
1207
|
+
emitToolReplySeparator();
|
|
1208
|
+
this.opts.display.capabilityCard(result.toolLoopCard);
|
|
1209
|
+
}
|
|
1210
|
+
else if (result.finalContent && !streamingActive) {
|
|
1211
|
+
// When streaming was active and emitted the final content
|
|
1212
|
+
// already, skip the markdown re-render — we'd otherwise
|
|
1213
|
+
// duplicate text.
|
|
1214
|
+
//
|
|
1184
1215
|
// v4.1.5 Issue O — non-streaming reply path. Emit the muted
|
|
1185
1216
|
// rule between the tool trail and the agent header before
|
|
1186
1217
|
// the one-shot reply lands. Idempotent + tool-gated by
|
|
@@ -1188,6 +1219,30 @@ class ChatSession {
|
|
|
1188
1219
|
emitToolReplySeparator();
|
|
1189
1220
|
this.opts.display.write(this.opts.display.agentTurn(result.finalContent));
|
|
1190
1221
|
}
|
|
1222
|
+
// v4.1.6 Polish 2 — post-render skill-proposal handler.
|
|
1223
|
+
// The agent loop now SKIPS the inquirer prompt when a
|
|
1224
|
+
// prompt callback is wired, surfacing the SkillProposal
|
|
1225
|
+
// here instead. We fire the prompt AFTER the agent reply
|
|
1226
|
+
// has rendered so the user sees the answer before being
|
|
1227
|
+
// asked "save this as a reusable skill?" — fixing the
|
|
1228
|
+
// v4.1.5 visual-smoke regression where the prompt fired
|
|
1229
|
+
// mid-turn and clobbered the reply.
|
|
1230
|
+
//
|
|
1231
|
+
// Wrapped in try/catch so a buggy proposal flow never
|
|
1232
|
+
// breaks the chat loop. A successful save surfaces a
|
|
1233
|
+
// dim confirmation line that fits the established
|
|
1234
|
+
// memory-confirmation chrome.
|
|
1235
|
+
if (result.skillProposal && this.opts.callbacks?.handleSkillProposal) {
|
|
1236
|
+
try {
|
|
1237
|
+
const saveResult = await this.opts.callbacks.handleSkillProposal(result.skillProposal);
|
|
1238
|
+
if (saveResult?.created && saveResult.skillName) {
|
|
1239
|
+
this.opts.display.dim(` ✓ Saved as skill: ${saveResult.skillName}`);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
catch {
|
|
1243
|
+
/* defensive — never let proposal flow break the chat loop */
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1191
1246
|
if (this.sessionId) {
|
|
1192
1247
|
// Only persist the new tail of messages — what got added this turn.
|
|
1193
1248
|
const newSlice = this.history.slice(turnStart);
|
|
@@ -1195,6 +1250,20 @@ class ChatSession {
|
|
|
1195
1250
|
}
|
|
1196
1251
|
this.setStatusState({ kind: 'ready' });
|
|
1197
1252
|
this.lastTurnElapsedMs = Date.now() - turnStartedAt;
|
|
1253
|
+
// v4.5 Phase 8b — surface a deferred daemon-scheduling tip
|
|
1254
|
+
// queued at turn start. Renders AFTER the agent's response per
|
|
1255
|
+
// Q-P8b-3(b) — the user reads the answer first, then sees the
|
|
1256
|
+
// ambient capability hint.
|
|
1257
|
+
if (_deferredTip) {
|
|
1258
|
+
try {
|
|
1259
|
+
this.opts.display.dim(_deferredTip.message);
|
|
1260
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1261
|
+
const { getSuggestionEngine } = require('../../core/v4/suggestionEngine');
|
|
1262
|
+
getSuggestionEngine().recordFired(_deferredTip.slot);
|
|
1263
|
+
}
|
|
1264
|
+
catch { /* defensive */ }
|
|
1265
|
+
_deferredTip = null;
|
|
1266
|
+
}
|
|
1198
1267
|
// Tier-3.1a: dim full-width rule between the agent reply and the
|
|
1199
1268
|
// post-turn status footer.
|
|
1200
1269
|
this.opts.display.write(` ${this.opts.display.rule()}\n`);
|
|
@@ -1349,6 +1418,9 @@ class ChatSession {
|
|
|
1349
1418
|
providerOk: !this.opts.unconfigured,
|
|
1350
1419
|
version: version_1.VERSION,
|
|
1351
1420
|
}) + '\n');
|
|
1421
|
+
// v4.5 TUI polish — blank line so the status pills row doesn't
|
|
1422
|
+
// crowd the muted source annotation right beneath it.
|
|
1423
|
+
display.write('\n');
|
|
1352
1424
|
// v4.1.3-prebump: dim source annotation under the pills row so the
|
|
1353
1425
|
// user can see WHY this provider/model was chosen — closes the
|
|
1354
1426
|
// information gap that made Case 3 (persisted-config) look like a
|
|
@@ -1408,10 +1480,73 @@ class ChatSession {
|
|
|
1408
1480
|
}
|
|
1409
1481
|
// Scroll footer (parchment at ≥80 cols, single-line credits below).
|
|
1410
1482
|
display.write(display.scrollFooter() + '\n');
|
|
1483
|
+
// v4.5 update system — boxed three-option prompt rendered AFTER
|
|
1484
|
+
// the boot card / status pills (Q-U5b less-intrusive position),
|
|
1485
|
+
// BEFORE the bottomPromptHint. Fires only when:
|
|
1486
|
+
// - update check came back with `updateAvailable && !skipped`
|
|
1487
|
+
// - stdin is a TTY (non-interactive boots short-circuit to 'later')
|
|
1488
|
+
// 5-second timeout defaults to 'later' so a user away from
|
|
1489
|
+
// keyboard isn't held up. Skip-on-'n' writes the version to the
|
|
1490
|
+
// .update_check.json cache so subsequent boots stay quiet until
|
|
1491
|
+
// a newer release ships.
|
|
1492
|
+
try {
|
|
1493
|
+
await this.maybeShowBootUpdatePrompt();
|
|
1494
|
+
}
|
|
1495
|
+
catch { /* never let the update prompt crash boot */ }
|
|
1411
1496
|
// Bottom prompt hint — final line of the boot card.
|
|
1412
1497
|
display.write('\n');
|
|
1413
1498
|
display.write(display.bottomPromptHint() + '\n');
|
|
1414
1499
|
}
|
|
1500
|
+
/**
|
|
1501
|
+
* v4.5 update system — orchestrates the boot prompt. Lazy-imports
|
|
1502
|
+
* the update modules so non-boot code paths (e.g. test harness
|
|
1503
|
+
* sessions constructed without paths wired) don't pay the cost.
|
|
1504
|
+
*/
|
|
1505
|
+
async maybeShowBootUpdatePrompt() {
|
|
1506
|
+
if (!this.opts.paths)
|
|
1507
|
+
return;
|
|
1508
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1509
|
+
const cu = require('../../core/v4/update/checkUpdate');
|
|
1510
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1511
|
+
const md = require('../../core/v4/update/installMethodDetect');
|
|
1512
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1513
|
+
const ss = require('../../core/v4/update/skipState');
|
|
1514
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1515
|
+
const bp = require('./updateBootPrompt');
|
|
1516
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1517
|
+
const ei = require('../../core/v4/update/executeInstall');
|
|
1518
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1519
|
+
const ver = require('../../core/version');
|
|
1520
|
+
const status = await cu.checkForUpdate({ paths: this.opts.paths, installedVersion: ver.VERSION });
|
|
1521
|
+
if (!status.updateAvailable || !status.latest || status.skipped)
|
|
1522
|
+
return;
|
|
1523
|
+
const method = md.detectInstallMethod();
|
|
1524
|
+
const choice = await bp.showBootUpdatePrompt({
|
|
1525
|
+
status, method,
|
|
1526
|
+
display: { write: (s) => this.opts.display.write(s), dim: (s) => this.opts.display.dim(s) },
|
|
1527
|
+
});
|
|
1528
|
+
if (choice === 'install') {
|
|
1529
|
+
if (method.inProcessInstallSupported) {
|
|
1530
|
+
this.opts.display.write(`Installing aiden-runtime ${status.latest}…\n`);
|
|
1531
|
+
const result = await ei.executeInstall({ packageSpec: `aiden-runtime@${status.latest}` });
|
|
1532
|
+
if (result.success) {
|
|
1533
|
+
this.opts.display.write(` ✓ aiden-runtime ${result.installedVersion ?? status.latest} installed.\n`);
|
|
1534
|
+
this.opts.display.dim('Restart Aiden to apply: type /quit then re-run `aiden`.');
|
|
1535
|
+
}
|
|
1536
|
+
else {
|
|
1537
|
+
this.opts.display.warn(result.error ?? 'Install failed (no error message).');
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
else {
|
|
1541
|
+
this.opts.display.write(`To update, run:\n ${method.updateCommand(status.latest)}\n`);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
else if (choice === 'skip') {
|
|
1545
|
+
await cu.updateCacheFile(this.opts.paths, (current) => ss.applySkip(current, status.latest));
|
|
1546
|
+
this.opts.display.dim(` skipped ${status.latest}. Boot prompt resumes when a newer version ships.`);
|
|
1547
|
+
}
|
|
1548
|
+
// 'later' = no-op; prompt fires again next session.
|
|
1549
|
+
}
|
|
1415
1550
|
/** Phase 22 Task 4: state transitions for the right-most segment. */
|
|
1416
1551
|
setStatusState(state) {
|
|
1417
1552
|
this.statusState = state;
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
};
|
|
35
|
+
const CONFIG_DOTTED = {
|
|
36
|
+
sandbox: 'runtime_toggles.sandbox',
|
|
37
|
+
tce: 'runtime_toggles.tce',
|
|
38
|
+
browser_depth: 'runtime_toggles.browser_depth',
|
|
39
|
+
suggestions: 'runtime_toggles.suggestions',
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Apply a toggle change. When `ctx.config` is wired, persists to
|
|
43
|
+
* config.yaml. Otherwise the flip is in-process only (current
|
|
44
|
+
* session sees the new value; next process boot doesn't).
|
|
45
|
+
*
|
|
46
|
+
* Returns nothing — prints status via ctx.display.
|
|
47
|
+
*/
|
|
48
|
+
async function flip(key, value, ctx) {
|
|
49
|
+
const rt = (0, runtimeToggles_1.getRuntimeToggles)();
|
|
50
|
+
// Persist when a ConfigManager is wired.
|
|
51
|
+
if (ctx.config) {
|
|
52
|
+
try {
|
|
53
|
+
ctx.config.set(CONFIG_DOTTED[key], value);
|
|
54
|
+
await ctx.config.save();
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
ctx.display.warn(`[${key}] config.yaml save failed (${e instanceof Error ? e.message : String(e)}); ` +
|
|
58
|
+
`flip applies to this session only.`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
await rt.set(key, value, { persist: false });
|
|
62
|
+
printStatus(key, ctx);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Print the current state of one toggle. One-line output per
|
|
66
|
+
* Q-P8a-2(a):
|
|
67
|
+
*
|
|
68
|
+
* `Sandbox: ON (source: config)`
|
|
69
|
+
*
|
|
70
|
+
* `source` reveals which precedence layer provided the value —
|
|
71
|
+
* critical for debugging "why is it ON when my .env says 0".
|
|
72
|
+
*/
|
|
73
|
+
function printStatus(key, ctx) {
|
|
74
|
+
const snap = (0, runtimeToggles_1.getRuntimeToggles)().snapshot()[key];
|
|
75
|
+
const label = LABEL[key];
|
|
76
|
+
const state = snap.value ? 'ON' : 'OFF';
|
|
77
|
+
ctx.display.write(`${label}: ${state} (source: ${snap.source})\n`);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse the on/off/status subcommand. Returns null when the input
|
|
81
|
+
* is unrecognised (caller prints usage).
|
|
82
|
+
*/
|
|
83
|
+
function parseSubcommand(raw) {
|
|
84
|
+
const s = (raw ?? 'status').toLowerCase();
|
|
85
|
+
if (s === 'on' || s === 'enable' || s === '1' || s === 'true')
|
|
86
|
+
return 'on';
|
|
87
|
+
if (s === 'off' || s === 'disable' || s === '0' || s === 'false')
|
|
88
|
+
return 'off';
|
|
89
|
+
if (s === 'status' || s === '' || s === undefined)
|
|
90
|
+
return 'status';
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
@@ -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
|
+
};
|