@gavin-hu/my-pi-agent 0.11.4 → 0.13.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 +38 -26
- package/README.zh-CN.md +33 -23
- package/extensions/bytefly/README.md +381 -225
- package/extensions/bytefly/chrome.ts +7 -2
- package/extensions/bytefly/doctor/register.ts +6 -0
- package/extensions/bytefly/doctor/runtime.ts +52 -6
- package/extensions/bytefly/doctor/tools.ts +1 -3
- package/extensions/bytefly/footer/collect/snapshot.ts +12 -2
- package/extensions/bytefly/footer/config.ts +7 -1
- package/extensions/bytefly/footer/delegation.ts +100 -0
- package/extensions/bytefly/footer/price/config.ts +54 -15
- package/extensions/bytefly/footer/price/price.ts +20 -7
- package/extensions/bytefly/footer/price/types.ts +13 -7
- package/extensions/bytefly/footer/register.ts +13 -2
- package/extensions/bytefly/footer/render/format.ts +3 -2
- package/extensions/bytefly/footer/render/lines.ts +2 -0
- package/extensions/bytefly/footer/render/segments.ts +30 -2
- package/extensions/bytefly/footer/types.ts +12 -0
- package/extensions/bytefly/header/component.ts +21 -6
- package/extensions/bytefly/header/config.ts +3 -0
- package/extensions/bytefly/header/register.ts +34 -3
- package/extensions/bytefly/index.ts +15 -4
- package/extensions/bytefly/notify/config.ts +2 -0
- package/extensions/bytefly/notify/format.ts +19 -1
- package/extensions/bytefly/notify/register.ts +37 -13
- package/extensions/bytefly/notify/types.ts +2 -0
- package/extensions/bytefly/panel/commands.ts +34 -13
- package/extensions/bytefly/panel/settings-runtime.ts +71 -28
- package/extensions/bytefly/panel/settings.ts +293 -48
- package/extensions/bytefly/panel/tui/entries.ts +58 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +86 -0
- package/extensions/bytefly/panel/tui/frame.ts +71 -0
- package/extensions/bytefly/panel/tui/nav.ts +75 -0
- package/extensions/bytefly/panel/tui/settings-view.ts +115 -0
- package/extensions/bytefly/panel/tui.ts +205 -319
- package/extensions/bytefly/picker/runtime.ts +61 -2
- package/extensions/bytefly/picker/toggle.ts +56 -0
- package/extensions/bytefly/update/cache.ts +73 -0
- package/extensions/bytefly/update/check.ts +59 -0
- package/extensions/bytefly/update/format.ts +87 -0
- package/extensions/bytefly/update/register.ts +158 -0
- package/extensions/bytefly/update/state.ts +40 -0
- package/extensions/bytefly/update/version.ts +83 -0
- package/extensions/document/README.md +24 -14
- package/extensions/document/config.ts +4 -0
- package/extensions/document/deadline.ts +61 -0
- package/extensions/document/tool.ts +14 -5
- package/extensions/file-browse/README.md +12 -8
- package/extensions/file-browse/client.ts +6 -7
- package/extensions/file-browse/files.ts +0 -4
- package/extensions/file-browse/git.ts +9 -10
- package/extensions/file-browse/html.ts +17 -7
- package/extensions/file-browse/router.ts +24 -15
- package/extensions/file-open/README.md +30 -15
- package/extensions/file-open/index.ts +4 -3
- package/extensions/file-open/open.ts +52 -21
- package/extensions/goal/README.md +45 -13
- package/extensions/goal/clarify-prompt.ts +5 -36
- package/extensions/goal/clarify.ts +55 -86
- package/extensions/goal/commands.ts +41 -35
- package/extensions/goal/format.ts +12 -4
- package/extensions/goal/schema.ts +6 -2
- package/extensions/goal/tools.ts +28 -8
- package/extensions/goal/tui.ts +1 -1
- package/extensions/goal/types.ts +6 -2
- package/extensions/job/README.md +16 -12
- package/extensions/job/runtime.ts +23 -6
- package/extensions/job/status.ts +4 -2
- package/extensions/memory/README.md +56 -14
- package/extensions/memory/commands.ts +20 -2
- package/extensions/memory/doctor.ts +137 -0
- package/extensions/memory/format.ts +24 -7
- package/extensions/memory/runtime.ts +49 -0
- package/extensions/memory/store.ts +31 -0
- package/extensions/memory/tui.ts +136 -12
- package/extensions/plan/README.md +83 -36
- package/extensions/plan/blocks.ts +1 -35
- package/extensions/plan/commands.ts +84 -19
- package/extensions/plan/index.ts +35 -11
- package/extensions/plan/plan-index.ts +234 -0
- package/extensions/plan/plan-text.ts +95 -0
- package/extensions/plan/plans.ts +91 -2
- package/extensions/plan/policy.ts +4 -3
- package/extensions/plan/review.ts +246 -0
- package/extensions/plan/runtime.ts +73 -24
- package/extensions/plan/steps.ts +60 -39
- package/extensions/plan/todos.ts +29 -60
- package/extensions/plan/tools.ts +188 -244
- package/extensions/plan/tui.ts +18 -31
- package/extensions/plan/types.ts +12 -0
- package/extensions/plan/web-client.ts +51 -18
- package/extensions/plan/web-page.ts +7 -2
- package/extensions/plan/web-router.ts +11 -3
- package/extensions/plan/web.ts +106 -41
- package/extensions/question/README.md +1 -1
- package/extensions/question/tools.ts +1 -1
- package/extensions/rewind/README.md +28 -8
- package/extensions/rewind/commands.ts +1 -1
- package/extensions/rewind/format.ts +4 -4
- package/extensions/rewind/git.ts +56 -18
- package/extensions/rewind/index.ts +3 -5
- package/extensions/rewind/path-picker.ts +198 -0
- package/extensions/rewind/paths.ts +128 -0
- package/extensions/rewind/restore.ts +69 -28
- package/extensions/rewind/rewind.ts +80 -3
- package/extensions/rewind/runtime.ts +13 -5
- package/extensions/rewind/snapshot-run.ts +5 -2
- package/extensions/rewind/types.ts +2 -0
- package/extensions/subagent/README.md +7 -1
- package/extensions/subagent/accounting.ts +34 -0
- package/extensions/subagent/index.ts +23 -3
- package/extensions/subagent/stream.ts +2 -0
- package/extensions/subagent/types.ts +2 -0
- package/extensions/todo/README.md +19 -11
- package/extensions/todo/index.ts +5 -3
- package/extensions/todo/nudge.ts +58 -9
- package/extensions/todo/runtime.ts +28 -17
- package/extensions/watch/README.md +8 -3
- package/extensions/watch/index.ts +19 -0
- package/extensions/web-access/README.md +46 -16
- package/extensions/web-access/charset.ts +58 -0
- package/extensions/web-access/fetch/find.ts +11 -7
- package/extensions/web-access/fetch/format.ts +64 -18
- package/extensions/web-access/fetch/page.ts +5 -1
- package/extensions/web-access/fetch/tool.ts +20 -21
- package/extensions/web-access/http.ts +49 -8
- package/extensions/web-access/search/facts.ts +38 -0
- package/extensions/web-access/search/tool.ts +14 -3
- package/extensions/web-access/text.ts +50 -11
- package/extensions/wechat/README.md +5 -4
- package/extensions/wechat/tools.ts +0 -2
- package/extensions/worktree/README.md +74 -15
- package/extensions/worktree/config.ts +11 -0
- package/extensions/worktree/exit.ts +35 -3
- package/extensions/worktree/gaps.ts +74 -0
- package/extensions/worktree/guard.ts +50 -4
- package/extensions/worktree/handoff.ts +55 -0
- package/extensions/worktree/index.ts +12 -2
- package/extensions/worktree/runtime.ts +20 -0
- package/extensions/worktree/status.ts +15 -2
- package/extensions/worktree/tools.ts +3 -2
- package/lib/README.md +12 -9
- package/lib/config.ts +43 -1
- package/lib/env.ts +12 -0
- package/lib/events.ts +86 -1
- package/lib/goal-state.ts +95 -0
- package/lib/requirements.ts +340 -46
- package/lib/todo-state.ts +62 -0
- package/lib/tui/README.md +91 -0
- package/lib/tui/glyphs.ts +2 -0
- package/lib/web/layout.ts +12 -2
- package/package.json +5 -3
- package/themes/bytefly-dark.json +1 -1
- package/assets/bytefly.schema.json +0 -785
- package/assets/chrome.png +0 -0
- package/assets/file-browse.png +0 -0
- package/assets/job.png +0 -0
- package/assets/plan-review.png +0 -0
- package/assets/question.png +0 -0
- package/assets/rewind.png +0 -0
- package/assets/todo.png +0 -0
- package/assets/tool-renderings.png +0 -0
- package/assets/tools/ask_user_question-cancelled.png +0 -0
- package/assets/tools/ask_user_question.png +0 -0
- package/assets/tools/bash.png +0 -0
- package/assets/tools/edit.png +0 -0
- package/assets/tools/enter_plan_mode.png +0 -0
- package/assets/tools/enter_worktree.png +0 -0
- package/assets/tools/exit_plan_mode.png +0 -0
- package/assets/tools/exit_worktree.png +0 -0
- package/assets/tools/find.png +0 -0
- package/assets/tools/goal.png +0 -0
- package/assets/tools/grep.png +0 -0
- package/assets/tools/job.png +0 -0
- package/assets/tools/list_worktrees.png +0 -0
- package/assets/tools/ls.png +0 -0
- package/assets/tools/memory-long-subject.png +0 -0
- package/assets/tools/memory.png +0 -0
- package/assets/tools/notice.png +0 -0
- package/assets/tools/open_path.png +0 -0
- package/assets/tools/prune_worktrees.png +0 -0
- package/assets/tools/read.png +0 -0
- package/assets/tools/read_doc-running.png +0 -0
- package/assets/tools/read_doc.png +0 -0
- package/assets/tools/run_doctor.png +0 -0
- package/assets/tools/send_wechat.png +0 -0
- package/assets/tools/set_plan_steps.png +0 -0
- package/assets/tools/subagent.png +0 -0
- package/assets/tools/todo.png +0 -0
- package/assets/tools/turn-rule.png +0 -0
- package/assets/tools/user-message.png +0 -0
- package/assets/tools/watch.png +0 -0
- package/assets/tools/web_fetch-rejected.png +0 -0
- package/assets/tools/web_fetch.png +0 -0
- package/assets/tools/web_search.png +0 -0
- package/assets/tools/write.png +0 -0
- package/assets/tools/write_plan.png +0 -0
|
@@ -8,10 +8,14 @@ marks 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,
|
|
11
|
-
a job finishes, or the agent asks for input
|
|
11
|
+
a job finishes, or the agent asks for input, and a published-version check that
|
|
12
|
+
marks the header and posts one notice when a newer bytefly is on npm.
|
|
12
13
|
DeepSeek is priced from the provider's published yuan rates and peak tariff by
|
|
13
|
-
default
|
|
14
|
-
|
|
14
|
+
default; set `price.currency.code` to `USD` to price it from the provider's
|
|
15
|
+
published dollar table instead, under the same peak windows, with Pi's recorded
|
|
16
|
+
dollars covering a model the table does not list. Any other model can be priced
|
|
17
|
+
from your own yuan rates when Pi's
|
|
18
|
+
catalog does not match what a provider actually charges.
|
|
15
19
|
|
|
16
20
|
```
|
|
17
21
|
pi --extension ./extensions/bytefly # load just this extension
|
|
@@ -27,10 +31,10 @@ How the `run_doctor` call and its result look in the transcript — the call lin
|
|
|
27
31
|
|
|
28
32
|
## What it does
|
|
29
33
|
|
|
30
|
-
The extension owns
|
|
34
|
+
The extension owns ten surfaces: the startup header, the footer, the
|
|
31
35
|
user-message marker, the rule between completed turns, the notice block for
|
|
32
36
|
shared transcript notices, the tool-block frame, the live working counter, the
|
|
33
|
-
two-section control center, and
|
|
37
|
+
two-section control center, notifications, and the published-version check.
|
|
34
38
|
|
|
35
39
|
**Header.** A nameplate: a two-tone half-block mark from the active theme's
|
|
36
40
|
`accent` / `warn` roles, the agent identity (`Pi`, bold, and its version), the
|
|
@@ -38,7 +42,8 @@ chrome identity (`enhanced by bytefly` and the package version), a signature
|
|
|
38
42
|
rule whose `accent` lead sits under the mark, and a grouped help block. The help
|
|
39
43
|
keys resolve live from Pi's keybindings, so a rebound key shows the user's
|
|
40
44
|
chord; the identity and help text are `meta` / `faint` so the mark keeps the
|
|
41
|
-
eye. It
|
|
45
|
+
eye. It opens with a blank row, so the nameplate clears the top edge. It is
|
|
46
|
+
installed only in interactive (`tui`) sessions when Pi's
|
|
42
47
|
`quietStartup` is `true` — the Settings row **Bytefly startup TUI** — and
|
|
43
48
|
`false`, `"header"`, and unset leave Pi's built-in header in place; on terminals
|
|
44
49
|
that cannot render half blocks the mark falls back to a two-tone `Pi` wordmark.
|
|
@@ -52,9 +57,9 @@ that cannot render half blocks the mark falls back to a two-tone `Pi` wordmark.
|
|
|
52
57
|
/ commands · ! bash · !! bash (no context) · /bytefly extensions
|
|
53
58
|
```
|
|
54
59
|
|
|
55
|
-
As the terminal narrows, the identity sheds the
|
|
56
|
-
line, then the agent name, and the help rows drop whole
|
|
57
|
-
clips mid-word. The nameplate sits on the transcript margin, which follows Pi's
|
|
60
|
+
As the terminal narrows, the identity sheds the update marker, then the chrome
|
|
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. The nameplate sits on the transcript margin, which follows Pi's
|
|
58
63
|
`outputPad` (the value the tool frame and the notice use too), and the help hangs
|
|
59
64
|
one body indent inside it. The mark, tokens, identity, and grouped help are tuned
|
|
60
65
|
in [`header/config.ts`](./header/config.ts).
|
|
@@ -64,18 +69,25 @@ in [`header/config.ts`](./header/config.ts).
|
|
|
64
69
|
- **Line 1 — identity:** `pwd` and the session name on the left; git state
|
|
65
70
|
(`⎇ branch · ⑂ worktree`) and the `◉` browse chip on the right.
|
|
66
71
|
- **Line 2 — resources:** the context gauge and usage meters on the left, the
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
child-run roll-up (`↳ 3 · ¥0.12`, present only once a subagent has finished),
|
|
73
|
+
the mode/alert slot (`⋮ plan`) trailing after a `╎` when present, and the model
|
|
74
|
+
and thinking level on the right. The gauge is anchored at column 0, so `%` is
|
|
69
75
|
always in the same place.
|
|
76
|
+
The roll-up is its own number, never folded into the session's cost: a subagent
|
|
77
|
+
runs in its own process, and the footer prices those runs with the same rate
|
|
78
|
+
table and currency as the session so a currency change moves both. It covers
|
|
79
|
+
the last 500 child runs of the current process, and starts from zero after
|
|
80
|
+
`/resume` (the events are not session entries).
|
|
70
81
|
|
|
71
82
|
```
|
|
72
|
-
~/repo/my-pi-agent
|
|
73
|
-
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% ╎ ⋮ plan opus-4.5 · high
|
|
83
|
+
~/repo/my-pi-agent ⎇ main · ⑂ smoke
|
|
84
|
+
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↳ 2 · ¥0.06 · ↑42k ↓8.0k · R96k CH 87% ╎ ⋮ plan opus-4.5 · high
|
|
74
85
|
```
|
|
75
86
|
|
|
76
|
-
(The examples show the default `¥` symbol.
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
(The examples show the default `¥` symbol. A model that neither `rates` nor the
|
|
88
|
+
built-in table covers contributes nothing in `CNY` mode, so the bar shows no
|
|
89
|
+
figure rather than a guessed one; `USD` mode adds Pi's recorded dollars for that
|
|
90
|
+
record instead — see the currency note under [Configuration](#configuration).)
|
|
79
91
|
|
|
80
92
|
With no plan mode there is no mode slot, so the `╎` is omitted:
|
|
81
93
|
|
|
@@ -113,8 +125,9 @@ rounding to a misleading `¥0.3` — and a positive amount below one minor unit
|
|
|
113
125
|
shows as `¥<0.01` rather than `¥0.00`.
|
|
114
126
|
|
|
115
127
|
DeepSeek needs no config file at all: the bar prices it from the published yuan
|
|
116
|
-
rates and peak tariff built into [`footer/price/config.ts`](./footer/price/config.ts).
|
|
117
|
-
`bytefly.json`
|
|
128
|
+
rates and peak tariff built into [`footer/price/config.ts`](./footer/price/config.ts).
|
|
129
|
+
A `bytefly.json` can switch to the published dollar table, add models of your own,
|
|
130
|
+
or replace a built-in entry:
|
|
118
131
|
|
|
119
132
|
```
|
|
120
133
|
~/repo/project ⎇ main
|
|
@@ -259,15 +272,26 @@ underneath, and `run_doctor` returns the same report as text and typed data.
|
|
|
259
272
|
Only a missing
|
|
260
273
|
*hard* requirement posts a notice at session start; everything else waits in the
|
|
261
274
|
report.
|
|
262
|
-
See [The
|
|
275
|
+
See [The control center](#the-control-center).
|
|
263
276
|
|
|
264
277
|
**Notify.** Off by default, one `notify` section in `bytefly.json` turns on OS
|
|
265
278
|
notifications for unattended runs: when a full agent run settles, when a
|
|
266
|
-
background job finishes,
|
|
279
|
+
background job finishes, when the agent opens a prompt asking for input, and when
|
|
280
|
+
a `watch` wake match fires while the agent is idle. The
|
|
267
281
|
method follows the terminal (OSC 777, OSC 99, a Windows toast) or falls back to
|
|
268
282
|
the platform notifier (`osascript`, `notify-send`); see
|
|
269
283
|
[Notifications](#notifications).
|
|
270
284
|
|
|
285
|
+
**Update check.** In the background at an interactive session's start, the loaded
|
|
286
|
+
version is compared with the published `@gavin-hu/my-pi-agent` on npm. A newer
|
|
287
|
+
one puts `↑0.12.0` after the header's chrome version and posts one transcript
|
|
288
|
+
notice naming `pi update --extensions` and its release URL. The answer is cached
|
|
289
|
+
in `<agentDir>/bytefly/update.json` with the time it was read, so a session
|
|
290
|
+
starting inside the 24-hour window reuses it instead of repeating the request,
|
|
291
|
+
and `/bytefly update` re-runs the check on demand. The check is skipped when
|
|
292
|
+
`PI_OFFLINE` is set, makes no request outside `tui`, and a failed background
|
|
293
|
+
request is silent; see [Update check](#update-check).
|
|
294
|
+
|
|
271
295
|
## Commands
|
|
272
296
|
|
|
273
297
|
| Command | What it does |
|
|
@@ -275,9 +299,10 @@ the platform notifier (`osascript`, `notify-send`); see
|
|
|
275
299
|
| `/bytefly [extension] [refresh]` | Open the control center on Extensions. In `tui` it mounts a docked list; every other mode prints the grouped listing, or one extension's full findings when named. |
|
|
276
300
|
| `/bytefly settings` | Open the control center on Settings. Outside `tui` it prints the grouped settings listing with each row's value. |
|
|
277
301
|
| `/bytefly doctor [extension] [refresh]` | Print the doctor report. Outside `tui` it prints `run_doctor`'s text for every extension, or one named extension's findings; in `tui` it mounts the same docked screen. |
|
|
302
|
+
| `/bytefly update` | Re-run the published-version check now, ignoring the cache window, and report what it found — the newest version, an available release with its URL, `PI_OFFLINE`, or an unreadable registry. It resolves no packages and probes no extension. |
|
|
278
303
|
|
|
279
304
|
Tab completes the subcommands, extension names after `doctor`, and `refresh`;
|
|
280
|
-
`/bytefly settings`
|
|
305
|
+
`/bytefly settings` and `/bytefly update` take no name.
|
|
281
306
|
|
|
282
307
|
## Behaviour by mode
|
|
283
308
|
|
|
@@ -303,45 +328,36 @@ print/JSON/RPC run gets an OS-level notification or none.
|
|
|
303
328
|
|
|
304
329
|
## Configuration
|
|
305
330
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
[`separator/config.ts`](./separator/config.ts)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
top-level section per extension (see
|
|
322
|
-
It is read from the global
|
|
323
|
-
per-extension
|
|
324
|
-
(`document.json`, `jobs.json`, …) are no longer read; run
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
The
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
Both documents are optional, and neither is needed for DeepSeek. The built-in
|
|
333
|
-
table is the base layer for every session: DeepSeek is priced in yuan at the
|
|
334
|
-
published peak and off-peak rates, and every other model keeps Pi's recorded
|
|
335
|
-
cost, whose number comes from Pi's catalog. A document can change the currency,
|
|
336
|
-
replace a built-in rate, or add rates of your own. A malformed document is
|
|
337
|
-
ignored, a wrong-typed value falls back to its default, and an unknown timezone
|
|
338
|
-
drops the schedule rather than the session.
|
|
331
|
+
Two things are JSON: the transcript rail's glyph (`chrome.rail`, default `╎`,
|
|
332
|
+
read at `session_start`/`session_tree`) and this extension's three config
|
|
333
|
+
sections — `price`, `notify`, and `doctor`. Everything else about the look is
|
|
334
|
+
tuned in code: the header in [`header/config.ts`](./header/config.ts) (mark and
|
|
335
|
+
identity tokens, tagline, grouped help), the footer in
|
|
336
|
+
[`footer/config.ts`](./footer/config.ts) (thresholds, gauge glyphs and widths,
|
|
337
|
+
separators, icons, status keys), and the turn separator in
|
|
338
|
+
[`separator/config.ts`](./separator/config.ts) (glyph, label prefix, padding,
|
|
339
|
+
colors).
|
|
340
|
+
|
|
341
|
+
Every key of every section is listed with its type, default, and bounds in the
|
|
342
|
+
[configuration reference](../../docs/references/bytefly-config.md); this section
|
|
343
|
+
covers what that table cannot — when each section is read, and what the cost
|
|
344
|
+
does when no rate matches.
|
|
345
|
+
|
|
346
|
+
`bytefly.json` is one shared document with a top-level section per extension (see
|
|
347
|
+
[Configuration](../../docs/configuration.md)). It is read from the global
|
|
348
|
+
document, then `<cwd>/.pi/bytefly.json`, project winning. The old per-extension
|
|
349
|
+
files (`document.json`, `jobs.json`, …) are no longer read; `bun run config:migrate`
|
|
350
|
+
moves them into the shared document.
|
|
351
|
+
|
|
352
|
+
Neither document is needed for DeepSeek. The built-in table for the selected
|
|
353
|
+
currency is the base layer for every session: `CNY` prices DeepSeek in yuan and
|
|
354
|
+
`USD` in dollars, both at the published peak and off-peak rates. A document can
|
|
355
|
+
change the currency, replace a built-in rate, or add rates of your own.
|
|
339
356
|
|
|
340
357
|
```json
|
|
341
358
|
{
|
|
342
359
|
"price": {
|
|
343
|
-
"currency": { "
|
|
344
|
-
"fallbackToRecordedCost": true,
|
|
360
|
+
"currency": { "code": "CNY", "decimals": 2 },
|
|
345
361
|
"rates": {
|
|
346
362
|
"deepseek/deepseek-flash": { "input": 2, "output": 8, "cacheRead": 0.04, "cacheWrite": 0, "offPeakMultiplier": 0.5 },
|
|
347
363
|
"deepseek/deepseek-v4-pro": { "input": 9, "output": 27, "cacheRead": 0.3, "cacheWrite": 0, "offPeakMultiplier": 0.5 }
|
|
@@ -357,101 +373,45 @@ drops the schedule rather than the session.
|
|
|
357
373
|
}
|
|
358
374
|
```
|
|
359
375
|
|
|
360
|
-
That file restates the built-in defaults, so on its own it changes nothing; it
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
| Key | Meaning |
|
|
364
|
-
|---|---|
|
|
365
|
-
| `currency.symbol` | Printed before the number; at most four characters. Default `¥`. |
|
|
366
|
-
| `currency.decimals` | Digits after the point, 0–4. Default `2`. |
|
|
367
|
-
| `fallbackToRecordedCost` | Use Pi's recorded cost for a record no rate matches. Default `true`. |
|
|
368
|
-
| `rates` | Per-million-token prices in `currency`, keyed by `provider/model`; the built-in DeepSeek table is the base, and a key stated here replaces its entry. |
|
|
369
|
-
| `rates.<key>.input` · `output` · `cacheRead` · `cacheWrite` | Rate per token class; a class left out is free. |
|
|
370
|
-
| `rates.<key>.offPeakMultiplier` | Multiplier outside every peak window, 0–1. Default `1`. |
|
|
371
|
-
| `peak.timezone` | The IANA zone the windows are stated in. |
|
|
372
|
-
| `peak.windows` | `days` (1 = Monday … 7 = Sunday; omitted means every day), `from` inclusive, `to` exclusive, `HH:MM` local; a `to` before `from` wraps midnight. An empty list means every moment is peak. |
|
|
376
|
+
That file restates the built-in defaults, so on its own it changes nothing; it is
|
|
377
|
+
shown to document every key at once.
|
|
373
378
|
|
|
374
379
|
Rates merge key by key across the two documents, so a project document can
|
|
375
380
|
override one model without restating the rest; a key present in both is replaced,
|
|
376
381
|
not merged field by field. No windows means every moment is peak, so a rate with
|
|
377
|
-
`offPeakMultiplier: 0.5` and no schedule stays flat.
|
|
378
|
-
once per install — at `session_start`, and again on a `session_tree` change — so
|
|
379
|
-
a new session or tree change picks up an edit; there is no toggle, and nothing
|
|
380
|
-
watches the file.
|
|
381
|
-
|
|
382
|
-
**Mixing currencies is the sharp edge of the built-in default.** A record priced
|
|
383
|
-
from `rates` or the built-in table is in `currency.symbol` units — yuan for
|
|
384
|
-
DeepSeek — while a record that fell back carries Pi's USD number under that same
|
|
385
|
-
symbol. A session mixing DeepSeek with another provider therefore sums two
|
|
386
|
-
currencies into one number. Either cover every model you use (a `"*"` catch-all
|
|
387
|
-
helps) and set `fallbackToRecordedCost: false`, or set the symbol to `"$"` and
|
|
388
|
-
treat the rates purely as an off-peak correction.
|
|
389
|
-
|
|
390
|
-
### The `notify` section
|
|
391
|
-
|
|
392
|
-
The notify capability reads the same shared `bytefly.json`, under its own
|
|
393
|
-
top-level `notify` key.
|
|
394
|
-
It is **off by default**; `enabled: true` is the whole setup needed for an
|
|
395
|
-
unattended run, and the three trigger flags then narrow what is announced. The
|
|
396
|
-
price section and the notify section are parsed independently, so a wrong-typed
|
|
397
|
-
value in one never reaches the other.
|
|
398
|
-
|
|
399
|
-
```json
|
|
400
|
-
{
|
|
401
|
-
"notify": {
|
|
402
|
-
"enabled": true,
|
|
403
|
-
"turnFinished": true,
|
|
404
|
-
"needsInput": true,
|
|
405
|
-
"jobFinished": true,
|
|
406
|
-
"method": "auto",
|
|
407
|
-
"title": "Pi"
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
| Key | Default | Meaning |
|
|
413
|
-
|---|---|---|
|
|
414
|
-
| `enabled` | `false` | Master switch. A notification on every turn is intrusive, so it is opt-in. |
|
|
415
|
-
| `turnFinished` | `true` | Notify when a full agent run settles and Pi waits for the next message. An aborted run is never announced. |
|
|
416
|
-
| `needsInput` | `true` | Notify when the agent opens a prompt mid-run (`select`, `confirm`, `input`, `editor`, `custom`). A prompt the user typed while idle does not fire. |
|
|
417
|
-
| `jobFinished` | `true` | Notify when a background `job` reaches a terminal state. |
|
|
418
|
-
| `method` | `auto` | `auto`, `osascript`, `notify-send`, `toast`, `osc777`, or `osc99`. An explicit method that the environment cannot deliver is a no-op. |
|
|
419
|
-
| `title` | `Pi` | Notification title, clamped to 40 characters. |
|
|
420
|
-
|
|
421
|
-
A `notify` section is read at `session_start`, so a new session or `/reload`
|
|
422
|
-
picks up an edit.
|
|
423
|
-
|
|
424
|
-
### The `doctor` section
|
|
382
|
+
`offPeakMultiplier: 0.5` and no schedule stays flat.
|
|
425
383
|
|
|
426
|
-
|
|
427
|
-
|
|
384
|
+
**Only configured models get a price.** A record priced from `rates` or the
|
|
385
|
+
built-in table contributes to the total. A model with no matching rate differs by
|
|
386
|
+
currency: `CNY` contributes nothing, so the bar shows no cost rather than a
|
|
387
|
+
mixed-currency figure, while `USD` adds Pi's recorded dollar amount, which is
|
|
388
|
+
already in the right unit. To price the model yourself, add a rate entry (a `"*"`
|
|
389
|
+
catch-all prices everything).
|
|
428
390
|
|
|
429
|
-
|
|
430
|
-
{
|
|
431
|
-
"doctor": {
|
|
432
|
-
"proactive": true,
|
|
433
|
-
"timeoutMs": 2000,
|
|
434
|
-
"skip": []
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
```
|
|
391
|
+
### When each section is read
|
|
438
392
|
|
|
439
|
-
|
|
|
393
|
+
| Section | Read at | Applies to |
|
|
440
394
|
|---|---|---|
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
395
|
+
| `chrome` | `session_start`, `session_tree` | the rail glyph |
|
|
396
|
+
| `price` | the footer install (`session_start`, `session_tree`) | the cost total |
|
|
397
|
+
| `notify` | `session_start` | which events are announced, and how |
|
|
398
|
+
| `doctor` | `session_start` | the proactive check |
|
|
399
|
+
|
|
400
|
+
Nothing watches a file, so an edit applies at the next read — a reload, a new
|
|
401
|
+
session, or a tree change. A malformed document is ignored by every loader and
|
|
402
|
+
named by `doctor`; a wrong-typed value falls back to its default, and a
|
|
403
|
+
wrong-typed timezone drops the peak schedule rather than the session.
|
|
404
|
+
|
|
405
|
+
The notify capability is **off by default**: `enabled: true` is the whole setup
|
|
406
|
+
needed for an unattended run, and the four trigger flags then narrow what is
|
|
407
|
+
announced (see [Notifications](#notifications)).
|
|
408
|
+
|
|
409
|
+
The proactive doctor check is part of the bytefly startup TUI, so it posts only
|
|
410
|
+
when Pi starts with `"quietStartup": true`, the same setting that hides Pi's own
|
|
411
|
+
header and loaded-resource listing. `false`, `"header"`, and unset leave Pi's
|
|
412
|
+
startup surface in charge, so the notice stays silent. When it does post, the
|
|
413
|
+
notice enters the message flow once per session, so it stays in scrollback and
|
|
414
|
+
the model reads the same sentence.
|
|
455
415
|
|
|
456
416
|
## Security
|
|
457
417
|
|
|
@@ -464,11 +424,12 @@ session, so it stays in scrollback and the model reads the same sentence.
|
|
|
464
424
|
## Cost calculation
|
|
465
425
|
|
|
466
426
|
Pi prices every model call from its bundled catalog in USD, and the provider API
|
|
467
|
-
never returns a price.
|
|
468
|
-
they match:
|
|
427
|
+
never returns a price. The footer can total that catalog directly, or replace
|
|
428
|
+
it with its own rates for the models they match:
|
|
469
429
|
|
|
470
430
|
- **Which built-in rates.** [`footer/price/config.ts`](./footer/price/config.ts) ships DeepSeek's published
|
|
471
|
-
yuan prices
|
|
431
|
+
yuan prices for `CNY` and its published dollar prices for `USD`, per model, with
|
|
432
|
+
the peak schedule that doubles the price during peak hours
|
|
472
433
|
([DeepSeek's pricing page](https://api-docs.deepseek.com/quick_start/pricing)).
|
|
473
434
|
They are ordinary rate entries, so a `bytefly.json` key replaces one of
|
|
474
435
|
them, and the schedule is the default `peak` every rate is judged against.
|
|
@@ -482,9 +443,16 @@ they match:
|
|
|
482
443
|
applied at the local time the tokens were actually spent: an off-peak turn
|
|
483
444
|
stays off-peak when peak starts. A record with no usable timestamp is priced
|
|
484
445
|
at peak, because it cannot be dated.
|
|
485
|
-
- **Which
|
|
486
|
-
|
|
487
|
-
`
|
|
446
|
+
- **Which algorithm.** `price.currency.code` chooses `CNY` (the default) or
|
|
447
|
+
`USD`. Both price matched records from `rates` and the same `peak` tariff; the
|
|
448
|
+
code selects which built-in table `rates` starts from — DeepSeek's yuan prices
|
|
449
|
+
or its dollar prices — and the two are separate methods, not an exchange rate.
|
|
450
|
+
Neither mode converts one currency into the other. `currency.decimals` only
|
|
451
|
+
changes how the total is printed; the symbol follows the code.
|
|
452
|
+
- **Which source.** Both currencies price a matched record from `rates`; only
|
|
453
|
+
the unmatched case differs. `CNY` contributes nothing for a model it cannot
|
|
454
|
+
price, while `USD` adds Pi's recorded dollar amount for that record, because
|
|
455
|
+
that number is already dollars.
|
|
488
456
|
|
|
489
457
|
For example, DeepSeek flash at `¥2` in and `¥8` out per million, with the
|
|
490
458
|
published peak windows and `offPeakMultiplier: 0.5`, prices a 1M-in + 1M-out
|
|
@@ -598,6 +566,12 @@ installs or fetches a package.
|
|
|
598
566
|
into `extensions` (and package filter objects) in the active scope. A row in
|
|
599
567
|
the trailing `Not resolved` group has no pattern to write, so `Space` says so
|
|
600
568
|
instead of doing nothing.
|
|
569
|
+
- Clears the focused extension's project override with `x` in Project scope: the
|
|
570
|
+
`+`/`-` entry the toggle wrote is removed (with the plain path entry beside it
|
|
571
|
+
for an inherited personal resource, and a project package entry the toggle
|
|
572
|
+
created), and the row returns to its inherited state. A row with no override
|
|
573
|
+
says so instead of writing, and Global scope does not offer the key — the
|
|
574
|
+
personal file is the base layer.
|
|
601
575
|
- Switches the write scope with `Tab`: **Global** (personal `settings.json`) or
|
|
602
576
|
**Project** (`.pi/settings.json`, only when the project is trusted; an
|
|
603
577
|
untrusted project refuses with a notice).
|
|
@@ -611,37 +585,64 @@ installs or fetches a package.
|
|
|
611
585
|
without leaving the session.
|
|
612
586
|
|
|
613
587
|
**Settings.** The second section uses the same frame and the same `Tab` scope
|
|
614
|
-
switch, with its own cursor. It lists
|
|
588
|
+
switch, with its own cursor. It lists fifteen preferences in six groups:
|
|
615
589
|
|
|
616
590
|
```
|
|
617
|
-
bytefly
|
|
618
|
-
Extensions · Settings
|
|
619
|
-
|
|
620
|
-
Startup
|
|
621
|
-
|
|
622
|
-
Notifications
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
591
|
+
bytefly ──────────────────────────────────────────────────────────────
|
|
592
|
+
Extensions · Settings Global · 15 settings
|
|
593
|
+
|
|
594
|
+
Startup (1)
|
|
595
|
+
❯ ◻ Bytefly startup TUI Off
|
|
596
|
+
Notifications (7)
|
|
597
|
+
◻ Desktop notifications Off
|
|
598
|
+
◼ Turn finished On
|
|
599
|
+
◼ Needs input On
|
|
600
|
+
◼ Job finished On
|
|
601
|
+
◼ Watcher matched On
|
|
602
|
+
Delivery method Auto
|
|
603
|
+
Notification title Pi
|
|
604
|
+
Diagnostics (2)
|
|
605
|
+
◼ Proactive health check On
|
|
606
|
+
Probe timeout 2000
|
|
607
|
+
Pricing (2)
|
|
608
|
+
Currency CNY (¥)
|
|
609
|
+
Cost decimals 2
|
|
610
|
+
Transcript (2)
|
|
611
|
+
Rail glyph ╎
|
|
612
|
+
Output padding 1
|
|
613
|
+
Terminal (1)
|
|
614
|
+
◻ Hardware cursor Off
|
|
630
615
|
Bytefly startup TUI — Pi setting
|
|
631
616
|
Use bytefly's startup header in place of Pi's startup TUI.
|
|
632
617
|
Default · ~/.pi/agent/settings.json
|
|
618
|
+
|
|
619
|
+
Esc close · ←/→ · Space change · Tab scope · ↑/↓ select
|
|
633
620
|
```
|
|
634
621
|
|
|
635
622
|
| Group | Row | Source | Values |
|
|
636
623
|
|---|---|---|---|
|
|
637
624
|
| Startup | Bytefly startup TUI | Pi `settings.json` (`quietStartup`) | Off, On |
|
|
638
|
-
| Notifications | Desktop notifications | `
|
|
639
|
-
|
|
|
625
|
+
| Notifications | Desktop notifications | `notify.enabled` | Off, On |
|
|
626
|
+
| Notifications | Turn finished | `notify.turnFinished` | Off, On |
|
|
627
|
+
| Notifications | Needs input | `notify.needsInput` | Off, On |
|
|
628
|
+
| Notifications | Job finished | `notify.jobFinished` | Off, On |
|
|
629
|
+
| Notifications | Watcher matched | `notify.matchFound` | Off, On |
|
|
630
|
+
| Notifications | Delivery method | `notify.method` | Auto, OSC 777, OSC 99, Windows toast, macOS, Linux |
|
|
631
|
+
| Notifications | Notification title | `notify.title` | Text, up to 40 characters |
|
|
632
|
+
| Diagnostics | Proactive health check | `doctor.proactive` | Off, On |
|
|
633
|
+
| Diagnostics | Probe timeout | `doctor.timeoutMs` | Number, 100–30000 ms |
|
|
634
|
+
| Pricing | Currency | `price.currency.code` | CNY, USD |
|
|
635
|
+
| Pricing | Cost decimals | `price.currency.decimals` | 0, 1, 2, 3, 4 |
|
|
636
|
+
| Transcript | Rail glyph | `chrome.rail` | Text, exactly one column |
|
|
640
637
|
| Transcript | Output padding | Pi `settings.json` | 0, 1 |
|
|
641
638
|
| Terminal | Hardware cursor | Pi `settings.json` | Off, On |
|
|
642
639
|
|
|
640
|
+
Choosing **Currency** also writes that currency's default precision. Both rows
|
|
641
|
+
stay editable afterwards, so the footer follows the explicit values.
|
|
642
|
+
|
|
643
643
|
`Space` (or `Enter`) changes the focused row: a boolean toggles, an enum cycles
|
|
644
|
-
forward
|
|
644
|
+
forward, and a text row opens an inline editor on its value — the notification
|
|
645
|
+
title, the probe timeout, and the rail glyph. The detail pane names the source, describes the preference, and shows
|
|
645
646
|
where the value is written — `Global · ~/.pi/agent/settings.json`, `Project ·
|
|
646
647
|
.pi/bytefly.json`, or `Inherited from Global · .pi/bytefly.json`. A successful
|
|
647
648
|
change marks the screen `Saved · reload to apply` — even when the terminal is too
|
|
@@ -656,21 +657,29 @@ with `true` or `false`.
|
|
|
656
657
|
|
|
657
658
|
App rows are **Global only**: Pi's setters for them write the personal
|
|
658
659
|
`settings.json`, and only resource paths have public project setters. Project
|
|
659
|
-
scope therefore lists the
|
|
660
|
+
scope therefore lists the twelve `bytefly.json` rows and a
|
|
660
661
|
`App settings are Global only.` note rather than promising a write that cannot
|
|
661
|
-
happen. A `bytefly.json` row writes one
|
|
662
|
-
|
|
663
|
-
|
|
662
|
+
happen. A `bytefly.json` row writes one leaf in the selected scope (a depth-1
|
|
663
|
+
key, or a nested one such as `price.currency.decimals`) through a per-key
|
|
664
|
+
writer, so every sibling key, section, `$schema`, and `version` is preserved. A
|
|
665
|
+
malformed document is refused with a notice instead of overwritten.
|
|
664
666
|
|
|
665
667
|
The non-`tui` form prints the headline, the groups, and `Label: Value` rows:
|
|
666
668
|
|
|
667
669
|
```
|
|
668
|
-
bytefly: Settings · Global ·
|
|
670
|
+
bytefly: Settings · Global · 15 settings
|
|
669
671
|
|
|
670
672
|
Startup (1)
|
|
671
673
|
Bytefly startup TUI: Off
|
|
672
|
-
|
|
674
|
+
|
|
675
|
+
Notifications (7)
|
|
673
676
|
Desktop notifications: Off
|
|
677
|
+
Turn finished: On
|
|
678
|
+
Needs input: On
|
|
679
|
+
Job finished: On
|
|
680
|
+
Watcher matched: On
|
|
681
|
+
Delivery method: Auto
|
|
682
|
+
Notification title: Pi
|
|
674
683
|
...
|
|
675
684
|
```
|
|
676
685
|
|
|
@@ -682,6 +691,14 @@ means reduced capability — an optional package that is absent, a config file
|
|
|
682
691
|
that fails to parse, a login that has not happened. Doctor never changes
|
|
683
692
|
anything and never installs anything.
|
|
684
693
|
|
|
694
|
+
`web-access` publishes what it resolved at `session_start`, so the report names
|
|
695
|
+
the search provider and why it won (an explicit choice, the `auto` rule, or the
|
|
696
|
+
keyless default), and — when the provider takes a key and no literal `apiKey` is
|
|
697
|
+
set — its API-key variable and whether that variable is set. The key value is
|
|
698
|
+
never part of the report, only its variable name. Reachability is the one check
|
|
699
|
+
that touches the network, so it runs only on an explicit `/bytefly refresh`:
|
|
700
|
+
a session-start check stays offline.
|
|
701
|
+
|
|
685
702
|
`/bytefly <extension>` narrows the same screen to that extension; `Enter`
|
|
686
703
|
expands its findings, including the checks that passed, into the free height.
|
|
687
704
|
`/bytefly refresh` ignores the findings cached earlier in the session. Every
|
|
@@ -693,8 +710,12 @@ headline; a named extension prints its full findings instead.
|
|
|
693
710
|
| Key | Effect |
|
|
694
711
|
|---|---|
|
|
695
712
|
| `←` / `→` | Show the Extensions or Settings section. |
|
|
696
|
-
| `Space` | Toggle the focused extension (Extensions); change the focused setting (Settings). |
|
|
697
|
-
| `
|
|
713
|
+
| `Space` | Toggle the focused extension (Extensions); change the focused setting (Settings), or open the editor on a text row. |
|
|
714
|
+
| `x` | Clear the focused extension's project override (Extensions, Project scope). |
|
|
715
|
+
| `Enter` | Expand or collapse the focused extension's findings (Extensions); change the focused setting, or open the editor on a text row (Settings). |
|
|
716
|
+
| `←` / `→`, `Home` / `End` (`Ctrl+A` / `Ctrl+E`) | Move the editor's caret while a text row is being edited. |
|
|
717
|
+
| `Backspace` / `Delete`, `Ctrl+U` | Edit the buffer: delete a character, or clear it. |
|
|
718
|
+
| `Enter` / `Esc` (editing) | Save the buffer, or cancel and restore the stored value. |
|
|
698
719
|
| `Tab` | Switch between Global and Project scope, in either section. |
|
|
699
720
|
| `r` | Re-probe the doctor (Extensions). |
|
|
700
721
|
| `↑`/`↓`, `j`/`k` | Move the selection, in either section. |
|
|
@@ -720,7 +741,7 @@ has a keyboard path.
|
|
|
720
741
|
| wechat | — | `wechat/credentials.json` (not logged in) |
|
|
721
742
|
| job | — | Windows has no `EXIT` trap; `ps` is POSIX-only |
|
|
722
743
|
| todo, goal | — | their config files |
|
|
723
|
-
| bytefly | — | TUI only for the header, footer, and turn rule; the screen's project scope needs a trusted project; `bytefly.json` |
|
|
744
|
+
| bytefly | — | TUI only for the header, footer, and turn rule; the screen's project scope needs a trusted project; an explicit `notify.method` this host cannot deliver; `bytefly.json` |
|
|
724
745
|
| question | — | needs an interactive UI |
|
|
725
746
|
|
|
726
747
|
Every extension that reads a config file also gets the malformed-JSON check:
|
|
@@ -732,7 +753,7 @@ the same entry in `lib/requirements.ts`, so the two cannot drift.
|
|
|
732
753
|
|
|
733
754
|
The notify capability exists for unattended runs: start a long turn, leave the
|
|
734
755
|
terminal, and get a desktop notification when Pi needs you — or when it is done.
|
|
735
|
-
It fires on
|
|
756
|
+
It fires on four events, each independently switchable in the `notify` section
|
|
736
757
|
of `bytefly.json` (see [Configuration](#configuration)):
|
|
737
758
|
|
|
738
759
|
- **A run settled** (`agent_settled`). Pi finished the turn and waits for the
|
|
@@ -745,6 +766,12 @@ of `bytefly.json` (see [Configuration](#configuration)):
|
|
|
745
766
|
- **A background job finished.** The [job](../job/) extension announces each
|
|
746
767
|
terminal transition on the shared `pi.events` bus
|
|
747
768
|
(`my-pi-agent/job-finished`); bytefly listens and never imports it.
|
|
769
|
+
- **A watcher matched.** A [watch](../watch/) whose `wake` is on matched while the
|
|
770
|
+
agent was idle, and announced it on `my-pi-agent/watch-match` with the cycle's
|
|
771
|
+
count, the watcher, and its newest line. The body names that line, sanitized and
|
|
772
|
+
clipped to 80 characters, because the line is the alert: a watch usually tails a
|
|
773
|
+
build or an error log. A match the agent was busy for does not notify — it is
|
|
774
|
+
collected for the next turn instead.
|
|
748
775
|
|
|
749
776
|
### Delivery method
|
|
750
777
|
|
|
@@ -775,6 +802,64 @@ adds its id, status, exit code, and label. Every piece that can carry model- or
|
|
|
775
802
|
user-authored text — a prompt title, a job label — is sanitized to one
|
|
776
803
|
control-free line before it reaches an escape sequence or a shell argument.
|
|
777
804
|
|
|
805
|
+
## Update check
|
|
806
|
+
|
|
807
|
+
A session can be days behind npm without anyone noticing, and Pi's own startup
|
|
808
|
+
check only covers package sources it manages — a `pi -e ./`, a linked checkout,
|
|
809
|
+
or a git install gets nothing. So bytefly reads its own published version and
|
|
810
|
+
reports it once, in the two places it already owns:
|
|
811
|
+
|
|
812
|
+
```
|
|
813
|
+
█▀█ Pi v1.3.0
|
|
814
|
+
█▀ █ enhanced by bytefly v0.11.4 ↑0.12.0
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
```
|
|
818
|
+
bytefly · 0.12.0 is available
|
|
819
|
+
╎ ◌ current · v0.11.4
|
|
820
|
+
╎ ◌ update · pi update --extensions
|
|
821
|
+
╎ ◌ release · https://github.com/gavin-hu/my-pi-agent/releases/tag/v0.12.0
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
The marker is the shared `↑` glyph and the newest version — the arrow is one cell
|
|
825
|
+
wide, so the header's column math is unchanged — painted with the header's
|
|
826
|
+
`accent` role: an action rather than a warning, so the muted version beside it
|
|
827
|
+
stays quiet and the transcript is not turned yellow for a normal state. It is
|
|
828
|
+
part of the chrome cell, so the two identity cells still line up, and it is the
|
|
829
|
+
first thing the identity sheds when the terminal narrows.
|
|
830
|
+
|
|
831
|
+
The notice is the shared transcript block: the `bytefly · title` headline at the
|
|
832
|
+
margin, then the loaded version and the fixing command behind the rail, all at
|
|
833
|
+
`info` severity because nothing is broken. It stays in scrollback and the model
|
|
834
|
+
reads the same sentence in plain text.
|
|
835
|
+
|
|
836
|
+
The request is `GET https://registry.npmjs.org/@gavin-hu%2Fmy-pi-agent/latest`,
|
|
837
|
+
fired without awaiting at `session_start` (Pi awaits handlers, and a five-second
|
|
838
|
+
registry call must not hold the first prompt) with a five-second abort. Only a
|
|
839
|
+
strictly newer, parseable semver announces anything: a non-ok status, a body
|
|
840
|
+
that is not JSON, a missing version, a network error, or an older or equal
|
|
841
|
+
version is silent. `PI_OFFLINE` (Pi's own switch) turns the check off;
|
|
842
|
+
`PI_SKIP_VERSION_CHECK` only governs Pi's `pi.dev` request.
|
|
843
|
+
|
|
844
|
+
The answer is cached in `<agentDir>/bytefly/update.json` as `latest` plus the
|
|
845
|
+
epoch milliseconds it was read at. A session that starts inside the 24-hour
|
|
846
|
+
window reuses that entry — painting the marker and posting the notice from it —
|
|
847
|
+
instead of repeating the request, so a working day of sessions costs one read.
|
|
848
|
+
Past the window the check runs again and rewrites the entry; a failed read writes
|
|
849
|
+
nothing, so the next session tries again. Offline, the cached entry is used with
|
|
850
|
+
no request and no write, because a stale version is still the best answer
|
|
851
|
+
available and the marker is advisory.
|
|
852
|
+
|
|
853
|
+
`/bytefly update` is the on-demand half: it ignores the window, reads the registry
|
|
854
|
+
now, records and paints the result, and prints one line — `up to date — v0.12.0
|
|
855
|
+
is the newest published version`, `v0.13.0 is available (current 0.12.0) —
|
|
856
|
+
https://…/releases/tag/v0.13.0`, `the update check was skipped (PI_OFFLINE is
|
|
857
|
+
set)`, or `could not read the published version`. It resolves no packages, probes
|
|
858
|
+
no extension, and posts no second notice, so a session open for days can see a
|
|
859
|
+
release without a restart. Opening the control center never checks: the screen has
|
|
860
|
+
no update surface, and the only request paths are `session_start` and this
|
|
861
|
+
command.
|
|
862
|
+
|
|
778
863
|
## Limitations
|
|
779
864
|
|
|
780
865
|
- **Bytefly startup TUI is the startup surface only.** It installs or removes
|
|
@@ -788,19 +873,42 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
788
873
|
- **Project overrides of app settings are not shown.** App rows read the
|
|
789
874
|
personal file; a `.pi/settings.json` override still wins at runtime, and
|
|
790
875
|
`pi config` is the place to manage it.
|
|
791
|
-
- **
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
876
|
+
- **Arrays and tables stay JSON.** A row is one boolean, enum, string, or number.
|
|
877
|
+
`doctor.skip` (an array), the `rates` table, and `price.peak` (a nested object)
|
|
878
|
+
are still `bytefly.json` edits; every scalar preference has a row.
|
|
879
|
+
- **A custom rail glyph must be one column.** The editor refuses anything else,
|
|
880
|
+
because a two-column glyph would push every railed body one cell right; the
|
|
881
|
+
former four-glyph menu is now the description's example list.
|
|
882
|
+
- **`doctor`, `settings`, and `update` are reserved first arguments.**
|
|
883
|
+
`/bytefly doctor` always selects the report, so an extension literally named
|
|
884
|
+
`doctor` cannot be narrowed to by name; `settings` and `update` are reserved
|
|
885
|
+
the same way.
|
|
796
886
|
- **The picker manages only the `extensions` resource type.** Skills, prompts,
|
|
797
887
|
and themes are handled by Pi's own `pi config`.
|
|
798
888
|
- **Built-in extensions are not listed** (`builtin:mcp`, `builtin:codemode`, …):
|
|
799
889
|
their authoritative names are not exposed to extensions. Use `pi config`.
|
|
800
890
|
- **Packages are not installed, removed, or updated here**; use `pi install`,
|
|
801
891
|
`pi remove`, and `pi update`.
|
|
802
|
-
- **
|
|
803
|
-
|
|
892
|
+
- **The update check reads npm's `latest` only.** A private registry, a version
|
|
893
|
+
published under another tag, or a checkout ahead of npm reports nothing, and
|
|
894
|
+
there is no channel or prerelease preference: `0.11.4` is behind `0.12.0`
|
|
895
|
+
whether or not a `0.12.0-beta.1` exists.
|
|
896
|
+
- **The cache window is fixed at 24 hours.** It runs at `session_start` in `tui`
|
|
897
|
+
mode and reuses an entry younger than that; there is no setting for it, and
|
|
898
|
+
`/bytefly` and `doctor` do not report the version. A failed or slow background
|
|
899
|
+
request is retried by the next session, or immediately by `/bytefly update`.
|
|
900
|
+
- **The marker needs the bytefly startup TUI.** It lives on the custom header,
|
|
901
|
+
so with `quietStartup` off the notice is the whole signal.
|
|
902
|
+
- **Pi may report the same update.** An npm-installed copy can also show Pi's
|
|
903
|
+
own `Package Updates Available` block; the two checks are independent and
|
|
904
|
+
`pi update --extensions` satisfies both.
|
|
905
|
+
- **No auto-update, and the release URL is text only.** The notice names a
|
|
906
|
+
command instead of running one, and the notice renderer cannot open a link, so
|
|
907
|
+
the release row prints the URL and nothing more — no changelog body, no way to
|
|
908
|
+
click through from the transcript.
|
|
909
|
+
- **Project toggles are two-state per press** (`enable`/`disable`). `x` clears
|
|
910
|
+
the override, which is the third state; a resource with no project entry in
|
|
911
|
+
`pi config` is still managed there.
|
|
804
912
|
- **One built-in provider.** DeepSeek is the only provider with a built-in
|
|
805
913
|
table; another provider's currency and tariff have to be stated in
|
|
806
914
|
`bytefly.json`.
|
|
@@ -810,10 +918,17 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
810
918
|
with an `offPeakMultiplier` below 1 is affected by it, so two providers with
|
|
811
919
|
different time-of-day tariffs cannot both be described.
|
|
812
920
|
- **No tiers, no special cache writes.** Per-request input tiers and
|
|
813
|
-
Anthropic's 2× one-hour cache write are not modelled;
|
|
814
|
-
|
|
815
|
-
- **No FX conversion.**
|
|
816
|
-
the
|
|
921
|
+
Anthropic's 2× one-hour cache write are not modelled; switch to `USD` mode
|
|
922
|
+
to use Pi's recorded cost for those models.
|
|
923
|
+
- **No FX conversion.** The two modes are separate methods; neither converts
|
|
924
|
+
one currency into the other. A model is priced only by the selected method.
|
|
925
|
+
- **Two built-in footer indicators cannot be reproduced.** Pi's `(auto)` marks
|
|
926
|
+
that auto-compaction is on, and its `(sub)` marks a subscription-backed
|
|
927
|
+
provider. Auto-compaction is session state on Pi's own session object, and
|
|
928
|
+
`ReadonlyFooterDataProvider` exposes only the branch, the extension statuses,
|
|
929
|
+
and the provider count; `isUsingSubscription` lives on the internal model
|
|
930
|
+
runtime, not on the `ModelRegistry` an extension receives. A subscription
|
|
931
|
+
session therefore shows no cost segment at all, where Pi's footer said `(sub)`.
|
|
817
932
|
- **The displayed number is the total, rounded once** — not the sum of rounded
|
|
818
933
|
records.
|
|
819
934
|
- **`doctor` can only check what is declared.** A new extension with no
|
|
@@ -835,8 +950,9 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
835
950
|
prompt raised during a run, not a process waiting on stdin: jobs are
|
|
836
951
|
fire-and-observe, so that state does not exist.
|
|
837
952
|
- **An explicit `method` is honored blindly.** Point `method` at a notifier the
|
|
838
|
-
host lacks (or an OSC method off a TTY) and the notification is
|
|
839
|
-
|
|
953
|
+
host lacks (or an OSC method off a TTY) and the notification is dropped; only
|
|
954
|
+
`auto` inspects the environment. `doctor` now reports the mismatch as a
|
|
955
|
+
`degraded` finding, which says what to change but does not fix it for you.
|
|
840
956
|
- **The working counter is one global row.** `setWorkingMessage` drives the
|
|
841
957
|
single working row, so bytefly and any other extension setting it race — last
|
|
842
958
|
writer wins — and the message is reset to Pi's default at `agent_end`. If the
|
|
@@ -861,8 +977,8 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
861
977
|
- No status chip for the doctor report: it is on demand plus one notice, not a
|
|
862
978
|
permanent footer segment.
|
|
863
979
|
- No notification for individual tool calls, message turns, or subagent steps:
|
|
864
|
-
the
|
|
865
|
-
whole surface.
|
|
980
|
+
the four triggers — a settled run, a prompt mid-run, a finished job, a watcher
|
|
981
|
+
match — are the whole surface.
|
|
866
982
|
- No sound, action buttons, notification history, or de-duplication; a
|
|
867
983
|
notification is a fire-and-forget message, not an inbox.
|
|
868
984
|
- No frozen per-run working duration: the counter is live and is handed back to
|
|
@@ -878,7 +994,7 @@ rather than showing a wrong value.
|
|
|
878
994
|
|
|
879
995
|
| Integration point | Details |
|
|
880
996
|
|---|---|
|
|
881
|
-
| 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. |
|
|
997
|
+
| 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. |
|
|
882
998
|
| Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
|
|
883
999
|
| 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. |
|
|
884
1000
|
| 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. |
|
|
@@ -889,14 +1005,15 @@ rather than showing a wrong value.
|
|
|
889
1005
|
| 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. |
|
|
890
1006
|
| Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
|
|
891
1007
|
| Picker writes | `SettingsManager` setters (`setExtensionPaths`, `setPackages`, `setProjectExtensionPaths`, `setProjectPackages`) then `flush()`; `ctx.reload()` after the screen closes when changes are pending. |
|
|
892
|
-
| Settings writes | App rows call `SettingsManager` setters (`setQuietStartup`, `setOutputPad`, `setShowHardwareCursor`) on the global store and `flush()`, then surface `drainErrors()`; `bytefly.json` rows call `
|
|
893
|
-
| Doctor tool | `pi.registerTool("run_doctor")`, read-only (`readOnlyHint: true`),
|
|
1008
|
+
| 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. |
|
|
1009
|
+
| 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. |
|
|
894
1010
|
| 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. |
|
|
895
1011
|
| 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. |
|
|
896
|
-
|
|
|
1012
|
+
| Update check | `session_start` in `tui` mode reads `<agentDir>/bytefly/update.json` and, when the entry is inside `UPDATE_CACHE_TTL_MS` (24 h), records it without a request; otherwise it fires `fetchLatestVersion()` without awaiting, so the handler never holds the prompt, and writes the answer back. The registry read is a fixed `GET` of npm's `latest` document over an injected `fetch`, bounded by an abort timeout; a strictly newer, parseable semver is recorded in the per-load `UpdateSource`, `onUpdate` re-installs the header, and `pi.sendMessage(updateNotice(...))` posts the one notice with its release URL. `PI_OFFLINE` uses the cache with no request and no write. `/bytefly update` calls the returned `UpdateHandle.recheck()`, which bypasses the window, records, paints, and returns an `UpdateOutcome`; a generation counter drops a background reply that lands after `session_shutdown`, and a per-load flag keeps it to one announcement. |
|
|
1013
|
+
| Config | `bytefly.json` from the agent directory and `<cwd>/.pi`; the `price` section is read once per footer install (a new session or a session-tree change), the `notify` and `doctor` sections at `session_start`, and the `chrome` section's rail glyph at `session_start`/`session_tree`. Nothing watches the document. The update check has no section: it is on whenever `PI_OFFLINE` is off, and its 24 h window is not configurable. |
|
|
897
1014
|
| Data sources | `footerData.getGitBranch()`, `footerData.getAvailableProviderCount()`, `ctx.getContextUsage()`, `ctx.sessionManager.getEntries()`, `ctx.sessionManager.getSessionName()`, `ctx.model`; subscribes to `footerData.onBranchChange` to repaint. |
|
|
898
|
-
| State storage | None besides the inert separator entries. The footer reads session entries and the price config (a memoized scan) and writes no tool-result `details`; it appends `bytefly-separator` entries excluded from LLM context. The screen derives its view from the picker runtime and the current reports, and persists extension toggles through `settings.json`. The Settings section derives from a snapshot of the personal `settings.json` and both `bytefly.json` documents, and persists through the same `SettingsManager` setters or the per-
|
|
899
|
-
| Lifecycle | `session_start` installs the header when Pi's `quietStartup` is `true` and the footer, loads the chrome rail glyph, points the doctor runtime at the session cwd, probes the extensions with hard requirements when `bytefly.json`'s `doctor.proactive` is on and `quietStartup` is `true`,
|
|
1015
|
+
| State storage | None besides the inert separator entries. The footer reads session entries and the price config (a memoized scan) and writes no tool-result `details`; it appends `bytefly-separator` entries excluded from LLM context. The screen derives its view from the picker runtime and the current reports, and persists extension toggles through `settings.json`. The Settings section derives from a snapshot of the personal `settings.json` and both `bytefly.json` documents, and persists through the same `SettingsManager` setters or the per-leaf `bytefly.json` writer. Doctor's findings are session-scoped in memory only — cached for the session, never written to the session or a file. Notify holds only the config and the run-active flag in its closure. The update check holds one version in the per-load `UpdateSource` and writes no session entry; its one file is `<agentDir>/bytefly/update.json`, the cached `latest` plus `checkedAt`, written atomically and read back on the next session. |
|
|
1016
|
+
| Lifecycle | `session_start` installs the header when Pi's `quietStartup` is `true` and the footer, loads the chrome rail glyph, points the doctor runtime at the session cwd, probes the extensions with hard requirements when `bytefly.json`'s `doctor.proactive` is on and `quietStartup` is `true`, loads the notify config and subscribes to the job bus, and starts the published-version check; `session_tree` reinstalls the header and footer and reloads the rail; `session_shutdown` restores Pi's built-in header and footer, disposes the branch subscription, drops doctor's cached findings, drops the notify bus subscription, and clears the recorded version. |
|
|
900
1017
|
|
|
901
1018
|
## Design notes
|
|
902
1019
|
|
|
@@ -986,19 +1103,21 @@ rather than showing a wrong value.
|
|
|
986
1103
|
- **Two lines, four zones.** Line 1 is identity (`pwd` left, branch + worktree
|
|
987
1104
|
right); line 2 is resources (meters left, model + level right). This keeps
|
|
988
1105
|
"where am I / what state" separate from "how much am I burning".
|
|
1106
|
+
- **Child spend is a second figure.** The footer never adds a subagent's tokens to
|
|
1107
|
+
the session's; it prices the `my-pi-agent/subagent-finished` events with the
|
|
1108
|
+
same pricer and renders them as `↳`, so "this session" keeps meaning the
|
|
1109
|
+
dispatching process and the delegated work stays visible beside it.
|
|
989
1110
|
- **The gauge is anchored at column 0.** A leading mode slot would shift the
|
|
990
1111
|
context percentage left and right as plan mode came and went; trailing it
|
|
991
1112
|
after a `╎` keeps the percentage in a fixed place while the mode stays
|
|
992
1113
|
visible as the meters drop.
|
|
993
1114
|
- **Statuses are routed, not repeated.** The built-in footer renders every
|
|
994
|
-
`setStatus` value on its own line
|
|
995
|
-
and `browse` are location,
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
filesystem identity (path + session name) and every "where am I / what is
|
|
1001
|
-
running" fact sits on line 1 right.
|
|
1115
|
+
`setStatus` value on its own line, so the bar sorts them by meaning instead:
|
|
1116
|
+
`worktree` and `browse` are location, so the `◉ <port>` chip sits beside `⎇`
|
|
1117
|
+
branch and `⑂` worktree and the left zone stays the filesystem identity; a
|
|
1118
|
+
plan or a failed job is mode or alert, so it takes the slot after the gauge.
|
|
1119
|
+
The [wechat](../wechat/) extension publishes `✆ wechat` while its bridge runs,
|
|
1120
|
+
which is why the bar has to render `getExtensionStatuses()` at all.
|
|
1002
1121
|
- **Segments degrade; they do not clip.** Each `Segment` carries progressively
|
|
1003
1122
|
shorter `forms` and a `weight`; the layout advances the lowest-priority form
|
|
1004
1123
|
or drops the lowest-priority segment, and only truncates once everything is
|
|
@@ -1043,10 +1162,10 @@ rather than showing a wrong value.
|
|
|
1043
1162
|
usage records (`collect/`), records are priced into totals (`price/`), and the
|
|
1044
1163
|
snapshot is drawn (`render/`). Pricing is a pure function of records and rates
|
|
1045
1164
|
— no session, terminal, or theme to stub — which is what lets the published
|
|
1046
|
-
DeepSeek example be a unit test.
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1165
|
+
DeepSeek example be a unit test. In `CNY` mode a record with no matching rate
|
|
1166
|
+
simply contributes nothing; `USD` mode is the opt-in path to Pi's own number.
|
|
1167
|
+
The built-in DeepSeek table is only the default value of `rates`, so nothing
|
|
1168
|
+
in the pricer is provider-specific.
|
|
1050
1169
|
- **DeepSeek is built in, not bundled as a catalog.** Its published price is in
|
|
1051
1170
|
yuan at two tariffs, while Pi's catalog holds one USD number per model, so an
|
|
1052
1171
|
unconfigured DeepSeek session showed a bill that was neither the right
|
|
@@ -1084,12 +1203,18 @@ rather than showing a wrong value.
|
|
|
1084
1203
|
- **The install hint has one home.** `document` and `web-access` format their
|
|
1085
1204
|
tool errors from the same `REQUIREMENTS` entries `doctor` prints, so a user
|
|
1086
1205
|
reads the same command whether a tool failed or the report named it.
|
|
1087
|
-
- **Settings are a curated table, not a schema walker.**
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1206
|
+
- **Settings are a curated table, not a schema walker.** The rows cover every
|
|
1207
|
+
scalar preference across four kinds — a boolean, an enum, a string with
|
|
1208
|
+
its own rule, and a number — both scopes, both write paths, and one nested leaf
|
|
1209
|
+
(`price.currency.decimals`). A row states its section and the path to its leaf,
|
|
1210
|
+
and one reader per section resolves it project-over-global-over-defaults through
|
|
1211
|
+
the same normalizer the extension loads with, so the screen and the runtime
|
|
1212
|
+
cannot disagree about a value; a text row's rule calls the same predicate the
|
|
1213
|
+
loader uses, so an accepted value is never replaced on the next read. A generic
|
|
1214
|
+
walker would expose keys the screen cannot edit safely — arrays, nested
|
|
1215
|
+
tariffs, credentials — so the catalogue grows one proven family at a time. App
|
|
1216
|
+
rows are Global-only because Pi's setters write the personal file; Project
|
|
1217
|
+
scope lists only the `bytefly.json` rows it can actually write.
|
|
1093
1218
|
- **The doctor is quiet unless the bytefly startup TUI is on.** Only `missing`
|
|
1094
1219
|
findings post a notice, only for enabled extensions, only with a UI, only once
|
|
1095
1220
|
per session, and only when Pi's `quietStartup` is `true` — the same state that
|
|
@@ -1108,6 +1233,21 @@ rather than showing a wrong value.
|
|
|
1108
1233
|
- **The prerequisite table is data.** No I/O, no host access, no module state:
|
|
1109
1234
|
every probe dependency arrives as a seam, so `probeExtension` is testable with
|
|
1110
1235
|
no process and `lib/` stays value-only.
|
|
1236
|
+
- **The update check is a self-report, not an update mechanism.** It answers one
|
|
1237
|
+
question — is the version in this checkout current? — and answers it in the two
|
|
1238
|
+
surfaces bytefly already owns, the header identity and one notice. It never
|
|
1239
|
+
installs, never edits settings, and never fails a session: an unreachable
|
|
1240
|
+
registry and an up-to-date one are indistinguishable to the user, which is the
|
|
1241
|
+
only failure mode a background check is allowed to have.
|
|
1242
|
+
- **The marker is an action, the notice is information.** The same fact is drawn
|
|
1243
|
+
in the accent on the header, where it sits beside a control the user can act
|
|
1244
|
+
on, and at `info` severity in the transcript, where a `warn` rail would say
|
|
1245
|
+
something is broken. Neither surface needs per-frame polling: the value arrives
|
|
1246
|
+
once, the memo key includes it, and one `setHeader` re-install repaints.
|
|
1247
|
+
- **The transport stays local.** `web-access`'s runner does redirects, SSRF
|
|
1248
|
+
checks, HTML extraction, and caching — none of it applies to one fixed JSON
|
|
1249
|
+
endpoint, and an extension may not import another's modules, so the ~20-line
|
|
1250
|
+
read lives beside the check with the `fetch` and timeout as seams.
|
|
1111
1251
|
|
|
1112
1252
|
## Files
|
|
1113
1253
|
|
|
@@ -1117,7 +1257,8 @@ calculation), `message/` (the user-message marker), `notice/` (the shared notice
|
|
|
1117
1257
|
elapsed counter on Pi's working row), `panel/` (the
|
|
1118
1258
|
unified screen and its merged model),
|
|
1119
1259
|
`picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
|
|
1120
|
-
and its probe runtime),
|
|
1260
|
+
and its probe runtime), `notify/`, and `update/` (the published-version check
|
|
1261
|
+
behind the header marker, its disk cache, and the on-demand re-check). The chrome and the screen are drawn from
|
|
1121
1262
|
the package's [`lib/tui/`](../../lib/tui/) design system (tokens, rules, rows,
|
|
1122
1263
|
columns, gauge), so the look follows the active theme.
|
|
1123
1264
|
|
|
@@ -1135,10 +1276,10 @@ columns, gauge), so the look follows the active theme.
|
|
|
1135
1276
|
| `footer/collect/entries.ts` | Session entries → usage records, each resolved to the model in effect. |
|
|
1136
1277
|
| `footer/collect/snapshot.ts` | Snapshot assembly; memoized pricing scan per branch leaf. |
|
|
1137
1278
|
| `footer/price/types.ts` | `Currency`, `ModelRates`, `PeakWindow`/`PeakSchedule`, `ByteflyConfig`. |
|
|
1138
|
-
| `footer/price/config.ts` | The `price` section of `bytefly.json`: schema, defaults, built-in DeepSeek rates/tariff, normalizer, loader. |
|
|
1279
|
+
| `footer/price/config.ts` | The `price` section of `bytefly.json`: schema, defaults, built-in DeepSeek rates/tariff, currency defaults, normalizer, loader. |
|
|
1139
1280
|
| `footer/price/rates.ts` | Model-key matching: exact and wildcard, most specific first. |
|
|
1140
1281
|
| `footer/price/schedule.ts` | Peak-window parsing and evaluation in a configured zone. |
|
|
1141
|
-
| `footer/price/price.ts` | Records → totals:
|
|
1282
|
+
| `footer/price/price.ts` | Records → totals: rated cost in both modes, Pi's recorded cost as USD's fallback, and the peak multiplier. |
|
|
1142
1283
|
| `footer/render/format.ts` | Generic formatters (tokens, cost in the configured currency, cwd, the context role, truncation). |
|
|
1143
1284
|
| `footer/render/status.ts` | Status-chip transforms: `compactStatus` and `dropStatusDetail`. |
|
|
1144
1285
|
| `footer/render/segments.ts` | The segment builders: forms, weights, separators, roles. |
|
|
@@ -1166,8 +1307,8 @@ columns, gauge), so the look follows the active theme.
|
|
|
1166
1307
|
| `panel/types.ts` | `PanelSection` (`extensions` | `settings`), `PanelRow`, and `PanelModel`, the merged row model. |
|
|
1167
1308
|
| `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
|
|
1168
1309
|
| `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. |
|
|
1169
|
-
| `panel/settings.ts` | The curated Settings catalogue: descriptors, snapshot views and origins, value cycling, stored-value mapping, validation, and grouping. |
|
|
1170
|
-
| `panel/settings-runtime.ts` | The Settings runtime: snapshot cache, scope guards, app setters, per-
|
|
1310
|
+
| `panel/settings.ts` | The curated Settings catalogue: descriptors, snapshot views and origins, value cycling, stored-value mapping, validation, and grouping, plus the per-section readers that resolve a row's leaf over the global and project documents. |
|
|
1311
|
+
| `panel/settings-runtime.ts` | The Settings runtime: snapshot cache, scope guards, app setters, per-leaf `bytefly.json` writes, dirty state, and change listeners. |
|
|
1171
1312
|
| `panel/tui.ts` | `PanelComponent`, the two-section dock screen (Extensions list and Settings list), drawn from the `lib/tui/` kit. |
|
|
1172
1313
|
| `panel/commands.ts` | `/bytefly`: one command for the screen sections, the `doctor` report, the settings listing, and their text modes. |
|
|
1173
1314
|
| `picker/types.ts` | Picker host seams (`SettingsStore`, `ExtensionResolver`, `Discovery`), the app-settings setters, and row types. |
|
|
@@ -1184,8 +1325,14 @@ columns, gauge), so the look follows the active theme.
|
|
|
1184
1325
|
| `notify/types.ts` | `NotifyConfig`, `NotifyMethod`, and the method list. |
|
|
1185
1326
|
| `notify/config.ts` | The `notify` section of `bytefly.json`: defaults, validation, loader. |
|
|
1186
1327
|
| `notify/dispatch.ts` | Method selection and delivery: OSC 777/99, Windows toast, `osascript`, `notify-send`, over injected seams. |
|
|
1187
|
-
| `notify/format.ts` | Pure notification text: session label and the turn / input / job bodies. |
|
|
1188
|
-
| `notify/register.ts` | Registers the
|
|
1328
|
+
| `notify/format.ts` | Pure notification text: session label and the turn / input / job / watch bodies. |
|
|
1329
|
+
| `notify/register.ts` | Registers the four triggers, loads the config at `session_start`, and owns the job- and watch-bus subscriptions. |
|
|
1330
|
+
| `update/check.ts` | The one registry read: the `latest` URL, the abort timeout, the `FetchLike` seam, and the failure-to-`undefined` mapping. |
|
|
1331
|
+
| `update/version.ts` | Pure semver parsing and `isNewerVersion`, including prerelease precedence. |
|
|
1332
|
+
| `update/state.ts` | `UpdateSource` and `createUpdateState` (the newest known version, one record per load), plus `UpdateOutcome`, the four answers an explicit re-check can give. |
|
|
1333
|
+
| `update/cache.ts` | The disk cache: `<agentDir>/bytefly/update.json`, the 24 h window in `isFresh`, a validated read that degrades to a miss, and the atomic write. |
|
|
1334
|
+
| `update/format.ts` | `updateMarker`, `updateNotice`, `releaseUrl`, and `updateOutcomeText`: the header's `↑0.12.0`, the transcript notice with its release row, and the one line `/bytefly update` prints. |
|
|
1335
|
+
| `update/register.ts` | The lifecycle: the cache-first session-start read, the async registry call, the `PI_OFFLINE` and `tui` gates, the once-per-load announcement, the shutdown generation guard, and the `recheck()` handle `/bytefly update` calls. |
|
|
1189
1336
|
|
|
1190
1337
|
## Testing
|
|
1191
1338
|
|
|
@@ -1216,7 +1363,8 @@ transforms (`picker/toggle`), discovery with an injected factory
|
|
|
1216
1363
|
(`picker/discovery`), and the runtime state machine (`picker/runtime`). The
|
|
1217
1364
|
unified screen is covered by its pure model merge (`panel/join`), its text
|
|
1218
1365
|
formatter (`panel/format`), the settings catalogue and its cycling
|
|
1219
|
-
(`panel/settings`), the scope-aware settings runtime (`panel/settings-runtime
|
|
1366
|
+
(`panel/settings`), the scope-aware settings runtime (`panel/settings-runtime`,
|
|
1367
|
+
including a nested leaf write that keeps its siblings),
|
|
1220
1368
|
the screen's rendering and key handling (`panel/tui`), and the command through
|
|
1221
1369
|
the bytefly factory (`panel/commands`).
|
|
1222
1370
|
The doctor surface is covered by its pure report
|
|
@@ -1224,8 +1372,16 @@ formatting (`doctor/format`), its config loader (`doctor/config`), and
|
|
|
1224
1372
|
registration, the proactive notice, the session cache, and `run_doctor`
|
|
1225
1373
|
through the factory (`doctor/register`). The notify capability is covered by its section loader
|
|
1226
1374
|
(`notify/config`), the pure text builders (`notify/format`), method selection and
|
|
1227
|
-
delivery over recording seams (`notify/dispatch`), and its
|
|
1228
|
-
reload, and shutdown through `registerNotify` (`notify/register`).
|
|
1375
|
+
delivery over recording seams (`notify/dispatch`), and its four triggers,
|
|
1376
|
+
reload, and shutdown through `registerNotify` (`notify/register`). The update
|
|
1377
|
+
check is covered by the version-ordering table (`update/version`), the transport
|
|
1378
|
+
with an injected fetch (`update/check`), the per-load store (`update/state`), the
|
|
1379
|
+
marker, notice, and release-URL wording (`update/format`), the cache read, write,
|
|
1380
|
+
and window (`update/cache`), and its lifecycle — a fresh entry reused without a
|
|
1381
|
+
request, a stale one re-read and rewritten, silent on failure or an older version,
|
|
1382
|
+
cache-only offline, skipped outside `tui`, an explicit `recheck()` that ignores
|
|
1383
|
+
the window, and immunity to a reply that lands after shutdown
|
|
1384
|
+
(`update/register`).
|
|
1229
1385
|
The user-message nameplate is covered by its pure transform
|
|
1230
1386
|
(`message/format`, including a render through pi-tui's real `Markdown`) and its
|
|
1231
1387
|
registration (`message/register`), plus the factory check in `extension`.
|