@aragon-agent/cli 0.5.11 → 0.5.12
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/CHANGELOG.md +24 -0
- package/README.md +5 -3
- package/dist/agent/controller.d.ts +5 -1
- package/dist/agent/controller.js +9 -4
- package/dist/agent/reducer.js +14 -4
- package/dist/commands/builtins.js +21 -2
- package/dist/todo/store.d.ts +12 -6
- package/dist/todo/store.js +12 -6
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/ActivityLine.d.ts +16 -0
- package/dist/ui/App.js +32 -3
- package/dist/ui/Transcript.js +15 -0
- package/dist/ui/entries/FastCard.js +20 -2
- package/dist/ui/overlays/HelpOverlay.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,30 @@ were written.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`/clear` now takes the todo panel with it.** Finish a plan, type `/clear`,
|
|
13
|
+
and the transcript emptied while the `7/7 done` rail stayed exactly where it
|
|
14
|
+
was — on a screen you had just asked to be cleared. It stayed until your next
|
|
15
|
+
real message, which is precisely the pause you take after clearing to decide
|
|
16
|
+
what to ask next, so essentially everyone met it.
|
|
17
|
+
|
|
18
|
+
This was deliberate once. The list is a projection of what the model believes,
|
|
19
|
+
and `/clear` wipes the transcript rather than the conversation, so the model
|
|
20
|
+
still believed in the plan and a panel contradicting the model was judged worse
|
|
21
|
+
than no panel. That reasoning holds for everything the CLI does on its own; it
|
|
22
|
+
does not hold against you typing the command. An explicit instruction was
|
|
23
|
+
always this rule's exception — `/todo clear` has used the same door from the
|
|
24
|
+
start — and `/clear` now goes through it.
|
|
25
|
+
|
|
26
|
+
The list itself is cleared, not just the pixels, so `/save`, `/todo status`,
|
|
27
|
+
`/todo continue` and auto follow-through all agree with the screen: no plan is
|
|
28
|
+
written into a saved session, nothing offers to continue one, and nothing spends
|
|
29
|
+
a follow-through on it. `/reset` is unchanged and still the only command that
|
|
30
|
+
clears the conversation. Nothing is stranded either way — the model's next
|
|
31
|
+
`todo_write` is a full replacement, so the panel comes back the moment there is
|
|
32
|
+
a plan to show.
|
|
33
|
+
|
|
10
34
|
### Added
|
|
11
35
|
|
|
12
36
|
- **An update that will not start is undone automatically.** The updater shipped
|
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ Exit codes: `0` success · `1` agent/runtime error · `2` config/usage error ·
|
|
|
107
107
|
| `/thinking <level>` | Set the thinking level |
|
|
108
108
|
| `/max-tokens [<n>\|auto\|default]` | Output token cap; with no argument, reports the setting **and** the effective cap for the live model |
|
|
109
109
|
| `/tools` | List active tools |
|
|
110
|
-
| `/clear` | Clear the visible transcript |
|
|
110
|
+
| `/clear` | Clear the visible transcript and the todo panel |
|
|
111
111
|
| `/reset` | New conversation |
|
|
112
112
|
| `/cwd [dir]` | Show / change the tool working directory |
|
|
113
113
|
| `/save [file]` | Save the session to JSON |
|
|
@@ -498,9 +498,11 @@ against an empty plan is refused outright. A session that never plans has
|
|
|
498
498
|
byte-identical layout to a build without this feature.
|
|
499
499
|
|
|
500
500
|
**The list is a projection of what the model believes**, so nothing that does
|
|
501
|
-
not also tell the model may change it:
|
|
501
|
+
not also tell the model may change it — except you, saying so:
|
|
502
502
|
|
|
503
|
-
- `/clear`
|
|
503
|
+
- `/clear` and `/todo clear` take the plan off the screen. Neither touches the
|
|
504
|
+
conversation, so the model still knows what it was doing and its next update
|
|
505
|
+
brings the panel straight back.
|
|
504
506
|
- `/reset` clears the conversation, so the plan goes with it.
|
|
505
507
|
- `/save` writes the list and `/resume` restores it. Resuming a session that has
|
|
506
508
|
no list clears the current one, rather than leaving a plan on screen whose
|
|
@@ -339,7 +339,11 @@ export declare class AgentController {
|
|
|
339
339
|
subscribeTodos(listener: TodoEventListener): () => void;
|
|
340
340
|
/** `/resume`. An absent or empty list CLEARS — see `TodoStore.restore` (P0-2). */
|
|
341
341
|
restoreTodos(items: unknown): void;
|
|
342
|
-
/**
|
|
342
|
+
/**
|
|
343
|
+
* `/todo clear` and `/clear`. A user override of a projection (I-2's standing
|
|
344
|
+
* exception). `/todo` refuses mid-run before it gets here; `/clear` does not,
|
|
345
|
+
* on purpose — refusing there would leave it unable to clear the transcript.
|
|
346
|
+
*/
|
|
343
347
|
clearTodos(): void;
|
|
344
348
|
getRetryConfig(): RetryConfig;
|
|
345
349
|
/**
|
package/dist/agent/controller.js
CHANGED
|
@@ -916,7 +916,11 @@ export class AgentController {
|
|
|
916
916
|
restoreTodos(items) {
|
|
917
917
|
this.todos?.restore(items);
|
|
918
918
|
}
|
|
919
|
-
/**
|
|
919
|
+
/**
|
|
920
|
+
* `/todo clear` and `/clear`. A user override of a projection (I-2's standing
|
|
921
|
+
* exception). `/todo` refuses mid-run before it gets here; `/clear` does not,
|
|
922
|
+
* on purpose — refusing there would leave it unable to clear the transcript.
|
|
923
|
+
*/
|
|
920
924
|
clearTodos() {
|
|
921
925
|
this.todos?.clear('user');
|
|
922
926
|
}
|
|
@@ -1057,9 +1061,10 @@ export class AgentController {
|
|
|
1057
1061
|
// in this conversation" hint would otherwise start lying (P2-9).
|
|
1058
1062
|
this.skills.getRegistry().clearActive();
|
|
1059
1063
|
// `/reset` clears `messages`, so the belief that justifies the panel is gone
|
|
1060
|
-
// with them and the list must go too (I-2). `/clear`
|
|
1061
|
-
// and
|
|
1062
|
-
//
|
|
1064
|
+
// with them and the list must go too (I-2). `/clear` also drops the list, but
|
|
1065
|
+
// through `clearTodos()` and reason `'user'`, which is the distinction that
|
|
1066
|
+
// remains: this path is a belief that no longer exists, that one is a user
|
|
1067
|
+
// overriding a projection of a belief the model still holds.
|
|
1063
1068
|
this.todos?.clear('reset');
|
|
1064
1069
|
}
|
|
1065
1070
|
/** Read-only view of the live system prompt (regression tests, /debug). */
|
package/dist/agent/reducer.js
CHANGED
|
@@ -692,10 +692,20 @@ export function viewReducer(state, action) {
|
|
|
692
692
|
streamingId: undefined,
|
|
693
693
|
expandedToolIds: {},
|
|
694
694
|
teamEntryId: undefined,
|
|
695
|
-
// The
|
|
696
|
-
//
|
|
697
|
-
// the
|
|
698
|
-
//
|
|
695
|
+
// The list goes with the screen it was on. `/clear` is an explicit user
|
|
696
|
+
// instruction, which is I-2's standing exception (`TodoClearReason`
|
|
697
|
+
// `'user'`, the same door `/todo clear` uses) — so the command clears the
|
|
698
|
+
// STORE via `controller.clearTodos()` and this clears the MIRROR, exactly
|
|
699
|
+
// the pairing `resetConversation` below has always had.
|
|
700
|
+
//
|
|
701
|
+
// The mirror write is same-valued with the store's own `todoCleared`
|
|
702
|
+
// dispatch rather than redundant with it: it is what makes this action
|
|
703
|
+
// stand on its own. `builtins.ts` is the only dispatcher of it TODAY, and
|
|
704
|
+
// a second one — a real clear-screen key, a session-restore path — would
|
|
705
|
+
// otherwise bring the bug back in identical form and in silence.
|
|
706
|
+
//
|
|
707
|
+
// The entry id is dropped because the entry it pointed at is gone.
|
|
708
|
+
todos: null,
|
|
699
709
|
todoEntryId: undefined,
|
|
700
710
|
// Same reasoning for the retry card: the entry is gone, so the id must go
|
|
701
711
|
// with it or `retryScheduled` would `mapEntry` an id that no longer
|
|
@@ -831,8 +831,27 @@ const COMMANDS = [
|
|
|
831
831
|
},
|
|
832
832
|
{
|
|
833
833
|
name: 'clear',
|
|
834
|
-
description: 'Clear the visible transcript',
|
|
835
|
-
run: (ctx) =>
|
|
834
|
+
description: 'Clear the visible transcript and the todo panel',
|
|
835
|
+
run: (ctx) => {
|
|
836
|
+
// A USER OVERRIDE OF A PROJECTION, the same class as `/todo clear`, so it
|
|
837
|
+
// reuses reason `'user'`; I-2 and its standing exception are stated on
|
|
838
|
+
// `TodoStore`. Clearing the STORE and not merely the view mirror is what
|
|
839
|
+
// keeps `/save`, `/todo status|continue` and follow-through agreeing with
|
|
840
|
+
// the screen — they read the store directly. `messages` is untouched:
|
|
841
|
+
// clearing the conversation is still `/reset` alone.
|
|
842
|
+
//
|
|
843
|
+
// DELIBERATELY WITHOUT `/todo clear`'s two guards: an `isRunning()` refusal
|
|
844
|
+
// would leave `/clear` unable to clear even the transcript mid-run, and an
|
|
845
|
+
// early return on an empty list would skip the dispatch below entirely.
|
|
846
|
+
// Clearing an empty store is already silent — it emits into a reducer
|
|
847
|
+
// branch that writes two fields that are both already at their defaults.
|
|
848
|
+
//
|
|
849
|
+
// Bounded side effect: the emptied store re-arms `todo_write`'s "a fresh
|
|
850
|
+
// plan has at least two items" gate, so a model that shrinks a plan to one
|
|
851
|
+
// item right after a mid-run `/clear` gets the refusal text and retries.
|
|
852
|
+
ctx.controller.clearTodos();
|
|
853
|
+
ctx.dispatch({ type: 'clearTranscript' });
|
|
854
|
+
},
|
|
836
855
|
},
|
|
837
856
|
{
|
|
838
857
|
name: 'reset',
|
package/dist/todo/store.d.ts
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* `TeamRuntime`.
|
|
11
11
|
*
|
|
12
12
|
* INVARIANT I-2 — the list is a PROJECTION OF WHAT THE MODEL BELIEVES, and
|
|
13
|
-
* nothing that does not tell the model may change it
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* nothing that does not tell the model may change it — EXCEPT AN EXPLICIT USER
|
|
14
|
+
* INSTRUCTION, which is the rule's standing exception and is marked
|
|
15
|
+
* `TodoClearReason = 'user'`. Two commands come through that door: `/todo clear`
|
|
16
|
+
* and `/clear`. Neither tells the model anything, and that is fine, because what
|
|
17
|
+
* they leave behind is "panel absent, model still remembers" - the direction I-9
|
|
18
|
+
* below calls safe, and one the model's next full-replacement write heals on its
|
|
19
|
+
* own. `/reset` (reason `'reset'`) remains the only clear caused by the belief
|
|
20
|
+
* itself being destroyed, because it clears `messages`.
|
|
21
|
+
*
|
|
22
|
+
* Every write is a FULL REPLACEMENT and every clear is a FULL REMOVAL - there is
|
|
23
|
+
* no partial update anywhere, which is what makes I-9 ("the panel is never a
|
|
24
|
+
* contradiction of the model") provable rather than argued.
|
|
19
25
|
*/
|
|
20
26
|
import { type NormalizeResult } from './normalize.js';
|
|
21
27
|
import type { TodoClearReason, TodoEventListener, TodoSnapshot } from './types.js';
|
package/dist/todo/store.js
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
* `TeamRuntime`.
|
|
11
11
|
*
|
|
12
12
|
* INVARIANT I-2 — the list is a PROJECTION OF WHAT THE MODEL BELIEVES, and
|
|
13
|
-
* nothing that does not tell the model may change it
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* nothing that does not tell the model may change it — EXCEPT AN EXPLICIT USER
|
|
14
|
+
* INSTRUCTION, which is the rule's standing exception and is marked
|
|
15
|
+
* `TodoClearReason = 'user'`. Two commands come through that door: `/todo clear`
|
|
16
|
+
* and `/clear`. Neither tells the model anything, and that is fine, because what
|
|
17
|
+
* they leave behind is "panel absent, model still remembers" - the direction I-9
|
|
18
|
+
* below calls safe, and one the model's next full-replacement write heals on its
|
|
19
|
+
* own. `/reset` (reason `'reset'`) remains the only clear caused by the belief
|
|
20
|
+
* itself being destroyed, because it clears `messages`.
|
|
21
|
+
*
|
|
22
|
+
* Every write is a FULL REPLACEMENT and every clear is a FULL REMOVAL - there is
|
|
23
|
+
* no partial update anywhere, which is what makes I-9 ("the panel is never a
|
|
24
|
+
* contradiction of the model") provable rather than argued.
|
|
19
25
|
*/
|
|
20
26
|
import { getLogger } from '../logging/logger.js';
|
|
21
27
|
import { TODO_LIMITS } from './limits.js';
|
package/dist/todo/types.d.ts
CHANGED
|
@@ -42,7 +42,8 @@ export interface TodoSnapshot {
|
|
|
42
42
|
/**
|
|
43
43
|
* Why a list went away.
|
|
44
44
|
*
|
|
45
|
-
* `user` - `/todo clear`.
|
|
45
|
+
* `user` - `/todo clear`, or `/clear`. Both are an explicit user instruction,
|
|
46
|
+
* which is I-2's standing exception; neither touches `messages`.
|
|
46
47
|
* `reset` - `/reset`, or a `/resume` whose file carried no list (§3.13).
|
|
47
48
|
* `turn` - every item was completed when the next user turn started.
|
|
48
49
|
* `stale` - unfinished, and unwritten for more than `TODO_LIMITS.staleTurns`
|
|
@@ -21,6 +21,22 @@
|
|
|
21
21
|
*
|
|
22
22
|
* What is left is the one thing the status bar cannot say: THAT THE SILENCE IS
|
|
23
23
|
* WORK. The bar shows state; this row shows life.
|
|
24
|
+
*
|
|
25
|
+
* THIS ROW OWNS THE ONLY ANIMATED SPINNER WHILE IT IS MOUNTED
|
|
26
|
+
* (single-spinner-while-running D-1). Seven other sites can animate — the
|
|
27
|
+
* streaming assistant marker, the tool badge, the team/fast/retry cards, and the
|
|
28
|
+
* two panels' rows — and each was independently right to. Emergently they put up
|
|
29
|
+
* to five braille animations on adjacent rows, each on its own 80 ms timer: one
|
|
30
|
+
* piece of information rendered five times and out of phase, which is the same
|
|
31
|
+
* duplication the paragraph above legislates against for text. So `App` widens
|
|
32
|
+
* the `reducedMotion` it hands those seven to `cfg.reducedMotion || (running &&
|
|
33
|
+
* !overlay)`, and they all fall back to the static glyphs they already had.
|
|
34
|
+
*
|
|
35
|
+
* THIS COMPONENT KEEPS THE RAW CONFIG FLAG, and that asymmetry is deliberate
|
|
36
|
+
* (D-5). It reads the flag TWICE: once for the spinner below, and once for
|
|
37
|
+
* `pickActivityPhrase`'s rotation. Handing it the widened value would freeze the
|
|
38
|
+
* phrase at the first word of every run — a second, unrelated regression, and
|
|
39
|
+
* one nobody would connect to a change about icons.
|
|
24
40
|
*/
|
|
25
41
|
import React from 'react';
|
|
26
42
|
import type { Theme } from './theme.js';
|
package/dist/ui/App.js
CHANGED
|
@@ -1422,6 +1422,30 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1422
1422
|
const opener = empty ? (_jsx(SessionOpener, { variant: fullscreen ? pickOpenerVariant(viewportBudget, contentCols, caps) : 'none', version: version, cwd: controller.getCwd(), hasKey: hasKey, viewportRows: fullscreen ? viewportBudget : Number.POSITIVE_INFINITY, theme: theme, caps: caps })) : null;
|
|
1423
1423
|
const density = cfg.density;
|
|
1424
1424
|
const transcriptWindow = cfg.transcriptWindow ?? DEFAULT_TRANSCRIPT_WINDOW;
|
|
1425
|
+
// --- One spinner per run (single-spinner-while-running D-1 .. D-5). ------
|
|
1426
|
+
//
|
|
1427
|
+
// THE MOUNT CONDITION, NAMED ONCE AND USED TWICE (D-4). This const both gates
|
|
1428
|
+
// the `<ActivityLine>` element below and feeds the derivation on the next
|
|
1429
|
+
// line, so "the line is up" and "every other site is still" can never
|
|
1430
|
+
// disagree. Two copies of the same boolean expression is exactly how that
|
|
1431
|
+
// invariant becomes false in six months, silently and in only one branch.
|
|
1432
|
+
//
|
|
1433
|
+
// NOT `running` ALONE (D-3). While an overlay owns the screen the activity row
|
|
1434
|
+
// is suppressed, so keying suppression off `running` would leave the frame
|
|
1435
|
+
// with ZERO life signals: the bar shows state, this row shows life. Never
|
|
1436
|
+
// trade one duplicate for one absence.
|
|
1437
|
+
const activityVisible = running && !overlayNode;
|
|
1438
|
+
// The FOUR VIEW CONSUMERS (transcript x 2, team panel, todo panel) take this;
|
|
1439
|
+
// the activity line keeps the raw config flag (D-5), because it reads that
|
|
1440
|
+
// flag a second time to gate phrase rotation and the widened value would
|
|
1441
|
+
// freeze the phrase at the first word of every run.
|
|
1442
|
+
//
|
|
1443
|
+
// So below `App` the prop now means "this frame does not animate here" rather
|
|
1444
|
+
// than "the user asked for reduced motion". `cfg.reducedMotion` (`:279`) stays
|
|
1445
|
+
// the sole config source, and `spinner-census.test.ts` pins this wiring —
|
|
1446
|
+
// threading the raw flag into a fifth consumer is the one silent way back to
|
|
1447
|
+
// two spinners (R-2).
|
|
1448
|
+
const viewReducedMotion = reducedMotion || activityVisible;
|
|
1425
1449
|
// Assigned during render so `/perf` reads the CURRENT frame (see `perfRef`).
|
|
1426
1450
|
// `mountedEntries` is the same number `render-budget.test.tsx` asserts (AC-9).
|
|
1427
1451
|
perfRef.current = {
|
|
@@ -1440,20 +1464,25 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1440
1464
|
viewportRows: fullscreen ? viewportBudget : rows,
|
|
1441
1465
|
offset: scrolledLines,
|
|
1442
1466
|
};
|
|
1443
|
-
const viewport = fullscreen ? (overlayNode ? (_jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: overlayNode })) : (_jsxs(ScrollViewport, { intent: scrollIntent, pinToBottomNonce: pinToBottomNonce, onScrolledLinesChange: setScrolledLines, showScrollIndicator: true, cols: contentCols, theme: theme, caps: caps, children: [opener, _jsx(TranscriptList, { entries: state.entries, expandedToolIds: state.expandedToolIds, thinkingVisible: state.thinkingVisible, reducedMotion:
|
|
1467
|
+
const viewport = fullscreen ? (overlayNode ? (_jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: overlayNode })) : (_jsxs(ScrollViewport, { intent: scrollIntent, pinToBottomNonce: pinToBottomNonce, onScrolledLinesChange: setScrolledLines, showScrollIndicator: true, cols: contentCols, theme: theme, caps: caps, children: [opener, _jsx(TranscriptList, { entries: state.entries, expandedToolIds: state.expandedToolIds, thinkingVisible: state.thinkingVisible, reducedMotion: viewReducedMotion, density: density, mode: mode, nowSec: nowSec, theme: theme, caps: caps, windowSize: transcriptWindow, cols: contentCols, heights: heights, mountedSink: mountedCount })] }))) : (_jsxs(_Fragment, { children: [opener, _jsx(Transcript, { entries: state.entries, expandedToolIds: state.expandedToolIds, thinkingVisible: state.thinkingVisible, reducedMotion: viewReducedMotion, density: density, mode: mode, nowSec: nowSec, theme: theme, caps: caps }), overlayNode] }));
|
|
1444
1468
|
// The completion popup lives inside the bottom chrome and can add up to 9
|
|
1445
1469
|
// rows; unbounded, it pushes the status bar past the frame height where
|
|
1446
1470
|
// `overflow: hidden` clips it away (R-14 / M-14). Cap it against the viewport.
|
|
1447
1471
|
const popupMaxRows = fullscreen ? Math.max(1, viewportBudget - 4) : undefined;
|
|
1448
1472
|
const composer = fullscreen ? (_jsx(Composer, { isActive: overlay === null, running: state.status === 'running', history: promptHistory, commands: commandOptions, cwd: controller.getCwd(), showHint: rows >= HINT_MIN_ROWS, submitCount: cfg.submitCount, hintsEnabled: cfg.hints, agentMode: state.agentMode, popupMaxRows: popupMaxRows, theme: theme, caps: caps, onSubmit: (text) => void handleSubmit(text), onHelp: () => dispatch({ type: 'setOverlay', overlay: 'help' }) })) : (_jsx(Box, { marginTop: 1, children: _jsx(PromptInput, { isActive: overlay === null, running: state.status === 'running', history: promptHistory, commands: commandOptions, cwd: controller.getCwd(), agentMode: state.agentMode, theme: theme, caps: caps, onSubmit: (text) => void handleSubmit(text), onHelp: () => dispatch({ type: 'setOverlay', overlay: 'help' }) }) }));
|
|
1449
|
-
return (_jsx(AppShell, { mode: mode, rows: rows, cols: cols, header: header, viewport: viewport, team: state.team ? (_jsx(TeamPanel, { snapshot: state.team, rows: rows, cols: cols, reducedMotion:
|
|
1473
|
+
return (_jsx(AppShell, { mode: mode, rows: rows, cols: cols, header: header, viewport: viewport, team: state.team ? (_jsx(TeamPanel, { snapshot: state.team, rows: rows, cols: cols, reducedMotion: viewReducedMotion, theme: theme, caps: caps })) : null, rail: showRail && state.todos ? (_jsx(TodoPanel, { snapshot: state.todos, width: railWidth, rows: railRows, running: running, reducedMotion: viewReducedMotion, theme: theme, caps: caps })) : null, strip: showStrip && state.todos ? (_jsx(TodoStrip, { snapshot: state.todos, cols: cols, theme: theme, caps: caps })) : null, toast: _jsx(BottomStatusRow, { mode: mode, toasts: state.toasts,
|
|
1450
1474
|
// `!overlayNode` suppresses the line while an overlay owns the screen:
|
|
1451
1475
|
// the full-screen branch replaces the whole viewport with the overlay,
|
|
1452
1476
|
// and a working line under a settings screen is noise attached to a
|
|
1453
1477
|
// surface the run is not visible on. (Inline overlays are part of the
|
|
1454
1478
|
// document flow, so there the suppression is conservative rather than
|
|
1455
1479
|
// necessary — P2-5.)
|
|
1456
|
-
|
|
1480
|
+
//
|
|
1481
|
+
// THE CONDITION IS `activityVisible`, THE SAME CONST THE SUPPRESSION
|
|
1482
|
+
// SIGNAL IS DERIVED FROM (D-4): inlining it here again is how the two
|
|
1483
|
+
// drift apart, and the drift is silent in whichever branch is not
|
|
1484
|
+
// edited.
|
|
1485
|
+
activity: activityVisible ? (_jsx(ActivityLine, { startedAt: runStartedAt.current, elapsedMs: elapsedMs, reducedMotion: reducedMotion, runningTool: runningTool, theme: theme, caps: caps })) : null,
|
|
1457
1486
|
// PRESENCE IS DECIDED HERE, NOT BY `UpdateLine` RETURNING `null`
|
|
1458
1487
|
// (C-15 / cli-auto-update P0-1 / D-19). `BottomStatusRow` tests this
|
|
1459
1488
|
// prop for truthiness, and a React element is truthy however it
|
package/dist/ui/Transcript.js
CHANGED
|
@@ -138,6 +138,21 @@ function EntryViewImpl({ entry, prev, expanded, thinkingVisible, reducedMotion,
|
|
|
138
138
|
// While text streams, the spinner IS the role marker. Braille dots are
|
|
139
139
|
// both an animation and a Unicode-only glyph, so reduced motion and an
|
|
140
140
|
// ASCII terminal fall back to the same static marker (P2-11 / A-10).
|
|
141
|
+
//
|
|
142
|
+
// `reducedMotion` ARRIVES PRE-WIDENED FROM `App`: it is now
|
|
143
|
+
// `cfg.reducedMotion || activityVisible`, so this marker is static for the
|
|
144
|
+
// whole of every run and the activity row above the composer carries the
|
|
145
|
+
// one animation (single-spinner-while-running D-2). Nothing here changes —
|
|
146
|
+
// the expression already meant "do not animate here".
|
|
147
|
+
//
|
|
148
|
+
// THAT IS SAFE FOR `<Static>` BY CONSTRUCTION, not by luck: a settled
|
|
149
|
+
// prefix is printed once and can never be re-printed, so a boolean that
|
|
150
|
+
// changed how an already-printed entry renders would tear the history.
|
|
151
|
+
// `computeSettledCount` below breaks on EVERY condition that makes an
|
|
152
|
+
// entry animate (`:84` streaming, `:85` running tool, `:91` active team,
|
|
153
|
+
// `:104` retry, `:109` live fast), so every animating entry is in the live
|
|
154
|
+
// region and every entry in `<Static>` already has `animate === false` on
|
|
155
|
+
// both sides of the flip. Weakening a break clause breaks that proof.
|
|
141
156
|
const animate = entry.streaming && !reducedMotion && caps.unicode;
|
|
142
157
|
return frame(animate ? _jsx(Spinner, { type: "dots" }) : glyphs.assistant, theme.primary, _jsx(AssistantEntry, { text: entry.text, thinking: entry.thinking, thinkingOpen: entry.thinkingOpen, thinkingVisible: thinkingVisible, streaming: entry.streaming, aborted: entry.aborted,
|
|
143
158
|
// `EntryViewImpl` hands each renderer EXPLICIT SCALAR PROPS, so
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* FastCard — one fast-model review in the transcript (fast-model-tier §6).
|
|
4
4
|
*
|
|
@@ -76,7 +76,25 @@ function FastCardImpl(props) {
|
|
|
76
76
|
const duration = durationMs === undefined ? '' : ` ${glyphs.midDot} ${formatDuration(durationMs)}`;
|
|
77
77
|
const head = `fast review #${reviewIndex} ${glyphs.midDot} ${model} ${glyphs.midDot} turn ${turn}${duration}`;
|
|
78
78
|
const showBody = status === 'advice' && text !== undefined && text.trim().length > 0;
|
|
79
|
-
|
|
79
|
+
// COLOUR TRACKS `live`, MOTION TRACKS `animate` — one headline, two separate
|
|
80
|
+
// decisions (single-spinner-while-running C-1 / D-7).
|
|
81
|
+
//
|
|
82
|
+
// These used to be two whole `<Text>` elements, so the still branch also
|
|
83
|
+
// demoted the head to `theme.muted`. That was harmless while stillness meant
|
|
84
|
+
// "the user set reducedMotion", but `App` now widens the prop to "this frame
|
|
85
|
+
// does not animate here" for the whole of every run — and under the old shape
|
|
86
|
+
// that would have greyed out every live fast review for the whole run, losing
|
|
87
|
+
// the status colour along with the motion.
|
|
88
|
+
//
|
|
89
|
+
// Three things are load-bearing and all three are silent if dropped: the
|
|
90
|
+
// leading two-space gutter that separates the head from the bold `fast` label,
|
|
91
|
+
// the single space after the marker (the still form bakes it into its template
|
|
92
|
+
// string), and `head` itself. `wrap="truncate"` now applies to the animated
|
|
93
|
+
// path too, which it did not before: it is what the still branch always did,
|
|
94
|
+
// it is the behaviour this card has in every state that is now common, and an
|
|
95
|
+
// unbounded wrap inside a one-row headline is the worse failure.
|
|
96
|
+
const animate = live && !reducedMotion && caps.unicode;
|
|
97
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: theme.accent, bold: true, children: "fast" }), _jsxs(Text, { color: live ? color : theme.muted, wrap: "truncate", children: [' ', animate ? (_jsxs(_Fragment, { children: [_jsx(Spinner, { type: "dots" }), ' '] })) : live ? (`${glyphs.spinnerStill} `) : (''), head] })] }), showBody ? (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, ...railBorderProps(glyphs.railVertical, theme.border), children: textRows(text).map((line, index) => (_jsx(Text, { color: theme.primary, children: line }, index))) })) : (_jsx(Box, { flexDirection: "column", flexShrink: 0, paddingLeft: 1, children: _jsx(Text, { color: color, wrap: "truncate", children: statusLine(props) }) }))] }));
|
|
80
98
|
}
|
|
81
99
|
/**
|
|
82
100
|
* `React.memo` with the DEFAULT comparator (tui-render-performance L2 / R3).
|
|
@@ -33,7 +33,7 @@ const COMMANDS = [
|
|
|
33
33
|
'Todo planning: status, switches, the rail, follow-through',
|
|
34
34
|
],
|
|
35
35
|
['/tools', 'List active tools'],
|
|
36
|
-
['/clear', 'Clear the visible transcript'],
|
|
36
|
+
['/clear', 'Clear the visible transcript and the todo panel'],
|
|
37
37
|
['/reset', 'New conversation'],
|
|
38
38
|
['/cwd [dir]', 'Show or change the tool working directory'],
|
|
39
39
|
['/save [file]', 'Save the session to JSON'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AragonAgent contributors",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"prepublishOnly": "npm run build"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@aragon-agent/core": "^0.2.
|
|
55
|
+
"@aragon-agent/core": "^0.2.12",
|
|
56
56
|
"chalk": "^5.6.2",
|
|
57
57
|
"cli-highlight": "^2.0.0",
|
|
58
58
|
"commander": "^12.1.0",
|