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
|
@@ -0,0 +1,214 @@
|
|
|
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
|
+
* core/v4/runtimeToggles.ts — v4.5 Phase 8a.
|
|
10
|
+
*
|
|
11
|
+
* Single source of truth for the v4.2/v4.3/v4.4 subsystem
|
|
12
|
+
* default-on toggles (TCE, browser depth, sandbox). Replaces the
|
|
13
|
+
* direct `process.env.AIDEN_*` reads scattered across:
|
|
14
|
+
*
|
|
15
|
+
* - core/v4/sandboxConfig.ts (AIDEN_SANDBOX)
|
|
16
|
+
* - core/v4/turnState.ts (AIDEN_TCE)
|
|
17
|
+
* - core/v4/browserState.ts (AIDEN_BROWSER_DEPTH)
|
|
18
|
+
*
|
|
19
|
+
* with a centralised resolver that supports:
|
|
20
|
+
*
|
|
21
|
+
* - **Live flip** via slash commands (/sandbox on|off, /tce on|off,
|
|
22
|
+
* /browser-depth on|off). The slash command updates the in-process
|
|
23
|
+
* state + persists to config.yaml, and fires onChange callbacks so
|
|
24
|
+
* cached consumers (sandboxConfig's singleton) invalidate.
|
|
25
|
+
*
|
|
26
|
+
* - **Persistence** across restarts via
|
|
27
|
+
* `<AIDEN_HOME>/config.yaml :: runtime_toggles.{sandbox,tce,browser_depth}`.
|
|
28
|
+
*
|
|
29
|
+
* - **Env-var precedence** (Q-P8a-1a): explicit env var > config.yaml >
|
|
30
|
+
* default (true for all three). Matches the existing AIDEN_*
|
|
31
|
+
* escape-hatch contract.
|
|
32
|
+
*
|
|
33
|
+
* The singleton is initialised by the CLI at boot via `initRuntimeToggles`
|
|
34
|
+
* with a ConfigProvider seam. Core modules that read the toggles call
|
|
35
|
+
* `getRuntimeToggles().isEnabled(key)` — when the singleton hasn't been
|
|
36
|
+
* initialised (test bench, core-only invocation), an env-only fallback
|
|
37
|
+
* resolver is used so the modules keep working with their pre-v4.5
|
|
38
|
+
* semantics.
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports._TOGGLE_KEYS = void 0;
|
|
42
|
+
exports.buildRuntimeToggles = buildRuntimeToggles;
|
|
43
|
+
exports.getRuntimeToggles = getRuntimeToggles;
|
|
44
|
+
exports.initRuntimeToggles = initRuntimeToggles;
|
|
45
|
+
exports._resetRuntimeTogglesForTests = _resetRuntimeTogglesForTests;
|
|
46
|
+
// ── Env var mapping ────────────────────────────────────────────────────────
|
|
47
|
+
const ENV_VAR = {
|
|
48
|
+
sandbox: 'AIDEN_SANDBOX',
|
|
49
|
+
tce: 'AIDEN_TCE',
|
|
50
|
+
browser_depth: 'AIDEN_BROWSER_DEPTH',
|
|
51
|
+
// v4.5 Phase 8b — contextual capability suggestions. Rarely set as
|
|
52
|
+
// env (this is mostly a UX toggle) but included for symmetry with
|
|
53
|
+
// the other subsystem toggles.
|
|
54
|
+
suggestions: 'AIDEN_SUGGESTIONS',
|
|
55
|
+
// v4.6 Phase 2M — keyword-based per-turn tool narrowing.
|
|
56
|
+
// Default OFF: smart models (GPT-5.5, Claude Sonnet 4.5+, Opus)
|
|
57
|
+
// pick tools fine from a full catalog. PlannerGuard adds latency
|
|
58
|
+
// (1 LLM call when mode=llm_classified) and occasionally strips
|
|
59
|
+
// tools the model genuinely needed. Opt in for small local models
|
|
60
|
+
// that get overwhelmed by 50+ tool schemas.
|
|
61
|
+
planner_guard: 'AIDEN_PLANNER_GUARD',
|
|
62
|
+
};
|
|
63
|
+
const CONFIG_KEY = {
|
|
64
|
+
sandbox: 'runtime_toggles.sandbox',
|
|
65
|
+
tce: 'runtime_toggles.tce',
|
|
66
|
+
browser_depth: 'runtime_toggles.browser_depth',
|
|
67
|
+
suggestions: 'runtime_toggles.suggestions',
|
|
68
|
+
planner_guard: 'runtime_toggles.planner_guard',
|
|
69
|
+
};
|
|
70
|
+
const ALL_KEYS = [
|
|
71
|
+
'sandbox', 'tce', 'browser_depth', 'suggestions', 'planner_guard',
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* v4.6 Phase 2M — per-key default. Pre-2M every toggle defaulted to
|
|
75
|
+
* `true` (sandbox/tce/browser-depth/suggestions all ship on); the
|
|
76
|
+
* `planner_guard` toggle is the first to default `false`, so the
|
|
77
|
+
* resolver needs a per-key default map rather than a hardcoded `true`.
|
|
78
|
+
*
|
|
79
|
+
* Smart models (GPT-5.5, Claude Sonnet 4.5+, Opus) pick from the
|
|
80
|
+
* full tool catalog without help — keyword-based narrowing is a
|
|
81
|
+
* legacy workaround for smaller local models, opt in when needed.
|
|
82
|
+
*/
|
|
83
|
+
const DEFAULT_VALUE = {
|
|
84
|
+
sandbox: true,
|
|
85
|
+
tce: true,
|
|
86
|
+
browser_depth: true,
|
|
87
|
+
suggestions: true,
|
|
88
|
+
planner_guard: false,
|
|
89
|
+
};
|
|
90
|
+
// ── Resolver primitives ────────────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Strict env interpretation matching existing v4.2/v4.3/v4.4
|
|
93
|
+
* semantics: literal `'0'` (or `'false'` for forgiveness) means off;
|
|
94
|
+
* unset means defer to next leg; anything else means on. Returns
|
|
95
|
+
* `null` when the env var is unset / empty — caller falls through to
|
|
96
|
+
* config or default.
|
|
97
|
+
*/
|
|
98
|
+
function readEnv(env, key) {
|
|
99
|
+
const raw = env[ENV_VAR[key]];
|
|
100
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
101
|
+
return null;
|
|
102
|
+
const trimmed = raw.trim().toLowerCase();
|
|
103
|
+
if (trimmed === '0' || trimmed === 'false' || trimmed === 'off' || trimmed === 'no') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
function readConfig(cfg, key) {
|
|
109
|
+
if (!cfg)
|
|
110
|
+
return null;
|
|
111
|
+
const raw = cfg(CONFIG_KEY[key]);
|
|
112
|
+
if (raw === undefined || raw === null)
|
|
113
|
+
return null;
|
|
114
|
+
if (typeof raw === 'boolean')
|
|
115
|
+
return raw;
|
|
116
|
+
if (typeof raw === 'string') {
|
|
117
|
+
const t = raw.trim().toLowerCase();
|
|
118
|
+
if (t === 'true' || t === '1' || t === 'on' || t === 'yes')
|
|
119
|
+
return true;
|
|
120
|
+
if (t === 'false' || t === '0' || t === 'off' || t === 'no')
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (typeof raw === 'number')
|
|
124
|
+
return raw !== 0;
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
// ── Singleton ──────────────────────────────────────────────────────────────
|
|
128
|
+
let _singleton = null;
|
|
129
|
+
/**
|
|
130
|
+
* Build a RuntimeToggles instance bound to the supplied deps.
|
|
131
|
+
* Public so tests can construct isolated instances.
|
|
132
|
+
*/
|
|
133
|
+
function buildRuntimeToggles(deps = {}) {
|
|
134
|
+
const env = deps.env ?? process.env;
|
|
135
|
+
// In-process overrides — set() updates this map; subsequent
|
|
136
|
+
// isEnabled() reads see the override before falling through to
|
|
137
|
+
// env/config/default.
|
|
138
|
+
const overrides = new Map();
|
|
139
|
+
const subscribers = new Map();
|
|
140
|
+
function resolve(key) {
|
|
141
|
+
// 1. env (Q-P8a-1a — explicit env always wins)
|
|
142
|
+
const envValue = readEnv(env, key);
|
|
143
|
+
if (envValue !== null)
|
|
144
|
+
return { value: envValue, source: 'env' };
|
|
145
|
+
// 2. in-process override (slash-command flip not yet persisted)
|
|
146
|
+
if (overrides.has(key))
|
|
147
|
+
return { value: overrides.get(key), source: 'config' };
|
|
148
|
+
// 3. config.yaml
|
|
149
|
+
const cfgValue = readConfig(deps.configRead, key);
|
|
150
|
+
if (cfgValue !== null)
|
|
151
|
+
return { value: cfgValue, source: 'config' };
|
|
152
|
+
// 4. default (v4.6 Phase 2M — per-key, see DEFAULT_VALUE)
|
|
153
|
+
return { value: DEFAULT_VALUE[key], source: 'default' };
|
|
154
|
+
}
|
|
155
|
+
function fire(key) {
|
|
156
|
+
const set = subscribers.get(key);
|
|
157
|
+
if (!set)
|
|
158
|
+
return;
|
|
159
|
+
for (const cb of set) {
|
|
160
|
+
try {
|
|
161
|
+
cb();
|
|
162
|
+
}
|
|
163
|
+
catch { /* never let an invalidation callback crash the flip */ }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
isEnabled(key) { return resolve(key).value; },
|
|
168
|
+
async set(key, value, opts = {}) {
|
|
169
|
+
overrides.set(key, value);
|
|
170
|
+
if (opts.persist !== false && deps.configWriteAndSave) {
|
|
171
|
+
await deps.configWriteAndSave(CONFIG_KEY[key], value);
|
|
172
|
+
}
|
|
173
|
+
fire(key);
|
|
174
|
+
},
|
|
175
|
+
snapshot() {
|
|
176
|
+
const out = {};
|
|
177
|
+
for (const k of ALL_KEYS)
|
|
178
|
+
out[k] = resolve(k);
|
|
179
|
+
return out;
|
|
180
|
+
},
|
|
181
|
+
onChange(key, cb) {
|
|
182
|
+
let set = subscribers.get(key);
|
|
183
|
+
if (!set) {
|
|
184
|
+
set = new Set();
|
|
185
|
+
subscribers.set(key, set);
|
|
186
|
+
}
|
|
187
|
+
set.add(cb);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Return the process-wide RuntimeToggles. When `initRuntimeToggles`
|
|
193
|
+
* hasn't been called, returns a env-only fallback resolver so core
|
|
194
|
+
* modules (sandboxConfig, turnState, browserState) keep working in
|
|
195
|
+
* test benches + core-only invocations.
|
|
196
|
+
*/
|
|
197
|
+
function getRuntimeToggles() {
|
|
198
|
+
if (!_singleton)
|
|
199
|
+
_singleton = buildRuntimeToggles();
|
|
200
|
+
return _singleton;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Initialise the singleton with the CLI's ConfigManager seam. Called
|
|
204
|
+
* once by `aidenCLI.ts::buildAgentRuntime` after config.yaml is loaded.
|
|
205
|
+
*/
|
|
206
|
+
function initRuntimeToggles(deps) {
|
|
207
|
+
_singleton = buildRuntimeToggles(deps);
|
|
208
|
+
return _singleton;
|
|
209
|
+
}
|
|
210
|
+
/** Test-only reset. */
|
|
211
|
+
function _resetRuntimeTogglesForTests() {
|
|
212
|
+
_singleton = null;
|
|
213
|
+
}
|
|
214
|
+
exports._TOGGLE_KEYS = ALL_KEYS;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveRealPath = resolveRealPath;
|
|
7
|
+
exports._clearRealPathCacheForTests = _clearRealPathCacheForTests;
|
|
8
|
+
exports.inferDefaultRiskTier = inferDefaultRiskTier;
|
|
9
|
+
exports.readSandboxConfig = readSandboxConfig;
|
|
10
|
+
exports.getSandboxConfig = getSandboxConfig;
|
|
11
|
+
exports._resetSandboxConfigForTests = _resetSandboxConfigForTests;
|
|
12
|
+
/**
|
|
13
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
14
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
15
|
+
*
|
|
16
|
+
* Aiden — local-first agent.
|
|
17
|
+
*/
|
|
18
|
+
const runtimeToggles_1 = require("./runtimeToggles");
|
|
19
|
+
/**
|
|
20
|
+
* core/v4/sandboxConfig.ts — v4.4 Phase 1: Execution sandbox configuration.
|
|
21
|
+
*
|
|
22
|
+
* Single source of truth for sandbox enablement + resource policies +
|
|
23
|
+
* filesystem allow/deny lists + Docker hardening flags. Read from
|
|
24
|
+
* environment variables at construction time (matches v4.2's TurnState
|
|
25
|
+
* + v4.3's BrowserState env-driven pattern).
|
|
26
|
+
*
|
|
27
|
+
* Phase 1 ships the config types + reader + default-tier inference;
|
|
28
|
+
* downstream phases consume:
|
|
29
|
+
* - Phase 2 — fsAllowList / fsDenyList used by file_* tools
|
|
30
|
+
* - Phase 3 — defaultBackend / resourceLimits / networkMode /
|
|
31
|
+
* persistent / idleReaperMs used by shell_exec + Docker
|
|
32
|
+
* backend (long-lived container reuse, hardening flags)
|
|
33
|
+
* - Phase 4 — dryRun used by all `mutates: true` tools
|
|
34
|
+
* - Phase 5 — riskTier annotations consumed by FailureClassifier +
|
|
35
|
+
* ApprovalEngine (as a FLOOR; patterns can escalate)
|
|
36
|
+
*
|
|
37
|
+
* v4.4 Phase 6 — default-on transition. AIDEN_SANDBOX is now
|
|
38
|
+
* enabled by default; users opt out with `AIDEN_SANDBOX=0`. The
|
|
39
|
+
* strict `!== '0'` flip mirrors v4.2 Phase 6 (TCE) + v4.3 Phase 6
|
|
40
|
+
* (browser depth) semantics exactly.
|
|
41
|
+
*
|
|
42
|
+
* AIDEN_DRYRUN is orthogonal — independent flag, independent semantics.
|
|
43
|
+
* Phase 6 does NOT flip dry-run; it stays opt-in by design (dry-run
|
|
44
|
+
* is a deliberate "preview-only" mode users opt into, not a default).
|
|
45
|
+
*
|
|
46
|
+
* Pure module — no I/O, no side effects, no Playwright/Docker
|
|
47
|
+
* dependencies. Just env-var reads + path normalization helpers.
|
|
48
|
+
* Easy to unit test by passing a stubbed `env` argument.
|
|
49
|
+
*/
|
|
50
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
51
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
52
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
53
|
+
// ── Defaults ────────────────────────────────────────────────────────────────
|
|
54
|
+
const DEFAULT_RESOURCE_LIMITS = {
|
|
55
|
+
memory: '1g',
|
|
56
|
+
cpus: '2',
|
|
57
|
+
pidsLimit: 256,
|
|
58
|
+
};
|
|
59
|
+
const DEFAULT_IDLE_REAPER_MS = 5 * 60 * 1000; // 5 minutes
|
|
60
|
+
const DEFAULT_IMAGE = 'node:22-alpine';
|
|
61
|
+
/**
|
|
62
|
+
* v4.4 Phase 2 — default write-permitted paths. Real-resolved at
|
|
63
|
+
* config-build time to handle symlinked HOME / cwd. Caller can
|
|
64
|
+
* extend via `AIDEN_SANDBOX_ALLOW=p1:p2:...`.
|
|
65
|
+
*/
|
|
66
|
+
function buildDefaultAllowList() {
|
|
67
|
+
const home = node_os_1.default.homedir();
|
|
68
|
+
const tmp = node_os_1.default.tmpdir();
|
|
69
|
+
const cwd = process.cwd();
|
|
70
|
+
const paths = [
|
|
71
|
+
cwd,
|
|
72
|
+
node_path_1.default.join(home, 'Documents'),
|
|
73
|
+
node_path_1.default.join(home, 'Downloads'),
|
|
74
|
+
node_path_1.default.join(home, 'Desktop'),
|
|
75
|
+
tmp,
|
|
76
|
+
];
|
|
77
|
+
return resolveRealPaths(paths);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* v4.4 Phase 2 — default write-denied paths. Always wins over the
|
|
81
|
+
* allow list. Mirrors the consult-shaped deny-list pattern (sensitive
|
|
82
|
+
* configs, system dirs).
|
|
83
|
+
*/
|
|
84
|
+
function buildDefaultDenyList() {
|
|
85
|
+
const home = node_os_1.default.homedir();
|
|
86
|
+
const paths = [
|
|
87
|
+
node_path_1.default.join(home, '.ssh'),
|
|
88
|
+
node_path_1.default.join(home, '.aws'),
|
|
89
|
+
node_path_1.default.join(home, '.gnupg'),
|
|
90
|
+
node_path_1.default.join(home, '.env'),
|
|
91
|
+
node_path_1.default.join(home, '.netrc'),
|
|
92
|
+
node_path_1.default.join(home, '.pgpass'),
|
|
93
|
+
node_path_1.default.join(home, '.npmrc'),
|
|
94
|
+
node_path_1.default.join(home, '.pypirc'),
|
|
95
|
+
node_path_1.default.join(home, '.bashrc'),
|
|
96
|
+
node_path_1.default.join(home, '.zshrc'),
|
|
97
|
+
node_path_1.default.join(home, '.profile'),
|
|
98
|
+
'/etc',
|
|
99
|
+
'/var',
|
|
100
|
+
'/usr',
|
|
101
|
+
'/boot',
|
|
102
|
+
'/sys',
|
|
103
|
+
'/proc',
|
|
104
|
+
];
|
|
105
|
+
return resolveRealPaths(paths);
|
|
106
|
+
}
|
|
107
|
+
// ── Path normalization ──────────────────────────────────────────────────────
|
|
108
|
+
const _realPathCache = new Map();
|
|
109
|
+
/**
|
|
110
|
+
* Resolve a path to its canonical absolute form. `path.resolve` first
|
|
111
|
+
* (handles relative + `..`); then `fs.realpathSync` to follow symlinks.
|
|
112
|
+
* Symlink resolution defeats the bypass attack where an allowlisted
|
|
113
|
+
* directory contains a symlink to a denied path.
|
|
114
|
+
*
|
|
115
|
+
* Results cached for the lifetime of the module (paths rarely change
|
|
116
|
+
* during a process; the cache hit rate is high on the file-tool path
|
|
117
|
+
* where every call resolves the same handful of allowlist entries).
|
|
118
|
+
*
|
|
119
|
+
* Falls back gracefully when the path doesn't exist (returns the
|
|
120
|
+
* resolved-but-unrealpath form) — caller may be checking a path
|
|
121
|
+
* about to be created.
|
|
122
|
+
*/
|
|
123
|
+
function resolveRealPath(input) {
|
|
124
|
+
if (_realPathCache.has(input))
|
|
125
|
+
return _realPathCache.get(input);
|
|
126
|
+
const resolved = node_path_1.default.resolve(input);
|
|
127
|
+
let real = resolved;
|
|
128
|
+
try {
|
|
129
|
+
real = node_fs_1.default.realpathSync.native ? node_fs_1.default.realpathSync.native(resolved) : node_fs_1.default.realpathSync(resolved);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Path may not exist yet (e.g. file_write target). Use the
|
|
133
|
+
// resolved form; symlink-bypass on a non-existent path isn't
|
|
134
|
+
// a real attack vector.
|
|
135
|
+
}
|
|
136
|
+
_realPathCache.set(input, real);
|
|
137
|
+
return real;
|
|
138
|
+
}
|
|
139
|
+
/** Resolve an array of paths to their canonical forms; deduplicate. */
|
|
140
|
+
function resolveRealPaths(paths) {
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const p of paths) {
|
|
144
|
+
const real = resolveRealPath(p);
|
|
145
|
+
if (!seen.has(real)) {
|
|
146
|
+
seen.add(real);
|
|
147
|
+
out.push(real);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
/** Public for tests — clears the realpath cache so env-var changes
|
|
153
|
+
* in test isolation pick up fresh resolutions. Production code never
|
|
154
|
+
* calls this. */
|
|
155
|
+
function _clearRealPathCacheForTests() {
|
|
156
|
+
_realPathCache.clear();
|
|
157
|
+
}
|
|
158
|
+
// ── Env-var parsing helpers ─────────────────────────────────────────────────
|
|
159
|
+
function parseList(raw) {
|
|
160
|
+
if (!raw)
|
|
161
|
+
return [];
|
|
162
|
+
return raw.split(':').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
163
|
+
}
|
|
164
|
+
function parseIntSafe(raw, fallback) {
|
|
165
|
+
if (raw === undefined || raw === null || raw === '')
|
|
166
|
+
return fallback;
|
|
167
|
+
const n = Number.parseInt(raw, 10);
|
|
168
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
169
|
+
}
|
|
170
|
+
function parseNetworkMode(raw) {
|
|
171
|
+
if (raw === 'none')
|
|
172
|
+
return 'none';
|
|
173
|
+
return 'bridge'; // bridge for unset / 'bridge' / junk
|
|
174
|
+
}
|
|
175
|
+
// ── Risk-tier inference ─────────────────────────────────────────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Default risk tier for a tool that doesn't carry an explicit
|
|
178
|
+
* `riskTier` annotation. Leverages the existing `mutates: boolean`
|
|
179
|
+
* field — mutating tools default to `caution`, read-only tools
|
|
180
|
+
* default to `safe`. Plugin tools without annotation get a safe
|
|
181
|
+
* default for free.
|
|
182
|
+
*
|
|
183
|
+
* Phase 5 ApprovalEngine integration treats explicit annotations as
|
|
184
|
+
* a FLOOR — DANGEROUS_PATTERNS can escalate but never demote. The
|
|
185
|
+
* inference here is the floor when no annotation exists at all.
|
|
186
|
+
*/
|
|
187
|
+
function inferDefaultRiskTier(mutates) {
|
|
188
|
+
return mutates ? 'caution' : 'safe';
|
|
189
|
+
}
|
|
190
|
+
// ── Reader ──────────────────────────────────────────────────────────────────
|
|
191
|
+
/**
|
|
192
|
+
* Pure factory. Reads env vars + defaults into a frozen-snapshot
|
|
193
|
+
* SandboxConfig. Idempotent for a given env. The CLI calls this
|
|
194
|
+
* once at boot via the singleton factory below; tests pass a custom
|
|
195
|
+
* `env` argument.
|
|
196
|
+
*/
|
|
197
|
+
function readSandboxConfig(env = process.env) {
|
|
198
|
+
// v4.4 Phase 6 — default-on transition. AIDEN_SANDBOX is now
|
|
199
|
+
// enabled by default; users opt out with `AIDEN_SANDBOX=0`.
|
|
200
|
+
//
|
|
201
|
+
// v4.5 Phase 8a — the actual read goes through the runtimeToggles
|
|
202
|
+
// singleton so /sandbox slash-command flips and config.yaml
|
|
203
|
+
// overrides take effect without restart. When the singleton was
|
|
204
|
+
// initialised by the CLI it consults env > config.yaml > default;
|
|
205
|
+
// when not initialised (test bench) it falls back to env > default
|
|
206
|
+
// — the existing semantics.
|
|
207
|
+
// unset / '1' / 'true' / junk → enabled
|
|
208
|
+
// '0' → disabled
|
|
209
|
+
// Mirrors v4.2 Phase 6 (TCE) + v4.3 Phase 6 (browser depth)
|
|
210
|
+
// semantics exactly. The strict `!== '0'` check matches the
|
|
211
|
+
// pattern those phases set: any explicit "off" value disables;
|
|
212
|
+
// everything else (including unset) enables.
|
|
213
|
+
// v4.5 Phase 8a — when env (the readSandboxConfig input) is not the
|
|
214
|
+
// process env (test bench passing a custom env), honour the input
|
|
215
|
+
// directly to keep test isolation. Otherwise route through the
|
|
216
|
+
// runtimeToggles singleton.
|
|
217
|
+
const enabled = env === process.env
|
|
218
|
+
? (0, runtimeToggles_1.getRuntimeToggles)().isEnabled('sandbox')
|
|
219
|
+
: env.AIDEN_SANDBOX !== '0';
|
|
220
|
+
// Allow/deny lists: defaults + user-provided extensions.
|
|
221
|
+
const customAllow = parseList(env.AIDEN_SANDBOX_ALLOW).map(resolveRealPath);
|
|
222
|
+
const customDeny = parseList(env.AIDEN_SANDBOX_DENY).map(resolveRealPath);
|
|
223
|
+
const fsAllowList = Array.from(new Set([...buildDefaultAllowList(), ...customAllow]));
|
|
224
|
+
const fsDenyList = Array.from(new Set([...buildDefaultDenyList(), ...customDeny]));
|
|
225
|
+
// Resource limits — string values pass through Docker as-is.
|
|
226
|
+
const resourceLimits = {
|
|
227
|
+
memory: env.AIDEN_SANDBOX_MEMORY ?? DEFAULT_RESOURCE_LIMITS.memory,
|
|
228
|
+
cpus: env.AIDEN_SANDBOX_CPUS ?? DEFAULT_RESOURCE_LIMITS.cpus,
|
|
229
|
+
pidsLimit: parseIntSafe(env.AIDEN_SANDBOX_PIDS, DEFAULT_RESOURCE_LIMITS.pidsLimit),
|
|
230
|
+
};
|
|
231
|
+
const networkMode = parseNetworkMode(env.AIDEN_SANDBOX_NETWORK);
|
|
232
|
+
const persistent = env.AIDEN_SANDBOX_PERSISTENT !== '0'; // default true
|
|
233
|
+
const idleReaperMs = parseIntSafe(env.AIDEN_SANDBOX_IDLE_MS, DEFAULT_IDLE_REAPER_MS);
|
|
234
|
+
const dryRun = env.AIDEN_DRYRUN === '1';
|
|
235
|
+
const image = typeof env.AIDEN_SANDBOX_IMAGE === 'string' && env.AIDEN_SANDBOX_IMAGE.trim().length > 0
|
|
236
|
+
? env.AIDEN_SANDBOX_IMAGE.trim()
|
|
237
|
+
: DEFAULT_IMAGE;
|
|
238
|
+
// Phase 3 will route to 'docker' when enabled AND docker is
|
|
239
|
+
// available. Phase 1 reports the abstract default — Phase 3's
|
|
240
|
+
// runtime probe decides the actual route.
|
|
241
|
+
const defaultBackend = enabled ? 'docker' : 'local';
|
|
242
|
+
return {
|
|
243
|
+
enabled,
|
|
244
|
+
fsAllowList,
|
|
245
|
+
fsDenyList,
|
|
246
|
+
defaultBackend,
|
|
247
|
+
persistent,
|
|
248
|
+
resourceLimits,
|
|
249
|
+
networkMode,
|
|
250
|
+
idleReaperMs,
|
|
251
|
+
dryRun,
|
|
252
|
+
image,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// ── Singleton ───────────────────────────────────────────────────────────────
|
|
256
|
+
let _singleton = null;
|
|
257
|
+
let _toggleHookInstalled = false;
|
|
258
|
+
/**
|
|
259
|
+
* Read the singleton sandbox config. Initialized on first call from
|
|
260
|
+
* `process.env` (matches v4.2 TurnState / v4.3 BrowserState lifecycle).
|
|
261
|
+
* Tests construct fresh configs via `readSandboxConfig(env)` directly
|
|
262
|
+
* — the singleton path is for production CLI startup.
|
|
263
|
+
*/
|
|
264
|
+
function getSandboxConfig() {
|
|
265
|
+
if (!_toggleHookInstalled) {
|
|
266
|
+
// v4.5 Phase 8a — invalidate the singleton when /sandbox toggles.
|
|
267
|
+
// Registered lazily on first read so test benches that build
|
|
268
|
+
// their own runtimeToggles + reset between cases don't carry
|
|
269
|
+
// hooks across instances.
|
|
270
|
+
try {
|
|
271
|
+
(0, runtimeToggles_1.getRuntimeToggles)().onChange('sandbox', () => { _singleton = null; });
|
|
272
|
+
_toggleHookInstalled = true;
|
|
273
|
+
}
|
|
274
|
+
catch { /* runtimeToggles import / init race — best-effort */ }
|
|
275
|
+
}
|
|
276
|
+
if (!_singleton)
|
|
277
|
+
_singleton = readSandboxConfig();
|
|
278
|
+
return _singleton;
|
|
279
|
+
}
|
|
280
|
+
/** Reset the singleton for test isolation. Production code never calls this. */
|
|
281
|
+
function _resetSandboxConfigForTests() {
|
|
282
|
+
_singleton = null;
|
|
283
|
+
_toggleHookInstalled = false;
|
|
284
|
+
_clearRealPathCacheForTests();
|
|
285
|
+
}
|