aiden-runtime 4.8.0 → 4.9.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 +88 -1
- package/dist/cli/v4/aidenCLI.js +35 -4
- package/dist/cli/v4/chatSession.js +43 -16
- package/dist/cli/v4/commands/daemon.js +47 -2
- package/dist/cli/v4/commands/daemonDoctor.js +212 -0
- package/dist/cli/v4/commands/daemonStatus.js +1 -1
- package/dist/cli/v4/commands/help.js +2 -0
- package/dist/cli/v4/commands/hooks.js +428 -0
- package/dist/cli/v4/commands/index.js +5 -1
- package/dist/cli/v4/commands/mcp.js +89 -1
- package/dist/cli/v4/commands/mcpClientInstall.js +359 -0
- package/dist/cli/v4/commands/memory.js +702 -0
- package/dist/cli/v4/commands/recovery.js +1 -1
- package/dist/cli/v4/commands/skin.js +7 -0
- package/dist/cli/v4/commands/theme.js +217 -0
- package/dist/cli/v4/commands/trigger.js +1 -1
- package/dist/cli/v4/commands/update.js +14 -2
- package/dist/cli/v4/design/tokens.js +52 -4
- package/dist/cli/v4/display.js +102 -46
- package/dist/cli/v4/pasteIntercept.js +214 -70
- package/dist/cli/v4/replyRenderer.js +145 -5
- package/dist/cli/v4/skinEngine.js +67 -0
- package/dist/core/v4/aidenAgent.js +45 -2
- package/dist/core/v4/daemon/api/runs.js +131 -0
- package/dist/core/v4/daemon/bootstrap.js +368 -13
- package/dist/core/v4/daemon/db/migrations.js +169 -0
- package/dist/core/v4/daemon/idempotency/runIdempotencyStore.js +128 -0
- package/dist/core/v4/daemon/incarnationStore.js +47 -0
- package/dist/core/v4/daemon/runs/attemptStore.js +67 -0
- package/dist/core/v4/daemon/runs/reclaim.js +88 -0
- package/dist/core/v4/daemon/runs/retryPolicy.js +73 -0
- package/dist/core/v4/daemon/runs/runWithRetry.js +80 -0
- package/dist/core/v4/daemon/runs/stuckAttemptWatchdog.js +72 -0
- package/dist/core/v4/daemon/spans/spanHelpers.js +272 -0
- package/dist/core/v4/daemon/spans/spanStore.js +113 -0
- package/dist/core/v4/daemon/triggerBus.js +50 -19
- package/dist/core/v4/hooks/auditQuery.js +67 -0
- package/dist/core/v4/hooks/dispatcher.js +286 -0
- package/dist/core/v4/hooks/index.js +46 -0
- package/dist/core/v4/hooks/lifecycle.js +27 -0
- package/dist/core/v4/hooks/manifest.js +142 -0
- package/dist/core/v4/hooks/registry.js +149 -0
- package/dist/core/v4/hooks/runtime/subprocessRunner.js +188 -0
- package/dist/core/v4/hooks/toolHookGate.js +76 -0
- package/dist/core/v4/hooks/trust.js +14 -0
- package/dist/core/v4/identity/contextManager.js +83 -0
- package/dist/core/v4/identity/daemonId.js +85 -0
- package/dist/core/v4/identity/enforcement.js +103 -0
- package/dist/core/v4/identity/executionContext.js +153 -0
- package/dist/core/v4/identity/hookExecution.js +62 -0
- package/dist/core/v4/identity/httpContext.js +68 -0
- package/dist/core/v4/identity/ids.js +185 -0
- package/dist/core/v4/identity/index.js +60 -0
- package/dist/core/v4/identity/subprocessContext.js +98 -0
- package/dist/core/v4/identity/traceparent.js +114 -0
- package/dist/core/v4/logger/index.js +3 -1
- package/dist/core/v4/logger/logger.js +28 -1
- package/dist/core/v4/logger/redact.js +149 -0
- package/dist/core/v4/logger/sinks/fileSink.js +13 -0
- package/dist/core/v4/logger/sinks/stdSink.js +19 -1
- package/dist/core/v4/mcp/install/backup.js +78 -0
- package/dist/core/v4/mcp/install/clientPaths.js +90 -0
- package/dist/core/v4/mcp/install/clients.js +203 -0
- package/dist/core/v4/mcp/install/healthCheck.js +83 -0
- package/dist/core/v4/mcp/install/jsoncMerge.js +174 -0
- package/dist/core/v4/mcp/install/profiles.js +109 -0
- package/dist/core/v4/mcp/install/wslDetect.js +62 -0
- package/dist/core/v4/memory/namespaceRegistry.js +117 -0
- package/dist/core/v4/memory/projectRoot.js +76 -0
- package/dist/core/v4/memory/reviewer/index.js +162 -0
- package/dist/core/v4/memory/reviewer/pendingStore.js +136 -0
- package/dist/core/v4/memory/reviewer/prompt.js +105 -0
- package/dist/core/v4/memory/reviewer/skipRules.js +92 -0
- package/dist/core/v4/memoryManager.js +57 -10
- package/dist/core/v4/paths.js +2 -0
- package/dist/core/v4/promptBuilder.js +6 -0
- package/dist/core/v4/subagent/spawnSubAgent.js +20 -7
- package/dist/core/v4/theme/bundledThemes.js +106 -0
- package/dist/core/v4/theme/themeLoader.js +160 -0
- package/dist/core/v4/theme/themeRegistry.js +97 -0
- package/dist/core/v4/theme/themeWatcher.js +95 -0
- package/dist/core/v4/toolRegistry.js +71 -8
- package/dist/core/v4/update/executeInstall.js +10 -6
- package/dist/core/v4/update/installMethodDetect.js +7 -0
- package/dist/core/version.js +67 -2
- package/dist/moat/approvalEngine.js +4 -0
- package/dist/moat/memoryGuard.js +8 -1
- package/dist/providers/v4/anthropicAdapter.js +10 -4
- package/dist/tools/v4/backends/local.js +19 -2
- package/dist/tools/v4/sessions/recallSession.js +6 -1
- package/package.json +3 -3
- package/plugins/aiden-plugin-chatgpt-plus/index.js +10 -1
- package/themes/default.yaml +52 -0
- package/themes/dracula.yaml +32 -0
- package/themes/light.yaml +32 -0
- package/themes/monochrome.yaml +31 -0
- package/themes/tokyo-night.yaml +32 -0
- package/dist/core/pluginSystem.js +0 -121
- package/dist/tools/v4/ui/_uiSmokeTool.js +0 -60
|
@@ -35,7 +35,7 @@ function pad(value, width) {
|
|
|
35
35
|
}
|
|
36
36
|
exports.recovery = {
|
|
37
37
|
name: 'recovery',
|
|
38
|
-
description: 'Inspect recurring failure patterns + recoveries
|
|
38
|
+
description: 'Inspect recurring failure patterns + recoveries.',
|
|
39
39
|
category: 'system',
|
|
40
40
|
icon: '🩹',
|
|
41
41
|
handler: async (ctx) => {
|
|
@@ -7,6 +7,13 @@ exports.skin = {
|
|
|
7
7
|
category: 'system',
|
|
8
8
|
icon: '🎨',
|
|
9
9
|
handler: async (ctx) => {
|
|
10
|
+
// v4.9.0 Slice 1a — /skin is now an alias for the new /theme system.
|
|
11
|
+
// The legacy color skins (~/.aiden/skins/*.yaml, RGB-tuple format)
|
|
12
|
+
// continue to work alongside the new theme system; /skin still
|
|
13
|
+
// manages the SkinEngine palette. Theme tokens (panel chrome,
|
|
14
|
+
// status footer glyphs, shimmer) are controlled by /theme.
|
|
15
|
+
ctx.display.warn('/skin is deprecated in v4.9 — use /theme for full visual customisation. ' +
|
|
16
|
+
'/skin continues to work for legacy colour skins.');
|
|
10
17
|
const engine = ctx.skin;
|
|
11
18
|
if (!engine) {
|
|
12
19
|
ctx.display.warn('Skin engine not wired in this context.');
|
|
@@ -0,0 +1,217 @@
|
|
|
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.theme = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright (c) 2026 Shiva Deore (Taracod).
|
|
9
|
+
* Licensed under AGPL-3.0. See LICENSE for details.
|
|
10
|
+
*
|
|
11
|
+
* Aiden — local-first agent.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* cli/v4/commands/theme.ts — v4.9.0 Slice 1a.
|
|
15
|
+
*
|
|
16
|
+
* `/theme` show current theme name + path + hint
|
|
17
|
+
* `/theme reload` force re-read of ~/.aiden/theme.yaml
|
|
18
|
+
* `/theme reset` delete ~/.aiden/theme.yaml, restore baseline
|
|
19
|
+
* `/theme edit` print ~/.aiden/theme.yaml path
|
|
20
|
+
*
|
|
21
|
+
* `/theme list` and `/theme set <name>` ship in Slice 1b alongside
|
|
22
|
+
* the remaining 4 bundled themes.
|
|
23
|
+
*/
|
|
24
|
+
const node_fs_1 = require("node:fs");
|
|
25
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
26
|
+
const themeLoader_1 = require("../../../core/v4/theme/themeLoader");
|
|
27
|
+
const themeRegistry_1 = require("../../../core/v4/theme/themeRegistry");
|
|
28
|
+
const bundledThemes_1 = require("../../../core/v4/theme/bundledThemes");
|
|
29
|
+
function themeFilePath(ctx) {
|
|
30
|
+
const root = ctx.paths?.root;
|
|
31
|
+
if (!root)
|
|
32
|
+
return null;
|
|
33
|
+
return node_path_1.default.join(root, 'theme.yaml');
|
|
34
|
+
}
|
|
35
|
+
function userThemesDir(ctx) {
|
|
36
|
+
const root = ctx.paths?.root;
|
|
37
|
+
if (!root)
|
|
38
|
+
return null;
|
|
39
|
+
return node_path_1.default.join(root, 'themes');
|
|
40
|
+
}
|
|
41
|
+
function listUserThemes(dir) {
|
|
42
|
+
if (!dir || !(0, node_fs_1.existsSync)(dir))
|
|
43
|
+
return [];
|
|
44
|
+
try {
|
|
45
|
+
return (0, node_fs_1.readdirSync)(dir)
|
|
46
|
+
.filter((f) => f.endsWith('.yaml'))
|
|
47
|
+
.map((f) => {
|
|
48
|
+
const name = f.replace(/\.yaml$/, '');
|
|
49
|
+
let description = '';
|
|
50
|
+
try {
|
|
51
|
+
const yaml = (0, node_fs_1.readFileSync)(node_path_1.default.join(dir, f), 'utf8');
|
|
52
|
+
const { parsed } = (0, themeLoader_1.parseThemeYaml)(yaml);
|
|
53
|
+
description = parsed?.description ?? '';
|
|
54
|
+
}
|
|
55
|
+
catch { /* tolerate unreadable */ }
|
|
56
|
+
return { name, description };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.theme = {
|
|
64
|
+
name: 'theme',
|
|
65
|
+
description: 'Show, reload, reset, or open the user theme.yaml.',
|
|
66
|
+
category: 'system',
|
|
67
|
+
icon: '🎨',
|
|
68
|
+
handler: async (ctx) => {
|
|
69
|
+
const sub = ctx.rawArgs.trim();
|
|
70
|
+
const themePath = themeFilePath(ctx);
|
|
71
|
+
if (sub === '' || sub === 'show') {
|
|
72
|
+
const current = (0, themeRegistry_1.getCurrentName)();
|
|
73
|
+
const active = (0, themeRegistry_1.getActivePath)();
|
|
74
|
+
ctx.display.info(`Active theme: ${current}`);
|
|
75
|
+
if (active) {
|
|
76
|
+
ctx.display.info(`Source: ${active}`);
|
|
77
|
+
}
|
|
78
|
+
else if (themePath) {
|
|
79
|
+
ctx.display.info(`No user theme.yaml at ${themePath} — using bundled default.`);
|
|
80
|
+
}
|
|
81
|
+
ctx.display.info('Use /theme list, /theme set <name>, /theme reload, /theme reset, or /theme edit.');
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
if (sub === 'list') {
|
|
85
|
+
const current = (0, themeRegistry_1.getCurrentName)();
|
|
86
|
+
const bundled = (0, bundledThemes_1.listBundled)();
|
|
87
|
+
const userDir = userThemesDir(ctx);
|
|
88
|
+
const userList = listUserThemes(userDir);
|
|
89
|
+
ctx.display.info(`Active theme: ${current}`);
|
|
90
|
+
ctx.display.info('Available themes:');
|
|
91
|
+
const labelW = Math.max(...bundled.map((b) => b.name.length), ...userList.map((u) => u.name.length), 4);
|
|
92
|
+
for (const b of bundled) {
|
|
93
|
+
const marker = b.name === current ? '●' : '○';
|
|
94
|
+
const padded = b.name.padEnd(labelW);
|
|
95
|
+
ctx.display.info(` ${marker} ${padded} (bundled) ${b.description}`);
|
|
96
|
+
}
|
|
97
|
+
for (const u of userList) {
|
|
98
|
+
const marker = u.name === current ? '●' : '○';
|
|
99
|
+
const padded = u.name.padEnd(labelW);
|
|
100
|
+
ctx.display.info(` ${marker} ${padded} (user) ${u.description}`);
|
|
101
|
+
}
|
|
102
|
+
if (bundled.length === 0 && userList.length === 0) {
|
|
103
|
+
ctx.display.warn('No themes found. Reinstall aiden-runtime or use /theme edit to author one.');
|
|
104
|
+
}
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
if (sub.startsWith('set ') || sub === 'set') {
|
|
108
|
+
const name = sub.replace(/^set\s*/, '').trim();
|
|
109
|
+
if (!name) {
|
|
110
|
+
ctx.display.printError('Usage: /theme set <name>', `Try one of: ${bundledThemes_1.BUNDLED_NAMES.join(', ')} or /theme list for full options.`);
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
if (!themePath) {
|
|
114
|
+
ctx.display.warn('/theme set needs Aiden user-data paths — try in a real session.');
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
// Resolution order: bundled → user themes/ dir.
|
|
118
|
+
let yamlText = null;
|
|
119
|
+
let sourceLabel = '';
|
|
120
|
+
if ((0, bundledThemes_1.isBundled)(name)) {
|
|
121
|
+
yamlText = (0, bundledThemes_1.getYaml)(name);
|
|
122
|
+
sourceLabel = 'bundled';
|
|
123
|
+
}
|
|
124
|
+
if (!yamlText) {
|
|
125
|
+
const userDir = userThemesDir(ctx);
|
|
126
|
+
if (userDir) {
|
|
127
|
+
const userFile = node_path_1.default.join(userDir, `${name}.yaml`);
|
|
128
|
+
if ((0, node_fs_1.existsSync)(userFile)) {
|
|
129
|
+
try {
|
|
130
|
+
yamlText = (0, node_fs_1.readFileSync)(userFile, 'utf8');
|
|
131
|
+
sourceLabel = 'user';
|
|
132
|
+
}
|
|
133
|
+
catch { /* fall through to not-found */ }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!yamlText) {
|
|
138
|
+
ctx.display.printError(`Theme not found: "${name}"`, `Available bundled: ${bundledThemes_1.BUNDLED_NAMES.join(', ')}. Or create ~/.aiden/themes/${name}.yaml.`);
|
|
139
|
+
return {};
|
|
140
|
+
}
|
|
141
|
+
// Copy to ~/.aiden/theme.yaml and apply immediately (don't wait
|
|
142
|
+
// for the chokidar watcher debounce — slash-command-driven
|
|
143
|
+
// changes should feel instant).
|
|
144
|
+
try {
|
|
145
|
+
(0, node_fs_1.mkdirSync)(node_path_1.default.dirname(themePath), { recursive: true });
|
|
146
|
+
(0, node_fs_1.writeFileSync)(themePath, yamlText, 'utf8');
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
ctx.display.printError(`Could not write ${themePath}: ${err.message}`, 'Check filesystem permissions and try again.');
|
|
150
|
+
return {};
|
|
151
|
+
}
|
|
152
|
+
const { parsed, warnings } = (0, themeLoader_1.parseThemeYaml)(yamlText);
|
|
153
|
+
for (const w of warnings)
|
|
154
|
+
ctx.display.warn(`theme: ${w}`);
|
|
155
|
+
if (parsed) {
|
|
156
|
+
(0, themeRegistry_1.applyTheme)(parsed, themePath);
|
|
157
|
+
ctx.display.success(`✓ Theme set to ${name} (${sourceLabel}). Run /theme reset to revert to default.`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
ctx.display.printError(`Theme "${name}" parsed empty; current theme retained.`, 'Check the warnings above for the specific YAML / hex error.');
|
|
161
|
+
}
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
if (sub === 'reload') {
|
|
165
|
+
if (!themePath) {
|
|
166
|
+
ctx.display.warn('/theme reload needs Aiden user-data paths — try in a real session.');
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
if (!(0, node_fs_1.existsSync)(themePath)) {
|
|
170
|
+
ctx.display.warn(`No theme file at ${themePath}. Use /theme edit to create one.`);
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
const { parsed, warnings } = (0, themeLoader_1.loadThemeFile)(themePath);
|
|
174
|
+
for (const w of warnings)
|
|
175
|
+
ctx.display.warn(`theme: ${w}`);
|
|
176
|
+
if (parsed) {
|
|
177
|
+
(0, themeRegistry_1.applyTheme)(parsed, themePath);
|
|
178
|
+
ctx.display.success(`Theme reloaded: ${parsed.name}`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
ctx.display.printError('Theme parse failed; current theme retained.', 'Check the warnings above for the specific YAML / hex error.');
|
|
182
|
+
}
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
if (sub === 'reset') {
|
|
186
|
+
if (!themePath) {
|
|
187
|
+
ctx.display.warn('/theme reset needs Aiden user-data paths — try in a real session.');
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
if ((0, node_fs_1.existsSync)(themePath)) {
|
|
191
|
+
try {
|
|
192
|
+
(0, node_fs_1.unlinkSync)(themePath);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
ctx.display.warn(`Could not delete ${themePath}: ${err.message}`);
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
(0, themeRegistry_1.resetToDefault)();
|
|
200
|
+
ctx.display.success('Theme reset to bundled default.');
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
if (sub === 'edit') {
|
|
204
|
+
if (!themePath) {
|
|
205
|
+
ctx.display.warn('/theme edit needs Aiden user-data paths — try in a real session.');
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
208
|
+
ctx.display.info(`Theme file: ${themePath}`);
|
|
209
|
+
if (!(0, node_fs_1.existsSync)(themePath)) {
|
|
210
|
+
ctx.display.info('File does not exist yet. Create it with your editor; Aiden hot-reloads on save.');
|
|
211
|
+
}
|
|
212
|
+
return {};
|
|
213
|
+
}
|
|
214
|
+
ctx.display.printError(`Unknown /theme subcommand: ${sub}`, 'Available: /theme | /theme list | /theme set <name> | /theme reload | /theme reset | /theme edit');
|
|
215
|
+
return {};
|
|
216
|
+
},
|
|
217
|
+
};
|
|
@@ -369,7 +369,7 @@ async function runAddEmail(db, argv, out, err) {
|
|
|
369
369
|
out(`poll interval: ${spec.pollIntervalMs}ms\n`);
|
|
370
370
|
out(`allow-senders: ${spec.allowedSenders.join(', ')}\n`);
|
|
371
371
|
out(`⚠ Password stored in plaintext in daemon.db (chmod 600 on POSIX,\n`);
|
|
372
|
-
out(` user-private on Windows). Encryption-at-rest is deferred to
|
|
372
|
+
out(` user-private on Windows). Encryption-at-rest is deferred to a future release.\n`);
|
|
373
373
|
out(`Restart the daemon to activate the trigger.\n`);
|
|
374
374
|
// Note: runAddEmail returns a Promise<number>, so the outer switch must
|
|
375
375
|
// await it. (Already handled — runTriggerSubcommand is async.)
|
|
@@ -78,14 +78,26 @@ async function runInstall(ctx) {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
ctx.display.write(`Installing aiden-runtime v${status.latest} (current: v${status.installed})…\n`);
|
|
81
|
-
|
|
81
|
+
// v4.8.1 Slice 2 — reuse the v4.8.0 sliding-block shimmer indicator
|
|
82
|
+
// so the user sees motion while npm install runs (typically 5–15s
|
|
83
|
+
// on a warm cache, longer on cold). The indicator paints to a TTY
|
|
84
|
+
// only — non-TTY callers (CI, pipes) see the static "Installing…"
|
|
85
|
+
// line above and the result row below, no shimmer.
|
|
86
|
+
const indicator = ctx.display.activityIndicator('updating');
|
|
87
|
+
let result;
|
|
88
|
+
try {
|
|
89
|
+
result = await (0, executeInstall_1.executeInstall)();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
indicator.stop();
|
|
93
|
+
}
|
|
82
94
|
if (result.success) {
|
|
83
95
|
const v = result.installedVersion ?? status.latest;
|
|
84
96
|
ctx.display.write(`\n ✓ aiden-runtime v${v} installed.\n`);
|
|
85
97
|
ctx.display.dim('Restart Aiden to apply: type /quit then re-run `aiden`.');
|
|
86
98
|
return;
|
|
87
99
|
}
|
|
88
|
-
ctx.display.
|
|
100
|
+
ctx.display.write(`\n ✗ Update failed: ${result.error ?? 'no error message'}\n`);
|
|
89
101
|
}
|
|
90
102
|
// ── v4.5 update system — skip + auto subcommands ───────────────────────────
|
|
91
103
|
async function runSkip(ctx) {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* isVerbose() reads env at call time (Ollama-options precedent).
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.VERBOSE_MODE_ENV = exports.spacing = exports.glyphs = exports.colors = exports.TRAIL_PIPE = void 0;
|
|
19
|
+
exports.VERBOSE_MODE_ENV = exports.spacing = exports.BASELINE_GLYPHS = exports.BASELINE_COLORS = exports.glyphs = exports.colors = exports.TRAIL_PIPE = void 0;
|
|
20
|
+
exports._restoreBaselineForTokens = _restoreBaselineForTokens;
|
|
20
21
|
exports.isVerbose = isVerbose;
|
|
21
22
|
// Re-export so consumers can import the trail gutter from tokens
|
|
22
23
|
// alongside the rest of the design system. The original constant
|
|
@@ -24,6 +25,20 @@ exports.isVerbose = isVerbose;
|
|
|
24
25
|
// that imports from `display/toolTrail` directly.
|
|
25
26
|
var toolTrail_1 = require("../display/toolTrail");
|
|
26
27
|
Object.defineProperty(exports, "TRAIL_PIPE", { enumerable: true, get: function () { return toolTrail_1.TRAIL_PIPE; } });
|
|
28
|
+
/**
|
|
29
|
+
* v4.9.0 Slice 1a — `colors` and `glyphs` were previously exported with
|
|
30
|
+
* `as const` (frozen literal-typed object). They are now mutable
|
|
31
|
+
* singletons so the ThemeRegistry can swap values at runtime when a
|
|
32
|
+
* user theme is loaded from `~/.aiden/theme.yaml`. Every existing
|
|
33
|
+
* consumer (`import { colors, glyphs } from '../design/tokens'`)
|
|
34
|
+
* continues to work because property reads now reflect the LIVE
|
|
35
|
+
* current values.
|
|
36
|
+
*
|
|
37
|
+
* `BASELINE_COLORS` / `BASELINE_GLYPHS` are deep-frozen snapshots
|
|
38
|
+
* captured at module load. The registry's `resetToDefault()` restores
|
|
39
|
+
* from these. Consumers that need the unmodified default (e.g. tests)
|
|
40
|
+
* can import these directly.
|
|
41
|
+
*/
|
|
27
42
|
// ── Colors ────────────────────────────────────────────────────────────────
|
|
28
43
|
/**
|
|
29
44
|
* Hex color tokens. Mirrors skinEngine RGB tuples for existing kinds;
|
|
@@ -119,9 +134,9 @@ exports.glyphs = {
|
|
|
119
134
|
dotOpen: '○',
|
|
120
135
|
/** Status footer column separator. */
|
|
121
136
|
sep: '│',
|
|
122
|
-
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
137
|
+
/** Turn counter retired per v4.9.0 pre-ship UX feedback —
|
|
138
|
+
* value-to-pixel ratio too low. Field kept (empty) so the
|
|
139
|
+
* token-table shape stays stable for downstream consumers. */
|
|
125
140
|
turn: '',
|
|
126
141
|
/** Slice 7 — session timer prefix. Slice 9 hotfix: `⌛` (U+231B
|
|
127
142
|
* HOURGLASS WITH FLOWING SAND) restored. The font set Shiva
|
|
@@ -224,6 +239,39 @@ exports.glyphs = {
|
|
|
224
239
|
track: '─',
|
|
225
240
|
},
|
|
226
241
|
};
|
|
242
|
+
/**
|
|
243
|
+
* Deep-frozen snapshots of the baseline values, captured at module
|
|
244
|
+
* load. The ThemeRegistry uses these to reset to defaults via
|
|
245
|
+
* `resetToDefault()`, and tests can import them as the source of
|
|
246
|
+
* truth even after a user theme has been applied to the mutable
|
|
247
|
+
* `colors` / `glyphs` exports above.
|
|
248
|
+
*/
|
|
249
|
+
function deepFreeze(o) {
|
|
250
|
+
if (o === null || typeof o !== 'object')
|
|
251
|
+
return o;
|
|
252
|
+
for (const k of Object.keys(o)) {
|
|
253
|
+
deepFreeze(o[k]);
|
|
254
|
+
}
|
|
255
|
+
return Object.freeze(o);
|
|
256
|
+
}
|
|
257
|
+
function deepClone(o) {
|
|
258
|
+
if (o === null || typeof o !== 'object')
|
|
259
|
+
return o;
|
|
260
|
+
if (Array.isArray(o))
|
|
261
|
+
return o.map(deepClone);
|
|
262
|
+
const out = {};
|
|
263
|
+
for (const k of Object.keys(o)) {
|
|
264
|
+
out[k] = deepClone(o[k]);
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
exports.BASELINE_COLORS = deepFreeze(deepClone(exports.colors));
|
|
269
|
+
exports.BASELINE_GLYPHS = deepFreeze(deepClone(exports.glyphs));
|
|
270
|
+
/** Used by ThemeRegistry to restore baseline values into the live singletons. */
|
|
271
|
+
function _restoreBaselineForTokens() {
|
|
272
|
+
Object.assign(exports.colors, deepClone(exports.BASELINE_COLORS));
|
|
273
|
+
Object.assign(exports.glyphs, deepClone(exports.BASELINE_GLYPHS));
|
|
274
|
+
}
|
|
227
275
|
// ── Spacing ───────────────────────────────────────────────────────────────
|
|
228
276
|
/**
|
|
229
277
|
* Integer spacing tokens. 0-indexed column counts; subagent depth
|
package/dist/cli/v4/display.js
CHANGED
|
@@ -23,6 +23,8 @@ exports.iconForTool = iconForTool;
|
|
|
23
23
|
exports.detectConfiguredChannels = detectConfiguredChannels;
|
|
24
24
|
exports.summarizeConfiguredChannels = summarizeConfiguredChannels;
|
|
25
25
|
exports.summarizeChannelState = summarizeChannelState;
|
|
26
|
+
exports.computeContextBarFill = computeContextBarFill;
|
|
27
|
+
exports.renderContextBar = renderContextBar;
|
|
26
28
|
exports.voiceIndicator = voiceIndicator;
|
|
27
29
|
exports.makeNoOpToolRowHandle = makeNoOpToolRowHandle;
|
|
28
30
|
exports.previewToolArgs = previewToolArgs;
|
|
@@ -214,6 +216,22 @@ const AIDEN_BANNER = String.raw `
|
|
|
214
216
|
██║ ██║██║██████╔╝███████╗██║ ╚████║
|
|
215
217
|
╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝
|
|
216
218
|
`;
|
|
219
|
+
/**
|
|
220
|
+
* v4.9.0 pre-ship UI hotfix — pure context-bar helpers. Extracted
|
|
221
|
+
* for testability + to fix the "always empty" symptom. Scale:
|
|
222
|
+
* 0% → 0 cells, 1-19% → 1, 20-39% → 2, 40-59% → 3, 60-79% → 4,
|
|
223
|
+
* 80-100% → 5. `renderContextBar` returns the glyph array.
|
|
224
|
+
*/
|
|
225
|
+
function computeContextBarFill(pct, barW = 5) {
|
|
226
|
+
if (pct <= 0)
|
|
227
|
+
return 0;
|
|
228
|
+
if (pct >= 100)
|
|
229
|
+
return barW;
|
|
230
|
+
return Math.min(barW, Math.floor(pct / 20) + 1);
|
|
231
|
+
}
|
|
232
|
+
function renderContextBar(filled, barW = 5) {
|
|
233
|
+
return Array.from({ length: barW }, (_, i) => i < filled ? tokens_1.glyphs.bar.filled : tokens_1.glyphs.bar.empty);
|
|
234
|
+
}
|
|
217
235
|
class Display {
|
|
218
236
|
constructor(opts = {}) {
|
|
219
237
|
// ── Phase 16c: streaming surface ─────────────────────────────────────
|
|
@@ -605,23 +623,23 @@ class Display {
|
|
|
605
623
|
statusFooter(args) {
|
|
606
624
|
const sk = this.skin;
|
|
607
625
|
const SEP = sk.applyColors(' │ ', 'muted');
|
|
608
|
-
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
const provModel = `${tri} ${sk.applyColors(args.provider, 'muted')}` +
|
|
626
|
+
// v4.9.0 pre-ship UI hotfix — dropped the leading `▲` from
|
|
627
|
+
// provModel (prompt `▲` owns the marker; footer `▲` read as
|
|
628
|
+
// a duplicate orphan). Slice 7 per-metric palette preserved.
|
|
629
|
+
const provModel = `${sk.applyColors(args.provider, 'muted')}` +
|
|
613
630
|
`${sk.applyColors(' · ', 'muted')}` +
|
|
614
631
|
sk.applyColors(args.model, 'tool');
|
|
615
632
|
const pct = args.ctxMax > 0
|
|
616
633
|
? Math.min(100, Math.round((args.ctxUsed / args.ctxMax) * 100))
|
|
617
634
|
: 0;
|
|
618
|
-
//
|
|
619
|
-
//
|
|
620
|
-
//
|
|
635
|
+
// v4.9.0 pre-ship UI hotfix — bar math extracted to
|
|
636
|
+
// `computeContextBarFill` (1 cell per 20% bucket, ≥1 when any
|
|
637
|
+
// context used). Old `Math.round(pct/100 * barW)` floored to 0
|
|
638
|
+
// below ~10% so the bar stayed empty all session at typical use.
|
|
621
639
|
const barW = 5;
|
|
622
|
-
const filled =
|
|
640
|
+
const filled = computeContextBarFill(pct, barW);
|
|
623
641
|
const ctxKind = pct < 60 ? 'success' : pct < 85 ? 'warn' : 'error';
|
|
624
|
-
const cells =
|
|
642
|
+
const cells = renderContextBar(filled, barW);
|
|
625
643
|
const bar = sk.applyColors(cells.join(' '), ctxKind);
|
|
626
644
|
const ctxRatio = sk.applyColors(`${formatCompactTokens(args.ctxUsed)}/${formatCompactTokens(args.ctxMax)}`, 'warn');
|
|
627
645
|
const ctxPctText = sk.applyColors(`${pct}%`, ctxKind);
|
|
@@ -638,12 +656,10 @@ class Display {
|
|
|
638
656
|
const stateDot = args.state
|
|
639
657
|
? sk.applyColors(tokens_1.glyphs.status.dot, this.stateKind(args.state))
|
|
640
658
|
: '';
|
|
641
|
-
// v4.
|
|
642
|
-
//
|
|
643
|
-
//
|
|
644
|
-
|
|
645
|
-
? sk.applyColors(String(args.turnCount), 'metric_turn')
|
|
646
|
-
: '';
|
|
659
|
+
// v4.9.0 pre-ship UI: turn counter retired entirely — value-to-pixel
|
|
660
|
+
// ratio too low. `args.turnCount` stays in the signature for caller
|
|
661
|
+
// back-compat; ignored here.
|
|
662
|
+
void args.turnCount;
|
|
647
663
|
// v4.8.0 Slice 9 hotfix — ⌛ restored ahead of the bare elapsed
|
|
648
664
|
// string. Wider font support than the retired ⏱. `sessionMs` arg
|
|
649
665
|
// stays plumbed-but-unused for backward compat with the field name.
|
|
@@ -654,14 +670,17 @@ class Display {
|
|
|
654
670
|
const ctxSegFull = `${ctxRatio} ${bar} ${ctxPctText}`;
|
|
655
671
|
const ctxSegCompact = `${bar} ${ctxPctText}`;
|
|
656
672
|
let segments;
|
|
657
|
-
if (cols >= 120 && stateDot &&
|
|
658
|
-
segments = [provModel, ctxSegFull,
|
|
673
|
+
if (cols >= 120 && stateDot && sessionSeg) {
|
|
674
|
+
segments = [provModel, ctxSegFull, sessionSeg, stateDot];
|
|
659
675
|
}
|
|
660
|
-
else if (cols >= 100
|
|
661
|
-
|
|
676
|
+
else if (cols >= 100) {
|
|
677
|
+
// v4.8.1 Slice 2 hotfix — sessionSeg keeps the ⌛ identity glyph
|
|
678
|
+
// (single-cell, cheap) even at this tier. v4.9.0 pre-ship UI:
|
|
679
|
+
// turn counter retired; mid tier collapses to 2 separators.
|
|
680
|
+
segments = [provModel, ctxSegFull, sessionSeg || elapsed];
|
|
662
681
|
}
|
|
663
682
|
else {
|
|
664
|
-
segments = [provModel, ctxSegCompact, elapsed];
|
|
683
|
+
segments = [provModel, ctxSegCompact, sessionSeg || elapsed];
|
|
665
684
|
}
|
|
666
685
|
return ` ${segments.join(SEP)}`;
|
|
667
686
|
}
|
|
@@ -892,6 +911,13 @@ class Display {
|
|
|
892
911
|
let stopped = false;
|
|
893
912
|
let printed = false;
|
|
894
913
|
let tickTimer = null;
|
|
914
|
+
// v4.8.1 Slice 2 hotfix #4 — true once the indicator has paused
|
|
915
|
+
// and resumed at least once (i.e. a tool row interrupted it). When
|
|
916
|
+
// false at stop() time, the indicator is still in its initial-paint
|
|
917
|
+
// row immediately below the leading blank, so stop()'s erase can
|
|
918
|
+
// safely consume BOTH rows. When true, the leading blank is far
|
|
919
|
+
// above and stop() erases only the current indicator row.
|
|
920
|
+
let movedFromInitial = false;
|
|
895
921
|
// Tunable cadence. v4.1.4 Phase 3b' (Issue G): bumped from 400ms
|
|
896
922
|
// to 250ms after visual smoke — 400ms felt sluggish, made the
|
|
897
923
|
// indicator look static between seconds. 250ms gives ~4 dot
|
|
@@ -951,7 +977,12 @@ class Display {
|
|
|
951
977
|
: '';
|
|
952
978
|
// Shimmer prefix (or none, when opts.waveBar === false).
|
|
953
979
|
const prefix = shimmerEnabled ? `${buildShimmer()} ` : '';
|
|
954
|
-
|
|
980
|
+
// v4.8.1 Slice 2 hotfix #4 — 2-space leading indent so the
|
|
981
|
+
// indicator line aligns at col 2, matching `▎ Aiden`, the
|
|
982
|
+
// user-prompt ` ▲ `, the panel ` │ ` bar, and every other
|
|
983
|
+
// structured surface. Prior buildLine started at col 0 which
|
|
984
|
+
// read as misaligned against the rest of the v4.8 chrome.
|
|
985
|
+
return ` ${prefix}${verb}${dots.padEnd(3, ' ')}${elapsedStr}`;
|
|
955
986
|
};
|
|
956
987
|
// v4.1.5 Part 1a — Issue M (Windows ConPTY buffering fix).
|
|
957
988
|
//
|
|
@@ -1014,12 +1045,16 @@ class Display {
|
|
|
1014
1045
|
return;
|
|
1015
1046
|
out.write(`${ANSI_UP_ERASE}\n`);
|
|
1016
1047
|
};
|
|
1017
|
-
// Initial paint — only on TTY.
|
|
1018
|
-
//
|
|
1019
|
-
//
|
|
1020
|
-
// the user-
|
|
1021
|
-
//
|
|
1022
|
-
//
|
|
1048
|
+
// Initial paint — only on TTY.
|
|
1049
|
+
//
|
|
1050
|
+
// v4.8.1 Slice 2 hotfix #4 — leading `\n` restored to give one
|
|
1051
|
+
// blank row between the user-input row and the indicator (hotfix
|
|
1052
|
+
// #3 dropped the dim rule that previously provided that gap).
|
|
1053
|
+
// To keep the post-stop layout at "exactly one blank between
|
|
1054
|
+
// user input and ▎ Aiden", stop() now walks up TWO rows when
|
|
1055
|
+
// the indicator never moved (no pause/resume), consuming both
|
|
1056
|
+
// the indicator row AND the leading blank. The `movedFromInitial`
|
|
1057
|
+
// flag below tracks that state.
|
|
1023
1058
|
if (isTty) {
|
|
1024
1059
|
out.write(`\n${buildLine()}\n`);
|
|
1025
1060
|
printed = true;
|
|
@@ -1031,6 +1066,12 @@ class Display {
|
|
|
1031
1066
|
return;
|
|
1032
1067
|
paused = true;
|
|
1033
1068
|
stopTick();
|
|
1069
|
+
// v4.8.1 Slice 2 hotfix #4 — mark the indicator as "moved" so
|
|
1070
|
+
// a subsequent stop() does NOT walk up 2 rows. The leading
|
|
1071
|
+
// blank from initial paint is now far above the current row
|
|
1072
|
+
// and shouldn't be consumed; doing so would erase tool-row
|
|
1073
|
+
// content instead.
|
|
1074
|
+
movedFromInitial = true;
|
|
1034
1075
|
eraseLine();
|
|
1035
1076
|
// After erase the cursor is at column 0 of the indicator's
|
|
1036
1077
|
// (now empty) line. Caller is expected to write its own
|
|
@@ -1071,7 +1112,20 @@ class Display {
|
|
|
1071
1112
|
return;
|
|
1072
1113
|
stopped = true;
|
|
1073
1114
|
stopTick();
|
|
1074
|
-
|
|
1115
|
+
// v4.8.1 Slice 2 hotfix #4 — when the indicator never moved
|
|
1116
|
+
// (no pause/resume happened during the turn), walk up TWO
|
|
1117
|
+
// rows: erase the indicator row AND the leading blank above
|
|
1118
|
+
// it. The trailing `\n` then lands the cursor exactly one
|
|
1119
|
+
// row below the user-input echo, so the next writer
|
|
1120
|
+
// (agentHeader → ▎ Aiden) produces a clean single-blank gap.
|
|
1121
|
+
if (!printed || !isTty)
|
|
1122
|
+
return;
|
|
1123
|
+
if (movedFromInitial) {
|
|
1124
|
+
out.write(`${ANSI_UP_ERASE}\n`);
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
out.write(`${ANSI_UP_ERASE}${ANSI_UP_ERASE}\n`);
|
|
1128
|
+
}
|
|
1075
1129
|
},
|
|
1076
1130
|
isPaused: () => paused,
|
|
1077
1131
|
isStopped: () => stopped,
|
|
@@ -2051,23 +2105,25 @@ class Display {
|
|
|
2051
2105
|
this.out.write(this.uiTrailRow(`${ok ? '✓' : '✗'} ${framework}: ${parts.join(', ')}${dur}`, ok ? 'success' : 'error'));
|
|
2052
2106
|
this.streamLastEndedNewline = true;
|
|
2053
2107
|
}
|
|
2054
|
-
renderUiApprovalRequest(
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2108
|
+
renderUiApprovalRequest(_args) {
|
|
2109
|
+
// v4.8.1 Slice 1 — silent no-op. The Phase 2.5 wiring fires both
|
|
2110
|
+
// `ui_approval_request` (this method) AND `callbacks.promptApproval`
|
|
2111
|
+
// (which paints the framed approval panel via `renderApprovalBox`)
|
|
2112
|
+
// for every single approval request. The intent was complementary —
|
|
2113
|
+
// succinct event row above, structured kv panel below — but in live
|
|
2114
|
+
// smoke the two surfaces stack as a visual duplicate ("Approval
|
|
2115
|
+
// needed: file_write {...}" event row + "│ tool / │ reason / │ args"
|
|
2116
|
+
// panel). The panel is the canonical, information-rich surface; this
|
|
2117
|
+
// event-row paint is redundant.
|
|
2118
|
+
//
|
|
2119
|
+
// Behavioural change is renderer-side only: `approvalEngine` still
|
|
2120
|
+
// fires `onUiEvent('ui_approval_request', ...)` so any future
|
|
2121
|
+
// telemetry / daemon-side run_events subscriber will still see the
|
|
2122
|
+
// event. Nothing paints to the chat surface from this method.
|
|
2123
|
+
//
|
|
2124
|
+
// The `_args` parameter is retained for the dispatch signature
|
|
2125
|
+
// contract (`renderUiEvent` calls it positionally) and for the day
|
|
2126
|
+
// we re-introduce a single-paint surface keyed off args.risk_tier.
|
|
2071
2127
|
}
|
|
2072
2128
|
renderUiToast(args) {
|
|
2073
2129
|
const message = typeof args.message === 'string' ? args.message : '';
|