@gavin-hu/my-pi-agent 0.20.0 → 0.22.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 +46 -30
- package/README.zh-CN.md +40 -25
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -23
- package/docs/extensions/board.md +397 -168
- package/docs/extensions/bytefly.md +234 -197
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +57 -63
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +197 -140
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +145 -0
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +83 -44
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +79 -25
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +17 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +5 -20
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +65 -93
- package/extensions/board/result.ts +30 -39
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +49 -11
- package/extensions/board/snapshot.ts +8 -6
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +154 -55
- package/extensions/board/transport/router.ts +3 -5
- package/extensions/board/transport/server.ts +59 -28
- package/extensions/board/types.ts +36 -42
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +35 -16
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +90 -28
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +38 -13
- package/extensions/board/view/render.ts +219 -277
- package/extensions/board/view/styles.ts +130 -32
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +7 -8
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- package/extensions/bytefly/footer/render/key.ts +46 -0
- package/extensions/bytefly/footer/render/layout.ts +13 -3
- package/extensions/bytefly/footer/render/lines.ts +10 -7
- package/extensions/bytefly/footer/render/segments.ts +41 -38
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/render.ts +10 -1
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/index.ts +89 -79
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -4
- package/extensions/file-browse/transport/server.ts +61 -28
- package/extensions/file-browse/view/chrome.ts +4 -4
- package/extensions/file-browse/view/client.ts +3 -1
- package/extensions/file-browse/view/html.ts +73 -40
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +13 -14
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +9 -9
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +21 -9
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -99
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/feedback.ts +62 -16
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +58 -51
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +61 -41
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-blocks.ts +76 -5
- package/extensions/plan/web-chrome.ts +4 -0
- package/extensions/plan/web-client.ts +102 -2
- package/extensions/plan/web-collab.ts +4 -4
- package/extensions/plan/web-comments.ts +39 -8
- package/extensions/plan/web-composer.ts +14 -3
- package/extensions/plan/web-keys.ts +149 -25
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +10 -68
- package/extensions/plan/web-outline.ts +22 -2
- package/extensions/plan/web-page.ts +56 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +23 -13
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web-settle.ts +14 -7
- package/extensions/plan/web-share-session.ts +24 -55
- package/extensions/plan/web-share.ts +19 -37
- package/extensions/plan/web-write.ts +112 -0
- package/extensions/plan/web.ts +67 -131
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +39 -0
- package/extensions/serve/index.ts +85 -0
- package/extensions/serve/registry.ts +260 -0
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +17 -9
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -5
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/{tui.ts → view/todo-screen.ts} +18 -90
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +28 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +30 -5
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +199 -51
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +119 -36
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +81 -63
- package/lib/bus-request.ts +83 -0
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +17 -11
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +30 -0
- package/lib/http/serve.ts +170 -46
- package/lib/http/server.ts +3 -3
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +46 -21
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +10 -10
- package/lib/session-label.ts +129 -7
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +39 -22
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +29 -11
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +11 -11
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +33 -4
- package/lib/web/README.md +21 -0
- package/lib/web/client-runtime.ts +27 -1
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +20 -2
- package/lib/web/runtime.ts +9 -10
- package/package.json +8 -2
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/config.ts +0 -36
- package/extensions/board/seat.ts +0 -28
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
|
@@ -8,22 +8,20 @@ Modelled on Claude Code's `AskUserQuestion`.
|
|
|
8
8
|
|
|
9
9
|
## Capabilities
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
| Cancelling | `Esc` | The model is told you cancelled and can continue or ask in prose. | Shipped |
|
|
26
|
-
| Branch-scoped answers | tool-result `details` | The answers stay with the transcript, so `/resume` and `/tree` show the same history. | Shipped |
|
|
11
|
+
| Capability | How | What you get |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Mid-turn asking | `ask_user_question` | The model asks 1–4 questions and the turn waits until you answer or cancel. |
|
|
14
|
+
| Labelled options | `questions[].options` | Each question offers up to four choices, each with an optional second line describing it. |
|
|
15
|
+
| Free-form answers | the automatic `Other (type something)` row, `questions[].options` omitted | You can type an answer the options do not cover, and a question with no options asks for plain text. |
|
|
16
|
+
| Multiple selections | `questions[].multiSelect` | `Space` toggles options and `Enter` confirms the set. |
|
|
17
|
+
| Batched questions | `questions[]` | Up to four questions ride in one call and are answered in one pass. |
|
|
18
|
+
| Submit gating | the `Submit` tab | Submit refuses until every question is answered and names the missing ones; a single question submits on `Enter` without a Submit tab. |
|
|
19
|
+
| Tab headers | `questions[].header` | Each question gets a short tab label, defaulted to `Q{n}` and clipped to 12 columns. |
|
|
20
|
+
| Language-aware batch | `dialogUI.language` | A remote turn in Chinese gets the batch prompt, retry text, and format line in Chinese. |
|
|
21
|
+
| Dialog fallback | `select`, `input`, `editor` | RPC and remote turns answer the same questions without the TUI screen. |
|
|
22
|
+
| No-UI degradation | `print`, `json` | The tool returns `unavailable` with an error instead of hanging when no dialog channel exists. |
|
|
23
|
+
| Cancelling | `Esc` | The model is told you cancelled and can continue or ask in prose. |
|
|
24
|
+
| Branch-scoped answers | tool-result `details` | The answers stay with the transcript, so `/resume` and `/tree` show the same history. |
|
|
27
25
|
|
|
28
26
|
## Quickstart
|
|
29
27
|
|
|
@@ -183,7 +181,7 @@ UI.
|
|
|
183
181
|
for free.
|
|
184
182
|
- **A pure state machine behind the TUI.** `tui-state.ts` holds a reducer
|
|
185
183
|
(`reduce(state, action) → { state, effect }`) so navigation, toggling, tab
|
|
186
|
-
movement, and submit gating are tested without a terminal; `
|
|
184
|
+
movement, and submit gating are tested without a terminal; `view/question-screen.ts` only maps
|
|
187
185
|
state to lines and forwards input. Render output is cached and cleared from
|
|
188
186
|
`invalidate()`, and themed strings are never stored across renders.
|
|
189
187
|
- **Claude-Code-compatible parameter shape.** `{ label, description? }` options
|
|
@@ -212,7 +210,7 @@ UI.
|
|
|
212
210
|
| [`types.ts`](../../extensions/question/types.ts) | Shared raw/normalized types |
|
|
213
211
|
| [`answers.ts`](../../extensions/question/answers.ts) | Answer normalization and model-facing formatting (pure) |
|
|
214
212
|
| [`tui-state.ts`](../../extensions/question/tui-state.ts) | Pure keyboard/navigation state machine |
|
|
215
|
-
| [`
|
|
213
|
+
| [`view/question-screen.ts`](../../extensions/question/view/question-screen.ts) | Custom terminal component (render + input), loaded on first use, drawn from the `lib/tui/` kit (`titleRule`/`plainRule`, `checkbox`, `flowCells`, `screenHint`, `editorTheme`). |
|
|
216
214
|
| [`dialogs.ts`](../../extensions/question/dialogs.ts) | `select`/`input`/`editor` fallback driver |
|
|
217
215
|
| [`tools.ts`](../../extensions/question/tools.ts) | Tool registration and transcript renderers |
|
|
218
216
|
|
|
@@ -12,24 +12,22 @@ file, a selection, or the whole turn.
|
|
|
12
12
|
|
|
13
13
|
## Capabilities
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
| Review plan guard | `/review` in plan mode | Review refuses while plan mode is on, matching the read-only planning rule. | Shipped |
|
|
32
|
-
| Prompt-count chip | `↺ N`, the `showStatus` key | The status bar counts the prompts on the active branch, whether or not each has a snapshot. | Shipped |
|
|
15
|
+
| Capability | How | What you get |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Automatic snapshots | the `autoSnapshots` key | One snapshot per prompt, taken before its first mutating call, so every task has a restore point. |
|
|
18
|
+
| Durable snapshots | `refNamespace` | Snapshots survive the session and are listed from git rather than from session state. |
|
|
19
|
+
| Prompt timeline | `/rewind` | Every user prompt on the active branch, newest first, marked `⬥` where a code snapshot was captured. |
|
|
20
|
+
| Scoped restore | `/rewind` | Code, conversation, or both, from the prompt you pick. |
|
|
21
|
+
| Undoable restore | the `safetySnapshot` key | A code restore snapshots the state it replaces first, so it can itself be undone. |
|
|
22
|
+
| Partial restore | `/rewind` | The changed paths grouped by directory, chosen with a checklist, and previewed as the subset you selected. |
|
|
23
|
+
| Latest-turn review | `/review` | The changed files of the latest prompt, with `M`/`A`/`D`/`R` marks and line counts. |
|
|
24
|
+
| File, batch, and hunk revert | `/review` | Revert one file, a selection, the whole turn, or chosen hunks, each with its own safety snapshot. |
|
|
25
|
+
| Busy-git refusal | `/rewind`, `/review` | A merge, rebase, cherry-pick, revert, or bisect in progress refuses the restore instead of fighting it. |
|
|
26
|
+
| Root-scoped snapshots | `refs/pi/rewind` | Worktrees share the ref store, while another root's snapshot is filtered out and refused. |
|
|
27
|
+
| Untracked capture | `autoSnapshots` | Non-ignored untracked files are captured, and a restore removes the ones that appeared after the snapshot. |
|
|
28
|
+
| Ref cap and auto-prune | `max`, `autoPrune` | The snapshot store is bounded and pruned instead of growing without limit. |
|
|
29
|
+
| Review plan guard | `/review` in plan mode | Review refuses while plan mode is on, matching the read-only planning rule. |
|
|
30
|
+
| Prompt-count chip | `↺ N`, the `showStatus` key | The status bar counts the prompts on the active branch, whether or not each has a snapshot. |
|
|
33
31
|
|
|
34
32
|
## Quickstart
|
|
35
33
|
|
|
@@ -278,7 +276,7 @@ commit. These refs are not branches and are not touched by `git push --all`.
|
|
|
278
276
|
| `transport/store.ts` | Versioned metadata encoding and ref-backed listing/pruning. |
|
|
279
277
|
| `state/policy.ts` | Which tool calls are worth a snapshot. |
|
|
280
278
|
| `transport/temp-index.ts` | Per-process `GIT_INDEX_FILE` paths: create, release, and sweep stale files. |
|
|
281
|
-
| `
|
|
279
|
+
| `status.ts` | The `↺ N` status chip. |
|
|
282
280
|
| `transport/queue.ts` | The serialized git operation queue for staging calls (`GIT_INDEX_FILE`). |
|
|
283
281
|
| `state/runtime.ts` | Root/config caches and the snapshot/list/plan/patch/numstat/restore/hunk-revert operations. |
|
|
284
282
|
| `state/timeline.ts` | Pure: prompts on the branch, paired with their snapshots. |
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# serve — the session's one loopback server
|
|
2
|
+
|
|
3
|
+
## Capabilities
|
|
4
|
+
|
|
5
|
+
| Capability | How | What you get |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| One port for every page of a session | automatic: the first page that opens binds it | the file browser at `/files/`, the board at `/board/`, and a review at `/plan/<id>/` answer on one origin, so a bookmark, a firewall rule, and a port to remember are one thing |
|
|
8
|
+
| One chip for that port | `◉ <port>` in the status bar | the session's port while anything is served on it, and nothing when no page is open |
|
|
9
|
+
| A port that survives a restart | automatic: the project's own port | a session that starts again lands where a bookmark already points, so a saved URL still opens the page it names; `serve.port` overrides it |
|
|
10
|
+
| Two projects on one machine | automatic: the port is the repository's, hashed into the band | neither needs a setting to avoid the other, and a worktree of one repository keeps the number the main checkout uses |
|
|
11
|
+
| A second session on its own port | automatic: servers are keyed by Pi session | a WeChat peer conversation never answers on this session's port |
|
|
12
|
+
| One page reachable on the LAN, on request | the asking page calls `mount.share()` | a teammate's link names exactly one address, and only that page answers there |
|
|
13
|
+
|
|
14
|
+
## Quickstart
|
|
15
|
+
|
|
16
|
+
There is nothing to run: `serve` is infrastructure, and the pages that use it are
|
|
17
|
+
`/board`, `/browse`, and `/plan review`.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
/board # http://127.0.0.1:<port>/board/?token=…
|
|
21
|
+
/browse # http://127.0.0.1:<port>/files/
|
|
22
|
+
/plan review # http://127.0.0.1:<port>/plan/3f9c21aa/?token=…
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
All three answer on that one port — the project's own, in `4780`–`5779` — and the
|
|
26
|
+
footer shows `◉ <port>` while any of them is open, dropping the chip when the last
|
|
27
|
+
one closes.
|
|
28
|
+
|
|
29
|
+
## Routes
|
|
30
|
+
|
|
31
|
+
The server holds prefixes, not pages. Each one is claimed by the extension that
|
|
32
|
+
serves it, and a request reaches that extension with its prefix stripped:
|
|
33
|
+
|
|
34
|
+
| Prefix | Served by | Note |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `/files/` | `file-browse` | the file browser: its tree, its file pages, its own script, and the links it rewrites out of Markdown, all built from this prefix; `/files/browse/<path>` is one directory |
|
|
37
|
+
| `/board/` | `board` | the read-only board page, with its token in the query |
|
|
38
|
+
| `/plan/<id>/` | `plan` | one review; the id is the review's, so two reviews never share a namespace |
|
|
39
|
+
|
|
40
|
+
Anything else is a `404` with a plain-text body, and a prefix named without its
|
|
41
|
+
trailing slash (`/board`) is redirected to the slashed form, because a page's own
|
|
42
|
+
links are relative to its directory. A prefix another page already holds is
|
|
43
|
+
refused by name rather than served twice.
|
|
44
|
+
|
|
45
|
+
## Behaviour by mode
|
|
46
|
+
|
|
47
|
+
The server runs in every mode, but the chip needs a UI: RPC and JSON sessions serve
|
|
48
|
+
their pages and skip it. Nothing here reads or writes the transcript.
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
The `serve` section of the shared `bytefly.json` (global, then project, project wins:
|
|
53
|
+
|
|
54
|
+
| Key | Default | What it does |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `port` | `0` | `0` takes the project's own port — its repository key hashed into `4780`–`5779` — and falls back to an OS-chosen one when that port is taken; a positive value pins the session's server so a bookmarked address survives a restart, and a taken pin is reported by name instead of moved |
|
|
57
|
+
|
|
58
|
+
The default is the repository's, so two repositories take two numbers without a
|
|
59
|
+
setting and a restart lands where the bookmark was. A second session of one
|
|
60
|
+
repository — a WeChat peer, or another checkout — prefers the same number and takes
|
|
61
|
+
an OS-chosen port when the first one holds it.
|
|
62
|
+
|
|
63
|
+
The pin belongs to the first session in a process that binds: a second session takes
|
|
64
|
+
an OS-chosen port instead of failing on a port the host already holds. A second
|
|
65
|
+
*process* that finds the pinned port taken is told so rather than moved silently off
|
|
66
|
+
the address a bookmark names.
|
|
67
|
+
|
|
68
|
+
## Security
|
|
69
|
+
|
|
70
|
+
The server binds `127.0.0.1`. The unit of exposure is the mount: a prefix answers
|
|
71
|
+
only what its page routes, and each page keeps its own `Host` allowlist, its own
|
|
72
|
+
token, and its own method list in `lib/http`.
|
|
73
|
+
|
|
74
|
+
A shared review is the one exception, and it is the reviewing page's own: it asks
|
|
75
|
+
for a listener on a single LAN address, and that listener carries only the prefixes
|
|
76
|
+
that asked for it. `file-browse` and `board` are never reachable that way, and a page
|
|
77
|
+
that stops sharing leaves the listener at once — the socket closes with the last page
|
|
78
|
+
that was still sharing it.
|
|
79
|
+
|
|
80
|
+
## Limitations
|
|
81
|
+
|
|
82
|
+
- A page needs `serve` loaded. Without it every page refuses with a sentence naming
|
|
83
|
+
the extension, instead of binding a rival socket.
|
|
84
|
+
- Two projects can hash into the same port, and the second start takes an OS-chosen one
|
|
85
|
+
instead: its bookmark moves until that project sets `serve.port`.
|
|
86
|
+
- Two checkouts of one repository at once prefer one number, so the second takes an
|
|
87
|
+
OS-chosen port each session and only the first keeps the address a bookmark names.
|
|
88
|
+
- The default is a guess: a port another process holds moves the session to an
|
|
89
|
+
OS-chosen one without a word — the chip shows the number — while a pinned port that is
|
|
90
|
+
taken is refused by name.
|
|
91
|
+
|
|
92
|
+
## Non-goals
|
|
93
|
+
|
|
94
|
+
- It serves no page of its own, and registers no tool and no command.
|
|
95
|
+
- It is not a general web server: loopback, a page's own routes, one optional LAN
|
|
96
|
+
address per shared page, and nothing else.
|
|
97
|
+
- It does not proxy or rewrite; a page's paths are its own, under its prefix.
|
|
98
|
+
|
|
99
|
+
## Pi integration
|
|
100
|
+
|
|
101
|
+
- Registers `pi.events.on(HTTP_MOUNT_CHANNEL, …)` in the factory — registration only.
|
|
102
|
+
Binding happens on the first mount, never at load and never on `session_start`.
|
|
103
|
+
- `session_start` records the session's working directory and its context, so the
|
|
104
|
+
pin is read from that session's config and the chip is drawn in its UI.
|
|
105
|
+
- `session_shutdown` closes the session's socket, its LAN listener, and its chip.
|
|
106
|
+
It is idempotent.
|
|
107
|
+
|
|
108
|
+
## Design notes
|
|
109
|
+
|
|
110
|
+
A session has one port, so the socket cannot belong to a page. The page asks for a
|
|
111
|
+
prefix over `pi.events` — the mechanism the repository already uses for a
|
|
112
|
+
cross-extension request that must be answered — and `lib/http` owns the socket, the
|
|
113
|
+
dispatch, the body cap per mount, and the idle grace per mount.
|
|
114
|
+
|
|
115
|
+
The handshake is *claimed*, not awaited: `server.listen` cannot answer synchronously,
|
|
116
|
+
so the owner responds with the pending bind and only a request nobody claimed is
|
|
117
|
+
refused. Without that distinction a bind that is still starting looks exactly like a
|
|
118
|
+
session with no server.
|
|
119
|
+
|
|
120
|
+
The default is the repository key hashed into the band rather than a fresh draw. The
|
|
121
|
+
key is the one identity this repository already has — `session-bus` files sessions by
|
|
122
|
+
it, `plan` files plans by it — and every worktree of a repository resolves the same
|
|
123
|
+
one, so a bookmark saved in the main checkout opens the worktree's page. A draw was
|
|
124
|
+
the reason a bookmark only worked until the next start.
|
|
125
|
+
|
|
126
|
+
## Files
|
|
127
|
+
|
|
128
|
+
| Path | Responsibility |
|
|
129
|
+
|---|---|
|
|
130
|
+
| [`../../extensions/serve/index.ts`](../../extensions/serve/index.ts) | The Pi wiring: the bus listener, the per-session directory and context, the port chip, and the shutdown |
|
|
131
|
+
| [`../../extensions/serve/registry.ts`](../../extensions/serve/registry.ts) | The session's servers: binding on first mount, the pin's owner, the remembered port, prefix refusals, the LAN listener a shared page borrows, and closing with the last mount |
|
|
132
|
+
| [`../../extensions/serve/config.ts`](../../extensions/serve/config.ts) | The `serve` section: `port`, its clamp, and the two documents it resolves over |
|
|
133
|
+
|
|
134
|
+
## Testing
|
|
135
|
+
|
|
136
|
+
`config.test.ts`, `registry.test.ts`, and `extension.test.ts`, declared under
|
|
137
|
+
`serve` in `test/suite-manifest.ts`.
|
|
138
|
+
|
|
139
|
+
The registry cases bind real sockets on `127.0.0.1`: one port for two pages of a
|
|
140
|
+
session, a second session on its own port, a refused duplicate prefix, the socket
|
|
141
|
+
closing with its last page and reopening where it was, the project's port taken by the
|
|
142
|
+
first bind and the OS-chosen fallback when another listener holds it, the pin belonging
|
|
143
|
+
to the first session that binds, and a LAN share reaching one page only. The extension
|
|
144
|
+
cases drive the shipped bus path through `requestMount`, including the project's port
|
|
145
|
+
surviving a restart, the chip, and the shutdown.
|
|
@@ -12,11 +12,9 @@ a running turn settles. There is no network service and no hidden context.
|
|
|
12
12
|
|
|
13
13
|
## Capabilities
|
|
14
14
|
|
|
15
|
-
Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
|
|
16
|
-
|
|
17
15
|
| Capability | How | What you get | Status |
|
|
18
16
|
|---|---|---|---|
|
|
19
|
-
| Membership | `session join [label]`, `/team join [<label>]`, `/board`,
|
|
17
|
+
| Membership | `session join [label]`, `/team join [<label>]`, `/board`, the first `board` claim, the board's `w` | The join: this session is on the bus under a stable label peers address, and listens for mail unless told not to. That label is what the footer draws; a label given to `session join` or `/team join` is also Pi's session name, so `/resume` agrees with the record; `/board` joins as the board's lead (`board.lead`, `lead` by default) or, on a page it is only watching, under its own preset teammate name (`tom`, `lily`, … — `team-agent-<id>` once every preset name is held); the first `board` claim joins under that same name, and renames nothing; `w` joins under the name the bus derives and writes nothing to Pi. | Shipped |
|
|
20
18
|
| Peer discovery | `session status` | Every active session with its unread count and claims, plus this session's identity, in one read — and a session whose process is gone says so, its paths free from that read on. | Unreleased |
|
|
21
19
|
| Live board | `/sessions` | Sessions, this session's unread mail, and every claim held on the repository, on one screen. | Shipped |
|
|
22
20
|
| Board actions | `/sessions` (`w`, `x`) | Start or stop listening from the board, or release an own claim behind a confirmation, without leaving it. | Shipped |
|
|
@@ -56,11 +54,11 @@ collapsed line that expands to the board's rows.
|
|
|
56
54
|
| Action | Inputs | Result |
|
|
57
55
|
|---|---|---|
|
|
58
56
|
| `status` | — | Active sessions with unread counts, their state, claims, and this session's identity. A session whose process is gone is listed as `gone` until its heartbeat ages out, and its claims are not listed at all. Prunes stale records. A read: it never adds this session to the bus. |
|
|
59
|
-
| `send` | `to`, `text`, optional `kind`, optional `paths` | Writes one message per recipient. `to: "*"` is only valid with `kind: "event"`. |
|
|
57
|
+
| `send` | `to`, `text`, optional `kind`, optional `paths` | Writes one message per recipient. `to: "*"` is only valid with `kind: "event"`. A session whose process is gone is not a recipient: naming it is refused, and a broadcast skips it. |
|
|
60
58
|
| `inbox` | optional `limit` | Returns and deletes up to `limit` messages, oldest first (default 20, max 50). |
|
|
61
59
|
| `claim` | `paths` | Claims repository-relative files or directory prefixes; returns conflicts with their owner. |
|
|
62
60
|
| `release` | `paths` | Removes claims owned by this session; reports paths owned by someone else. |
|
|
63
|
-
| `join` | optional `label`, `wake`, `listen` | Joins the bus, names this session on it and in Pi when a label is given — the same path and uniqueness rule as `/team join`, so
|
|
61
|
+
| `join` | optional `label`, `wake`, `listen` | Joins the bus, names this session on it and in Pi when a label is given — the same path and uniqueness rule as `/team join`, so `/resume` agrees with the record peers address — and starts listening for mail. The footer draws the label either way, so a plain join changes the name on the bar without renaming the session. `wake: true` raises one turn per burst once the agent is idle; `wake: false` reports at the next turn; `listen: false` joins without listening. A label a live session holds is refused, naming the session that holds it — its id, since the label it holds is the one you asked for — and the refusal leaves both names and the watch alone. The result reports the label the bus settled on and whether mail will reach this session. |
|
|
64
62
|
|
|
65
63
|
A claim is all-or-nothing: if one path conflicts, nothing is claimed. A claim on
|
|
66
64
|
a directory conflicts with claims below it and above it, so `src/api` and
|
|
@@ -68,7 +66,10 @@ a directory conflicts with claims below it and above it, so `src/api` and
|
|
|
68
66
|
takeover; wait for the owner, ask it to release, or let the session expire.
|
|
69
67
|
|
|
70
68
|
Paths resolve against the caller's working directory and are stored relative to
|
|
71
|
-
its repository root; a path outside the repository is refused
|
|
69
|
+
its repository root; a path outside the repository is refused, and so is one that
|
|
70
|
+
resolves to the repository itself — `path "." is the repository, not a path in
|
|
71
|
+
it.` — because a claim on the root would own every file in it. Below the root,
|
|
72
|
+
the same `.` claims the directory the session is in.
|
|
72
73
|
|
|
73
74
|
`wake` is the mode, not an arm-time switch: `join({ wake: true })` raises a turn
|
|
74
75
|
for a session that was reporting at the next turn, and `join({ wake: false })` —
|
|
@@ -91,9 +92,12 @@ Labels are how another session addresses you. Name one by the outcome it owns
|
|
|
91
92
|
(`billing-core`, `integrator`, `verify-billing`), not by a layer or a role;
|
|
92
93
|
duplicate labels get a short id suffix automatically.
|
|
93
94
|
|
|
94
|
-
A label is
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
A label is what the footer's `[label]` draws: bytefly reads this session's own record
|
|
96
|
+
and follows the `session-label` event, so the bar names the session the way a card, a
|
|
97
|
+
handoff, and `/team` do. `/team join <label>` makes the label Pi's session name too, so
|
|
98
|
+
`/resume` agrees with the bus; any other join leaves Pi's name alone and only the bar
|
|
99
|
+
moves. Because the bus derives a joining session's label from that name, a named session
|
|
100
|
+
comes back under the same label after a resume.
|
|
97
101
|
|
|
98
102
|
The tool's `join` action is the same join from the model's side, so a `board` or a
|
|
99
103
|
`team` call can put a session on the bus without the user naming it, and
|
|
@@ -181,6 +185,11 @@ listening watch is what raises the turn.
|
|
|
181
185
|
both sides to keep replying; the answer is to stop listening on one of them.
|
|
182
186
|
- **No broadcast history.** A broadcast reaches the sessions active at send time;
|
|
183
187
|
a session that joins later does not receive it.
|
|
188
|
+
- **Mail to a gone session is refused, not queued.** A record whose process has
|
|
189
|
+
exited is not a recipient: `send` naming it answers `no active session`, and a
|
|
190
|
+
broadcast skips it. Its inbox outlives a `/resume`, but a sender is never told a
|
|
191
|
+
message arrived at a process that has exited — the bus's rule for a claim, and
|
|
192
|
+
for `status`, is the same one.
|
|
184
193
|
- **No forced claims.** There is no takeover while an owner can still answer: a
|
|
185
194
|
claim is released by its owner, or by the next bus call once the owner's process
|
|
186
195
|
is gone. A *hung* owner still blocks its paths until the session TTL expires,
|
|
@@ -203,9 +212,10 @@ listening watch is what raises the turn.
|
|
|
203
212
|
pane's shutdown leaves the record and the claims to the newer one instead of
|
|
204
213
|
releasing them. Listening is still per process, so the survivor's watch is the
|
|
205
214
|
survivor's.
|
|
206
|
-
- **A message file that does not parse is
|
|
207
|
-
consumes mail,
|
|
208
|
-
|
|
215
|
+
- **A message file that does not parse is skipped, and left to `inbox` and the TTL.** The
|
|
216
|
+
board's read never consumes mail, so it deletes nothing — not even a file it cannot
|
|
217
|
+
validate, which may be mail this build cannot read and the next one can. Consuming it
|
|
218
|
+
or the 24-hour expiry is what clears the file.
|
|
209
219
|
- **No network.** It is not a distributed bus and does not synchronize machines.
|
|
210
220
|
|
|
211
221
|
## Pi integration
|
|
@@ -214,7 +224,7 @@ listening watch is what raises the turn.
|
|
|
214
224
|
|---|---|
|
|
215
225
|
| Tools | `session` — one action-shaped tool; `readOnlyHint: false`, `destructiveHint: true`, `openWorldHint: false`, `executionMode: "sequential"`. |
|
|
216
226
|
| Commands | `/sessions` — the board in `tui`, the listing elsewhere; no arguments and no sub-commands. |
|
|
217
|
-
| UI | `tui` mounts `SessionListComponent` through `withPolledScreen` (rails suppressed, a 1 s read-only poll of `snapshot()` + `peek()`); every other mode prints through `ctx.ui.notify`. No status chip
|
|
227
|
+
| UI | `tui` mounts `SessionListComponent` through `withPolledScreen` (rails suppressed, a 1 s read-only poll of `snapshot()` + `peek()`); every other mode prints through `ctx.ui.notify`. No status chip: the watch state changes no footer segment, and the label is the bus's own `session-label` event rather than a chip. |
|
|
218
228
|
| Events | `session_start` resolves the repository and arms the mail trigger, without joining; `session_tree` only repaints, since navigating the tree changes no pending record; `agent_start` marks the run busy; `before_agent_start` reports queued non-wake mail at the next turn; `agent_settled` retries a wake the run suppressed; `context` prunes stale notices; `session_shutdown` clears the heartbeat, the watch ticker, and the record. The seven hooks are `lib/report.ts`'s `registerPendingChannel`, not a local copy. |
|
|
219
229
|
| State | Files under `<agent-dir>/session-bus/<repo-key>/`; nothing in the session branch. Listening is session-only. `/resume` re-registers the same session id. |
|
|
220
230
|
| Lifecycle | The factory only registers. The runtime starts on `session_start`; heartbeat, watch ticker, and cleanup belong to it. Disabling `session-bus` through `PI_DISABLED_EXTENSIONS` registers nothing. |
|
|
@@ -233,7 +243,11 @@ listening watch is what raises the turn.
|
|
|
233
243
|
started through a link named differently is named after the directory it is
|
|
234
244
|
actually in. An absolute path argument that sits outside the repository is
|
|
235
245
|
refused before it is re-keyed, and a relative one whose target leaves the
|
|
236
|
-
repository is refused by the re-keying itself.
|
|
246
|
+
repository is refused by the re-keying itself. A path whose target is the
|
|
247
|
+
repository root is refused there too, and by that one rule: the spelling check
|
|
248
|
+
hands the root on rather than pre-empting it, so the sentence a caller reads
|
|
249
|
+
says what happened (`is the repository, not a path in it`) instead of claiming
|
|
250
|
+
a path inside the repository is outside it.
|
|
237
251
|
- **One file per item.** Sessions never append to a shared registry: each session
|
|
238
252
|
writes its own record, each message is its own file, and a claim is a temp file
|
|
239
253
|
hard-linked into place — an exclusive create no reader can see half-written.
|
|
@@ -252,6 +266,14 @@ listening watch is what raises the turn.
|
|
|
252
266
|
with one `readdir` every 2 s, the same trade `watch` makes for a file tail, so
|
|
253
267
|
it does not depend on a platform's event API or a filename in the event. Only
|
|
254
268
|
a listening session polls.
|
|
269
|
+
- **The poll reads the mail it has not announced, and its queue by what is on disk.** That one
|
|
270
|
+
read returns up to 50 messages from the front of the inbox whose ids the watch does not have,
|
|
271
|
+
plus the ids of everything still unread, so where a message's id sorts cannot hide it: a
|
|
272
|
+
backlog deeper than one page arrives a page at a time, and a peer whose clock lags writes an id
|
|
273
|
+
before every other and is announced just the same. The queue and the watch's memory are pruned
|
|
274
|
+
by what is still on disk rather than by a count of ids, so mail already queued keeps the place
|
|
275
|
+
it held and nothing is announced twice. `inbox` still consumes oldest first, and a limit never
|
|
276
|
+
starves.
|
|
255
277
|
- **Read the board, prune on the next call.** The open board polls a read-only
|
|
256
278
|
`snapshot()` and `peek()` that hide a session past the TTL, a claim held by one,
|
|
257
279
|
and consume nothing. The physical prune, and the claim release that goes with
|
|
@@ -274,14 +296,14 @@ listening watch is what raises the turn.
|
|
|
274
296
|
|---|---|
|
|
275
297
|
| `index.ts` | Disable guard, registration, the pending-report channel, and the session hooks. |
|
|
276
298
|
| `join-request.ts` | The `board`/`team` join request: one `bus.join` answer, and the label it settles becomes Pi's session name. |
|
|
277
|
-
| `runtime.ts` | Lifecycle, repo key, joining, heartbeat, and the session's own reads and writes. |
|
|
299
|
+
| `runtime.ts` | Lifecycle, repo key (`<agent-dir>/session-bus/<repo-key>/`), joining, heartbeat, and the session's own reads and writes. |
|
|
278
300
|
| `mail-watch.ts` | The armed inbox watch: the poll, the not-yet-reported queue, and its state. |
|
|
279
301
|
| `store.ts` | Atomic JSON files, inbox, peek, claims, pruning, and the one release loop. |
|
|
280
302
|
| `protocol.ts` | Pure labels, targets, paths, validation, conflict rules. |
|
|
281
303
|
| `overlap.ts` | The one "do these paths collide?" predicate: `claimsConflict`, which normalizes both spellings of a claim path first. |
|
|
282
304
|
| `resolve.ts` | `resolveClaimPaths` and `claimPathMessage`: a claim path made repository-relative, posix, and canonical through its real target, or the refusal naming why. |
|
|
283
305
|
| `types.ts` | The stored shapes — session record, message, claim — and the tool's `details`. |
|
|
284
|
-
| `
|
|
306
|
+
| `view/session-list.ts` | The `/sessions` board, its scopes, and the pane over one row, loaded on first use; the row grammar is in `rows.ts`. |
|
|
285
307
|
| `rows.ts` | The session/claim/message row builders and summaries shared by the board and the transcript. |
|
|
286
308
|
| `result.ts` | The `session` tool's expanded result block and its collapsed line. |
|
|
287
309
|
| `notice.ts` | The mail notice's model text and renderer details. |
|
|
@@ -8,27 +8,25 @@ can also start in the background and be inspected, collected, or cancelled with
|
|
|
8
8
|
|
|
9
9
|
## Capabilities
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
| Optional transcripts | `transcripts` | Per-run streams under `.pi/subagents/`, written by the parent and pruned by age and count. | Shipped |
|
|
31
|
-
| Inherited model | `subagent` | Children use the dispatching session's model and thinking level unless an agent overrides them. | Shipped |
|
|
11
|
+
| Capability | How | What you get |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Isolated context | `subagent` | Each task runs in its own process with its own transcript, and only its final reply returns to the caller. |
|
|
14
|
+
| Explicit tool allowlist | `--tools` for every child | An agent gets only its declared tools, not the parent's ambient extensions; `worker` cannot recurse into `subagent`. |
|
|
15
|
+
| Read-only delegation | `subagent` (`readOnly: true`) | Every spawned agent runs with a reader-only tool set, enforced at the process boundary. |
|
|
16
|
+
| Plan-mode guard | `subagent`, plan mode | Plan mode forces every call read-only and refuses `/agents` until the plan is approved. |
|
|
17
|
+
| Project-agent trust gate | `agentScope` with the trust gate | A `.pi/agents` file loads only when the project is trusted or confirmed, so a repository cannot inject a prompt by adding a file. |
|
|
18
|
+
| Three delegation modes | `subagent` (`agent` + `task`, `tasks[]`, `chain[]`) | One agent, an independent batch, or an ordered chain whose steps receive `{previous}`. |
|
|
19
|
+
| Parallel and chain bounds | `tasks[]`, `chain[]` | Parallel accepts up to 8 tasks with 4 running at once; a chain accepts up to 8 steps and stops at the first failure. |
|
|
20
|
+
| Per-step working directory | `cwd` | A single run, each parallel task, or each chain step can name its working directory; a relative path resolves against the active root. |
|
|
21
|
+
| Built-in agents | `subagent` (`agent`) | Recon, planning, review, implementation, research, testing, debugging, and docs work without writing an agent file. |
|
|
22
|
+
| External agents | `subagent` (`agentScope`) | Markdown agents from the user directory and the nearest `.pi/agents`, opt-in per scope and never replacing a built-in silently. |
|
|
23
|
+
| Background runs | `subagent` (`background: true`), `/agents` | A single run continues after the call returns and is inspected, collected, or cancelled from the board. |
|
|
24
|
+
| Background concurrency cap | `backgroundMax` | At most four background children run at once; finished runs release their slot. |
|
|
25
|
+
| Session-owned background run | `background: true` | `/tree` does not move the run and a restart cannot resume it; an unfinished start shows as `interrupted`. |
|
|
26
|
+
| Live progress | `subagent` | Tool calls and progress update in the transcript while the child runs, with a `↳ N` footer chip and a finish notice. |
|
|
27
|
+
| Bounded runs | `timeoutMs` | A child that hangs is killed at the deadline and reported as a failed run rather than rejecting the whole call. |
|
|
28
|
+
| Optional transcripts | `transcripts` | Per-run streams under `.pi/subagents/`, written by the parent and pruned by age and count. |
|
|
29
|
+
| Inherited model | `subagent` | Children use the dispatching session's model and thinking level unless an agent overrides them. |
|
|
32
30
|
|
|
33
31
|
## Quickstart
|
|
34
32
|
|
|
@@ -229,7 +227,7 @@ picks up ambient extensions and `worker` cannot recurse into `subagent`.
|
|
|
229
227
|
for another agent (typically `worker`) to execute, and it works headlessly. It
|
|
230
228
|
returns the plan as text and writes nothing. For **interactive** planning where
|
|
231
229
|
the user reviews and approves a saved plan, use the plan-mode extension instead
|
|
232
|
-
(`
|
|
230
|
+
(`plan` actions `enter` / `write` / `exit`), which operates on the
|
|
233
231
|
main session and persists to the agent directory. Plan mode keeps `subagent` available
|
|
234
232
|
for exploration but forces it read-only, so delegated recon is safe mid-plan.
|
|
235
233
|
|
|
@@ -454,15 +452,15 @@ step's `Output`.
|
|
|
454
452
|
|---|---|
|
|
455
453
|
| `index.ts` | Registers the `subagent` tool and assembles the mode context. |
|
|
456
454
|
| `orchestrate.ts` | The `single`/`parallel`/`chain` mode runners. |
|
|
457
|
-
| `agents.ts` | Built-in agent definitions, external markdown discovery, and pool resolution. |
|
|
455
|
+
| `agents.ts` | Built-in agent definitions, external markdown discovery (`<agent-dir>/agents`, and the nearest `<repo>/.pi/agents`), and pool resolution. |
|
|
458
456
|
| `schema.ts` | TypeBox params, limits, and `resolveMode`. |
|
|
459
457
|
| `invocation.ts` | `pi` subprocess invocation and argument construction. |
|
|
460
458
|
| `stream.ts` | JSON-event parsing, guarded usage accounting, output helpers (pure). |
|
|
461
|
-
| `transcripts.ts` | Per-run transcript buffer, run ids, the store directory, and pruning. |
|
|
459
|
+
| `transcripts.ts` | Per-run transcript buffer, run ids, the store directory (`<agent-dir>/subagents/`), and pruning. |
|
|
462
460
|
| `config.ts` | The `subagent` section of `bytefly.json`: deadline, transcripts, retention. |
|
|
463
461
|
| `run.ts` | Runs one subagent process and captures its result (including the background abort-as-result path). |
|
|
464
462
|
| `background.ts` | Background run entries, replay, capacity, cancel, collect, and shutdown. |
|
|
465
|
-
| `
|
|
463
|
+
| `view/agents-board.ts` | The `/agents` board: a `DockListScreen` with a live output pane and width-aware facts, loaded on first use. |
|
|
466
464
|
| `commands.ts` | The `/agents` command. |
|
|
467
465
|
| `ui.ts` | The running-count footer chip and finish notices. |
|
|
468
466
|
| `accounting.ts` | The `subagent-finished` event payload built from a result (pure). |
|