@gavin-hu/my-pi-agent 0.13.1 → 0.14.0
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 +14 -11
- package/README.zh-CN.md +12 -12
- package/extensions/bytefly/README.md +140 -58
- package/extensions/bytefly/doctor/config.ts +4 -1
- package/extensions/bytefly/footer/price/config.ts +3 -2
- package/extensions/bytefly/header/component.ts +30 -8
- package/extensions/bytefly/header/config.ts +22 -0
- package/extensions/bytefly/header/register.ts +2 -1
- package/extensions/bytefly/index.ts +3 -3
- package/extensions/bytefly/notify/config.ts +1 -1
- package/extensions/bytefly/panel/commands.ts +1 -1
- package/extensions/bytefly/panel/format.ts +12 -19
- package/extensions/bytefly/panel/settings-runtime.ts +62 -18
- package/extensions/bytefly/panel/settings.ts +239 -43
- package/extensions/bytefly/panel/tui/settings-view.ts +49 -3
- package/extensions/bytefly/panel/tui.ts +87 -19
- package/extensions/bytefly/separator/component.ts +7 -6
- package/extensions/bytefly/separator/config.ts +15 -0
- package/extensions/bytefly/separator/format.ts +60 -6
- package/extensions/bytefly/separator/register.ts +68 -9
- package/extensions/bytefly/separator/types.ts +8 -0
- package/extensions/bytefly/working/config.ts +4 -5
- package/extensions/bytefly/working/format.ts +24 -7
- package/extensions/bytefly/working/register.ts +34 -3
- package/extensions/memory/README.md +2 -0
- package/extensions/plan/README.md +12 -0
- package/extensions/subagent/README.md +60 -5
- package/extensions/subagent/config.ts +58 -0
- package/extensions/subagent/index.ts +27 -1
- package/extensions/subagent/orchestrate.ts +16 -3
- package/extensions/subagent/render.ts +12 -0
- package/extensions/subagent/run.ts +100 -37
- package/extensions/subagent/transcripts.ts +202 -0
- package/extensions/subagent/types.ts +12 -0
- package/extensions/watch/README.md +2 -0
- package/extensions/wechat/README.md +2 -0
- package/lib/README.md +1 -1
- package/lib/config.ts +33 -9
- package/lib/format.ts +6 -0
- package/package.json +2 -1
- package/themes/bytefly-dark.json +2 -3
- package/themes/bytefly-light.json +2 -3
package/README.md
CHANGED
|
@@ -31,13 +31,16 @@ want the agent to *think first and work safely*:
|
|
|
31
31
|
- **Plan, then execute.** A read-only planning mode turns a task into a reviewed
|
|
32
32
|
plan file that the agent then follows step by step.
|
|
33
33
|
- **Delegate and parallelize.** Run specialist subagents in their own Pi
|
|
34
|
-
processes
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
processes, push a long build into a background job, and let a watcher tail a
|
|
35
|
+
log until it matters — all while you keep the thread.
|
|
36
|
+
- **Never wreck the checkout.** Isolation is one command: the task moves onto its
|
|
37
|
+
own branch in a `git worktree`, and every prompt is snapshotted so a bad turn
|
|
38
|
+
is one `/rewind` away.
|
|
37
39
|
- **Remember across sessions.** Durable notes live in plain markdown that you
|
|
38
40
|
can read, edit, and version.
|
|
39
41
|
- **Reach the web and your files.** Search, fetch, browse, open, and read
|
|
40
|
-
documents
|
|
42
|
+
documents from inside the session, and answer a WeChat chat through a bridge to
|
|
43
|
+
the live session.
|
|
41
44
|
|
|
42
45
|
Everything is optional and independently toggleable. Nothing is patched or
|
|
43
46
|
forked: Pi discovers the resources through the `pi` manifest in
|
|
@@ -49,13 +52,13 @@ other.
|
|
|
49
52
|
Requires a working [Pi](https://pi.dev) installation.
|
|
50
53
|
|
|
51
54
|
```bash
|
|
52
|
-
pi install npm:@gavin-hu/my-pi-agent
|
|
53
|
-
pi install git:github.com/gavin-hu/my-pi-agent # install from git
|
|
54
|
-
pi install git:github.com/gavin-hu/my-pi-agent@v0.13.1 # pin a release
|
|
55
|
-
pi install ./ -l # project-local (.pi/settings.json)
|
|
56
|
-
pi -e . # try it for one run
|
|
55
|
+
pi install npm:@gavin-hu/my-pi-agent
|
|
57
56
|
```
|
|
58
57
|
|
|
58
|
+
[Getting started](./docs/getting-started.md) has the other sources — git, a
|
|
59
|
+
pinned release, a project-local install, and a trial that writes nothing — plus
|
|
60
|
+
the fit decision, the first workflows, and how to remove the pack.
|
|
61
|
+
|
|
59
62
|
`git` on `PATH` is needed by [`worktree`](./extensions/worktree/) and
|
|
60
63
|
[`rewind`](./extensions/rewind/); everything else degrades or disables cleanly,
|
|
61
64
|
and `/bytefly doctor` names what is missing, including optional runtime pieces.
|
|
@@ -121,7 +124,7 @@ extension's own `README.md` has the full tool and command surface.
|
|
|
121
124
|
|
|
122
125
|
| Extension | Tools and commands | What it does |
|
|
123
126
|
|---|---|---|
|
|
124
|
-
| [`bytefly`](./extensions/bytefly/) | `/bytefly` (with the `doctor` and `settings` subcommands), `run_doctor` | The visible layer: a themed header with a width-adaptive footer, a live
|
|
127
|
+
| [`bytefly`](./extensions/bytefly/) | `/bytefly` (with the `doctor` and `settings` subcommands), `run_doctor` | The visible layer: a themed header with a width-adaptive footer, a live run line on Pi's working row (tool, tool count, and elapsed time), and a transcript where every tool block leads with its call line and rails its result under it. `/bytefly` switches extensions and holds the curated settings; opt-in `notify` raises OS notifications when a run, a job, or a question needs you. `bytefly.json` is the config document every extension reads. |
|
|
125
128
|
|
|
126
129
|
### Themes
|
|
127
130
|
|
|
@@ -184,7 +187,7 @@ stops the factories from registering; to drop the whole package, use Pi's
|
|
|
184
187
|
|
|
185
188
|
| Guide | What it covers |
|
|
186
189
|
|---|---|
|
|
187
|
-
| [Getting started](./docs/getting-started.md) |
|
|
190
|
+
| [Getting started](./docs/getting-started.md) | Whether the pack fits, a trial run, install options, verifying the load, first workflows, and removal. |
|
|
188
191
|
| [Extensions](./docs/extensions.md) | The full catalog of every extension and theme. |
|
|
189
192
|
| [Configuration](./docs/configuration.md) | Config-file resolution, the per-extension index, environment variables. |
|
|
190
193
|
| [Configuration reference](./docs/references/bytefly-config.md) | Every `bytefly.json` section and key, with its type, default, and bounds. |
|
package/README.zh-CN.md
CHANGED
|
@@ -29,12 +29,13 @@ Pi 提供一个精简而锐利的内核。这个包是在其之上的一层,
|
|
|
29
29
|
|
|
30
30
|
- **先规划,再执行。** 只读的规划模式把一次任务变成一份可审阅的计划文件,智能体
|
|
31
31
|
随后按步骤执行。
|
|
32
|
-
- **委派与并行。** 在各自的 Pi
|
|
33
|
-
你继续把握主线。
|
|
34
|
-
- **绝不搞乱检出区。**
|
|
35
|
-
|
|
32
|
+
- **委派与并行。** 在各自的 Pi 进程中运行专职子智能体,把耗时构建交给后台任务,
|
|
33
|
+
让 watcher 盯住日志直到出现关键行 —— 你继续把握主线。
|
|
34
|
+
- **绝不搞乱检出区。** 隔离只需一条命令:任务切到自己分支的 `git worktree` 中,
|
|
35
|
+
每次提示还会生成快照,因此一次糟糕的回合只需一个 `/rewind` 就能回退。
|
|
36
36
|
- **跨会话记忆。** 持久笔记以纯 Markdown 保存,可读、可改、可纳入版本控制。
|
|
37
|
-
- **连通网络与文件。**
|
|
37
|
+
- **连通网络与文件。** 在会话内搜索、抓取、浏览、打开和读取文档,还可通过桥接
|
|
38
|
+
在微信里直接回答当前会话。
|
|
38
39
|
|
|
39
40
|
一切都是可选的,且可独立开关。没有任何东西被补丁或分叉:Pi 通过
|
|
40
41
|
[`package.json`](./package.json) 里的 `pi` manifest 发现这些资源,因此该包能像
|
|
@@ -45,13 +46,12 @@ Pi 提供一个精简而锐利的内核。这个包是在其之上的一层,
|
|
|
45
46
|
需要一个可用的 [Pi](https://pi.dev) 安装。
|
|
46
47
|
|
|
47
48
|
```bash
|
|
48
|
-
pi install npm:@gavin-hu/my-pi-agent
|
|
49
|
-
pi install git:github.com/gavin-hu/my-pi-agent # 从 git 安装
|
|
50
|
-
pi install git:github.com/gavin-hu/my-pi-agent@v0.13.1 # 固定到某个版本
|
|
51
|
-
pi install ./ -l # 仅项目本地(.pi/settings.json)
|
|
52
|
-
pi -e . # 试运行一次
|
|
49
|
+
pi install npm:@gavin-hu/my-pi-agent
|
|
53
50
|
```
|
|
54
51
|
|
|
52
|
+
[快速开始](./docs/getting-started.md) 介绍其余安装方式——git、固定版本、
|
|
53
|
+
仅项目本地安装,以及不写入任何内容的试用——还有适用性判断、首个工作流和移除方式。
|
|
54
|
+
|
|
55
55
|
`git` 需在 `PATH` 上,供 [`worktree`](./extensions/worktree/) 与
|
|
56
56
|
[`rewind`](./extensions/rewind/) 使用;其余部分会优雅降级或禁用,
|
|
57
57
|
`/bytefly doctor` 会指出缺少什么,包括可选的运行时组件。
|
|
@@ -112,7 +112,7 @@ Windows、macOS 和 Linux 均可运行,完整检查套件在每个平台上都
|
|
|
112
112
|
|
|
113
113
|
| 扩展 | 工具与命令 | 说明 |
|
|
114
114
|
|---|---|---|
|
|
115
|
-
| [`bytefly`](./extensions/bytefly/) | `/bytefly`(`doctor`、`settings` 子命令)、`run_doctor` | 可见的那一层:主题化头部配宽度自适应页脚,Pi
|
|
115
|
+
| [`bytefly`](./extensions/bytefly/) | `/bytefly`(`doctor`、`settings` 子命令)、`run_doctor` | 可见的那一层:主题化头部配宽度自适应页脚,Pi 工作行上实时显示工具、调用次数与计时,转录中每个工具块以调用行为标题、结果正文挂在其下方。`/bytefly` 可开关扩展并集中存放精选设置;可选的 `notify` 会在一次运行、后台任务或待回答的问题需要你时发出系统通知。`bytefly.json` 是每个扩展都会读取的配置文档。 |
|
|
116
116
|
|
|
117
117
|
### 主题
|
|
118
118
|
|
|
@@ -171,7 +171,7 @@ Pi 的 `--no-extensions`,或从你的设置中删除该包。
|
|
|
171
171
|
|
|
172
172
|
| 指南 | 内容 |
|
|
173
173
|
|---|---|
|
|
174
|
-
| [快速开始](./docs/getting-started.md) |
|
|
174
|
+
| [快速开始](./docs/getting-started.md) | 是否适合你、试用、安装选项、验证加载、首个工作流、移除。 |
|
|
175
175
|
| [扩展](./docs/extensions.md) | 所有扩展与主题的完整目录。 |
|
|
176
176
|
| [配置](./docs/configuration.md) | 配置文件解析、按扩展索引、环境变量。 |
|
|
177
177
|
| [配置参考](./docs/references/bytefly-config.md) | 列出 `bytefly.json` 的每个 section 与键,包括类型、默认值和取值范围。 |
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
`bytefly` owns the terminal chrome, configuration, and diagnostics surfaces
|
|
4
4
|
this package adds: one themed startup header whose two-tone nameplate carries
|
|
5
5
|
the agent and chrome identities, one compact, colorful, width-adaptive two-line
|
|
6
|
-
footer, a two-row `You` nameplate that opens each user message, a
|
|
7
|
-
|
|
6
|
+
footer, a two-row `You` nameplate that opens each user message, a receipt rule that
|
|
7
|
+
records each completed turn in the transcript, one keyboard-driven screen with two
|
|
8
8
|
sections — Extensions, every resolved Pi extension with its enable checkbox and
|
|
9
9
|
its health, and Settings, the common app and `bytefly.json` preferences — and
|
|
10
10
|
an opt-in notify capability that raises an OS notification when a run finishes,
|
|
@@ -33,7 +33,7 @@ How the `run_doctor` call and its result look in the transcript — the call lin
|
|
|
33
33
|
|
|
34
34
|
The extension owns ten surfaces: the startup header, the footer, the
|
|
35
35
|
user-message marker, the rule between completed turns, the notice block for
|
|
36
|
-
shared transcript notices, the tool-block frame, the live working
|
|
36
|
+
shared transcript notices, the tool-block frame, the live working line, the
|
|
37
37
|
two-section control center, notifications, and the published-version check.
|
|
38
38
|
|
|
39
39
|
**Header.** A nameplate: a two-tone half-block mark from the active theme's
|
|
@@ -59,10 +59,15 @@ that cannot render half blocks the mark falls back to a two-tone `Pi` wordmark.
|
|
|
59
59
|
|
|
60
60
|
As the terminal narrows, the identity sheds the update marker, then the chrome
|
|
61
61
|
version, then the chrome line, then the agent name, and the help rows drop whole
|
|
62
|
-
keys, so nothing wraps or clips mid-word.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
keys, so nothing wraps or clips mid-word. Height sheds the same way: at 28 rows
|
|
63
|
+
all three help groups print, below 28 the middle group (the expand and model
|
|
64
|
+
chords) goes first, below 22 only the `/` commands row survives, and below 16 the
|
|
65
|
+
help block and the leading blank row go, leaving the nameplate and its rule. An
|
|
66
|
+
unknown height keeps the full header. The nameplate sits on the transcript
|
|
67
|
+
margin, which follows Pi's `outputPad` (the value the tool frame and the notice
|
|
68
|
+
use too), and the help hangs one body indent inside it. The mark, tokens,
|
|
69
|
+
identity, and grouped help are tuned in
|
|
70
|
+
[`header/config.ts`](./header/config.ts).
|
|
66
71
|
|
|
67
72
|
**Footer.** Two lines, each with a left and right zone:
|
|
68
73
|
|
|
@@ -168,26 +173,36 @@ as it would without it, and a soft wrap of one long line stays at the margin
|
|
|
168
173
|
because the renderer wraps it, not bytefly. The label and the rule are tuned
|
|
169
174
|
in [`message/config.ts`](./message/config.ts).
|
|
170
175
|
|
|
171
|
-
**Working
|
|
176
|
+
**Working line.** Pi's working row shows a static `Working`, so a long run
|
|
172
177
|
reads the same as a short one. While the row is up, bytefly advances its
|
|
173
|
-
message once a second and hands it back to Pi when the row comes down
|
|
178
|
+
message once a second and hands it back to Pi when the row comes down. Before
|
|
179
|
+
the first tool starts it is a count:
|
|
174
180
|
|
|
175
181
|
```
|
|
176
182
|
Working… 12s
|
|
177
183
|
```
|
|
178
184
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
Once a top-level tool starts, the row becomes a live run line: the tool, how
|
|
186
|
+
many top-level tools this run has started, and the elapsed time.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
edit · 3 tools · 12s
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A nested tool call (one carrying `parentToolCallId`, such as a codemode script's
|
|
193
|
+
inner call) neither counts nor takes over the line, so the number matches the
|
|
194
|
+
tool blocks in the transcript. The line keeps the last tool name while the
|
|
195
|
+
model streams after it, and a tool event repaints the row immediately instead
|
|
196
|
+
of waiting for the next tick. The line covers the whole run, tool calls
|
|
197
|
+
included. It is tied to the row's visibility: it starts on `turn_start`, stops
|
|
198
|
+
on `agent_end`, and is reset on `session_shutdown`; turns inside one run do not
|
|
199
|
+
restart it, and a retry starts a new run. The words and the interval are tuned
|
|
200
|
+
in [`working/config.ts`](./working/config.ts).
|
|
186
201
|
|
|
187
202
|
**Turn separator.** After each completed turn the extension appends an inert
|
|
188
|
-
entry and draws a centered `╌╌╌ turn N
|
|
189
|
-
[Turn separator](#turn-separator). No config file is
|
|
190
|
-
command.
|
|
203
|
+
entry and draws a centered receipt rule — `╌╌╌ turn N · duration · N tools ╌╌╌`
|
|
204
|
+
— across the transcript; see [Turn separator](#turn-separator). No config file is
|
|
205
|
+
needed and there is no command.
|
|
191
206
|
|
|
192
207
|
**Notices.** Any extension in the package can post a transcript notice under the
|
|
193
208
|
shared `my-pi-agent/notice` type; bytefly draws it at the transcript's margin —
|
|
@@ -219,6 +234,15 @@ are tuned in [`notice/config.ts`](./notice/config.ts). Every field is sanitized
|
|
|
219
234
|
and clipped at the boundary, and a message without `details` keeps Pi's default
|
|
220
235
|
custom-message box.
|
|
221
236
|
|
|
237
|
+
**Compaction.** `/compact` is Pi's own surface: its summary block is a built-in
|
|
238
|
+
message component, so bytefly cannot replace, hide, or shorten it. The shipped
|
|
239
|
+
themes only remove its fill — `customMessageBg` is the terminal default — and
|
|
240
|
+
paint the `[compaction]` label in the accent, so the block sits on the canvas
|
|
241
|
+
like the rest of the transcript. Pi's branch summaries and skill invocations
|
|
242
|
+
share the same tokens.
|
|
243
|
+
|
|
244
|
+
<img src="../../assets/compaction.png" alt="The /compact summary block: a [compaction] label, the compacted token count, and the summary Markdown on the terminal canvas, with no background band" width="100%">
|
|
245
|
+
|
|
222
246
|
**Tool blocks.** Every tool block leads with the tool's own call line as the
|
|
223
247
|
headline — the tool name in the theme's tool-title colour — and its result body
|
|
224
248
|
sits two columns in directly beneath it, led by the transcript rail, instead of
|
|
@@ -348,7 +372,7 @@ identity tokens, tagline, grouped help), the footer in
|
|
|
348
372
|
[`footer/config.ts`](./footer/config.ts) (thresholds, gauge glyphs and widths,
|
|
349
373
|
separators, icons, status keys), and the turn separator in
|
|
350
374
|
[`separator/config.ts`](./separator/config.ts) (glyph, label prefix, padding,
|
|
351
|
-
colors).
|
|
375
|
+
minimum duration, outcome wording, colors).
|
|
352
376
|
|
|
353
377
|
Every key of every section is listed with its type, default, and bounds in the
|
|
354
378
|
[configuration reference](../../docs/references/bytefly-config.md); this section
|
|
@@ -507,32 +531,46 @@ the model, which sits at the end of the line.
|
|
|
507
531
|
`agent_settled` fires once after an agent run fully settles — exactly one user
|
|
508
532
|
turn — which is why it is used instead of `turn_end` (that fires once per
|
|
509
533
|
assistant message, so tool-calling rounds would each get a line). On each
|
|
510
|
-
settled run the extension appends an inert custom entry and a renderer draws
|
|
511
|
-
|
|
534
|
+
settled run the extension appends an inert custom entry and a renderer draws it
|
|
535
|
+
as a receipt: the turn number, then the run's duration, top-level tool count,
|
|
536
|
+
and outcome as the width allows.
|
|
512
537
|
|
|
513
538
|
```
|
|
514
|
-
|
|
539
|
+
╌╌╌╌╌╌╌╌╌╌╌╌ turn 3 · 1m12s · 7 tools ╌╌╌╌╌╌╌╌╌╌╌╌
|
|
515
540
|
```
|
|
516
541
|
|
|
517
542
|
- The turn number is `count(separators on the current branch) + 1`, so a
|
|
518
543
|
resumed or forked session continues the parent's numbering and abandoned
|
|
519
544
|
branches do not leak counts.
|
|
545
|
+
- The receipt opens on the run's first `agent_start` and closes on
|
|
546
|
+
`agent_settled`, so a retry stays inside one duration. It counts only
|
|
547
|
+
top-level tool calls; a nested call (one carrying `parentToolCallId`) is an
|
|
548
|
+
implementation detail of its parent and does not inflate the number.
|
|
549
|
+
- The outcome is part of the receipt: a failed tool adds `✕N`, and an aborted
|
|
550
|
+
run adds `aborted` instead of the failure count. A sub-second run prints no
|
|
551
|
+
duration, so quick turns stay quiet. Entries written before the receipt
|
|
552
|
+
existed have only a turn number and still render as `turn N`.
|
|
553
|
+
- The label sheds fields rather than overflowing: the tool count first, then
|
|
554
|
+
the duration, then the outcome. When even the turn number does not fit, the
|
|
555
|
+
line becomes a plain dashed rule instead of clipping.
|
|
556
|
+
- A clean turn paints the label `meta`, a turn with a failed tool paints it
|
|
557
|
+
`bad`, and an aborted turn paints it `warn`.
|
|
520
558
|
- The entry is excluded from LLM context, so it costs no tokens, and it
|
|
521
559
|
survives resume and fork. It is drawn under the `bytefly-separator` custom
|
|
522
560
|
entry type.
|
|
523
|
-
- The line is centered and width-adaptive: the label is filled out with the
|
|
524
|
-
glyph, and when it cannot fit the line becomes a plain dashed rule rather
|
|
525
|
-
than overflowing.
|
|
526
561
|
- The rule is installed only in interactive (`tui`) sessions; RPC, JSON, and
|
|
527
562
|
print sessions append nothing.
|
|
528
563
|
- Separators recorded by the retired `turn-separator` extension are not
|
|
529
564
|
rendered, and `PI_DISABLED_EXTENSIONS=turn-separator` no longer affects the
|
|
530
565
|
rule; disable the whole extension with `bytefly` instead.
|
|
531
|
-
- There is no runtime command; the glyph, label prefix, padding,
|
|
532
|
-
tuned in
|
|
566
|
+
- There is no runtime command; the glyph, label prefix, padding, colors, and
|
|
567
|
+
minimum duration are tuned in
|
|
568
|
+
[`separator/config.ts`](./separator/config.ts).
|
|
533
569
|
|
|
534
570
|
## The control center
|
|
535
571
|
|
|
572
|
+
<img src="../../assets/bytefly.png" alt="The /bytefly control center: the Extensions and Settings section bar with the `Global · 10 extensions · 2 off · 1 missing` counts, one checkbox row per extension with its verdict, the focused extension's report, and the key hints" width="100%">
|
|
573
|
+
|
|
536
574
|
`/bytefly [extension] [refresh]` opens the control center on **Extensions**: a
|
|
537
575
|
keyboard-driven dock list of every extension Pi resolves for the session, so the
|
|
538
576
|
list matches what actually loads. `←`/`→` switch to **Settings**, where the same
|
|
@@ -597,11 +635,11 @@ installs or fetches a package.
|
|
|
597
635
|
without leaving the session.
|
|
598
636
|
|
|
599
637
|
**Settings.** The second section uses the same frame and the same `Tab` scope
|
|
600
|
-
switch, with its own cursor. It lists
|
|
638
|
+
switch, with its own cursor. It lists twenty-eight preferences in ten groups:
|
|
601
639
|
|
|
602
640
|
```
|
|
603
641
|
bytefly ──────────────────────────────────────────────────────────────
|
|
604
|
-
Extensions · Settings Global ·
|
|
642
|
+
Extensions · Settings Global · 28 settings
|
|
605
643
|
|
|
606
644
|
Startup (1)
|
|
607
645
|
❯ ◻ Bytefly startup TUI Off
|
|
@@ -620,16 +658,32 @@ bytefly ────────────────────────
|
|
|
620
658
|
Currency CNY (¥)
|
|
621
659
|
Cost decimals 2
|
|
622
660
|
Session budget 0
|
|
623
|
-
Transcript (
|
|
661
|
+
Transcript (3)
|
|
624
662
|
Rail glyph ╎
|
|
663
|
+
Achieved goal Hide
|
|
625
664
|
Output padding 1
|
|
626
665
|
Terminal (1)
|
|
627
666
|
◻ Hardware cursor Off
|
|
667
|
+
Worktree (4)
|
|
668
|
+
◼ Fetch remote On
|
|
669
|
+
Base ref Fresh
|
|
670
|
+
Branch prefix worktree-
|
|
671
|
+
Prune after (days) 7
|
|
672
|
+
Snapshots (3)
|
|
673
|
+
◼ Automatic snapshots On
|
|
674
|
+
Snapshots kept 20
|
|
675
|
+
◼ Safety snapshot On
|
|
676
|
+
Jobs and watchers (3)
|
|
677
|
+
◻ Wake when a job finishes Off
|
|
678
|
+
◻ Keep jobs after exit Off
|
|
679
|
+
◻ Wake on a match Off
|
|
680
|
+
Memory (1)
|
|
681
|
+
◼ Inject notes On
|
|
628
682
|
Bytefly startup TUI — Pi setting
|
|
629
683
|
Use bytefly's startup header in place of Pi's startup TUI.
|
|
630
684
|
Default · ~/.pi/agent/settings.json
|
|
631
685
|
|
|
632
|
-
Esc close · ←/→ · Space change · Tab scope ·
|
|
686
|
+
Esc close · ←/→ · Space change · Tab scope · u reset · J JSON · [ ] group
|
|
633
687
|
```
|
|
634
688
|
|
|
635
689
|
| Group | Row | Source | Values |
|
|
@@ -648,21 +702,43 @@ bytefly ────────────────────────
|
|
|
648
702
|
| Pricing | Cost decimals | `price.currency.decimals` | 0, 1, 2, 3, 4 |
|
|
649
703
|
| Pricing | Session budget | `price.budget` | Number, `0` = off |
|
|
650
704
|
| Transcript | Rail glyph | `chrome.rail` | Text, exactly one column |
|
|
705
|
+
| Transcript | Achieved goal | `goal.achieved` | Show, Hide |
|
|
651
706
|
| Transcript | Output padding | Pi `settings.json` | 0, 1 |
|
|
652
707
|
| Terminal | Hardware cursor | Pi `settings.json` | Off, On |
|
|
708
|
+
| Worktree | Base ref | `worktree.baseRef` | Fresh, Head |
|
|
709
|
+
| Worktree | Branch prefix | `worktree.branchPrefix` | Text, trimmed, required |
|
|
710
|
+
| Worktree | Fetch remote | `worktree.fetchRemote` | Off, On |
|
|
711
|
+
| Worktree | Prune after (days) | `worktree.pruneAfterDays` | Number, 0–3650 |
|
|
712
|
+
| Snapshots | Automatic snapshots | `rewind.autoSnapshots` | Off, On |
|
|
713
|
+
| Snapshots | Snapshots kept | `rewind.max` | Number, 0–1000 |
|
|
714
|
+
| Snapshots | Safety snapshot | `rewind.safetySnapshot` | Off, On |
|
|
715
|
+
| Jobs and watchers | Wake when a job finishes | `job.wakeOnFinish` | Off, On |
|
|
716
|
+
| Jobs and watchers | Keep jobs after exit | `job.detachedByDefault` | Off, On |
|
|
717
|
+
| Jobs and watchers | Wake on a match | `watch.wakeOnMatch` | Off, On |
|
|
718
|
+
| Memory | Inject notes | `memory.inject` | Off, On |
|
|
653
719
|
|
|
654
720
|
Choosing **Currency** also writes that currency's default precision. Both rows
|
|
655
721
|
stay editable afterwards, so the footer follows the explicit values.
|
|
656
722
|
|
|
657
723
|
`Space` (or `Enter`) changes the focused row: a boolean toggles, an enum cycles
|
|
658
724
|
forward, and a text row opens an inline editor on its value — the notification
|
|
659
|
-
title, the probe timeout, and the rail glyph.
|
|
725
|
+
title, the probe timeout, the branch prefix, and the rail glyph. `u` deletes the
|
|
726
|
+
focused row's key so its default applies again (`Already at its default.` when
|
|
727
|
+
nothing is stored), `J` swaps the detail pane for the focused row's whole section
|
|
728
|
+
as the active scope stores it — the only view of the keys this screen leaves to
|
|
729
|
+
JSON — and `[` / `]` jump to the previous or next group. The detail pane names
|
|
730
|
+
the source, describes the preference, and shows
|
|
660
731
|
where the value is written — `Global · ~/.pi/agent/settings.json`, `Project ·
|
|
661
732
|
.pi/bytefly.json`, or `Inherited from Global · .pi/bytefly.json`. A successful
|
|
662
733
|
change marks the screen `Saved · reload to apply` — even when the terminal is too
|
|
663
734
|
short for the detail body — and closing offers the same
|
|
664
735
|
reload confirm as an extension toggle.
|
|
665
736
|
|
|
737
|
+
Six of the ten groups belong to other extensions. The screen cannot import their
|
|
738
|
+
normalizers, so it reports the leaf as the file states it and declares the
|
|
739
|
+
loader's default; `test/settings-coverage.test.ts` holds the catalogue to every
|
|
740
|
+
loader's defaults and bounds in both directions.
|
|
741
|
+
|
|
666
742
|
**Bytefly startup TUI** is the one row whose label differs from the Pi key it
|
|
667
743
|
writes: On writes `"quietStartup": true` (bytefly's header replaces Pi's startup
|
|
668
744
|
detail), and Off writes `false` (Pi's built-in header stays). A hand-set
|
|
@@ -671,17 +747,18 @@ with `true` or `false`.
|
|
|
671
747
|
|
|
672
748
|
App rows are **Global only**: Pi's setters for them write the personal
|
|
673
749
|
`settings.json`, and only resource paths have public project setters. Project
|
|
674
|
-
scope therefore lists the
|
|
750
|
+
scope therefore lists the twenty-five `bytefly.json` rows and a
|
|
675
751
|
`App settings are Global only.` note rather than promising a write that cannot
|
|
676
752
|
happen. A `bytefly.json` row writes one leaf in the selected scope (a depth-1
|
|
677
753
|
key, or a nested one such as `price.currency.decimals`) through a per-key
|
|
678
754
|
writer, so every sibling key, section, `$schema`, and `version` is preserved. A
|
|
679
|
-
malformed document is refused with a notice instead of overwritten
|
|
755
|
+
malformed document is refused with a notice instead of overwritten, and `u`
|
|
756
|
+
removes that one leaf through the same writer's `unsetConfigValueAt`.
|
|
680
757
|
|
|
681
758
|
The non-`tui` form prints the headline, the groups, and `Label: Value` rows:
|
|
682
759
|
|
|
683
760
|
```
|
|
684
|
-
bytefly: Settings · Global ·
|
|
761
|
+
bytefly: Settings · Global · 28 settings
|
|
685
762
|
|
|
686
763
|
Startup (1)
|
|
687
764
|
Bytefly startup TUI: Off
|
|
@@ -966,6 +1043,9 @@ command.
|
|
|
966
1043
|
- **Notifications are not focus-aware.** They fire whether or not the terminal
|
|
967
1044
|
has focus, and once per trigger rather than once per unseen event. There is
|
|
968
1045
|
no focus API to consult.
|
|
1046
|
+
- **`/compact` is Pi's block.** The summary component is built in, so bytefly
|
|
1047
|
+
cannot render, hide, or shorten it; the themes only choose its fill and label
|
|
1048
|
+
color. Branch summaries and skill invocations share the same theme tokens.
|
|
969
1049
|
- **A background job cannot ask for input.** The `needsInput` trigger is a
|
|
970
1050
|
prompt raised during a run, not a process waiting on stdin: jobs are
|
|
971
1051
|
fire-and-observe, so that state does not exist.
|
|
@@ -973,11 +1053,11 @@ command.
|
|
|
973
1053
|
host lacks (or an OSC method off a TTY) and the notification is dropped; only
|
|
974
1054
|
`auto` inspects the environment. `doctor` now reports the mismatch as a
|
|
975
1055
|
`degraded` finding, which says what to change but does not fix it for you.
|
|
976
|
-
- **The working
|
|
1056
|
+
- **The working line is one global row.** `setWorkingMessage` drives the
|
|
977
1057
|
single working row, so bytefly and any other extension setting it race — last
|
|
978
1058
|
writer wins — and the message is reset to Pi's default at `agent_end`. If the
|
|
979
|
-
row is hidden (`ctx.ui.setWorkingVisible(false)`) the
|
|
980
|
-
and a reload mid-run does not resume
|
|
1059
|
+
row is hidden (`ctx.ui.setWorkingVisible(false)`) the line has no surface,
|
|
1060
|
+
and a reload mid-run does not resume the count until the next turn.
|
|
981
1061
|
|
|
982
1062
|
## Non-goals
|
|
983
1063
|
|
|
@@ -1001,7 +1081,7 @@ command.
|
|
|
1001
1081
|
match — are the whole surface.
|
|
1002
1082
|
- No sound, action buttons, notification history, or de-duplication; a
|
|
1003
1083
|
notification is a fire-and-forget message, not an inbox.
|
|
1004
|
-
- No frozen per-run working duration: the
|
|
1084
|
+
- No frozen per-run working duration: the line is live and is handed back to
|
|
1005
1085
|
Pi when the working row comes down, because the extension API exposes no hook
|
|
1006
1086
|
to keep a finished run's total.
|
|
1007
1087
|
|
|
@@ -1014,18 +1094,18 @@ rather than showing a wrong value.
|
|
|
1014
1094
|
|
|
1015
1095
|
| Integration point | Details |
|
|
1016
1096
|
|---|---|
|
|
1017
|
-
| Header | `ctx.ui.setHeader` with a custom component, replacing Pi's built-in header; installed only when `ctx.mode === "tui"` and `pi.getSettings().quietStartup === true` (the **Bytefly startup TUI** row). `false`, `"header"`, and unset call `setHeader(undefined)`, leaving Pi's built-in header in place. The nameplate insets by Pi's `outputPad` (read through `pi.getSettings()`), so it sits on the transcript margin, and the help hangs one body indent inside it. The mark and text are drawn from the active theme's tokens, the help keys resolve live through `keyText`, and the mark falls back to a wordmark on Apple Terminal (`isAppleTerminalSession`), mirroring Pi's built-in. The chrome version gains `↑<latest>` from the `UpdateSource`, and `registerHeader` returns a `refresh()` handle that re-installs the last context's header when that value arrives. |
|
|
1097
|
+
| Header | `ctx.ui.setHeader` with a custom component, replacing Pi's built-in header; installed only when `ctx.mode === "tui"` and `pi.getSettings().quietStartup === true` (the **Bytefly startup TUI** row). `false`, `"header"`, and unset call `setHeader(undefined)`, leaving Pi's built-in header in place. The nameplate insets by Pi's `outputPad` (read through `pi.getSettings()`), so it sits on the transcript margin, and the help hangs one body indent inside it. The mark and text are drawn from the active theme's tokens, the help keys resolve live through `keyText`, and the mark falls back to a wordmark on Apple Terminal (`isAppleTerminalSession`), mirroring Pi's built-in. The factory reads `tui.terminal.rows` on every render, so the height ladder in `header/config.ts` sheds help groups and then the leading blank row as the terminal shortens (unknown height keeps the full header). The chrome version gains `↑<latest>` from the `UpdateSource`, and `registerHeader` returns a `refresh()` handle that re-installs the last context's header when that value arrives. |
|
|
1018
1098
|
| Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
|
|
1019
1099
|
| User message | `pi.registerMarkdownTransformer` puts a two-row `You` nameplate (the label over a full-width rule) above each user message; one padded level-2 heading carries the accent through `mdHeading` for both rows, and the shipped themes set `userMessageBg` to the terminal default, so the block has no fill. Pi renders the result as Markdown, so the surface adds no component or color of its own. |
|
|
1020
1100
|
| Notice | `registerMessageRenderer(NOTICE_TYPE)` draws every shared transcript notice at the margin from its `details`; the headline stays at the margin and its rows hang from the transcript rail, painted by the notice's worst severity, whose glyph comes from the shared `RailSource`. `job` and `watch` post one when their work settles, and `doctor` posts one at `session_start` for a missing hard requirement. The type and shape come from `lib/notice.ts`. A message without `details` falls back to Pi's default custom-message box. |
|
|
1021
1101
|
| Tool blocks | `registerToolRenderer` returns, for every tool, `renderShell: "self"`; `renderCall` leaves the tool's own first line as the block's headline (a shell prompt shown as the tool name, its command repainted in the body colour, and a primary subject demoted to the body colour) and indents the rest of the call two columns under it, `renderResult` indents its body the same and leads it with the status-coloured rail, and each wraps the tool's own renderer (or a generic fallback). `edit` and `write` carry their result on the call body, so that body is railed too. |
|
|
1022
1102
|
| Screen command | `pi.registerCommand("bytefly")` takes `[doctor] [settings] [extension] [refresh]`: `tui` mounts a dock screen via `ctx.ui.custom()` with the rails suppressed; other modes print through `ctx.ui.notify()` — the grouped listing, `run_doctor`'s report for `doctor`, or the grouped settings listing with values for `settings`. A named extension is checked against the discovered set before any probe. Tab completes the subcommands, extension names after `doctor`, and `refresh`; `/bytefly settings` rejects a name. |
|
|
1023
|
-
| Turn separator | `registerEntryRenderer("bytefly-separator")` draws each
|
|
1024
|
-
| Working
|
|
1103
|
+
| Turn separator | `registerEntryRenderer("bytefly-separator")` draws each stored receipt; `agent_start` opens it, top-level `tool_execution_start`/`tool_execution_end` count tools and failures, and `agent_settled` appends `pi.appendEntry("bytefly-separator", { turn, durationMs, tools, failures, aborted })` in `tui` mode only. The receipt fields are omitted when no `agent_start` was seen, so an older or partially observed entry stays `{ turn }`. |
|
|
1104
|
+
| Working line | `turn_start` starts a 1s tick that drives `ctx.ui.setWorkingMessage` in `tui` mode; before a tool runs the message is `Working… Ns`, and each top-level `tool_execution_start` repaints it as `tool · N tools · Ns` (nested calls carrying `parentToolCallId` are skipped). `agent_end` restores Pi's default and cancels the tick (also on `session_shutdown`). No config file and no persisted state; the count lives in the register's closure. |
|
|
1025
1105
|
| Notifications | `agent_settled` (skipped when aborted), `ui_prompt_start` only while `agent_start`…`agent_settled` marks a run active, and `my-pi-agent/job-finished` on `pi.events`; the `notify` section of `bytefly.json` gates each. Delivery writes an OSC 777/99 sequence to stdout or spawns `osascript`, `notify-send`, or `powershell.exe`. The bus subscription is created in `session_start` and released in `session_shutdown`, and the `job` extension never imports bytefly. |
|
|
1026
1106
|
| Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
|
|
1027
1107
|
| Picker writes | `SettingsManager` setters (`setExtensionPaths`, `setPackages`, `setProjectExtensionPaths`, `setProjectPackages`) then `flush()`; `ctx.reload()` after the screen closes when changes are pending. |
|
|
1028
|
-
| Settings writes | App rows call `SettingsManager` setters (`setQuietStartup`, `setOutputPad`, `setShowHardwareCursor`) on the global store and `flush()`, then surface `drainErrors()`; `bytefly.json` rows call `writeConfigValueAt` on the selected scope's document with the row's leaf path, so a nested key (`price.currency.decimals`) and a flat one (`notify.method`) share one writer. A failed or refused write shows a notice in the screen's hint row and leaves the displayed value unchanged; a successful one marks the screen dirty. |
|
|
1108
|
+
| Settings writes | App rows call `SettingsManager` setters (`setQuietStartup`, `setOutputPad`, `setShowHardwareCursor`) on the global store and `flush()`, then surface `drainErrors()`; `bytefly.json` rows call `writeConfigValueAt` on the selected scope's document with the row's leaf path, so a nested key (`price.currency.decimals`) and a flat one (`notify.method`) share one writer, and `u` calls `unsetConfigValueAt` with the same path. A failed or refused write shows a notice in the screen's hint row and leaves the displayed value unchanged; a successful one marks the screen dirty. |
|
|
1029
1109
|
| Doctor tool | `pi.registerTool("run_doctor")`, read-only (`readOnlyHint: true`, `openWorldHint: false`), direct exposure and active on registration (the defaults, so not declared), with a typed `outputSchema` mirroring `structuredContent`. Its `readOnlyHint` is what the shared default-deny policy in `lib/policy.ts` reads to allow it in plan mode. |
|
|
1030
1110
|
| Doctor probes | `run_doctor` and the screen share the session cache created by `registerDoctor`; the screen mounts on that cache and probes on the first Extensions view (the command's `refresh` forces it), while the text modes probe before printing, and `/bytefly refresh` and `refresh: true` re-probe. Probes spawn `<binary> --version`, lazily import optional packages, and read config files through `lib/requirements.ts`; nothing is ever written. |
|
|
1031
1111
|
| Status routing | Reads `footerData.getExtensionStatuses()` (a custom footer replaces the built-in one entirely); `worktree` routes to line 1, plan/alert statuses to line 2's trailing slot. |
|
|
@@ -1118,8 +1198,10 @@ rather than showing a wrong value.
|
|
|
1118
1198
|
and a grouped help block. It takes its margin from Pi's `outputPad`, like the
|
|
1119
1199
|
tool frame and the notice, and hangs the help one body indent inside it, so the
|
|
1120
1200
|
header follows a non-default pad instead of assuming a column. It adds no
|
|
1121
|
-
session or model data — that is the footer's job — and
|
|
1122
|
-
|
|
1201
|
+
session or model data — that is the footer's job — and its shape follows the
|
|
1202
|
+
terminal: the height ladder drops whole help groups, then the leading blank
|
|
1203
|
+
row, before the nameplate gives up anything, so a short pane spends its rows
|
|
1204
|
+
on the transcript instead of chrome.
|
|
1123
1205
|
- **Two lines, four zones.** Line 1 is identity (`pwd` left, branch + worktree
|
|
1124
1206
|
right); line 2 is resources (meters left, model + level right). This keeps
|
|
1125
1207
|
"where am I / what state" separate from "how much am I burning".
|
|
@@ -1274,7 +1356,7 @@ rather than showing a wrong value.
|
|
|
1274
1356
|
The surfaces are folders: `header/`, `footer/` (the status bar and its price
|
|
1275
1357
|
calculation), `message/` (the user-message marker), `notice/` (the shared notice),
|
|
1276
1358
|
`block/` (the tool-block frame), `separator/`, `working/` (the live
|
|
1277
|
-
|
|
1359
|
+
run line on Pi's working row), `panel/` (the
|
|
1278
1360
|
unified screen and its merged model),
|
|
1279
1361
|
`picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
|
|
1280
1362
|
and its probe runtime), `notify/`, and `update/` (the published-version check
|
|
@@ -1316,14 +1398,14 @@ columns, gauge), so the look follows the active theme.
|
|
|
1316
1398
|
| `block/component.ts` | Pure `toolBlockLines`, `headlineWithToolName`, `recolorShellCall`, `recolorCallHeadline`, and `ToolBlock`: the headline, the body's two-column indent (optionally rail-led), the reduced-width inner render, the leading-blank trim, the shell-prompt rewrite (the name keeps the accent, the command reopens in `label`), the subject demotion (a path, URL, query, or pattern reads in `label` so the name is the line's only accent, including the tail a bare `edit <path>` headline wraps onto the body), and the image-line passthrough. |
|
|
1317
1399
|
| `block/fallback.ts` | Generic `callFallback` / `resultFallback` for a tool with no renderer: the subject in the body colour, its options faint, and a capped result preview. |
|
|
1318
1400
|
| `block/register.ts` | The `registerToolRenderer` resolver that frames every tool; it recolours a shell call's string before the frame wraps it, rails the result body by status, and rails the call body for `edit`/`write` (the two whose call body carries the result). |
|
|
1319
|
-
| `separator/config.ts` | Turn-rule tuning: `customType`, glyph, label prefix, padding, and roles. |
|
|
1320
|
-
| `separator/register.ts` | Registers the entry renderer and appends
|
|
1401
|
+
| `separator/config.ts` | Turn-rule tuning: `customType`, glyph, label prefix, padding, receipt minimum duration and outcome wording, and roles. |
|
|
1402
|
+
| `separator/register.ts` | Registers the entry renderer, tracks the run's tools and failures, and appends the receipt on `agent_settled`. |
|
|
1321
1403
|
| `separator/component.ts` | `SeparatorComponent`: the stateless entry renderer. |
|
|
1322
|
-
| `separator/format.ts` | Pure `
|
|
1323
|
-
| `separator/types.ts` | `TurnSeparatorData
|
|
1324
|
-
| `working/config.ts` | Counter tuning (`WORKING_CONFIG`): the
|
|
1325
|
-
| `working/format.ts` | Pure `formatElapsed` (whole seconds) and the working-message shape. |
|
|
1326
|
-
| `working/register.ts` | Starts the tick on `turn_start` and cancels
|
|
1404
|
+
| `separator/format.ts` | Pure `receiptLabel` (the width ladder), `receiptRole`, and `separatorLine`, drawn by the kit's `centeredRule`. |
|
|
1405
|
+
| `separator/types.ts` | `TurnSeparatorData`: the turn plus the optional receipt fields. |
|
|
1406
|
+
| `working/config.ts` | Counter tuning (`WORKING_CONFIG`): the words, the tool singular/plural, and the tick interval. |
|
|
1407
|
+
| `working/format.ts` | Pure `formatElapsed` (whole seconds) and the working-message shape: the plain count and the `tool · N tools · elapsed` run line. |
|
|
1408
|
+
| `working/register.ts` | Starts the tick on `turn_start`, tracks top-level tools on `tool_execution_start`, and cancels on `agent_end`/`session_shutdown`. |
|
|
1327
1409
|
| `panel/types.ts` | `PanelSection` (`extensions` | `settings`), `PanelRow`, and `PanelModel`, the merged row model. |
|
|
1328
1410
|
| `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
|
|
1329
1411
|
| `panel/format.ts` | Non-TUI text: the extension headline and grouped listing with a per-row health suffix, plus the section segments, the Settings counts/headline, and the grouped Settings listing. |
|
|
@@ -1375,10 +1457,10 @@ folding across a width sweep from 6 to 120 plus wide characters
|
|
|
1375
1457
|
(`footer/render/layout`), exact
|
|
1376
1458
|
golden output at fixed widths (`footer/render/render`), and the install/skip/
|
|
1377
1459
|
restore/toggle lifecycle plus a config-driven price (`extension`). The turn
|
|
1378
|
-
separator
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1460
|
+
separator is covered by its pure formatting (`separator/format`, including the
|
|
1461
|
+
receipt's width ladder), its register/append/render/disable lifecycle
|
|
1462
|
+
(`separator/register`), and the run stats it records. The picker helpers are
|
|
1463
|
+
covered by the pure helpers (`picker/resources`), the toggle
|
|
1382
1464
|
transforms (`picker/toggle`), discovery with an injected factory
|
|
1383
1465
|
(`picker/discovery`), and the runtime state machine (`picker/runtime`). The
|
|
1384
1466
|
unified screen is covered by its pure model merge (`panel/join`), its text
|
|
@@ -14,6 +14,9 @@ import { clampInteger, loadConfigSection } from "../../../lib/config.ts";
|
|
|
14
14
|
/** How long a single probe may take before it counts as unusable. */
|
|
15
15
|
const DEFAULT_TIMEOUT_MS = 2_000;
|
|
16
16
|
|
|
17
|
+
/** The probe deadline range, in milliseconds; the panel's editor offers the same bounds. */
|
|
18
|
+
export const TIMEOUT_MS_RANGE = { min: 100, max: 30_000 } as const;
|
|
19
|
+
|
|
17
20
|
export interface DoctorConfig {
|
|
18
21
|
/** Probe enabled extensions at session start and warn when one is broken. */
|
|
19
22
|
proactive: boolean;
|
|
@@ -34,7 +37,7 @@ export function normalizeConfig(raw: Record<string, unknown> | undefined, base:
|
|
|
34
37
|
if (!raw) return base;
|
|
35
38
|
return {
|
|
36
39
|
proactive: typeof raw.proactive === "boolean" ? raw.proactive : base.proactive,
|
|
37
|
-
timeoutMs: clampInteger(raw.timeoutMs, base.timeoutMs,
|
|
40
|
+
timeoutMs: clampInteger(raw.timeoutMs, base.timeoutMs, TIMEOUT_MS_RANGE.min, TIMEOUT_MS_RANGE.max),
|
|
38
41
|
skip: Array.isArray(raw.skip)
|
|
39
42
|
? raw.skip.filter((name): name is string => typeof name === "string" && name.trim().length > 0)
|
|
40
43
|
: base.skip,
|
|
@@ -101,9 +101,10 @@ export const DEFAULT_BYTEFLY_CONFIG: ByteflyConfig = {
|
|
|
101
101
|
budget: 0,
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
/** The highest `currency.decimals` the screen and the loader both accept. */
|
|
105
|
+
export const MAX_DECIMALS = 4;
|
|
105
106
|
/** Upper bound for `budget`; large enough for any real session, small enough to clamp a typo. */
|
|
106
|
-
const MAX_BUDGET = 10_000_000;
|
|
107
|
+
export const MAX_BUDGET = 10_000_000;
|
|
107
108
|
const MAX_RATE = 1_000_000;
|
|
108
109
|
const MAX_RATE_KEYS = 200;
|
|
109
110
|
const MAX_WINDOWS = 16;
|