aiden-runtime 4.7.0 → 4.8.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 +12 -1
- package/dist/cli/v4/aidenCLI.js +40 -5
- package/dist/cli/v4/callbacks.js +52 -31
- package/dist/cli/v4/chatSession.js +46 -1
- package/dist/cli/v4/commands/help.js +22 -11
- package/dist/cli/v4/commands/runs.js +42 -24
- package/dist/cli/v4/commands/skills.js +15 -17
- package/dist/cli/v4/commands/usage.js +17 -5
- package/dist/cli/v4/daemonAgentBuilder.js +1 -0
- package/dist/cli/v4/design/tokens.js +265 -0
- package/dist/cli/v4/display/framedPanel.js +116 -0
- package/dist/cli/v4/display/toolTrail.js +2 -2
- package/dist/cli/v4/display.js +446 -164
- package/dist/cli/v4/onboarding/disclaimer.js +42 -10
- package/dist/cli/v4/onboarding/loading.js +24 -1
- package/dist/cli/v4/onboarding/successScreen.js +17 -8
- package/dist/cli/v4/replyRenderer.js +74 -58
- package/dist/cli/v4/setupWizard.js +19 -2
- package/dist/cli/v4/skinEngine.js +13 -0
- package/dist/cli/v4/table.js +65 -8
- package/dist/core/v4/aidenAgent.js +23 -0
- package/dist/core/v4/auxiliaryClient.js +46 -13
- package/dist/core/v4/daemon/dispatcher/realAgentRunner.js +13 -8
- package/dist/core/v4/promptBuilder.js +45 -0
- package/dist/core/v4/subagent/childBuilder.js +1 -0
- package/dist/core/v4/subagent/spawnSubAgent.js +7 -1
- package/dist/core/v4/ui/banner.js +16 -16
- package/dist/core/version.js +1 -1
- package/dist/moat/approvalEngine.js +14 -0
- package/dist/tools/v4/index.js +54 -0
- package/dist/tools/v4/subagent/spawnSubAgentTool.js +23 -0
- package/package.json +1 -1
|
@@ -60,9 +60,24 @@ const readline = __importStar(require("node:readline"));
|
|
|
60
60
|
const banner_1 = require("../../../core/v4/ui/banner");
|
|
61
61
|
const theme_1 = require("../../../core/v4/ui/theme");
|
|
62
62
|
const version_1 = require("../../../core/version");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// v4.8.0 Slice 10c — replaced the single-paragraph prose with two
|
|
64
|
+
// scannable bullet lists (capability + acknowledgments). Legal terms
|
|
65
|
+
// surfaced as a checklist instead of buried inside prose.
|
|
66
|
+
const DISCLAIMER_HEAD = 'Aiden is an autonomous AI engine that runs on your machine. Aiden can:';
|
|
67
|
+
const CAPABILITY_BULLETS = [
|
|
68
|
+
'Read, write, and modify files on your computer',
|
|
69
|
+
'Execute shell commands and run code',
|
|
70
|
+
'Browse the web and interact with online services',
|
|
71
|
+
'Connect to AI providers using YOUR API keys (BYOK)',
|
|
72
|
+
'Generate and execute new skills based on your prompts',
|
|
73
|
+
];
|
|
74
|
+
const ACK_HEAD = 'By continuing, you acknowledge:';
|
|
75
|
+
const ACK_BULLETS = [
|
|
76
|
+
'Aiden operates on your behalf with full local-system access',
|
|
77
|
+
'You are responsible for outcomes of commands you approve',
|
|
78
|
+
'Open source under AGPL-3.0 — read the code at github.com/taracodlabs/aiden',
|
|
79
|
+
'This is beta software, built solo, still rough in spots',
|
|
80
|
+
];
|
|
66
81
|
/**
|
|
67
82
|
* Word-wrap `text` to `width` columns. Preserves single spaces; does
|
|
68
83
|
* not handle ANSI codes (callers pass plain text here).
|
|
@@ -97,18 +112,35 @@ function clearScreen(out) {
|
|
|
97
112
|
out.write('\x1b[2J\x1b[H');
|
|
98
113
|
}
|
|
99
114
|
/**
|
|
100
|
-
* Render the disclaimer body —
|
|
101
|
-
*
|
|
115
|
+
* Render the disclaimer body — v4.8.0 Slice 10c: banner + framed-panel
|
|
116
|
+
* capability list + acknowledgments. Orange `▎` bar on every line of
|
|
117
|
+
* the panel matches the rest of v4.8.0 chrome. `▸` bullets keep
|
|
118
|
+
* capability/ack items scannable rather than buried in prose.
|
|
102
119
|
*/
|
|
103
120
|
function renderDisclaimerBody(version) {
|
|
104
121
|
const w = (0, theme_1.termWidth)();
|
|
122
|
+
const innerW = Math.min(w - 4, 70);
|
|
105
123
|
const body = [];
|
|
106
124
|
body.push((0, banner_1.renderBanner)({ version }));
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
// Slice 10c framed-panel chrome. Orange bar at col 2; content + 2
|
|
126
|
+
// inner spaces; muted `─` divider between sections.
|
|
127
|
+
// v4.8.0 Slice 11c — `▎` swapped for `│` (U+2502); same swap as
|
|
128
|
+
// glyphs.panel.bar in the design tokens.
|
|
129
|
+
const bar = theme_1.c.primary('│');
|
|
130
|
+
const divider = theme_1.c.muted('─'.repeat(innerW - 2));
|
|
131
|
+
const line = (s) => ` ${bar} ${s}\n`;
|
|
132
|
+
body.push(line(theme_1.c.text(DISCLAIMER_HEAD)));
|
|
133
|
+
body.push(line(''));
|
|
134
|
+
for (const item of CAPABILITY_BULLETS) {
|
|
135
|
+
body.push(line(theme_1.c.muted('▸ ') + theme_1.c.text(item)));
|
|
136
|
+
}
|
|
137
|
+
body.push(line(''));
|
|
138
|
+
body.push(line(divider));
|
|
139
|
+
body.push(line(''));
|
|
140
|
+
body.push(line(theme_1.c.text(ACK_HEAD)));
|
|
141
|
+
body.push(line(''));
|
|
142
|
+
for (const item of ACK_BULLETS) {
|
|
143
|
+
body.push(line(theme_1.c.muted('▸ ') + theme_1.c.text(item)));
|
|
112
144
|
}
|
|
113
145
|
body.push('\n');
|
|
114
146
|
return body.join('');
|
|
@@ -86,13 +86,29 @@ async function runLoadingSequence(steps, opts = {}) {
|
|
|
86
86
|
return { ok: results.every((r) => r.ok), steps: results };
|
|
87
87
|
}
|
|
88
88
|
out.write('\n ' + theme_1.c.text(heading) + '\n\n');
|
|
89
|
+
// v4.8.0 Slice 10c — progress bar above the step rows. 10 cells
|
|
90
|
+
// (●/○) split proportionally across the steps; each completed step
|
|
91
|
+
// fills floor(10 * (i+1) / N) cells. Uses the same hex-dot glyphs
|
|
92
|
+
// as the status footer's context bar for visual consistency.
|
|
93
|
+
const BAR_CELLS = 10;
|
|
94
|
+
const buildBar = (completed) => {
|
|
95
|
+
const filled = Math.min(BAR_CELLS, Math.floor((BAR_CELLS * completed) / steps.length));
|
|
96
|
+
const pct = Math.round((completed / steps.length) * 100);
|
|
97
|
+
const fillSeg = theme_1.c.primary('●'.repeat(filled));
|
|
98
|
+
const emptySeg = theme_1.c.muted('○'.repeat(BAR_CELLS - filled));
|
|
99
|
+
const label = completed < steps.length
|
|
100
|
+
? theme_1.c.muted(steps[completed].label + '...')
|
|
101
|
+
: theme_1.c.muted('done');
|
|
102
|
+
return ` ${fillSeg}${emptySeg} ${theme_1.c.text(String(pct).padStart(3) + '%')} ${label}`;
|
|
103
|
+
};
|
|
104
|
+
out.write(buildBar(0) + '\n\n');
|
|
89
105
|
// Pre-paint placeholder rows so the spinner overwrites in place.
|
|
90
106
|
for (const step of steps) {
|
|
91
107
|
const line = ' ' + theme_1.c.muted('·') + ' ' + rpad(step.label, labelCol) +
|
|
92
108
|
' ' + theme_1.c.muted(lpad('—', statusCol));
|
|
93
109
|
out.write(line + '\n');
|
|
94
110
|
}
|
|
95
|
-
// Walk back up to the top of the block.
|
|
111
|
+
// Walk back up to the top of the step block.
|
|
96
112
|
out.write(`\x1b[${steps.length}A`);
|
|
97
113
|
for (let i = 0; i < steps.length; i++) {
|
|
98
114
|
const step = steps[i];
|
|
@@ -127,6 +143,13 @@ async function runLoadingSequence(steps, opts = {}) {
|
|
|
127
143
|
' ' + lpad(statusText, statusCol) + ' ' + timing;
|
|
128
144
|
out.write('\x1b[2K\r' + row + '\n');
|
|
129
145
|
results.push({ label: step.label, ok, status, ms });
|
|
146
|
+
// v4.8.0 Slice 10c — repaint the progress bar above the step
|
|
147
|
+
// block after each step completes. Cursor is currently on the
|
|
148
|
+
// line below the just-completed step; walk up to the bar line
|
|
149
|
+
// (steps.length - i - 1 rows of remaining steps + 1 blank line
|
|
150
|
+
// separator + the bar itself), rewrite, then walk back down.
|
|
151
|
+
const upCount = (steps.length - i - 1) + 2;
|
|
152
|
+
out.write(`\x1b[${upCount}A\x1b[2K\r${buildBar(i + 1)}\x1b[${upCount}B\r`);
|
|
130
153
|
}
|
|
131
154
|
out.write('\n ' + (0, theme_1.separator)(Math.min(w - 4, 64)) + '\n');
|
|
132
155
|
return { ok: results.every((r) => r.ok), steps: results };
|
|
@@ -47,22 +47,31 @@ function renderSuccessScreen(opts = {}) {
|
|
|
47
47
|
out.write('setup-complete\n');
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
+
// v4.8.0 Slice 10b — Aiden-native framed panel chrome. Each row
|
|
51
|
+
// carries the orange bar; content (title + examples + closing
|
|
52
|
+
// hint) preserved verbatim so content-level test assertions hold.
|
|
53
|
+
// v4.8.0 Slice 11c — `▎` swapped for `│` (U+2502); same swap as
|
|
54
|
+
// glyphs.panel.bar in the design tokens.
|
|
50
55
|
const w = (0, theme_1.termWidth)();
|
|
51
56
|
const sepW = Math.min(w - 4, 64);
|
|
52
57
|
const narrow = w < 60;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
const bar = theme_1.c.primary('│');
|
|
59
|
+
const divider = theme_1.c.muted('─'.repeat(sepW - 2));
|
|
60
|
+
const line = (s) => ` ${bar} ${s}`;
|
|
56
61
|
out.write('\n');
|
|
62
|
+
out.write(line((0, theme_1.bold)(theme_1.c.primary('All set!'))) + '\n');
|
|
63
|
+
out.write(line(divider) + '\n');
|
|
64
|
+
out.write(line(theme_1.c.text('Aiden is ready. Try these to start:')) + '\n');
|
|
65
|
+
out.write(line('') + '\n');
|
|
57
66
|
if (narrow) {
|
|
58
|
-
|
|
59
|
-
out.write(' ' + theme_1.c.muted('▸ ') + theme_1.c.accent(examples[0]) + '\n');
|
|
67
|
+
out.write(line(theme_1.c.muted('▸ ') + theme_1.c.accent(examples[0])) + '\n');
|
|
60
68
|
}
|
|
61
69
|
else {
|
|
62
70
|
for (const ex of examples) {
|
|
63
|
-
out.write(
|
|
71
|
+
out.write(line(theme_1.c.muted('▸ ') + theme_1.c.accent(ex)) + '\n');
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
|
-
out.write(
|
|
67
|
-
out.write('
|
|
74
|
+
out.write(line('') + '\n');
|
|
75
|
+
out.write(line(theme_1.c.muted('Or just say hi.')) + '\n');
|
|
76
|
+
out.write('\n');
|
|
68
77
|
}
|
|
@@ -32,6 +32,8 @@ exports._resetForTests = _resetForTests;
|
|
|
32
32
|
const marked_1 = require("marked");
|
|
33
33
|
const skinEngine_1 = require("./skinEngine");
|
|
34
34
|
const syntaxHighlight_1 = require("./syntaxHighlight");
|
|
35
|
+
// v4.8.0 Slice 8 — token-sourced bullet glyphs + task-list markers.
|
|
36
|
+
const tokens_1 = require("./design/tokens");
|
|
35
37
|
// v4.1.4 reply-quality polish: single source of truth for frame math.
|
|
36
38
|
// Replaces 3 inline `Math.min(process.stdout.columns ?? 80, 100) - 4`
|
|
37
39
|
// callsites in this file with `getBodyWidth()` and adds soft-wrap for
|
|
@@ -131,61 +133,45 @@ function paintBold(kind) {
|
|
|
131
133
|
const CODE_BG_ON = '\x1b[48;2;50;50;60m';
|
|
132
134
|
const CODE_BG_OFF = '\x1b[49m';
|
|
133
135
|
function renderCodeBlock(code, lang) {
|
|
136
|
+
// v4.8.0 Slice 9 hotfix — top-divider asymmetric chrome.
|
|
137
|
+
//
|
|
138
|
+
// ── python ─────────────────────────────────────────
|
|
139
|
+
// print("Hello, world!")
|
|
140
|
+
// greet("Aiden")
|
|
141
|
+
//
|
|
142
|
+
// The earlier Slice 9 version used `▎` left-rail on every line and
|
|
143
|
+
// visually competed with the dark-bg syntax highlighting. This
|
|
144
|
+
// revision drops the rail, keeps a single muted `──` top divider
|
|
145
|
+
// with the language label in brand orange, indents body content,
|
|
146
|
+
// and omits the bottom border (Slice 4 asymmetric signature).
|
|
147
|
+
// CODE_BG_ON/OFF envelope preserved.
|
|
134
148
|
const sk = (0, skinEngine_1.getSkinEngine)();
|
|
135
|
-
// v4.1.4 reply-quality polish: width sourced from frame.ts. Same
|
|
136
|
-
// visual budget as the v4.1.3 formula (cols capped at 100, minus
|
|
137
|
-
// gutter+2) — but expressed via the shared helper so it tracks any
|
|
138
|
-
// future width-policy change in one place.
|
|
139
149
|
const width = (0, frame_1.getBodyWidth)();
|
|
140
150
|
const langLabel = (lang ?? '').trim();
|
|
141
|
-
// v4.1.3-essentials reply-polish: language tag on the top rule
|
|
142
|
-
// already shipped; keep it. Bottom rule unlabeled (closing fence).
|
|
143
|
-
const top = langLabel
|
|
144
|
-
? `── ${langLabel} ${'─'.repeat(Math.max(0, width - langLabel.length - 4))}`
|
|
145
|
-
: '─'.repeat(width);
|
|
146
|
-
const bot = '─'.repeat(width);
|
|
147
151
|
const body = (0, syntaxHighlight_1.isSupportedLang)(langLabel)
|
|
148
152
|
? (0, syntaxHighlight_1.highlightCode)(code, langLabel)
|
|
149
153
|
: code;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// - left rail `│ ` painted muted (mirrors blockquote's `┃ ` rail
|
|
164
|
-
// with a different glyph so they're visually distinct)
|
|
165
|
-
// - 24-bit dark background wrapping the rail + content (subtle
|
|
166
|
-
// "this is code" affordance without going full TUI box-frame)
|
|
167
|
-
const rail = sk.applyColors('│', 'muted');
|
|
168
|
-
const gutter = (0, frame_1.getIndent)(0);
|
|
169
|
-
// Wrap each source line independently — code-block semantics demand
|
|
170
|
-
// that a "logical line" remains visible as one continued unit even
|
|
171
|
-
// when soft-wrapped. The CODE_BG painting closes per VISUAL line so
|
|
172
|
-
// a wrap break doesn't bleed bg across the rail of the next row.
|
|
154
|
+
const indent = ' ';
|
|
155
|
+
const hLine = tokens_1.glyphs.chrome.hLine;
|
|
156
|
+
// Top divider: `── <lang> ─────` (lang in brand) OR full-width
|
|
157
|
+
// `────────────` when no language declared.
|
|
158
|
+
const top = langLabel
|
|
159
|
+
? `${indent}${sk.applyColors(`${hLine.repeat(2)} `, 'muted')}` +
|
|
160
|
+
`${sk.applyColors(langLabel, 'brand')}` +
|
|
161
|
+
` ${sk.applyColors(hLine.repeat(Math.max(1, width - langLabel.length - 4)), 'muted')}`
|
|
162
|
+
: `${indent}${sk.applyColors(hLine.repeat(width), 'muted')}`;
|
|
163
|
+
// Body content lands at col 4 (4-space indent inside the divider).
|
|
164
|
+
// Width budget: leave room for body indent + CODE_BG envelope spaces.
|
|
165
|
+
const bodyIndent = ' ';
|
|
166
|
+
const contentBudget = Math.max(8, width - 6);
|
|
173
167
|
const wrappedLines = [];
|
|
174
168
|
for (const srcLine of body.split('\n')) {
|
|
175
169
|
const wrapped = (0, frame_1.wrap)(srcLine, contentBudget, { trim: false, hard: true });
|
|
176
170
|
for (const visualLine of wrapped.split('\n')) {
|
|
177
|
-
wrappedLines.push(`${
|
|
171
|
+
wrappedLines.push(`${bodyIndent}${CODE_BG_ON} ${visualLine} ${CODE_BG_OFF}`);
|
|
178
172
|
}
|
|
179
173
|
}
|
|
180
|
-
|
|
181
|
-
// Top + bottom fence rules sit at the gutter too — visually anchors
|
|
182
|
-
// the block as a unit inside the assistant frame.
|
|
183
|
-
return [
|
|
184
|
-
`${gutter}${sk.applyColors(top, 'muted')}`,
|
|
185
|
-
indented,
|
|
186
|
-
`${gutter}${sk.applyColors(bot, 'muted')}`,
|
|
187
|
-
'',
|
|
188
|
-
].join('\n') + '\n';
|
|
174
|
+
return [top, ...wrappedLines].join('\n') + '\n';
|
|
189
175
|
}
|
|
190
176
|
/**
|
|
191
177
|
* Render a block quote with a `┃` left rail in muted colour.
|
|
@@ -337,7 +323,16 @@ function renderListItemTokens(it, parser) {
|
|
|
337
323
|
// block parser handles these via the normal dispatch (which calls
|
|
338
324
|
// back into our own `renderer.list` override for nested lists —
|
|
339
325
|
// depth counter is already incremented before we got here).
|
|
326
|
+
//
|
|
327
|
+
// v4.8.0 Slice 8 hotfix — ensure inline text and following block
|
|
328
|
+
// tokens are separated by a newline. Without this, a tight-list
|
|
329
|
+
// item like `- Python` followed by a nested `- Django` collapses
|
|
330
|
+
// to `● Python ○ Django` on a single line because head/tail
|
|
331
|
+
// split in renderer.list takes only the first line as `head`.
|
|
340
332
|
if (parser.parse) {
|
|
333
|
+
if (out.length > 0 && !out[out.length - 1].endsWith('\n')) {
|
|
334
|
+
out.push('\n');
|
|
335
|
+
}
|
|
341
336
|
out.push(parser.parse([tk]));
|
|
342
337
|
continue;
|
|
343
338
|
}
|
|
@@ -535,6 +530,10 @@ function getReplyRenderer() {
|
|
|
535
530
|
let isOrdered = false;
|
|
536
531
|
let startNum = 1;
|
|
537
532
|
let items;
|
|
533
|
+
// v4.8.0 Slice 8 — task/checked flags collected alongside items so
|
|
534
|
+
// the marker dispatch below can pick ✔ (checked) or ○ (unchecked).
|
|
535
|
+
// Default false (not a task) so the bullet path stays unchanged.
|
|
536
|
+
let itemTasks = [];
|
|
538
537
|
// CRITICAL: increment depth BEFORE walking items. Item walking via
|
|
539
538
|
// `parser.parse(it.tokens)` recurses into our own override for any
|
|
540
539
|
// nested list tokens — those nested calls need to see the parent's
|
|
@@ -570,10 +569,14 @@ function getReplyRenderer() {
|
|
|
570
569
|
// Confirmed against marked v15 token shapes from `marked.lexer`
|
|
571
570
|
// (see scripts/smoke-issue-c-tokens.ts).
|
|
572
571
|
const parser = this.parser;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
572
|
+
// v4.8.0 Slice 8 — capture GFM task/checked flags alongside the
|
|
573
|
+
// rendered text so the marker dispatch below can pick the right
|
|
574
|
+
// glyph (✔ checked / ○ unchecked) for task-list items.
|
|
575
|
+
const rawItems = tok.items ?? [];
|
|
576
|
+
items = rawItems.map((it) => parser ? renderListItemTokens(it, parser) : (it.text ?? ''));
|
|
577
|
+
itemTasks = rawItems.map((it) => {
|
|
578
|
+
const itx = it;
|
|
579
|
+
return { task: itx.task === true, checked: itx.checked === true };
|
|
577
580
|
});
|
|
578
581
|
}
|
|
579
582
|
else {
|
|
@@ -587,20 +590,33 @@ function getReplyRenderer() {
|
|
|
587
590
|
items = raw.split('\n').filter((ln) => ln.trim().length > 0);
|
|
588
591
|
}
|
|
589
592
|
const indent = ' '.repeat(depth);
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
const bulletGlyph = depth === 1 ?
|
|
593
|
+
// v4.8.0 Slice 8 — token-sourced bullet glyphs. Top-level (depth 1)
|
|
594
|
+
// uses filled `●`, nested (depth ≥ 2) uses hollow `○`. Both painted
|
|
595
|
+
// brand orange to give lists visual identity (was `muted` grey).
|
|
596
|
+
const bulletGlyph = depth === 1 ? tokens_1.glyphs.util.bullet : tokens_1.glyphs.util.bulletOpen;
|
|
594
597
|
const lines = [];
|
|
595
598
|
for (let i = 0; i < items.length; i += 1) {
|
|
596
599
|
const item = items[i];
|
|
597
|
-
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
600
|
+
const task = itemTasks[i] ?? { task: false, checked: false };
|
|
601
|
+
// v4.8.0 Slice 8 — marker dispatch:
|
|
602
|
+
// • GFM checked task → ✔ in semantic success (green)
|
|
603
|
+
// • GFM unchecked task → ○ in tertiary dim (looks incomplete)
|
|
604
|
+
// • Ordered list → `N.` right-padded to 3 cols, brand orange
|
|
605
|
+
// • Bullet → ●/○ by depth, brand orange
|
|
606
|
+
let marker;
|
|
607
|
+
if (task.task && task.checked) {
|
|
608
|
+
marker = paint('success')(tokens_1.glyphs.util.check);
|
|
609
|
+
}
|
|
610
|
+
else if (task.task) {
|
|
611
|
+
marker = paint('tertiary')(tokens_1.glyphs.util.bulletOpen);
|
|
612
|
+
}
|
|
613
|
+
else if (isOrdered) {
|
|
614
|
+
const numStr = `${startNum + i}.`.padStart(3);
|
|
615
|
+
marker = paint('brand')(numStr);
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
marker = paint('brand')(bulletGlyph);
|
|
619
|
+
}
|
|
604
620
|
const itemLines = item.split('\n');
|
|
605
621
|
const head = itemLines[0] ?? '';
|
|
606
622
|
const tail = itemLines.slice(1);
|
|
@@ -47,6 +47,8 @@ const box_1 = require("./box");
|
|
|
47
47
|
// the cost of broken unit tests under the test runtime.
|
|
48
48
|
const successScreen_1 = require("./onboarding/successScreen");
|
|
49
49
|
const providerPicker_1 = require("./onboarding/providerPicker");
|
|
50
|
+
// v4.8.0 Slice 10b — bar + chrome tokens for step headers.
|
|
51
|
+
const tokens_1 = require("./design/tokens");
|
|
50
52
|
const modelFetch_1 = require("../../core/v4/providers/modelFetch");
|
|
51
53
|
const probe_1 = require("../../core/v4/providers/probe");
|
|
52
54
|
// Phase 30.2.1 — provider order optimised for new-user time-to-first-chat.
|
|
@@ -537,8 +539,19 @@ async function runSetupWizard(opts = {}) {
|
|
|
537
539
|
if (opts.prompts) {
|
|
538
540
|
display.printBanner();
|
|
539
541
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
+
// v4.8.0 Slice 10b — step-header helper. Each major wizard step
|
|
543
|
+
// starts with ` ▎ Set up Aiden step N` painted with the orange
|
|
544
|
+
// panel bar so the flow visually consistent with /help and the
|
|
545
|
+
// approval panel. Inquirer widgets render below unchanged.
|
|
546
|
+
const stepHeader = (n) => {
|
|
547
|
+
const bar = display.applyColors(tokens_1.glyphs.panel.bar, 'brand');
|
|
548
|
+
const title = display.applyColors('Set up Aiden', 'heading');
|
|
549
|
+
const sub = display.applyColors(`step ${n}`, 'muted');
|
|
550
|
+
return `\n ${bar} ${title} ${sub}\n`;
|
|
551
|
+
};
|
|
552
|
+
display.write(stepHeader(1));
|
|
553
|
+
display.write(' Welcome — let\'s pick a provider.\n');
|
|
554
|
+
display.write(` ${kleur_1.default.dim('(Press Enter to accept Groq — free + fastest setup.)')}\n\n`);
|
|
542
555
|
// Phase 30.2.1 — Groq is the new recommended default for first-time
|
|
543
556
|
// users: free tier, fastest signup, and avoids the surprise charge
|
|
544
557
|
// path of paid providers. Together AI moved to position [8] paid.
|
|
@@ -695,6 +708,9 @@ async function runSetupWizard(opts = {}) {
|
|
|
695
708
|
// anyway, so the reorder bought nothing there. Existing test
|
|
696
709
|
// fixtures provide inputs in legacy order; preserving custom's
|
|
697
710
|
// order keeps them green.
|
|
711
|
+
if (provider.kind === 'key' || provider.kind === 'subscription' || provider.kind === 'local') {
|
|
712
|
+
display.write(stepHeader(2));
|
|
713
|
+
}
|
|
698
714
|
let apiKey;
|
|
699
715
|
let baseUrl;
|
|
700
716
|
if (provider.kind === 'local') {
|
|
@@ -713,6 +729,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
713
729
|
}
|
|
714
730
|
// provider.kind === 'custom' — defer credential prompts until AFTER
|
|
715
731
|
// the model picker below.
|
|
732
|
+
display.write(stepHeader(3));
|
|
716
733
|
// Step 3: live model fetch + pick.
|
|
717
734
|
//
|
|
718
735
|
// Test-harness gate: when the caller injected `opts.prompts` (only
|
|
@@ -66,6 +66,13 @@ const DEFAULT_SKIN = {
|
|
|
66
66
|
// (which shares the colour) so callers can differentiate in code
|
|
67
67
|
// even though they render identically.
|
|
68
68
|
degraded: [0xff, 0xc1, 0x07],
|
|
69
|
+
// v4.8.0 Slice 7 hotfix #2 — purple accent for the turn-counter
|
|
70
|
+
// segment (⌘) in the status footer. #a48be0 reads as a soft
|
|
71
|
+
// lavender that doesn't compete with brand orange.
|
|
72
|
+
metric_turn: [0xa4, 0x8b, 0xe0],
|
|
73
|
+
// v4.8.0 Slice 8 — tertiary dim grey, dimmer than `muted` (warm
|
|
74
|
+
// tint) for lowest-priority text like unchecked task markers.
|
|
75
|
+
tertiary: [0x6a, 0x6a, 0x6a],
|
|
69
76
|
},
|
|
70
77
|
glyphs: {
|
|
71
78
|
bullet: '•',
|
|
@@ -99,6 +106,10 @@ const LIGHT_SKIN = {
|
|
|
99
106
|
heading: [0xc4, 0x42, 0x10],
|
|
100
107
|
session: [0x00, 0x55, 0x88],
|
|
101
108
|
degraded: [0x80, 0x60, 0x00],
|
|
109
|
+
// Slice 7 hotfix #2 — deeper purple on light bg keeps contrast budget.
|
|
110
|
+
metric_turn: [0x6e, 0x50, 0xaa],
|
|
111
|
+
// Slice 8 — lighter grey on light bg keeps the dim-but-readable feel.
|
|
112
|
+
tertiary: [0x9a, 0x9a, 0x9a],
|
|
102
113
|
},
|
|
103
114
|
glyphs: { ...DEFAULT_SKIN.glyphs },
|
|
104
115
|
};
|
|
@@ -118,6 +129,8 @@ const MONOCHROME_SKIN = {
|
|
|
118
129
|
heading: null,
|
|
119
130
|
session: null,
|
|
120
131
|
degraded: null,
|
|
132
|
+
metric_turn: null,
|
|
133
|
+
tertiary: null,
|
|
121
134
|
},
|
|
122
135
|
glyphs: {
|
|
123
136
|
bullet: '*',
|
package/dist/cli/v4/table.js
CHANGED
|
@@ -23,6 +23,7 @@ exports.renderTable = renderTable;
|
|
|
23
23
|
const stringWidth = require('string-width');
|
|
24
24
|
const skinEngine_1 = require("./skinEngine");
|
|
25
25
|
const box_1 = require("./box");
|
|
26
|
+
const tokens_1 = require("./design/tokens");
|
|
26
27
|
/**
|
|
27
28
|
* Visible (post-ANSI-strip) column width. Falls back to
|
|
28
29
|
* `visibleLength` from box.ts when string-width is unavailable
|
|
@@ -167,13 +168,32 @@ function renderTable(rows, cols, opts = {}) {
|
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
|
-
// Border characters (
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const
|
|
171
|
+
// Border characters — token-sourced from design/tokens.ts (v4.8.0 Slice 3).
|
|
172
|
+
const { topLeft: TL, topRight: TR, botLeft: BL, botRight: BR } = tokens_1.glyphs.chrome;
|
|
173
|
+
const { teeDown: T, teeUp: B, teeRight: L, teeLeft: R } = tokens_1.glyphs.chrome;
|
|
174
|
+
const { cross: X, hLine: H, vLine: V } = tokens_1.glyphs.chrome;
|
|
174
175
|
const ind = ' '.repeat(indent);
|
|
175
|
-
//
|
|
176
|
-
|
|
176
|
+
// Total inner content width across all cells + inner separators.
|
|
177
|
+
// Used by title-embedded top border + page footer.
|
|
178
|
+
const innerWidth = widths.reduce((s, w) => s + w + 2, 0) + (numCols - 1);
|
|
179
|
+
// v4.8.0 Slice 3 — top border with optional embedded title + count.
|
|
180
|
+
// Format: `┌─ title ──────────── totalCount ──┐`
|
|
181
|
+
// Pads the centre with `─` so the right edge stays aligned regardless
|
|
182
|
+
// of title / count length. Falls back to the legacy plain top border
|
|
183
|
+
// when neither field is supplied.
|
|
184
|
+
let top;
|
|
185
|
+
if (opts.title || opts.totalCount) {
|
|
186
|
+
const titleText = opts.title ? ` ${opts.title} ` : '';
|
|
187
|
+
const countText = opts.totalCount ? ` ${opts.totalCount} ` : '';
|
|
188
|
+
const fixed = vWidth(titleText) + vWidth(countText);
|
|
189
|
+
const filler = Math.max(0, innerWidth - fixed);
|
|
190
|
+
const titlePainted = opts.title ? skin.applyColors(titleText, 'heading') : '';
|
|
191
|
+
const countPainted = opts.totalCount ? skin.applyColors(countText, 'muted') : '';
|
|
192
|
+
top = TL + H + titlePainted + H.repeat(filler) + countPainted + H + TR;
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
top = TL + widths.map((w) => H.repeat(w + 2)).join(T) + TR;
|
|
196
|
+
}
|
|
177
197
|
// Header row — heading colour, padded. Truncate first if the
|
|
178
198
|
// header itself is wider than the allocated width (rare, but
|
|
179
199
|
// keeps borders aligned under aggressive narrow-width pressure).
|
|
@@ -203,13 +223,50 @@ function renderTable(rows, cols, opts = {}) {
|
|
|
203
223
|
});
|
|
204
224
|
bodyLines.push(V + cells.join(V) + V);
|
|
205
225
|
});
|
|
206
|
-
//
|
|
207
|
-
|
|
226
|
+
// v4.8.0 Slice 3 — pagination footer above the bottom border. Renders
|
|
227
|
+
// `← prev · page X/Y · next →` centred inside the inner width. Side
|
|
228
|
+
// arrows are dim when the page is at the edge so users can read
|
|
229
|
+
// "at-end" cleanly. Caller wires hotkeys; we just paint the chrome.
|
|
230
|
+
let pageFooter = null;
|
|
231
|
+
if (opts.page) {
|
|
232
|
+
const { current, total } = opts.page;
|
|
233
|
+
const atStart = current <= 1;
|
|
234
|
+
const atEnd = current >= total;
|
|
235
|
+
const leftKind = atStart ? 'muted' : 'session';
|
|
236
|
+
const rightKind = atEnd ? 'muted' : 'session';
|
|
237
|
+
const left = skin.applyColors('← prev', leftKind);
|
|
238
|
+
const mid = skin.applyColors(`page ${current}/${total}`, 'muted');
|
|
239
|
+
const right = skin.applyColors('next →', rightKind);
|
|
240
|
+
const sep = skin.applyColors(' · ', 'muted');
|
|
241
|
+
const body = `${left}${sep}${mid}${sep}${right}`;
|
|
242
|
+
const bodyW = vWidth('← prev') + vWidth(` · page ${current}/${total} · `) + vWidth('next →');
|
|
243
|
+
const padW = Math.max(0, innerWidth - bodyW);
|
|
244
|
+
const lpad = Math.floor(padW / 2);
|
|
245
|
+
const rpad = padW - lpad;
|
|
246
|
+
pageFooter = V + ' '.repeat(lpad) + body + ' '.repeat(rpad) + V;
|
|
247
|
+
}
|
|
248
|
+
// Bottom border. Skip the inner tees when the title-style top was
|
|
249
|
+
// used (legacy plain bottom keeps column alignment for un-titled
|
|
250
|
+
// tables; a title-only border on top reads cleanest with a plain
|
|
251
|
+
// bottom mirror).
|
|
252
|
+
const bot = (opts.title || opts.totalCount)
|
|
253
|
+
? BL + H.repeat(innerWidth) + BR
|
|
254
|
+
: BL + widths.map((w) => H.repeat(w + 2)).join(B) + BR;
|
|
255
|
+
// v4.8.0 Slice 3 — empty-state path. Borders stay so the layout
|
|
256
|
+
// weight matches a populated table; the body is one centered line.
|
|
257
|
+
if (rows.length === 0 && opts.emptyMessage) {
|
|
258
|
+
const msg = skin.applyColors(opts.emptyMessage, 'muted');
|
|
259
|
+
const pad = Math.max(0, innerWidth - vWidth(opts.emptyMessage));
|
|
260
|
+
const lpad = Math.floor(pad / 2);
|
|
261
|
+
const emptyRow = V + ' '.repeat(lpad) + msg + ' '.repeat(pad - lpad) + V;
|
|
262
|
+
return [top, emptyRow, bot].map((l) => ind + l).join('\n') + '\n';
|
|
263
|
+
}
|
|
208
264
|
const allLines = [
|
|
209
265
|
top,
|
|
210
266
|
headerRow,
|
|
211
267
|
...(showRule ? [rule] : []),
|
|
212
268
|
...bodyLines,
|
|
269
|
+
...(pageFooter ? [pageFooter] : []),
|
|
213
270
|
bot,
|
|
214
271
|
].map((l) => ind + l);
|
|
215
272
|
return allLines.join('\n') + '\n';
|
|
@@ -166,6 +166,7 @@ class AidenAgent {
|
|
|
166
166
|
this.resolveVerifiedFlag = opts.resolveVerifiedFlag;
|
|
167
167
|
this.resolveToolset = opts.resolveToolset;
|
|
168
168
|
this.resolveMutates = opts.resolveMutates;
|
|
169
|
+
this.resolveUiOnly = opts.resolveUiOnly;
|
|
169
170
|
this.promptBuilder = opts.promptBuilder;
|
|
170
171
|
this.promptBuilderOptions = opts.promptBuilderOptions;
|
|
171
172
|
this.contextCompressor = opts.contextCompressor;
|
|
@@ -839,6 +840,28 @@ class AidenAgent {
|
|
|
839
840
|
finalContent = '';
|
|
840
841
|
break;
|
|
841
842
|
}
|
|
843
|
+
// v4.8.0 — uiOnly tools are signal channels, not executable
|
|
844
|
+
// tools. The model calls them to communicate render-time
|
|
845
|
+
// state. Dispatch loop skips execute / iteration / mutation
|
|
846
|
+
// marking / verifier / trace / observability hooks and fires
|
|
847
|
+
// onUiEvent on the caller. A '(no output)' tool_result is
|
|
848
|
+
// pushed to satisfy the provider protocol (every tool_call_id
|
|
849
|
+
// needs a matching tool_result). Listener exceptions are
|
|
850
|
+
// swallowed so a bad UI handler cannot break the turn.
|
|
851
|
+
if (this.resolveUiOnly?.(call.name) === true) {
|
|
852
|
+
turnToolMessages.push({
|
|
853
|
+
role: 'tool',
|
|
854
|
+
toolCallId: call.id,
|
|
855
|
+
content: '(no output)',
|
|
856
|
+
});
|
|
857
|
+
try {
|
|
858
|
+
runOptions.onUiEvent?.(call.name, call.arguments);
|
|
859
|
+
}
|
|
860
|
+
catch {
|
|
861
|
+
// defensive — UI listener faults must never break dispatch
|
|
862
|
+
}
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
842
865
|
this.onToolCall?.(call, 'before');
|
|
843
866
|
// v4.2 Phase 4 — mark any active checkpoints as containing a
|
|
844
867
|
// mutating call BEFORE dispatch. Done pre-dispatch (not post)
|