@gavin-hu/my-pi-agent 0.11.3 → 0.12.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 +13 -8
- package/README.zh-CN.md +5 -3
- package/assets/chrome.png +0 -0
- package/assets/plan-review.png +0 -0
- package/extensions/bytefly/README.md +291 -207
- package/extensions/bytefly/doctor/runtime.ts +16 -0
- package/extensions/bytefly/doctor/tools.ts +1 -3
- 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/render/format.ts +3 -2
- package/extensions/bytefly/footer/render/segments.ts +3 -2
- package/extensions/bytefly/footer/types.ts +1 -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 +13 -3
- package/extensions/bytefly/panel/settings-runtime.ts +71 -28
- package/extensions/bytefly/panel/settings.ts +201 -38
- 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/check.ts +59 -0
- package/extensions/bytefly/update/format.ts +32 -0
- package/extensions/bytefly/update/register.ts +72 -0
- package/extensions/bytefly/update/state.ts +28 -0
- package/extensions/bytefly/update/version.ts +83 -0
- package/extensions/document/README.md +2 -2
- package/extensions/document/tool.ts +1 -3
- 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 +39 -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/tools.ts +28 -8
- package/extensions/goal/tui.ts +1 -1
- package/extensions/memory/README.md +1 -1
- package/extensions/plan/README.md +24 -8
- package/extensions/plan/blocks.ts +1 -35
- package/extensions/plan/commands.ts +18 -12
- package/extensions/plan/plans.ts +1 -1
- package/extensions/plan/policy.ts +4 -3
- package/extensions/plan/review.ts +243 -0
- package/extensions/plan/runtime.ts +4 -0
- package/extensions/plan/steps.ts +34 -23
- package/extensions/plan/todos.ts +5 -58
- package/extensions/plan/tools.ts +66 -222
- package/extensions/plan/tui.ts +17 -9
- package/extensions/plan/types.ts +3 -0
- package/extensions/plan/web-client.ts +51 -18
- package/extensions/plan/web-page.ts +5 -0
- package/extensions/plan/web-router.ts +11 -3
- package/extensions/plan/web.ts +100 -36
- package/extensions/question/README.md +1 -1
- package/extensions/question/tools.ts +1 -1
- package/extensions/subagent/index.ts +0 -2
- 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/web-access/README.md +3 -3
- package/extensions/web-access/fetch/tool.ts +1 -3
- package/extensions/web-access/search/tool.ts +1 -3
- package/extensions/wechat/README.md +22 -11
- package/extensions/wechat/bridge.ts +90 -22
- package/extensions/wechat/commands.ts +45 -1
- package/extensions/wechat/doctor.ts +1 -0
- package/extensions/wechat/index.ts +3 -1
- package/extensions/wechat/status-screen.ts +32 -2
- package/extensions/wechat/tools.ts +0 -2
- package/lib/README.md +2 -1
- package/lib/config.ts +43 -1
- package/lib/env.ts +12 -0
- package/lib/requirements.ts +76 -2
- package/lib/todo-state.ts +62 -0
- package/lib/tui/glyphs.ts +1 -0
- package/package.json +1 -1
- package/assets/bytefly.schema.json +0 -785
|
@@ -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).
|
|
@@ -73,9 +78,10 @@ in [`header/config.ts`](./header/config.ts).
|
|
|
73
78
|
▰▰▰▰▰▰▱▱▱▱ 62%/200k · ¥0.31 · ↑42k ↓8.0k · R96k CH 87% ╎ ⋮ plan opus-4.5 · high
|
|
74
79
|
```
|
|
75
80
|
|
|
76
|
-
(The examples show the default `¥` symbol.
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
(The examples show the default `¥` symbol. A model that neither `rates` nor the
|
|
82
|
+
built-in table covers contributes nothing in `CNY` mode, so the bar shows no
|
|
83
|
+
figure rather than a guessed one; `USD` mode adds Pi's recorded dollars for that
|
|
84
|
+
record instead — see the currency note under [Configuration](#configuration).)
|
|
79
85
|
|
|
80
86
|
With no plan mode there is no mode slot, so the `╎` is omitted:
|
|
81
87
|
|
|
@@ -113,8 +119,9 @@ rounding to a misleading `¥0.3` — and a positive amount below one minor unit
|
|
|
113
119
|
shows as `¥<0.01` rather than `¥0.00`.
|
|
114
120
|
|
|
115
121
|
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`
|
|
122
|
+
rates and peak tariff built into [`footer/price/config.ts`](./footer/price/config.ts).
|
|
123
|
+
A `bytefly.json` can switch to the published dollar table, add models of your own,
|
|
124
|
+
or replace a built-in entry:
|
|
118
125
|
|
|
119
126
|
```
|
|
120
127
|
~/repo/project ⎇ main
|
|
@@ -268,6 +275,13 @@ method follows the terminal (OSC 777, OSC 99, a Windows toast) or falls back to
|
|
|
268
275
|
the platform notifier (`osascript`, `notify-send`); see
|
|
269
276
|
[Notifications](#notifications).
|
|
270
277
|
|
|
278
|
+
**Update check.** Once per interactive session, in the background, the loaded
|
|
279
|
+
version is compared with the published `@gavin-hu/my-pi-agent` on npm. A newer
|
|
280
|
+
one puts `↑0.12.0` after the header's chrome version and posts one transcript
|
|
281
|
+
notice naming `pi update --extensions`. The check is skipped when `PI_OFFLINE`
|
|
282
|
+
is set, makes no request outside `tui`, and a failed request is silent; see
|
|
283
|
+
[Update check](#update-check).
|
|
284
|
+
|
|
271
285
|
## Commands
|
|
272
286
|
|
|
273
287
|
| Command | What it does |
|
|
@@ -303,45 +317,36 @@ print/JSON/RPC run gets an OS-level notification or none.
|
|
|
303
317
|
|
|
304
318
|
## Configuration
|
|
305
319
|
|
|
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.
|
|
320
|
+
Two things are JSON: the transcript rail's glyph (`chrome.rail`, default `╎`,
|
|
321
|
+
read at `session_start`/`session_tree`) and this extension's three config
|
|
322
|
+
sections — `price`, `notify`, and `doctor`. Everything else about the look is
|
|
323
|
+
tuned in code: the header in [`header/config.ts`](./header/config.ts) (mark and
|
|
324
|
+
identity tokens, tagline, grouped help), the footer in
|
|
325
|
+
[`footer/config.ts`](./footer/config.ts) (thresholds, gauge glyphs and widths,
|
|
326
|
+
separators, icons, status keys), and the turn separator in
|
|
327
|
+
[`separator/config.ts`](./separator/config.ts) (glyph, label prefix, padding,
|
|
328
|
+
colors).
|
|
329
|
+
|
|
330
|
+
Every key of every section is listed with its type, default, and bounds in the
|
|
331
|
+
[configuration reference](../../docs/references/configuration-reference.md); this section
|
|
332
|
+
covers what that table cannot — when each section is read, and what the cost
|
|
333
|
+
does when no rate matches.
|
|
334
|
+
|
|
335
|
+
`bytefly.json` is one shared document with a top-level section per extension (see
|
|
336
|
+
[Configuration](../../docs/configuration.md)). It is read from the global
|
|
337
|
+
document, then `<cwd>/.pi/bytefly.json`, project winning. The old per-extension
|
|
338
|
+
files (`document.json`, `jobs.json`, …) are no longer read; `bun run config:migrate`
|
|
339
|
+
moves them into the shared document.
|
|
340
|
+
|
|
341
|
+
Neither document is needed for DeepSeek. The built-in table for the selected
|
|
342
|
+
currency is the base layer for every session: `CNY` prices DeepSeek in yuan and
|
|
343
|
+
`USD` in dollars, both at the published peak and off-peak rates. A document can
|
|
344
|
+
change the currency, replace a built-in rate, or add rates of your own.
|
|
339
345
|
|
|
340
346
|
```json
|
|
341
347
|
{
|
|
342
348
|
"price": {
|
|
343
|
-
"currency": { "
|
|
344
|
-
"fallbackToRecordedCost": true,
|
|
349
|
+
"currency": { "code": "CNY", "decimals": 2 },
|
|
345
350
|
"rates": {
|
|
346
351
|
"deepseek/deepseek-flash": { "input": 2, "output": 8, "cacheRead": 0.04, "cacheWrite": 0, "offPeakMultiplier": 0.5 },
|
|
347
352
|
"deepseek/deepseek-v4-pro": { "input": 9, "output": 27, "cacheRead": 0.3, "cacheWrite": 0, "offPeakMultiplier": 0.5 }
|
|
@@ -357,101 +362,45 @@ drops the schedule rather than the session.
|
|
|
357
362
|
}
|
|
358
363
|
```
|
|
359
364
|
|
|
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. |
|
|
365
|
+
That file restates the built-in defaults, so on its own it changes nothing; it is
|
|
366
|
+
shown to document every key at once.
|
|
373
367
|
|
|
374
368
|
Rates merge key by key across the two documents, so a project document can
|
|
375
369
|
override one model without restating the rest; a key present in both is replaced,
|
|
376
370
|
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.
|
|
371
|
+
`offPeakMultiplier: 0.5` and no schedule stays flat.
|
|
398
372
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
"jobFinished": true,
|
|
406
|
-
"method": "auto",
|
|
407
|
-
"title": "Pi"
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
```
|
|
373
|
+
**Only configured models get a price.** A record priced from `rates` or the
|
|
374
|
+
built-in table contributes to the total. A model with no matching rate differs by
|
|
375
|
+
currency: `CNY` contributes nothing, so the bar shows no cost rather than a
|
|
376
|
+
mixed-currency figure, while `USD` adds Pi's recorded dollar amount, which is
|
|
377
|
+
already in the right unit. To price the model yourself, add a rate entry (a `"*"`
|
|
378
|
+
catch-all prices everything).
|
|
411
379
|
|
|
412
|
-
|
|
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. |
|
|
380
|
+
### When each section is read
|
|
420
381
|
|
|
421
|
-
|
|
422
|
-
picks up an edit.
|
|
423
|
-
|
|
424
|
-
### The `doctor` section
|
|
425
|
-
|
|
426
|
-
The doctor surface reads the same shared `bytefly.json`, under its own top-level
|
|
427
|
-
`doctor` key:
|
|
428
|
-
|
|
429
|
-
```json
|
|
430
|
-
{
|
|
431
|
-
"doctor": {
|
|
432
|
-
"proactive": true,
|
|
433
|
-
"timeoutMs": 2000,
|
|
434
|
-
"skip": []
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
| Key | Default | Meaning |
|
|
382
|
+
| Section | Read at | Applies to |
|
|
440
383
|
|---|---|---|
|
|
441
|
-
| `
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
384
|
+
| `chrome` | `session_start`, `session_tree` | the rail glyph |
|
|
385
|
+
| `price` | the footer install (`session_start`, `session_tree`) | the cost total |
|
|
386
|
+
| `notify` | `session_start` | which events are announced, and how |
|
|
387
|
+
| `doctor` | `session_start` | the proactive check |
|
|
388
|
+
|
|
389
|
+
Nothing watches a file, so an edit applies at the next read — a reload, a new
|
|
390
|
+
session, or a tree change. A malformed document is ignored by every loader and
|
|
391
|
+
named by `doctor`; a wrong-typed value falls back to its default, and a
|
|
392
|
+
wrong-typed timezone drops the peak schedule rather than the session.
|
|
393
|
+
|
|
394
|
+
The notify capability is **off by default**: `enabled: true` is the whole setup
|
|
395
|
+
needed for an unattended run, and the three trigger flags then narrow what is
|
|
396
|
+
announced (see [Notifications](#notifications)).
|
|
397
|
+
|
|
398
|
+
The proactive doctor check is part of the bytefly startup TUI, so it posts only
|
|
399
|
+
when Pi starts with `"quietStartup": true`, the same setting that hides Pi's own
|
|
400
|
+
header and loaded-resource listing. `false`, `"header"`, and unset leave Pi's
|
|
401
|
+
startup surface in charge, so the notice stays silent. When it does post, the
|
|
402
|
+
notice enters the message flow once per session, so it stays in scrollback and
|
|
403
|
+
the model reads the same sentence.
|
|
455
404
|
|
|
456
405
|
## Security
|
|
457
406
|
|
|
@@ -464,11 +413,12 @@ session, so it stays in scrollback and the model reads the same sentence.
|
|
|
464
413
|
## Cost calculation
|
|
465
414
|
|
|
466
415
|
Pi prices every model call from its bundled catalog in USD, and the provider API
|
|
467
|
-
never returns a price.
|
|
468
|
-
they match:
|
|
416
|
+
never returns a price. The footer can total that catalog directly, or replace
|
|
417
|
+
it with its own rates for the models they match:
|
|
469
418
|
|
|
470
419
|
- **Which built-in rates.** [`footer/price/config.ts`](./footer/price/config.ts) ships DeepSeek's published
|
|
471
|
-
yuan prices
|
|
420
|
+
yuan prices for `CNY` and its published dollar prices for `USD`, per model, with
|
|
421
|
+
the peak schedule that doubles the price during peak hours
|
|
472
422
|
([DeepSeek's pricing page](https://api-docs.deepseek.com/quick_start/pricing)).
|
|
473
423
|
They are ordinary rate entries, so a `bytefly.json` key replaces one of
|
|
474
424
|
them, and the schedule is the default `peak` every rate is judged against.
|
|
@@ -482,9 +432,16 @@ they match:
|
|
|
482
432
|
applied at the local time the tokens were actually spent: an off-peak turn
|
|
483
433
|
stays off-peak when peak starts. A record with no usable timestamp is priced
|
|
484
434
|
at peak, because it cannot be dated.
|
|
485
|
-
- **Which
|
|
486
|
-
|
|
487
|
-
`
|
|
435
|
+
- **Which algorithm.** `price.currency.code` chooses `CNY` (the default) or
|
|
436
|
+
`USD`. Both price matched records from `rates` and the same `peak` tariff; the
|
|
437
|
+
code selects which built-in table `rates` starts from — DeepSeek's yuan prices
|
|
438
|
+
or its dollar prices — and the two are separate methods, not an exchange rate.
|
|
439
|
+
Neither mode converts one currency into the other. `currency.decimals` only
|
|
440
|
+
changes how the total is printed; the symbol follows the code.
|
|
441
|
+
- **Which source.** Both currencies price a matched record from `rates`; only
|
|
442
|
+
the unmatched case differs. `CNY` contributes nothing for a model it cannot
|
|
443
|
+
price, while `USD` adds Pi's recorded dollar amount for that record, because
|
|
444
|
+
that number is already dollars.
|
|
488
445
|
|
|
489
446
|
For example, DeepSeek flash at `¥2` in and `¥8` out per million, with the
|
|
490
447
|
published peak windows and `offPeakMultiplier: 0.5`, prices a 1M-in + 1M-out
|
|
@@ -598,6 +555,12 @@ installs or fetches a package.
|
|
|
598
555
|
into `extensions` (and package filter objects) in the active scope. A row in
|
|
599
556
|
the trailing `Not resolved` group has no pattern to write, so `Space` says so
|
|
600
557
|
instead of doing nothing.
|
|
558
|
+
- Clears the focused extension's project override with `x` in Project scope: the
|
|
559
|
+
`+`/`-` entry the toggle wrote is removed (with the plain path entry beside it
|
|
560
|
+
for an inherited personal resource, and a project package entry the toggle
|
|
561
|
+
created), and the row returns to its inherited state. A row with no override
|
|
562
|
+
says so instead of writing, and Global scope does not offer the key — the
|
|
563
|
+
personal file is the base layer.
|
|
601
564
|
- Switches the write scope with `Tab`: **Global** (personal `settings.json`) or
|
|
602
565
|
**Project** (`.pi/settings.json`, only when the project is trusted; an
|
|
603
566
|
untrusted project refuses with a notice).
|
|
@@ -611,37 +574,53 @@ installs or fetches a package.
|
|
|
611
574
|
without leaving the session.
|
|
612
575
|
|
|
613
576
|
**Settings.** The second section uses the same frame and the same `Tab` scope
|
|
614
|
-
switch, with its own cursor. It lists
|
|
577
|
+
switch, with its own cursor. It lists ten preferences in six groups:
|
|
615
578
|
|
|
616
579
|
```
|
|
617
|
-
bytefly
|
|
618
|
-
Extensions · Settings
|
|
619
|
-
|
|
620
|
-
Startup
|
|
621
|
-
|
|
622
|
-
Notifications
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
580
|
+
bytefly ──────────────────────────────────────────────────────────────
|
|
581
|
+
Extensions · Settings Global · 10 settings
|
|
582
|
+
|
|
583
|
+
Startup (1)
|
|
584
|
+
❯ ◻ Bytefly startup TUI Off
|
|
585
|
+
Notifications (3)
|
|
586
|
+
◻ Desktop notifications Off
|
|
587
|
+
Delivery method Auto
|
|
588
|
+
Notification title Pi
|
|
589
|
+
Diagnostics (1)
|
|
590
|
+
◼ Proactive health check On
|
|
591
|
+
Pricing (2)
|
|
592
|
+
Currency CNY (¥)
|
|
593
|
+
Cost decimals 2
|
|
594
|
+
Transcript (2)
|
|
595
|
+
Rail glyph ╎
|
|
596
|
+
Output padding 1
|
|
597
|
+
Terminal (1)
|
|
598
|
+
◻ Hardware cursor Off
|
|
630
599
|
Bytefly startup TUI — Pi setting
|
|
631
600
|
Use bytefly's startup header in place of Pi's startup TUI.
|
|
632
601
|
Default · ~/.pi/agent/settings.json
|
|
602
|
+
|
|
603
|
+
Esc close · ←/→ · Space change · Tab scope · ↑/↓ select
|
|
633
604
|
```
|
|
634
605
|
|
|
635
606
|
| Group | Row | Source | Values |
|
|
636
607
|
|---|---|---|---|
|
|
637
608
|
| Startup | Bytefly startup TUI | Pi `settings.json` (`quietStartup`) | Off, On |
|
|
638
|
-
| Notifications | Desktop notifications | `
|
|
639
|
-
|
|
|
609
|
+
| Notifications | Desktop notifications | `notify.enabled` | Off, On |
|
|
610
|
+
| Notifications | Delivery method | `notify.method` | Auto, OSC 777, OSC 99, Windows toast, macOS, Linux |
|
|
611
|
+
| Notifications | Notification title | `notify.title` | Text, up to 40 characters |
|
|
612
|
+
| Diagnostics | Proactive health check | `doctor.proactive` | Off, On |
|
|
613
|
+
| Pricing | Currency | `price.currency.code` | CNY, USD |
|
|
614
|
+
| Pricing | Cost decimals | `price.currency.decimals` | 0, 1, 2, 3, 4 |
|
|
615
|
+
| Transcript | Rail glyph | `chrome.rail` | `╎`, `│`, `┃`, `▏` |
|
|
640
616
|
| Transcript | Output padding | Pi `settings.json` | 0, 1 |
|
|
641
617
|
| Terminal | Hardware cursor | Pi `settings.json` | Off, On |
|
|
642
618
|
|
|
619
|
+
Choosing **Currency** also writes that currency's default precision. Both rows
|
|
620
|
+
stay editable afterwards, so the footer follows the explicit values.
|
|
621
|
+
|
|
643
622
|
`Space` (or `Enter`) changes the focused row: a boolean toggles, an enum cycles
|
|
644
|
-
forward. The detail pane names the source, describes the preference, and shows
|
|
623
|
+
forward, and a text row opens an inline editor on its value. The detail pane names the source, describes the preference, and shows
|
|
645
624
|
where the value is written — `Global · ~/.pi/agent/settings.json`, `Project ·
|
|
646
625
|
.pi/bytefly.json`, or `Inherited from Global · .pi/bytefly.json`. A successful
|
|
647
626
|
change marks the screen `Saved · reload to apply` — even when the terminal is too
|
|
@@ -656,21 +635,24 @@ with `true` or `false`.
|
|
|
656
635
|
|
|
657
636
|
App rows are **Global only**: Pi's setters for them write the personal
|
|
658
637
|
`settings.json`, and only resource paths have public project setters. Project
|
|
659
|
-
scope therefore lists the
|
|
638
|
+
scope therefore lists the six `bytefly.json` rows and a
|
|
660
639
|
`App settings are Global only.` note rather than promising a write that cannot
|
|
661
|
-
happen. A `bytefly.json` row writes one
|
|
662
|
-
|
|
663
|
-
|
|
640
|
+
happen. A `bytefly.json` row writes one leaf in the selected scope (a depth-1
|
|
641
|
+
key, or a nested one such as `price.currency.decimals`) through a per-key
|
|
642
|
+
writer, so every sibling key, section, `$schema`, and `version` is preserved. A
|
|
643
|
+
malformed document is refused with a notice instead of overwritten.
|
|
664
644
|
|
|
665
645
|
The non-`tui` form prints the headline, the groups, and `Label: Value` rows:
|
|
666
646
|
|
|
667
647
|
```
|
|
668
|
-
bytefly: Settings · Global ·
|
|
648
|
+
bytefly: Settings · Global · 9 settings
|
|
669
649
|
|
|
670
650
|
Startup (1)
|
|
671
651
|
Bytefly startup TUI: Off
|
|
672
|
-
|
|
652
|
+
|
|
653
|
+
Notifications (2)
|
|
673
654
|
Desktop notifications: Off
|
|
655
|
+
Delivery method: Auto
|
|
674
656
|
...
|
|
675
657
|
```
|
|
676
658
|
|
|
@@ -693,8 +675,12 @@ headline; a named extension prints its full findings instead.
|
|
|
693
675
|
| Key | Effect |
|
|
694
676
|
|---|---|
|
|
695
677
|
| `←` / `→` | Show the Extensions or Settings section. |
|
|
696
|
-
| `Space` | Toggle the focused extension (Extensions); change the focused setting (Settings). |
|
|
697
|
-
| `
|
|
678
|
+
| `Space` | Toggle the focused extension (Extensions); change the focused setting (Settings), or open the editor on a text row. |
|
|
679
|
+
| `x` | Clear the focused extension's project override (Extensions, Project scope). |
|
|
680
|
+
| `Enter` | Expand or collapse the focused extension's findings (Extensions); change the focused setting, or open the editor on a text row (Settings). |
|
|
681
|
+
| `←` / `→`, `Home` / `End` (`Ctrl+A` / `Ctrl+E`) | Move the editor's caret while a text row is being edited. |
|
|
682
|
+
| `Backspace` / `Delete`, `Ctrl+U` | Edit the buffer: delete a character, or clear it. |
|
|
683
|
+
| `Enter` / `Esc` (editing) | Save the buffer, or cancel and restore the stored value. |
|
|
698
684
|
| `Tab` | Switch between Global and Project scope, in either section. |
|
|
699
685
|
| `r` | Re-probe the doctor (Extensions). |
|
|
700
686
|
| `↑`/`↓`, `j`/`k` | Move the selection, in either section. |
|
|
@@ -720,7 +706,7 @@ has a keyboard path.
|
|
|
720
706
|
| wechat | — | `wechat/credentials.json` (not logged in) |
|
|
721
707
|
| job | — | Windows has no `EXIT` trap; `ps` is POSIX-only |
|
|
722
708
|
| 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` |
|
|
709
|
+
| 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
710
|
| question | — | needs an interactive UI |
|
|
725
711
|
|
|
726
712
|
Every extension that reads a config file also gets the malformed-JSON check:
|
|
@@ -775,6 +761,46 @@ adds its id, status, exit code, and label. Every piece that can carry model- or
|
|
|
775
761
|
user-authored text — a prompt title, a job label — is sanitized to one
|
|
776
762
|
control-free line before it reaches an escape sequence or a shell argument.
|
|
777
763
|
|
|
764
|
+
## Update check
|
|
765
|
+
|
|
766
|
+
A session can be days behind npm without anyone noticing, and Pi's own startup
|
|
767
|
+
check only covers package sources it manages — a `pi -e ./`, a linked checkout,
|
|
768
|
+
or a git install gets nothing. So bytefly reads its own published version and
|
|
769
|
+
reports it once, in the two places it already owns:
|
|
770
|
+
|
|
771
|
+
```
|
|
772
|
+
█▀█ Pi v1.3.0
|
|
773
|
+
█▀ █ enhanced by bytefly v0.11.4 ↑0.12.0
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
```
|
|
777
|
+
bytefly · 0.12.0 is available
|
|
778
|
+
╎ ◌ current · v0.11.4
|
|
779
|
+
╎ ◌ update · pi update --extensions
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
The marker is the shared `↑` glyph and the newest version — the arrow is one cell
|
|
783
|
+
wide, so the header's column math is unchanged — painted with the header's
|
|
784
|
+
`accent` role: an action rather than a warning, so the muted version beside it
|
|
785
|
+
stays quiet and the transcript is not turned yellow for a normal state. It is
|
|
786
|
+
part of the chrome cell, so the two identity cells still line up, and it is the
|
|
787
|
+
first thing the identity sheds when the terminal narrows.
|
|
788
|
+
|
|
789
|
+
The notice is the shared transcript block: the `bytefly · title` headline at the
|
|
790
|
+
margin, then the loaded version and the fixing command behind the rail, all at
|
|
791
|
+
`info` severity because nothing is broken. It stays in scrollback and the model
|
|
792
|
+
reads the same sentence in plain text.
|
|
793
|
+
|
|
794
|
+
The request is `GET https://registry.npmjs.org/@gavin-hu%2Fmy-pi-agent/latest`,
|
|
795
|
+
fired without awaiting at `session_start` (Pi awaits handlers, and a five-second
|
|
796
|
+
registry call must not hold the first prompt) with a five-second abort. Only a
|
|
797
|
+
strictly newer, parseable semver announces anything: a non-ok status, a body
|
|
798
|
+
that is not JSON, a missing version, a network error, or an older or equal
|
|
799
|
+
version is silent. Nothing is cached to disk and nothing is retried, so a
|
|
800
|
+
session that starts offline simply sees no marker. `PI_OFFLINE` (Pi's own
|
|
801
|
+
switch) turns the check off; `PI_SKIP_VERSION_CHECK` only governs Pi's
|
|
802
|
+
`pi.dev` request.
|
|
803
|
+
|
|
778
804
|
## Limitations
|
|
779
805
|
|
|
780
806
|
- **Bytefly startup TUI is the startup surface only.** It installs or removes
|
|
@@ -790,6 +816,10 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
790
816
|
`pi config` is the place to manage it.
|
|
791
817
|
- **The rail glyph picker is curated.** Four one-column glyphs are offered; a
|
|
792
818
|
custom glyph is still a `bytefly.json` edit.
|
|
819
|
+
- **Settings cover scalars only.** A row is a boolean, a short enum, or a short
|
|
820
|
+
string. The per-trigger notify flags (`notify.turnFinished`, `needsInput`,
|
|
821
|
+
`jobFinished`), arrays (`doctor.skip`), a numeric range (`doctor.timeoutMs`),
|
|
822
|
+
the `rates` table, and `price.peak` are still `bytefly.json` edits.
|
|
793
823
|
- **`doctor` is a reserved first argument.** `/bytefly doctor` always selects the
|
|
794
824
|
report, so an extension literally named `doctor` cannot be narrowed to by
|
|
795
825
|
name. `settings` is reserved the same way.
|
|
@@ -799,8 +829,24 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
799
829
|
their authoritative names are not exposed to extensions. Use `pi config`.
|
|
800
830
|
- **Packages are not installed, removed, or updated here**; use `pi install`,
|
|
801
831
|
`pi remove`, and `pi update`.
|
|
802
|
-
- **
|
|
803
|
-
|
|
832
|
+
- **The update check reads npm's `latest` only.** A private registry, a version
|
|
833
|
+
published under another tag, or a checkout ahead of npm reports nothing, and
|
|
834
|
+
there is no channel or prerelease preference: `0.11.4` is behind `0.12.0`
|
|
835
|
+
whether or not a `0.12.0-beta.1` exists.
|
|
836
|
+
- **One check per session, with no refresh.** It runs at `session_start` in
|
|
837
|
+
`tui` mode; `/bytefly` and `doctor` do not report it, nothing is cached to
|
|
838
|
+
disk, and a failed or slow request is simply not retried that session.
|
|
839
|
+
- **The marker needs the bytefly startup TUI.** It lives on the custom header,
|
|
840
|
+
so with `quietStartup` off the notice is the whole signal.
|
|
841
|
+
- **Pi may report the same update.** An npm-installed copy can also show Pi's
|
|
842
|
+
own `Package Updates Available` block; the two checks are independent and
|
|
843
|
+
`pi update --extensions` satisfies both.
|
|
844
|
+
- **No auto-update and no release notes.** The notice names a command instead
|
|
845
|
+
of running one, and the notice renderer has no link support, so there is no
|
|
846
|
+
changelog URL.
|
|
847
|
+
- **Project toggles are two-state per press** (`enable`/`disable`). `x` clears
|
|
848
|
+
the override, which is the third state; a resource with no project entry in
|
|
849
|
+
`pi config` is still managed there.
|
|
804
850
|
- **One built-in provider.** DeepSeek is the only provider with a built-in
|
|
805
851
|
table; another provider's currency and tariff have to be stated in
|
|
806
852
|
`bytefly.json`.
|
|
@@ -810,10 +856,17 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
810
856
|
with an `offPeakMultiplier` below 1 is affected by it, so two providers with
|
|
811
857
|
different time-of-day tariffs cannot both be described.
|
|
812
858
|
- **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
|
|
859
|
+
Anthropic's 2× one-hour cache write are not modelled; switch to `USD` mode
|
|
860
|
+
to use Pi's recorded cost for those models.
|
|
861
|
+
- **No FX conversion.** The two modes are separate methods; neither converts
|
|
862
|
+
one currency into the other. A model is priced only by the selected method.
|
|
863
|
+
- **Two built-in footer indicators cannot be reproduced.** Pi's `(auto)` marks
|
|
864
|
+
that auto-compaction is on, and its `(sub)` marks a subscription-backed
|
|
865
|
+
provider. Auto-compaction is session state on Pi's own session object, and
|
|
866
|
+
`ReadonlyFooterDataProvider` exposes only the branch, the extension statuses,
|
|
867
|
+
and the provider count; `isUsingSubscription` lives on the internal model
|
|
868
|
+
runtime, not on the `ModelRegistry` an extension receives. A subscription
|
|
869
|
+
session therefore shows no cost segment at all, where Pi's footer said `(sub)`.
|
|
817
870
|
- **The displayed number is the total, rounded once** — not the sum of rounded
|
|
818
871
|
records.
|
|
819
872
|
- **`doctor` can only check what is declared.** A new extension with no
|
|
@@ -835,8 +888,9 @@ control-free line before it reaches an escape sequence or a shell argument.
|
|
|
835
888
|
prompt raised during a run, not a process waiting on stdin: jobs are
|
|
836
889
|
fire-and-observe, so that state does not exist.
|
|
837
890
|
- **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
|
-
|
|
891
|
+
host lacks (or an OSC method off a TTY) and the notification is dropped; only
|
|
892
|
+
`auto` inspects the environment. `doctor` now reports the mismatch as a
|
|
893
|
+
`degraded` finding, which says what to change but does not fix it for you.
|
|
840
894
|
- **The working counter is one global row.** `setWorkingMessage` drives the
|
|
841
895
|
single working row, so bytefly and any other extension setting it race — last
|
|
842
896
|
writer wins — and the message is reset to Pi's default at `agent_end`. If the
|
|
@@ -878,7 +932,7 @@ rather than showing a wrong value.
|
|
|
878
932
|
|
|
879
933
|
| Integration point | Details |
|
|
880
934
|
|---|---|
|
|
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. |
|
|
935
|
+
| 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
936
|
| Footer | `ctx.ui.setFooter` with a custom component, replacing Pi's built-in footer; installed only when `ctx.mode === "tui"`. |
|
|
883
937
|
| 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
938
|
| 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 +943,15 @@ rather than showing a wrong value.
|
|
|
889
943
|
| 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
944
|
| Picker discovery | `SettingsManager.create(...)` plus `DefaultPackageManager.resolve({ onMissing: "skip" })`, both public exports; both views drop `bytefly`'s own entrypoint. |
|
|
891
945
|
| 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`),
|
|
946
|
+
| 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. |
|
|
947
|
+
| 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
948
|
| 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
949
|
| 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
|
-
|
|
|
950
|
+
| Update check | `session_start` in `tui` mode, when `PI_OFFLINE` is off, fires `fetchLatestVersion()` without awaiting, so the handler never holds the prompt. 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. A generation counter drops a reply that lands after `session_shutdown`, and a per-load flag keeps it to one announcement. |
|
|
951
|
+
| 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. |
|
|
897
952
|
| 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`,
|
|
953
|
+
| 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 nothing to the session or a file. |
|
|
954
|
+
| 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
955
|
|
|
901
956
|
## Design notes
|
|
902
957
|
|
|
@@ -991,14 +1046,12 @@ rather than showing a wrong value.
|
|
|
991
1046
|
after a `╎` keeps the percentage in a fixed place while the mode stays
|
|
992
1047
|
visible as the meters drop.
|
|
993
1048
|
- **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.
|
|
1049
|
+
`setStatus` value on its own line, so the bar sorts them by meaning instead:
|
|
1050
|
+
`worktree` and `browse` are location, so the `◉ <port>` chip sits beside `⎇`
|
|
1051
|
+
branch and `⑂` worktree and the left zone stays the filesystem identity; a
|
|
1052
|
+
plan or a failed job is mode or alert, so it takes the slot after the gauge.
|
|
1053
|
+
The [wechat](../wechat/) extension publishes `✆ wechat` while its bridge runs,
|
|
1054
|
+
which is why the bar has to render `getExtensionStatuses()` at all.
|
|
1002
1055
|
- **Segments degrade; they do not clip.** Each `Segment` carries progressively
|
|
1003
1056
|
shorter `forms` and a `weight`; the layout advances the lowest-priority form
|
|
1004
1057
|
or drops the lowest-priority segment, and only truncates once everything is
|
|
@@ -1043,10 +1096,10 @@ rather than showing a wrong value.
|
|
|
1043
1096
|
usage records (`collect/`), records are priced into totals (`price/`), and the
|
|
1044
1097
|
snapshot is drawn (`render/`). Pricing is a pure function of records and rates
|
|
1045
1098
|
— no session, terminal, or theme to stub — which is what lets the published
|
|
1046
|
-
DeepSeek example be a unit test.
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1099
|
+
DeepSeek example be a unit test. In `CNY` mode a record with no matching rate
|
|
1100
|
+
simply contributes nothing; `USD` mode is the opt-in path to Pi's own number.
|
|
1101
|
+
The built-in DeepSeek table is only the default value of `rates`, so nothing
|
|
1102
|
+
in the pricer is provider-specific.
|
|
1050
1103
|
- **DeepSeek is built in, not bundled as a catalog.** Its published price is in
|
|
1051
1104
|
yuan at two tariffs, while Pi's catalog holds one USD number per model, so an
|
|
1052
1105
|
unconfigured DeepSeek session showed a bill that was neither the right
|
|
@@ -1084,12 +1137,16 @@ rather than showing a wrong value.
|
|
|
1084
1137
|
- **The install hint has one home.** `document` and `web-access` format their
|
|
1085
1138
|
tool errors from the same `REQUIREMENTS` entries `doctor` prints, so a user
|
|
1086
1139
|
reads the same command whether a tool failed or the report named it.
|
|
1087
|
-
- **Settings are a curated table, not a schema walker.**
|
|
1088
|
-
row types,
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1140
|
+
- **Settings are a curated table, not a schema walker.** Eleven rows prove the
|
|
1141
|
+
row types — a boolean, an enum, and a short string with its own rule — both
|
|
1142
|
+
scopes, both write paths, and one nested leaf (`price.currency.decimals`). A row states its section and the path to its
|
|
1143
|
+
leaf, and one reader per section resolves it project-over-global-over-defaults
|
|
1144
|
+
through the same normalizer the extension loads with, so the screen and the
|
|
1145
|
+
runtime cannot disagree about a value. A generic walker would expose keys the
|
|
1146
|
+
screen cannot edit safely yet — arrays, nested tariffs, free text,
|
|
1147
|
+
credentials — so the catalogue grows one proven family at a time. App rows are
|
|
1148
|
+
Global-only because Pi's setters write the personal file; Project scope lists
|
|
1149
|
+
only the `bytefly.json` rows it can actually write.
|
|
1093
1150
|
- **The doctor is quiet unless the bytefly startup TUI is on.** Only `missing`
|
|
1094
1151
|
findings post a notice, only for enabled extensions, only with a UI, only once
|
|
1095
1152
|
per session, and only when Pi's `quietStartup` is `true` — the same state that
|
|
@@ -1108,6 +1165,21 @@ rather than showing a wrong value.
|
|
|
1108
1165
|
- **The prerequisite table is data.** No I/O, no host access, no module state:
|
|
1109
1166
|
every probe dependency arrives as a seam, so `probeExtension` is testable with
|
|
1110
1167
|
no process and `lib/` stays value-only.
|
|
1168
|
+
- **The update check is a self-report, not an update mechanism.** It answers one
|
|
1169
|
+
question — is the version in this checkout current? — and answers it in the two
|
|
1170
|
+
surfaces bytefly already owns, the header identity and one notice. It never
|
|
1171
|
+
installs, never edits settings, and never fails a session: an unreachable
|
|
1172
|
+
registry and an up-to-date one are indistinguishable to the user, which is the
|
|
1173
|
+
only failure mode a background check is allowed to have.
|
|
1174
|
+
- **The marker is an action, the notice is information.** The same fact is drawn
|
|
1175
|
+
in the accent on the header, where it sits beside a control the user can act
|
|
1176
|
+
on, and at `info` severity in the transcript, where a `warn` rail would say
|
|
1177
|
+
something is broken. Neither surface needs per-frame polling: the value arrives
|
|
1178
|
+
once, the memo key includes it, and one `setHeader` re-install repaints.
|
|
1179
|
+
- **The transport stays local.** `web-access`'s runner does redirects, SSRF
|
|
1180
|
+
checks, HTML extraction, and caching — none of it applies to one fixed JSON
|
|
1181
|
+
endpoint, and an extension may not import another's modules, so the ~20-line
|
|
1182
|
+
read lives beside the check with the `fetch` and timeout as seams.
|
|
1111
1183
|
|
|
1112
1184
|
## Files
|
|
1113
1185
|
|
|
@@ -1117,7 +1189,8 @@ calculation), `message/` (the user-message marker), `notice/` (the shared notice
|
|
|
1117
1189
|
elapsed counter on Pi's working row), `panel/` (the
|
|
1118
1190
|
unified screen and its merged model),
|
|
1119
1191
|
`picker/` (discovery and the settings store), `doctor/` (the `run_doctor` tool
|
|
1120
|
-
and its probe runtime),
|
|
1192
|
+
and its probe runtime), `notify/`, and `update/` (the published-version check
|
|
1193
|
+
behind the header marker). The chrome and the screen are drawn from
|
|
1121
1194
|
the package's [`lib/tui/`](../../lib/tui/) design system (tokens, rules, rows,
|
|
1122
1195
|
columns, gauge), so the look follows the active theme.
|
|
1123
1196
|
|
|
@@ -1135,10 +1208,10 @@ columns, gauge), so the look follows the active theme.
|
|
|
1135
1208
|
| `footer/collect/entries.ts` | Session entries → usage records, each resolved to the model in effect. |
|
|
1136
1209
|
| `footer/collect/snapshot.ts` | Snapshot assembly; memoized pricing scan per branch leaf. |
|
|
1137
1210
|
| `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. |
|
|
1211
|
+
| `footer/price/config.ts` | The `price` section of `bytefly.json`: schema, defaults, built-in DeepSeek rates/tariff, currency defaults, normalizer, loader. |
|
|
1139
1212
|
| `footer/price/rates.ts` | Model-key matching: exact and wildcard, most specific first. |
|
|
1140
1213
|
| `footer/price/schedule.ts` | Peak-window parsing and evaluation in a configured zone. |
|
|
1141
|
-
| `footer/price/price.ts` | Records → totals:
|
|
1214
|
+
| `footer/price/price.ts` | Records → totals: rated cost in both modes, Pi's recorded cost as USD's fallback, and the peak multiplier. |
|
|
1142
1215
|
| `footer/render/format.ts` | Generic formatters (tokens, cost in the configured currency, cwd, the context role, truncation). |
|
|
1143
1216
|
| `footer/render/status.ts` | Status-chip transforms: `compactStatus` and `dropStatusDetail`. |
|
|
1144
1217
|
| `footer/render/segments.ts` | The segment builders: forms, weights, separators, roles. |
|
|
@@ -1166,8 +1239,8 @@ columns, gauge), so the look follows the active theme.
|
|
|
1166
1239
|
| `panel/types.ts` | `PanelSection` (`extensions` | `settings`), `PanelRow`, and `PanelModel`, the merged row model. |
|
|
1167
1240
|
| `panel/join.ts` | The item/report union with the unambiguous-name health rule and unique row keys, groups, and ordering. |
|
|
1168
1241
|
| `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-
|
|
1242
|
+
| `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. |
|
|
1243
|
+
| `panel/settings-runtime.ts` | The Settings runtime: snapshot cache, scope guards, app setters, per-leaf `bytefly.json` writes, dirty state, and change listeners. |
|
|
1171
1244
|
| `panel/tui.ts` | `PanelComponent`, the two-section dock screen (Extensions list and Settings list), drawn from the `lib/tui/` kit. |
|
|
1172
1245
|
| `panel/commands.ts` | `/bytefly`: one command for the screen sections, the `doctor` report, the settings listing, and their text modes. |
|
|
1173
1246
|
| `picker/types.ts` | Picker host seams (`SettingsStore`, `ExtensionResolver`, `Discovery`), the app-settings setters, and row types. |
|
|
@@ -1186,6 +1259,11 @@ columns, gauge), so the look follows the active theme.
|
|
|
1186
1259
|
| `notify/dispatch.ts` | Method selection and delivery: OSC 777/99, Windows toast, `osascript`, `notify-send`, over injected seams. |
|
|
1187
1260
|
| `notify/format.ts` | Pure notification text: session label and the turn / input / job bodies. |
|
|
1188
1261
|
| `notify/register.ts` | Registers the three triggers, loads the config at `session_start`, and owns the job-bus subscription. |
|
|
1262
|
+
| `update/check.ts` | The one registry read: the `latest` URL, the abort timeout, the `FetchLike` seam, and the failure-to-`undefined` mapping. |
|
|
1263
|
+
| `update/version.ts` | Pure semver parsing and `isNewerVersion`, including prerelease precedence. |
|
|
1264
|
+
| `update/state.ts` | `UpdateSource` and `createUpdateState`: the newest known version, one record per load. |
|
|
1265
|
+
| `update/format.ts` | `updateMarker` and `updateNotice`: the header's `↑0.12.0` and the two-row transcript notice. |
|
|
1266
|
+
| `update/register.ts` | The session-start check: the async read, the `PI_OFFLINE` and `tui` gates, the once-per-load announcement, and the shutdown generation guard. |
|
|
1189
1267
|
|
|
1190
1268
|
## Testing
|
|
1191
1269
|
|
|
@@ -1216,7 +1294,8 @@ transforms (`picker/toggle`), discovery with an injected factory
|
|
|
1216
1294
|
(`picker/discovery`), and the runtime state machine (`picker/runtime`). The
|
|
1217
1295
|
unified screen is covered by its pure model merge (`panel/join`), its text
|
|
1218
1296
|
formatter (`panel/format`), the settings catalogue and its cycling
|
|
1219
|
-
(`panel/settings`), the scope-aware settings runtime (`panel/settings-runtime
|
|
1297
|
+
(`panel/settings`), the scope-aware settings runtime (`panel/settings-runtime`,
|
|
1298
|
+
including a nested leaf write that keeps its siblings),
|
|
1220
1299
|
the screen's rendering and key handling (`panel/tui`), and the command through
|
|
1221
1300
|
the bytefly factory (`panel/commands`).
|
|
1222
1301
|
The doctor surface is covered by its pure report
|
|
@@ -1225,7 +1304,12 @@ registration, the proactive notice, the session cache, and `run_doctor`
|
|
|
1225
1304
|
through the factory (`doctor/register`). The notify capability is covered by its section loader
|
|
1226
1305
|
(`notify/config`), the pure text builders (`notify/format`), method selection and
|
|
1227
1306
|
delivery over recording seams (`notify/dispatch`), and its three triggers,
|
|
1228
|
-
reload, and shutdown through `registerNotify` (`notify/register`).
|
|
1307
|
+
reload, and shutdown through `registerNotify` (`notify/register`). The update
|
|
1308
|
+
check is covered by the version-ordering table (`update/version`), the transport
|
|
1309
|
+
with an injected fetch (`update/check`), the per-load store (`update/state`), the
|
|
1310
|
+
marker and notice wording (`update/format`), and its lifecycle — once per load,
|
|
1311
|
+
silent on failure or an older version, skipped offline and outside `tui`, and
|
|
1312
|
+
immune to a reply that lands after shutdown (`update/register`).
|
|
1229
1313
|
The user-message nameplate is covered by its pure transform
|
|
1230
1314
|
(`message/format`, including a render through pi-tui's real `Markdown`) and its
|
|
1231
1315
|
registration (`message/register`), plus the factory check in `extension`.
|