@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
|
@@ -25,12 +25,22 @@
|
|
|
25
25
|
* methods delegate to a compactor that reads config LIVE - which is what lets
|
|
26
26
|
* `/compact off`, `/compact threshold 0.8` and a settings-screen save take effect
|
|
27
27
|
* mid-session without touching the `Agent`.
|
|
28
|
+
*
|
|
29
|
+
* DECLINED COMPACTIONS PRODUCE NO CLI EVENTS (quiet-noop §3.6). A `pressure`
|
|
30
|
+
* compaction that changed nothing, called nothing and spent nothing never emits
|
|
31
|
+
* `compaction_start`, so `onCompactorEvent` never opens a card and `settlePending`
|
|
32
|
+
* finds neither a held record nor a skeleton to synthesize. CORE still emits its
|
|
33
|
+
* own pair for every attempt, which is why `settlePending` still runs - it just
|
|
34
|
+
* refreshes the snapshot and returns. `onCompactorEvent`'s `if (!this.open)`
|
|
35
|
+
* guard is now unreachable for declines and stays for the abort race it was
|
|
36
|
+
* written for.
|
|
28
37
|
*/
|
|
29
38
|
import { DEFAULT_RETRY_POLICY, estimatePromptTokens, initProviders, validateHistory, } from '@aragon-agent/core';
|
|
30
39
|
import { getLogger } from '../logging/logger.js';
|
|
31
40
|
import { mintRunId, writeArchive } from './archive.js';
|
|
32
41
|
import { createChildContextManager } from './child.js';
|
|
33
42
|
import { Compactor } from './compactor.js';
|
|
43
|
+
import { ContextMeter } from './meter.js';
|
|
34
44
|
/**
|
|
35
45
|
* One retry, not ten and not zero - the `fast/wiring.ts` argument unchanged
|
|
36
46
|
* (D-10).
|
|
@@ -59,6 +69,7 @@ export function offCompactionSnapshot() {
|
|
|
59
69
|
headroom: 0,
|
|
60
70
|
source: 'estimate',
|
|
61
71
|
windowKnown: false,
|
|
72
|
+
windowOverridden: false,
|
|
62
73
|
deltaTokens: 0,
|
|
63
74
|
},
|
|
64
75
|
};
|
|
@@ -68,6 +79,8 @@ export class CompactionWiring {
|
|
|
68
79
|
log = getLogger().child('compaction');
|
|
69
80
|
listeners = new Set();
|
|
70
81
|
compactor;
|
|
82
|
+
/** The occupancy meter, injected or private (R-1). Forwarded to the compactor. */
|
|
83
|
+
meter;
|
|
71
84
|
/** The compactor's OWN transport (D-10). Lazy - see `getRegistry()`. */
|
|
72
85
|
registry = null;
|
|
73
86
|
unsubscribe = null;
|
|
@@ -116,6 +129,15 @@ export class CompactionWiring {
|
|
|
116
129
|
constructor(deps) {
|
|
117
130
|
this.deps = deps;
|
|
118
131
|
this.enabled = deps.getConfig().compaction.enabled;
|
|
132
|
+
this.meter =
|
|
133
|
+
deps.meter ??
|
|
134
|
+
new ContextMeter({
|
|
135
|
+
getMessages: deps.getMessages,
|
|
136
|
+
getSystemPrompt: deps.getSystemPrompt,
|
|
137
|
+
getModelInfo: () => deps.getModelInfoFor(this.mainRef()),
|
|
138
|
+
isWindowKnown: () => deps.isPricedModel(this.mainRef()),
|
|
139
|
+
getWindowOverride: () => deps.getConfig().contextWindow,
|
|
140
|
+
});
|
|
119
141
|
this.compactor = new Compactor({
|
|
120
142
|
getConfig: deps.getConfig,
|
|
121
143
|
hasKey: deps.hasKey,
|
|
@@ -130,6 +152,9 @@ export class CompactionWiring {
|
|
|
130
152
|
complete: (providerId, request) => this.getRegistry().complete(providerId, request),
|
|
131
153
|
emit: (event) => this.onCompactorEvent(event),
|
|
132
154
|
notify: deps.notify,
|
|
155
|
+
// ONE METER PER PROCESS. The compactor must not build its own fallback
|
|
156
|
+
// here, or the trigger would read a different number from the gauge.
|
|
157
|
+
meter: this.meter,
|
|
133
158
|
// ONE NUMBER ON SCREEN AND IN THE TRIGGER (R-11 / DH-15). The checkpoint
|
|
134
159
|
// has already measured; publishing THAT pressure rather than re-measuring
|
|
135
160
|
// is what keeps the two from disagreeing by one turn's tool results.
|
|
@@ -196,9 +221,23 @@ export class CompactionWiring {
|
|
|
196
221
|
* `/clear`, `/resume`, and any future host-side rewrite. A recorded prefix
|
|
197
222
|
* length indexes an array that no longer exists, so the delta must be
|
|
198
223
|
* abandoned rather than applied to a different history.
|
|
224
|
+
*
|
|
225
|
+
* THE **DEEP** RESET (I-8). `/resume` can change the model in the same breath,
|
|
226
|
+
* so `estimateOffset` - a calibration for one toolset on one model - goes too.
|
|
227
|
+
* The SHALLOW reset is `settlePending`'s splice branch, and the two are not
|
|
228
|
+
* interchangeable: swapping them produces silent errors in OPPOSITE
|
|
229
|
+
* directions (a splice that drops the offset reads too low and climbs back; a
|
|
230
|
+
* resume that keeps it reads too high and never self-corrects).
|
|
231
|
+
*
|
|
232
|
+
* DUPLICATED BY `AgentController` ON PURPOSE. The controller calls
|
|
233
|
+
* `contextMeter.onHistoryReplaced()` directly at the same two sites, because
|
|
234
|
+
* this class does not exist at all when `compaction.enabled` is false - and
|
|
235
|
+
* that unconditional call is the only thing that keeps `/resume` re-measuring
|
|
236
|
+
* for such a session. A deep reset is idempotent, so both firing costs
|
|
237
|
+
* nothing; deleting the controller's copy "to de-duplicate" reintroduces P0-2.
|
|
199
238
|
*/
|
|
200
239
|
onHistoryReplaced() {
|
|
201
|
-
this.
|
|
240
|
+
this.meter.onHistoryReplaced();
|
|
202
241
|
}
|
|
203
242
|
/**
|
|
204
243
|
* The per-child manager factory, or `null` when children get none
|
|
@@ -305,13 +344,19 @@ export class CompactionWiring {
|
|
|
305
344
|
// -----------------------------------------------------------------------
|
|
306
345
|
// Status and events
|
|
307
346
|
// -----------------------------------------------------------------------
|
|
347
|
+
/**
|
|
348
|
+
* The status a reader gets right now.
|
|
349
|
+
*
|
|
350
|
+
* IT READS `meter.current()`, WHICH RE-MEASURES WHEN DIRTY - and that is the
|
|
351
|
+
* fix for P0-1 (§3.3). This method is called one statement after
|
|
352
|
+
* `settlePending` marks the meter spliced, so the `snapshot` event emitted
|
|
353
|
+
* immediately after `compaction_end` carries the POST-compaction occupancy.
|
|
354
|
+
* The old body preferred `compactor.lastMeasured()`, i.e. the measurement that
|
|
355
|
+
* TRIGGERED this compaction, so the gauge fell and was bounced straight back
|
|
356
|
+
* to the pre-compaction figure inside the same synchronous emit loop.
|
|
357
|
+
*/
|
|
308
358
|
snapshot() {
|
|
309
|
-
|
|
310
|
-
this.compactor.measure({
|
|
311
|
-
messages: this.deps.getMessages(),
|
|
312
|
-
systemPrompt: this.deps.getSystemPrompt(),
|
|
313
|
-
});
|
|
314
|
-
return this.snapshotWith(pressure);
|
|
359
|
+
return this.snapshotWith(this.meter.current());
|
|
315
360
|
}
|
|
316
361
|
/**
|
|
317
362
|
* `snapshot()` with the pressure argument substituted for the re-measurement.
|
|
@@ -328,6 +373,10 @@ export class CompactionWiring {
|
|
|
328
373
|
live: this.enabled && summarizer !== null,
|
|
329
374
|
model: summarizer?.ref.modelId ?? '',
|
|
330
375
|
compactions: totals.compactions,
|
|
376
|
+
// OMITTED WHEN ZERO (quiet-noop D-9), so a healthy session's snapshot is
|
|
377
|
+
// shaped exactly as it was before this feature and `/compact status` has
|
|
378
|
+
// nothing extra to render.
|
|
379
|
+
...(totals.declined > 0 ? { declined: totals.declined } : {}),
|
|
331
380
|
tokensReclaimed: totals.tokensReclaimed,
|
|
332
381
|
usage: totals.usage,
|
|
333
382
|
pricingUnknown: totals.pricingUnknown,
|
|
@@ -349,6 +398,17 @@ export class CompactionWiring {
|
|
|
349
398
|
// -----------------------------------------------------------------------
|
|
350
399
|
// Internals
|
|
351
400
|
// -----------------------------------------------------------------------
|
|
401
|
+
/**
|
|
402
|
+
* The main model, for the FALLBACK meter's deps only (R-1).
|
|
403
|
+
*
|
|
404
|
+
* Production injects a meter built by `AgentController`, which resolves the
|
|
405
|
+
* same ref from the same live config; this exists so a test that constructs a
|
|
406
|
+
* bare wiring still gets a working denominator.
|
|
407
|
+
*/
|
|
408
|
+
mainRef() {
|
|
409
|
+
const config = this.deps.getConfig();
|
|
410
|
+
return { providerId: config.provider, modelId: config.model };
|
|
411
|
+
}
|
|
352
412
|
/**
|
|
353
413
|
* The transport one summarization call goes through - separate from the lead's,
|
|
354
414
|
* and fail-fast (D-10).
|
|
@@ -395,19 +455,16 @@ export class CompactionWiring {
|
|
|
395
455
|
return;
|
|
396
456
|
}
|
|
397
457
|
if (event.type === 'turn_end') {
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
messages: this.deps.getMessages(),
|
|
409
|
-
systemPrompt: this.deps.getSystemPrompt(),
|
|
410
|
-
});
|
|
458
|
+
// THE MEASUREMENT IS THE METER'S (context-usage-gauge-accuracy §3.3.4). It
|
|
459
|
+
// subscribes to `turn_end` on the same stream and recomputes the offset,
|
|
460
|
+
// the prefix length and the pressure there; the two calls that used to
|
|
461
|
+
// live here - `compactor.onTurnEnd(...)` and `compactor.measure(...)` -
|
|
462
|
+
// were both forwarders to it and doing the same work twice.
|
|
463
|
+
//
|
|
464
|
+
// THE SNAPSHOT EMIT STAYS. The other fields on it - `inFlight`, the
|
|
465
|
+
// session counts, the summarizer - are this class's own and still need
|
|
466
|
+
// refreshing on a turn boundary, and `snapshot()` picks up the meter's
|
|
467
|
+
// freshly published pressure for free.
|
|
411
468
|
this.emit({ type: 'snapshot', snapshot: this.snapshot() });
|
|
412
469
|
return;
|
|
413
470
|
}
|
|
@@ -445,8 +502,20 @@ export class CompactionWiring {
|
|
|
445
502
|
// compaction no matter which branch the verdict took - including the early
|
|
446
503
|
// `return` below.
|
|
447
504
|
const dropped = this.compactor.takeLastDropped();
|
|
448
|
-
if (!record)
|
|
505
|
+
if (!record) {
|
|
506
|
+
// A SILENT DECLINE (quiet-noop §3.6). No card - but the gauge and the chip
|
|
507
|
+
// are still refreshed: `inFlight` has just gone false and `/compact status`
|
|
508
|
+
// reads this same snapshot, which is where the `declined` count surfaces.
|
|
509
|
+
//
|
|
510
|
+
// `snapshot()` NOW GOES THROUGH `meter.current()`, so this path DOES pay a
|
|
511
|
+
// re-measurement when the history has moved since the last one (RV-11 -
|
|
512
|
+
// the previous sentence here claimed the opposite and was left stale by
|
|
513
|
+
// context-usage-gauge-accuracy). The cost is bounded the same way it always
|
|
514
|
+
// was: guard 4 caps declines per session at `stuckLimit`, and a re-measure
|
|
515
|
+
// on the measured branch walks one turn's worth of appended messages.
|
|
516
|
+
this.emit({ type: 'snapshot', snapshot: this.snapshot() });
|
|
449
517
|
return;
|
|
518
|
+
}
|
|
450
519
|
const settled = {
|
|
451
520
|
...record,
|
|
452
521
|
applied: verdict.applied,
|
|
@@ -457,10 +526,23 @@ export class CompactionWiring {
|
|
|
457
526
|
};
|
|
458
527
|
if (verdict.applied) {
|
|
459
528
|
this.compactor.recordApplied(settled.tokensBefore, settled.tokensAfter);
|
|
460
|
-
// SITE 1 OF THE FOUR INVALIDATION SITES (hardening §3.2.3)
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
this
|
|
529
|
+
// SITE 1 OF THE FOUR INVALIDATION SITES (hardening §3.2.3), and THE LOAD-
|
|
530
|
+
// BEARING LINE OF THE P0-1 FIX (context-usage-gauge-accuracy I-9).
|
|
531
|
+
//
|
|
532
|
+
// ITS POSITION IS THE WHOLE ARGUMENT: it runs in this class's own
|
|
533
|
+
// synchronous code, BEFORE both `emit` calls below, so the `snapshot()`
|
|
534
|
+
// two statements down is guaranteed to see a dirty meter and re-measure -
|
|
535
|
+
// no matter which of the two agent-stream listeners the emitter reached
|
|
536
|
+
// first. Deleting it and relying on the meter's own `compaction_end`
|
|
537
|
+
// subscription instead moves correctness onto subscription ORDER, which is
|
|
538
|
+
// true in every unit test and false in production import order.
|
|
539
|
+
//
|
|
540
|
+
// THE **SHALLOW** RESET (I-8). A splice changes the history and nothing
|
|
541
|
+
// else, so `estimateOffset` - a calibration for this toolset on this model
|
|
542
|
+
// - is still valid and is the only thing keeping the post-compaction
|
|
543
|
+
// estimate honest (I-5). `onHistoryReplaced()` is the other depth and is
|
|
544
|
+
// NOT interchangeable with this one.
|
|
545
|
+
this.meter.onHistorySpliced();
|
|
464
546
|
const path = this.archive(settled, dropped);
|
|
465
547
|
if (path)
|
|
466
548
|
settled.archivePath = path;
|
|
@@ -405,6 +405,11 @@ function maskConfig(config) {
|
|
|
405
405
|
function displayOverride(key, value) {
|
|
406
406
|
if (key === 'maxTokens' && value === null)
|
|
407
407
|
return 'auto';
|
|
408
|
+
// The SECOND tri-state key, and it needs this for the same reason (§4.5):
|
|
409
|
+
// `contextWindow: null` is AUTO ("use the model table"), not "unset", and a
|
|
410
|
+
// bare `null` sends the user looking for a bug that is not there.
|
|
411
|
+
if (key === 'contextWindow' && value === null)
|
|
412
|
+
return 'auto';
|
|
408
413
|
return undefined;
|
|
409
414
|
}
|
|
410
415
|
/** Resolve a flat or one-level-dotted key against the persisted config. */
|
package/dist/config/env.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Overrides: ARAGON_PROVIDER, ARAGON_MODEL, ARAGON_BASE_URL, ARAGON_THINKING,
|
|
8
8
|
* ARAGON_SHOW_THINKING,
|
|
9
9
|
* ARAGON_MAX_TOKENS (a number, or `auto` / `0` for the per-model ceiling),
|
|
10
|
-
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PLAN, ARAGON_TEAM,
|
|
10
|
+
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PASTE, ARAGON_PLAN, ARAGON_TEAM,
|
|
11
11
|
* ARAGON_TEAM_MAX, ARAGON_RETRY, ARAGON_RETRY_MAX, ARAGON_FAST,
|
|
12
12
|
* ARAGON_FAST_PROVIDER, ARAGON_FAST_MODEL, ARAGON_FAST_BASE_URL,
|
|
13
13
|
* ARAGON_LOG_LEVEL,
|
package/dist/config/env.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Overrides: ARAGON_PROVIDER, ARAGON_MODEL, ARAGON_BASE_URL, ARAGON_THINKING,
|
|
8
8
|
* ARAGON_SHOW_THINKING,
|
|
9
9
|
* ARAGON_MAX_TOKENS (a number, or `auto` / `0` for the per-model ceiling),
|
|
10
|
-
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PLAN, ARAGON_TEAM,
|
|
10
|
+
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PASTE, ARAGON_PLAN, ARAGON_TEAM,
|
|
11
11
|
* ARAGON_TEAM_MAX, ARAGON_RETRY, ARAGON_RETRY_MAX, ARAGON_FAST,
|
|
12
12
|
* ARAGON_FAST_PROVIDER, ARAGON_FAST_MODEL, ARAGON_FAST_BASE_URL,
|
|
13
13
|
* ARAGON_LOG_LEVEL,
|
|
@@ -37,7 +37,7 @@ import process from 'node:process';
|
|
|
37
37
|
import dotenv from 'dotenv';
|
|
38
38
|
import { registerSecretsFrom } from '../logging/secret-registry.js';
|
|
39
39
|
import { clampLogLevel } from '../logging/levels.js';
|
|
40
|
-
import { clampMaxTokens, clampTheme, clampThinkingLevel, isAutoToken, parseThresholdInput, } from './schema.js';
|
|
40
|
+
import { clampContextWindow, clampMaxTokens, clampTheme, clampThinkingLevel, isAutoToken, parseThresholdInput, } from './schema.js';
|
|
41
41
|
/**
|
|
42
42
|
* Load a project `.env` from the given directory into `process.env` (existing
|
|
43
43
|
* process env wins — dotenv never overrides already-set variables).
|
|
@@ -113,6 +113,20 @@ export function readEnvConfig() {
|
|
|
113
113
|
partial.maxTokens = clamped;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
+
// `auto` / `0` mean AUTO (`null`); a number is clamped. Anything else is LEFT
|
|
117
|
+
// ABSENT rather than written as `null`, the rule `ARAGON_MAX_TOKENS` states one
|
|
118
|
+
// block up: a typo must not silently discard the number in the config file.
|
|
119
|
+
const contextWindow = process.env.ARAGON_CONTEXT_WINDOW?.trim();
|
|
120
|
+
if (contextWindow !== undefined && contextWindow.length > 0) {
|
|
121
|
+
if (isAutoToken(contextWindow)) {
|
|
122
|
+
partial.contextWindow = null;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const clamped = clampContextWindow(contextWindow, null);
|
|
126
|
+
if (clamped !== null)
|
|
127
|
+
partial.contextWindow = clamped;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
116
130
|
if (process.env.ARAGON_THEME) {
|
|
117
131
|
partial.theme = clampTheme(process.env.ARAGON_THEME.trim(), 'auto');
|
|
118
132
|
}
|
|
@@ -141,6 +155,13 @@ export function readEnvConfig() {
|
|
|
141
155
|
mouseSelect === 'on' ||
|
|
142
156
|
mouseSelect === 'yes';
|
|
143
157
|
}
|
|
158
|
+
// ARAGON_PASTE=0 turns paste recognition off, which is the documented one-env
|
|
159
|
+
// revert to v0.6.3 input handling (tui-paste-handling section 7.2). Parsed by
|
|
160
|
+
// the POSITIVE list, exactly as its two neighbours above are.
|
|
161
|
+
const paste = process.env.ARAGON_PASTE?.trim().toLowerCase();
|
|
162
|
+
if (paste !== undefined && paste.length > 0) {
|
|
163
|
+
partial.paste = paste === '1' || paste === 'true' || paste === 'on' || paste === 'yes';
|
|
164
|
+
}
|
|
144
165
|
// ARAGON_SCROLL_RESUME_MS=<n> — idle delay before a paused viewport returns to
|
|
145
166
|
// the newest line; `0` disables auto-resume. THE ONLY CHANNEL BESIDES THE
|
|
146
167
|
// CONFIG FILE, because §6.1 gives this key no CLI flag on purpose. `load.ts`
|
|
@@ -239,6 +260,46 @@ export function readEnvConfig() {
|
|
|
239
260
|
if (Object.keys(section).length > 0)
|
|
240
261
|
partial.todo = section;
|
|
241
262
|
}
|
|
263
|
+
// ARAGON_BASH_BACKGROUND=0 turns background services off entirely (no
|
|
264
|
+
// `background` param, no `bash_output`/`bash_kill`, no prompt block, no chip);
|
|
265
|
+
// ARAGON_BASH_AUTO_BACKGROUND=0 keeps them but stops the classifier firing on
|
|
266
|
+
// an unset `background`. Parsed by the POSITIVE list, exactly as
|
|
267
|
+
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO
|
|
268
|
+
// are - NOT by `envBool` below, which uses a negative list.
|
|
269
|
+
//
|
|
270
|
+
// Both pass the test every env var in this file has to pass: an unattended run
|
|
271
|
+
// in a container with a fixed entrypoint can reach neither a flag nor a config
|
|
272
|
+
// file, and "do not leave a dev server running in my CI job" is exactly the
|
|
273
|
+
// kind of decision that channel needs to make.
|
|
274
|
+
//
|
|
275
|
+
// ONE ACCUMULATED SECTION, ASSIGNED ONCE - the shape the `team` and `todo`
|
|
276
|
+
// branches above already use, and NOT two `if` blocks each assigning
|
|
277
|
+
// `partial.bash` (the `todo` P1-4 trap). THE CAST IS WHAT WOULD MAKE THAT
|
|
278
|
+
// MISTAKE SILENT: a second assignment compiles clean and drops the first key,
|
|
279
|
+
// so `ARAGON_BASH_BACKGROUND=0 ARAGON_BASH_AUTO_BACKGROUND=0` would quietly
|
|
280
|
+
// re-register the tools the user turned off.
|
|
281
|
+
const bashBackground = process.env.ARAGON_BASH_BACKGROUND?.trim().toLowerCase();
|
|
282
|
+
const bashAuto = process.env.ARAGON_BASH_AUTO_BACKGROUND?.trim().toLowerCase();
|
|
283
|
+
if ((bashBackground !== undefined && bashBackground.length > 0) ||
|
|
284
|
+
(bashAuto !== undefined && bashAuto.length > 0)) {
|
|
285
|
+
const section = {};
|
|
286
|
+
if (bashBackground !== undefined && bashBackground.length > 0) {
|
|
287
|
+
section.background =
|
|
288
|
+
bashBackground === '1' ||
|
|
289
|
+
bashBackground === 'true' ||
|
|
290
|
+
bashBackground === 'on' ||
|
|
291
|
+
bashBackground === 'yes';
|
|
292
|
+
}
|
|
293
|
+
if (bashAuto !== undefined && bashAuto.length > 0) {
|
|
294
|
+
section.autoBackground =
|
|
295
|
+
bashAuto === '1' || bashAuto === 'true' || bashAuto === 'on' || bashAuto === 'yes';
|
|
296
|
+
}
|
|
297
|
+
// A PARTIAL section, safe only because every consumer deep-merges it - the
|
|
298
|
+
// same contract `partial.skills`, `partial.log`, `partial.team` and
|
|
299
|
+
// `partial.todo` rely on.
|
|
300
|
+
if (Object.keys(section).length > 0)
|
|
301
|
+
partial.bash = section;
|
|
302
|
+
}
|
|
242
303
|
// ARAGON_RETRY=0 is the kill switch for API retry; ARAGON_RETRY_MAX=<n>
|
|
243
304
|
// overrides the count. Parsed by the POSITIVE list, exactly as
|
|
244
305
|
// ARAGON_FULLSCREEN / ARAGON_MOUSE / ARAGON_PLAN / ARAGON_TEAM / ARAGON_TODO
|
package/dist/config/load.d.ts
CHANGED
|
@@ -49,6 +49,17 @@ export interface CliFlags {
|
|
|
49
49
|
* `false` on every run (the `--todo-panel` trap).
|
|
50
50
|
*/
|
|
51
51
|
mouseSelect?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* `--paste` ⇒ true, `--no-paste` ⇒ false, absent ⇒ undefined
|
|
54
|
+
* (tui-paste-handling section 7.1).
|
|
55
|
+
*
|
|
56
|
+
* DECLARED AS A PAIR, and carried through `toFlags`, for the reason the two
|
|
57
|
+
* notes above record. `paste` is additionally PERSISTED and defaults to
|
|
58
|
+
* `true`, so a lone `--no-paste` would make commander default `opts.paste` to
|
|
59
|
+
* `true` -- indistinguishable from silence, and silently overriding a stored
|
|
60
|
+
* `false` on every run that passed no flag at all.
|
|
61
|
+
*/
|
|
62
|
+
paste?: boolean;
|
|
52
63
|
/** `--no-skills` ⇒ false. Turns the whole skill subsystem off (§7.5). */
|
|
53
64
|
skills?: boolean;
|
|
54
65
|
/** `--skill <name>` ×N — force Level 2 injection for this run. */
|
|
@@ -174,6 +185,23 @@ export interface CliFlags {
|
|
|
174
185
|
* to keep in step with `toFlags`.
|
|
175
186
|
*/
|
|
176
187
|
fastReview?: string;
|
|
188
|
+
/**
|
|
189
|
+
* `--fast-delegate` ⇒ true, `--no-fast-delegate` ⇒ false, absent ⇒ undefined
|
|
190
|
+
* (web-use-tier-cooperation-and-control-closure §4.1.5).
|
|
191
|
+
*
|
|
192
|
+
* DECLARED AS A PAIR — MANDATORY, for the reason `--team`, `--todo-panel`,
|
|
193
|
+
* `--render-governor`, `--retry` and `--fast` each record in turn, and here
|
|
194
|
+
* with the SAME polarity as `--update`: `fast.delegate` is PERSISTED and
|
|
195
|
+
* defaults to `true`, so a lone `--no-fast-delegate` would make commander
|
|
196
|
+
* default `opts.fastDelegate` to `true` and silently overwrite a stored
|
|
197
|
+
* `false` on every run that passed no flag at all.
|
|
198
|
+
*
|
|
199
|
+
* It exists because it is the ONLY `fast.*` sub-switch with no channel a
|
|
200
|
+
* wrapper can reach: `--fast-review <n|off>` already covers `review` and
|
|
201
|
+
* `reviewEveryTurns`, and `enabled` / `model` / `provider` have both a flag
|
|
202
|
+
* and an environment variable.
|
|
203
|
+
*/
|
|
204
|
+
fastDelegate?: boolean;
|
|
177
205
|
/**
|
|
178
206
|
* `--update` ⇒ true, `--no-update` ⇒ false, absent ⇒ undefined
|
|
179
207
|
* (cli-auto-update §4.2).
|
package/dist/config/load.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* legitimately long single tool run can never be watchdog-killed.
|
|
7
7
|
*/
|
|
8
8
|
import process from 'node:process';
|
|
9
|
-
import { DEFAULT_CONFIG, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampMaxTokens, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, parseThresholdInput, } from './schema.js';
|
|
9
|
+
import { DEFAULT_CONFIG, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampBashConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, parseThresholdInput, } from './schema.js';
|
|
10
10
|
import { loadDotenv, readEnvConfig, readEnvDisabledSkills } from './env.js';
|
|
11
11
|
import { readConfigFile } from './store.js';
|
|
12
12
|
import { getConfigPath } from './app-paths.js';
|
|
@@ -80,6 +80,24 @@ function resolveMouseSelect(flags, env, file) {
|
|
|
80
80
|
return file.mouseSelect;
|
|
81
81
|
return DEFAULT_CONFIG.mouseSelect;
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Paste handling: flag > env > file > default `true` (tui-paste-handling
|
|
85
|
+
* section 7.4).
|
|
86
|
+
*
|
|
87
|
+
* SHAPED EXACTLY LIKE `resolveMouse` above, for the reason its own note gives:
|
|
88
|
+
* reaching for the `hints` shape -- `flags.x !== undefined ? ... : file.x ??
|
|
89
|
+
* DEFAULT`, which reads NO ENV AT ALL -- would ship `ARAGON_PASTE` as
|
|
90
|
+
* documented-but-dead.
|
|
91
|
+
*/
|
|
92
|
+
function resolvePaste(flags, env, file) {
|
|
93
|
+
if (flags.paste !== undefined)
|
|
94
|
+
return flags.paste;
|
|
95
|
+
if (env.paste !== undefined)
|
|
96
|
+
return env.paste;
|
|
97
|
+
if (file.paste !== undefined)
|
|
98
|
+
return file.paste;
|
|
99
|
+
return DEFAULT_CONFIG.paste;
|
|
100
|
+
}
|
|
83
101
|
/**
|
|
84
102
|
* Idle scroll-resume delay: env › file › default 5000, clamped to [0, 120000].
|
|
85
103
|
*
|
|
@@ -153,6 +171,24 @@ function resolveTodoConfig(flags, env, file) {
|
|
|
153
171
|
...(flags.todoFollow !== undefined ? { followThrough: flags.todoFollow } : {}),
|
|
154
172
|
});
|
|
155
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Merge the `bash` section across the three layers that supply it
|
|
176
|
+
* (defaults > file > env), background-service-supervision §5.4.
|
|
177
|
+
*
|
|
178
|
+
* THREE LAYERS, NOT FOUR, AND THAT IS NOT AN OVERSIGHT. There is deliberately no
|
|
179
|
+
* `--no-background` flag: the two switches this section carries are already
|
|
180
|
+
* reachable from `config.json` (a per-machine preference) and from the
|
|
181
|
+
* environment (the channel a container entrypoint has), which is the two-part
|
|
182
|
+
* test every knob in this file is held to. A third spelling would be a third
|
|
183
|
+
* place for the answer to differ.
|
|
184
|
+
*/
|
|
185
|
+
function resolveBashConfig(env, file) {
|
|
186
|
+
return clampBashConfig({
|
|
187
|
+
...DEFAULT_CONFIG.bash,
|
|
188
|
+
...(file.bash ?? {}),
|
|
189
|
+
...(env.bash ?? {}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
156
192
|
/**
|
|
157
193
|
* Merge the `retry` section across all four layers (defaults › file › env ›
|
|
158
194
|
* flags), llm-api-retry-backoff §6.2.
|
|
@@ -214,6 +250,12 @@ function resolveFastConfig(flags, env, file) {
|
|
|
214
250
|
...(flags.fastProvider !== undefined && flags.fastProvider !== ''
|
|
215
251
|
? { provider: flags.fastProvider }
|
|
216
252
|
: {}),
|
|
253
|
+
// `!== undefined`, for the reason stated on `flags.fast` above: commander
|
|
254
|
+
// materialises a lone `--no-fast-delegate` as `opts.fastDelegate = true`
|
|
255
|
+
// when the flag is absent, and `delegate` defaults to TRUE — so a truthiness
|
|
256
|
+
// check would force it on for every run that passed no flag at all,
|
|
257
|
+
// overwriting a stored `false` with nothing to show for it.
|
|
258
|
+
...(flags.fastDelegate !== undefined ? { delegate: flags.fastDelegate } : {}),
|
|
217
259
|
...reviewPatch,
|
|
218
260
|
});
|
|
219
261
|
}
|
|
@@ -374,6 +416,42 @@ function resolveMaxTokens(flags, env, file) {
|
|
|
374
416
|
}
|
|
375
417
|
return DEFAULT_CONFIG.maxTokens ?? undefined;
|
|
376
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Resolve the context-window override: env > file > default, where an EXPLICIT
|
|
421
|
+
* `null` at either layer means AUTO (context-usage-gauge-accuracy §3.6).
|
|
422
|
+
*
|
|
423
|
+
* IT CANNOT USE `pick()` FOR THE REASON `resolveMaxTokens` GIVES: `pick` skips
|
|
424
|
+
* `null`, and `null` is exactly the value being looked for. NO CLI FLAG feeds
|
|
425
|
+
* this - it is a per-installation correction for a model table entry, not a
|
|
426
|
+
* per-invocation choice, so `config set` and the environment variable are the
|
|
427
|
+
* two surfaces the design names.
|
|
428
|
+
*/
|
|
429
|
+
function resolveContextWindow(env, file) {
|
|
430
|
+
const layers = [
|
|
431
|
+
{
|
|
432
|
+
name: 'ARAGON_CONTEXT_WINDOW',
|
|
433
|
+
value: hasOwn(env, 'contextWindow') ? env.contextWindow : undefined,
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'config.json contextWindow',
|
|
437
|
+
value: hasOwn(file, 'contextWindow') ? file.contextWindow : undefined,
|
|
438
|
+
},
|
|
439
|
+
];
|
|
440
|
+
for (const layer of layers) {
|
|
441
|
+
if (layer.value === undefined)
|
|
442
|
+
continue; // this layer said nothing
|
|
443
|
+
if (layer.value === null || isAutoToken(layer.value))
|
|
444
|
+
return null; // AUTO
|
|
445
|
+
const n = clampContextWindow(layer.value, null);
|
|
446
|
+
if (n !== null)
|
|
447
|
+
return n;
|
|
448
|
+
getLogger().warn('config', 'context_window_unusable', {
|
|
449
|
+
source: layer.name,
|
|
450
|
+
value: String(layer.value),
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return DEFAULT_CONFIG.contextWindow;
|
|
454
|
+
}
|
|
377
455
|
/**
|
|
378
456
|
* Resolve the effective config by merging all layers, then derive the runtime
|
|
379
457
|
* (non-persisted) fields and enforce the timeout invariant.
|
|
@@ -409,6 +487,7 @@ export function loadConfig(flags = {}) {
|
|
|
409
487
|
const liveToolOutput = pick(flags.liveToolOutput, env.partial.liveToolOutput, file.liveToolOutput) ??
|
|
410
488
|
DEFAULT_CONFIG.liveToolOutput;
|
|
411
489
|
const maxTokens = resolveMaxTokens(flags, env.partial, file);
|
|
490
|
+
const contextWindow = resolveContextWindow(env.partial, file);
|
|
412
491
|
// `clampTheme` is also where the v0.3.0 `dark` name is migrated to `cool`,
|
|
413
492
|
// so reading a legacy config file needs no extra step here (§4.5 / R-4).
|
|
414
493
|
const theme = clampTheme(pick(flags.theme, env.partial.theme, file.theme), DEFAULT_CONFIG.theme);
|
|
@@ -498,6 +577,7 @@ export function loadConfig(flags = {}) {
|
|
|
498
577
|
showThinking,
|
|
499
578
|
liveToolOutput,
|
|
500
579
|
maxTokens,
|
|
580
|
+
contextWindow,
|
|
501
581
|
theme,
|
|
502
582
|
reducedMotion,
|
|
503
583
|
fullscreen: resolveFullscreen(flags, env.partial, file),
|
|
@@ -520,6 +600,7 @@ export function loadConfig(flags = {}) {
|
|
|
520
600
|
hints,
|
|
521
601
|
mouse: resolveMouse(flags, env.partial, file),
|
|
522
602
|
mouseSelect: resolveMouseSelect(flags, env.partial, file),
|
|
603
|
+
paste: resolvePaste(flags, env.partial, file),
|
|
523
604
|
scrollResumeMs: resolveScrollResumeMs(env.partial, file),
|
|
524
605
|
// From `<home>/state.json`, not the config file — see `ui-state.ts`.
|
|
525
606
|
submitCount: readSubmitCount(),
|
|
@@ -545,6 +626,7 @@ export function loadConfig(flags = {}) {
|
|
|
545
626
|
fast: resolveFastConfig(flags, env.partial, file),
|
|
546
627
|
update: resolveUpdateConfig(flags, env.partial, file),
|
|
547
628
|
compaction: resolveCompactionConfig(flags, env.partial, file),
|
|
629
|
+
bash: resolveBashConfig(env.partial, file),
|
|
548
630
|
cwd,
|
|
549
631
|
color,
|
|
550
632
|
colorLevel,
|