@aragon-agent/cli 0.6.3 → 0.6.7
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 +142 -0
- package/README.md +203 -13
- package/dist/agent/controller.d.ts +158 -2
- package/dist/agent/controller.js +400 -1
- package/dist/agent/reducer.d.ts +102 -19
- package/dist/agent/reducer.js +162 -71
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/agent/usage.d.ts +34 -0
- package/dist/agent/usage.js +43 -0
- package/dist/cli.js +132 -23
- package/dist/commands/builtins.js +101 -1
- package/dist/compaction/child.js +5 -0
- package/dist/compaction/command.js +14 -1
- package/dist/compaction/compactor.d.ts +94 -24
- package/dist/compaction/compactor.js +247 -98
- package/dist/compaction/context-command.d.ts +33 -0
- package/dist/compaction/context-command.js +166 -0
- package/dist/compaction/meter.d.ts +251 -0
- package/dist/compaction/meter.js +428 -0
- package/dist/compaction/pressure.d.ts +6 -0
- package/dist/compaction/pressure.js +4 -0
- package/dist/compaction/types.d.ts +77 -0
- package/dist/compaction/wiring.d.ts +56 -0
- package/dist/compaction/wiring.js +107 -25
- package/dist/config/cli-commands.js +5 -0
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.js +63 -2
- package/dist/config/load.d.ts +28 -0
- package/dist/config/load.js +83 -1
- package/dist/config/schema.d.ts +129 -0
- package/dist/config/schema.js +69 -0
- package/dist/config/store.js +47 -1
- package/dist/exec/events.d.ts +138 -1
- package/dist/exec/events.js +42 -0
- package/dist/exec/index.js +43 -6
- package/dist/exec/progress-reporter.d.ts +23 -0
- package/dist/exec/progress-reporter.js +88 -0
- package/dist/exec/runner.d.ts +34 -0
- package/dist/exec/runner.js +137 -25
- package/dist/fast/reviewer.d.ts +19 -1
- package/dist/fast/reviewer.js +22 -0
- package/dist/fast/types.d.ts +15 -0
- package/dist/fast/wiring.d.ts +10 -1
- package/dist/fast/wiring.js +18 -1
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/paste-parse.d.ts +71 -0
- package/dist/input/paste-parse.js +95 -0
- package/dist/input/stdin-filter.d.ts +81 -0
- package/dist/input/stdin-filter.js +379 -0
- package/dist/logging/install.d.ts +2 -0
- package/dist/logging/install.js +53 -1
- package/dist/proc/classify.d.ts +52 -0
- package/dist/proc/classify.js +143 -0
- package/dist/proc/kill-tree.d.ts +42 -0
- package/dist/proc/kill-tree.js +119 -0
- package/dist/proc/limits.d.ts +84 -0
- package/dist/proc/limits.js +84 -0
- package/dist/proc/log-ring.d.ts +68 -0
- package/dist/proc/log-ring.js +102 -0
- package/dist/proc/prompt.d.ts +24 -0
- package/dist/proc/prompt.js +49 -0
- package/dist/proc/readiness.d.ts +102 -0
- package/dist/proc/readiness.js +219 -0
- package/dist/proc/supervisor.d.ts +103 -0
- package/dist/proc/supervisor.js +465 -0
- package/dist/proc/types.d.ts +175 -0
- package/dist/proc/types.js +15 -0
- package/dist/session/persist.js +36 -1
- package/dist/todo/limits.d.ts +4 -17
- package/dist/todo/limits.js +4 -17
- package/dist/todo/panel-rows.d.ts +8 -6
- package/dist/todo/panel-rows.js +19 -6
- package/dist/tools/bash-tool.d.ts +51 -2
- package/dist/tools/bash-tool.js +321 -51
- package/dist/tools/fs-tools.d.ts +17 -0
- package/dist/tools/index.d.ts +55 -2
- package/dist/tools/index.js +39 -1
- package/dist/tools/proc-tools.d.ts +25 -0
- package/dist/tools/proc-tools.js +102 -0
- package/dist/ui/App.d.ts +15 -2
- package/dist/ui/App.js +330 -69
- package/dist/ui/AutocompletePopup.d.ts +3 -1
- package/dist/ui/AutocompletePopup.js +9 -11
- package/dist/ui/BottomStatusRow.d.ts +1 -1
- package/dist/ui/Composer.d.ts +56 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +52 -3
- package/dist/ui/PromptInput.js +201 -55
- package/dist/ui/StatusBar.d.ts +40 -15
- package/dist/ui/StatusBar.js +51 -9
- package/dist/ui/TeamPanel.d.ts +3 -1
- package/dist/ui/TeamPanel.js +13 -9
- package/dist/ui/TodoPanel.d.ts +5 -5
- package/dist/ui/TodoPanel.js +56 -24
- package/dist/ui/Transcript.js +29 -1
- package/dist/ui/UpdateLine.d.ts +1 -1
- package/dist/ui/capabilities.js +5 -5
- package/dist/ui/composer-limits.d.ts +31 -0
- package/dist/ui/composer-limits.js +50 -0
- package/dist/ui/composer-rows.d.ts +73 -0
- package/dist/ui/composer-rows.js +201 -0
- package/dist/ui/editor-reducer.d.ts +37 -0
- package/dist/ui/editor-reducer.js +88 -12
- package/dist/ui/entries/ServiceCard.d.ts +66 -0
- package/dist/ui/entries/ServiceCard.js +116 -0
- package/dist/ui/entries/UserEntry.d.ts +17 -3
- package/dist/ui/entries/UserEntry.js +34 -5
- package/dist/ui/exit-snapshot.d.ts +9 -0
- package/dist/ui/layout/ScrollViewport.d.ts +1 -1
- package/dist/ui/layout/autocomplete.d.ts +18 -0
- package/dist/ui/layout/autocomplete.js +26 -0
- package/dist/ui/layout/budget.d.ts +34 -6
- package/dist/ui/layout/budget.js +32 -7
- package/dist/ui/layout/rail.d.ts +4 -53
- package/dist/ui/layout/rail.js +10 -62
- package/dist/ui/layout/team-panel.d.ts +18 -0
- package/dist/ui/layout/team-panel.js +22 -0
- package/dist/ui/layout/todo-layout.d.ts +24 -0
- package/dist/ui/layout/todo-layout.js +27 -0
- package/dist/ui/layout/virtual-window.js +51 -1
- package/dist/ui/overlays/HelpOverlay.js +3 -0
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.js +14 -1
- package/dist/ui/paste-frames.d.ts +87 -0
- package/dist/ui/paste-frames.js +112 -0
- package/dist/ui/paste-tokens.d.ts +90 -0
- package/dist/ui/paste-tokens.js +147 -0
- package/dist/ui/screen.d.ts +16 -0
- package/dist/ui/screen.js +45 -2
- package/dist/ui/selection/selection-controller.d.ts +1 -1
- package/dist/ui/transcript-text.js +55 -2
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/package.json +2 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -47
- package/dist/input/stdin-mouse-filter.js +0 -153
package/dist/agent/reducer.d.ts
CHANGED
|
@@ -14,8 +14,9 @@ import type { AgentEvent, ModelCost, ToolResult, TokenUsage } from '@aragon-agen
|
|
|
14
14
|
import type { AgentMode } from './agent-mode.js';
|
|
15
15
|
import type { DispatchOutcome, SubagentRun, SubagentSpec, TeamSnapshot } from '../team/types.js';
|
|
16
16
|
import type { FastReview, FastSnapshot } from '../fast/types.js';
|
|
17
|
-
import type { CompactionMode, CompactionRecord, CompactionSnapshot, CompactionUiTrigger } from '../compaction/types.js';
|
|
17
|
+
import type { CompactionMode, CompactionRecord, CompactionSnapshot, CompactionUiTrigger, ContextUsageSnapshot } from '../compaction/types.js';
|
|
18
18
|
import type { TodoItem, TodoSnapshot } from '../todo/types.js';
|
|
19
|
+
import type { ServiceSnapshot, ServiceStatus } from '../proc/types.js';
|
|
19
20
|
import type { RetryPhase } from './retry-view.js';
|
|
20
21
|
import type { FilePatch } from '../tools/patch.js';
|
|
21
22
|
export type NoticeLevel = 'info' | 'warn' | 'error';
|
|
@@ -274,10 +275,70 @@ export type Entry = {
|
|
|
274
275
|
charsRemoved: number;
|
|
275
276
|
};
|
|
276
277
|
live: boolean;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* One supervised background service (background-service-supervision §3.10).
|
|
281
|
+
*
|
|
282
|
+
* JSON-SERIALIZABLE by construction, so `/save` needs no format change.
|
|
283
|
+
*
|
|
284
|
+
* IT IS THE ONE LIVE CARD THAT DOES **NOT** BLOCK THE SETTLED BOUNDARY ONCE IT
|
|
285
|
+
* IS UP (D-11 / P0-4). Every other live clause in
|
|
286
|
+
* `Transcript.tsx::computeSettledCount` is bounded by an OPERATION — a
|
|
287
|
+
* dispatch, a turn, one LLM call, one countdown — and each carries a comment
|
|
288
|
+
* saying that an entry which never settles is re-rendered on every frame for
|
|
289
|
+
* the rest of the session. A service is bounded by the USER'S INTENT: a dev
|
|
290
|
+
* server left up for an hour is the normal case. So only `starting` blocks
|
|
291
|
+
* (bounded by `readyTimeoutMs`), a `ready`/`running` card commits its body to
|
|
292
|
+
* `<Static>`, and a later terminal transition appends a NEW one-row entry
|
|
293
|
+
* rather than trying to rewrite a card `<Static>` has already printed.
|
|
294
|
+
*
|
|
295
|
+
* `terminal: true` marks that second entry. `session/persist.ts` rewrites any
|
|
296
|
+
* restored non-terminal card to `stopped`, next to `team.active`, `todo.live`,
|
|
297
|
+
* the retry phase, `fast.live` and `compaction.live`, and for the identical
|
|
298
|
+
* reason.
|
|
299
|
+
*/
|
|
300
|
+
| {
|
|
301
|
+
id: string;
|
|
302
|
+
kind: 'service';
|
|
303
|
+
/** `s1`, `s2`, ... — the id the model quotes back. */
|
|
304
|
+
serviceId: string;
|
|
305
|
+
command: string;
|
|
306
|
+
status: ServiceStatus;
|
|
307
|
+
url?: string;
|
|
308
|
+
port?: number;
|
|
309
|
+
exitCode: number | null;
|
|
310
|
+
startedAt: number;
|
|
311
|
+
readyAt?: number;
|
|
312
|
+
endedAt?: number;
|
|
313
|
+
rows: readonly string[];
|
|
314
|
+
/**
|
|
315
|
+
* MONOTONIC row cursor — the revision term, NEVER `rows.length` (P0-3).
|
|
316
|
+
*
|
|
317
|
+
* The tail is a FIXED-SIZE RING: it evicts its oldest row while appending
|
|
318
|
+
* a new one, so the joined length is unchanged while the content is not.
|
|
319
|
+
* That is verbatim the non-append mutation the `tool` branch's `liveSeq`
|
|
320
|
+
* comment was written for, and a length term would go on matching while
|
|
321
|
+
* the card changed underneath it.
|
|
322
|
+
*/
|
|
323
|
+
rowsSeen: number;
|
|
324
|
+
/** The one-row entry a terminal transition appends (D-11). */
|
|
325
|
+
terminal?: boolean;
|
|
326
|
+
/** The stop reported the pid still alive afterwards (R-2 / P2-6). */
|
|
327
|
+
killIncomplete?: boolean;
|
|
277
328
|
};
|
|
278
329
|
export interface UsageTotal {
|
|
279
330
|
inputTokens: number;
|
|
280
331
|
outputTokens: number;
|
|
332
|
+
/**
|
|
333
|
+
* Cache read hits (context-usage-gauge-accuracy §3.6 / P1-3).
|
|
334
|
+
*
|
|
335
|
+
* `occupiedTokens` counts them and `computeCost` PRICES them, so a session
|
|
336
|
+
* total that omits them makes the status bar's `^` read lower than the `$`
|
|
337
|
+
* next to it - on the same row, from the same turns. That was three units on
|
|
338
|
+
* one line, and this field plus its sibling are what collapse them to one.
|
|
339
|
+
*/
|
|
340
|
+
cacheReadTokens: number;
|
|
341
|
+
cacheWriteTokens: number;
|
|
281
342
|
costUsd: number;
|
|
282
343
|
}
|
|
283
344
|
/**
|
|
@@ -298,8 +359,20 @@ export interface ViewState {
|
|
|
298
359
|
entries: Entry[];
|
|
299
360
|
status: 'idle' | 'running';
|
|
300
361
|
usageTotal: UsageTotal;
|
|
301
|
-
/**
|
|
302
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Context occupancy, as published by `AgentController`'s `ContextMeter`.
|
|
364
|
+
*
|
|
365
|
+
* ITS ONLY WRITER IS `case 'contextUsage'` (I-1), and that is the structural
|
|
366
|
+
* half of the P0-1 fix. It replaced `contextTokens` + `contextTokensEstimated`,
|
|
367
|
+
* which between them had THREE writing branches (`turnEnd`,
|
|
368
|
+
* `contextTokensEstimated`, `resetConversation`) fed by two competing
|
|
369
|
+
* upstreams. Two writers in one synchronous fan-out means the later one wins,
|
|
370
|
+
* and the later one was the stale `snapshot` carrying the PRE-compaction
|
|
371
|
+
* figure - so the bar fell and bounced straight back, every time, with nothing
|
|
372
|
+
* logging a fault. `context-one-writer.test.ts` scans this file to keep it
|
|
373
|
+
* that way.
|
|
374
|
+
*/
|
|
375
|
+
context: ContextUsageSnapshot;
|
|
303
376
|
overlay: Overlay;
|
|
304
377
|
thinkingVisible: boolean;
|
|
305
378
|
/** Ephemeral acks; auto-dismissed by App (spec §3.9). */
|
|
@@ -357,16 +430,6 @@ export interface ViewState {
|
|
|
357
430
|
* totals. `null` is what leaves an ordinary session's status bar unchanged.
|
|
358
431
|
*/
|
|
359
432
|
compaction: CompactionSnapshot | null;
|
|
360
|
-
/**
|
|
361
|
-
* Whether `contextTokens` is currently a DERIVED figure rather than a measured
|
|
362
|
-
* one (§6.2 / P1-11).
|
|
363
|
-
*
|
|
364
|
-
* Set by `compactionEnd`, cleared by the next `turnEnd`. The status bar widens
|
|
365
|
-
* its leading `~` to cover it: today `~` means "the window is a guess", and
|
|
366
|
-
* this makes it also mean "the occupancy is a guess". Honesty about a guess is
|
|
367
|
-
* cheaper than a support thread about a wrong percentage.
|
|
368
|
-
*/
|
|
369
|
-
contextTokensEstimated: boolean;
|
|
370
433
|
seq: number;
|
|
371
434
|
/** The `kind: 'team'` entry the live dispatch is writing into. */
|
|
372
435
|
teamEntryId?: string;
|
|
@@ -597,15 +660,35 @@ export type ViewAction = {
|
|
|
597
660
|
snapshot: CompactionSnapshot;
|
|
598
661
|
}
|
|
599
662
|
/**
|
|
600
|
-
*
|
|
663
|
+
* The occupancy reading, from `AgentController.subscribeContextUsage`.
|
|
664
|
+
*
|
|
665
|
+
* THE ONLY ACTION THAT WRITES `state.context`, AND IT IS DISPATCHED FROM
|
|
666
|
+
* EXACTLY ONE PLACE (I-1 / T11). It replaced `contextTokensEstimated`, which
|
|
667
|
+
* `App` fired from two branches of the compaction stream while `turnEnd` wrote
|
|
668
|
+
* the same field from the agent stream - three writers, two upstreams, one
|
|
669
|
+
* number, and the P0-1 bounce as the result.
|
|
670
|
+
*/
|
|
671
|
+
| {
|
|
672
|
+
type: 'contextUsage';
|
|
673
|
+
snapshot: ContextUsageSnapshot;
|
|
674
|
+
} | {
|
|
675
|
+
type: 'serviceStart';
|
|
676
|
+
service: ServiceSnapshot;
|
|
677
|
+
} | {
|
|
678
|
+
type: 'serviceUpdate';
|
|
679
|
+
service: ServiceSnapshot;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* A service reached a terminal state.
|
|
601
683
|
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
684
|
+
* IT APPENDS A NEW ONE-ROW ENTRY rather than rewriting the card (D-11). The
|
|
685
|
+
* card may already have been printed into `<Static>`, and `<Static>` cannot
|
|
686
|
+
* un-print — so any design that rewrites a printed card is wrong whatever it
|
|
687
|
+
* claims. A second event at a second time is also simply more honest.
|
|
605
688
|
*/
|
|
606
689
|
| {
|
|
607
|
-
type: '
|
|
608
|
-
|
|
690
|
+
type: 'serviceEnd';
|
|
691
|
+
service: ServiceSnapshot;
|
|
609
692
|
};
|
|
610
693
|
export declare const STORED_PREVIEW_CHARS = 8000;
|
|
611
694
|
export declare const STORED_PREVIEW_LINES = 200;
|
package/dist/agent/reducer.js
CHANGED
|
@@ -11,11 +11,25 @@
|
|
|
11
11
|
* synthesizes a generic error notice (a swallowed throw is never a no-op).
|
|
12
12
|
*/
|
|
13
13
|
import process from 'node:process';
|
|
14
|
-
import {
|
|
15
|
-
import { computeCost } from './usage.js';
|
|
14
|
+
import { emptyContextUsage } from '../compaction/meter.js';
|
|
15
|
+
import { addUsage, computeCost } from './usage.js';
|
|
16
16
|
import { ENTRY_LIMITS, appendBounded, entryRetain, trimEntries } from './entry-limits.js';
|
|
17
17
|
/** Default auto-dismiss window for a toast. */
|
|
18
18
|
export const DEFAULT_TOAST_TTL_MS = 2500;
|
|
19
|
+
/**
|
|
20
|
+
* The zero session total.
|
|
21
|
+
*
|
|
22
|
+
* ONE LITERAL FOR TWO SITES (`initialViewState` and `resetConversation`). They
|
|
23
|
+
* used to spell the object out twice, which is how `cacheReadTokens` would have
|
|
24
|
+
* been added to one of them and not the other.
|
|
25
|
+
*/
|
|
26
|
+
const EMPTY_USAGE_TOTAL = {
|
|
27
|
+
inputTokens: 0,
|
|
28
|
+
outputTokens: 0,
|
|
29
|
+
cacheReadTokens: 0,
|
|
30
|
+
cacheWriteTokens: 0,
|
|
31
|
+
costUsd: 0,
|
|
32
|
+
};
|
|
19
33
|
/**
|
|
20
34
|
* The initial view state, optionally seeded from config.
|
|
21
35
|
*
|
|
@@ -33,8 +47,8 @@ export function initialViewState(seed = {}) {
|
|
|
33
47
|
return {
|
|
34
48
|
entries: [],
|
|
35
49
|
status: 'idle',
|
|
36
|
-
usageTotal:
|
|
37
|
-
|
|
50
|
+
usageTotal: EMPTY_USAGE_TOTAL,
|
|
51
|
+
context: emptyContextUsage(),
|
|
38
52
|
overlay: null,
|
|
39
53
|
thinkingVisible: seed.thinkingVisible ?? false,
|
|
40
54
|
toasts: [],
|
|
@@ -48,7 +62,6 @@ export function initialViewState(seed = {}) {
|
|
|
48
62
|
retry: null,
|
|
49
63
|
fast: null,
|
|
50
64
|
compaction: null,
|
|
51
|
-
contextTokensEstimated: false,
|
|
52
65
|
seq: 0,
|
|
53
66
|
teamEntryId: undefined,
|
|
54
67
|
todoEntryId: undefined,
|
|
@@ -610,22 +623,12 @@ export function viewReducer(state, action) {
|
|
|
610
623
|
...state,
|
|
611
624
|
streamingId: undefined,
|
|
612
625
|
turnProduced: true,
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
|
|
619
|
-
// number from the one the user is watching.
|
|
620
|
-
contextTokens: occupiedTokens(action.usage),
|
|
621
|
-
// A MEASUREMENT SUPERSEDES A DERIVATION (§6.2). `compactionEnd` writes an
|
|
622
|
-
// estimate so the bar falls immediately; this is where it stops being one.
|
|
623
|
-
contextTokensEstimated: false,
|
|
624
|
-
usageTotal: {
|
|
625
|
-
inputTokens: state.usageTotal.inputTokens + action.usage.inputTokens,
|
|
626
|
-
outputTokens: state.usageTotal.outputTokens + action.usage.outputTokens,
|
|
627
|
-
costUsd: state.usageTotal.costUsd + action.costDelta,
|
|
628
|
-
},
|
|
626
|
+
// `contextTokens` IS NOT WRITTEN HERE ANY MORE (I-1). The occupancy this
|
|
627
|
+
// turn produced reaches the view through `ContextMeter`, which subscribes
|
|
628
|
+
// to the same `turn_end` and publishes the measured pressure - window,
|
|
629
|
+
// percentage and approximation markers included, none of which this
|
|
630
|
+
// branch ever had. Two branches writing one number is what P0-1 was.
|
|
631
|
+
usageTotal: addUsage(state.usageTotal, action.usage, action.costDelta),
|
|
629
632
|
// The turn produced an answer, so whatever it had to retry, it recovered.
|
|
630
633
|
...settleRetryCard(state, entries, 'recovered'),
|
|
631
634
|
};
|
|
@@ -763,8 +766,13 @@ export function viewReducer(state, action) {
|
|
|
763
766
|
droppedEntries: 0,
|
|
764
767
|
streamingId: undefined,
|
|
765
768
|
expandedToolIds: {},
|
|
766
|
-
|
|
767
|
-
|
|
769
|
+
// `context` IS DELIBERATELY UNTOUCHED (I-1). `builtins.ts` calls
|
|
770
|
+
// `controller.clearMessages()` BEFORE dispatching this, and that marks
|
|
771
|
+
// the meter dirty and schedules a publication; zeroing here would make
|
|
772
|
+
// the final state `0` rather than the meter's real answer - which for an
|
|
773
|
+
// emptied conversation is the system prompt's own couple of percent, not
|
|
774
|
+
// nothing. `usageTotal` DOES reset: that is session spend, not occupancy.
|
|
775
|
+
usageTotal: EMPTY_USAGE_TOTAL,
|
|
768
776
|
teamEntryId: undefined,
|
|
769
777
|
// `/reset` clears `messages`, so the belief is gone and the list goes
|
|
770
778
|
// with it — the other half of I-2. `controller.clearMessages()` clears
|
|
@@ -774,10 +782,6 @@ export function viewReducer(state, action) {
|
|
|
774
782
|
retryEntryId: undefined,
|
|
775
783
|
retry: null,
|
|
776
784
|
compactionEntryId: undefined,
|
|
777
|
-
// `contextTokens` is zeroed above, so the flag that says it is a
|
|
778
|
-
// DERIVATION has to go with it — otherwise a fresh conversation renders
|
|
779
|
-
// `~0%` and the tilde is a lie about a number that is simply zero.
|
|
780
|
-
contextTokensEstimated: false,
|
|
781
785
|
};
|
|
782
786
|
case 'setOverlay':
|
|
783
787
|
return { ...state, overlay: action.overlay };
|
|
@@ -860,18 +864,11 @@ export function viewReducer(state, action) {
|
|
|
860
864
|
}
|
|
861
865
|
case 'teamUsage':
|
|
862
866
|
// `usageTotal` AND NOTHING ELSE (§3.9 / AC-15). In particular NOT
|
|
863
|
-
// `
|
|
867
|
+
// `context`: that gauge shows the LEAD's context occupancy against
|
|
864
868
|
// the model's window, and folding five children into it would read 180%
|
|
865
869
|
// on a perfectly healthy session. Child spend is real money and has to
|
|
866
870
|
// appear in the cost readout; it is not the lead's context.
|
|
867
|
-
return {
|
|
868
|
-
...state,
|
|
869
|
-
usageTotal: {
|
|
870
|
-
inputTokens: state.usageTotal.inputTokens + action.usage.inputTokens,
|
|
871
|
-
outputTokens: state.usageTotal.outputTokens + action.usage.outputTokens,
|
|
872
|
-
costUsd: state.usageTotal.costUsd + action.costDelta,
|
|
873
|
-
},
|
|
874
|
-
};
|
|
871
|
+
return { ...state, usageTotal: addUsage(state.usageTotal, action.usage, action.costDelta) };
|
|
875
872
|
case 'teamEnd': {
|
|
876
873
|
const { outcome } = action;
|
|
877
874
|
const entries = state.teamEntryId
|
|
@@ -991,20 +988,19 @@ export function viewReducer(state, action) {
|
|
|
991
988
|
}
|
|
992
989
|
case 'fastUsage':
|
|
993
990
|
// `usageTotal` AND NOTHING ELSE, exactly as `teamUsage` (§3.6 / AC-18). In
|
|
994
|
-
// particular NOT `
|
|
991
|
+
// particular NOT `context`: that gauge shows the LEAD's context
|
|
995
992
|
// occupancy against the lead model's window, and a review is a separate
|
|
996
993
|
// conversation with a different model entirely.
|
|
997
|
-
return {
|
|
998
|
-
...state,
|
|
999
|
-
usageTotal: {
|
|
1000
|
-
inputTokens: state.usageTotal.inputTokens + action.usage.inputTokens,
|
|
1001
|
-
outputTokens: state.usageTotal.outputTokens + action.usage.outputTokens,
|
|
1002
|
-
costUsd: state.usageTotal.costUsd + action.costDelta,
|
|
1003
|
-
},
|
|
1004
|
-
};
|
|
994
|
+
return { ...state, usageTotal: addUsage(state.usageTotal, action.usage, action.costDelta) };
|
|
1005
995
|
case 'fastTier':
|
|
1006
996
|
return { ...state, fast: action.snapshot };
|
|
1007
997
|
// --- Context compaction (context-auto-compaction §5.5) ----------------
|
|
998
|
+
// NOT A TOTAL FUNCTION OVER COMPACTION ATTEMPTS (quiet-noop §3.1). A
|
|
999
|
+
// `pressure` compaction that declines before calling a model or changing the
|
|
1000
|
+
// history dispatches NOTHING - no `compactionStart`, no `compactionEnd`, no
|
|
1001
|
+
// entry - exactly as a checkpoint below the threshold dispatches nothing.
|
|
1002
|
+
// Counting cards here counts compactions that RAN, never checkpoints that
|
|
1003
|
+
// fired; `/compact status` and the diagnostic log are where the attempts are.
|
|
1008
1004
|
case 'compactionStart': {
|
|
1009
1005
|
const { id, seq } = nextId(state);
|
|
1010
1006
|
let entries = state.entries;
|
|
@@ -1122,40 +1118,41 @@ export function viewReducer(state, action) {
|
|
|
1122
1118
|
}
|
|
1123
1119
|
case 'compactionUsage':
|
|
1124
1120
|
// `usageTotal` AND NOTHING ELSE, exactly as `teamUsage` and `fastUsage`. In
|
|
1125
|
-
// particular NOT `
|
|
1121
|
+
// particular NOT `context`: that gauge shows the LEAD's context
|
|
1126
1122
|
// occupancy against the lead model's window, and a summarization is a
|
|
1127
1123
|
// separate conversation with a possibly different model entirely.
|
|
1128
|
-
return {
|
|
1129
|
-
...state,
|
|
1130
|
-
usageTotal: {
|
|
1131
|
-
inputTokens: state.usageTotal.inputTokens + action.usage.inputTokens,
|
|
1132
|
-
outputTokens: state.usageTotal.outputTokens + action.usage.outputTokens,
|
|
1133
|
-
costUsd: state.usageTotal.costUsd + action.costDelta,
|
|
1134
|
-
},
|
|
1135
|
-
};
|
|
1124
|
+
return { ...state, usageTotal: addUsage(state.usageTotal, action.usage, action.costDelta) };
|
|
1136
1125
|
case 'compactionSnapshot':
|
|
1137
1126
|
return { ...state, compaction: action.snapshot };
|
|
1138
1127
|
/**
|
|
1139
|
-
* THE
|
|
1128
|
+
* THE ONE WRITER OF `state.context` (I-1).
|
|
1140
1129
|
*
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
* the
|
|
1130
|
+
* THE IDENTITY SHORT-CIRCUIT BELOW IS LOAD-BEARING, NOT A MICRO-OPTIMISATION
|
|
1131
|
+
* (§5.2 / R-3). The meter publishes on a 400 ms tick throughout a turn and
|
|
1132
|
+
* most of those ticks measure the same number - the history did not change
|
|
1133
|
+
* between two tool calls of the same size, or the percentage rounded to the
|
|
1134
|
+
* same integer. Returning the SAME state object lets React skip the whole
|
|
1135
|
+
* subtree; returning a fresh one would make a permanently visible row into a
|
|
1136
|
+
* 2.5 Hz re-render source and walk the render governor up its ladder for no
|
|
1137
|
+
* visible change at all.
|
|
1146
1138
|
*
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
* never counts them, so writing the raw figure would make the bar fall too
|
|
1150
|
-
* far and then visibly climb again on the next `turn_end` for no reason the
|
|
1151
|
-
* user can see.
|
|
1139
|
+
* `ContextUsageSnapshot` carries no timestamp for exactly this reason: a
|
|
1140
|
+
* field that moves every tick would make this comparison never hold.
|
|
1152
1141
|
*/
|
|
1153
|
-
case '
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1142
|
+
case 'contextUsage': {
|
|
1143
|
+
const prev = state.context;
|
|
1144
|
+
const next = action.snapshot;
|
|
1145
|
+
if (prev.occupied === next.occupied &&
|
|
1146
|
+
prev.window === next.window &&
|
|
1147
|
+
prev.pct === next.pct &&
|
|
1148
|
+
prev.source === next.source &&
|
|
1149
|
+
prev.deltaTokens === next.deltaTokens &&
|
|
1150
|
+
prev.windowKnown === next.windowKnown &&
|
|
1151
|
+
prev.windowOverridden === next.windowOverridden) {
|
|
1152
|
+
return state;
|
|
1153
|
+
}
|
|
1154
|
+
return { ...state, context: next };
|
|
1155
|
+
}
|
|
1159
1156
|
// --- API retry (llm-api-retry-backoff §6.4) ---------------------------
|
|
1160
1157
|
case 'retryScheduled': {
|
|
1161
1158
|
const snapshot = {
|
|
@@ -1317,10 +1314,104 @@ export function viewReducer(state, action) {
|
|
|
1317
1314
|
fastEntryId: undefined,
|
|
1318
1315
|
};
|
|
1319
1316
|
}
|
|
1317
|
+
// --- Background services (§3.10 / D-11) -----------------------------
|
|
1318
|
+
case 'serviceStart': {
|
|
1319
|
+
const { id, seq } = nextId(state);
|
|
1320
|
+
const entry = { id, ...serviceFields(action.service) };
|
|
1321
|
+
const appended = appendEntry(state, state.entries, entry);
|
|
1322
|
+
return {
|
|
1323
|
+
...state,
|
|
1324
|
+
seq,
|
|
1325
|
+
entries: appended.entries,
|
|
1326
|
+
droppedEntries: appended.droppedEntries,
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Rewrite the LIVE card in place.
|
|
1331
|
+
*
|
|
1332
|
+
* SAFE ONLY BECAUSE OF D-11'S OTHER HALF: a `ready` / `running` card has
|
|
1333
|
+
* reached `<Static>` and cannot be re-printed, so `serviceEnd` appends a new
|
|
1334
|
+
* entry instead of routing through here. What this handles is the window in
|
|
1335
|
+
* which the card is still in the live region — `starting`, and the one
|
|
1336
|
+
* transition out of it — plus the output tail while it is there.
|
|
1337
|
+
*
|
|
1338
|
+
* The lookup is by `serviceId` and SKIPS terminal entries, so a stray update
|
|
1339
|
+
* cannot rewrite the one-row record `serviceEnd` appended.
|
|
1340
|
+
*/
|
|
1341
|
+
case 'serviceUpdate': {
|
|
1342
|
+
const target = findServiceEntryId(state, action.service.id);
|
|
1343
|
+
if (!target)
|
|
1344
|
+
return state;
|
|
1345
|
+
return {
|
|
1346
|
+
...state,
|
|
1347
|
+
entries: mapEntry(state.entries, target, (e) => e.kind === 'service' ? { ...e, ...serviceFields(action.service) } : e),
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* A service reached a terminal state.
|
|
1352
|
+
*
|
|
1353
|
+
* TWO WRITES, AND BOTH ARE REQUIRED. The live card is settled in place so
|
|
1354
|
+
* `computeSettledCount` can move past it and `estimateEntryRows` stops
|
|
1355
|
+
* charging for a tail nothing will add to; the NEW one-row entry is what the
|
|
1356
|
+
* user actually reads, because the card may already be printed into
|
|
1357
|
+
* `<Static>` and `<Static>` cannot un-print (D-11 / R-13).
|
|
1358
|
+
*/
|
|
1359
|
+
case 'serviceEnd': {
|
|
1360
|
+
const target = findServiceEntryId(state, action.service.id);
|
|
1361
|
+
const settled = target
|
|
1362
|
+
? mapEntry(state.entries, target, (e) => e.kind === 'service' ? { ...e, ...serviceFields(action.service) } : e)
|
|
1363
|
+
: state.entries;
|
|
1364
|
+
const { id, seq } = nextId(state);
|
|
1365
|
+
const appended = appendEntry(state, settled, {
|
|
1366
|
+
id,
|
|
1367
|
+
...serviceFields(action.service),
|
|
1368
|
+
terminal: true,
|
|
1369
|
+
});
|
|
1370
|
+
return {
|
|
1371
|
+
...state,
|
|
1372
|
+
seq,
|
|
1373
|
+
entries: appended.entries,
|
|
1374
|
+
droppedEntries: appended.droppedEntries,
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1320
1377
|
default:
|
|
1321
1378
|
return state;
|
|
1322
1379
|
}
|
|
1323
1380
|
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Project a supervisor snapshot onto the entry's own fields.
|
|
1383
|
+
*
|
|
1384
|
+
* ONE PROJECTION FOR ALL THREE ACTIONS, so a field that reaches the card through
|
|
1385
|
+
* `serviceStart` can never fail to reach it through `serviceUpdate` — the kind of
|
|
1386
|
+
* asymmetry that produces a card frozen at its first values with nothing
|
|
1387
|
+
* reporting it.
|
|
1388
|
+
*/
|
|
1389
|
+
function serviceFields(service) {
|
|
1390
|
+
return {
|
|
1391
|
+
kind: 'service',
|
|
1392
|
+
serviceId: service.id,
|
|
1393
|
+
command: service.command,
|
|
1394
|
+
status: service.status,
|
|
1395
|
+
...(service.url !== undefined ? { url: service.url } : {}),
|
|
1396
|
+
...(service.port !== undefined ? { port: service.port } : {}),
|
|
1397
|
+
exitCode: service.exitCode,
|
|
1398
|
+
startedAt: service.startedAt,
|
|
1399
|
+
...(service.readyAt !== undefined ? { readyAt: service.readyAt } : {}),
|
|
1400
|
+
...(service.endedAt !== undefined ? { endedAt: service.endedAt } : {}),
|
|
1401
|
+
rows: service.rows,
|
|
1402
|
+
rowsSeen: service.rowsSeen,
|
|
1403
|
+
...(service.killIncomplete ? { killIncomplete: true } : {}),
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
/** The LIVE card for a service id, newest first; terminal records are skipped. */
|
|
1407
|
+
function findServiceEntryId(state, serviceId) {
|
|
1408
|
+
for (let i = state.entries.length - 1; i >= 0; i -= 1) {
|
|
1409
|
+
const e = state.entries[i];
|
|
1410
|
+
if (e && e.kind === 'service' && e.serviceId === serviceId && !e.terminal)
|
|
1411
|
+
return e.id;
|
|
1412
|
+
}
|
|
1413
|
+
return undefined;
|
|
1414
|
+
}
|
|
1324
1415
|
function safeStringify(value) {
|
|
1325
1416
|
try {
|
|
1326
1417
|
return JSON.stringify(value);
|
|
@@ -88,6 +88,20 @@ export interface SystemPromptParams {
|
|
|
88
88
|
* for a fixed `tools` array. Every existing caller passes nothing.
|
|
89
89
|
*/
|
|
90
90
|
appendSystemPrompt?: string;
|
|
91
|
+
/**
|
|
92
|
+
* The rendered `<background_services>` block, or `''`
|
|
93
|
+
* (background-service-supervision §5.2).
|
|
94
|
+
*
|
|
95
|
+
* Spliced conditionally exactly as `skillsBlock`, the plan block, `teamBlock`,
|
|
96
|
+
* `todoBlock` and `fastBlock` are, which is what keeps I-2 true:
|
|
97
|
+
* `bash.background: false` produces the pre-feature prompt BYTE FOR BYTE for a
|
|
98
|
+
* fixed tool array.
|
|
99
|
+
*
|
|
100
|
+
* A CHILD'S PROMPT NEVER CARRIES IT: subagents build tools from the same
|
|
101
|
+
* factory but are given no supervisor, so telling one about `bash_output` /
|
|
102
|
+
* `bash_kill` would describe tools it does not have.
|
|
103
|
+
*/
|
|
104
|
+
backgroundBlock?: string;
|
|
91
105
|
}
|
|
92
106
|
/** Describe the active shell so the model does not emit incompatible syntax. */
|
|
93
107
|
export declare function describeShell(): {
|
|
@@ -41,6 +41,7 @@ export function buildSystemPrompt(params) {
|
|
|
41
41
|
const subagentBlock = params.subagentBlock ?? '';
|
|
42
42
|
const todoBlock = params.todoBlock ?? '';
|
|
43
43
|
const fastBlock = params.fastBlock ?? '';
|
|
44
|
+
const backgroundBlock = params.backgroundBlock ?? '';
|
|
44
45
|
const appended = (params.appendSystemPrompt ?? '').trim();
|
|
45
46
|
return [
|
|
46
47
|
'You are AragonAgent, an autonomous coding assistant operating inside a terminal (TUI).',
|
|
@@ -59,6 +60,7 @@ export function buildSystemPrompt(params) {
|
|
|
59
60
|
...(subagentBlock ? ['', subagentBlock] : []),
|
|
60
61
|
...(todoBlock ? ['', todoBlock] : []),
|
|
61
62
|
...(fastBlock ? ['', fastBlock] : []),
|
|
63
|
+
...(backgroundBlock ? ['', backgroundBlock] : []),
|
|
62
64
|
'',
|
|
63
65
|
'Operating guidance:',
|
|
64
66
|
'- Prefer reading files and inspecting the workspace before making changes.',
|
package/dist/agent/usage.d.ts
CHANGED
|
@@ -3,8 +3,42 @@
|
|
|
3
3
|
* (which is expressed in USD per 1M tokens). See spec §6.2 status bar.
|
|
4
4
|
*/
|
|
5
5
|
import type { ModelCost, TokenUsage } from '@aragon-agent/core';
|
|
6
|
+
import type { UsageTotal } from './reducer.js';
|
|
6
7
|
/** Compute the USD cost of a single `TokenUsage` given a model's cost table. */
|
|
7
8
|
export declare function computeCost(usage: TokenUsage, cost?: ModelCost): number;
|
|
9
|
+
/**
|
|
10
|
+
* Fold one `TokenUsage` into a running session total
|
|
11
|
+
* (context-usage-gauge-accuracy §3.6 / W5).
|
|
12
|
+
*
|
|
13
|
+
* THE ONE ADDITION SITE, and collapsing four hand-written copies into it is the
|
|
14
|
+
* point rather than a tidy-up. The reducer had the same three lines in four
|
|
15
|
+
* branches - `turnEnd`, `teamUsage`, `fastUsage`, `compactionUsage` - and all
|
|
16
|
+
* four silently omitted the cache terms, so the status bar's `^` read LOWER than
|
|
17
|
+
* the `$` beside it on any session behind a caching gateway. A fifth source
|
|
18
|
+
* added later would have made the identical omission; now it cannot.
|
|
19
|
+
*
|
|
20
|
+
* `costDelta` IS PRECOMPUTED BY THE CALLER, exactly as the four branches already
|
|
21
|
+
* required: the summarizer and the fast tier may be different models with
|
|
22
|
+
* different price tables, and a reducer that priced its own would have to know
|
|
23
|
+
* about three of them.
|
|
24
|
+
*/
|
|
25
|
+
export declare function addUsage(total: UsageTotal, usage: TokenUsage, costDelta: number): UsageTotal;
|
|
26
|
+
/**
|
|
27
|
+
* The number the status bar's `^` shows: the PROMPT side of the session total.
|
|
28
|
+
*
|
|
29
|
+
* DELIBERATELY THE SAME TERMS AS `occupiedTokens` MINUS `outputTokens`
|
|
30
|
+
* (`compaction/pressure.ts`). Anthropic reports `input_tokens` EXCLUDING cached
|
|
31
|
+
* tokens and puts them in two separate fields, so `inputTokens` alone
|
|
32
|
+
* under-reports for anyone behind a caching gateway - while `computeCost` above
|
|
33
|
+
* has always priced all three. That is the "three units on one row" defect
|
|
34
|
+
* (P1-3): the percentage counted cache, the dollars counted cache, and the arrow
|
|
35
|
+
* did not.
|
|
36
|
+
*
|
|
37
|
+
* `$` IS STILL NOT THE SAME TOTAL, and the distinction is worth keeping straight
|
|
38
|
+
* (RV-14): `computeCost` also prices OUTPUT tokens. What this function asserts is
|
|
39
|
+
* narrower and exact - both cache terms are counted, on both sides.
|
|
40
|
+
*/
|
|
41
|
+
export declare function promptTokensOf(total: UsageTotal): number;
|
|
8
42
|
/** Format a USD amount for the status bar (`$0.0000`, or `$1.23` when large). */
|
|
9
43
|
export declare function formatCost(usd: number): string;
|
|
10
44
|
/** Format a token count compactly (`1.2k`, `3.4M`). */
|
package/dist/agent/usage.js
CHANGED
|
@@ -12,6 +12,49 @@ export function computeCost(usage, cost) {
|
|
|
12
12
|
perM(usage.cacheReadTokens ?? 0, cost.cacheRead) +
|
|
13
13
|
perM(usage.cacheWriteTokens ?? 0, cost.cacheWrite));
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Fold one `TokenUsage` into a running session total
|
|
17
|
+
* (context-usage-gauge-accuracy §3.6 / W5).
|
|
18
|
+
*
|
|
19
|
+
* THE ONE ADDITION SITE, and collapsing four hand-written copies into it is the
|
|
20
|
+
* point rather than a tidy-up. The reducer had the same three lines in four
|
|
21
|
+
* branches - `turnEnd`, `teamUsage`, `fastUsage`, `compactionUsage` - and all
|
|
22
|
+
* four silently omitted the cache terms, so the status bar's `^` read LOWER than
|
|
23
|
+
* the `$` beside it on any session behind a caching gateway. A fifth source
|
|
24
|
+
* added later would have made the identical omission; now it cannot.
|
|
25
|
+
*
|
|
26
|
+
* `costDelta` IS PRECOMPUTED BY THE CALLER, exactly as the four branches already
|
|
27
|
+
* required: the summarizer and the fast tier may be different models with
|
|
28
|
+
* different price tables, and a reducer that priced its own would have to know
|
|
29
|
+
* about three of them.
|
|
30
|
+
*/
|
|
31
|
+
export function addUsage(total, usage, costDelta) {
|
|
32
|
+
return {
|
|
33
|
+
inputTokens: total.inputTokens + usage.inputTokens,
|
|
34
|
+
outputTokens: total.outputTokens + usage.outputTokens,
|
|
35
|
+
cacheReadTokens: total.cacheReadTokens + (usage.cacheReadTokens ?? 0),
|
|
36
|
+
cacheWriteTokens: total.cacheWriteTokens + (usage.cacheWriteTokens ?? 0),
|
|
37
|
+
costUsd: total.costUsd + costDelta,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The number the status bar's `^` shows: the PROMPT side of the session total.
|
|
42
|
+
*
|
|
43
|
+
* DELIBERATELY THE SAME TERMS AS `occupiedTokens` MINUS `outputTokens`
|
|
44
|
+
* (`compaction/pressure.ts`). Anthropic reports `input_tokens` EXCLUDING cached
|
|
45
|
+
* tokens and puts them in two separate fields, so `inputTokens` alone
|
|
46
|
+
* under-reports for anyone behind a caching gateway - while `computeCost` above
|
|
47
|
+
* has always priced all three. That is the "three units on one row" defect
|
|
48
|
+
* (P1-3): the percentage counted cache, the dollars counted cache, and the arrow
|
|
49
|
+
* did not.
|
|
50
|
+
*
|
|
51
|
+
* `$` IS STILL NOT THE SAME TOTAL, and the distinction is worth keeping straight
|
|
52
|
+
* (RV-14): `computeCost` also prices OUTPUT tokens. What this function asserts is
|
|
53
|
+
* narrower and exact - both cache terms are counted, on both sides.
|
|
54
|
+
*/
|
|
55
|
+
export function promptTokensOf(total) {
|
|
56
|
+
return total.inputTokens + total.cacheReadTokens + total.cacheWriteTokens;
|
|
57
|
+
}
|
|
15
58
|
/** Format a USD amount for the status bar (`$0.0000`, or `$1.23` when large). */
|
|
16
59
|
export function formatCost(usd) {
|
|
17
60
|
if (usd <= 0)
|