@aragon-agent/cli 0.6.15 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -24
- package/dist/agent/controller.d.ts +10 -0
- package/dist/agent/controller.js +13 -0
- package/dist/cli.js +49 -2
- package/dist/commands/builtins.js +24 -26
- package/dist/compaction/context-command.js +14 -3
- package/dist/compaction/types.d.ts +6 -1
- package/dist/config/app-paths.d.ts +10 -0
- package/dist/config/app-paths.js +12 -0
- package/dist/config/env.d.ts +2 -1
- package/dist/config/env.js +13 -1
- package/dist/config/load.d.ts +9 -0
- package/dist/config/load.js +14 -0
- package/dist/config/model-windows.d.ts +91 -0
- package/dist/config/model-windows.js +181 -0
- package/dist/config/schema.d.ts +17 -0
- package/dist/config/schema.js +1 -0
- package/dist/input/csiu-keys.d.ts +66 -0
- package/dist/input/csiu-keys.js +170 -0
- package/dist/input/keyboard-enhancement.d.ts +42 -0
- package/dist/input/keyboard-enhancement.js +65 -0
- package/dist/input/stdin-filter.d.ts +9 -0
- package/dist/input/stdin-filter.js +45 -9
- package/dist/input/win32-input-mode.d.ts +61 -0
- package/dist/input/win32-input-mode.js +251 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -433,7 +433,7 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
433
433
|
| Key | Action |
|
|
434
434
|
| --- | --- |
|
|
435
435
|
| `Enter` | 空闲时发送,运行时加入队列;收到 Core 已纳入会话的接收凭据后才移除 Queue 提示 |
|
|
436
|
-
| `Ctrl+J` / `Alt+Enter` / `Shift+Enter` |
|
|
436
|
+
| `Ctrl+J` / `Alt+Enter` / `Shift+Enter` | 插入换行。应用默认请求终端区分修饰键(Windows 用 win32-input-mode,macOS/Linux 用 kitty/modifyOtherKeys),Shift+Enter 在支持的终端开箱即用;不支持的终端用 Ctrl+J。参见 [终端设置](#terminal-setup-for-shiftenter) |
|
|
437
437
|
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
|
|
438
438
|
| `Ctrl+P` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing |
|
|
439
439
|
| `Esc` | Close an overlay or popup; otherwise ask for interruption confirmation. While idle, cancel pending auto-continuation |
|
|
@@ -490,10 +490,13 @@ is what it used to do. Inline mode is unaffected either way.
|
|
|
490
490
|
|
|
491
491
|
## Terminal setup for Shift+Enter
|
|
492
492
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
应用现在会主动请求终端区分修饰键:Windows 上推送 win32-input-mode(conhost 与
|
|
494
|
+
Windows Terminal 均支持),macOS/Linux 上推送 kitty disambiguate 与
|
|
495
|
+
`modifyOtherKeys=2`(iTerm2、kitty、ghostty、WezTerm、foot、Alacritty 实现其一),
|
|
496
|
+
并在 stdin 过滤器里把新编码翻译回传统字节,Ink 的按键解析不受影响。上述推送不满足时
|
|
497
|
+
(macOS Terminal.app、旧 Node 下的 conhost、或不支持任何协议的终端)Shift+Enter 仍与
|
|
498
|
+
Enter 同为 CR,此时用 `Ctrl+J` 换行,或按 `/terminal-setup` 给终端加一条让
|
|
499
|
+
Shift+Enter 发送 `\u001b[13;2u` 的绑定(合并进已有配置,不要覆盖其他快捷键)。参考绑定如下:
|
|
497
500
|
|
|
498
501
|
- **Windows Terminal** — `settings.json` 的 `actions` 与 `keybindings` 分别定义动作和按键;完整示例见下文“输入、待处理队列与底部状态区”。
|
|
499
502
|
- **VS Code integrated terminal** — `keybindings.json`:
|
|
@@ -507,10 +510,14 @@ Shift+Enter 与 Enter 都发送为 CR,应用无法区分;请用 `/terminal-s
|
|
|
507
510
|
- **macOS Terminal.app / legacy conhost** — cannot rebind `Enter`; use
|
|
508
511
|
`Ctrl+J` or `Alt+Enter`
|
|
509
512
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
513
|
+
The push and its translation ship as one unit: `input/win32-input-mode.ts` and
|
|
514
|
+
`input/csiu-keys.ts` translate every enhanced encoding back to the legacy
|
|
515
|
+
bytes before Ink parses them, so a translated stream is byte-identical to a
|
|
516
|
+
pre-enhancement one (Enter family excepted: a modified Enter becomes the
|
|
517
|
+
newline frame). Windows additionally requires Node >= 22.17 / >= 24.2 --
|
|
518
|
+
older versions never turn on virtual-terminal input. Opt out with
|
|
519
|
+
`aragon config set keyboardEnhancement false`, `--no-keyboard-enhancement`,
|
|
520
|
+
or `ARAGON_KEYBOARD_ENHANCEMENT=0`.
|
|
514
521
|
|
|
515
522
|
## Queued messages while the agent runs
|
|
516
523
|
|
|
@@ -1353,7 +1360,8 @@ Context
|
|
|
1353
1360
|
```
|
|
1354
1361
|
|
|
1355
1362
|
Two lines are worth reading closely. **`Window`** always names where the number
|
|
1356
|
-
came from — the model API, the model table, your own `contextWindow`,
|
|
1363
|
+
came from — the model API, the model table, your own `contextWindow`, a
|
|
1364
|
+
`model-windows.json` entry, or the invented
|
|
1357
1365
|
placeholder — because those are indistinguishable everywhere else. And
|
|
1358
1366
|
**`Compaction`** reports what is actually true of this session, including
|
|
1359
1367
|
`off - not registered for this session (started with --no-compaction)`; it never
|
|
@@ -1361,8 +1369,9 @@ promises a threshold that nothing will act on.
|
|
|
1361
1369
|
|
|
1362
1370
|
### `contextWindow`
|
|
1363
1371
|
|
|
1364
|
-
Window resolution uses your explicit override first, then
|
|
1365
|
-
|
|
1372
|
+
Window resolution uses your explicit override first, then per-model declarations
|
|
1373
|
+
in `model-windows.json`, then API metadata from the configured endpoint, then the
|
|
1374
|
+
built-in catalog (including known aliases and dated
|
|
1366
1375
|
snapshots). Startup, connection changes, and subsequent prompts refresh discovery
|
|
1367
1376
|
in the background, with a five-minute cache. OpenAI-compatible endpoints may expose
|
|
1368
1377
|
`context_length` or `context_window`; Anthropic uses `max_input_tokens`; Google uses
|
|
@@ -1384,6 +1393,33 @@ Values are clamped to `[8000, 5000000]`; `auto` (the default) is stored as
|
|
|
1384
1393
|
and `/context` still shows that it came from you, which is what makes a wrong
|
|
1385
1394
|
value findable.
|
|
1386
1395
|
|
|
1396
|
+
#### `model-windows.json` — one window per model
|
|
1397
|
+
|
|
1398
|
+
`contextWindow` describes ONE model: the active one. To pin several at once — a
|
|
1399
|
+
main model plus a fast tier plus a gateway alias — declare them by id in
|
|
1400
|
+
`~/.aragon-agent/model-windows.json`:
|
|
1401
|
+
|
|
1402
|
+
```json
|
|
1403
|
+
{
|
|
1404
|
+
"version": 1,
|
|
1405
|
+
"windows": {
|
|
1406
|
+
"kimi-k3": 1048576,
|
|
1407
|
+
"glm-5.3": 1048576,
|
|
1408
|
+
"glm-5.3-flash": 1048576
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
A bare map without the wrapper works too. Keys are matched case-insensitively
|
|
1414
|
+
after stripping a `models/`-style prefix and a dated snapshot suffix, so
|
|
1415
|
+
`Kimi-K3-20260716` still resolves. Entries must be whole tokens between 1024 and
|
|
1416
|
+
1e9; at most 512 are kept. The file is read live (mtime-cached), never written
|
|
1417
|
+
by the CLI, and outranks API discovery and the catalog while still losing to the
|
|
1418
|
+
active model's `contextWindow` override. `/context` reports these windows as
|
|
1419
|
+
`from model-windows.json`, and a file that cannot be parsed is ignored with one
|
|
1420
|
+
log line rather than trusted. UTF-8 (with or without BOM) and UTF-16 saves are
|
|
1421
|
+
both accepted.
|
|
1422
|
+
|
|
1387
1423
|
## Context compaction
|
|
1388
1424
|
|
|
1389
1425
|
A long agentic run fills the model's context window. Before this existed, the
|
|
@@ -2150,6 +2186,8 @@ Everything AragonAgent keeps for you lives under one directory in your home:
|
|
|
2150
2186
|
~/.aragon-agent/ (Windows: C:\Users\<you>\.aragon-agent\)
|
|
2151
2187
|
├── config.json settings + API keys (0600 on POSIX)
|
|
2152
2188
|
├── config.json.bak written by `aragon config edit` before it opens
|
|
2189
|
+
├── model-windows.json per-model context windows you declare by hand;
|
|
2190
|
+
│ read live, never written by the CLI
|
|
2153
2191
|
├── prompt-history.jsonl prompts you submitted, for ↑ recall (0600 on POSIX)
|
|
2154
2192
|
├── state.json UI bookkeeping: submit count, one-shot notices
|
|
2155
2193
|
├── update-state.json auto-update: last check, pending restart, skipped version,
|
|
@@ -2366,7 +2404,9 @@ UTF-16 编辑和 ZWJ/组合字形仍有既有限制,本次修复保证视觉
|
|
|
2366
2404
|
## 输入、待处理队列与底部状态区
|
|
2367
2405
|
|
|
2368
2406
|
Enter 在空闲时发送,运行时加入队列;启动阶段拒绝重复提交并保留草稿。
|
|
2369
|
-
Ctrl+J
|
|
2407
|
+
Ctrl+J 换行;应用默认推送键盘增强模式(Windows 用 win32-input-mode,其余平台用
|
|
2408
|
+
kitty disambiguate + modifyOtherKeys=2)并将新编码翻译回传统字节,因此 Shift+Enter
|
|
2409
|
+
与 Alt+Enter 在支持的终端开箱即用,Ctrl+J 始终可用。
|
|
2370
2410
|
光标按完整字素移动,支持中文、组合字符和 emoji;输入最多占终端高度四分之一且不超过六行。
|
|
2371
2411
|
括号粘贴保留换行,不触发发送。过滤器或粘贴支持关闭时,不保证未标记多行粘贴安全。
|
|
2372
2412
|
过滤器不可用时 Delete 保守按 Backspace 处理,避免退格误删后文。
|
|
@@ -2375,14 +2415,14 @@ Ctrl+J 换行;终端能发送独立序列时,Shift+Enter 与 Alt+Enter 也
|
|
|
2375
2415
|
原生剪贴板工具完成后显示确认,OSC 52 只表示请求已发送,终端可能拒绝。
|
|
2376
2416
|
Shift+拖动或 `/mouse off` 后的选区由宿主管理,应用无法关闭宿主的自动复制。
|
|
2377
2417
|
|
|
2378
|
-
Queue 面板逐条显示待处理消息,数量在展开状态详情中按空间显示。只有 Core 已把消息纳入会话并发回接收凭据,
|
|
2418
|
+
Queue 面板逐条显示待处理消息,数量在展开状态详情中按空间显示。只有 Core 已把消息纳入会话并发回接收凭据,
|
|
2379
2419
|
该条提示才会消失。中断时未接收消息显示暂停;`/queue` 可分页查看全部原文,Esc 关闭。
|
|
2380
2420
|
`/clear` 不丢待处理消息;`/reset`、切换会话会明确报告取消数量;恢复保存的未发送历史不会自动重发。
|
|
2381
2421
|
|
|
2382
|
-
底部依次为固定输入框和一行核心状态;Ctrl+G 展开或收起第二行详情。五个核心字段按显示宽度缩写,始终保留。
|
|
2383
|
-
核心上下文显示当前主 Agent 的百分比;展开详情或 /context 可查看已用/上限 tokens;`~` 表示含估算,`?` 表示上限未知。
|
|
2422
|
+
底部依次为固定输入框和一行核心状态;Ctrl+G 展开或收起第二行详情。五个核心字段按显示宽度缩写,始终保留。
|
|
2423
|
+
核心上下文显示当前主 Agent 的百分比;展开详情或 /context 可查看已用/上限 tokens;`~` 表示含估算,`?` 表示上限未知。
|
|
2384
2424
|
费用是含子代理、快速模型及压缩的会话估算 USD,恢复会话沿用累计,缺价格按零计,零值不保证免费。
|
|
2385
|
-
本轮均速只累计主 Agent 已报告的 output tokens,再除以本轮总耗时(含工具与等待);排除辅助调用,并非瞬时生成速度。结束后保留耗时,速度显示 --。
|
|
2425
|
+
本轮均速只累计主 Agent 已报告的 output tokens,再除以本轮总耗时(含工具与等待);排除辅助调用,并非瞬时生成速度。结束后保留耗时,速度显示 --。
|
|
2386
2426
|
`/context` 提供统计范围和完整值。
|
|
2387
2427
|
|
|
2388
2428
|
Windows Terminal:在设置 JSON **根级**添加 `"copyOnSelect": false`,把以下条目合并到已有数组:
|
|
@@ -2432,10 +2472,10 @@ VS Code 用户设置添加 `"terminal.integrated.copyOnSelection": false`,快
|
|
|
2432
2472
|
|
|
2433
2473
|
配置库仍位于 `ARAGON_HOME` 指向目录中的 `config.json`,不写入会话文件。密钥按现有机制明文保存:POSIX 创建权限为 0600,Windows 依赖用户目录 ACL,不提供加密。`config get/list` 对方案密钥使用固定掩码;损坏或未来版本的配置段整体隐藏。既有 `config edit` 备份也包含凭据。关闭通用日志脱敏、使用短密钥或分享第三方 SDK 错误日志时,仍需自行检查日志内容。
|
|
2434
2474
|
|
|
2435
|
-
### 固定输入与状态详情人工验收
|
|
2436
|
-
|
|
2437
|
-
在 Windows Terminal/PowerShell、VS Code 集成终端以及 macOS/Linux 终端分别检查
|
|
2438
|
-
40x12、80x24、120x40。保留草稿,连续输出思考、正文与工具日志至少 30 秒:
|
|
2439
|
-
输入上下边框不得移动;拖动消息滚动条不会隐藏输入。切换 Ctrl+G 仅改变一行,
|
|
2440
|
-
复制、队列、通知和更新不自动展开。验证补全、Unicode 草稿和光标在切换后保留,
|
|
2441
|
-
弹层期间 Ctrl+G 无效,小屏恢复后草稿与状态模式保留。自动化测试不能替代这些宿主观察。
|
|
2475
|
+
### 固定输入与状态详情人工验收
|
|
2476
|
+
|
|
2477
|
+
在 Windows Terminal/PowerShell、VS Code 集成终端以及 macOS/Linux 终端分别检查
|
|
2478
|
+
40x12、80x24、120x40。保留草稿,连续输出思考、正文与工具日志至少 30 秒:
|
|
2479
|
+
输入上下边框不得移动;拖动消息滚动条不会隐藏输入。切换 Ctrl+G 仅改变一行,
|
|
2480
|
+
复制、队列、通知和更新不自动展开。验证补全、Unicode 草稿和光标在切换后保留,
|
|
2481
|
+
弹层期间 Ctrl+G 无效,小屏恢复后草稿与状态模式保留。自动化测试不能替代这些宿主观察。
|
|
@@ -15,6 +15,7 @@ import type { PromptOptions, PromptOutcome } from './prompt-options.js';
|
|
|
15
15
|
import { ModelRegistry, type AgentEvent, type AgentTool, type Message, type ModelInfo, type ModelRef, type ProviderRegistry, type ThinkingLevel } from '@aragon-agent/core';
|
|
16
16
|
import type { CliConfig, CompactionConfig, FastConfig, RetryConfig, TeamConfig, ThemeName, TodoConfig } from '../config/schema.js';
|
|
17
17
|
import type { ModelRole } from '../config/model-profiles.js';
|
|
18
|
+
import { ModelWindows } from '../config/model-windows.js';
|
|
18
19
|
import { type ConfirmRequest } from '../tools/index.js';
|
|
19
20
|
import type { ToolPermission } from '../exec/permission.js';
|
|
20
21
|
import { type ToolOutputListener } from '../tools/tool-output-store.js';
|
|
@@ -102,6 +103,13 @@ export interface ControllerDeps {
|
|
|
102
103
|
* once at construction and lost at the first `rebuildSystemPrompt()`.
|
|
103
104
|
*/
|
|
104
105
|
appendSystemPrompt?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The per-model window table from ~/.aragon-agent/model-windows.json.
|
|
108
|
+
* Injectable so tests can hand the controller a table without writing into
|
|
109
|
+
* the shared vitest home root (app-paths.ts TEST-ISOLATION CONTRACT);
|
|
110
|
+
* production leaves it absent and the constructor builds the real one.
|
|
111
|
+
*/
|
|
112
|
+
modelWindows?: ModelWindows;
|
|
105
113
|
}
|
|
106
114
|
/** What `setAgentMode` actually adopted — see `AgentController.setAgentMode`. */
|
|
107
115
|
export interface AgentModeState {
|
|
@@ -124,6 +132,8 @@ export declare class AgentController {
|
|
|
124
132
|
private readonly agent;
|
|
125
133
|
private readonly providerRegistry;
|
|
126
134
|
private readonly modelRegistry;
|
|
135
|
+
/** Per-model user-declared context windows; see `config/model-windows.ts`. */
|
|
136
|
+
private readonly modelWindows;
|
|
127
137
|
private modelMetadataEnabled;
|
|
128
138
|
private modelMetadataGeneration;
|
|
129
139
|
private modelMetadataAbort;
|
package/dist/agent/controller.js
CHANGED
|
@@ -7,6 +7,7 @@ import { clampCompactionConfig, clampFastConfig, clampRetryConfig, clampTeamConf
|
|
|
7
7
|
import { resolveModelProfileState } from '../config/model-profile-resolution.js';
|
|
8
8
|
import { makeGetApiKey } from '../config/load.js';
|
|
9
9
|
import { updatePersistedConfig } from '../config/store.js';
|
|
10
|
+
import { ModelWindows } from '../config/model-windows.js';
|
|
10
11
|
import { createBuiltinTools } from '../tools/index.js';
|
|
11
12
|
import { createFileChangeStore } from '../tools/file-change-store.js';
|
|
12
13
|
import { createToolOutputStore, } from '../tools/tool-output-store.js';
|
|
@@ -53,6 +54,8 @@ export class AgentController {
|
|
|
53
54
|
agent;
|
|
54
55
|
providerRegistry;
|
|
55
56
|
modelRegistry;
|
|
57
|
+
/** Per-model user-declared context windows; see `config/model-windows.ts`. */
|
|
58
|
+
modelWindows;
|
|
56
59
|
modelMetadataEnabled = false;
|
|
57
60
|
modelMetadataGeneration = 0;
|
|
58
61
|
modelMetadataAbort = new AbortController();
|
|
@@ -275,6 +278,7 @@ export class AgentController {
|
|
|
275
278
|
// reuses this very registry instance.
|
|
276
279
|
this.providerRegistry = initProviders({ retryPolicy: toRetryPolicy(config.retry) });
|
|
277
280
|
this.modelRegistry = new ModelRegistry(this.providerRegistry);
|
|
281
|
+
this.modelWindows = deps.modelWindows ?? new ModelWindows();
|
|
278
282
|
this.skillsEnabled = config.skills.enabled;
|
|
279
283
|
this.onSkillsChanged = deps.onSkillsChanged;
|
|
280
284
|
this.skills = new SkillService({
|
|
@@ -1737,9 +1741,18 @@ export class AgentController {
|
|
|
1737
1741
|
const found = this.modelRegistry.getModel(ref.providerId, ref.modelId);
|
|
1738
1742
|
const baseUrl = ref.baseUrl ?? (ref.providerId === this.config.provider && ref.modelId === this.config.model
|
|
1739
1743
|
? this.config.baseUrl : undefined);
|
|
1744
|
+
const userWindow = this.modelWindows.lookup(ref.modelId);
|
|
1740
1745
|
return {
|
|
1741
1746
|
...(found ?? this.modelRegistry.buildRuntimeModel(ref.providerId, ref.modelId)),
|
|
1742
1747
|
...this.modelRegistry.getContextWindow(ref.providerId, ref.modelId, baseUrl),
|
|
1748
|
+
// A USER-DECLARED WINDOW OUTRANKS EVERY TABLE (model-windows.json): the
|
|
1749
|
+
// user asserted the number for exactly this id, while api/catalog are
|
|
1750
|
+
// what this process managed to look up about it. It still LOSES to
|
|
1751
|
+
// config.json's `contextWindow`, which `ContextMeter.resolveWindow`
|
|
1752
|
+
// consults before it ever reaches here.
|
|
1753
|
+
...(userWindow !== undefined
|
|
1754
|
+
? { contextWindow: userWindow, contextWindowSource: 'user' }
|
|
1755
|
+
: {}),
|
|
1743
1756
|
};
|
|
1744
1757
|
}
|
|
1745
1758
|
/** Best-effort metadata lookup; never block typing or a model request. */
|
package/dist/cli.js
CHANGED
|
@@ -46,6 +46,7 @@ import { createFrameDiffer } from './ui/frame-differ.js';
|
|
|
46
46
|
import { wrapStdoutForFrames } from './ui/stdout-frame-writer.js';
|
|
47
47
|
import { setFrameStatsProvider } from './commands/perf.js';
|
|
48
48
|
import { tryCreateStdinFilter } from './input/stdin-filter.js';
|
|
49
|
+
import { enableKeyboardEnhancement, NOOP_KEYBOARD_ENHANCEMENT, } from './input/keyboard-enhancement.js';
|
|
49
50
|
import { createScrollbarBridge } from './ui/scrollbar-controller.js';
|
|
50
51
|
import { createPointerRouter } from './input/pointer-router.js';
|
|
51
52
|
import { createFrameObserver } from './ui/frame-observer.js';
|
|
@@ -120,6 +121,10 @@ function toFlags(opts) {
|
|
|
120
121
|
// `CliFlags`, AND a line here; `config.test.ts` is the regression pin that
|
|
121
122
|
// exists because `--no-mouse` shipped inert once already.
|
|
122
123
|
paste: opts.paste,
|
|
124
|
+
// Site three of three for `--keyboard-enhancement` /
|
|
125
|
+
// `--no-keyboard-enhancement`, for the reason `paste` one line up
|
|
126
|
+
// records: the key is persisted and defaults to true.
|
|
127
|
+
keyboardEnhancement: opts.keyboardEnhancement,
|
|
123
128
|
skills: opts.skills,
|
|
124
129
|
skill: opts.skill,
|
|
125
130
|
skillsYes: opts.skillsYes,
|
|
@@ -465,8 +470,22 @@ function runInteractive(flags, extras = {}) {
|
|
|
465
470
|
// advising the user about a mode they just disabled.
|
|
466
471
|
const wantPaste = config.paste && !!process.stdin.isTTY;
|
|
467
472
|
const pasteBridge = { notify: null };
|
|
473
|
+
// --- Keyboard enhancement (Shift+Enter newline). --------------------------
|
|
474
|
+
//
|
|
475
|
+
// THE THIRD STREAM GATE, AND THE SAME DISCIPLINE AS THE OTHER TWO. On
|
|
476
|
+
// win32 the enhancement rides the SAME `vtInputSupported` measurement
|
|
477
|
+
// the mouse does: Node < 22.17 / < 24.2 never turns on
|
|
478
|
+
// `ENABLE_VIRTUAL_TERMINAL_INPUT`, the win32-input-mode records cannot
|
|
479
|
+
// reach the process, and pushing `?9001h` there would be a mode nobody
|
|
480
|
+
// can observe -- the exact gap the mouse gate closes for `?1000h`.
|
|
481
|
+
// Elsewhere the push is kitty disambiguate + modifyOtherKeys, which a
|
|
482
|
+
// terminal that implements neither ignores (verified byte-identical on
|
|
483
|
+
// conhost), so no capability gate is possible or needed there.
|
|
484
|
+
const wantEnhancedKeys = config.keyboardEnhancement &&
|
|
485
|
+
!!process.stdin.isTTY &&
|
|
486
|
+
(process.platform !== 'win32' || vtInputSupported);
|
|
468
487
|
const filter = process.stdin.isTTY
|
|
469
|
-
? tryCreateStdinFilter(process.stdin, { mouse: wantMouse, paste: wantPaste }, (reason) => logger.warn('cli', 'stdin_filter_failed', { reason }), pasteBridge)
|
|
488
|
+
? tryCreateStdinFilter(process.stdin, { mouse: wantMouse, paste: wantPaste, enhancedKeys: wantEnhancedKeys }, (reason) => logger.warn('cli', 'stdin_filter_failed', { reason }), pasteBridge)
|
|
470
489
|
: null;
|
|
471
490
|
/**
|
|
472
491
|
* THE ONE EXPRESSION EVERY MOUSE SITE READS (I-11 / D-17).
|
|
@@ -478,6 +497,8 @@ function runInteractive(flags, extras = {}) {
|
|
|
478
497
|
const mouseOn = wantMouse && filter !== null;
|
|
479
498
|
/** Same discipline for DEC 2004: already decided here, never re-derived. */
|
|
480
499
|
const pasteOn = wantPaste && filter !== null;
|
|
500
|
+
/** Same discipline: the translation runs only when a filter fronts Ink. */
|
|
501
|
+
const enhancedKeysOn = wantEnhancedKeys && filter !== null;
|
|
481
502
|
const disposeStdinFilter = () => filter?.dispose();
|
|
482
503
|
// --- Drag-select (tui-selection-and-scroll-follow §4.4). ------------------
|
|
483
504
|
//
|
|
@@ -565,6 +586,16 @@ function runInteractive(flags, extras = {}) {
|
|
|
565
586
|
...(snapshot.droppedEntries ? { droppedEntries: snapshot.droppedEntries } : {}),
|
|
566
587
|
}));
|
|
567
588
|
};
|
|
589
|
+
// AFTER the filter, BEFORE the render: the enhanced encodings this mode
|
|
590
|
+
// turns on must never reach Ink un-translated, and the ordering invariant
|
|
591
|
+
// `stdin-filter.ts` states for `?1000h` / `?2004h` covers `?9001h`
|
|
592
|
+
// and the CSI-u pushes identically.
|
|
593
|
+
const keyboardEnhancement = enhancedKeysOn
|
|
594
|
+
? enableKeyboardEnhancement(process.stdout, {
|
|
595
|
+
platform: process.platform,
|
|
596
|
+
vtInputSupported,
|
|
597
|
+
})
|
|
598
|
+
: NOOP_KEYBOARD_ENHANCEMENT;
|
|
568
599
|
{
|
|
569
600
|
// `mouse` here means "a mouse-parsing filter is installed", NOT "the user
|
|
570
601
|
// wants mouse support" — see `AltScreenOptions`. `screen.ts` must never
|
|
@@ -581,7 +612,14 @@ function runInteractive(flags, extras = {}) {
|
|
|
581
612
|
motion: mouseOn,
|
|
582
613
|
bracketedPaste: pasteOn,
|
|
583
614
|
});
|
|
584
|
-
|
|
615
|
+
// Input-encoding modes unwind FIRST, for the same reason the mouse
|
|
616
|
+
// reporting does inside `screen.restore()`: the terminal must stop
|
|
617
|
+
// sending enhanced encodings before anything else tears the reader
|
|
618
|
+
// down, or a keystroke taken in that instant survives into the shell.
|
|
619
|
+
const restore = () => {
|
|
620
|
+
keyboardEnhancement.restore();
|
|
621
|
+
screen?.restore();
|
|
622
|
+
};
|
|
585
623
|
// Four idempotent restore paths (§4.4). The signal hook is NOT redundant:
|
|
586
624
|
// - `waitUntilExit().then()` is a microtask and may never be reached when
|
|
587
625
|
// the process is killed;
|
|
@@ -948,6 +986,9 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
948
986
|
// `mouse` records: a key present here but absent there falls through every
|
|
949
987
|
// case, writes nothing, and still prints `Set paste = false`.
|
|
950
988
|
'paste',
|
|
989
|
+
// Listed here AND cased in the switch below, for the reason the note above
|
|
990
|
+
// `paste` records.
|
|
991
|
+
'keyboardEnhancement',
|
|
951
992
|
'scrollResumeMs',
|
|
952
993
|
'historyEnabled',
|
|
953
994
|
'planModeDefault',
|
|
@@ -1187,6 +1228,9 @@ function runConfigSetValue(key, value) {
|
|
|
1187
1228
|
case 'paste':
|
|
1188
1229
|
patch.paste = value === 'true' || value === '1';
|
|
1189
1230
|
break;
|
|
1231
|
+
case 'keyboardEnhancement':
|
|
1232
|
+
patch.keyboardEnhancement = value === 'true' || value === '1';
|
|
1233
|
+
break;
|
|
1190
1234
|
// Clamped rather than rejected, for the same reason `planModeDefault` below
|
|
1191
1235
|
// records: hardening only the READ path leaves a bad value on disk that
|
|
1192
1236
|
// reverts to the default on every launch, which presents as "my setting
|
|
@@ -1451,6 +1495,9 @@ function buildProgram() {
|
|
|
1451
1495
|
// silence, and silently overriding a stored `false` on every run.
|
|
1452
1496
|
.option('--paste', 'Collapse large pastes into a placeholder (the default)')
|
|
1453
1497
|
.option('--no-paste', 'Treat pasted bytes as keystrokes (pre-0.6.3 behavior)')
|
|
1498
|
+
// Same persisted-defaults-true trap as `--paste` above.
|
|
1499
|
+
.option('--keyboard-enhancement', 'Make Shift+Enter insert a newline (the default where the terminal can)')
|
|
1500
|
+
.option('--no-keyboard-enhancement', 'Never push keyboard-enhancement modes (Shift+Enter then needs a terminal binding)')
|
|
1454
1501
|
.option('--no-exit-transcript', 'Do not replay the session summary after exiting')
|
|
1455
1502
|
.option('--plan', 'Start the session in PLAN mode (read-only research + review)')
|
|
1456
1503
|
.option('--no-plan', 'Start the session in BUILD mode (overrides planModeDefault)')
|
|
@@ -1054,55 +1054,53 @@ const COMMANDS = [
|
|
|
1054
1054
|
},
|
|
1055
1055
|
{
|
|
1056
1056
|
name: 'terminal-setup',
|
|
1057
|
-
description: 'How
|
|
1057
|
+
description: 'How Shift+Enter sends a newline in your terminal',
|
|
1058
1058
|
run: (ctx) => {
|
|
1059
|
-
//
|
|
1060
|
-
//
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1065
|
-
//
|
|
1066
|
-
// either. The lines below are for the terminals that send a plain
|
|
1067
|
-
// CR for Shift+Enter and CAN be configured.
|
|
1059
|
+
// THE APP NOW PUSHES THE MODES ITSELF (shift-enter-newline fix):
|
|
1060
|
+
// win32-input-mode on Windows, kitty disambiguate + modifyOtherKeys=2
|
|
1061
|
+
// elsewhere, each translated back to legacy bytes before Ink parses
|
|
1062
|
+
// them. The lines below cover the terminals where pushing is not
|
|
1063
|
+
// enough: macOS Terminal.app and conhost under an old Node publish no
|
|
1064
|
+
// keyboard-enhancement protocol at all, and a terminal that ignores
|
|
1065
|
+
// the push still sends a plain CR for Shift+Enter.
|
|
1068
1066
|
const lines = [
|
|
1069
1067
|
'Shift+Enter newline - terminal setup',
|
|
1070
1068
|
'',
|
|
1071
|
-
'
|
|
1072
|
-
'
|
|
1073
|
-
'
|
|
1069
|
+
'This app asks the terminal to report modified keys itself:',
|
|
1070
|
+
' Windows: win32-input-mode (conhost and Windows Terminal)',
|
|
1071
|
+
' macOS/Linux: kitty disambiguate + modifyOtherKeys (iTerm2,',
|
|
1072
|
+
' kitty, ghostty, WezTerm, foot, Alacritty)',
|
|
1073
|
+
'A terminal that implements none of these sends a plain CR for',
|
|
1074
|
+
'Shift+Enter, indistinguishable from Enter. There Ctrl+J inserts a',
|
|
1075
|
+
'newline with no setup, and Alt+Enter may also work.',
|
|
1076
|
+
'',
|
|
1077
|
+
'If your terminal can be configured, make Shift+Enter send the',
|
|
1078
|
+
'CSI-u sequence \\u001b[13;2u and it works even without the push:',
|
|
1074
1079
|
'',
|
|
1075
1080
|
' Windows Terminal settings.json, merge root-level arrays:',
|
|
1076
1081
|
' "copyOnSelect": false,',
|
|
1077
1082
|
' "actions": [{ "id": "User.AragonNewline", "command":',
|
|
1078
1083
|
' { "action": "sendInput", "input": "\\u001b[13;2u" } }],',
|
|
1079
1084
|
' "keybindings": [{ "keys": "shift+enter", "id": "User.AragonNewline" }]',
|
|
1080
|
-
' PowerShell runs inside the terminal; configure this binding in',
|
|
1081
|
-
' Windows Terminal, not in the PowerShell profile. If both keys',
|
|
1082
|
-
' send CR, the application cannot distinguish Shift+Enter from Enter.',
|
|
1083
1085
|
' VS Code terminal keybindings.json:',
|
|
1084
1086
|
' { "key": "shift+enter",',
|
|
1085
1087
|
' "command": "workbench.action.terminal.sendSequence",',
|
|
1086
1088
|
' "when": "terminalFocus",',
|
|
1087
1089
|
' "args": { "text": "\\u001b[13;2u" } }',
|
|
1088
|
-
' User settings: "terminal.integrated.copyOnSelection": false',
|
|
1089
1090
|
' iTerm2 Settings > Profiles > Keys > Key Mappings:',
|
|
1090
1091
|
' Shift+Enter -> Send Escape Sequence: [13;2u',
|
|
1091
1092
|
' kitty/WezTerm/foot native once keyboard enhancement is on',
|
|
1092
1093
|
' (kitty: map shift+enter send_text all \\u001b[13;2u)',
|
|
1093
1094
|
' Alacritty alacritty.toml, [keyboard] bindings:',
|
|
1094
1095
|
' { key = "Enter", mods = "Shift", chars = "\\u001b[13;2u" }',
|
|
1095
|
-
' macOS Terminal.app / conhost cannot rebind Enter; use Ctrl+J',
|
|
1096
|
-
' or Alt+Enter',
|
|
1096
|
+
' macOS Terminal.app / old conhost cannot rebind Enter; use Ctrl+J',
|
|
1097
1097
|
'',
|
|
1098
|
+
'Windows needs Node >= 22.17 (or >= 24.2): older versions never',
|
|
1099
|
+
'turn on virtual-terminal input, so no report can arrive.',
|
|
1100
|
+
'Opt out of the push entirely:',
|
|
1101
|
+
' aragon config set keyboardEnhancement false',
|
|
1102
|
+
' --no-keyboard-enhancement / ARAGON_KEYBOARD_ENHANCEMENT=0',
|
|
1098
1103
|
'This command only prints advice; it changes no terminal modes.',
|
|
1099
|
-
'Host selections (Shift+drag or /mouse off) belong to the terminal.',
|
|
1100
|
-
'App selections wait for Ctrl+C; only a native tool exit confirms copying.',
|
|
1101
|
-
'SSH uses OSC 52: the terminal may deny it; sent does not mean confirmed.',
|
|
1102
|
-
'Without the stdin filter, Delete falls back to Backspace.',
|
|
1103
|
-
'Without paste support, multiline paste cannot be guaranteed safe.',
|
|
1104
|
-
'IME confirmation has no composition signal in PTY bytes. If it emits CR,',
|
|
1105
|
-
'edit externally and use bracketed paste; this host combination needs validation.',
|
|
1106
1104
|
];
|
|
1107
1105
|
ctx.notify('info', lines.join('\n'));
|
|
1108
1106
|
},
|
|
@@ -74,7 +74,9 @@ function occupancyLine(usage) {
|
|
|
74
74
|
return `${pct} ${formatTokens(usage.occupied)} of ${formatTokens(usage.window)} ${breakdown}`;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* Where the denominator came from.
|
|
77
|
+
* Where the denominator came from. FOUR SOURCES, ALL NAMED - the fourth
|
|
78
|
+
* being the user's own model-windows.json, which exists precisely because
|
|
79
|
+
* the other three cannot know a gateway alias released last week.
|
|
78
80
|
*
|
|
79
81
|
* THE PLACEHOLDER BRANCH IS THE ONE THAT MATTERS. `buildRuntimeModel` returns a
|
|
80
82
|
* flat 128000 for any model the static table has never seen - a custom
|
|
@@ -85,11 +87,20 @@ function occupancyLine(usage) {
|
|
|
85
87
|
*/
|
|
86
88
|
function windowLine(usage) {
|
|
87
89
|
if (usage.windowOverridden) {
|
|
88
|
-
return `${usage.window} from contextWindow configuration (overrides API and model
|
|
90
|
+
return `${usage.window} from contextWindow configuration (overrides API, model table, and model-windows.json)`;
|
|
89
91
|
}
|
|
90
92
|
if (!usage.windowKnown) {
|
|
91
93
|
return (`UNKNOWN - no verified model limit; internal fallback budget ${usage.window}; ` +
|
|
92
|
-
'set contextWindow to correct it');
|
|
94
|
+
'set contextWindow or list the model in model-windows.json to correct it');
|
|
95
|
+
}
|
|
96
|
+
if (usage.windowSource === 'user') {
|
|
97
|
+
return `${usage.window} from model-windows.json (your per-model table)`;
|
|
98
|
+
}
|
|
99
|
+
if (usage.windowSource === undefined) {
|
|
100
|
+
// A child agent can be windowKnown through the pricing-table heuristic
|
|
101
|
+
// without naming a source; claiming 'the model table' there would be an
|
|
102
|
+
// evidence-free assertion in the one report that exists to be checked.
|
|
103
|
+
return `${usage.window} source unverified`;
|
|
93
104
|
}
|
|
94
105
|
return `${usage.window} from ${usage.windowSource === 'api' ? 'the model API' : 'the model table'}`;
|
|
95
106
|
}
|
|
@@ -65,7 +65,12 @@ export interface CompactionDecision {
|
|
|
65
65
|
deltaTokens: number;
|
|
66
66
|
}
|
|
67
67
|
export interface Pressure {
|
|
68
|
-
|
|
68
|
+
/**
|
|
69
|
+
* `user` means the CLI's model-windows.json supplied the denominator - a
|
|
70
|
+
* hand-typed window for a model no table knows. It outranks `api` and
|
|
71
|
+
* `catalog` in `getModelInfoFor` precisely because the user asserted it.
|
|
72
|
+
*/
|
|
73
|
+
windowSource?: 'api' | 'catalog' | 'fallback' | 'user';
|
|
69
74
|
occupied: number;
|
|
70
75
|
contextWindow: number;
|
|
71
76
|
/** `occupied / contextWindow`, clamped to [0, 1]. */
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* ~/.aragon-agent/
|
|
13
13
|
* ├── config.json persisted config (0600 on POSIX)
|
|
14
14
|
* ├── config.json.bak `aragon config edit` pre-edit backup
|
|
15
|
+
* ├── model-windows.json per-model context windows, user-edited
|
|
15
16
|
* ├── prompt-history.jsonl submitted prompts, append-only (0600 on POSIX)
|
|
16
17
|
* ├── state.json UI bookkeeping scalars (0600 on POSIX)
|
|
17
18
|
* ├── update-state.json auto-update coordination (0600 on POSIX)
|
|
@@ -72,6 +73,15 @@ export declare function getHomeRoot(): string;
|
|
|
72
73
|
export declare function getConfigPath(): string;
|
|
73
74
|
/** Written by `aragon config edit` before spawning the editor. */
|
|
74
75
|
export declare function getConfigBackupPath(): string;
|
|
76
|
+
/**
|
|
77
|
+
* `<home>/model-windows.json` — per-model context windows the user declares by
|
|
78
|
+
* hand for ids no table knows (`kimi-k3`, a gateway alias, a self-hosted model).
|
|
79
|
+
*
|
|
80
|
+
* READ-ONLY from the application's side: nothing ever writes or creates it, so
|
|
81
|
+
* a user's file cannot be clobbered by an upgrade and its absence is not an
|
|
82
|
+
* error. `config/model-windows.ts` owns the format and the lookup.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getModelWindowsPath(): string;
|
|
75
85
|
/**
|
|
76
86
|
* `<home>/prompt-history.jsonl` — the prompts the user submitted, one JSON
|
|
77
87
|
* object per line. Deliberately NOT under `logs/`: that directory is governed
|
package/dist/config/app-paths.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* ~/.aragon-agent/
|
|
13
13
|
* ├── config.json persisted config (0600 on POSIX)
|
|
14
14
|
* ├── config.json.bak `aragon config edit` pre-edit backup
|
|
15
|
+
* ├── model-windows.json per-model context windows, user-edited
|
|
15
16
|
* ├── prompt-history.jsonl submitted prompts, append-only (0600 on POSIX)
|
|
16
17
|
* ├── state.json UI bookkeeping scalars (0600 on POSIX)
|
|
17
18
|
* ├── update-state.json auto-update coordination (0600 on POSIX)
|
|
@@ -129,6 +130,17 @@ export function getConfigPath() {
|
|
|
129
130
|
export function getConfigBackupPath() {
|
|
130
131
|
return join(resolution.root, 'config.json.bak');
|
|
131
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* `<home>/model-windows.json` — per-model context windows the user declares by
|
|
135
|
+
* hand for ids no table knows (`kimi-k3`, a gateway alias, a self-hosted model).
|
|
136
|
+
*
|
|
137
|
+
* READ-ONLY from the application's side: nothing ever writes or creates it, so
|
|
138
|
+
* a user's file cannot be clobbered by an upgrade and its absence is not an
|
|
139
|
+
* error. `config/model-windows.ts` owns the format and the lookup.
|
|
140
|
+
*/
|
|
141
|
+
export function getModelWindowsPath() {
|
|
142
|
+
return join(resolution.root, 'model-windows.json');
|
|
143
|
+
}
|
|
132
144
|
/**
|
|
133
145
|
* `<home>/prompt-history.jsonl` — the prompts the user submitted, one JSON
|
|
134
146
|
* object per line. Deliberately NOT under `logs/`: that directory is governed
|
package/dist/config/env.d.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
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_PASTE,
|
|
10
|
+
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PASTE, ARAGON_KEYBOARD_ENHANCEMENT,
|
|
11
|
+
* ARAGON_PLAN, ARAGON_TEAM,
|
|
11
12
|
* ARAGON_TEAM_MAX, ARAGON_RETRY, ARAGON_RETRY_MAX, ARAGON_FAST,
|
|
12
13
|
* ARAGON_FAST_PROVIDER, ARAGON_FAST_MODEL, ARAGON_FAST_BASE_URL,
|
|
13
14
|
* ARAGON_LOG_LEVEL,
|
package/dist/config/env.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
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_PASTE,
|
|
10
|
+
* ARAGON_THEME, ARAGON_MOUSE, ARAGON_PASTE, ARAGON_KEYBOARD_ENHANCEMENT,
|
|
11
|
+
* ARAGON_PLAN, ARAGON_TEAM,
|
|
11
12
|
* ARAGON_TEAM_MAX, ARAGON_RETRY, ARAGON_RETRY_MAX, ARAGON_FAST,
|
|
12
13
|
* ARAGON_FAST_PROVIDER, ARAGON_FAST_MODEL, ARAGON_FAST_BASE_URL,
|
|
13
14
|
* ARAGON_LOG_LEVEL,
|
|
@@ -151,6 +152,17 @@ export function readEnvConfig() {
|
|
|
151
152
|
if (paste !== undefined && paste.length > 0) {
|
|
152
153
|
partial.paste = paste === '1' || paste === 'true' || paste === 'on' || paste === 'yes';
|
|
153
154
|
}
|
|
155
|
+
// ARAGON_KEYBOARD_ENHANCEMENT=0 stops the Shift+Enter mode push and its
|
|
156
|
+
// translation -- the one-env revert to pre-enhancement input handling.
|
|
157
|
+
// Parsed by the POSITIVE list, exactly as its three neighbours above are.
|
|
158
|
+
const keyboardEnhancement = process.env.ARAGON_KEYBOARD_ENHANCEMENT?.trim().toLowerCase();
|
|
159
|
+
if (keyboardEnhancement !== undefined && keyboardEnhancement.length > 0) {
|
|
160
|
+
partial.keyboardEnhancement =
|
|
161
|
+
keyboardEnhancement === '1' ||
|
|
162
|
+
keyboardEnhancement === 'true' ||
|
|
163
|
+
keyboardEnhancement === 'on' ||
|
|
164
|
+
keyboardEnhancement === 'yes';
|
|
165
|
+
}
|
|
154
166
|
// ARAGON_SCROLL_RESUME_MS=<n> — idle delay before a paused viewport returns to
|
|
155
167
|
// the newest line; `0` disables auto-resume. THE ONLY CHANNEL BESIDES THE
|
|
156
168
|
// CONFIG FILE, because §6.1 gives this key no CLI flag on purpose. `load.ts`
|
package/dist/config/load.d.ts
CHANGED
|
@@ -59,6 +59,15 @@ export interface CliFlags {
|
|
|
59
59
|
* `false` on every run that passed no flag at all.
|
|
60
60
|
*/
|
|
61
61
|
paste?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* `--keyboard-enhancement` ⇒ true, `--no-keyboard-enhancement` ⇒ false,
|
|
64
|
+
* absent ⇒ undefined.
|
|
65
|
+
*
|
|
66
|
+
* DECLARED AS A PAIR and carried through `toFlags`, for the reason the
|
|
67
|
+
* `paste` note above records: the key is persisted and defaults to
|
|
68
|
+
* `true`, so a lone negative flag must not read as silence.
|
|
69
|
+
*/
|
|
70
|
+
keyboardEnhancement?: boolean;
|
|
62
71
|
/** `--no-skills` ⇒ false. Turns the whole skill subsystem off (§7.5). */
|
|
63
72
|
skills?: boolean;
|
|
64
73
|
/** `--skill <name>` ×N — force Level 2 injection for this run. */
|
package/dist/config/load.js
CHANGED
|
@@ -78,6 +78,19 @@ function resolvePaste(flags, env, file) {
|
|
|
78
78
|
return file.paste;
|
|
79
79
|
return DEFAULT_CONFIG.paste;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Keyboard enhancement: flag > env > file > default `true`. Shaped exactly
|
|
83
|
+
* like `resolvePaste` above, for the reason its own note gives.
|
|
84
|
+
*/
|
|
85
|
+
function resolveKeyboardEnhancement(flags, env, file) {
|
|
86
|
+
if (flags.keyboardEnhancement !== undefined)
|
|
87
|
+
return flags.keyboardEnhancement;
|
|
88
|
+
if (env.keyboardEnhancement !== undefined)
|
|
89
|
+
return env.keyboardEnhancement;
|
|
90
|
+
if (file.keyboardEnhancement !== undefined)
|
|
91
|
+
return file.keyboardEnhancement;
|
|
92
|
+
return DEFAULT_CONFIG.keyboardEnhancement;
|
|
93
|
+
}
|
|
81
94
|
/**
|
|
82
95
|
* Idle scroll-resume delay: env › file › default 5000, clamped to [0, 120000].
|
|
83
96
|
*
|
|
@@ -585,6 +598,7 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
585
598
|
mouse: resolveMouse(flags, env.partial, file),
|
|
586
599
|
mouseSelect: resolveMouseSelect(flags, env.partial, file),
|
|
587
600
|
paste: resolvePaste(flags, env.partial, file),
|
|
601
|
+
keyboardEnhancement: resolveKeyboardEnhancement(flags, env.partial, file),
|
|
588
602
|
scrollResumeMs: resolveScrollResumeMs(env.partial, file),
|
|
589
603
|
// From `<home>/state.json`, not the config file — see `ui-state.ts`.
|
|
590
604
|
submitCount: readSubmitCount(),
|