@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
|
@@ -1,92 +1,63 @@
|
|
|
1
1
|
# todo — a TodoWrite-style task list for Pi
|
|
2
2
|
|
|
3
|
-
A whole-list task tracker for Pi, modelled on Claude Code's `TodoWrite
|
|
4
|
-
model sends the complete list on every call
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
A whole-list task tracker for Pi, modelled on Claude Code's `TodoWrite`: the
|
|
4
|
+
model sends the complete list on every call, so there are no ids to juggle and
|
|
5
|
+
no add/toggle bookkeeping. The list follows the active session branch and is
|
|
6
|
+
rebuilt on `/resume` and `/tree`, and the extension keeps it honest by asking for
|
|
7
|
+
one update when work lands without one.
|
|
8
|
+
|
|
9
|
+
<img src="../../assets/screens/todo.png" alt="The /todos screen: a Todos title rule, a 3/7 completed summary, and the list with check, spinner, and circle glyphs" width="100%">
|
|
10
|
+
|
|
11
|
+
## Capabilities
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
| Capability | How | What you get | Status |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| Whole-list tracking | `todo` | One call replaces the list; an empty list clears it. | Shipped |
|
|
18
|
+
| Live progress rail | the `todo` widget | Progress and the active item, one line above the editor. | Shipped |
|
|
19
|
+
| Atomic validation | `todo` validation | At most one `in_progress`, at most 50 items, unique non-empty content, and a known status; a violation returns an error with the unchanged list. | Shipped |
|
|
20
|
+
| Terminal-safe normalization | sanitized `content` and `activeForm` | Control characters, newlines, tabs, and repeated spaces collapse to one terminal-safe line before the list is stored. | Shipped |
|
|
21
|
+
| Compact model result | the `todo` result | The model gets progress and the active item, not an echo of the whole list, so every update does not repeat the table. | Shipped |
|
|
22
|
+
| Transcript rendering | the `todo` transcript block | The call line carries progress; the collapsed rows lead with active work, the expanded rows keep submission order, and a cap ends with `… N more`. | Shipped |
|
|
23
|
+
| Full list on demand | `/todos` | A scrollable screen in the order the model submitted. | Shipped |
|
|
24
|
+
| Headless listing | `/todos` outside `tui` | RPC, print, and JSON modes get the same list as a notification, with no terminal screen. | Shipped |
|
|
25
|
+
| Branch-following state | tool-result `details` | The list survives `/resume` and `/tree`, and abandoned branches never leak in. | Shipped |
|
|
26
|
+
| Lag reminder | the `agent_before_settle` nudge | One hidden reminder and one forced continuation when mutating work lands without a todo update. | Shipped |
|
|
27
|
+
| Structured result | `structuredContent` | Codemode scripts read the list as data. | Shipped |
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
```bash
|
|
14
32
|
pi --extension ./extensions/todo # load just this extension
|
|
15
33
|
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
16
|
-
pi install ./ # install the package
|
|
34
|
+
pi install ./ # install the package for a user
|
|
17
35
|
```
|
|
18
36
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
How the `todo` call and its result look in the transcript — the call line as a headline and the body indented under it:
|
|
22
|
-
|
|
23
|
-
<img src="../../assets/tools/todo.png" alt="The todo tool's result: the call line and an indented body with the progress line and the task rows below it, each led by a check, spinner, or circle glyph" width="100%">
|
|
24
|
-
|
|
25
|
-
## What it does
|
|
26
|
-
|
|
27
|
-
- Registers one model-callable tool, `todo`, that **replaces** the task list.
|
|
28
|
-
An empty list clears it. The tool result is a compact summary — progress plus
|
|
29
|
-
the current item — not an echo of the list the model just sent.
|
|
30
|
-
- Each item has `content`, a `status` (`pending` | `in_progress` |
|
|
31
|
-
`completed`), and an optional `activeForm` ("Running tests") shown while the
|
|
32
|
-
item is in progress.
|
|
33
|
-
- Shows a persistent widget above the editor while the list has unfinished
|
|
34
|
-
work (hidden when empty or fully completed), and a scrollable `/todos`
|
|
35
|
-
screen on demand. It is the bottom rail, below the [`goal`](../goal/) widget
|
|
36
|
-
(`Goal / Todos`).
|
|
37
|
-
- Stores the list in tool-result `details`, so it follows the active session
|
|
38
|
-
branch and survives `/resume` and `/tree` — abandoned branches never leak
|
|
39
|
-
into the current list, and stored lists are re-sanitized on replay.
|
|
40
|
-
- Keeps the list honest: when a run is about to settle after a successful
|
|
41
|
-
mutating tool call (`write`/`edit`/`bash`/`powershell`, or a tool declaring
|
|
42
|
-
`readOnlyHint: false`) without a todo update, it injects one hidden reminder
|
|
43
|
-
and forces a single continuation. A failed or blocked call changed nothing, so
|
|
44
|
-
it never triggers one. It fires while the list has unfinished items, and —
|
|
45
|
-
when no list exists at all — only after three such calls in one turn, at least
|
|
46
|
-
one of them a file write (`write`/`edit`, or a tool declaring
|
|
47
|
-
`readOnlyHint: false`), so repeated `bash` inspection is not mistaken for
|
|
48
|
-
unplanned work. A list the model wrote or cleared during the turn is left
|
|
49
|
-
alone. At most one reminder per user turn, and it is dropped on the next user
|
|
50
|
-
turn, so a reader can never trigger it.
|
|
51
|
-
- Declares an `outputSchema` and returns matching `structuredContent`
|
|
52
|
-
(`{ todos, action, error? }`), so codemode/scripts can read the list as data.
|
|
53
|
-
|
|
54
|
-
The widget is a one-line summary — progress plus the current item — so the
|
|
55
|
-
active task is always visible without expanding the list:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
Todos · 1/3 · Writing the tests
|
|
37
|
+
```text
|
|
38
|
+
/todos # show the list, and its progress
|
|
59
39
|
```
|
|
60
40
|
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
The model calls `todo` with the complete list; the widget appears above the
|
|
42
|
+
editor while anything is unfinished.
|
|
63
43
|
|
|
64
|
-
|
|
65
|
-
result keeps the `✓` / `◒` / `◌` rows, directly under the call line. The item
|
|
66
|
-
text lives on the rows, so the call line never repeats it. Collapsed, it leads
|
|
67
|
-
with active work; expanded, it keeps the submitted order:
|
|
44
|
+
## Tool
|
|
68
45
|
|
|
69
|
-
|
|
70
|
-
todo → 1/4
|
|
71
|
-
◒ Writing tests
|
|
72
|
-
◌ Ship it
|
|
73
|
-
◌ Write docs
|
|
74
|
-
✓ Write schema
|
|
75
|
-
```
|
|
46
|
+
<img src="../../assets/tools/todo.png" alt="The todo tool's result: the call line and an indented body with the progress line and the task rows below it, each led by a check, spinner, or circle glyph" width="100%">
|
|
76
47
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
48
|
+
`todo` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
|
|
49
|
+
read-write, closed-world, idempotent. It replaces the list; an empty list clears
|
|
50
|
+
it. The result is a compact summary — progress plus the current item — not an
|
|
51
|
+
echo of the list the model just sent.
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
| Parameter | Type | Meaning |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `todos` | array, required | The complete list. Pass `[]` to clear. |
|
|
56
|
+
| `todos[].content` | string | Imperative description, e.g. `"Write the parser tests"`. |
|
|
57
|
+
| `todos[].status` | `pending` \| `in_progress` \| `completed` | Where the item stands. |
|
|
58
|
+
| `todos[].activeForm` | string | Present-continuous label, required when the item is `in_progress` ("Running tests"). |
|
|
82
59
|
|
|
83
|
-
|
|
84
|
-
|---|---|
|
|
85
|
-
| `name` | `todo` |
|
|
86
|
-
| `todos` | The complete list. Pass `[]` to clear. |
|
|
87
|
-
| `todos[].content` | Imperative description, e.g. `"Write the parser tests"`. |
|
|
88
|
-
| `todos[].status` | `pending`, `in_progress`, or `completed`. |
|
|
89
|
-
| `todos[].activeForm` | Present-continuous label, required when the item is `in_progress`. |
|
|
60
|
+
Result (`details` / `structuredContent`): `{ todos, action, error? }`.
|
|
90
61
|
|
|
91
62
|
Validation rules (a violation leaves the list unchanged):
|
|
92
63
|
|
|
@@ -108,20 +79,45 @@ unknown `status` or a wrong argument type is rejected by Pi before `execute`
|
|
|
108
79
|
runs, so the host's validation message is returned instead; the list is still
|
|
109
80
|
unchanged.
|
|
110
81
|
|
|
111
|
-
```
|
|
82
|
+
```text
|
|
112
83
|
todo({ todos: [{ content: "Write the parser tests", status: "in_progress", activeForm: "Writing the parser tests" }] })
|
|
113
84
|
todo({ todos: [] }) // clear
|
|
114
85
|
```
|
|
115
86
|
|
|
116
|
-
|
|
87
|
+
The widget is a one-line summary — progress plus the current item — so the
|
|
88
|
+
active task is always visible without expanding the list:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
Todos · 1/3 · Writing the tests
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
In the transcript, the call line carries progress only (`todo → 1/4`); the
|
|
95
|
+
result keeps the `✓` / `◒` / `◌` rows, directly under the call line. The item
|
|
96
|
+
text lives on the rows, so the call line never repeats it. Collapsed, it leads
|
|
97
|
+
with active work; expanded, it keeps the submitted order:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
todo → 1/4
|
|
101
|
+
◒ Writing tests
|
|
102
|
+
◌ Ship it
|
|
103
|
+
◌ Write docs
|
|
104
|
+
✓ Write schema
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Progress lives on the call line, so the rows have no footer; the persistent
|
|
108
|
+
widget and `/todos` still show the count. A capped collapsed rail ends with
|
|
109
|
+
`… N more` and the `app.tools.expand` hint.
|
|
110
|
+
|
|
111
|
+
## Commands
|
|
117
112
|
|
|
118
113
|
| Command | What it does |
|
|
119
114
|
|---|---|
|
|
120
115
|
| `/todos` | Show the current list. An interactive, scrollable screen in `tui` mode; a notification elsewhere. |
|
|
121
116
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
117
|
+
`/todos` is the expanded view and keeps the model's list order, so it shows the
|
|
118
|
+
plan as written. On the screen: `↑`/`↓` or `j`/`k` scroll one row, `PgUp`/`PgDn`
|
|
119
|
+
page, `g`/`G` or `Home`/`End` jump to the ends, and `Esc` (or `Ctrl-C`) closes.
|
|
120
|
+
The window grows and shrinks with the terminal height, so the footer hint stays
|
|
125
121
|
visible.
|
|
126
122
|
|
|
127
123
|
The TUI label is status-dependent: an `in_progress` row shows its `activeForm`
|
|
@@ -135,13 +131,14 @@ The tool works in every mode. The persistent widget and the `/todos` screen
|
|
|
135
131
|
require interactive (`tui`) mode; RPC and non-interactive runs simply get the
|
|
136
132
|
tool result. The lag reminder is mode-independent, because it rides the agent
|
|
137
133
|
boundary and not the terminal UI. While any dock screen is open (`/todos`,
|
|
138
|
-
`/jobs`, `/rewind`, or the `question` questionnaire), the above-editor
|
|
139
|
-
|
|
134
|
+
`/jobs`, `/rewind`, or the `question` questionnaire), the above-editor rails are
|
|
135
|
+
hidden and return on close.
|
|
140
136
|
|
|
141
137
|
## Configuration
|
|
142
138
|
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
One key, in the `todo` section of the shared `bytefly.json` (global document,
|
|
140
|
+
then project document, project wins; resolution rules in
|
|
141
|
+
[`../configuration.md`](../configuration.md#todo--the-todo-widget)):
|
|
145
142
|
|
|
146
143
|
| Key | Default | Meaning |
|
|
147
144
|
|---|---|---|
|
|
@@ -201,7 +198,7 @@ depends on config.
|
|
|
201
198
|
a goal update would sink the list; the list subscribes on `pi.events` and
|
|
202
199
|
re-runs `setWidget` whenever a rail above it changes, keeping the stack
|
|
203
200
|
`Goal / Todos`. It never announces, so the chain cannot ping-pong. See
|
|
204
|
-
[`lib/rails.ts`](../../lib/rails.ts) and [`goal`](
|
|
201
|
+
[`lib/rails.ts`](../../lib/rails.ts) and [`goal`](./goal.md).
|
|
205
202
|
- **The reminder is a bounded nudge, not a nag.** A prompt guideline cannot
|
|
206
203
|
guarantee a timely update, so the extension backs it with one forced
|
|
207
204
|
continuation. `tool_execution_end` counts a successful call by an explicit
|
|
@@ -237,8 +234,7 @@ depends on config.
|
|
|
237
234
|
| File | Responsibility |
|
|
238
235
|
|---|---|
|
|
239
236
|
| `index.ts` | Factory: wire the runtime, tool, command, reminder events, and session events. |
|
|
240
|
-
| `types.ts` | `Todo`, `TodoStatus
|
|
241
|
-
| `types.ts` | `Todo` and `TodoDetails`; re-exports `TodoStatus` / `TODO_STATUSES` from `lib/todo-state.ts`. |
|
|
237
|
+
| `types.ts` | `Todo`, `TodoDetails`; re-exports `TodoStatus` / `TODO_STATUSES` from `lib/todo-state.ts`. |
|
|
242
238
|
| `schema.ts` | TypeBox parameters; the parser and limits live in `lib/todo-state.ts`. |
|
|
243
239
|
| `state.ts` | Status queries over the replayed list (pure). |
|
|
244
240
|
| `nudge.ts` | Lag detection: mutating and file-write classification, due check, reminder text (pure). |
|
|
@@ -248,3 +244,4 @@ depends on config.
|
|
|
248
244
|
| `runtime.ts` | Session-scoped state, lag tracking, and widget synchronization. |
|
|
249
245
|
| `tools.ts` | `todo` tool registration and rendering. |
|
|
250
246
|
| `commands.ts` | `/todos`. |
|
|
247
|
+
| `tools.test.ts`, `runtime.test.ts`, `ui.test.ts` | The capability suites, declared in `test/suite-manifest.ts`. |
|
|
@@ -6,41 +6,56 @@ the TUI, reported to the model at the next turn, or — when a watcher opts into
|
|
|
6
6
|
`wake` — raise one triggered turn as soon as the agent is idle, including right
|
|
7
7
|
after a running turn settles.
|
|
8
8
|
|
|
9
|
-
|
|
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
|
+
### Watching
|
|
14
|
+
|
|
15
|
+
| Capability | How | What you get | Status |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| Tail a source | `watch add` | A file is followed from its current end unless `fromStart` is set, and a shrinking file is re-read from the start; a command's merged stdout/stderr is streamed. | Shipped |
|
|
18
|
+
| Line matching | `watch add` | An include pattern with an optional exclude list, case-insensitive unless `caseSensitive` is set. | Shipped |
|
|
19
|
+
| Match delivery | `watch add`, `watch matches` | Pending matches are reported to the model at the start of the next turn. | Shipped |
|
|
20
|
+
| Wake turn | `watch add`, the `wakeOnMatch` key | One triggered turn as soon as the agent is idle, including right after a running turn settles. | Shipped |
|
|
21
|
+
| Desktop alert | `my-pi-agent/watch-match`, `notify.enabled`, `notify.matchFound` | A wake match leaves the terminal as an OS notification. | Shipped |
|
|
22
|
+
| Live status | `watch add`, the `showStatus` key | A `⊚ N` running chip and a `! N` unreported-match chip, plus one alert per unreported burst. | Shipped |
|
|
23
|
+
| Command outcome | `watch status`, `watch list` | A command watcher reports its exit code or error, including the captured signal, instead of only "stopped". | Shipped |
|
|
24
|
+
| Liveness detail | `watch list`, `watch status` | A running watcher names its pid and elapsed time; list rows sort running watchers first, newest next. | Shipped |
|
|
25
|
+
| Transcript rendering | the `watch` transcript block | The outcome line, at most eight rows, and a `(ctrl+o to expand)` hint keep the transcript compact. | Shipped |
|
|
26
|
+
| Sanitized output | `watch matches`, `/watch matches` | Model- and remote-authored output is stripped of control characters before it reaches a terminal or a notification. | Shipped |
|
|
27
|
+
|
|
28
|
+
### Reading and managing
|
|
29
|
+
|
|
30
|
+
| Capability | How | What you get | Status |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| Watcher inventory | `watch list`, `watch status`, `/watch list` | Each watcher's source, state, and line and match counts, singly or as a table. | Shipped |
|
|
33
|
+
| Matched lines | `watch matches`, `/watch matches` | The matching lines, sanitized and read from a bounded history. | Shipped |
|
|
34
|
+
| Raw output | `watch logs`, `/watch logs` | The merged output tail, for checking a pattern that matches nothing. | Shipped |
|
|
35
|
+
| Watcher screen | `/watch` | A dock screen with the watcher list, the focused watcher's detail, and its matches pane. | Shipped |
|
|
36
|
+
| Stopping and cleanup | `watch stop`, `watch clear`, `/watch stop`, `/watch clear` | A running watcher stops, stopped ones are removed, and `all` takes the running ones too. | Shipped |
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```bash
|
|
10
41
|
pi --extension ./extensions/watch # load just this extension
|
|
11
42
|
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
12
43
|
pi install ./ # install the package
|
|
13
44
|
```
|
|
14
45
|
|
|
15
|
-
|
|
46
|
+
```text
|
|
47
|
+
watch add { "path": "server.log", "pattern": "ERROR|FATAL", "label": "server" }
|
|
48
|
+
/watch
|
|
49
|
+
```
|
|
16
50
|
|
|
17
|
-
|
|
51
|
+
## Tool
|
|
18
52
|
|
|
19
53
|
<img src="../../assets/tools/watch.png" alt="The watch tool's result: the call line and an indented body with a running file watcher and an exited command watcher" width="100%">
|
|
20
54
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- Tails a text file by polling it, or a shell command by streaming its merged
|
|
26
|
-
stdout/stderr. A file is followed from its current end unless `fromStart` is
|
|
27
|
-
set; a shrinking file (rotation/truncation) is re-read from the start.
|
|
28
|
-
- Matches with a regular expression and an optional exclude list,
|
|
29
|
-
case-insensitive by default and case-sensitive with `caseSensitive: true`.
|
|
30
|
-
- Shows a `⊚ N` running-watcher chip and a `! N` unreported-match chip, raises a
|
|
31
|
-
`ctx.ui.notify` alert once per unreported burst, and offers a `/watch` screen.
|
|
32
|
-
- Reports pending matches to the model at the start of the next turn, or wakes a
|
|
33
|
-
turn for a watcher added with `wake: true` as soon as the agent is idle. A wake
|
|
34
|
-
that arrives mid-turn is raised once that run settles. A wake also leaves the
|
|
35
|
-
terminal: the
|
|
36
|
-
match is announced on `my-pi-agent/watch-match`, which bytefly's
|
|
37
|
-
[notify](../bytefly/README.md#notifications) capability turns into a desktop
|
|
38
|
-
notification when `notify.enabled` and `notify.matchFound` are on.
|
|
39
|
-
- Watchers are session-only: `shutdown` flushes a partial line, sends SIGTERM to
|
|
40
|
-
every command's process tree (escalating to SIGKILL after a short grace), and
|
|
41
|
-
clears the chips.
|
|
42
|
-
|
|
43
|
-
## Tool
|
|
55
|
+
`watch` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
|
|
56
|
+
read-write, destructive, open-world, not idempotent. One tool with seven
|
|
57
|
+
actions; a watcher outlives the call, so `add` returns an id immediately and the
|
|
58
|
+
rest read it back.
|
|
44
59
|
|
|
45
60
|
| Field | Value |
|
|
46
61
|
|---|---|
|
|
@@ -104,15 +119,10 @@ capped at eight rows, minus the selection-marker column (`listRow`'s
|
|
|
104
119
|
render the tail of the sanitized lines, with the hidden count noted above and
|
|
105
120
|
`(ctrl+o to expand)`.
|
|
106
121
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<img src="../../assets/watch.png" alt="The /watch screen: a Watch title rule, a `2 watchers · 1 running · 3 matches` summary, a selected file watcher and a finished command watcher, the focused watcher's path and pattern with its line and match counts, and the key hints" width="100%">
|
|
122
|
+
## Commands
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
behind a `y/N` confirm, `d` clears stopped ones behind a `y/N` confirm, `Esc`
|
|
114
|
-
closes, and `g`/`G` and PgUp/PgDn scroll the matches pane. Subcommands work in
|
|
115
|
-
every mode:
|
|
124
|
+
`/watch` manages watchers in every mode; a bare `/watch` opens the screen in the
|
|
125
|
+
TUI and prints the list everywhere else.
|
|
116
126
|
|
|
117
127
|
| Command | Effect |
|
|
118
128
|
|---|---|
|
|
@@ -123,6 +133,13 @@ every mode:
|
|
|
123
133
|
| `/watch stop <id>` | Stop a watcher. |
|
|
124
134
|
| `/watch clear [all]` | Remove stopped watchers, or every watcher with `all`. |
|
|
125
135
|
|
|
136
|
+
<img src="../../assets/screens/watch.png" alt="The /watch screen: a Watch title rule, a `2 watchers · 1 running · 3 matches` summary, a selected file watcher and a finished command watcher, the focused watcher's path and pattern with its line and match counts, and the key hints" width="100%">
|
|
137
|
+
|
|
138
|
+
A bare `/watch` opens a dock screen listing watchers. `j`/`k` (and the wheel)
|
|
139
|
+
move, `Enter`/`l` opens a watcher's matches, `x`/`K` stops a running watcher
|
|
140
|
+
behind a `y/N` confirm, `d` clears stopped ones behind a `y/N` confirm, `Esc`
|
|
141
|
+
closes, and `g`/`G` and PgUp/PgDn scroll the matches pane.
|
|
142
|
+
|
|
126
143
|
## Behaviour by mode
|
|
127
144
|
|
|
128
145
|
- **tui** — the status chips, the match alert, and the `/watch` dock screen.
|
|
@@ -132,8 +149,9 @@ every mode:
|
|
|
132
149
|
|
|
133
150
|
## Configuration
|
|
134
151
|
|
|
135
|
-
The `watch` section of the shared `bytefly.json
|
|
136
|
-
|
|
152
|
+
The `watch` section of the shared `bytefly.json`; resolution rules in
|
|
153
|
+
[`../configuration.md`](../configuration.md#watch--file-and-command-watchers).
|
|
154
|
+
Every value is clamped; a missing or malformed document is ignored.
|
|
137
155
|
|
|
138
156
|
| Key | Default | Meaning |
|
|
139
157
|
|---|---|---|
|
|
@@ -3,53 +3,57 @@
|
|
|
3
3
|
One extension, two tools, keyless and dependency-free (native `fetch`, no
|
|
4
4
|
`curl`): `web_search` runs general web search through a pluggable provider
|
|
5
5
|
(keyless DuckDuckGo by default; optional SearXNG or Brave), and `web_fetch`
|
|
6
|
-
reads a URL as text. Both are `direct
|
|
7
|
-
|
|
6
|
+
reads a URL as text. Both are `direct`, active on registration, and read the
|
|
7
|
+
`web-access` section of the shared `bytefly.json`.
|
|
8
8
|
|
|
9
|
-
|
|
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
|
+
| Web search | `web_search` | Titles, URLs, snippets, and a direct answer when the provider has one. | Shipped |
|
|
16
|
+
| Page reading | `web_fetch` | A URL's readable text, with links kept as `[text](url)`. | Shipped |
|
|
17
|
+
| Structured bodies | `web_fetch` | A JSON, CSV, or XML body arrives as an aligned table or an indented tree instead of the raw blob. | Unreleased |
|
|
18
|
+
| Write-style fetch | `web_fetch` (`method`, `headers`, `body`) | POST with custom headers and a bounded body, so form and GraphQL endpoints are reachable; a body implies POST. | Shipped |
|
|
19
|
+
| Paged output | `web_fetch`, `startIndex`, `maxChars` | A long page arrives in budgeted slices and names the offset that continues it. | Shipped |
|
|
20
|
+
| Passage search | `web_fetch`, `find`, `contextChars` | Matching passages instead of the whole page, with a note when a cap hid the rest. | Shipped |
|
|
21
|
+
| Private-address refusal | `web_fetch`, `fetch.allowPrivateHosts` | Loopback, private, link-local, and metadata targets are refused on the first request and on every redirect hop. | Shipped |
|
|
22
|
+
| Charset-aware decoding | `web_fetch` | A GBK/GB18030 or `<meta charset>` page reads as text instead of replacement characters. | Shipped |
|
|
23
|
+
| Page cache | `web_fetch`, `refresh`, `fetch.cacheEnabled` | Paging and `find` reuse one fetch, and `refresh: true` bypasses a stale entry. | Shipped |
|
|
24
|
+
| PDF extraction | `web_fetch`, `fetch.pdfEnabled` | A PDF becomes text once the optional `unpdf` package is installed. | Shipped |
|
|
25
|
+
| Provider choice | `search.provider`, `search.endpoint`, `search.apiKeyEnv` | The keyless default works with no configuration; SearXNG or Brave add full result lists. | Shipped |
|
|
26
|
+
| Structured result | `nextIndex`, `matchesTruncated`, `cached` | A caller can continue paging and tell a capped match set from a complete one without parsing the text. | Shipped |
|
|
27
|
+
| Politeness throttle | `minIntervalMs` | Search requests are spaced instead of hammering a provider on a rapid series of calls. | Shipped |
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
```bash
|
|
10
32
|
pi --extension ./extensions/web-access # load just this extension
|
|
11
33
|
pi -e . # load the whole @gavin-hu/my-pi-agent package
|
|
12
34
|
pi install ./ # or install the package
|
|
13
35
|
```
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
```text
|
|
38
|
+
web_search({ query: "pi coding agent" }) # keyless, no configuration
|
|
39
|
+
web_fetch({ url: "https://pi.dev/" }) # the page as readable text
|
|
40
|
+
```
|
|
16
41
|
|
|
17
|
-
|
|
42
|
+
## Tools
|
|
18
43
|
|
|
19
44
|
<img src="../../assets/tools/web_search.png" alt="The web_search tool's call and result: the call line and an indented body with the query and the ranked results" width="100%">
|
|
20
45
|
|
|
21
46
|
<img src="../../assets/tools/web_fetch.png" alt="The web_fetch tool's call and result: the call line and an indented body with the URL, title, and fetched text preview" width="100%">
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- Registers `web_search`, general web search through a pluggable provider:
|
|
26
|
-
keyless DuckDuckGo by default, or a self-hosted SearXNG instance / keyed Brave
|
|
27
|
-
search. Returns titles, URLs, snippets, and a direct answer when available,
|
|
28
|
-
always with no configuration required.
|
|
29
|
-
- Registers `web_fetch`, which fetches a URL (GET or POST) and
|
|
30
|
-
returns readable text with links kept as `[text](url)`.
|
|
31
|
-
- Decodes by the byte-order mark, the declared `Content-Type` charset, or the
|
|
32
|
-
page's own `<meta charset>`, so a GBK/GB18030 page reads as text instead of
|
|
33
|
-
replacement characters.
|
|
34
|
-
- Pages long output and reports the next `startIndex` (`nextIndex` in
|
|
35
|
-
`structuredContent`); `find` returns matching passages instead of the whole
|
|
36
|
-
page, and says when the match cap hid further hits.
|
|
37
|
-
- Re-validates every redirect hop against private/internal addresses, and
|
|
38
|
-
refuses transport headers and oversized bodies.
|
|
39
|
-
- Optionally extracts PDFs (`unpdf`); the package loads lazily and is never
|
|
40
|
-
required.
|
|
41
|
-
- Caches fetched pages in one in-process cache per registration, so paging and
|
|
42
|
-
`find` do not refetch; the cache is cleared on `session_start` and
|
|
43
|
-
`session_shutdown`.
|
|
44
|
-
|
|
45
|
-
## Tools
|
|
48
|
+
Both tools are `direct` (the default) and active on registration; neither needs
|
|
49
|
+
configuration to run.
|
|
46
50
|
|
|
47
51
|
| Tool | What it does |
|
|
48
52
|
|---|---|
|
|
49
53
|
| `web_search` | General web search; keyless by default, SearXNG or Brave optional. |
|
|
50
54
|
| `web_fetch` | Fetch a URL and return readable text, pageable and searchable. |
|
|
51
55
|
|
|
52
|
-
```
|
|
56
|
+
```text
|
|
53
57
|
web_search({ query: "pi coding agent" })
|
|
54
58
|
web_fetch({ url: "https://pi.dev/" })
|
|
55
59
|
web_fetch({ url: "https://pi.dev/", find: ["documentation", "extensions"] })
|
|
@@ -68,7 +72,7 @@ flight it draws a railed `Searching… 8s` or `Fetching example.com… 3s` line,
|
|
|
68
72
|
a finished call appends its duration. A failed page or a host-rejected call
|
|
69
73
|
renders one sanitized `Error:` line in the error role.
|
|
70
74
|
|
|
71
|
-
```
|
|
75
|
+
```text
|
|
72
76
|
web_search "pi coding agent" (max 8)
|
|
73
77
|
|
|
74
78
|
via searxng · 8 results · 2.1s
|
|
@@ -80,7 +84,7 @@ Answer: Pi is a coding agent harness …
|
|
|
80
84
|
+3 more (ctrl+o to expand)
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
```
|
|
87
|
+
```text
|
|
84
88
|
web_fetch https://pi.dev/
|
|
85
89
|
Pi — a coding-agent component · 200 · 12k chars (cached) · 3.4s
|
|
86
90
|
```
|
|
@@ -90,9 +94,10 @@ which URL supplied the text.
|
|
|
90
94
|
|
|
91
95
|
## Configuration
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
and
|
|
97
|
+
The `web-access` section of the shared `bytefly.json`: `search` configures
|
|
98
|
+
`web_search`, `fetch` configures `web_fetch`. Resolution rules are in
|
|
99
|
+
[`../configuration.md#web-access--search-and-fetch`](../configuration.md#web-access--search-and-fetch),
|
|
100
|
+
which also lists every key with its default and accepted values.
|
|
96
101
|
|
|
97
102
|
```jsonc
|
|
98
103
|
// bytefly.json → "web-access"
|
|
@@ -183,7 +188,7 @@ whether it came from an explicit choice, the `auto` rule, or the keyless default
|
|
|
183
188
|
the API-key variable when the provider takes one and no literal `apiKey` is set,
|
|
184
189
|
and whether that variable is present; and, only on an explicit `refresh`, whether
|
|
185
190
|
the configured `endpoint` answers. The key is never reported, only its variable
|
|
186
|
-
name. See [`bytefly`](
|
|
191
|
+
name. See [`bytefly`](./bytefly.md#capabilities) for the report.
|
|
187
192
|
|
|
188
193
|
| Parameter | Type | Notes |
|
|
189
194
|
|---|---|---|
|
|
@@ -199,11 +204,16 @@ web_search({ query: "python list comprehension", maxResults: 3 })
|
|
|
199
204
|
## `web_fetch`
|
|
200
205
|
|
|
201
206
|
GETs (or POSTs) an http(s) URL and returns readable text. HTML becomes
|
|
202
|
-
plain text with links kept as `[text](url)`; JSON
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
+
plain text with links kept as `[text](url)`; a JSON, CSV, or XML body becomes a
|
|
208
|
+
table or an indented tree ([Structured bodies](#structured-bodies)); other textual
|
|
209
|
+
content is returned as-is; PDFs are text-extracted when the optional `unpdf`
|
|
210
|
+
package is installed; other binary content is reported by type and size. Bodies
|
|
211
|
+
are decoded by the
|
|
212
|
+
byte-order mark, the declared `Content-Type` charset, or the page's own
|
|
213
|
+
`<meta charset>`, so a GBK/GB18030 page reads as text instead of replacement
|
|
214
|
+
characters. Redirects are followed manually and **every hop is re-validated**, so
|
|
215
|
+
a public URL cannot redirect into a private address. Loopback, private,
|
|
216
|
+
link-local, and cloud-metadata addresses are refused.
|
|
207
217
|
|
|
208
218
|
| Parameter | Type | Notes |
|
|
209
219
|
|---|---|---|
|
|
@@ -237,6 +247,32 @@ web_fetch({ url: "https://pi.dev/", find: ["documentation", "extensions"] })
|
|
|
237
247
|
web_fetch({ url: "https://api.example/graphql", method: "POST", headers: { "Content-Type": "application/json" }, body: '{"query":"{ viewer { login } }"}' })
|
|
238
248
|
```
|
|
239
249
|
|
|
250
|
+
### Structured bodies
|
|
251
|
+
|
|
252
|
+
A JSON, CSV, or XML body is rendered as text instead of the blob it downloaded: an
|
|
253
|
+
array of records becomes a table with the union of their keys and padded columns,
|
|
254
|
+
an object becomes indented `key: value` lines, comma-separated rows become the same
|
|
255
|
+
table, and XML becomes an indented tree with attributes as `@name`. The declared
|
|
256
|
+
content type decides first; a body that declares nothing usable (`text/plain`) is
|
|
257
|
+
sniffed only when it has a shape — an opening brace, a uniform comma count, a root
|
|
258
|
+
tag — and anything that does not parse keeps its raw form, so a malformed document
|
|
259
|
+
is never made harder to read than it already was.
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
URL: https://api.example/users
|
|
263
|
+
Status: 200 (application/json)
|
|
264
|
+
|
|
265
|
+
id name role
|
|
266
|
+
1 Ada admin
|
|
267
|
+
2 Grace user
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Rendering happens once per fetch, before the result is cached, so `startIndex`,
|
|
271
|
+
`find`, and a second call all read the same string. Two bounds keep it honest: a
|
|
272
|
+
body past `MAX_SOURCE_CHARS` is returned raw, because a paged raw body beats a
|
|
273
|
+
rendered one, and the rendered text stops at `MAX_RENDERED_CHARS` with a note saying
|
|
274
|
+
so — both constants live in `fetch/structured.ts`.
|
|
275
|
+
|
|
240
276
|
### Optional dependencies
|
|
241
277
|
|
|
242
278
|
PDF extraction needs a package that is not installed by default; it is loaded
|
|
@@ -259,6 +295,11 @@ the content type and size.
|
|
|
259
295
|
- **Heuristic extraction**: `web_fetch` strips scripts/styles/nav/footer and
|
|
260
296
|
prefers `<main>`/`<article>`, but is not a full readability engine. PDF
|
|
261
297
|
extraction is optional (`unpdf`) and off unless installed.
|
|
298
|
+
- **A structured body is rendered by a reader, not a parser.** JSON, CSV, and XML
|
|
299
|
+
are read well enough to show a shape — CSV by RFC 4180 quoting, XML by balancing
|
|
300
|
+
tags — but a body it cannot parse keeps its raw form rather than a partial one,
|
|
301
|
+
and the `text/plain` sniff is a heuristic: a page of prose with a uniform comma
|
|
302
|
+
in every line reads as a one-row table.
|
|
262
303
|
- **`find` output is budgeted.** With `maxMatches: 50` and `contextChars: 2000`
|
|
263
304
|
the passages alone would exceed `maxOutputChars`; the result shows the passages
|
|
264
305
|
that fit and names the knob to change.
|
|
@@ -296,11 +337,11 @@ the content type and size.
|
|
|
296
337
|
`config.ts`, `schema.ts`, `types.ts`, `format.ts`, and `tool.ts`, and do not
|
|
297
338
|
import each other; `index.ts` only registers. Shared text and HTML helpers
|
|
298
339
|
live at the top level (`text.ts`, `html.ts`, `transcript.ts`, `http.ts`).
|
|
299
|
-
- **Shared transport.** [`http.ts`](
|
|
340
|
+
- **Shared transport.** [`http.ts`](../../extensions/web-access/http.ts) holds the fetch runner
|
|
300
341
|
(`HttpRunner`, `createFetchRunner`, HTTP error types), used by both halves and
|
|
301
342
|
injectable for tests. It reads the body through a byte-counting stream, so
|
|
302
343
|
`maxBytes` stops a chunked or compressed response before it is buffered whole.
|
|
303
|
-
- **Charset-aware decoding.** [`charset.ts`](
|
|
344
|
+
- **Charset-aware decoding.** [`charset.ts`](../../extensions/web-access/charset.ts) is the one place a
|
|
304
345
|
byte body becomes text: BOM first, then the declared `Content-Type` charset,
|
|
305
346
|
then the page's own `<meta charset>`, then UTF-8. An unknown label falls back
|
|
306
347
|
to UTF-8 instead of failing the fetch.
|
|
@@ -311,9 +352,9 @@ the content type and size.
|
|
|
311
352
|
lazily and falls back to a binary note when it is absent.
|
|
312
353
|
- **Per-hop SSRF validation.** Redirects are followed with `redirect: "manual"`
|
|
313
354
|
under one shared deadline, re-resolving and re-checking each `Location`.
|
|
314
|
-
- **Pluggable search providers.** [`registry.ts`](
|
|
355
|
+
- **Pluggable search providers.** [`registry.ts`](../../extensions/web-access/search/registry.ts) holds a
|
|
315
356
|
value-only provider list (`duckduckgo`, `searxng`, `brave`) and a pure
|
|
316
|
-
`resolveProvider`; [`search.ts`](
|
|
357
|
+
`resolveProvider`; [`search.ts`](../../extensions/web-access/search/search.ts) owns selection, key
|
|
317
358
|
resolution, a per-instance throttle, and error mapping. A provider with no
|
|
318
359
|
answer and no results reports `provider: "none"`.
|
|
319
360
|
- **Find-in-page.** `exact` is case-sensitive, `insensitive` lowercases both
|
|
@@ -328,17 +369,17 @@ the content type and size.
|
|
|
328
369
|
truncation note against `maxChars`, so a paged page always carries its
|
|
329
370
|
`startIndex` hint inside the limit. `formatMatches` applies the same rule to
|
|
330
371
|
`find`: it includes the passages that fit and names the knob that hid the rest.
|
|
331
|
-
- **Code points without a copy.** [`text.ts`](
|
|
372
|
+
- **Code points without a copy.** [`text.ts`](../../extensions/web-access/text.ts) counts and slices by
|
|
332
373
|
walking the string in place, so a multi-megabyte page is never expanded into an
|
|
333
374
|
array of one-character strings on every call.
|
|
334
375
|
- **One JSON boundary per concern.** `search/json.ts` owns `requestJson` (status
|
|
335
376
|
check and parse error) and `collectResults` (title + http(s) URL + de-dupe), so
|
|
336
377
|
the three providers cannot drift on either.
|
|
337
|
-
- **Page cache.** [`fetch/cache.ts`](
|
|
378
|
+
- **Page cache.** [`fetch/cache.ts`](../../extensions/web-access/fetch/cache.ts) is a factory
|
|
338
379
|
(`createPageCache`) holding an LRU/TTL map keyed by requested URL, so paging and
|
|
339
380
|
find are a single fetch; entries evict by count and total bytes, and one
|
|
340
381
|
instance belongs to one registration.
|
|
341
|
-
- **Transcript sanitization and reuse.** [`transcript.ts`](
|
|
382
|
+
- **Transcript sanitization and reuse.** [`transcript.ts`](../../extensions/web-access/transcript.ts)
|
|
342
383
|
holds `oneLine`, shared by both tools to collapse untrusted web text to one
|
|
343
384
|
sanitized, optionally clipped line; the renderers reuse
|
|
344
385
|
`context.lastComponent` instead of allocating each render.
|
|
@@ -357,6 +398,7 @@ the content type and size.
|
|
|
357
398
|
| `search/tool.ts` | `web_search` definition, activation, rendering |
|
|
358
399
|
| `search/provider.ts` | Provider interface (`SearchProvider`, `ProviderContext`) |
|
|
359
400
|
| `search/registry.ts` | Value-only provider list, `resolveProvider`, labels |
|
|
401
|
+
| `search/facts.ts` | What this session resolved to, published for `doctor` (`providerFacts`) |
|
|
360
402
|
| `search/config.ts` | Search config type, defaults, clamping, API-key resolution |
|
|
361
403
|
| `search/schema.ts` | Params/output schema, `resolveRequest()` |
|
|
362
404
|
| `search/search.ts` | Provider resolution, throttle, and outcome mapping |
|
|
@@ -368,10 +410,10 @@ the content type and size.
|
|
|
368
410
|
| `fetch/tool.ts` | `web_fetch` definition, activation, rendering |
|
|
369
411
|
| `fetch/config.ts` | Fetch config type, defaults, clamping |
|
|
370
412
|
| `fetch/schema.ts` | Params/output schema, `resolveRequest()` |
|
|
371
|
-
| `fetch/page.ts` | Fetch + redirect-following + content-type routing |
|
|
413
|
+
| `fetch/page.ts` | Fetch + redirect-following + content-type routing; PDF extraction is injected via `FetchDeps` from the shared `lib/pdf.ts` |
|
|
414
|
+
| `fetch/structured.ts` | JSON/CSV/XML body → readable text, or `undefined` to keep the raw body |
|
|
372
415
|
| `fetch/ssrf.ts` | URL/host validation and private-address blocking |
|
|
373
416
|
| `fetch/extract.ts` | HTML → title + readable text |
|
|
374
|
-
| `fetch/page.ts` | Fetch + redirect-following + content-type routing; PDF extraction is injected via `FetchDeps` from the shared `lib/pdf.ts` |
|
|
375
417
|
| `fetch/find.ts` | Passage search (exact/insensitive/fuzzy) |
|
|
376
418
|
| `fetch/cache.ts` | Per-registration LRU/TTL page cache (`createPageCache`) |
|
|
377
419
|
| `fetch/format.ts`, `fetch/types.ts` | Result text and data types |
|
|
@@ -386,7 +428,8 @@ charset decoding (declared, `<meta>`, and BOM precedence), a streamed body over
|
|
|
386
428
|
the byte cap, redirect re-validation (per-hop SSRF with literal IPs),
|
|
387
429
|
method/header/body validation, the nested config merge and section isolation,
|
|
388
430
|
extraction, formatting/paging through the tool (including a regression test that
|
|
389
|
-
a truncated page keeps its `startIndex` hint),
|
|
431
|
+
a truncated page keeps its `startIndex` hint), structured rendering per format and
|
|
432
|
+
its malformed fallbacks, find and its cap/budget notices,
|
|
390
433
|
the per-registration cache, the SSRF guard, the injected PDF dependency, and both
|
|
391
434
|
tools' registration/execution. Tests inject the runner, throttle, and PDF extractor
|
|
392
435
|
through the tools' dependencies, so no network or PDF library is needed; the
|