@aragon-agent/cli 0.6.2 → 0.6.4
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 +321 -0
- package/README.md +297 -2
- package/dist/agent/controller.d.ts +193 -1
- package/dist/agent/controller.js +475 -2
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +47 -0
- package/dist/agent/reducer.d.ts +189 -0
- package/dist/agent/reducer.js +301 -2
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +297 -16
- package/dist/commands/builtins.js +165 -24
- package/dist/commands/registry.d.ts +27 -0
- package/dist/compaction/archive.d.ts +175 -0
- package/dist/compaction/archive.js +267 -0
- package/dist/compaction/child.d.ts +97 -0
- package/dist/compaction/child.js +118 -0
- package/dist/compaction/command.d.ts +41 -0
- package/dist/compaction/command.js +355 -0
- package/dist/compaction/compactor.d.ts +302 -0
- package/dist/compaction/compactor.js +810 -0
- package/dist/compaction/digest.d.ts +62 -0
- package/dist/compaction/digest.js +164 -0
- package/dist/compaction/limits.d.ts +165 -0
- package/dist/compaction/limits.js +172 -0
- package/dist/compaction/pressure.d.ts +135 -0
- package/dist/compaction/pressure.js +167 -0
- package/dist/compaction/summarize-call.d.ts +70 -0
- package/dist/compaction/summarize-call.js +86 -0
- package/dist/compaction/summary-prompt.d.ts +85 -0
- package/dist/compaction/summary-prompt.js +197 -0
- package/dist/compaction/tail-budget.d.ts +52 -0
- package/dist/compaction/tail-budget.js +67 -0
- package/dist/compaction/types.d.ts +215 -0
- package/dist/compaction/types.js +22 -0
- package/dist/compaction/wiring.d.ts +264 -0
- package/dist/compaction/wiring.js +577 -0
- package/dist/config/app-paths.d.ts +14 -0
- package/dist/config/app-paths.js +16 -0
- package/dist/config/cli-commands.d.ts +41 -0
- package/dist/config/cli-commands.js +77 -1
- package/dist/config/env.d.ts +5 -2
- package/dist/config/env.js +140 -3
- package/dist/config/load.d.ts +42 -0
- package/dist/config/load.js +101 -1
- package/dist/config/schema.d.ts +244 -0
- package/dist/config/schema.js +170 -0
- package/dist/config/store.js +37 -1
- package/dist/config/ui-state.d.ts +28 -1
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +50 -1
- package/dist/exec/runner.js +42 -0
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/mouse-events.d.ts +31 -5
- package/dist/input/mouse-events.js +86 -27
- 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 +53 -1
- package/dist/logging/install.js +81 -1
- package/dist/logging/logger.d.ts +1 -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 +55 -1
- package/dist/team/report.js +7 -1
- package/dist/team/runtime.d.ts +10 -0
- package/dist/team/runtime.js +7 -0
- package/dist/team/subagent.d.ts +31 -1
- package/dist/team/subagent.js +42 -15
- package/dist/team/types.d.ts +8 -0
- 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/ActivityLine.d.ts +19 -1
- package/dist/ui/ActivityLine.js +10 -2
- package/dist/ui/App.d.ts +62 -2
- package/dist/ui/App.js +546 -16
- package/dist/ui/Composer.d.ts +66 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +82 -3
- package/dist/ui/PromptInput.js +232 -49
- package/dist/ui/StatusBar.d.ts +59 -0
- package/dist/ui/StatusBar.js +16 -4
- package/dist/ui/TeamPanel.js +7 -1
- package/dist/ui/Transcript.d.ts +21 -1
- package/dist/ui/Transcript.js +61 -3
- package/dist/ui/clipboard.d.ts +52 -0
- package/dist/ui/clipboard.js +91 -0
- 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/CompactionCard.d.ts +75 -0
- package/dist/ui/entries/CompactionCard.js +158 -0
- 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/frame-differ.d.ts +46 -0
- package/dist/ui/frame-differ.js +94 -10
- package/dist/ui/gauge.d.ts +16 -1
- package/dist/ui/gauge.js +25 -6
- package/dist/ui/glyphs.d.ts +11 -0
- package/dist/ui/glyphs.js +2 -0
- package/dist/ui/layout/ScrollViewport.d.ts +43 -6
- package/dist/ui/layout/ScrollViewport.js +128 -21
- package/dist/ui/layout/budget.d.ts +44 -9
- package/dist/ui/layout/budget.js +42 -10
- package/dist/ui/layout/follow-state.d.ts +137 -0
- package/dist/ui/layout/follow-state.js +150 -0
- package/dist/ui/layout/virtual-window.d.ts +11 -0
- package/dist/ui/layout/virtual-window.js +85 -1
- package/dist/ui/overlays/HelpOverlay.js +9 -1
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.d.ts +47 -1
- package/dist/ui/overlays/SettingsScreen.js +74 -2
- package/dist/ui/palettes.d.ts +16 -0
- package/dist/ui/palettes.js +6 -0
- 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 +38 -0
- package/dist/ui/screen.js +113 -8
- package/dist/ui/selection/highlight.d.ts +39 -0
- package/dist/ui/selection/highlight.js +64 -0
- package/dist/ui/selection/screen-mirror.d.ts +24 -0
- package/dist/ui/selection/screen-mirror.js +35 -0
- package/dist/ui/selection/selection-controller.d.ts +88 -0
- package/dist/ui/selection/selection-controller.js +282 -0
- package/dist/ui/selection/selection.d.ts +90 -0
- package/dist/ui/selection/selection.js +223 -0
- package/dist/ui/stdout-frame-writer.d.ts +33 -0
- package/dist/ui/stdout-frame-writer.js +15 -0
- package/dist/ui/theme.d.ts +11 -0
- package/dist/ui/theme.js +2 -0
- package/dist/ui/transcript-text.js +72 -0
- package/dist/ui/use-startup-notices.d.ts +27 -1
- package/dist/ui/use-startup-notices.js +43 -11
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/dist/ui/use-wheel-routing.js +8 -0
- package/package.json +4 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -42
- package/dist/input/stdin-mouse-filter.js +0 -153
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
* /help /model /settings /thinking /max-tokens /tools /clear /reset /cwd /save
|
|
4
4
|
* /resume /copy /exit (/quit), plus the /team, /fast and /todo subsystems.
|
|
5
5
|
*/
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
6
|
import { existsSync, statSync } from 'node:fs';
|
|
8
7
|
import { isAbsolute, resolve } from 'node:path';
|
|
9
|
-
import process from 'node:process';
|
|
10
8
|
import { resolveOutputTokens } from '@aragon-agent/core';
|
|
11
9
|
import { clampThinkingLevel, clampTheme, isAdapterProvider, isThemeName, parseMaxTokensInput, ADAPTER_PROVIDERS, DEFAULT_MAX_TOKENS, HARD_MAX_RETRIES, LEGACY_DARK_THEME, THEME_NAMES, THINKING_LEVELS, } from '../config/schema.js';
|
|
12
|
-
import { computeCost, formatCost, formatTokens } from '../agent/usage.js';
|
|
10
|
+
import { computeCost, formatCost, formatDuration, formatTokens } from '../agent/usage.js';
|
|
13
11
|
import { TODO_FOLLOW_LIMITS } from '../todo/limits.js';
|
|
14
12
|
import { buildContinuationMessage } from '../todo/follow-through.js';
|
|
15
13
|
import { MODE_LABEL, nextMode } from '../agent/agent-mode.js';
|
|
16
14
|
import { loadConfig } from '../config/load.js';
|
|
17
15
|
import { loadSession, resolveSessionPath, saveSession } from '../session/persist.js';
|
|
18
16
|
import { getLogger } from '../logging/logger.js';
|
|
17
|
+
import { copyText } from '../ui/clipboard.js';
|
|
19
18
|
import { perfCommand } from './perf.js';
|
|
19
|
+
import { runCompactCommand } from '../compaction/command.js';
|
|
20
20
|
/**
|
|
21
21
|
* `/fast status` — THE GUARANTEED REPORTING SURFACE for the tier (§4.4 / R-8).
|
|
22
22
|
*
|
|
@@ -801,8 +801,21 @@ const COMMANDS = [
|
|
|
801
801
|
ctx.notify('warn', `Unknown argument "${verb}" - use /retry [show|on|off|max <n>].`);
|
|
802
802
|
},
|
|
803
803
|
},
|
|
804
|
+
{
|
|
805
|
+
// REGISTRATION ONLY. The body lives in `compaction/command.ts` because this
|
|
806
|
+
// file is already past the 1000-line guideline and `/compact` has six forms
|
|
807
|
+
// plus the longest status report in the package (C-16 / P1-12).
|
|
808
|
+
name: 'compact',
|
|
809
|
+
description: 'Context compaction: status | on | off | threshold <n> | keep <n> | <instructions>',
|
|
810
|
+
run: (ctx) => runCompactCommand(ctx),
|
|
811
|
+
},
|
|
804
812
|
{
|
|
805
813
|
name: 'expand',
|
|
814
|
+
// DELIBERATELY LEFT TOOL-ONLY, and the asymmetry with Ctrl+O is a decision
|
|
815
|
+
// rather than an oversight (§6.3). Its description says "a recent tool card",
|
|
816
|
+
// it takes an N-from-last index over tool cards specifically, and widening it
|
|
817
|
+
// would change what `/expand 3` means for every existing user. Ctrl+O is the
|
|
818
|
+
// general expansion surface — that is where `'compaction'` was added.
|
|
806
819
|
description: 'Expand / collapse a recent tool card (default: last)',
|
|
807
820
|
run: (ctx) => {
|
|
808
821
|
const toolEntries = ctx.state.entries.filter((e) => e.kind === 'tool');
|
|
@@ -942,8 +955,60 @@ const COMMANDS = [
|
|
|
942
955
|
ctx.notify('warn', 'No assistant message to copy.');
|
|
943
956
|
return;
|
|
944
957
|
}
|
|
945
|
-
|
|
946
|
-
|
|
958
|
+
// ROUTED THROUGH `ui/clipboard.ts` (§4.4.5). The duplicate spawn helper this
|
|
959
|
+
// command used to own is gone, and the command gained SSH support on the
|
|
960
|
+
// way: OSC 52 reaches the clipboard of the machine the USER is sitting at,
|
|
961
|
+
// which the local `pbcopy` never could.
|
|
962
|
+
//
|
|
963
|
+
// `ctx.writeForeign` and not `stdout.write` — see the field's comment.
|
|
964
|
+
const via = copyText(last.text, ctx.writeForeign);
|
|
965
|
+
if (via === 'none') {
|
|
966
|
+
ctx.toast('warn', 'Clipboard not available.');
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
// NAMES THE MECHANISM RATHER THAN CLAIMING SUCCESS: neither path is
|
|
970
|
+
// detectable, and tmux without `set -g set-clipboard on` swallows OSC 52
|
|
971
|
+
// silently (R-7).
|
|
972
|
+
ctx.toast('success', via === 'osc52' ? 'Sent to the terminal clipboard.' : 'Sent to the system clipboard.');
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
name: 'mouse',
|
|
977
|
+
description: 'Release the mouse to the terminal, or take it back',
|
|
978
|
+
run: (ctx) => {
|
|
979
|
+
const port = ctx.mouse;
|
|
980
|
+
if (!port) {
|
|
981
|
+
// Inline mode, `--no-mouse`, a non-TTY, or a console that cannot deliver
|
|
982
|
+
// reports. Saying so beats pretending to toggle something that is not on.
|
|
983
|
+
ctx.notify('info', 'The mouse is not captured in this session, so there is nothing to release.');
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
const arg = ctx.args.trim().toLowerCase();
|
|
987
|
+
if (arg === 'off') {
|
|
988
|
+
port.setCapture(false);
|
|
989
|
+
// NAMING THE COST IS THE HONEST THING TO DO, and it is one clause. The
|
|
990
|
+
// wheel goes INERT rather than back to scrolling: alternate scroll is
|
|
991
|
+
// saved and disabled alongside the capture, because otherwise every notch
|
|
992
|
+
// becomes a burst of arrow keys that `PromptInput` reads as prompt-history
|
|
993
|
+
// recall (I-10 / P1-1).
|
|
994
|
+
ctx.toast('info', 'Mouse released to the terminal - the wheel will not scroll. /mouse on gives it back.');
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
if (arg === 'on') {
|
|
998
|
+
port.setCapture(true);
|
|
999
|
+
ctx.toast('info', 'Mouse captured - the wheel scrolls the transcript again.');
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
const captured = port.captured();
|
|
1003
|
+
const lines = [
|
|
1004
|
+
`Mouse capture: ${captured ? 'on' : 'off'}`,
|
|
1005
|
+
` drag-select ${port.selectEnabled() ? 'on' : 'off (mouseSelect is false)'}`,
|
|
1006
|
+
' /mouse off hand the mouse to your terminal for this session',
|
|
1007
|
+
' /mouse on take it back',
|
|
1008
|
+
' --no-mouse never capture it (also ARAGON_MOUSE=0)',
|
|
1009
|
+
' aragon config set mouseSelect false keep the wheel, drop drag-select',
|
|
1010
|
+
];
|
|
1011
|
+
ctx.notify('info', lines.join('\n'));
|
|
947
1012
|
},
|
|
948
1013
|
},
|
|
949
1014
|
{
|
|
@@ -983,6 +1048,95 @@ const COMMANDS = [
|
|
|
983
1048
|
// render layer, and threading `ui/render-cache.js` into this file would make
|
|
984
1049
|
// every other command's import graph pull the renderer in with it.
|
|
985
1050
|
perfCommand,
|
|
1051
|
+
{
|
|
1052
|
+
name: 'bg',
|
|
1053
|
+
description: 'Background services: list | logs <id> [n] | stop <id|all> | status',
|
|
1054
|
+
/**
|
|
1055
|
+
* THE USER'S HANDLE ON WHAT THE AGENT LEFT RUNNING (§5.3).
|
|
1056
|
+
*
|
|
1057
|
+
* It is the LIVE view, and that is why it exists at all: the transcript
|
|
1058
|
+
* records EVENTS about a service (D-11) — a card printed into `<Static>`
|
|
1059
|
+
* cannot be rewritten — so "what is running right now" lives here, in the
|
|
1060
|
+
* status chip, and in `bash_output`. Without this command a user whose
|
|
1061
|
+
* server card scrolled away an hour ago has no way to name it.
|
|
1062
|
+
*/
|
|
1063
|
+
run: async (ctx) => {
|
|
1064
|
+
const arg = ctx.args.trim();
|
|
1065
|
+
const [verb = 'list', value, count] = arg.split(/\s+/);
|
|
1066
|
+
const controller = ctx.controller;
|
|
1067
|
+
if (!controller.isBackgroundRegistered()) {
|
|
1068
|
+
ctx.notify('info', 'Background services are off for this session (bash.background).');
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const services = controller.listServices();
|
|
1072
|
+
if (verb === 'status') {
|
|
1073
|
+
const live = controller.liveServiceCount();
|
|
1074
|
+
ctx.notify('info', live === 0
|
|
1075
|
+
? 'No background services running.'
|
|
1076
|
+
: `${live} background service${live === 1 ? '' : 's'} running.`);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
if (verb === 'logs') {
|
|
1080
|
+
if (!value) {
|
|
1081
|
+
ctx.notify('warn', 'Usage: /bg logs <id> [n]');
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
const service = controller.getServiceSnapshot(value);
|
|
1085
|
+
if (!service) {
|
|
1086
|
+
ctx.notify('warn', `No service "${value}".`);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const page = controller.readServiceLog(value);
|
|
1090
|
+
const n = count ? Number.parseInt(count, 10) : DEFAULT_BG_LOG_ROWS;
|
|
1091
|
+
const rows = page?.rows ?? [];
|
|
1092
|
+
const take = Number.isFinite(n) && n > 0 ? n : DEFAULT_BG_LOG_ROWS;
|
|
1093
|
+
const shown = rows.slice(-take);
|
|
1094
|
+
ctx.notify('info', shown.length === 0
|
|
1095
|
+
? `service ${service.id}: no output yet.`
|
|
1096
|
+
: `service ${service.id} (last ${shown.length} rows)\n${shown.join('\n')}`);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (verb === 'stop') {
|
|
1100
|
+
if (!value) {
|
|
1101
|
+
ctx.notify('warn', 'Usage: /bg stop <id|all>');
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
// AWAITED, unlike `doExit`'s force path: a slash command runs on a live
|
|
1105
|
+
// event loop, so the graceful SIGTERM -> SIGKILL ladder can run and a
|
|
1106
|
+
// dev server gets its chance to release its port cleanly.
|
|
1107
|
+
const stopped = await controller.stopService(value);
|
|
1108
|
+
if (stopped.length === 0) {
|
|
1109
|
+
ctx.notify('info', 'Nothing to stop.');
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const leaked = stopped.filter((svc) => svc.killIncomplete);
|
|
1113
|
+
ctx.toast('success', `Stopped ${stopped.length} service${stopped.length === 1 ? '' : 's'}.`);
|
|
1114
|
+
if (leaked.length > 0) {
|
|
1115
|
+
// AN HONEST WARNING BEATS A SILENT LEAK (R-2 / P2-6). `taskkill /t`
|
|
1116
|
+
// cannot reach a grandchild that re-parented itself, and saying so is
|
|
1117
|
+
// the difference between a puzzle and a `netstat` the user knows to run.
|
|
1118
|
+
ctx.notify('warn', `${leaked.map((svc) => svc.id).join(', ')} may have left a detached child - ` +
|
|
1119
|
+
'check with netstat/lsof if a port is still held.');
|
|
1120
|
+
}
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (verb !== 'list' && verb !== '') {
|
|
1124
|
+
ctx.notify('warn', `Unknown argument "${verb}" - use /bg [list|logs|stop|status].`);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
if (services.length === 0) {
|
|
1128
|
+
ctx.notify('info', 'No background services this session.');
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
const now = Date.now();
|
|
1132
|
+
const lines = services.map((svc) => {
|
|
1133
|
+
const uptime = formatDuration((svc.endedAt ?? now) - svc.startedAt);
|
|
1134
|
+
const where = svc.url ? ` ${svc.url}` : '';
|
|
1135
|
+
return `${svc.id} ${svc.status} ${uptime}${where} $ ${svc.command}`;
|
|
1136
|
+
});
|
|
1137
|
+
ctx.notify('info', `Background services:\n${lines.join('\n')}`);
|
|
1138
|
+
},
|
|
1139
|
+
},
|
|
986
1140
|
{
|
|
987
1141
|
name: 'exit',
|
|
988
1142
|
aliases: ['quit'],
|
|
@@ -990,6 +1144,8 @@ const COMMANDS = [
|
|
|
990
1144
|
run: (ctx) => ctx.exit(),
|
|
991
1145
|
},
|
|
992
1146
|
];
|
|
1147
|
+
/** Rows `/bg logs <id>` shows when the user names no count. */
|
|
1148
|
+
const DEFAULT_BG_LOG_ROWS = 40;
|
|
993
1149
|
export function registerBuiltinCommands(registry) {
|
|
994
1150
|
for (const command of COMMANDS)
|
|
995
1151
|
registry.register(command);
|
|
@@ -1006,22 +1162,7 @@ export const BUILTIN_COMMAND_NAMES = COMMANDS.flatMap((c) => [
|
|
|
1006
1162
|
c.name,
|
|
1007
1163
|
...(c.aliases ?? []),
|
|
1008
1164
|
]).sort();
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
: process.platform === 'darwin'
|
|
1014
|
-
? { file: 'pbcopy', args: [] }
|
|
1015
|
-
: { file: 'xclip', args: ['-selection', 'clipboard'] };
|
|
1016
|
-
try {
|
|
1017
|
-
const child = spawn(cmd.file, cmd.args, { stdio: ['pipe', 'ignore', 'ignore'] });
|
|
1018
|
-
child.on('error', () => {
|
|
1019
|
-
/* swallow — clipboard is best-effort */
|
|
1020
|
-
});
|
|
1021
|
-
child.stdin?.end(text);
|
|
1022
|
-
return true;
|
|
1023
|
-
}
|
|
1024
|
-
catch {
|
|
1025
|
-
return false;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1165
|
+
// The `copyToClipboard` helper that used to live here moved into
|
|
1166
|
+
// `ui/clipboard.ts` (tui-selection-and-scroll-follow §4.4.5), unchanged, so that
|
|
1167
|
+
// `/copy` and the drag-select release share ONE clipboard path instead of two
|
|
1168
|
+
// that can drift.
|
|
@@ -87,6 +87,33 @@ export interface CommandContext {
|
|
|
87
87
|
effective: AgentMode;
|
|
88
88
|
pending: AgentMode | null;
|
|
89
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Mouse capture, for `/mouse` (tui-selection-and-scroll-follow §6.2 / G3).
|
|
92
|
+
*
|
|
93
|
+
* OPTIONAL, and legitimately absent: inline mode, `--no-mouse`, a non-TTY, a
|
|
94
|
+
* Windows console that cannot deliver reports, and every test that builds this
|
|
95
|
+
* context by literal. `/mouse` has to have something honest to say when it is
|
|
96
|
+
* missing, which is the same reason `update` above it is optional.
|
|
97
|
+
*/
|
|
98
|
+
mouse?: MouseCommandPort;
|
|
99
|
+
/**
|
|
100
|
+
* The foreign-write door, for `/copy`'s OSC 52 half (§4.4.5).
|
|
101
|
+
*
|
|
102
|
+
* NOT `process.stdout.write`. In full-screen mode stdout is a `Proxy` in front
|
|
103
|
+
* of the frame differ, and an unrecognised chunk there is counted as a foreign
|
|
104
|
+
* write and prints a diagnostic at the user the first time they copy anything
|
|
105
|
+
* (P1-6). Absent in inline mode and with `--no-diff-render`, where the real
|
|
106
|
+
* stream is handed over directly.
|
|
107
|
+
*/
|
|
108
|
+
writeForeign?: (text: string) => void;
|
|
109
|
+
}
|
|
110
|
+
/** What `/mouse` can see and do. Read at command time, never captured. */
|
|
111
|
+
export interface MouseCommandPort {
|
|
112
|
+
/** Whether the CLI is holding the mouse right now. */
|
|
113
|
+
captured(): boolean;
|
|
114
|
+
/** Whether drag-select is on for this session (`mouseSelect`). */
|
|
115
|
+
selectEnabled(): boolean;
|
|
116
|
+
setCapture(on: boolean): void;
|
|
90
117
|
}
|
|
91
118
|
export interface SlashCommand {
|
|
92
119
|
name: string;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compaction archive - a compaction stops being irreversible
|
|
3
|
+
* (context-auto-compaction-hardening §3.5 / W4).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/compaction/**` is inside the glyph scanner's scope
|
|
6
|
+
* (`glyphs.test.ts::inScope`), and that INCLUDES the field names below and every
|
|
7
|
+
* string `/compact history` and `/compact show` print.
|
|
8
|
+
*
|
|
9
|
+
* WHAT THIS IS FOR. Round 1's non-goal was stated plainly: "the dropped messages
|
|
10
|
+
* are gone". Combined with R-1 - "the summary is wrong or lossy, and the agent
|
|
11
|
+
* proceeds confidently on a false record... this is the feature's defining risk
|
|
12
|
+
* and it cannot be eliminated" - the product was asking the user to accept an
|
|
13
|
+
* unverifiable, unrecoverable transformation of their session. That is the wrong
|
|
14
|
+
* default for a tool that runs unattended. This does not add undo (§12: restoring
|
|
15
|
+
* an over-full history re-creates the condition that triggered compaction). It
|
|
16
|
+
* adds FIDELITY ON DISK, so R-1 becomes something a human can check.
|
|
17
|
+
*
|
|
18
|
+
* NO IMPORTS FROM `ui/`. This module writes files and formats two report blocks;
|
|
19
|
+
* it has no terminal, no theme and no glyphs.
|
|
20
|
+
*/
|
|
21
|
+
import type { Message } from '@aragon-agent/core';
|
|
22
|
+
import type { CompactionMode, CompactionUiTrigger } from './types.js';
|
|
23
|
+
/** Bumped only when the SHAPE changes; the reader rejects anything else. */
|
|
24
|
+
export declare const ARCHIVE_FORMAT_VERSION = 1;
|
|
25
|
+
export interface CompactionArchive {
|
|
26
|
+
version: number;
|
|
27
|
+
/** `COMPACTION_BLOCK_VERSION` at write time, so a bad summary ties to a revision. */
|
|
28
|
+
blockVersion: string;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
/**
|
|
31
|
+
* Which process wrote this.
|
|
32
|
+
*
|
|
33
|
+
* STORED INSIDE THE DOCUMENT as well as in the name, so a file renamed or
|
|
34
|
+
* copied by hand is still self-describing and the reader never has to parse a
|
|
35
|
+
* filename for meaning it cannot verify.
|
|
36
|
+
*/
|
|
37
|
+
runId: string;
|
|
38
|
+
/** 1-based within this run. */
|
|
39
|
+
index: number;
|
|
40
|
+
trigger: CompactionUiTrigger;
|
|
41
|
+
mode: CompactionMode;
|
|
42
|
+
model: string;
|
|
43
|
+
generation: number;
|
|
44
|
+
tokensBefore: number;
|
|
45
|
+
tokensAfter: number;
|
|
46
|
+
messagesBefore: number;
|
|
47
|
+
messagesAfter: number;
|
|
48
|
+
/**
|
|
49
|
+
* THE NUMBER. Deliberately not called `droppedMessages`: that name already
|
|
50
|
+
* means a COUNT in three typed places (`CompactionPlan`, `finish`'s `applied`
|
|
51
|
+
* argument, and `AgentEvent.compaction_end`), and this is the document someone
|
|
52
|
+
* reads while diagnosing a bad summary (RV-3).
|
|
53
|
+
*/
|
|
54
|
+
droppedCount: number;
|
|
55
|
+
summary?: string;
|
|
56
|
+
tailRelief?: {
|
|
57
|
+
messages: number;
|
|
58
|
+
charsRemoved: number;
|
|
59
|
+
};
|
|
60
|
+
/** True when `dropped` was shortened to fit `archiveMaxBytes`. */
|
|
61
|
+
clipped: boolean;
|
|
62
|
+
/** THE ARRAY. Verbatim, oldest first. */
|
|
63
|
+
dropped: Message[];
|
|
64
|
+
}
|
|
65
|
+
/** One row of `/compact history`, already resolved from disk. */
|
|
66
|
+
export interface ArchiveListing {
|
|
67
|
+
entries: ArchiveEntry[];
|
|
68
|
+
/** Archives in the directory belonging to OTHER runs. Counted, never listed. */
|
|
69
|
+
otherRuns: number;
|
|
70
|
+
dir: string;
|
|
71
|
+
}
|
|
72
|
+
export interface ArchiveEntry {
|
|
73
|
+
path: string;
|
|
74
|
+
fileName: string;
|
|
75
|
+
index: number;
|
|
76
|
+
createdAt: number;
|
|
77
|
+
trigger: CompactionUiTrigger;
|
|
78
|
+
mode: CompactionMode;
|
|
79
|
+
model: string;
|
|
80
|
+
tokensBefore: number;
|
|
81
|
+
tokensAfter: number;
|
|
82
|
+
droppedCount: number;
|
|
83
|
+
summary?: string;
|
|
84
|
+
tailRelief?: {
|
|
85
|
+
messages: number;
|
|
86
|
+
charsRemoved: number;
|
|
87
|
+
};
|
|
88
|
+
clipped: boolean;
|
|
89
|
+
}
|
|
90
|
+
export interface ArchiveWriteInput {
|
|
91
|
+
runId: string;
|
|
92
|
+
index: number;
|
|
93
|
+
trigger: CompactionUiTrigger;
|
|
94
|
+
mode: CompactionMode;
|
|
95
|
+
model: string;
|
|
96
|
+
generation: number;
|
|
97
|
+
tokensBefore: number;
|
|
98
|
+
tokensAfter: number;
|
|
99
|
+
messagesBefore: number;
|
|
100
|
+
messagesAfter: number;
|
|
101
|
+
summary?: string;
|
|
102
|
+
tailRelief?: {
|
|
103
|
+
messages: number;
|
|
104
|
+
charsRemoved: number;
|
|
105
|
+
};
|
|
106
|
+
dropped: Message[];
|
|
107
|
+
/** Injected by the tests; production passes nothing. */
|
|
108
|
+
dir?: string;
|
|
109
|
+
now?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `compaction-<YYYY-MM-DD>-<hhmmss>-<runId>-<index>.json`.
|
|
113
|
+
*
|
|
114
|
+
* THE RUN ID IS IN THE NAME BECAUSE THE DIRECTORY IS SHARED (RV-6).
|
|
115
|
+
* `file-sink.ts` makes log retention best-effort precisely because "on Windows
|
|
116
|
+
* another `aragon` process may hold a handle on a file we would like to remove",
|
|
117
|
+
* so concurrent CLI processes are an acknowledged in-tree condition rather than
|
|
118
|
+
* an edge case. Without this segment `/compact history` presents another
|
|
119
|
+
* session's compactions as this one's and `/compact show 2` is ambiguous the
|
|
120
|
+
* moment two runs both reach their second compaction.
|
|
121
|
+
*/
|
|
122
|
+
export declare function archiveFileName(runId: string, index: number, at: Date): string;
|
|
123
|
+
/**
|
|
124
|
+
* Mint this process's archive key.
|
|
125
|
+
*
|
|
126
|
+
* NOT `SessionMeta.id`: that exists only once a session has been SAVED, and the
|
|
127
|
+
* unattended long run this feature serves is exactly the one nobody saved. Short,
|
|
128
|
+
* filename-safe, and only ever compared for equality - a grouping key, not an
|
|
129
|
+
* identity.
|
|
130
|
+
*/
|
|
131
|
+
export declare function mintRunId(pid?: number, now?: number): string;
|
|
132
|
+
/**
|
|
133
|
+
* Serialize, clipping the dropped bodies from the OLDEST end if the document
|
|
134
|
+
* exceeds `archiveMaxBytes`.
|
|
135
|
+
*
|
|
136
|
+
* THE METADATA AND THE SUMMARY ARE NEVER CLIPPED. A truncated archive that still
|
|
137
|
+
* says what happened, when, and with which model beats no archive at all - and
|
|
138
|
+
* the oldest dropped messages are the least likely to be the ones a human came
|
|
139
|
+
* looking for.
|
|
140
|
+
*/
|
|
141
|
+
export declare function serializeArchive(doc: CompactionArchive, maxBytes: number): string;
|
|
142
|
+
/**
|
|
143
|
+
* Write one archive, then prune. Returns the path written, or `null`.
|
|
144
|
+
*
|
|
145
|
+
* NEVER THROWS. The caller runs inside an event handler on the compaction path;
|
|
146
|
+
* a filesystem is not allowed to affect whether a session survives its context
|
|
147
|
+
* window. Every failure is a `warn` with the path and nothing else - a toast for
|
|
148
|
+
* a best-effort audit file trains users to ignore toasts.
|
|
149
|
+
*/
|
|
150
|
+
export declare function writeArchive(input: ArchiveWriteInput): string | null;
|
|
151
|
+
/**
|
|
152
|
+
* Two-tier retention, and the ORDER MATTERS (§3.5.2 / RV-6).
|
|
153
|
+
*
|
|
154
|
+
* 1. WITHIN THIS RUN: keep the newest `archiveMaxFiles` files carrying THIS
|
|
155
|
+
* `runId`. This is the bound that protects the user from their own long
|
|
156
|
+
* session, and it can never touch another run.
|
|
157
|
+
* 2. ACROSS RUNS: delete files older than `archiveMaxAgeMs`, regardless of
|
|
158
|
+
* `runId`, so an abandoned run's archives do not accumulate forever. AGE, not
|
|
159
|
+
* count - a count-based global prune is exactly the rule that deletes a live
|
|
160
|
+
* session's work while it is still running.
|
|
161
|
+
*
|
|
162
|
+
* Best-effort throughout, in the `enforceRetention` shape: every `rmSync` in its
|
|
163
|
+
* own `try`, a locked file skipped rather than fatal.
|
|
164
|
+
*/
|
|
165
|
+
export declare function pruneArchives(runId: string, dir: string, now: number): void;
|
|
166
|
+
/**
|
|
167
|
+
* This run's archives, newest first, plus a COUNT of everyone else's.
|
|
168
|
+
*
|
|
169
|
+
* THE COUNT IS REPORTED RATHER THAN THE FILES, because the alternative is a user
|
|
170
|
+
* concluding their own compactions went missing when the directory holds a
|
|
171
|
+
* neighbour's.
|
|
172
|
+
*/
|
|
173
|
+
export declare function listArchives(runId: string, dirOverride?: string): ArchiveListing;
|
|
174
|
+
/** One archive by its `#n`, or `null`. `n` is the index within THIS run. */
|
|
175
|
+
export declare function findArchive(runId: string, index: number, dirOverride?: string): ArchiveEntry | null;
|