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
package/README.md
CHANGED
|
@@ -242,7 +242,18 @@ Remove-Item -Recurse -Force $env:LOCALAPPDATA\aiden
|
|
|
242
242
|
|
|
243
243
|
<br>
|
|
244
244
|
|
|
245
|
-
<img width="938" height="1049" alt="preview (3)" src="https://github.com/user-attachments/assets/
|
|
245
|
+
<img width="938" height="1049" alt="preview (3)" src="https://github.com/user-attachments/assets/4e32ae38-74ad-433d-b986-0a15bc2dffec" />
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
## Recommended terminal setup
|
|
249
|
+
|
|
250
|
+
For best visual rendering, Aiden looks crispest with:
|
|
251
|
+
|
|
252
|
+
- **Font:** Cascadia Code, JetBrains Mono, or Fira Code at 13–14pt
|
|
253
|
+
- **Terminal:** Windows Terminal, iTerm2, or a modern emulator with truecolor support
|
|
254
|
+
- **Color depth:** truecolor (24-bit) — most modern terminals support this
|
|
255
|
+
|
|
256
|
+
Aiden works on any terminal but glyphs and color depth may degrade gracefully on older / minimal setups.
|
|
246
257
|
|
|
247
258
|
|
|
248
259
|
## Setup wizard
|
package/dist/cli/v4/aidenCLI.js
CHANGED
|
@@ -1089,12 +1089,30 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1089
1089
|
catch {
|
|
1090
1090
|
// Missing or unreadable file is fine — no permanent allowlist yet.
|
|
1091
1091
|
}
|
|
1092
|
-
// Auxiliary client (compression / risk-assessment
|
|
1093
|
-
//
|
|
1094
|
-
//
|
|
1092
|
+
// Auxiliary client (compression / risk-assessment / session-summary
|
|
1093
|
+
// / skill-describe). v4.8.0 Slice 11 — route through Groq's cheap
|
|
1094
|
+
// 8B model as the default, with the parent provider/model as the
|
|
1095
|
+
// fallback when Groq isn't configured. Fixes the ChatGPT Plus +
|
|
1096
|
+
// gpt-5 routing bug: pre-Slice-11 auxiliary inherited the parent
|
|
1097
|
+
// (codex backend, accepts only `gpt-5-codex`/`gpt-4.1-mini`/etc.)
|
|
1098
|
+
// and every aux call returned a 400 model-not-supported. Groq is
|
|
1099
|
+
// cheap, fast, and reliable for the cheap classify/summarise jobs
|
|
1100
|
+
// auxiliary is designed for. If the user has no GROQ_API_KEY, the
|
|
1101
|
+
// resolver throws and we fall through to the parent — no regression
|
|
1102
|
+
// for non-codex users.
|
|
1103
|
+
//
|
|
1104
|
+
// Skip the parent fallback when the parent IS already the Groq cheap
|
|
1105
|
+
// model — same identity attempt twice is just noise in the verbose
|
|
1106
|
+
// log. (Resolver dedup; the auxiliary client itself doesn't filter.)
|
|
1107
|
+
const AUX_DEFAULT_PROVIDER = 'groq';
|
|
1108
|
+
const AUX_DEFAULT_MODEL = 'llama-3.1-8b-instant';
|
|
1109
|
+
const parentSameAsDefault = providerId === AUX_DEFAULT_PROVIDER && modelId === AUX_DEFAULT_MODEL;
|
|
1095
1110
|
const auxiliaryClient = new auxiliaryClient_1.AuxiliaryClient({
|
|
1096
|
-
defaultProvider:
|
|
1097
|
-
defaultModel:
|
|
1111
|
+
defaultProvider: AUX_DEFAULT_PROVIDER,
|
|
1112
|
+
defaultModel: AUX_DEFAULT_MODEL,
|
|
1113
|
+
fallbacks: parentSameAsDefault
|
|
1114
|
+
? []
|
|
1115
|
+
: [{ providerId, modelId }],
|
|
1098
1116
|
// Phase 21 #5: ensure the auxiliary path also honors entry.oauth →
|
|
1099
1117
|
// tokenStore. If a user runs the auxiliary cheap LLM through an
|
|
1100
1118
|
// OAuth-only provider, omitting `paths` would skip the fast-path
|
|
@@ -1113,6 +1131,9 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1113
1131
|
approvalEngine['callbacks'] = {
|
|
1114
1132
|
promptUser: callbacks.promptApproval,
|
|
1115
1133
|
riskAssess: callbacks.riskAssess,
|
|
1134
|
+
// v4.8.0 Phase 2.5 — paint the structured approval row before the
|
|
1135
|
+
// existing y/n prompt runs. Additive; promptApproval flow unchanged.
|
|
1136
|
+
onUiEvent: (name, args) => display.renderUiEvent(name, args),
|
|
1116
1137
|
// Phase 16f: append-on-disk for "Allow always" choices. Single-process
|
|
1117
1138
|
// REPL — atomic write via tmp-then-rename.
|
|
1118
1139
|
persistAllow: (tool, signature) => {
|
|
@@ -1237,6 +1258,12 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1237
1258
|
// tools return undefined → agent treats them as non-mutating (no
|
|
1238
1259
|
// checkpoint flag); plugin authors must declare `mutates` honestly.
|
|
1239
1260
|
const resolveMutates = (name) => toolRegistry.get(name)?.mutates;
|
|
1261
|
+
// v4.8.0 Phase 2.1 — resolver for the uiOnly flag. The dispatch
|
|
1262
|
+
// loop branches on `=== true` so any non-true value (undefined for
|
|
1263
|
+
// unknown tools, false for explicit executables) keeps the normal
|
|
1264
|
+
// executable path. Closure-captures the live registry, same as
|
|
1265
|
+
// resolveMutates / resolveToolset.
|
|
1266
|
+
const resolveUiOnly = (name) => toolRegistry.get(name)?.uiOnly;
|
|
1240
1267
|
// ── Phase 16b.4: assemble system-prompt context ─────────────────────
|
|
1241
1268
|
// PromptBuilder needs SOUL.md (read at build time from `paths.soulMd`),
|
|
1242
1269
|
// a frozen MemorySnapshot (loaded once at boot — same lifecycle as
|
|
@@ -1379,6 +1406,7 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1379
1406
|
resolveVerifiedFlag,
|
|
1380
1407
|
resolveToolset,
|
|
1381
1408
|
resolveMutates,
|
|
1409
|
+
resolveUiOnly,
|
|
1382
1410
|
providerId,
|
|
1383
1411
|
modelId,
|
|
1384
1412
|
// Phase 16b.4: wire PromptBuilder so SOUL.md actually reaches the LLM.
|
|
@@ -1470,6 +1498,7 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1470
1498
|
resolveVerifiedFlag,
|
|
1471
1499
|
resolveToolset,
|
|
1472
1500
|
resolveMutates,
|
|
1501
|
+
resolveUiOnly,
|
|
1473
1502
|
// v4.7.0 Phase 2.4 — share the REPL's config-resolved honesty mode
|
|
1474
1503
|
// with daemon-built agents so autonomous turns honour the same
|
|
1475
1504
|
// setting interactive turns do.
|
|
@@ -1576,6 +1605,7 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1576
1605
|
resolveVerifiedFlag,
|
|
1577
1606
|
resolveToolset,
|
|
1578
1607
|
resolveMutates,
|
|
1608
|
+
resolveUiOnly,
|
|
1579
1609
|
runStore: replRunStore,
|
|
1580
1610
|
instanceId: replInstanceId,
|
|
1581
1611
|
logger: bootLogger.child('subagent'),
|
|
@@ -1650,6 +1680,11 @@ async function buildAgentRuntime(cliOpts, opts) {
|
|
|
1650
1680
|
resolveVerifiedFlag,
|
|
1651
1681
|
resolveToolset,
|
|
1652
1682
|
resolveMutates,
|
|
1683
|
+
resolveUiOnly,
|
|
1684
|
+
// v4.8.0 Phase 2.5 — emit ui_task_update/ui_task_done events so
|
|
1685
|
+
// subagent activity surfaces as gutter-indented trail rows in the
|
|
1686
|
+
// parent's chat surface alongside its own tool trail.
|
|
1687
|
+
onUiEvent: (name, args) => display.renderUiEvent(name, args),
|
|
1653
1688
|
runStore: replRunStore,
|
|
1654
1689
|
instanceId: replInstanceId,
|
|
1655
1690
|
// v4.6 Phase 2Q-B — REPL parent-run wiring. Reads the same
|
package/dist/cli/v4/callbacks.js
CHANGED
|
@@ -21,7 +21,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
exports.CliCallbacks = void 0;
|
|
22
22
|
exports.mapBlockerToCard = mapBlockerToCard;
|
|
23
23
|
exports.renderApprovalBox = renderApprovalBox;
|
|
24
|
-
|
|
24
|
+
// v4.8.0 Slice 5 — verbose-mode gate for internal-telemetry dim lines.
|
|
25
|
+
const tokens_1 = require("./design/tokens");
|
|
25
26
|
async function defaultPrompts() {
|
|
26
27
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
27
28
|
const inq = require('@inquirer/prompts');
|
|
@@ -379,11 +380,12 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
379
380
|
* own SKILL.md (best-effort parse; falls back to id slice).
|
|
380
381
|
*/
|
|
381
382
|
this.onSkillCandidate = (candidate) => {
|
|
383
|
+
// v4.8.0 Slice 5 — internal-telemetry cue; user already discovers
|
|
384
|
+
// candidates via /skills review. Surface only in verbose mode.
|
|
385
|
+
if (!(0, tokens_1.isVerbose)())
|
|
386
|
+
return;
|
|
382
387
|
let name = candidate.id.slice(0, 8);
|
|
383
388
|
try {
|
|
384
|
-
// Tier-3.1c sweep: do not import here — chatSession's display
|
|
385
|
-
// wraps strings, and the SKILL.md frontmatter is plain enough
|
|
386
|
-
// that a quick regex is fine for the cue line.
|
|
387
389
|
const m = /\bname\s*:\s*([^\n]+)/.exec(candidate.skillContent);
|
|
388
390
|
if (m)
|
|
389
391
|
name = m[1].trim();
|
|
@@ -402,6 +404,11 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
402
404
|
this.display.warn('[compress] auxiliary call failed; history unchanged');
|
|
403
405
|
return;
|
|
404
406
|
}
|
|
407
|
+
// v4.8.0 Slice 5 — successful auto-compress is technical telemetry
|
|
408
|
+
// (refused / failed variants stay visible since they explain action
|
|
409
|
+
// outcomes; success is just bookkeeping).
|
|
410
|
+
if (!(0, tokens_1.isVerbose)())
|
|
411
|
+
return;
|
|
405
412
|
this.display.dim(`[compress] removed ${result.removedMessageCount} msgs, kept ${result.preservedRecentCount} recent (~${result.summaryTokens} tok)`);
|
|
406
413
|
};
|
|
407
414
|
/** Budget warning sink. Caution = dim line, warning = visible warn. */
|
|
@@ -410,7 +417,9 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
410
417
|
if (level === 'warning') {
|
|
411
418
|
this.display.warn(`Budget: ${msg} — approaching the cap.`);
|
|
412
419
|
}
|
|
413
|
-
else {
|
|
420
|
+
else if ((0, tokens_1.isVerbose)()) {
|
|
421
|
+
// v4.8.0 Slice 5 — caution-level per-turn dim line is verbose-only;
|
|
422
|
+
// the actionable 'warning' tier above continues to fire unchanged.
|
|
414
423
|
this.display.dim(`[budget] ${msg}`);
|
|
415
424
|
}
|
|
416
425
|
};
|
|
@@ -424,6 +433,10 @@ Reply with ONE word: safe, caution, or dangerous.`;
|
|
|
424
433
|
this.onMemoryRefresh = (files) => {
|
|
425
434
|
// Phase v4.1.2: argument switched from single-string-or-'both' to
|
|
426
435
|
// the full sorted set of dirty files (SOUL.md joined the rotation).
|
|
436
|
+
// v4.8.0 Slice 5 — internal cache refresh; the user's "✓ Saved"
|
|
437
|
+
// confirmation lands separately when memory_add returns verified=true.
|
|
438
|
+
if (!(0, tokens_1.isVerbose)())
|
|
439
|
+
return;
|
|
427
440
|
const label = files.length > 0 ? files.join(', ') : 'none';
|
|
428
441
|
this.display.dim(`[memory] refreshed system prompt (${label})`);
|
|
429
442
|
};
|
|
@@ -616,23 +629,29 @@ const APPROVAL_BOX_WIDTH = 64;
|
|
|
616
629
|
// and the user wouldn't see they were viewing a partial value.
|
|
617
630
|
const APPROVAL_ARGS_LIMIT = 50;
|
|
618
631
|
/**
|
|
619
|
-
* Render an approval request
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
632
|
+
* Render an approval request with the Aiden-native framed-panel chrome
|
|
633
|
+
* (Slice 6) — orange left bar, no closing corners, footer hint always
|
|
634
|
+
* present. Token-sourced from cli/v4/design/tokens.ts. Returns the
|
|
635
|
+
* multi-line string; caller writes it. Args are truncated to
|
|
636
|
+
* APPROVAL_ARGS_LIMIT chars for display only; the full args stay with
|
|
637
|
+
* the tool call. Colour discipline: brand (bar + title + key glyphs),
|
|
638
|
+
* tier-semantic (badge), muted (everything else) — ≤3 distinct colours.
|
|
623
639
|
*/
|
|
624
640
|
function renderApprovalBox(req, display) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
const
|
|
641
|
+
// v4.8.0 Slice 6 hotfix:
|
|
642
|
+
// - Drop panel title + tier badge (Phase 2.5 ui_approval_request event
|
|
643
|
+
// row above already announces the headline + tier-by-colour).
|
|
644
|
+
// - Lead with structured key/value rows; unify inner-padding so keys
|
|
645
|
+
// and dividers share the same left edge.
|
|
646
|
+
// - Footer hint matches the actual inquirer select() mechanic
|
|
647
|
+
// (arrow-key navigation), not fictional y/a/n keystrokes.
|
|
648
|
+
// - Leading + trailing blank lines for vertical breathing room
|
|
649
|
+
// between the event row above and the inquirer picker below.
|
|
650
|
+
const indent = ' ';
|
|
651
|
+
const innerW = APPROVAL_BOX_WIDTH;
|
|
652
|
+
const bar = display.applyColors(tokens_1.glyphs.panel.bar, 'brand');
|
|
653
|
+
const line = (content) => `${indent}${bar} ${content}`;
|
|
654
|
+
const divider = display.muted(tokens_1.glyphs.chrome.hLine.repeat(innerW - 2));
|
|
636
655
|
let argsPreview = '';
|
|
637
656
|
try {
|
|
638
657
|
argsPreview = JSON.stringify(req.args);
|
|
@@ -643,17 +662,19 @@ function renderApprovalBox(req, display) {
|
|
|
643
662
|
if (argsPreview.length > APPROVAL_ARGS_LIMIT) {
|
|
644
663
|
argsPreview = argsPreview.slice(0, APPROVAL_ARGS_LIMIT - 1) + '…';
|
|
645
664
|
}
|
|
665
|
+
// Key-value rows. Key column padded to 12 cells for vertical alignment.
|
|
666
|
+
const KEY_W = 12;
|
|
667
|
+
const kv = (k, v) => `${display.muted(k.padEnd(KEY_W))}${v}`;
|
|
646
668
|
const lines = [
|
|
647
|
-
|
|
648
|
-
side(''),
|
|
649
|
-
side(` ${display.muted('Tool:')} ${req.toolName}${tierBadge ? ' ' + tierBadge : ''}`),
|
|
669
|
+
line(kv('tool', req.toolName)),
|
|
650
670
|
];
|
|
651
|
-
if (req.reason)
|
|
652
|
-
lines.push(
|
|
653
|
-
|
|
654
|
-
lines.push(
|
|
655
|
-
lines.push(
|
|
656
|
-
lines
|
|
657
|
-
|
|
658
|
-
|
|
671
|
+
if (req.reason)
|
|
672
|
+
lines.push(line(kv('reason', req.reason)));
|
|
673
|
+
lines.push(line(kv('args', argsPreview)));
|
|
674
|
+
lines.push(line(divider));
|
|
675
|
+
lines.push(line(display.muted('↑↓ navigate · enter select · esc cancel')));
|
|
676
|
+
// Leading + trailing blank lines: caller already adds one trailing
|
|
677
|
+
// newline, so producing '\n<panel>\n' yields one blank above + one
|
|
678
|
+
// blank below once the caller's own '\n' lands.
|
|
679
|
+
return '\n' + lines.join('\n') + '\n';
|
|
659
680
|
}
|
|
@@ -234,6 +234,11 @@ class ChatSession {
|
|
|
234
234
|
// turn's elapsed ms (rendered in the trailing footer) and the
|
|
235
235
|
// provider used last turn (so a switch surfaces as `groq ──→ together`).
|
|
236
236
|
this.lastTurnElapsedMs = 0;
|
|
237
|
+
// v4.8.0 Slice 7 — status-footer telemetry. turnCount increments on
|
|
238
|
+
// every completed turn (success OR failure paths); lastTurnOutcome
|
|
239
|
+
// maps result.finishReason to a colour-kind hint for the state dot.
|
|
240
|
+
this.turnCount = 0;
|
|
241
|
+
this.lastTurnOutcome = 'ok';
|
|
237
242
|
this.lastFooterProvider = null;
|
|
238
243
|
/**
|
|
239
244
|
* Phase v4.1.2-memory-AB:
|
|
@@ -991,6 +996,11 @@ class ChatSession {
|
|
|
991
996
|
const indicator = this.opts.display.activityIndicator('thinking');
|
|
992
997
|
let indicatorStopped = false;
|
|
993
998
|
let streamingActive = false;
|
|
999
|
+
// v4.8.0 Phase 2.3 fix-2 — clear the ui-event flag at turn-start.
|
|
1000
|
+
// The existing reset sites in Display (streamPartial first-delta +
|
|
1001
|
+
// streamComplete) only fire on text-streaming turns; tool-only
|
|
1002
|
+
// turns leave the flag sticky. This is the authoritative reset.
|
|
1003
|
+
this.opts.display.resetUiTurnState();
|
|
994
1004
|
// v4.1.5 Issue O — track whether this turn had any tool calls so
|
|
995
1005
|
// we can emit a single muted rule between the tool trail and the
|
|
996
1006
|
// reply header. Set true when the first tool's `before` phase
|
|
@@ -1187,6 +1197,19 @@ class ChatSession {
|
|
|
1187
1197
|
this.opts.display.streamToolIndicator(call.name);
|
|
1188
1198
|
}
|
|
1189
1199
|
: undefined,
|
|
1200
|
+
// v4.8.0 Phase 2.3 fix-2 — uiOnly events route to the display
|
|
1201
|
+
// layer. The Phase 2.1 dispatch branch in aidenAgent.ts skips
|
|
1202
|
+
// onToolCall('before'), which is what normally fires
|
|
1203
|
+
// beforeFirstToolHook → stopIndicatorOnce. Without this stop
|
|
1204
|
+
// call, the 250ms indicator tick walks up 2 lines and erases
|
|
1205
|
+
// our paint within a quarter-second. Stop the indicator here,
|
|
1206
|
+
// mirroring how a first regular tool call stops it. Phase 2.3
|
|
1207
|
+
// handles ui_task_update + ui_task_done; the other 5 event
|
|
1208
|
+
// names land in Phase 2.4 (renderer silent-ignores them).
|
|
1209
|
+
onUiEvent: (name, args) => {
|
|
1210
|
+
stopIndicatorOnce();
|
|
1211
|
+
this.opts.display.renderUiEvent(name, args);
|
|
1212
|
+
},
|
|
1190
1213
|
onProgress: streamingEnabled
|
|
1191
1214
|
? (outputTokens, maxTokens) => {
|
|
1192
1215
|
if (indicatorStopped === false)
|
|
@@ -1316,6 +1339,12 @@ class ChatSession {
|
|
|
1316
1339
|
}
|
|
1317
1340
|
this.setStatusState({ kind: 'ready' });
|
|
1318
1341
|
this.lastTurnElapsedMs = Date.now() - turnStartedAt;
|
|
1342
|
+
// v4.8.0 Slice 7 — record per-turn outcome for the status dot.
|
|
1343
|
+
this.turnCount += 1;
|
|
1344
|
+
this.lastTurnOutcome =
|
|
1345
|
+
result.finishReason === 'stop' ? 'ok' :
|
|
1346
|
+
result.finishReason === 'budget_exhausted' ? 'warn' :
|
|
1347
|
+
result.finishReason === 'interrupted' ? 'muted' : 'error';
|
|
1319
1348
|
// v4.5 Phase 8b — surface a deferred daemon-scheduling tip
|
|
1320
1349
|
// queued at turn start. Renders AFTER the agent's response per
|
|
1321
1350
|
// Q-P8b-3(b) — the user reads the answer first, then sees the
|
|
@@ -1414,6 +1443,10 @@ class ChatSession {
|
|
|
1414
1443
|
}
|
|
1415
1444
|
this.setStatusState({ kind: 'ready' });
|
|
1416
1445
|
this.lastTurnElapsedMs = Date.now() - turnStartedAt;
|
|
1446
|
+
// v4.8.0 Slice 7 — error path also bumps the turn counter and
|
|
1447
|
+
// records a red state-dot outcome for the next footer render.
|
|
1448
|
+
this.turnCount += 1;
|
|
1449
|
+
this.lastTurnOutcome = 'error';
|
|
1417
1450
|
// v4.1.5+ Path A — finalize the loop trace on the error path
|
|
1418
1451
|
// too. Loop patterns that ended in an error are exactly the
|
|
1419
1452
|
// ones most worth capturing for diagnosis.
|
|
@@ -1614,6 +1647,12 @@ class ChatSession {
|
|
|
1614
1647
|
}
|
|
1615
1648
|
catch { /* never let a missing marker crash boot */ }
|
|
1616
1649
|
// Bottom prompt hint — final line of the boot card.
|
|
1650
|
+
// v4.8.0 Slice 10d — full-width muted `─` divider between the
|
|
1651
|
+
// boot card (parchment / capability card / two-column block) and
|
|
1652
|
+
// the input hint prevents the boot chrome from merging visually
|
|
1653
|
+
// with the active prompt. Pattern: blank · rule · blank · hint.
|
|
1654
|
+
display.write('\n');
|
|
1655
|
+
display.write(` ${display.rule()}\n`);
|
|
1617
1656
|
display.write('\n');
|
|
1618
1657
|
display.write(display.bottomPromptHint() + '\n');
|
|
1619
1658
|
}
|
|
@@ -1694,12 +1733,18 @@ class ChatSession {
|
|
|
1694
1733
|
}
|
|
1695
1734
|
const usedTokens = this.modelMetadata.estimateMessageTokens(this.history);
|
|
1696
1735
|
const maxTokens = limits.contextLength;
|
|
1697
|
-
|
|
1736
|
+
// v4.8.0 Slice 7 hotfix — predictable 1-blank-line rhythm: one
|
|
1737
|
+
// blank above the footer (visual breath after the reply), one
|
|
1738
|
+
// blank below (before the next prompt).
|
|
1739
|
+
display.write('\n' + display.statusFooter({
|
|
1698
1740
|
provider,
|
|
1699
1741
|
model,
|
|
1700
1742
|
ctxUsed: usedTokens,
|
|
1701
1743
|
ctxMax: maxTokens,
|
|
1702
1744
|
elapsedMs: this.lastTurnElapsedMs,
|
|
1745
|
+
turnCount: this.turnCount,
|
|
1746
|
+
sessionMs: Date.now() - this.startedAt,
|
|
1747
|
+
state: this.lastTurnOutcome,
|
|
1703
1748
|
}) + '\n\n');
|
|
1704
1749
|
}
|
|
1705
1750
|
// ── Input ──────────────────────────────────────────────────────────
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.help = exports.SUBSECTION_MAP = exports.SUBSECTION_ORDER = void 0;
|
|
4
4
|
exports.subsectionFor = subsectionFor;
|
|
5
|
+
const framedPanel_1 = require("../display/framedPanel");
|
|
5
6
|
/**
|
|
6
7
|
* Order matters: sections render in this order. Commands within a
|
|
7
8
|
* section render in registration (alphabetical via registry.list)
|
|
@@ -98,25 +99,35 @@ exports.help = {
|
|
|
98
99
|
for (const c of system) {
|
|
99
100
|
buckets.get(subsectionFor(c.name)).push(c);
|
|
100
101
|
}
|
|
101
|
-
//
|
|
102
|
+
// v4.8.0 Slice 4 — every section renders as an Aiden-native framed
|
|
103
|
+
// panel: left orange accent bar, title + count subtitle, command
|
|
104
|
+
// rows, footer hint always present. AIDEN_UI_ICONS still respected
|
|
105
|
+
// for the inline glyph column. Sections stack vertically; one
|
|
106
|
+
// blank line between them comes from the panel's trailing newline.
|
|
102
107
|
const showIcons = process.env.AIDEN_UI_ICONS === '1';
|
|
108
|
+
const toRows = (cmds) => cmds.map((c) => ({
|
|
109
|
+
command: `${showIcons ? `${c.icon ?? ' '} ` : ''}/${c.name}`,
|
|
110
|
+
description: c.description,
|
|
111
|
+
}));
|
|
103
112
|
for (const sec of exports.SUBSECTION_ORDER) {
|
|
104
113
|
const cmds = buckets.get(sec);
|
|
105
114
|
if (cmds.length === 0)
|
|
106
115
|
continue;
|
|
107
|
-
ctx.display.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
ctx.display.write((0, framedPanel_1.renderFramedPanel)({
|
|
117
|
+
title: sec,
|
|
118
|
+
subtitle: `${cmds.length} ${cmds.length === 1 ? 'command' : 'commands'}`,
|
|
119
|
+
rows: toRows(cmds),
|
|
120
|
+
footer: 'type /<name> to run · /help for this list',
|
|
121
|
+
}));
|
|
112
122
|
ctx.display.write('\n');
|
|
113
123
|
}
|
|
114
124
|
if (skill.length > 0) {
|
|
115
|
-
ctx.display.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
ctx.display.write((0, framedPanel_1.renderFramedPanel)({
|
|
126
|
+
title: 'Skills',
|
|
127
|
+
subtitle: `${skill.length} ${skill.length === 1 ? 'command' : 'commands'}`,
|
|
128
|
+
rows: toRows(skill),
|
|
129
|
+
footer: 'type /<name> to run · /skills list to browse installed skills',
|
|
130
|
+
}));
|
|
120
131
|
}
|
|
121
132
|
return {};
|
|
122
133
|
},
|
|
@@ -35,6 +35,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
35
35
|
const node_path_1 = __importDefault(require("node:path"));
|
|
36
36
|
const daemon_1 = require("../../../core/v4/daemon");
|
|
37
37
|
const paths_1 = require("../../../core/v4/paths");
|
|
38
|
+
const table_1 = require("../table");
|
|
38
39
|
const noopOut = (s) => { process.stdout.write(s); };
|
|
39
40
|
const noopErr = (s) => { process.stderr.write(s); };
|
|
40
41
|
async function runRunsSubcommand(action, args, argv, opts = {}) {
|
|
@@ -74,18 +75,12 @@ function cmdList(runStore, argv, out) {
|
|
|
74
75
|
sessionIdPrefix: argv.trigger,
|
|
75
76
|
topLevelOnly: !includeChildren,
|
|
76
77
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const started = new Date(r.startedAt).toISOString().slice(0, 19) + 'Z';
|
|
84
|
-
const finish = r.finishReason ?? '-';
|
|
85
|
-
// v4.6 Phase 2Q-B — child-count badge. Only relevant for the
|
|
86
|
-
// top-level view (when --include-children is OFF). Skipped on
|
|
87
|
-
// the flat view to avoid double-counting visual weight: in flat
|
|
88
|
-
// mode the children are already on screen as their own rows.
|
|
78
|
+
// v4.8.0 Slice 3 — migrate from padEnd string concatenation to the
|
|
79
|
+
// framed table primitive. Title + count in the top border; empty
|
|
80
|
+
// state paints a framed message so layout weight matches populated
|
|
81
|
+
// runs. The trigger-badge (child-count summary) becomes part of the
|
|
82
|
+
// sessionId cell's `format` so column widths still auto-fit.
|
|
83
|
+
const tableRows = rows.map((r) => {
|
|
89
84
|
let badge = '';
|
|
90
85
|
if (!includeChildren) {
|
|
91
86
|
const { total, completed } = runStore.countChildren(r.id);
|
|
@@ -93,12 +88,31 @@ function cmdList(runStore, argv, out) {
|
|
|
93
88
|
badge = ` (${total} ${total === 1 ? 'child' : 'children'}, ${completed} OK)`;
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
|
-
|
|
91
|
+
return {
|
|
92
|
+
runId: String(r.id),
|
|
93
|
+
status: r.status,
|
|
94
|
+
finish: r.finishReason ?? '-',
|
|
95
|
+
started: new Date(r.startedAt).toISOString().slice(0, 19) + 'Z',
|
|
96
|
+
sessionId: r.sessionId + badge,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
out((0, table_1.renderTable)(tableRows, [
|
|
100
|
+
{ key: 'runId', header: 'runId', align: 'left' },
|
|
101
|
+
{ key: 'status', header: 'status', align: 'left' },
|
|
102
|
+
{ key: 'finish', header: 'finish', align: 'left' },
|
|
103
|
+
{ key: 'started', header: 'started', align: 'left' },
|
|
104
|
+
{ key: 'sessionId', header: 'sessionId', align: 'left', flex: true },
|
|
105
|
+
], {
|
|
106
|
+
title: 'Recent runs',
|
|
107
|
+
totalCount: `${rows.length} ${rows.length === 1 ? 'run' : 'runs'}`,
|
|
108
|
+
emptyMessage: 'no runs match the filter',
|
|
109
|
+
}));
|
|
110
|
+
if (rows.length > 0) {
|
|
111
|
+
const hint = includeChildren
|
|
112
|
+
? '(parents + sub-agent children)'
|
|
113
|
+
: '(top-level; use --include-children for sub-agents)';
|
|
114
|
+
out(` ${hint}\n`);
|
|
97
115
|
}
|
|
98
|
-
const hint = includeChildren
|
|
99
|
-
? ' (parents + sub-agent children)'
|
|
100
|
-
: ' (top-level; use --include-children for sub-agents)';
|
|
101
|
-
out(`\n${rows.length} run${rows.length === 1 ? '' : 's'} shown${hint}\n`);
|
|
102
116
|
return 0;
|
|
103
117
|
}
|
|
104
118
|
// ── show ──────────────────────────────────────────────────────────────────
|
|
@@ -205,13 +219,17 @@ function cmdStats(db, out) {
|
|
|
205
219
|
COUNT(*) AS n
|
|
206
220
|
FROM runs
|
|
207
221
|
WHERE status = 'completed' AND completed_at IS NOT NULL`).get();
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
222
|
+
// v4.8.0 Slice 3 — framed table replaces the padEnd block. Right-
|
|
223
|
+
// align the count column so multi-digit totals don't break visual
|
|
224
|
+
// rhythm. Empty state paints a framed message.
|
|
225
|
+
out((0, table_1.renderTable)(counts.map((r) => ({ status: r.status, count: String(r.c) })), [
|
|
226
|
+
{ key: 'status', header: 'status', align: 'left' },
|
|
227
|
+
{ key: 'count', header: 'count', align: 'right' },
|
|
228
|
+
], {
|
|
229
|
+
title: 'Run status counts',
|
|
230
|
+
totalCount: `${counts.length} ${counts.length === 1 ? 'status' : 'statuses'}`,
|
|
231
|
+
emptyMessage: 'no runs recorded',
|
|
232
|
+
}));
|
|
215
233
|
if (completed.n > 0 && completed.mean !== null) {
|
|
216
234
|
out('\nCompleted-run duration (ms):\n');
|
|
217
235
|
out(` mean ${Math.round(completed.mean)}\n`);
|
|
@@ -35,17 +35,17 @@ exports.skills = {
|
|
|
35
35
|
return {};
|
|
36
36
|
}
|
|
37
37
|
const skills = await ctx.skillLoader.list();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
ctx.display.info(`Installed skills (${skills.length}):`);
|
|
38
|
+
// v4.8.0 Slice 3 — title + count in the top border replaces the
|
|
39
|
+
// separate `Installed skills (N):` info line. Empty state paints
|
|
40
|
+
// a framed message so layout weight matches populated tables.
|
|
43
41
|
ctx.display.write((0, table_1.renderTable)(skills.map((s) => ({ name: s.name, description: s.description ?? '' })), [
|
|
44
42
|
{ key: 'name', header: 'Name', align: 'left', minWidth: 16 },
|
|
45
|
-
// Tier-3.1b: drop the legacy `truncate: 60` cap so the
|
|
46
|
-
// description column flexes to fill available width.
|
|
47
43
|
{ key: 'description', header: 'Description', align: 'left', flex: true },
|
|
48
|
-
]
|
|
44
|
+
], {
|
|
45
|
+
title: 'Skills',
|
|
46
|
+
totalCount: `${skills.length} installed`,
|
|
47
|
+
emptyMessage: 'no skills installed',
|
|
48
|
+
}));
|
|
49
49
|
return {};
|
|
50
50
|
}
|
|
51
51
|
if (sub === 'view') {
|
|
@@ -102,15 +102,7 @@ exports.skills = {
|
|
|
102
102
|
const store = new candidateStore_1.CandidateStore();
|
|
103
103
|
if (sub === 'review') {
|
|
104
104
|
const candidates = await store.list();
|
|
105
|
-
if (candidates.length === 0) {
|
|
106
|
-
ctx.display.dim('(no pending candidates — mined skills appear here after a successful 3+ tool turn)');
|
|
107
|
-
return {};
|
|
108
|
-
}
|
|
109
|
-
ctx.display.info(`Pending mined candidates (${candidates.length}):`);
|
|
110
105
|
ctx.display.write((0, table_1.renderTable)(candidates.map((c) => {
|
|
111
|
-
// Pull the name + 1-line description from the candidate's
|
|
112
|
-
// own SKILL.md so the table reflects what the user will
|
|
113
|
-
// accept verbatim.
|
|
114
106
|
let name = '(unparsed)';
|
|
115
107
|
let description = '';
|
|
116
108
|
try {
|
|
@@ -134,7 +126,13 @@ exports.skills = {
|
|
|
134
126
|
{ key: 'session', header: 'Session', align: 'left' },
|
|
135
127
|
{ key: 'created', header: 'Created', align: 'left' },
|
|
136
128
|
{ key: 'description', header: 'Description', align: 'left', flex: true },
|
|
137
|
-
]
|
|
129
|
+
], {
|
|
130
|
+
title: 'Pending skill candidates',
|
|
131
|
+
totalCount: `${candidates.length} pending`,
|
|
132
|
+
emptyMessage: 'no pending candidates — mined skills appear here after a successful 3+ tool turn',
|
|
133
|
+
}));
|
|
134
|
+
if (candidates.length === 0)
|
|
135
|
+
return {};
|
|
138
136
|
ctx.display.dim('Use `/skills view-candidate <id-prefix>` to preview, `/skills accept <id>` to promote, `/skills reject <id> [reason]` to dismiss.');
|
|
139
137
|
return {};
|
|
140
138
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.usage = void 0;
|
|
4
4
|
const modelCatalog_1 = require("../../../providers/v4/modelCatalog");
|
|
5
|
+
const table_1 = require("../table");
|
|
5
6
|
exports.usage = {
|
|
6
7
|
name: 'usage',
|
|
7
8
|
description: 'Show token consumption and estimated cost.',
|
|
@@ -33,12 +34,23 @@ exports.usage = {
|
|
|
33
34
|
const aux = ctx.auxiliaryClient.getUsage();
|
|
34
35
|
const purposes = Object.keys(aux);
|
|
35
36
|
if (purposes.length > 0) {
|
|
37
|
+
// v4.8.0 Slice 3 — framed auxiliary-calls table replaces the
|
|
38
|
+
// ad-hoc padEnd lines. Right-align numeric columns.
|
|
36
39
|
ctx.display.write('\n');
|
|
37
|
-
ctx.display.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
ctx.display.write((0, table_1.renderTable)(purposes.map((p) => ({
|
|
41
|
+
purpose: p,
|
|
42
|
+
calls: String(aux[p].calls),
|
|
43
|
+
in: String(aux[p].inputTokens),
|
|
44
|
+
out: String(aux[p].outputTokens),
|
|
45
|
+
})), [
|
|
46
|
+
{ key: 'purpose', header: 'purpose', align: 'left' },
|
|
47
|
+
{ key: 'calls', header: 'calls', align: 'right' },
|
|
48
|
+
{ key: 'in', header: 'in', align: 'right' },
|
|
49
|
+
{ key: 'out', header: 'out', align: 'right' },
|
|
50
|
+
], {
|
|
51
|
+
title: 'Auxiliary calls',
|
|
52
|
+
totalCount: `${purposes.length} ${purposes.length === 1 ? 'purpose' : 'purposes'}`,
|
|
53
|
+
}));
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
56
|
return {};
|
|
@@ -111,6 +111,7 @@ function buildDaemonAgentBuilder(deps) {
|
|
|
111
111
|
resolveVerifiedFlag: deps.resolveVerifiedFlag,
|
|
112
112
|
resolveToolset: deps.resolveToolset,
|
|
113
113
|
resolveMutates: deps.resolveMutates,
|
|
114
|
+
resolveUiOnly: deps.resolveUiOnly,
|
|
114
115
|
// Memory snapshot refresh — daemon agent doesn't track dirty
|
|
115
116
|
// bits because each instance is short-lived; we provide the
|
|
116
117
|
// refresh callback so honestyEnforcement (and any future
|