@gavin-hu/my-pi-agent 0.18.0 → 0.20.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 +66 -52
- package/README.zh-CN.md +56 -46
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +507 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +774 -574
- package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
- package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
- package/docs/extensions/file-browse.md +561 -0
- package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
- package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
- package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
- package/{extensions/job/README.md → docs/extensions/job.md} +62 -52
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +440 -322
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
- package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
- package/docs/extensions/session-bus.md +298 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +254 -0
- package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
- package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +260 -163
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +137 -79
- package/extensions/board/commands.ts +105 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +170 -0
- package/extensions/board/index.ts +40 -0
- package/extensions/board/input.ts +54 -0
- package/extensions/board/join.ts +42 -0
- package/extensions/board/page.ts +388 -0
- package/extensions/board/result.ts +108 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +90 -0
- package/extensions/board/seat.ts +28 -0
- package/extensions/board/snapshot.ts +42 -0
- package/extensions/board/tools.ts +444 -0
- package/extensions/board/transport/router.ts +91 -0
- package/extensions/board/transport/server.ts +138 -0
- package/extensions/board/types.ts +121 -0
- package/extensions/board/view/client.ts +51 -0
- package/extensions/board/view/dialog.ts +43 -0
- package/extensions/board/view/keys.ts +125 -0
- package/extensions/board/view/live.ts +231 -0
- package/extensions/board/view/names.ts +91 -0
- package/extensions/board/view/page.ts +118 -0
- package/extensions/board/view/render.ts +519 -0
- package/extensions/board/view/styles.ts +228 -0
- package/extensions/board/view/team.ts +327 -0
- package/extensions/bytefly/footer/budget.ts +6 -5
- package/extensions/bytefly/footer/collect/entries.ts +12 -4
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
- package/extensions/bytefly/footer/config.ts +19 -8
- package/extensions/bytefly/footer/price/price.ts +30 -8
- package/extensions/bytefly/footer/register.ts +5 -5
- package/extensions/bytefly/footer/render/format.ts +18 -0
- package/extensions/bytefly/footer/render/lines.ts +23 -7
- package/extensions/bytefly/footer/render/segments.ts +117 -31
- package/extensions/bytefly/footer/types.ts +29 -5
- package/extensions/bytefly/footer/unpriced.ts +42 -17
- package/extensions/bytefly/panel/commands.ts +67 -7
- package/extensions/bytefly/panel/filter.ts +68 -0
- package/extensions/bytefly/panel/format.ts +16 -15
- package/extensions/bytefly/panel/join.ts +56 -13
- package/extensions/bytefly/panel/prices.ts +43 -0
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +75 -43
- package/extensions/bytefly/panel/tui/entries.ts +26 -8
- package/extensions/bytefly/panel/tui/frame.ts +130 -93
- package/extensions/bytefly/panel/tui/keys.ts +352 -0
- package/extensions/bytefly/panel/tui/list-view.ts +25 -46
- package/extensions/bytefly/panel/tui/panes.ts +76 -0
- package/extensions/bytefly/panel/tui/section-view.ts +126 -0
- package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
- package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
- package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
- package/extensions/bytefly/panel/tui.ts +306 -259
- package/extensions/bytefly/picker/discovery.ts +0 -2
- package/extensions/bytefly/picker/types.ts +0 -2
- package/extensions/bytefly/update/check.ts +39 -0
- package/extensions/bytefly/update/format.ts +15 -0
- package/extensions/bytefly/update/register.ts +73 -34
- package/extensions/bytefly/update/state.ts +6 -3
- package/extensions/check/commands.ts +2 -1
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/files/classify.ts +136 -0
- package/extensions/file-browse/files/files.ts +12 -0
- package/extensions/file-browse/files/index.ts +90 -0
- package/extensions/file-browse/files/list.ts +261 -0
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/files/tree.ts +101 -0
- package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
- package/extensions/file-browse/index.ts +4 -4
- package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
- package/extensions/file-browse/{router.ts → transport/router.ts} +21 -17
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
- package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
- package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
- package/extensions/file-browse/view/emit.ts +51 -0
- package/extensions/file-browse/{html.ts → view/html.ts} +19 -18
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +18 -20
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +19 -14
- package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
- package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
- package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
- package/extensions/file-read/schema.ts +2 -1
- package/extensions/job/runtime.ts +4 -4
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +21 -16
- package/extensions/plan/feedback.ts +185 -52
- package/extensions/plan/format.ts +10 -3
- package/extensions/plan/index.ts +23 -5
- package/extensions/plan/plan-files.ts +88 -0
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +27 -34
- package/extensions/plan/policy.ts +30 -17
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +52 -11
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/todos.ts +51 -3
- package/extensions/plan/tools/exit.ts +20 -95
- package/extensions/plan/tools/todo-seed.ts +4 -2
- package/extensions/plan/tools/write.ts +12 -1
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +15 -8
- package/extensions/plan/web-blocks.ts +89 -0
- package/extensions/plan/web-chrome.ts +3 -5
- package/extensions/plan/web-client.ts +179 -330
- package/extensions/plan/web-collab.ts +3 -3
- package/extensions/plan/web-comments.ts +18 -8
- package/extensions/plan/web-composer.ts +80 -0
- package/extensions/plan/web-draft.ts +40 -0
- package/extensions/plan/web-keys.ts +134 -0
- package/extensions/plan/web-outline.ts +192 -0
- package/extensions/plan/web-page.ts +4 -8
- package/extensions/plan/web-plan.ts +150 -0
- package/extensions/plan/web-router.ts +3 -58
- package/extensions/plan/web-settle.ts +194 -0
- package/extensions/plan/web-share-session.ts +24 -30
- package/extensions/plan/web-streams.ts +14 -14
- package/extensions/plan/web-tokens.ts +2 -2
- package/extensions/plan/web.ts +66 -120
- package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
- package/extensions/rewind/{review.ts → commands/review.ts} +15 -14
- package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
- package/extensions/rewind/{config.ts → config/config.ts} +1 -1
- package/extensions/rewind/index.ts +3 -3
- package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
- package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
- package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
- package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
- package/extensions/rewind/{format.ts → text/format.ts} +3 -3
- package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
- package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
- package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
- package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
- package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
- package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
- package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
- package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
- package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
- package/extensions/rewind/{status.ts → view/status.ts} +4 -4
- package/extensions/session-bus/commands.ts +17 -69
- package/extensions/session-bus/format.ts +22 -45
- package/extensions/session-bus/index.ts +13 -7
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +32 -4
- package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
- package/extensions/session-bus/protocol.ts +19 -49
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +16 -51
- package/extensions/session-bus/rows.ts +11 -5
- package/extensions/session-bus/runtime.ts +220 -267
- package/extensions/session-bus/store.ts +55 -7
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +5 -5
- package/extensions/session-bus/types.ts +23 -14
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/commands.ts +2 -1
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +145 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +119 -0
- package/extensions/team/index.ts +34 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +267 -0
- package/extensions/team/types.ts +65 -0
- package/extensions/todo/index.ts +4 -1
- package/extensions/todo/nudge.ts +8 -2
- package/extensions/web-access/fetch/page.ts +4 -1
- package/extensions/web-access/fetch/structured.ts +447 -0
- package/extensions/web-access/fetch/tool.ts +2 -1
- package/extensions/web-access/search/tool.ts +2 -1
- package/extensions/wechat/bridge.ts +106 -251
- package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
- package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
- package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
- package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
- package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
- package/extensions/wechat/dialog/channels.ts +136 -0
- package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
- package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
- package/extensions/wechat/index.ts +10 -10
- package/extensions/wechat/intake.ts +245 -0
- package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
- package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
- package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
- package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
- package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
- package/extensions/wechat/{state.ts → state/state.ts} +14 -2
- package/extensions/wechat/{format.ts → text/format.ts} +3 -3
- package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
- package/extensions/wechat/tools.ts +29 -12
- package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
- package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
- package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
- package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
- package/extensions/wechat/transport/outbound.ts +142 -0
- package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
- package/extensions/wechat/types.ts +10 -1
- package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
- package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
- package/extensions/worktree/commands.ts +24 -17
- package/extensions/worktree/exit.ts +12 -6
- package/extensions/worktree/git.ts +22 -14
- package/extensions/worktree/guard.ts +18 -290
- package/extensions/worktree/handoff.ts +10 -2
- package/extensions/worktree/index.ts +2 -1
- package/extensions/worktree/lifecycle.ts +2 -0
- package/extensions/worktree/registry.ts +20 -11
- package/extensions/worktree/root-tools.ts +13 -3
- package/extensions/worktree/runtime.ts +8 -2
- package/extensions/worktree/screen.ts +15 -8
- package/extensions/worktree/shell.ts +303 -0
- package/extensions/worktree/state.ts +1 -1
- package/extensions/worktree/status.ts +42 -9
- package/extensions/worktree/tools.ts +19 -13
- package/lib/README.md +11 -8
- package/lib/board/card.ts +479 -0
- package/lib/board/links.ts +141 -0
- package/lib/board/page-pointer.ts +310 -0
- package/lib/board/round-text.ts +81 -0
- package/lib/board/round.ts +94 -0
- package/lib/board/rules.ts +437 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +497 -0
- package/lib/board/view.ts +234 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +35 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +237 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +135 -0
- package/lib/http/token.ts +38 -0
- package/lib/plan-entry.ts +11 -0
- package/lib/policy.ts +25 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +201 -0
- package/lib/session-tool.ts +184 -0
- package/lib/spawn.ts +37 -3
- package/lib/stray-fields.ts +45 -0
- package/lib/tool-names.ts +38 -2
- package/lib/tui/glyphs.ts +3 -2
- package/lib/web/README.md +57 -4
- package/lib/web/client-runtime.ts +123 -0
- package/lib/web/components.ts +5 -0
- package/lib/web/index.ts +4 -2
- package/lib/web/markup.ts +88 -1
- package/lib/web/names.ts +42 -0
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +26 -14
- package/skills/next/SKILL.md +4 -3
- package/skills/project-docs/SKILL.md +133 -0
- package/skills/project-docs/references/audit.md +131 -0
- package/skills/project-docs/references/changelog.md +105 -0
- package/skills/project-docs/references/discovery.md +127 -0
- package/skills/project-docs/references/readme.md +80 -0
- package/skills/project-docs/references/release.md +84 -0
- package/skills/project-docs/references/roadmap.md +93 -0
- package/skills/project-docs/scripts/state.ts +316 -0
- package/skills/refactor/SKILL.md +100 -125
- package/skills/refactor/references/handoff.md +35 -0
- package/skills/refactor/references/signals.md +54 -0
- package/skills/refactor/scripts/inventory.ts +587 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
- package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/files.ts +0 -564
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/job/process.ts +0 -35
- package/extensions/plan/split.ts +0 -87
- package/extensions/session-bus/README.md +0 -247
- package/extensions/session-bus/team.ts +0 -102
- package/extensions/wechat/outbound.ts +0 -100
- package/lib/plans/sections.ts +0 -172
- package/lib/plans/setting.ts +0 -52
- package/lib/plans/split.ts +0 -215
- package/lib/plans/store.ts +0 -19
- package/lib/plans/team.ts +0 -151
- package/skills/changelog/SKILL.md +0 -148
- package/skills/parallel-dev/SKILL.md +0 -150
- /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
- /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
- /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
- /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
- /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
- /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
- /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
- /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
- /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
- /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
- /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
|
@@ -6,49 +6,52 @@ exploration or parallel work does not fill the main conversation. A single run
|
|
|
6
6
|
can also start in the background and be inspected, collected, or cancelled with
|
|
7
7
|
`/agents`.
|
|
8
8
|
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
| Capability | How | What you get | Status |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| Isolated context | `subagent` | Each task runs in its own process with its own transcript, and only its final reply returns to the caller. | Shipped |
|
|
16
|
+
| 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`. | Shipped |
|
|
17
|
+
| Read-only delegation | `subagent` (`readOnly: true`) | Every spawned agent runs with a reader-only tool set, enforced at the process boundary. | Shipped |
|
|
18
|
+
| Plan-mode guard | `subagent`, plan mode | Plan mode forces every call read-only and refuses `/agents` until the plan is approved. | Shipped |
|
|
19
|
+
| 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. | Shipped |
|
|
20
|
+
| Three delegation modes | `subagent` (`agent` + `task`, `tasks[]`, `chain[]`) | One agent, an independent batch, or an ordered chain whose steps receive `{previous}`. | Shipped |
|
|
21
|
+
| 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. | Shipped |
|
|
22
|
+
| 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. | Shipped |
|
|
23
|
+
| Built-in agents | `subagent` (`agent`) | Recon, planning, review, implementation, research, testing, debugging, and docs work without writing an agent file. | Shipped |
|
|
24
|
+
| 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. | Shipped |
|
|
25
|
+
| Background runs | `subagent` (`background: true`), `/agents` | A single run continues after the call returns and is inspected, collected, or cancelled from the board. | Shipped |
|
|
26
|
+
| Background concurrency cap | `backgroundMax` | At most four background children run at once; finished runs release their slot. | Shipped |
|
|
27
|
+
| Session-owned background run | `background: true` | `/tree` does not move the run and a restart cannot resume it; an unfinished start shows as `interrupted`. | Shipped |
|
|
28
|
+
| Live progress | `subagent` | Tool calls and progress update in the transcript while the child runs, with a `↳ N` footer chip and a finish notice. | Shipped |
|
|
29
|
+
| Bounded runs | `timeoutMs` | A child that hangs is killed at the deadline and reported as a failed run rather than rejecting the whole call. | Shipped |
|
|
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 |
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
9
35
|
```bash
|
|
10
36
|
pi --extension ./extensions/subagent # load just this extension
|
|
11
37
|
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
12
|
-
pi install ./ # install the package
|
|
38
|
+
pi install ./ # install the package for a user
|
|
13
39
|
```
|
|
14
40
|
|
|
15
|
-
|
|
41
|
+
```text
|
|
42
|
+
subagent({ agent: "explorer", task: "Find every place the HTTP client is retried" })
|
|
43
|
+
/agents # inspect, collect, or cancel background runs
|
|
44
|
+
```
|
|
16
45
|
|
|
17
|
-
|
|
46
|
+
## Tool
|
|
18
47
|
|
|
19
48
|
<img src="../../assets/tools/subagent.png" alt="The subagent tool's call and result: the call line and an indented body with the two tasks, each agent's section rule, its summary, and the usage total" width="100%">
|
|
20
49
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- **Isolated context.** Each subagent is a separate `pi` process with its own
|
|
28
|
-
transcript; only its final reply is returned to the caller.
|
|
29
|
-
- **Built-in agents.** `explorer`, `planner`, `reviewer`, `worker`,
|
|
30
|
-
`researcher`, `tester`, `debugger`, and `documenter` ship with the extension.
|
|
31
|
-
- **External agents.** Optionally load markdown agents from `~/.pi/agents`
|
|
32
|
-
(user) and the nearest `.pi/agents` (project). They are opt-in per scope and
|
|
33
|
-
never replace the built-ins silently.
|
|
34
|
-
- **Three modes, plus background.** One agent, a parallel batch, or a sequential
|
|
35
|
-
chain; a single run can also continue after the tool returns and be collected
|
|
36
|
-
from `/agents`. A running helper adds a `↳ N` footer chip, and a finish notice
|
|
37
|
-
points back at `/agents`.
|
|
38
|
-
- **Streaming.** Tool calls and progress update live in the transcript while the
|
|
39
|
-
subagent runs; the `app.tools.expand` key (`ctrl+o` by default) expands the
|
|
40
|
-
full task, tool calls, Markdown output, and usage.
|
|
41
|
-
- **Bounded runs.** A per-run deadline kills a child that hangs, and reports it
|
|
42
|
-
as a failed run instead of rejecting the whole call.
|
|
43
|
-
- **Optional transcripts.** Opt-in per-run stream under `.pi/subagents/`,
|
|
44
|
-
written by the parent and pruned by age and count.
|
|
45
|
-
- **Inherited model.** Subagents use the dispatching session's model and thinking
|
|
46
|
-
level unless an agent overrides them (none do).
|
|
47
|
-
|
|
48
|
-
## Tool
|
|
49
|
-
|
|
50
|
-
`subagent` takes exactly one mode. Every call also accepts `agentScope` and
|
|
51
|
-
`readOnly`.
|
|
50
|
+
`subagent` — `exposure: "direct"` (the default), read-write, open-world,
|
|
51
|
+
destructive, and parallel across calls: only a chain orders itself. A call takes
|
|
52
|
+
exactly one mode, plus `agentScope`, `readOnly`, and `cwd`. A call with no mode,
|
|
53
|
+
or with two, is refused before any process starts, and an unknown agent name is
|
|
54
|
+
refused with the resolved roster.
|
|
52
55
|
|
|
53
56
|
| Field | Value |
|
|
54
57
|
|---|---|
|
|
@@ -59,78 +62,38 @@ How the `subagent` call and its result look in the transcript — the call line
|
|
|
59
62
|
| `cwd` | Optional working directory for the spawned process (single mode; per-step for the others). A relative path resolves against the active root. |
|
|
60
63
|
| `agentScope` | `user` (default), `project`, or `both` — which external agent directories to load. |
|
|
61
64
|
| `readOnly` | `true` forces every spawned agent to a reader-only tool set; plan mode sets it automatically. |
|
|
62
|
-
| `exposure` | `direct`, active by default. |
|
|
63
|
-
| `annotations` | `readOnlyHint: false`, `openWorldHint: true`. |
|
|
64
|
-
|
|
65
|
-
## Behaviour by mode
|
|
66
|
-
|
|
67
|
-
### Single
|
|
68
|
-
|
|
69
|
-
```json
|
|
70
|
-
{ "agent": "explorer", "task": "Find every place the HTTP client is retried", "cwd": "." }
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Background
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{ "agent": "explorer", "task": "Map the auth module", "background": true }
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
The tool returns immediately with a run id such as `a2`. `/agents` lists every
|
|
80
|
-
run with its status, elapsed time, and usage; `Enter` opens the run, `c` collects
|
|
81
|
-
a finished result into the conversation, and `x` cancels a running one. While a
|
|
82
|
-
run is active, its detail pane follows a bounded preview of the latest output,
|
|
83
|
-
and a `↳ N` chip in the footer counts running helpers. A finish notice points
|
|
84
|
-
back at `/agents`; the finalized result replaces the preview when the run
|
|
85
|
-
finishes. A background run is session-owned and is bounded by the same timeout
|
|
86
|
-
and transcript settings as a foreground run.
|
|
87
|
-
|
|
88
|
-
### Parallel
|
|
89
|
-
|
|
90
|
-
Runs up to 8 tasks, at most 4 at once. Independent tasks only.
|
|
91
|
-
|
|
92
|
-
```json
|
|
93
|
-
{
|
|
94
|
-
"tasks": [
|
|
95
|
-
{ "agent": "explorer", "task": "Map the auth module" },
|
|
96
|
-
{ "agent": "explorer", "task": "Map the billing module" }
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
65
|
|
|
101
|
-
|
|
102
|
-
output remains in the tool details.
|
|
103
|
-
|
|
104
|
-
### Chain
|
|
105
|
-
|
|
106
|
-
Runs steps in order. `{previous}` in a step's `task` is replaced by the prior
|
|
107
|
-
step's final output. The chain stops at the first failing step, and a chain has
|
|
108
|
-
at most 8 steps (like the parallel task cap).
|
|
109
|
-
|
|
110
|
-
```json
|
|
111
|
-
{
|
|
112
|
-
"chain": [
|
|
113
|
-
{ "agent": "explorer", "task": "Find the config loader" },
|
|
114
|
-
{ "agent": "planner", "task": "Using this context, plan a refactor:\n\n{previous}" },
|
|
115
|
-
{ "agent": "worker", "task": "Implement the plan:\n\n{previous}" }
|
|
116
|
-
]
|
|
117
|
-
}
|
|
118
|
-
```
|
|
66
|
+
## Commands
|
|
119
67
|
|
|
120
|
-
|
|
68
|
+
<img src="../../assets/screens/agents.png" alt="The /agents screen: running and finished background helpers with status, task, elapsed time, and usage" width="100%">
|
|
121
69
|
|
|
122
70
|
| Command | What it does |
|
|
123
71
|
|---|---|
|
|
124
72
|
| `/agents` | Open the background-run board: inspect, collect, or cancel. |
|
|
125
73
|
|
|
126
74
|
The board is TUI-only for actions; RPC, print, and JSON print the run list.
|
|
127
|
-
|
|
75
|
+
`Enter` opens the run, `c` collects a finished result into the conversation, and
|
|
76
|
+
`x` cancels a running one. Collecting sends the result as a user message, which
|
|
77
|
+
starts the next turn. Plan mode refuses the command until the plan is approved.
|
|
78
|
+
|
|
79
|
+
## Behaviour by mode
|
|
80
|
+
|
|
81
|
+
The tool and its background runs work in every mode; only the run board is a
|
|
82
|
+
terminal surface.
|
|
83
|
+
|
|
84
|
+
- In `tui` mode `/agents` opens the board, the footer carries a `↳ N` chip while
|
|
85
|
+
helpers run, and a finish notice points back at `/agents`.
|
|
86
|
+
- In RPC, print, and JSON `/agents` reports the same runs as a notification — a
|
|
87
|
+
count, then one line per run with agent, task, status, elapsed time, and usage
|
|
88
|
+
— and there is no board and no action.
|
|
89
|
+
- In plan mode `subagent` stays active but every call is forced read-only, and
|
|
90
|
+
`/agents` refuses until the plan is approved.
|
|
128
91
|
|
|
129
92
|
## Configuration
|
|
130
93
|
|
|
131
94
|
Read from the shared `bytefly.json` section `subagent` (global then project),
|
|
132
95
|
once per session. Keys and bounds live in the
|
|
133
|
-
[configuration page](
|
|
96
|
+
[configuration page](../configuration.md#subagent--delegated-runs);
|
|
134
97
|
the behaviour is here.
|
|
135
98
|
|
|
136
99
|
- **`timeoutMs`** (default ten minutes, `0` disables) bounds one child run. The
|
|
@@ -186,18 +149,75 @@ confirmation before running them in an interactive session; without a UI it
|
|
|
186
149
|
refuses instead of running them silently. The gate is driven only by the
|
|
187
150
|
project's trusted state — the model cannot turn it off.
|
|
188
151
|
|
|
152
|
+
## Delegation modes
|
|
153
|
+
|
|
154
|
+
One mode per call, with a single `background` run available on the single-agent
|
|
155
|
+
shape.
|
|
156
|
+
|
|
157
|
+
### Single
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{ "agent": "explorer", "task": "Find every place the HTTP client is retried", "cwd": "." }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Background
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{ "agent": "explorer", "task": "Map the auth module", "background": true }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The tool returns immediately with a run id such as `a2`. `/agents` lists every
|
|
170
|
+
run with its status, elapsed time, and usage. While a run is active, its detail
|
|
171
|
+
pane follows a bounded preview of the latest output, and a `↳ N` chip in the
|
|
172
|
+
footer counts running helpers. A finish notice points back at `/agents`; the
|
|
173
|
+
finalized result replaces the preview when the run finishes. A background run is
|
|
174
|
+
session-owned and is bounded by the same timeout and transcript settings as a
|
|
175
|
+
foreground run.
|
|
176
|
+
|
|
177
|
+
### Parallel
|
|
178
|
+
|
|
179
|
+
Runs up to 8 tasks, at most 4 at once. Independent tasks only.
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"tasks": [
|
|
184
|
+
{ "agent": "explorer", "task": "Map the auth module" },
|
|
185
|
+
{ "agent": "explorer", "task": "Map the billing module" }
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Each task's output is capped at 50 KiB in the model-facing reply; the complete
|
|
191
|
+
output remains in the tool details.
|
|
192
|
+
|
|
193
|
+
### Chain
|
|
194
|
+
|
|
195
|
+
Runs steps in order. `{previous}` in a step's `task` is replaced by the prior
|
|
196
|
+
step's final output. The chain stops at the first failing step, and a chain has
|
|
197
|
+
at most 8 steps (like the parallel task cap).
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"chain": [
|
|
202
|
+
{ "agent": "explorer", "task": "Find the config loader" },
|
|
203
|
+
{ "agent": "planner", "task": "Using this context, plan a refactor:\n\n{previous}" },
|
|
204
|
+
{ "agent": "worker", "task": "Implement the plan:\n\n{previous}" }
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
189
209
|
## Built-in agents
|
|
190
210
|
|
|
191
211
|
| Agent | Tools | Purpose |
|
|
192
212
|
|---|---|---|
|
|
193
|
-
| `explorer` | `read, grep, find, ls` | Fast codebase recon; returns files + line ranges, key code, architecture, and where to start. |
|
|
194
|
-
| `planner` | `read, grep, find, ls` | Delegated planning in an isolated context (headless/chain); never edits. |
|
|
195
|
-
| `reviewer` | `read, grep, find, ls, bash` | Reviews for bugs, security, and maintainability; bash limited to read-only `git diff/log/show`. |
|
|
196
|
-
| `worker` | `read, write, edit, bash, grep, find, ls` | General-purpose executor; reports what changed. |
|
|
197
|
-
| `researcher` | `read, grep, find, ls, web_search, web_fetch` | Investigates external questions and returns sourced findings with confidence. |
|
|
198
|
-
| `tester` | `read, write, edit, grep, find, ls, bash` | Adds or updates tests and runs them; never weakens assertions to pass. |
|
|
199
|
-
| `debugger` | `read, grep, find, ls, bash` | Root-causes a failure from evidence; read-only plus running tests/builds. |
|
|
200
|
-
| `documenter` | `read, write, edit, grep, find, ls, bash` | Updates READMEs, docs, and changelogs to match the code. |
|
|
213
|
+
| `explorer` | `read, grep, find, ls, read_doc` | Fast codebase recon; returns files + line ranges, key code, architecture, and where to start. |
|
|
214
|
+
| `planner` | `read, grep, find, ls, read_doc` | Delegated planning in an isolated context (headless/chain); never edits. |
|
|
215
|
+
| `reviewer` | `read, grep, find, ls, read_doc, bash` | Reviews for bugs, security, and maintainability; bash limited to read-only `git diff/log/show`. |
|
|
216
|
+
| `worker` | `read, write, edit, bash, grep, find, ls, read_doc` | General-purpose executor; reports what changed. |
|
|
217
|
+
| `researcher` | `read, grep, find, ls, read_doc, web_search, web_fetch` | Investigates external questions and returns sourced findings with confidence. |
|
|
218
|
+
| `tester` | `read, write, edit, grep, find, ls, read_doc, bash` | Adds or updates tests and runs them; never weakens assertions to pass. |
|
|
219
|
+
| `debugger` | `read, grep, find, ls, read_doc, bash` | Root-causes a failure from evidence; read-only plus running tests/builds. |
|
|
220
|
+
| `documenter` | `read, write, edit, grep, find, ls, read_doc, bash` | Updates READMEs, docs, and changelogs to match the code. |
|
|
201
221
|
|
|
202
222
|
Each agent's system prompt is appended to the subprocess, and its tool list is
|
|
203
223
|
passed as `--tools`. Every built-in has an explicit allowlist, so an agent never
|
|
@@ -259,9 +279,9 @@ is the safe way to delegate exploration, and plan mode sets it automatically.
|
|
|
259
279
|
|
|
260
280
|
The child is restricted at the process boundary, not by its prompt: the tool
|
|
261
281
|
list passed as `--tools` is the intersection of the agent's tools with
|
|
262
|
-
`read`, `grep`, `find`, `ls`, `web_search`, and `web_fetch`. An agent
|
|
263
|
-
reader tools (or no `tools` declaration) gets the full reader set
|
|
264
|
-
`worker` cannot write under `readOnly`, and an external agent file that
|
|
282
|
+
`read`, `grep`, `find`, `ls`, `read_doc`, `web_search`, and `web_fetch`. An agent
|
|
283
|
+
with no reader tools (or no `tools` declaration) gets the full reader set
|
|
284
|
+
instead. So `worker` cannot write under `readOnly`, and an external agent file that
|
|
265
285
|
customizes a built-in cannot widen it. A short read-only note is appended to the
|
|
266
286
|
child's system prompt for behavior; the tool list is the enforcement.
|
|
267
287
|
|
|
@@ -314,6 +334,19 @@ with `{previous}` left in place, rather than the substituted prior output that
|
|
|
314
334
|
was actually sent to the child; that output is already shown as the previous
|
|
315
335
|
step's `Output`.
|
|
316
336
|
|
|
337
|
+
## Limitations
|
|
338
|
+
|
|
339
|
+
- Background runs are single-mode only. Parallel and chain calls wait as before.
|
|
340
|
+
- A background run is session-owned: `/tree` does not move it, and a process
|
|
341
|
+
restart cannot resume it. A start entry without a finish is shown as
|
|
342
|
+
`interrupted`.
|
|
343
|
+
- The collected output is capped at 50 KiB; the optional transcript keeps the
|
|
344
|
+
full stream. The `/agents` detail keeps only a bounded live preview, and a
|
|
345
|
+
finished run keeps its bounded output in the session record rather than the
|
|
346
|
+
child's full result.
|
|
347
|
+
- A finish notice appears in the TUI, but there is no OS notification; the run
|
|
348
|
+
remains listed in `/agents`.
|
|
349
|
+
|
|
317
350
|
## Non-goals
|
|
318
351
|
|
|
319
352
|
- **No ambient external agents.** User agents load only when an explicit
|
|
@@ -340,29 +373,19 @@ step's `Output`.
|
|
|
340
373
|
per process: a resumed session starts from zero, and a run that never produced
|
|
341
374
|
an assistant message publishes nothing.
|
|
342
375
|
|
|
343
|
-
## Limitations
|
|
344
|
-
|
|
345
|
-
- Background runs are single-mode only. Parallel and chain calls wait as before.
|
|
346
|
-
- A background run is session-owned: `/tree` does not move it, and a process
|
|
347
|
-
restart cannot resume it. A start entry without a finish is shown as
|
|
348
|
-
`interrupted`.
|
|
349
|
-
- The collected output is capped at 50 KiB; the optional transcript keeps the
|
|
350
|
-
full stream. The `/agents` detail keeps only a bounded live preview, and a
|
|
351
|
-
finished run keeps its bounded output in the session record rather than the
|
|
352
|
-
child's full result.
|
|
353
|
-
- A finish notice appears in the TUI, but there is no OS notification; the run
|
|
354
|
-
remains listed in `/agents`.
|
|
355
|
-
|
|
356
376
|
## Pi integration
|
|
357
377
|
|
|
358
378
|
| Aspect | Detail |
|
|
359
379
|
|---|---|
|
|
360
380
|
| Tool | `subagent` |
|
|
361
381
|
| Exposure | `direct`, active by default |
|
|
362
|
-
|
|
|
382
|
+
| Execution mode | none: sibling calls are independent, each spawning its own child, so they stay parallel |
|
|
383
|
+
| Annotations | `readOnlyHint: false`, `destructiveHint: true`, `openWorldHint: true` |
|
|
363
384
|
| Output schema | none; the full result lives in tool-result `details` |
|
|
364
385
|
| Execution | spawns `pi --mode json` subprocesses; external agents discovered from `<agent-dir>/agents` and `.pi/agents` |
|
|
386
|
+
| Command | `/agents` |
|
|
365
387
|
| State storage | tool-result `details`; optional per-run transcripts under `.pi/subagents/`; background start/finish entries (`subagent-background`) on the session branch |
|
|
388
|
+
| Event | `my-pi-agent/subagent-finished` (`SUBAGENT_FINISHED_CHANNEL` in `lib/events.ts`) once per finished child |
|
|
366
389
|
| Lifecycle | `session_start` loads the config, prunes expired transcripts, and attaches the footer chip; `session_shutdown` aborts every live background child and clears the chip |
|
|
367
390
|
|
|
368
391
|
## Design notes
|
|
@@ -442,6 +465,30 @@ step's `Output`.
|
|
|
442
465
|
| `background-tui.ts` | The `/agents` board: a `DockListScreen` with a live output pane and width-aware facts. |
|
|
443
466
|
| `commands.ts` | The `/agents` command. |
|
|
444
467
|
| `ui.ts` | The running-count footer chip and finish notices. |
|
|
468
|
+
| `accounting.ts` | The `subagent-finished` event payload built from a result (pure). |
|
|
445
469
|
| `format.ts` | Token/usage/total line and tool-call formatting (pure, money-free). |
|
|
446
470
|
| `render.ts` | `renderCall` / `renderResult` transcript rendering. |
|
|
447
471
|
| `types.ts` | Shared result and process types. |
|
|
472
|
+
|
|
473
|
+
## Testing
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
bun test extensions/subagent
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Four co-located suites, declared in `test/suite-manifest.ts`:
|
|
480
|
+
|
|
481
|
+
- `agents.test.ts` — the built-in registry, markdown discovery and pool
|
|
482
|
+
resolution, config normalisation, the parameter schema, `resolveMode`,
|
|
483
|
+
subprocess argument construction, invocation, and the finished event.
|
|
484
|
+
- `run.test.ts` — the stream helpers, the concurrency limiter, `runSingleAgent`
|
|
485
|
+
against a scripted child, transcripts and pruning, the background runtime, and
|
|
486
|
+
the board component.
|
|
487
|
+
- `render.test.ts` — the call and result blocks in every mode, collapsed and
|
|
488
|
+
expanded, the failure text, and component reuse.
|
|
489
|
+
- `extension.test.ts` — registration, run settings, mode validation, all three
|
|
490
|
+
modes, the project-agent gate, accounting, `/agents`, the footer chip, and the
|
|
491
|
+
formatters.
|
|
492
|
+
|
|
493
|
+
`runSingleAgent` takes an injectable `SpawnFn`, so no suite launches `pi` or
|
|
494
|
+
spends tokens.
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# team — the lead's round over the board
|
|
2
|
+
|
|
3
|
+
`board` owns the cards. `team` owns the lead's three actions: `assign` hands a card to a
|
|
4
|
+
session that is **already running**, `offer` hands one to whoever is free, and `reclaim`
|
|
5
|
+
takes one back when its owner is gone or has gone quiet. `assign` writes the card and puts
|
|
6
|
+
a handoff on the session bus, `offer` writes only the moment it was made — never an owner,
|
|
7
|
+
because the first peer to claim is what makes an offer into work — and `reclaim` writes the
|
|
8
|
+
card alone, so it needs no bus.
|
|
9
|
+
The report says what the peer will actually do with it.
|
|
10
|
+
|
|
11
|
+
Nothing starts a process. A message lands in the peer's inbox and is reported at its next
|
|
12
|
+
turn, so `assign` and `offer` are messages, not launches.
|
|
13
|
+
|
|
14
|
+
<img src="../../assets/screens/team.png" alt="The Team tab as a dashboard: on the left, a Needs a decision queue of rows with a coloured kind edge, the reason on its own line, and the owner as a chip on the row's right; on the right, a Teammates list with a state dot, label, you chip, mail badge, a not listening chip on a peer whose watch is off, and a line of what each holds; and one muted Done line under both" width="100%">
|
|
15
|
+
|
|
16
|
+
*The Team tab: the cards that need the lead, then who is on the team — including an
|
|
17
|
+
owner whose label nothing answers to, and a teammate that is not listening.*
|
|
18
|
+
|
|
19
|
+
## Capabilities
|
|
20
|
+
|
|
21
|
+
| Capability | How | What you get |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Join the team | `/board` as the lead, `/team join <label>` as a teammate, or any card you work | The session is on the team under a label peers address, listening, and `/team` and the Team tab list it |
|
|
24
|
+
| Hand a feature to a running session | `team` action `assign` | The card is `doing`, owned by that session, and a handoff is in its inbox |
|
|
25
|
+
| Tell the peer what to do first | `team` action `assign` with `note` | The handoff carries the card, its paths, its plan, and that line |
|
|
26
|
+
| Hand a feature to whoever is free | `team` action `offer` | Every other live session gets an event naming the card; the card stays unowned until one claims it, and it records when the offer was made, so a lead can tell an offer nobody took from a card nobody has looked at |
|
|
27
|
+
| Know whether a message will be woken for | an `assign` or `offer` result, `/team`, and the Team tab | All three name the peers whose record says their next turn is when it lands: the result as `Not listening: <labels>`, `/team` as a lowercase `not listening:` inside its presence line, the Team tab as a chip |
|
|
28
|
+
| Take a card back | `team` action `reclaim` | The owner clears, the card returns to To do, and its notes stay |
|
|
29
|
+
| Keep the round to the team | `team` | Only `assign`, `offer`, and `reclaim`: no card is created, moved, planned, or finished here — that is `board`'s surface |
|
|
30
|
+
| See the round without a browser | `/team` | `buildRound`'s summary — the one the page's Team tab is served — laid out as text by `formatRound`: what needs a decision, who holds what, then one line naming the cards a dead owner left behind, and one presence line for who is live, who is not listening, who has gone quiet, and who is gone. The rollup itself is capped at ten lines, then `… N more`, and the gone and presence lines follow the cap rather than counting against it — the cap is a notify's limit, not a screen's. An empty board is one line |
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
lead: /board → on the team as team-lead, listening
|
|
36
|
+
peer: /team join billing-core → on the team as billing-core, listening
|
|
37
|
+
lead: team({action:"assign", card:"4", to:"billing-core", note:"start with the webhook"})
|
|
38
|
+
→ #4 handed off to billing-core.
|
|
39
|
+
peer: board({action:"plan", card:"4", path:"…"}) → the card records its plan
|
|
40
|
+
peer: board({action:"note", card:"4", text:"…"}) → a phase, as it happens
|
|
41
|
+
later: team({action:"reclaim", card:"4"}) → #4 reclaimed from billing-core
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Work nobody has taken instead of work you are routing:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
lead: team({action:"offer", card:"5", note:"whoever is free"})
|
|
48
|
+
→ #5 offered to billing-core, billing-web.
|
|
49
|
+
peer: board({action:"claim", card:"5"}) → the offer becomes work
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Joining the team
|
|
53
|
+
|
|
54
|
+
You do not configure a team; there are no members to add. The team is the session bus,
|
|
55
|
+
the local coordination channel between Pi sessions on this repository, and staying off
|
|
56
|
+
its surface is the point: `board` and `team` join this session for you.
|
|
57
|
+
|
|
58
|
+
| You are | Action | What happens |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| The lead | `/board` | The session joins as `team-lead` — the page's own seat name — and listens, so a handoff or an offer is reported at its next turn |
|
|
61
|
+
| A teammate | `/team join billing-core` | The session joins as `billing-core`, and Pi's footer and `/resume` show that name |
|
|
62
|
+
| Either | any `board` action that writes | The first one joins the session under the name it already has, and listens |
|
|
63
|
+
| A viewer | `/board` on a session that is not serving | The page is somebody else's to serve, so this session joins under its own label instead of the seat |
|
|
64
|
+
|
|
65
|
+
Joining is listening: mail is reported at that session's next turn, and **no turn is
|
|
66
|
+
raised** for it, so being on the team never spends a turn. `/team` says who is live and
|
|
67
|
+
who is live but not listening. `/board status` repeats only what the last `/board` in this
|
|
68
|
+
session did about the team — a join made by `/team join` or by a card write is not on that
|
|
69
|
+
line, and a page that is not running prints no team clause at all.
|
|
70
|
+
|
|
71
|
+
**The seat is not a session.** The page is a view: it has one name, `team-lead`, and an id
|
|
72
|
+
of its own derived from it, and it writes nothing, so it holds no card for a lead to take
|
|
73
|
+
back. Its cards come from the session that serves it, whose label is the same and whose id
|
|
74
|
+
is not: a card that session claimed is the session's, and `release` returns it.
|
|
75
|
+
|
|
76
|
+
## Tool
|
|
77
|
+
|
|
78
|
+
`team` is one action-shaped tool. `readOnlyHint: false`, `destructiveHint: true`,
|
|
79
|
+
`openWorldHint: false`, `executionMode: "sequential"`.
|
|
80
|
+
|
|
81
|
+
| Action | Parameters | Behaviour |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `assign` | `card`, `to`, `note?` | Checks the card's preconditions, sends the handoff as a `handoff` message, then records the owner |
|
|
84
|
+
| `offer` | `card`, `note?` | Checks the preconditions a claim would meet, broadcasts the card as an `event` to every other live session, then records the offer on the card — the moment, and nothing else: status and owner are untouched |
|
|
85
|
+
| `reclaim` | `card` | Clears the owner and returns the card to To do, keeping its notes and plan |
|
|
86
|
+
|
|
87
|
+
`card` takes `4` or `#4`. `to` is a teammate's label or id, as `/team` lists them — a
|
|
88
|
+
label nobody answers to cannot be handed a card, and the result says so.
|
|
89
|
+
|
|
90
|
+
The parameters are one flat object, so a field is published to every action at once.
|
|
91
|
+
An action reads only the fields in its row above, and a call carrying one that its
|
|
92
|
+
action does not read is **refused**, naming the action that takes it — not ignored.
|
|
93
|
+
`to` is the one that matters: an `offer` that named a peer was broadcast to every live
|
|
94
|
+
session while the lead believed it had named one, and a `reclaim` that named one took
|
|
95
|
+
the card back from its holder anyway.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
offer does not act on to: `assign` takes it.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`ACTION_FIELDS` in `types.ts` is the table that answers this, `lib/stray-fields.ts` is
|
|
102
|
+
the sentence, and a test holds every published parameter to an action that acts on it.
|
|
103
|
+
|
|
104
|
+
### The order is the point
|
|
105
|
+
|
|
106
|
+
`assign` sends **before** it assigns, and each failure leaves a state the report names:
|
|
107
|
+
|
|
108
|
+
| What failed | What the card is | What the lead reads |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| the card is missing, `done`, held by a third session, or waiting on unfinished work | untouched | the refusal, with no message sent at all |
|
|
111
|
+
| the send | untouched — `todo`, no owner | `#4 was not handed off to billing-core: …`, plus `The card is unchanged.` |
|
|
112
|
+
| the assignment, after a successful send | whatever the other writer left | `#4 was handed off to billing-core, but the card changed before it could be assigned: …`, and the peer is told to `board claim` it |
|
|
113
|
+
|
|
114
|
+
`offer` broadcasts **before** it records, for the same reason: an offer nobody received
|
|
115
|
+
must not leave a record claiming one happened. A refused broadcast leaves the card exactly
|
|
116
|
+
as the rules read it, and the write that follows changes one field — when the offer was
|
|
117
|
+
made. If the card changed in between, the peers already have the message, so the refusal
|
|
118
|
+
says the offer stands as a message and the board is what it now says. The first `board
|
|
119
|
+
claim` is what turns an offer into work, and the claim is what clears the record: every
|
|
120
|
+
answer to an offer — a claim, an assignment, a release — takes it with it, so a record
|
|
121
|
+
here means the offer is still open. Two peers claiming at once are settled by the store's
|
|
122
|
+
compare-and-set; one claim wins, and the other is refused with the owner named.
|
|
123
|
+
|
|
124
|
+
`delivered` in the details means a message file was written for that label — not that the
|
|
125
|
+
peer read it. `notListening` names the peers it reached whose record says they are not
|
|
126
|
+
listening, so the message waits for their next turn.
|
|
127
|
+
|
|
128
|
+
The owner `assign` records is the **session**, not just its name: before writing the card
|
|
129
|
+
it reads the team list and keeps the id of the session behind that label. Ownership is
|
|
130
|
+
compared by id first (`lib/board/rules.ts`), so a peer that renames itself still holds its
|
|
131
|
+
card; a card assigned by label alone — an older one, or one whose session the bus could
|
|
132
|
+
not name — goes back to `reclaim` if its holder renames.
|
|
133
|
+
|
|
134
|
+
### What the peer does on waking
|
|
135
|
+
|
|
136
|
+
A handoff is an instruction, not a notification: the peer's notice is headlined
|
|
137
|
+
`1 handoff`, carries enough of the body to be actionable, and says the work is theirs —
|
|
138
|
+
read it with `session inbox`, record the plan, and start. The card is already theirs
|
|
139
|
+
(`assign` wrote the owner), so there is no claim step; the peer plans, works, notes, and
|
|
140
|
+
moves it to `done` or `blocked`.
|
|
141
|
+
|
|
142
|
+
An offer is advice: nothing is theirs until they claim it, and a card nobody claims stays
|
|
143
|
+
`todo`, so it keeps showing up as unassigned in the round — with its age, which is the one
|
|
144
|
+
thing an event cannot carry. `board` prints it as `offered 1m ago, and nobody took it`, the
|
|
145
|
+
card face says `offered 1m ago · nobody took it`, and the Team tab's queue says
|
|
146
|
+
`offered 1m ago — nobody took it` in place of "nobody holds it". A lead reading that has a
|
|
147
|
+
decision the plain unassigned row never offered: re-offer, `assign` it, or take it.
|
|
148
|
+
|
|
149
|
+
## Security
|
|
150
|
+
|
|
151
|
+
- The tool writes the board store and calls one other extension's tool (`session`); it
|
|
152
|
+
starts no process, opens no socket, and reads no credential.
|
|
153
|
+
- A handoff or an offer is a message to other local sessions, so it carries nothing the
|
|
154
|
+
board would not show: the card's title, paths, plan, and the lead's note. An offer names
|
|
155
|
+
every session on the repository as a recipient, so it says nothing it would not say to
|
|
156
|
+
each of them.
|
|
157
|
+
|
|
158
|
+
## Limitations
|
|
159
|
+
|
|
160
|
+
- **Nothing here can start a peer's turn.** A message is reported at the recipient's next
|
|
161
|
+
turn; a session that is not listening at all reads it only when it reads its inbox.
|
|
162
|
+
- **`to` must be on the team.** A session that never opened the board, joined, or worked a
|
|
163
|
+
card cannot be handed a card, and an offer to nobody says so.
|
|
164
|
+
- **`team-lead` is a label, not a person.** The seat is not a session, so its actor id
|
|
165
|
+
`board:team-lead` answers no message; the label is the one the session serving the page
|
|
166
|
+
joins under, so a handoff to `team-lead` reaches whoever is serving the page just now —
|
|
167
|
+
and is refused, like any other label, when nobody is. The seat's cards come back with
|
|
168
|
+
`reclaim`.
|
|
169
|
+
- **A label-only owner is fragile.** If the bus could not name the session behind `to`,
|
|
170
|
+
the card records the label, and a rename would need a `reclaim` and a fresh handoff.
|
|
171
|
+
- **An offer is advice.** A card can sit offered indefinitely; the round keeps counting it
|
|
172
|
+
as unassigned, which is the honest signal that nobody took it.
|
|
173
|
+
- **An offer arrives as an `event`.** A burst that mixes one with ordinary messages is
|
|
174
|
+
headlined as new messages rather than named as an offer.
|
|
175
|
+
- **Liveness is a heartbeat; listening is a field.** A running session rewrites its record
|
|
176
|
+
every ten seconds and the record is believed for ten minutes, so `quiet:` means the
|
|
177
|
+
record went stale rather than that the peer died, while `not listening` is the separate
|
|
178
|
+
`watching` field, written when the session joins or arms and disarms its watch.
|
|
179
|
+
- **Plan mode makes this unavailable.** Neither `team` nor `session` is a read-only tool, so
|
|
180
|
+
plan mode refuses both by name and tells the model to exit and get approval; the board
|
|
181
|
+
stays readable, since `list` is the one action it admits. Assign after the plan is
|
|
182
|
+
approved.
|
|
183
|
+
- **Ownership is advisory.** `assign` does not create a path claim; the peer still claims
|
|
184
|
+
the paths it edits through `session`.
|
|
185
|
+
|
|
186
|
+
## Pi integration
|
|
187
|
+
|
|
188
|
+
- **`board`**: the same store, the same cards, the same rules — `team` calls `lib/board/`
|
|
189
|
+
directly, so a card it hands over is one `board` can see. Either extension works without
|
|
190
|
+
the other: without `team` the page still reclaims, and without `board` there is nothing
|
|
191
|
+
to assign.
|
|
192
|
+
- **`session-bus`**: the transport, and the one extension `team` talks to — through
|
|
193
|
+
`lib/session-tool.ts`, whose `join` is an in-process request (a command context cannot
|
|
194
|
+
call a tool) and whose `peers` and `send` are nested tool calls. The bus's actions and
|
|
195
|
+
labels never appear on this extension's user surface — `team` and `board` are the
|
|
196
|
+
surface — but its name does, in the sentence an unloaded bus produces: `team: not on the
|
|
197
|
+
team — the session bus is not loaded`. Every call returns that sentence instead of
|
|
198
|
+
throwing, which is the "card untouched" row above.
|
|
199
|
+
- **`plan`**: the handoff names the plan file when one is recorded, so the peer knows
|
|
200
|
+
whether it is executing a plan or writing one.
|
|
201
|
+
- **`subagent`**: a child process is a different Pi session; hand a card to a peer
|
|
202
|
+
session, not to a child you started for one question.
|
|
203
|
+
|
|
204
|
+
## Design notes
|
|
205
|
+
|
|
206
|
+
- **Two extensions, not one tool with a flag.** `board` is the work list any session uses
|
|
207
|
+
and must work with the bus disabled; `team` is the lead's round and is the only thing
|
|
208
|
+
that needs the bus. If the round ever stops needing it, `team` folds back in.
|
|
209
|
+
- **Joining is the bus's, wearing `board`'s clothes.** A lead should not have to learn a
|
|
210
|
+
second tool to be on a team, so `/board` joins, a card write joins, and `/team join` is
|
|
211
|
+
the one naming action. `wake` stays a bus-level choice and is never armed on a user's
|
|
212
|
+
behalf, because it spends turns.
|
|
213
|
+
- **Push and pull are two verbs.** `assign` writes the owner and sends one message;
|
|
214
|
+
`offer` broadcasts and writes nothing. One verb with an optional `to` would have two
|
|
215
|
+
outcomes and one details shape, and a reader could not tell which happened.
|
|
216
|
+
- **The offer's preconditions are a dry-run claim.** `applyOp` judges what a peer is about
|
|
217
|
+
to do, so a card nobody may claim is refused before anything is sent, and the refusal is
|
|
218
|
+
the rules' own sentence rather than a second copy.
|
|
219
|
+
- **Listening is a record field.** The bus writes it when a session joins or stops
|
|
220
|
+
listening, so the report, `/team`, and the Team tab read one fact from disk instead of
|
|
221
|
+
guessing from the send.
|
|
222
|
+
- **Send first.** A card assigned without a message is a card nobody knows about, while a
|
|
223
|
+
message for a card that then changed is one the peer can still claim.
|
|
224
|
+
- **`reclaim` is here, and in `board`.** It is the compensation for a dispatch that did not
|
|
225
|
+
land, so it belongs in the lead's context — and it is the same operation `board` and its
|
|
226
|
+
page offer, because a card whose owner went away must not need a second extension to come
|
|
227
|
+
back.
|
|
228
|
+
- **Rejected: `reclaim` messaging the previous owner.** A compensation path must not depend
|
|
229
|
+
on a second delivery — the reclaim is what you do when the owner is gone, so its message
|
|
230
|
+
would fail exactly when it is needed. The card is the record.
|
|
231
|
+
- **Rejected: a fifth message kind for an offer.** An offer is something that happened on
|
|
232
|
+
the board, which is what `event` already means; a new kind would change the protocol, the
|
|
233
|
+
notice, and every reader for one verb.
|
|
234
|
+
|
|
235
|
+
## Files
|
|
236
|
+
|
|
237
|
+
| File | Responsibility |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `index.ts` | The factory: gate on `PI_DISABLED_EXTENSIONS`, build the session, register the tool and the command |
|
|
240
|
+
| `schema.ts` | The tool's three actions and their parameters |
|
|
241
|
+
| `tools.ts` | The dispatch order, the refusals, and the report |
|
|
242
|
+
| `dispatch.ts` | The handoff and offer text, the bus behind a seam, and who is not listening |
|
|
243
|
+
| `commands.ts` | `/team` — the round, and `/team join [<label>]` |
|
|
244
|
+
| `format.ts` | The model-facing text for a handoff, an offer, a failure, a race, and a reclaim |
|
|
245
|
+
| `types.ts` | The tool's name, actions, and details |
|
|
246
|
+
|
|
247
|
+
The store, the rules, and the session identity are `lib/board/`; the join, the peer list,
|
|
248
|
+
and the message send are `lib/session-tool.ts` — see [`lib/README.md`](../../lib/README.md).
|
|
249
|
+
|
|
250
|
+
## Testing
|
|
251
|
+
|
|
252
|
+
`test/suite-manifest.ts` declares `team/{commands,dispatch,tools,extension}`: the handoff
|
|
253
|
+
and offer text, the bus's answers behind the seam, the assign/offer/reclaim ordering
|
|
254
|
+
against a recording dispatch, `/team` and `/team join`, and the factory.
|