aiden-runtime 4.1.4 → 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 +379 -53
- package/dist/cli/v4/callbacks.js +248 -0
- package/dist/cli/v4/chatSession.js +292 -4
- 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 +75 -3
- package/dist/cli/v4/display/capabilityCard.js +26 -0
- package/dist/cli/v4/display/progressBar.js +41 -8
- package/dist/cli/v4/display.js +258 -15
- package/dist/cli/v4/replyRenderer.js +31 -23
- package/dist/cli/v4/toolPreview.js +10 -0
- package/dist/cli/v4/updateBootPrompt.js +170 -0
- package/dist/core/playwrightBridge.js +129 -0
- package/dist/core/toolRegistry.js +7 -1
- package/dist/core/v4/aidenAgent.js +371 -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/loopTrace.js +257 -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/core/webSearch.js +64 -24
- 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/plugins/aiden-plugin-cdp-browser/.granted-permissions.json +8 -0
|
@@ -69,6 +69,11 @@ exports.renderProgressBar = renderProgressBar;
|
|
|
69
69
|
exports.formatTokens = formatTokens;
|
|
70
70
|
exports.formatDuration = formatDuration;
|
|
71
71
|
exports.renderMemoryConfirmations = renderMemoryConfirmations;
|
|
72
|
+
// v4.1.5+ Path A: env-var-gated loop trace logger. Captures tool-call
|
|
73
|
+
// sequence + system prompt + memory hashes when a turn shows loop
|
|
74
|
+
// symptoms (10+ calls OR 5+ consecutive same-name). Default off via
|
|
75
|
+
// `AIDEN_DEBUG_LOOP=1` env-var. Zero overhead when disabled.
|
|
76
|
+
const loopTrace_1 = require("../../core/v4/loopTrace");
|
|
72
77
|
const display_1 = require("./display");
|
|
73
78
|
// v4.1.4 Part 1.6 — per-turn token progress bar. Fed by `onProgress`
|
|
74
79
|
// events from the streaming adapter; hidden when the adapter doesn't
|
|
@@ -867,6 +872,21 @@ class ChatSession {
|
|
|
867
872
|
}
|
|
868
873
|
}
|
|
869
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 */ }
|
|
870
890
|
// Phase 30.2.1 — explore mode: short-circuit BEFORE building the
|
|
871
891
|
// turn-status spinner / agent call. The wizard skipped, so there's
|
|
872
892
|
// no real provider to talk to. Print a friendly redirect to /setup
|
|
@@ -934,6 +954,62 @@ class ChatSession {
|
|
|
934
954
|
const indicator = this.opts.display.activityIndicator('thinking');
|
|
935
955
|
let indicatorStopped = false;
|
|
936
956
|
let streamingActive = false;
|
|
957
|
+
// v4.1.5 Issue O — track whether this turn had any tool calls so
|
|
958
|
+
// we can emit a single muted rule between the tool trail and the
|
|
959
|
+
// reply header. Set true when the first tool's `before` phase
|
|
960
|
+
// fires (via the existing beforeFirstToolHook plumbing). Emitted
|
|
961
|
+
// once per turn — `separatorEmitted` gates idempotency against
|
|
962
|
+
// both streaming and non-streaming paths reaching the same hook.
|
|
963
|
+
//
|
|
964
|
+
// v4.1.5 Phase 1d (Q-OBV-b) — multi-tool separator regression:
|
|
965
|
+
// the prior v4.1.5 Phase 1c emission point was the streaming
|
|
966
|
+
// `onFirstDelta` callback, but that fires PER provider call
|
|
967
|
+
// (the agent resets `firstDeltaFired` each callProvider
|
|
968
|
+
// invocation), and on multi-tool turns where the model emits
|
|
969
|
+
// no preamble in early iterations + no preamble in the final
|
|
970
|
+
// reply iteration either, the relative ordering of "first
|
|
971
|
+
// delta" vs "first tool" could leave the flag/idempotency
|
|
972
|
+
// gate in an unexpected state. Definitive fix: tie emission
|
|
973
|
+
// to the FIRST STREAM BYTE LANDING ON SCREEN, which only
|
|
974
|
+
// happens once per turn regardless of how many provider
|
|
975
|
+
// iterations occurred. `firstStreamByteSeen` is the new gate;
|
|
976
|
+
// separator fires from inside `onDelta` BEFORE `streamPartial`
|
|
977
|
+
// writes the agent header.
|
|
978
|
+
let turnHadTools = false;
|
|
979
|
+
let separatorEmitted = false;
|
|
980
|
+
let firstStreamByteSeen = false;
|
|
981
|
+
// v4.1.5+ Path A: per-turn loop tracer (env-var gated, default off).
|
|
982
|
+
// Captures tool-call sequence + assembled system prompt + memory
|
|
983
|
+
// hashes + recent skills when a turn trips loop thresholds. The
|
|
984
|
+
// `onLoopWarning` callback surfaces a one-line dim hint to the
|
|
985
|
+
// user when consecutive-same-tool count crosses 8 — gives them a
|
|
986
|
+
// chance to Ctrl+C before the agent burns more budget.
|
|
987
|
+
const loopTracer = new loopTrace_1.LoopTracer({
|
|
988
|
+
paths: this.opts.paths,
|
|
989
|
+
providerId: this.currentProviderId,
|
|
990
|
+
modelId: this.currentModelId,
|
|
991
|
+
onLoopWarning: (line) => {
|
|
992
|
+
try {
|
|
993
|
+
this.opts.display.dim(line);
|
|
994
|
+
}
|
|
995
|
+
catch { /* defensive */ }
|
|
996
|
+
},
|
|
997
|
+
});
|
|
998
|
+
if (loopTracer.isEnabled()) {
|
|
999
|
+
loopTracer.setHistory(baseHistory);
|
|
1000
|
+
}
|
|
1001
|
+
const emitToolReplySeparator = () => {
|
|
1002
|
+
if (separatorEmitted || !turnHadTools)
|
|
1003
|
+
return;
|
|
1004
|
+
separatorEmitted = true;
|
|
1005
|
+
// Same chrome pattern as the existing pre-turn rule (line ~1100)
|
|
1006
|
+
// and the post-reply rule (line ~1297): two-space indent + the
|
|
1007
|
+
// body-width muted rule + newline. The 2-space indent is the
|
|
1008
|
+
// legacy convention used by adjacent rules; the v4.1.5 frame
|
|
1009
|
+
// gutter (3) is consciously NOT applied here so all three rules
|
|
1010
|
+
// in a turn share one left edge.
|
|
1011
|
+
this.opts.display.write(` ${this.opts.display.rule()}\n`);
|
|
1012
|
+
};
|
|
937
1013
|
const stopIndicatorOnce = () => {
|
|
938
1014
|
if (indicatorStopped)
|
|
939
1015
|
return;
|
|
@@ -946,12 +1022,55 @@ class ChatSession {
|
|
|
946
1022
|
this.opts.callbacks.setActivityIndicatorHooks?.({});
|
|
947
1023
|
}
|
|
948
1024
|
catch { /* defensive */ }
|
|
1025
|
+
// v4.1.5 Issue K — also clear the phase-verb sink so lifecycle
|
|
1026
|
+
// events fired during async cleanup don't try to update a
|
|
1027
|
+
// stopped indicator.
|
|
1028
|
+
try {
|
|
1029
|
+
this.opts.callbacks.setPhaseVerbHook?.(undefined);
|
|
1030
|
+
}
|
|
1031
|
+
catch { /* defensive */ }
|
|
1032
|
+
// v4.1.5+ Path A — clear the loop-trace sink so subsequent
|
|
1033
|
+
// turns don't fire into a stale tracer. Note: this clears the
|
|
1034
|
+
// HOOK, not the tracer's accumulated state — finalize() still
|
|
1035
|
+
// runs at end-of-try below to write the snapshot if thresholds
|
|
1036
|
+
// tripped.
|
|
1037
|
+
try {
|
|
1038
|
+
this.opts.callbacks.setToolTraceHook?.({});
|
|
1039
|
+
}
|
|
1040
|
+
catch { /* defensive */ }
|
|
949
1041
|
};
|
|
1042
|
+
// v4.1.5 Issue K — wire the per-turn phase-verb sink. Each
|
|
1043
|
+
// AidenAgent lifecycle event (memory refresh start, prompt built,
|
|
1044
|
+
// provider request start) flows through CliCallbacks and lands
|
|
1045
|
+
// here as a verb string ("refreshing memory" / "preparing prompt"
|
|
1046
|
+
// / "calling provider"). The closure captures the per-turn
|
|
1047
|
+
// indicator handle so verb mutations stay scoped to this turn.
|
|
1048
|
+
this.opts.callbacks.setPhaseVerbHook?.((verb) => {
|
|
1049
|
+
if (indicatorStopped)
|
|
1050
|
+
return;
|
|
1051
|
+
indicator.setVerb(verb);
|
|
1052
|
+
});
|
|
1053
|
+
// v4.1.5+ Path A — wire the loop-trace sink. Fires for EVERY tool
|
|
1054
|
+
// call (including hidden ones) so the trace captures the full
|
|
1055
|
+
// agent loop. Defensive — when AIDEN_DEBUG_LOOP is unset, the
|
|
1056
|
+
// tracer's `startTool`/`endTool` short-circuit immediately.
|
|
1057
|
+
this.opts.callbacks.setToolTraceHook?.({
|
|
1058
|
+
before: (id, name) => loopTracer.startTool(id, name),
|
|
1059
|
+
after: (id, name, args) => loopTracer.endTool(id, name, args),
|
|
1060
|
+
});
|
|
950
1061
|
// Phase 23.5 carried forward: stop the indicator the moment the
|
|
951
1062
|
// first tool row prints — the row itself is the activity surface
|
|
952
1063
|
// during a tool. Part 1.6 then resumes via `afterEachTool` so the
|
|
953
1064
|
// post-tool gap has its own indicator paint.
|
|
954
|
-
|
|
1065
|
+
//
|
|
1066
|
+
// v4.1.5 Issue O — also flip `turnHadTools = true` so the
|
|
1067
|
+
// separator emits before the reply header. Single hook captures
|
|
1068
|
+
// "any tool ran this turn" cleanly (it only fires for the FIRST
|
|
1069
|
+
// tool of the turn — subsequent tools don't re-trigger).
|
|
1070
|
+
this.opts.callbacks.setBeforeFirstToolHook?.(() => {
|
|
1071
|
+
turnHadTools = true;
|
|
1072
|
+
stopIndicatorOnce();
|
|
1073
|
+
});
|
|
955
1074
|
// Part 1.6: pause/resume hooks around every tool row. The
|
|
956
1075
|
// `beforeTool` hook fires before EACH tool row writes (not just
|
|
957
1076
|
// the first), so multi-tool sequences also keep the indicator
|
|
@@ -991,10 +1110,32 @@ class ChatSession {
|
|
|
991
1110
|
? () => {
|
|
992
1111
|
stopIndicatorOnce();
|
|
993
1112
|
streamingActive = true;
|
|
1113
|
+
// v4.1.5 Phase 1d (Q-OBV-b) — separator emission MOVED
|
|
1114
|
+
// out of onFirstDelta because that callback fires per
|
|
1115
|
+
// provider-call iteration (firstDeltaFired resets each
|
|
1116
|
+
// callProvider invocation). The separator-emit now
|
|
1117
|
+
// lives in onDelta below, gated by `firstStreamByteSeen`
|
|
1118
|
+
// which only flips once per turn.
|
|
994
1119
|
}
|
|
995
1120
|
: undefined,
|
|
996
1121
|
onDelta: streamingEnabled
|
|
997
1122
|
? (text) => {
|
|
1123
|
+
// v4.1.5 Phase 1d (Q-OBV-b) — definitive separator
|
|
1124
|
+
// emission point. This is the FIRST text byte landing
|
|
1125
|
+
// on screen this turn. Fires the muted rule BEFORE
|
|
1126
|
+
// streamPartial writes the `┃ Aiden` header so the
|
|
1127
|
+
// visual order is:
|
|
1128
|
+
// ┊ tool rows...
|
|
1129
|
+
// ──────────── ← separator
|
|
1130
|
+
// ┃ Aiden
|
|
1131
|
+
// {text}
|
|
1132
|
+
// Idempotent via `firstStreamByteSeen` + the
|
|
1133
|
+
// `separatorEmitted` flag inside emitToolReplySeparator.
|
|
1134
|
+
// No-op when no tool fired (turnHadTools=false).
|
|
1135
|
+
if (!firstStreamByteSeen) {
|
|
1136
|
+
firstStreamByteSeen = true;
|
|
1137
|
+
emitToolReplySeparator();
|
|
1138
|
+
}
|
|
998
1139
|
// v4.1.4 Part 1.6: bar lives ABOVE streamed text. Hide
|
|
999
1140
|
// it before each delta writes so the stream output
|
|
1000
1141
|
// doesn't land on the bar's line. The bar repaints on
|
|
@@ -1052,11 +1193,56 @@ class ChatSession {
|
|
|
1052
1193
|
if (result.toolCallTrace && result.toolCallTrace.length > 0) {
|
|
1053
1194
|
this.sessionToolTrace.push(...result.toolCallTrace);
|
|
1054
1195
|
}
|
|
1055
|
-
//
|
|
1056
|
-
//
|
|
1057
|
-
|
|
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
|
+
//
|
|
1215
|
+
// v4.1.5 Issue O — non-streaming reply path. Emit the muted
|
|
1216
|
+
// rule between the tool trail and the agent header before
|
|
1217
|
+
// the one-shot reply lands. Idempotent + tool-gated by
|
|
1218
|
+
// `emitToolReplySeparator`.
|
|
1219
|
+
emitToolReplySeparator();
|
|
1058
1220
|
this.opts.display.write(this.opts.display.agentTurn(result.finalContent));
|
|
1059
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
|
+
}
|
|
1060
1246
|
if (this.sessionId) {
|
|
1061
1247
|
// Only persist the new tail of messages — what got added this turn.
|
|
1062
1248
|
const newSlice = this.history.slice(turnStart);
|
|
@@ -1064,10 +1250,36 @@ class ChatSession {
|
|
|
1064
1250
|
}
|
|
1065
1251
|
this.setStatusState({ kind: 'ready' });
|
|
1066
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
|
+
}
|
|
1067
1267
|
// Tier-3.1a: dim full-width rule between the agent reply and the
|
|
1068
1268
|
// post-turn status footer.
|
|
1069
1269
|
this.opts.display.write(` ${this.opts.display.rule()}\n`);
|
|
1070
1270
|
this.renderStatusLine();
|
|
1271
|
+
// v4.1.5+ Path A — finalize the loop trace. No-op if the env
|
|
1272
|
+
// var is unset OR if the turn didn't trip any threshold. When
|
|
1273
|
+
// it DOES emit, the snapshot path goes to a dim status line so
|
|
1274
|
+
// the user (and any teammate they're sharing the log with)
|
|
1275
|
+
// knows where to grab the diagnostic file.
|
|
1276
|
+
try {
|
|
1277
|
+
const snapPath = await loopTracer.finalize();
|
|
1278
|
+
if (snapPath) {
|
|
1279
|
+
this.opts.display.dim(`[loop-trace] wrote ${snapPath}`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
catch { /* defensive */ }
|
|
1071
1283
|
}
|
|
1072
1284
|
catch (err) {
|
|
1073
1285
|
stopIndicatorOnce();
|
|
@@ -1116,6 +1328,16 @@ class ChatSession {
|
|
|
1116
1328
|
}
|
|
1117
1329
|
this.setStatusState({ kind: 'ready' });
|
|
1118
1330
|
this.lastTurnElapsedMs = Date.now() - turnStartedAt;
|
|
1331
|
+
// v4.1.5+ Path A — finalize the loop trace on the error path
|
|
1332
|
+
// too. Loop patterns that ended in an error are exactly the
|
|
1333
|
+
// ones most worth capturing for diagnosis.
|
|
1334
|
+
try {
|
|
1335
|
+
const snapPath = await loopTracer.finalize();
|
|
1336
|
+
if (snapPath) {
|
|
1337
|
+
this.opts.display.dim(`[loop-trace] wrote ${snapPath}`);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
catch { /* defensive */ }
|
|
1119
1341
|
}
|
|
1120
1342
|
}
|
|
1121
1343
|
// ── Startup card (Phase 26.2.4: neofetch-style sectioned) ──────────
|
|
@@ -1196,6 +1418,9 @@ class ChatSession {
|
|
|
1196
1418
|
providerOk: !this.opts.unconfigured,
|
|
1197
1419
|
version: version_1.VERSION,
|
|
1198
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');
|
|
1199
1424
|
// v4.1.3-prebump: dim source annotation under the pills row so the
|
|
1200
1425
|
// user can see WHY this provider/model was chosen — closes the
|
|
1201
1426
|
// information gap that made Case 3 (persisted-config) look like a
|
|
@@ -1255,10 +1480,73 @@ class ChatSession {
|
|
|
1255
1480
|
}
|
|
1256
1481
|
// Scroll footer (parchment at ≥80 cols, single-line credits below).
|
|
1257
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 */ }
|
|
1258
1496
|
// Bottom prompt hint — final line of the boot card.
|
|
1259
1497
|
display.write('\n');
|
|
1260
1498
|
display.write(display.bottomPromptHint() + '\n');
|
|
1261
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
|
+
}
|
|
1262
1550
|
/** Phase 22 Task 4: state transitions for the right-most segment. */
|
|
1263
1551
|
setStatusState(state) {
|
|
1264
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
|
+
};
|