@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
|
@@ -11,8 +11,9 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
11
11
|
import { mkdir, link, readdir, rename, rm, rmdir, writeFile } from "node:fs/promises";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
13
|
import { asRecord, readJson } from "../../lib/config.ts";
|
|
14
|
-
import { claimsConflict } from "./
|
|
14
|
+
import { claimsConflict } from "./overlap.ts";
|
|
15
15
|
import type { BusMessage, ClaimConflict, ClaimRecord, SessionRecord } from "./types.ts";
|
|
16
|
+
import { INBOX_DIR_NAME, SESSIONS_DIR_NAME } from "../../lib/session-roster.ts";
|
|
16
17
|
|
|
17
18
|
const DIR_MODE = 0o700;
|
|
18
19
|
const FILE_MODE = 0o600;
|
|
@@ -27,6 +28,8 @@ export interface BusStore {
|
|
|
27
28
|
ensure(): Promise<void>;
|
|
28
29
|
writeSession(record: SessionRecord): Promise<void>;
|
|
29
30
|
sessions(): Promise<SessionRecord[]>;
|
|
31
|
+
/** One session's record, or undefined when it is not on disk. */
|
|
32
|
+
session(id: string): Promise<SessionRecord | undefined>;
|
|
30
33
|
removeSession(id: string): Promise<void>;
|
|
31
34
|
writeMessage(target: string, message: BusMessage): Promise<void>;
|
|
32
35
|
/** Read a recipient's messages without consuming them, oldest first. */
|
|
@@ -44,12 +47,36 @@ export interface BusStore {
|
|
|
44
47
|
releaseAll(owner: string): Promise<string[]>;
|
|
45
48
|
prune(input: {
|
|
46
49
|
now: number;
|
|
50
|
+
/** The sessions known to be live when the caller read the bus. */
|
|
47
51
|
activeIds: ReadonlySet<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Records whose process the caller found gone: they keep their row until their
|
|
54
|
+
* heartbeat ages out, and own no path and no mail in the meantime.
|
|
55
|
+
*/
|
|
56
|
+
goneIds?: ReadonlySet<string>;
|
|
48
57
|
sessionTtlMs: number;
|
|
49
58
|
messageTtlMs: number;
|
|
50
59
|
}): Promise<void>;
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Release each path through `release`, reporting the ones this session did not
|
|
64
|
+
* hold. One copy, because `release` and `leave` answer the same two lists, and a
|
|
65
|
+
* second loop is how the two answers drift.
|
|
66
|
+
*/
|
|
67
|
+
export async function releaseClaimed(
|
|
68
|
+
paths: readonly string[],
|
|
69
|
+
release: (path: string) => Promise<boolean>,
|
|
70
|
+
): Promise<{ released: string[]; notOwned: string[] }> {
|
|
71
|
+
const released: string[] = [];
|
|
72
|
+
const notOwned: string[] = [];
|
|
73
|
+
for (const path of paths) {
|
|
74
|
+
if (await release(path)) released.push(path);
|
|
75
|
+
else notOwned.push(path);
|
|
76
|
+
}
|
|
77
|
+
return { released, notOwned };
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
const SHA = (value: string): string => createHash("sha256").update(value).digest("hex").slice(0, 32);
|
|
54
81
|
|
|
55
82
|
function isSession(value: unknown): value is SessionRecord {
|
|
@@ -63,10 +90,18 @@ function isSession(value: unknown): value is SessionRecord {
|
|
|
63
90
|
typeof entry.repoRoot === "string" &&
|
|
64
91
|
typeof entry.mode === "string" &&
|
|
65
92
|
typeof entry.startedAt === "number" &&
|
|
66
|
-
typeof entry.lastSeen === "number"
|
|
93
|
+
typeof entry.lastSeen === "number" &&
|
|
94
|
+
(entry.startToken === undefined || typeof entry.startToken === "string") &&
|
|
95
|
+
(entry.watching === undefined || isWatchingState(entry.watching))
|
|
67
96
|
);
|
|
68
97
|
}
|
|
69
98
|
|
|
99
|
+
/** The optional listening field, validated only when it is there. */
|
|
100
|
+
function isWatchingState(value: unknown): boolean {
|
|
101
|
+
const entry = asRecord(value);
|
|
102
|
+
return entry !== undefined && typeof entry.armed === "boolean" && typeof entry.wake === "boolean";
|
|
103
|
+
}
|
|
104
|
+
|
|
70
105
|
function isMessage(value: unknown): value is BusMessage {
|
|
71
106
|
const entry = asRecord(value);
|
|
72
107
|
if (!entry) return false;
|
|
@@ -136,8 +171,8 @@ async function dropIfEmpty(dir: string): Promise<void> {
|
|
|
136
171
|
/** Create a store rooted at one repository's bus directory. */
|
|
137
172
|
export function createBusStore(options: BusStoreOptions): BusStore {
|
|
138
173
|
const { dir } = options;
|
|
139
|
-
const sessionsDir = join(dir,
|
|
140
|
-
const inboxRoot = join(dir,
|
|
174
|
+
const sessionsDir = join(dir, SESSIONS_DIR_NAME);
|
|
175
|
+
const inboxRoot = join(dir, INBOX_DIR_NAME);
|
|
141
176
|
const claimsDir = join(dir, "claims");
|
|
142
177
|
|
|
143
178
|
const sessionFile = (id: string): string => join(sessionsDir, `${SHA(id)}.json`);
|
|
@@ -270,6 +305,8 @@ export function createBusStore(options: BusStoreOptions): BusStore {
|
|
|
270
305
|
|
|
271
306
|
sessions: readSessions,
|
|
272
307
|
|
|
308
|
+
session: async (id) => readTyped(sessionFile(id), isSession),
|
|
309
|
+
|
|
273
310
|
async removeSession(id) {
|
|
274
311
|
await rm(sessionFile(id), { force: true });
|
|
275
312
|
await dropIfEmpty(inboxDir(id));
|
|
@@ -305,16 +342,27 @@ export function createBusStore(options: BusStoreOptions): BusStore {
|
|
|
305
342
|
return released;
|
|
306
343
|
},
|
|
307
344
|
|
|
308
|
-
async prune({ now: at, activeIds, sessionTtlMs, messageTtlMs }) {
|
|
345
|
+
async prune({ now: at, activeIds, goneIds, sessionTtlMs, messageTtlMs }) {
|
|
346
|
+
// The caller's set can lag: a peer that joined and claimed since that read is
|
|
347
|
+
// live on disk, and its claims are its own. This read is the answer, so the
|
|
348
|
+
// set only adds to what it found rather than deciding who survives — except for
|
|
349
|
+
// a record whose process is gone, which the caller knows and the file cannot.
|
|
350
|
+
// That record keeps its row until the heartbeat ages out and owns nothing.
|
|
351
|
+
const live = new Set(activeIds);
|
|
352
|
+
const gone = new Set(goneIds ?? []);
|
|
309
353
|
for (const session of await readSessions()) {
|
|
310
354
|
if (at - session.lastSeen > sessionTtlMs) {
|
|
311
355
|
await rm(sessionFile(session.id), { force: true });
|
|
312
356
|
await dropIfEmpty(inboxDir(session.id));
|
|
313
|
-
|
|
357
|
+
live.delete(session.id);
|
|
358
|
+
} else if (gone.has(session.id)) {
|
|
359
|
+
live.delete(session.id);
|
|
360
|
+
} else {
|
|
361
|
+
live.add(session.id);
|
|
314
362
|
}
|
|
315
363
|
}
|
|
316
364
|
for (const claim of await readClaims()) {
|
|
317
|
-
if (!
|
|
365
|
+
if (!live.has(claim.owner)) await rm(claimFile(claim.path), { force: true });
|
|
318
366
|
}
|
|
319
367
|
for (const name of await readdir(inboxRoot).catch(() => [] as string[])) {
|
|
320
368
|
const directory = join(inboxRoot, name);
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* The `session` tool.
|
|
3
3
|
*
|
|
4
4
|
* One action-shaped tool keeps the prompt surface small: status, send, inbox,
|
|
5
|
-
* claim, release, join
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
5
|
+
* claim, release, join. The runtime owns file I/O and lifecycle; this
|
|
6
|
+
* module only validates the action, calls the runtime, and hands the result to
|
|
7
|
+
* the shared presentation module — one collapsed line, or the board's rows on
|
|
8
|
+
* expand.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
@@ -14,16 +14,7 @@ import { Type, type Static } from "typebox";
|
|
|
14
14
|
import { reuseText, toolDetails } from "../../lib/tui/chrome.ts";
|
|
15
15
|
import { errorResult, textResult } from "../../lib/tool-result.ts";
|
|
16
16
|
import { callLine, detailsOrLine } from "../../lib/tui/tool.ts";
|
|
17
|
-
import {
|
|
18
|
-
formatClaim,
|
|
19
|
-
formatInbox,
|
|
20
|
-
formatJoin,
|
|
21
|
-
formatName,
|
|
22
|
-
formatRelease,
|
|
23
|
-
formatSend,
|
|
24
|
-
formatStatus,
|
|
25
|
-
formatWatch,
|
|
26
|
-
} from "./format.ts";
|
|
17
|
+
import { formatClaim, formatInbox, formatJoin, formatRelease, formatSend, formatStatus } from "./format.ts";
|
|
27
18
|
import { normalizeAction, TOOL_NAME } from "./protocol.ts";
|
|
28
19
|
import type { SessionBus } from "./runtime.ts";
|
|
29
20
|
import { collapsedLine, resultSections, SessionResult } from "./result.ts";
|
|
@@ -32,8 +23,8 @@ import type { SessionDetails } from "./types.ts";
|
|
|
32
23
|
export { TOOL_NAME };
|
|
33
24
|
|
|
34
25
|
const SessionParams = Type.Object({
|
|
35
|
-
action: StringEnum(["status", "send", "inbox", "claim", "release", "join"
|
|
36
|
-
description: "status, send, inbox, claim, release,
|
|
26
|
+
action: StringEnum(["status", "send", "inbox", "claim", "release", "join"] as const, {
|
|
27
|
+
description: "status, send, inbox, claim, release, or join.",
|
|
37
28
|
}),
|
|
38
29
|
to: Type.Optional(Type.String({ description: "Target session label/id, or * for an event broadcast." })),
|
|
39
30
|
text: Type.Optional(Type.String({ description: "Message text, at most 4000 characters." })),
|
|
@@ -50,19 +41,16 @@ const SessionParams = Type.Object({
|
|
|
50
41
|
),
|
|
51
42
|
wake: Type.Optional(
|
|
52
43
|
Type.Boolean({
|
|
53
|
-
description:
|
|
54
|
-
"With watch: set the mode — true raises one turn per burst, false waits for the next turn, omitted keeps it.",
|
|
44
|
+
description: "With join: mail raises one turn per burst once the agent is idle; omitted waits for the next turn.",
|
|
55
45
|
}),
|
|
56
46
|
),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
description: "With join/leave: the split's name — the plan file name without `.md`.",
|
|
47
|
+
listen: Type.Optional(
|
|
48
|
+
Type.Boolean({
|
|
49
|
+
description: "With join: false joins without listening, so mail waits in the inbox until `inbox` reads it.",
|
|
61
50
|
}),
|
|
62
51
|
),
|
|
63
|
-
unit: Type.Optional(Type.String({ description: "With join/leave: the unit to claim or release, e.g. `core`." })),
|
|
64
52
|
label: Type.Optional(
|
|
65
|
-
Type.String({ description: "With
|
|
53
|
+
Type.String({ description: "With join: what other sessions call this one, e.g. `billing-lead`." }),
|
|
66
54
|
),
|
|
67
55
|
});
|
|
68
56
|
|
|
@@ -74,19 +62,14 @@ type SessionArgs = Static<typeof SessionParams>;
|
|
|
74
62
|
* identity of the call.
|
|
75
63
|
*/
|
|
76
64
|
function callSubject(action: string, args: Record<string, unknown>): string {
|
|
77
|
-
if (action === "
|
|
65
|
+
if (action === "join") {
|
|
78
66
|
const label = typeof args.label === "string" ? args.label.trim() : "";
|
|
79
|
-
return label ? `
|
|
67
|
+
return label ? `join ${label}` : "join";
|
|
80
68
|
}
|
|
81
69
|
if (action === "send") {
|
|
82
70
|
const to = typeof args.to === "string" ? args.to : "";
|
|
83
71
|
return to ? `send ${to}` : "send";
|
|
84
72
|
}
|
|
85
|
-
if (action === "join" || action === "leave") {
|
|
86
|
-
const unit = typeof args.unit === "string" ? args.unit : "";
|
|
87
|
-
const team = typeof args.team === "string" ? args.team : "";
|
|
88
|
-
return [action, unit, team && !unit ? team : ""].filter(Boolean).join(" ");
|
|
89
|
-
}
|
|
90
73
|
if (action !== "claim" && action !== "release") return action;
|
|
91
74
|
const paths = Array.isArray(args.paths) ? args.paths.filter((path): path is string => typeof path === "string") : [];
|
|
92
75
|
const [first, ...rest] = paths;
|
|
@@ -99,16 +82,14 @@ export function registerTools(pi: ExtensionAPI, bus: SessionBus): void {
|
|
|
99
82
|
name: TOOL_NAME,
|
|
100
83
|
label: "Session bus",
|
|
101
84
|
description:
|
|
102
|
-
"Coordinate parallel Pi sessions on this repository. Actions: `status` (sessions, unread, claims
|
|
85
|
+
"Coordinate parallel Pi sessions on this repository. Actions: `status` (sessions, unread, claims), " +
|
|
103
86
|
"`send` (to a label/id, or `*` for an event), `inbox` (consume messages), `claim`/`release` (paths), " +
|
|
104
|
-
"`join
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
promptSnippet: "Coordinate parallel sessions: status, send, inbox, claim, release, join, leave, watch, name.",
|
|
87
|
+
"`join` (get on the bus under a label, and listen for mail unless `listen: false`; `wake` raises a turn). " +
|
|
88
|
+
"Delivery is pull-based unless this session joined and is listening.",
|
|
89
|
+
promptSnippet: "Coordinate parallel sessions: status, send, inbox, claim, release, join.",
|
|
108
90
|
promptGuidelines: [
|
|
109
|
-
"Check `session inbox` when starting
|
|
91
|
+
"Check `session inbox` when starting a coordinated turn.",
|
|
110
92
|
"Claim the paths you will edit before changing them; release them when done.",
|
|
111
|
-
"When a plan was approved with a split, `join` its unit once at the start of the work and `leave` it when merged.",
|
|
112
93
|
'Send `kind: "event"` for contract, build, or test changes another session needs.',
|
|
113
94
|
"Never put credentials or secrets in a session-bus message.",
|
|
114
95
|
],
|
|
@@ -161,38 +142,22 @@ export function registerTools(pi: ExtensionAPI, bus: SessionBus): void {
|
|
|
161
142
|
});
|
|
162
143
|
}
|
|
163
144
|
case "join": {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
claimed: result.claimed,
|
|
170
|
-
conflicts: result.conflicts,
|
|
145
|
+
const label = typeof args.label === "string" ? args.label.trim() : "";
|
|
146
|
+
const joined = await bus.join({
|
|
147
|
+
...(label === "" ? {} : { label }),
|
|
148
|
+
...(args.wake === undefined ? {} : { wake: args.wake }),
|
|
149
|
+
...(args.listen === undefined ? {} : { listen: args.listen }),
|
|
171
150
|
});
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
151
|
+
// A label is the bus's name and Pi's session name in one act, so the footer,
|
|
152
|
+
// `/resume`, and the record a peer addresses agree. A bare join derives its
|
|
153
|
+
// label from the name Pi already shows and must not rename it back.
|
|
154
|
+
if (label !== "") pi.setSessionName(joined.label);
|
|
155
|
+
return textResult(formatJoin(joined.label, joined.watching), {
|
|
176
156
|
action,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
...(args.team === undefined ? {} : { team: args.team }),
|
|
180
|
-
...(args.unit === undefined ? {} : { unit: args.unit }),
|
|
157
|
+
label: joined.label,
|
|
158
|
+
watching: joined.watching,
|
|
181
159
|
});
|
|
182
160
|
}
|
|
183
|
-
case "watch": {
|
|
184
|
-
const watching = await bus.watch({ wake: args.wake, stop: args.stop });
|
|
185
|
-
return textResult(formatWatch(watching), { action, watching });
|
|
186
|
-
}
|
|
187
|
-
case "name": {
|
|
188
|
-
const label = typeof args.label === "string" ? args.label.trim() : "";
|
|
189
|
-
if (!label) return errorResult(action, 'name requires a label, e.g. "billing-lead".');
|
|
190
|
-
const named = await bus.rename(label);
|
|
191
|
-
// The bus's label and Pi's session name are one identity: the footer,
|
|
192
|
-
// `/resume`, and the record a peer addresses have to agree.
|
|
193
|
-
pi.setSessionName(named);
|
|
194
|
-
return textResult(formatName(named), { action, label: named });
|
|
195
|
-
}
|
|
196
161
|
}
|
|
197
162
|
} catch (error) {
|
|
198
163
|
return errorResult(action, error instanceof Error ? error.message : String(error));
|
|
@@ -17,7 +17,7 @@ import { type DetailContents, DockListScreen } from "../../lib/tui/dock-screen.t
|
|
|
17
17
|
import { SEPARATORS } from "../../lib/tui/glyphs.ts";
|
|
18
18
|
import { keyValueRow } from "../../lib/tui/rows.ts";
|
|
19
19
|
import { paint } from "../../lib/tui/tokens.ts";
|
|
20
|
-
import {
|
|
20
|
+
import { idSuffix } from "./format.ts";
|
|
21
21
|
import {
|
|
22
22
|
type SessionRow,
|
|
23
23
|
claimRowLine,
|
|
@@ -48,7 +48,7 @@ const SCOPE_TITLE: Readonly<Record<BoardScope, string>> = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const SCOPE_EMPTY: Readonly<Record<BoardScope, string>> = {
|
|
51
|
-
sessions: "No sessions on this bus.",
|
|
51
|
+
sessions: "No sessions on this bus yet. Press w to join and listen.",
|
|
52
52
|
inbox: "No unread mail.",
|
|
53
53
|
claims: "No claims.",
|
|
54
54
|
};
|
|
@@ -76,7 +76,7 @@ function paneFacts(theme: Theme, row: SessionRow, now: number, width: number): s
|
|
|
76
76
|
const labelWidth = 10;
|
|
77
77
|
return [
|
|
78
78
|
keyValueRow(theme, width, { label: "mode", labelWidth, value: sanitizeLine(row.session.mode) }),
|
|
79
|
-
keyValueRow(theme, width, { label: "id", labelWidth, value:
|
|
79
|
+
keyValueRow(theme, width, { label: "id", labelWidth, value: idSuffix(row.session.id) }),
|
|
80
80
|
keyValueRow(theme, width, {
|
|
81
81
|
label: "last seen",
|
|
82
82
|
labelWidth,
|
|
@@ -96,7 +96,7 @@ function boardKey(board: BoardSnapshot | undefined): string {
|
|
|
96
96
|
return [
|
|
97
97
|
...board.status.sessions.map(
|
|
98
98
|
(session) =>
|
|
99
|
-
`${session.id}:${session.label}:${session.unread}:${session.lastSeen}:${session.mode}:${session.cwd}`,
|
|
99
|
+
`${session.id}:${session.label}:${session.unread}:${session.lastSeen}:${session.mode}:${session.cwd}:${session.state}`,
|
|
100
100
|
),
|
|
101
101
|
...board.status.claims.map((claim) => `${claim.path}:${claim.owner}:${claim.ownerLabel}:${claim.at}`),
|
|
102
102
|
board.status.watching ? `${board.status.watching.armed}:${board.status.watching.wake}` : "",
|
|
@@ -229,7 +229,7 @@ export class SessionListComponent extends DockListScreen<BoardRow> {
|
|
|
229
229
|
switch (this.scope) {
|
|
230
230
|
case "sessions": {
|
|
231
231
|
const sessions = rows.flatMap((row) => (row.kind === "session" ? [row.row] : []));
|
|
232
|
-
return summaryCells(this.theme, sessions, watching);
|
|
232
|
+
return summaryCells(this.theme, sessions, watching, this.board?.status.onBus !== false);
|
|
233
233
|
}
|
|
234
234
|
case "inbox": {
|
|
235
235
|
const cells = [paint(this.theme, "meta", count(rows.length, "unread message"))];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* `store.ts` and the lifecycle in `runtime.ts`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { RosterState } from "../../lib/session-roster.ts";
|
|
8
9
|
import type { MessageKind, SessionMode } from "./protocol.ts";
|
|
9
10
|
|
|
10
11
|
/** One registered Pi session. */
|
|
@@ -12,6 +13,14 @@ export interface SessionRecord {
|
|
|
12
13
|
id: string;
|
|
13
14
|
label: string;
|
|
14
15
|
pid: number;
|
|
16
|
+
/**
|
|
17
|
+
* That process's start time, when the host can name one.
|
|
18
|
+
*
|
|
19
|
+
* It is what tells the record's process apart from an unrelated one that has since
|
|
20
|
+
* been given the same pid, so `gone` does not have to wait for the heartbeat to age.
|
|
21
|
+
* Absent on a platform without `ps`, and on a record written before this existed.
|
|
22
|
+
*/
|
|
23
|
+
startToken?: string;
|
|
15
24
|
/** Working directory of the session; the worktree root when there is one. */
|
|
16
25
|
cwd: string;
|
|
17
26
|
/** Repository root the session belongs to, for relative claims. */
|
|
@@ -20,6 +29,14 @@ export interface SessionRecord {
|
|
|
20
29
|
mode: SessionMode;
|
|
21
30
|
startedAt: number;
|
|
22
31
|
lastSeen: number;
|
|
32
|
+
/**
|
|
33
|
+
* Whether this session is listening for mail, written when it joins.
|
|
34
|
+
*
|
|
35
|
+
* Absent means "not listening": a record written before this existed and a session
|
|
36
|
+
* that stopped listening read the same way, which is the honest answer for a peer
|
|
37
|
+
* deciding whether mail will reach it.
|
|
38
|
+
*/
|
|
39
|
+
watching?: { armed: boolean; wake: boolean };
|
|
23
40
|
}
|
|
24
41
|
|
|
25
42
|
/** One message waiting in a recipient's inbox. */
|
|
@@ -47,15 +64,10 @@ export interface ClaimRecord {
|
|
|
47
64
|
/** What `session status` returns. */
|
|
48
65
|
export interface SessionStatus {
|
|
49
66
|
me: { id: string; label: string };
|
|
50
|
-
|
|
67
|
+
/** False while this session has only read the bus: it joins by naming itself. */
|
|
68
|
+
onBus: boolean;
|
|
69
|
+
sessions: Array<SessionRecord & { unread: number; state: RosterState }>;
|
|
51
70
|
claims: ClaimRecord[];
|
|
52
|
-
/** The newest split in this repository, and who holds each unit, on an explicit status call. */
|
|
53
|
-
units?: {
|
|
54
|
-
team: string;
|
|
55
|
-
joined: number;
|
|
56
|
-
total: number;
|
|
57
|
-
rows: Array<{ unit: string; lead?: boolean; holder?: string }>;
|
|
58
|
-
};
|
|
59
71
|
/** Present only when this session armed its inbox watch. */
|
|
60
72
|
watching?: { armed: boolean; wake: boolean };
|
|
61
73
|
}
|
|
@@ -78,10 +90,7 @@ export interface BoardSnapshot {
|
|
|
78
90
|
|
|
79
91
|
/** Structured result carried in the `session` tool's details. */
|
|
80
92
|
export interface SessionDetails {
|
|
81
|
-
action: "status" | "send" | "inbox" | "claim" | "release" | "join"
|
|
82
|
-
/** With join/leave: the split and the unit, so a resumed transcript still says which. */
|
|
83
|
-
team?: string;
|
|
84
|
-
unit?: string;
|
|
93
|
+
action: "status" | "send" | "inbox" | "claim" | "release" | "join";
|
|
85
94
|
status?: SessionStatus;
|
|
86
95
|
delivered?: string[];
|
|
87
96
|
messageId?: string;
|
|
@@ -90,9 +99,9 @@ export interface SessionDetails {
|
|
|
90
99
|
conflicts?: ClaimConflict[];
|
|
91
100
|
released?: string[];
|
|
92
101
|
notOwned?: string[];
|
|
93
|
-
/** Whether the inbox watch is on, and whether it raises a turn. */
|
|
102
|
+
/** Whether the inbox watch is on, and whether it raises a turn; `join` reports it. */
|
|
94
103
|
watching?: { armed: boolean; wake: boolean };
|
|
95
|
-
/** With
|
|
104
|
+
/** With join: the label the bus settled on, which is what this session is called. */
|
|
96
105
|
label?: string;
|
|
97
106
|
error?: string;
|
|
98
107
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import * as fs from "node:fs";
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
19
|
+
import { READ_DOC_TOOL, WEB_FETCH_TOOL, WEB_SEARCH_TOOL } from "../../lib/tool-names.ts";
|
|
19
20
|
|
|
20
21
|
/** Where an agent definition came from. */
|
|
21
22
|
export type AgentSource = "builtin" | "user" | "project";
|
|
@@ -44,7 +45,7 @@ export const BUILTIN_AGENTS: readonly AgentConfig[] = [
|
|
|
44
45
|
{
|
|
45
46
|
name: "explorer",
|
|
46
47
|
description: "Fast codebase recon that returns compressed context for handoff to other agents",
|
|
47
|
-
tools: ["read", "grep", "find", "ls"],
|
|
48
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL],
|
|
48
49
|
source: "builtin",
|
|
49
50
|
systemPrompt: `You are an explorer. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
|
|
50
51
|
|
|
@@ -94,7 +95,7 @@ Which file to look at first and why.`,
|
|
|
94
95
|
name: "planner",
|
|
95
96
|
description:
|
|
96
97
|
"Delegated implementation planning in an isolated context (headless/chain; use plan mode for interactive planning)",
|
|
97
|
-
tools: ["read", "grep", "find", "ls"],
|
|
98
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL],
|
|
98
99
|
source: "builtin",
|
|
99
100
|
systemPrompt: `You are a delegated planning specialist working in an isolated context. The caller hands you planning when it wants a plan produced without adding exploration to its own transcript — for example an explorer → planner → worker chain, or a headless run.
|
|
100
101
|
|
|
@@ -134,7 +135,7 @@ Keep the plan concrete. The worker agent will execute it verbatim.`,
|
|
|
134
135
|
{
|
|
135
136
|
name: "reviewer",
|
|
136
137
|
description: "Code review specialist for quality and security analysis",
|
|
137
|
-
tools: ["read", "grep", "find", "ls", "bash"],
|
|
138
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
138
139
|
source: "builtin",
|
|
139
140
|
systemPrompt: `You are a senior code reviewer. Analyze code for quality, security, and maintainability.
|
|
140
141
|
|
|
@@ -168,7 +169,7 @@ Be specific with file paths and line numbers.`,
|
|
|
168
169
|
{
|
|
169
170
|
name: "worker",
|
|
170
171
|
description: "General-purpose subagent with full capabilities and an isolated context window",
|
|
171
|
-
tools: ["read", "write", "edit", "bash", "grep", "find", "ls"],
|
|
172
|
+
tools: ["read", "write", "edit", "bash", "grep", "find", "ls", READ_DOC_TOOL],
|
|
172
173
|
source: "builtin",
|
|
173
174
|
systemPrompt: `You are a worker agent with full capabilities. You operate in an isolated context window to handle delegated tasks without polluting the main conversation.
|
|
174
175
|
|
|
@@ -192,7 +193,7 @@ If handing off to another agent (e.g. reviewer), include:
|
|
|
192
193
|
{
|
|
193
194
|
name: "researcher",
|
|
194
195
|
description: "Web research specialist that returns sourced findings on external questions",
|
|
195
|
-
tools: ["read", "grep", "find", "ls",
|
|
196
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, WEB_SEARCH_TOOL, WEB_FETCH_TOOL],
|
|
196
197
|
source: "builtin",
|
|
197
198
|
systemPrompt: `You are a research specialist. You investigate questions using web_search and web_fetch, then return findings another agent can act on.
|
|
198
199
|
|
|
@@ -228,7 +229,7 @@ What the caller should do with this, in 2-3 sentences.`,
|
|
|
228
229
|
{
|
|
229
230
|
name: "tester",
|
|
230
231
|
description: "Writes or updates tests for a change and runs them",
|
|
231
|
-
tools: ["read", "write", "edit", "grep", "find", "ls", "bash"],
|
|
232
|
+
tools: ["read", "write", "edit", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
232
233
|
source: "builtin",
|
|
233
234
|
systemPrompt: `You are a test engineer. You add or update tests for a change and run them.
|
|
234
235
|
|
|
@@ -260,7 +261,7 @@ What is still untested and why.`,
|
|
|
260
261
|
{
|
|
261
262
|
name: "debugger",
|
|
262
263
|
description: "Root-cause analysis for a failing test or bug, read-only plus running tests",
|
|
263
|
-
tools: ["read", "grep", "find", "ls", "bash"],
|
|
264
|
+
tools: ["read", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
264
265
|
source: "builtin",
|
|
265
266
|
systemPrompt: `You are a debugging specialist. You find the root cause of a failure from evidence, not guesses.
|
|
266
267
|
|
|
@@ -295,7 +296,7 @@ What else the bug or the fix could affect.`,
|
|
|
295
296
|
{
|
|
296
297
|
name: "documenter",
|
|
297
298
|
description: "Updates docs, READMEs, and changelog entries to match the code",
|
|
298
|
-
tools: ["read", "write", "edit", "grep", "find", "ls", "bash"],
|
|
299
|
+
tools: ["read", "write", "edit", "grep", "find", "ls", READ_DOC_TOOL, "bash"],
|
|
299
300
|
source: "builtin",
|
|
300
301
|
systemPrompt: `You are a documentation specialist. You keep docs accurate and consistent with the code.
|
|
301
302
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { planModeRefusal } from "../../lib/plan-entry.ts";
|
|
10
11
|
import { isPlanModeEnabled } from "../../lib/plan-state.ts";
|
|
11
12
|
import { withRailsSuppressed } from "../../lib/rails.ts";
|
|
12
13
|
import type { BackgroundRuntime } from "./background.ts";
|
|
@@ -20,7 +21,7 @@ async function openAgents(
|
|
|
20
21
|
ctx: ExtensionCommandContext,
|
|
21
22
|
): Promise<void> {
|
|
22
23
|
if (isPlanModeEnabled(ctx.sessionManager.getBranch())) {
|
|
23
|
-
ctx.ui.notify("
|
|
24
|
+
ctx.ui.notify(planModeRefusal("managing background runs"), "warning");
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* import for invocation concerns.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { WORKSPACE_READERS } from "../../lib/policy.ts";
|
|
10
|
+
import { WEB_FETCH_TOOL, WEB_SEARCH_TOOL } from "../../lib/tool-names.ts";
|
|
9
11
|
import type { AgentConfig } from "./agents.ts";
|
|
10
12
|
import type { DispatchDefaults } from "./types.ts";
|
|
11
13
|
|
|
12
14
|
export { getPiInvocation, type PiInvocation } from "../../lib/pi-invocation.ts";
|
|
13
15
|
|
|
14
|
-
/** Tools a read-only delegation may use: structured readers and web readers only. */
|
|
15
|
-
export const READ_ONLY_TOOLS = [
|
|
16
|
+
/** Tools a read-only delegation may use: structured readers, documents, and web readers only. */
|
|
17
|
+
export const READ_ONLY_TOOLS = [...WORKSPACE_READERS, WEB_SEARCH_TOOL, WEB_FETCH_TOOL] as const;
|
|
16
18
|
|
|
17
19
|
/** Appended to a read-only agent's system prompt; the `--tools` list is the real gate. */
|
|
18
20
|
export const READ_ONLY_NOTE =
|