@fradser/pi-agent-teams 0.8.2 → 0.9.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/.memory/MEMORY.md +1 -1
- package/.memory/follow-up-queue.md +8 -11
- package/.memory/team-centric-design.md +18 -14
- package/README.md +119 -161
- package/package.json +2 -2
- package/references/agent-roles.md +57 -12
- package/src/activity.ts +12 -33
- package/src/agent-actions.ts +101 -0
- package/src/agents.ts +1 -6
- package/src/guidance.ts +114 -199
- package/src/index.ts +117 -101
- package/src/leader-reports.ts +92 -0
- package/src/recipient.ts +44 -0
- package/src/spawner.ts +293 -38
- package/src/state.ts +250 -69
- package/src/statefile.ts +74 -31
- package/src/team-machine.ts +984 -424
- package/src/tool-copy.ts +374 -0
- package/src/tool-render.ts +47 -22
- package/src/tools.ts +182 -160
- package/src/types.ts +104 -65
- package/src/ui.ts +32 -56
- package/src/work-context.ts +88 -0
- package/src/worker-tools.ts +4 -0
- package/src/worker.ts +251 -184
- package/src/follow-up-queue.ts +0 -301
package/.memory/MEMORY.md
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
- `openai_teammate_api_reference.md` — OpenAI external Teammate / Workspace Agents API reference (Workspace Agents API, Files API, file search, vector stores, Threads API). Reference only, not for Pi extension usage guidance.
|
|
4
4
|
|
|
5
5
|
- `team-centric-design.md` — Agent Teams current architecture: named resident RPC teammates, shared task board with atomic self-claim, direct P2P inboxes, verify-gated completion, harness-driven wake-ups. Supersedes the deleted run-centric DAG design.
|
|
6
|
-
- `follow-up-queue.md` —
|
|
6
|
+
- `follow-up-queue.md` — Teammate report steering handoff: hand reports immediately to Pi's steering channel without an internal serialized follow-up queue; delivers at safe tool boundaries.
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: follow-up-queue
|
|
3
|
-
description: Agent Teams
|
|
3
|
+
description: Agent Teams hands teammate reports immediately to Pi's steering channel without an internal serialized follow-up queue
|
|
4
4
|
type: project
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Why
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
A native follow-up still runs only after the current tool/steering loop can naturally finish; it does not interrupt an in-flight tool or an unbounded leader loop. The leader guidance therefore forbids sleep-based polling, and urgent interruption needs an explicit steer/abort design rather than changing ordinary reports.
|
|
9
|
+
Using `deliverAs: "followUp"` delayed teammate reports until the entire leader tool-calling turn finished, and an internal serialized queue holding subsequent reports until `agent_settled` meant multiple reports backlogged during active leader work and then flooded the leader across subsequent turns. Additionally, worker reports need to arrive promptly when the leader is working, at the next safe tool boundary, rather than waiting for the entire run to settle.
|
|
12
10
|
|
|
13
11
|
## How to apply
|
|
14
12
|
|
|
15
|
-
- `packages/agent-teams/src/
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Keep BDD coverage in `features/agent-teams.feature` and the busy-leader serialization regression in `tests/test_teammate_package.py`.
|
|
13
|
+
- `packages/agent-teams/src/leader-reports.ts` provides report types, envelopes, and display grouping; the internal `FollowUpQueue` is removed.
|
|
14
|
+
- Reports from `team-machine.ts` are handed directly to `leaderPi.sendMessage(..., { triggerTurn: true, deliverAs: "steer" })`.
|
|
15
|
+
- Working leaders receive reports at the next safe tool boundary without interrupting in-flight tools; idle leaders wake immediately.
|
|
16
|
+
- The leader guidance emphasizes that workers execute assignments autonomously without progress nagging; leader steering is reserved for newly discovered information that affects the worker's assignment.
|
|
17
|
+
- Silence is console telemetry only; no heartbeat or stall notices reach the leader. Provider hangs surface through the terminal close-path diagnostic (repo ADR 0002).
|
|
18
|
+
- Keep BDD coverage in `features/agent-teams.feature` and native delivery regressions in `tests/test_immediate_reports.py`.
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Agent Teams
|
|
2
|
+
name: agent-teams-final-surface
|
|
3
|
+
description: Final Agent Teams public surface and lifecycle rules
|
|
4
4
|
type: project
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**Why:**
|
|
10
|
-
Exploratory / competitive-hypothesis work (multi-hypothesis debugging, three-way review with cross-challenge) cannot be represented cleanly by a static DAG because workers must exchange findings mid-task. One-step delegation avoids the old registry ceremony: `teammate_spawn(name, agent, prompt?)` carries the kickoff task and the board is optional. A later grilling decision identified tool count, conceptual overlap, and parameter breadth as separate problems: the surface was reduced from ten names to seven by making `send_message` the one addressed message primitive, registering `task_list` once for both sides, and moving model/worktree from spawn overrides into agent frontmatter.
|
|
7
|
+
**Why:** Agent Teams uses one explicit coordination surface so Work authority,
|
|
8
|
+
communication, and resident lifecycle cannot be confused.
|
|
11
9
|
|
|
12
10
|
**How to apply:**
|
|
13
|
-
1. **Seven tool names**: leader gets `teammate_spawn(name, agent, prompt?)`, `teammate_shutdown(name)`, `send_message(to, message, status?)`, `task_create`, and shared `task_list`; workers get the same `send_message`, shared `task_list`, `task_claim`, and `task_submit`. There is no `teammate_message`, cancel/retry/run/fanout, or polling tool.
|
|
14
|
-
2. **Unified messaging**: `send_message(to="leader", message, status?)` is a worker report written to the validated outbox; `send_message(to="<peer>", message)` writes the recipient inbox directly; leader-addressed teammate messages route through a running control stream or idle wake queue. Synchronous routing outcomes are deliberately narrow: `steered` means the active control stream accepted the write, while `queued` means the harness still owns delivery into a recipient turn. Peer inbox and leader outbox writes are therefore queued, not delivered; no routing label claims the recipient read or processed the message. `status` is invalid except for `to="leader"`. The first non-empty message line is the console title.
|
|
15
|
-
3. **Declarative role attributes**: agent frontmatter carries `model`, optional `verify`, and boolean `worktree`.Definitions live in one project directory with filename-based scopes: `<name>.local.md` is project-local (personal, never committed) and dedupes against its `<name>.md` counterpart (project scope, git-managed, team-shared); user-level sits in `~/.pi/agent/agents`; precedence is project-local > project > user > bundled and `resolveAgent` exposes scope + gitManaged for provenance. Spawn has no cwd/model/worktree override; create a role variant instead. `worktree: true` gives every instance of that role a dedicated Git worktree and diff capture at shutdown.
|
|
16
|
-
4. **Residents/wake-ups (constitution: detect and notify, never terminate)**: long-lived RPC children, 8-process session cap; wake-up sequences have NO turn-count or duration caps — no configuration may automatically terminate a working teammate. Turn counts and silence durations are telemetry only. The 500ms harness poll drains outboxes, applies intents, routes inboxes, wakes only for queued deliveries or a paced claimable-task notice, and runs the stall heartbeat: 30 minutes without any RPC output (`PI_TEAMMATE_STALL_NOTICE_MS`, 0 disables) sends the leader one actionable notice per silence episode — that notice is the LAST automatic action; continuing, steering, shutting down, or respawning a context-carrying successor (original kickoff + mailbox reports + board claims + liveText tail) belongs to the leader model alone. Any stream event or prompt delivery resets `lastOutputAt` and clears `stallNoticeSentAt`. Stall diagnostics render independently as `[agent] event` health notices and never decorate a `[message]` routing row.
|
|
17
|
-
5. **Board**: only leader writes `~/.pi/agent/tasks/<sessionKey>/board.json`; workers use exclusive-create claim/submission marker files. Task completion uses effective task verify or agent verify; failed verification returns the reviewer's findings to the claimer. The board persists across restarts; runtime state does not. `task_create` never spawns a teammate: it immediately offers newly claimable work to existing idle teammates and reports whether anyone was notified; with no living teammates it tells the leader to call `teammate_spawn`. Boards are keyed by the Pi session file (or cwd without one), so fresh sessions do not import another session's tasks unless they resume the same board directory.
|
|
18
|
-
6. **Reliability/UI**: per-spawn identity validation, event/message dedup, crash releases claimed tasks, role worktree diff at shutdown. Widget above the input box shows ONLY working/starting teammates as spinner rows with live activity and hides when nobody is working; idle/stopped teammates live only in the /teammate console roster; spawn prints a legacy-style `[agent] started · @name · task` transcript line; `/teammate` offers roster/board pages and message/task detail.
|
|
19
|
-
7. **Node-native TS**: relative imports use explicit `.ts` extensions (root tsconfig permits them), allowing node to strip-types modules in package tests.
|
|
20
11
|
|
|
21
|
-
|
|
12
|
+
1. Public model tools are `agent`, `work`, and `agent_event`; `/agent-teams`
|
|
13
|
+
is the human management surface.
|
|
14
|
+
2. `agent` uses strict `delegate`, `start`, `inspect`, and exact-session `stop`
|
|
15
|
+
actions. Session handles include the resident and spawn incarnation.
|
|
16
|
+
3. `work` owns create/list/assign/claim/submit/release/completed-only reopen/
|
|
17
|
+
supersede. Claim and submit are marker intents; the single-writer harness owns
|
|
18
|
+
acceptance and verification.
|
|
19
|
+
4. `agent_event` is communication-only with `inform` or `request`. It never
|
|
20
|
+
completes, releases, reopens, or reassigns Work.
|
|
21
|
+
5. Worker capability grants contain only `agent_event` and `work` plus requested
|
|
22
|
+
Pi built-ins. Verification freezes execution and archives deferred mail as Work
|
|
23
|
+
history.
|
|
24
|
+
6. Incompatible persisted runtime snapshots fail explicitly; no silent migration
|
|
25
|
+
or legacy-tool fallback is allowed.
|
package/README.md
CHANGED
|
@@ -1,179 +1,137 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fradser/pi-agent-teams
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Agent Teams provides one final coordination surface for Pi:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
The message surface is deliberately singular: `send_message` is the only messaging primitive. Workers report with `to: "leader"`; teammates address peers by name; the leader addresses teammates by name. The routing implementation varies by destination (outbox, inbox, or control stream), but callers use one schema.
|
|
12
|
-
|
|
13
|
-
## Install
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
pi install /path/to/pi-packages/packages/agent-teams
|
|
5
|
+
```text
|
|
6
|
+
agent
|
|
7
|
+
work
|
|
8
|
+
agent_event
|
|
9
|
+
/agent-teams
|
|
17
10
|
```
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Agents Are Declarative Files
|
|
12
|
+
`agent` manages Agent sessions through strict actions:
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
description: Read-only security reviewer in an isolated worktree
|
|
29
|
-
tools: read,bash
|
|
30
|
-
model: provider/model # optional; "inherit" = the leader's current model at spawn time
|
|
31
|
-
verify: review prompt # optional role-default gate; a fresh reviewer answers VERDICT: PASS/FAIL
|
|
32
|
-
worktree: true # optional role-default Git isolation
|
|
33
|
-
---
|
|
34
|
-
Review the assigned scope for exploitable security problems. Do not edit files.
|
|
14
|
+
```ts
|
|
15
|
+
agent({ action: "delegate", name: "reviewer", prompt: "Audit authentication" })
|
|
16
|
+
agent({ action: "start", name: "reviewer" })
|
|
17
|
+
agent({ action: "inspect", name: "reviewer", session: "session:reviewer-...:spawn-..." })
|
|
18
|
+
agent({ action: "stop", session: "session:reviewer-...:spawn-..." })
|
|
35
19
|
```
|
|
36
20
|
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(definition anatomy, archetype axes, invariants), registers it, and then spawns.
|
|
53
|
-
The role is not written to disk or reused in a later session unless the user
|
|
54
|
-
explicitly requests persistence; only then may the leader set `definition.persist`
|
|
55
|
-
and choose a project or project-local scope.
|
|
56
|
-
|
|
57
|
-
| Scope | Location | Git semantics |
|
|
58
|
-
|---|---|---|
|
|
59
|
-
| user | `~/.pi/agent/agents/*.md` | system-level, never committed |
|
|
60
|
-
| project | `<cwd>/.pi/agents/<name>.md` | **git-managed** — commit for the team |
|
|
61
|
-
| project-local | `<cwd>/.pi/agents/<name>.local.md` | **local** — personal override, gitignore by convention |
|
|
62
|
-
| session | in-memory registry | **ephemeral** — disappears at session start, no source file |
|
|
63
|
-
|
|
64
|
-
A teammate definition is just a Markdown file you own. Shared roles live in `.pi/agents/<name>.md`; a personal tweak to that exact role is `<name>.local.md` in the SAME directory — same teammate name, local scope wins, and discovery deduplicates the pair into one entry (never two). `resolveAgent` reports each definition's `scope` and `gitManaged`; guidance lists both so the leader knows provenance.
|
|
65
|
-
|
|
66
|
-
## Build a Team
|
|
67
|
-
|
|
68
|
-
```text
|
|
69
|
-
teammate_spawn({
|
|
70
|
-
name: "security",
|
|
71
|
-
agent: "reviewer",
|
|
72
|
-
prompt: "Review the auth middleware",
|
|
73
|
-
resources: ["packages/api/auth"]
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
teammate_spawn({ name: "backend", agent: "worker" })
|
|
21
|
+
A delegate action can include an inline definition. It is session-local unless
|
|
22
|
+
`persist: true` is explicitly supplied in that definition. Choose explicit minimal
|
|
23
|
+
canonical tool IDs (`read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`,
|
|
24
|
+
`powershell`); extension aliases such as `ffgrep` are not inherited. Omitted
|
|
25
|
+
`tools` or `tools: []` grants only `agent_event` and `work`, never default shell
|
|
26
|
+
or file access. Delegate/start results and inspection expose the recorded
|
|
27
|
+
`session.tools` grant, with a coordination-only warning when applicable.
|
|
28
|
+
Inspection uses the session's recorded grant, not a changed role definition;
|
|
29
|
+
legacy records without a grant leave it unknown rather than claiming no tools.
|
|
30
|
+
|
|
31
|
+
For an unknown role, provide a compact inline definition:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
agent({ action: "delegate", name: "reader", prompt: "Read README.md and cite its install command",
|
|
35
|
+
definition: { description: "Read evidence", prompt: "Read the assigned file and cite evidence", tools: ["read"] } })
|
|
77
36
|
```
|
|
78
37
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
resources: ["packages/api/auth"],
|
|
101
|
-
supersedes: ["old-auth-patch"],
|
|
102
|
-
verify: "Every scenario in features/gallery.feature holds in the built gallery; no horizontal overflow at 400px."
|
|
103
|
-
})
|
|
38
|
+
`agent start` creates an unassigned resident without a synthetic Work ID or an
|
|
39
|
+
initial model turn. Presence stays `starting` until a one-shot native readiness
|
|
40
|
+
acknowledgement, then becomes `idle` and eligible for exact-session assignment.
|
|
41
|
+
The public start call awaits that bounded acknowledgement before returning, so
|
|
42
|
+
its exact returned session accepts an immediate assignment without a caller wait.
|
|
43
|
+
Rejected readiness, timeout, or early exit fails the start call; a process ID is
|
|
44
|
+
not readiness. Reassigning the same still-claimed Work to its exact current owner
|
|
45
|
+
returns the existing Assignment Attempt without a second delivery. Later claimable-board notices can still wake
|
|
46
|
+
the resident and activate deliberate autonomous claims. Start is not a permanent
|
|
47
|
+
assignment-only scheduling mode.
|
|
48
|
+
|
|
49
|
+
`work` is activated only in valid assignment/board contexts.
|
|
50
|
+
|
|
51
|
+
`work` owns Work lifecycle. Leaders create/list/assign/release/reopen completed
|
|
52
|
+
Work/supersede; Workers list/claim/submit/release their current Work.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
work({ action: "create", subject: "Fix storage", resources: ["firmware/storage"] })
|
|
56
|
+
work({ action: "assign", id: "fix-storage", target: { session: "session:worker-...:spawn-..." } })
|
|
57
|
+
work({ action: "claim" })
|
|
58
|
+
work({ action: "submit", outcome: "success", result: "Verification evidence" })
|
|
104
59
|
```
|
|
105
60
|
|
|
106
|
-
`
|
|
61
|
+
`agent_event` is communication-only. It accepts a message, a recipient or bound
|
|
62
|
+
reply route, and optional `inform`/`request` intent. Work lifecycle authority is
|
|
63
|
+
never inferred from message text.
|
|
107
64
|
|
|
108
|
-
```
|
|
109
|
-
|
|
65
|
+
```ts
|
|
66
|
+
agent_event({ to: "session:reviewer-...:spawn-...", message: "The policy changed", intent: "inform" })
|
|
110
67
|
```
|
|
111
68
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
69
|
+
Automatic final answers (including autonomously claimed Work) and explicit Work
|
|
70
|
+
submission use the same attempt-bound acceptance pipeline. Explicit submission
|
|
71
|
+
ends the worker turn without a second automatic submission. Successful results
|
|
72
|
+
reach the Leader once, after execution settles and any verification gate passes.
|
|
73
|
+
Execution failures are reported after settlement without claiming acceptance.
|
|
74
|
+
`work list` includes bounded result evidence for deliberate recovery.
|
|
75
|
+
Verification freezes execution and retains deferred mail as Work history.
|
|
76
|
+
An ordinary final answer is a successful candidate, not independent verification;
|
|
77
|
+
ungated acceptance does not independently verify its claims. If requested work
|
|
78
|
+
cannot be performed, including missing tools, workers must explicitly submit
|
|
79
|
+
`work({ action: "submit", outcome: "failed", result: "Blocker and unverified work" })`.
|
|
80
|
+
Blocker prose is not classified as failure. Failed submission produces one failed
|
|
81
|
+
report, not completion or dependent unlock; a rejecting verification gate also
|
|
82
|
+
prevents acceptance. Leaders should delegate concrete acceptance criteria and an
|
|
83
|
+
explicit verification gate where appropriate. Informational reports need no
|
|
84
|
+
acknowledgment unless a decision or action changes.
|
|
85
|
+
Do not narrate repeated reports. This guidance does not suppress event transport
|
|
86
|
+
or infer lifecycle changes from communication.
|
|
87
|
+
|
|
88
|
+
After delegation, continue independent work or end the turn: results resume the
|
|
89
|
+
session automatically. Use `agent inspect` for deliberate diagnosis, not repeated
|
|
90
|
+
polling or sleep loops. `agent_event` never creates or retries Work; messaging a
|
|
91
|
+
completed assignment returns its recorded result without waking the worker.
|
|
92
|
+
Recovery reuses the existing Work ID. Request explicit `work release` and await
|
|
93
|
+
authoritative release before `work assign` to the chosen exact session; a queued
|
|
94
|
+
release is not yet released authority. Do not create competing recovery Work via
|
|
95
|
+
`agent delegate`. Read-only/no-write messages are steering, not cancellation or
|
|
96
|
+
proof that the current tool batch stopped. Native steering takes effect at safe
|
|
97
|
+
tool boundaries, before peer follow-up; it cannot undo already executed writes.
|
|
98
|
+
Parked verification requires explicit Work release and reassignment; ordinary
|
|
99
|
+
messages remain deferred. Exact session handles are incarnation-bound, so a stale
|
|
100
|
+
handle cannot stop or inspect a replacement resident. Work persists for later inspection,
|
|
101
|
+
but a different session file does not import another session's tasks automatically.
|
|
102
|
+
|
|
103
|
+
## Coordination and delivery
|
|
104
|
+
|
|
105
|
+
Leader guidance uses the same delivery contract before and after a team starts:
|
|
106
|
+
|
|
107
|
+
- Delegate separable work with a task-scoped baseline, acceptance criteria and safe local checks. Use `work create` with `dependsOn` for prerequisites and shared `resources` tags for conflicting writes; then assign eligible Work. Independent delegation is not a substitute for dependencies.
|
|
108
|
+
- Implementers own assigned local checks. One integration owner checks shared behavior against the integrated candidate. Determine isolation needs before verification; commands running locally may still access user data or production credentials.
|
|
109
|
+
- Record a revision or scoped snapshot for the candidate and exclude unrelated dirty changes. Review and integration checks judge that same candidate; later edits invalidate affected evidence rather than automatically requiring every Agent to rerun every suite.
|
|
110
|
+
- A completed review Work Item can report REWORK. It is not a PASS on the implementation. Review-only work ends with that report, without editing the implementation or waiting for repairs; the implementation owner owns remediation. Implementation delivery stays pending until required Work and blocking reviews have returned, findings are resolved, and applicable evidence matches the final candidate. If only results remain outstanding, yield without announcing completion.
|
|
111
|
+
- Group related findings by root cause. Before a bounded recheck, preserve the prior report outside the result that reopen clears, and prepare the retained baseline, new candidate fingerprint, correction delta, prior findings and safe checks. If the description already references an authoritative current-attempt brief, update that brief before reopening and assigning, then keep it stable. Reopen/assign and the reopen reason do not change the old description or forward prior findings. A fixed conflicting description needs bounded follow-up Work with a refreshed description and `dependsOn` on the completed review. Messages to a closed assignment do not restart it; use a new broad review only when scope or risk changes.
|
|
112
|
+
|
|
113
|
+
`verify` is an independent reviewer's acceptance prompt, **not a shell command**. It adds a model-based acceptance gate when appropriate; it does not replace tests or make ungated reports independently verified. The contract above is prompt guidance, not a new scheduler, automatic snapshot system, or cross-extension completion lock.
|
|
114
|
+
|
|
115
|
+
Read [the role reference](references/agent-roles.md#assignment-brief) when preparing an assignment brief.
|
|
116
|
+
|
|
117
|
+
## Message display
|
|
118
|
+
|
|
119
|
+
Collapsed `[message]` rows use the current terminal width for their preview and
|
|
120
|
+
reserve the complete configured expansion-key hint. Expanded rows show the full
|
|
121
|
+
message once, inline after the recipient and delivery state, with natural wrapping
|
|
122
|
+
and preserved line breaks. Literal JSON quotes and punctuation spacing remain
|
|
123
|
+
unchanged; message content bypasses prose cleanup. Routing identifiers remain
|
|
124
|
+
model-facing, not transcript copy; real extra notes are separate from the message
|
|
125
|
+
and are not repeated previews.
|
|
126
|
+
The renderer uses the same pi-kit lifecycle abstraction as `[context] researched`;
|
|
127
|
+
only the content model differs: research has a distinct query and answer, while a
|
|
128
|
+
sent message expands its inline text.
|
|
139
129
|
|
|
140
|
-
|
|
141
|
-
- **Queued means written**: peer `send_message` succeeds only after the recipient inbox write succeeds. The harness still owns delivery into a recipient turn; only an accepted active control-stream steer is labeled `steered`.
|
|
142
|
-
- **One writer per state file**: only the leader process writes runtime and board snapshots (atomic tmp+rename). Workers append leader reports to their outbox, peer mail to recipient inboxes, and task intent via exclusive-create marker files.
|
|
143
|
-
- **One assignment at a time**: direct assignments and board claims are mutually exclusive. A terminal direct report closes that assignment until an explicit `reopen`; a board claim stays open until `task_submit`.
|
|
144
|
-
- **Resource-safe board work**: overlapping path-like resource tags cannot run concurrently. Superseded tasks stay visible for audit but cannot be claimed, completed, or re-noticed.
|
|
145
|
-
- **Completion is gated, not self-reported**: a task completes only after its effective verify gate passes when one exists; no gate means the submission itself completes it. Explicit FAIL twice parks the task with its holder and escalates once. Missing verdict text is inconclusive, gets one clarification, then escalates without incrementing the failure count.
|
|
146
|
-
- **No caps, heartbeat only**: teammates run without turn-count or duration ceilings. The harness heartbeat tracks silence per working teammate and — after 30 minutes without any RPC output (`PI_TEAMMATE_STALL_NOTICE_MS`, 0 disables) — sends the leader one actionable health event per silence episode. The notice is the last automatic action: continuing, steering, shutting down, or respawning a context-carrying successor belongs to the leader alone. Any output or prompt delivery re-arms it. Health events carry diagnostics separately from message routing: silence duration, spawn age, and lifetime token/cost usage. A teammate with zero lifetime model output and no tool running gets flagged much earlier (5 minutes, `PI_TEAMMATE_SILENT_STALL_MS`, 0 disables): an in-flight request stuck on the provider will not recover by steering, so that notice names shutdown plus respawn as the effective remedy.
|
|
147
|
-
- **One-shot board notices**: an idle teammate is told about a claimable task exactly once; declined tasks never re-wake it, and released tasks re-arm. Notices are paced at least five minutes apart per teammate (`PI_TEAMMATE_NOTICE_PACE_MS` overrides in milliseconds).
|
|
148
|
-
- **One end-of-life line per teammate**: the first terminal report of a spawn incarnation renders the finish entry; shutting that incarnation down afterwards adds no second event row. Requested shutdown is process cleanup only, not proof of assignment completion; wait for the worker's terminal status report when possible. After the first accepted terminal report, later reports are suppressed until a new assignment prompt; distinct intermediate reports remain deliverable.
|
|
149
|
-
- **Failure semantics**: an unexpected crash marks the teammate stopped, reports a diagnostic, and releases its claimed tasks.
|
|
150
|
-
|
|
151
|
-
## State and Sessions
|
|
152
|
-
|
|
153
|
-
Runtime state lives in `~/.pi/agent/teammate/<sessionKey>/` and dies with the session (`state.json`, report outboxes, peer inboxes, roster). The task board lives in `~/.pi/agent/tasks/<sessionKey>/board.json` and persists for later inspection; nothing auto-deletes it. The board key is derived from the Pi session file (or cwd when no session file exists), so a fresh session with a different session file does not import another session's tasks automatically; resume the same board directory to continue them. Claims held by dead teammates return to pending on reload.
|
|
154
|
-
|
|
155
|
-
## Structure
|
|
130
|
+
## Install
|
|
156
131
|
|
|
132
|
+
```bash
|
|
133
|
+
pi install npm:@fradser/pi-agent-teams
|
|
157
134
|
```
|
|
158
|
-
agent-teams/
|
|
159
|
-
├── index.ts — package-root extension entry point
|
|
160
|
-
├── references/ — role templates consulted when generating new agents
|
|
161
|
-
├── src/
|
|
162
|
-
│ ├── index.ts — composition root and session lifecycle
|
|
163
|
-
│ ├── tools.ts — leader tools and /agent-teams command
|
|
164
|
-
│ ├── worker.ts — worker tools plus the shared task_list registration
|
|
165
|
-
│ ├── team-machine.ts — resident lifecycle, mail routing, intents, verify, wake-ups
|
|
166
|
-
│ ├── state.ts — roster, board, leader inbox state machine
|
|
167
|
-
│ ├── spawner.ts — resident RPC process spawner (uncapped sequences)
|
|
168
|
-
│ ├── agents.ts — agent discovery and frontmatter parsing
|
|
169
|
-
│ ├── statefile.ts — runtime/board/mail IO and marker-file intents
|
|
170
|
-
│ ├── ui.ts — passive widget and /agent-teams management console
|
|
171
|
-
│ ├── guidance.ts — static leader and worker protocols
|
|
172
|
-
│ └── follow-up-queue.ts — serialized automatic report delivery
|
|
173
|
-
├── features/ — BDD contract
|
|
174
|
-
└── tests/ — package tests
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## License
|
|
178
135
|
|
|
179
|
-
|
|
136
|
+
Run `/reload` after installation. `/agent-teams` is the human management
|
|
137
|
+
surface for Presence, Work, diagnostics, and exact-session stop controls.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fradser/pi-agent-teams",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Multi-agent team system for Pi — resident teammates, shared task board, and peer-to-peer messaging",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -39,6 +39,6 @@
|
|
|
39
39
|
]
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@fradser/pi-kit": "0.
|
|
42
|
+
"@fradser/pi-kit": "0.5.0"
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -13,8 +13,14 @@ Frontmatter:
|
|
|
13
13
|
- `name` — unique id among discovered roles.
|
|
14
14
|
- `description` — the routing contract: when the leader should choose this
|
|
15
15
|
role, phrased as "use when ...". This is the most load-bearing field.
|
|
16
|
-
- `tools` — the minimal set; follows the mutability axis (see below).
|
|
17
|
-
-
|
|
16
|
+
- `tools` — the explicit minimal set; follows the mutability axis (see below).
|
|
17
|
+
Canonical built-ins are `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`,
|
|
18
|
+
`powershell` (kept in sync with `src/worker-tools.ts`).
|
|
19
|
+
Omitted tools or `[]` grant only coordination tools (`agent_event`, `work`),
|
|
20
|
+
not file or shell access. No aliases or implicit default `bash`.
|
|
21
|
+
- optional `model`, `verify`, `worktree`. `verify` is an independent completion-gate
|
|
22
|
+
reviewer prompt, not a shell command. State the evidence and acceptance criteria
|
|
23
|
+
the gate should inspect; a concrete local gate is not a second broad project review.
|
|
18
24
|
|
|
19
25
|
The Markdown body is the role prompt, built from five parts:
|
|
20
26
|
|
|
@@ -23,10 +29,37 @@ The Markdown body is the role prompt, built from five parts:
|
|
|
23
29
|
observation targets, depending on the judgment axis.
|
|
24
30
|
3. Boundaries — what must never be touched or changed.
|
|
25
31
|
4. Evidence — what every claim or finding must carry.
|
|
26
|
-
5.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
5. Final result — an ordinary final answer is submitted automatically for the
|
|
33
|
+
current Work Item after execution settles. A Worker may explicitly use
|
|
34
|
+
`work({ action: "submit", outcome, result? })`; communication events never
|
|
35
|
+
carry completion authority.
|
|
36
|
+
|
|
37
|
+
## Assignment brief
|
|
38
|
+
|
|
39
|
+
Keep task-specific coordination in the Work description, not a permanently growing role:
|
|
40
|
+
|
|
41
|
+
- **Scope and baseline** — owned files or resources, originating requirements, and
|
|
42
|
+
the exact task delta, including untracked files and pre-existing dirty changes to exclude.
|
|
43
|
+
- **Dependencies** — required predecessor Work and any shared resource ownership.
|
|
44
|
+
Use `work create` with `dependsOn` for dependent work, then assign when eligible;
|
|
45
|
+
`agent delegate` is for independent work.
|
|
46
|
+
- **Verification owner** — assigned safe local checks for this Worker; one owner
|
|
47
|
+
integrates and checks shared behavior. Determine isolation needs before commands run.
|
|
48
|
+
- **Candidate and result** — identify the revision or scoped snapshot checked, commands
|
|
49
|
+
and outcomes, findings and limitations. Reviewer output keeps Standards and Spec
|
|
50
|
+
verdicts separate when both apply; REWORK is a valid completed review, not acceptance
|
|
51
|
+
of the implementation. Review-only assignments finish with that report, without
|
|
52
|
+
editing the implementation or waiting for repairs. Missing access or tools is failed
|
|
53
|
+
execution, not REWORK evidence.
|
|
54
|
+
- **Recheck handoff** — preserve the prior report outside the Work result before reopen
|
|
55
|
+
clears it. A reusable description should point to an authoritative brief for the current
|
|
56
|
+
attempt: refresh its candidate fingerprint, correction delta, baseline, findings and safe
|
|
57
|
+
checks before reopening/assignment, then keep it stable. A fixed conflicting description
|
|
58
|
+
requires bounded follow-up Work with the refreshed description and `dependsOn` on the
|
|
59
|
+
completed review. Reopen/assign and the reopen reason do not update the old description.
|
|
60
|
+
|
|
61
|
+
Leader delivery and pending-review rules are injected by the shared Leader guidance;
|
|
62
|
+
these brief fields make those rules actionable without redefining Work lifecycle here.
|
|
30
63
|
|
|
31
64
|
## Archetype axes
|
|
32
65
|
|
|
@@ -55,12 +88,18 @@ as long as every invariant below holds.
|
|
|
55
88
|
Non-negotiable in every generated definition:
|
|
56
89
|
|
|
57
90
|
- Tool sets match the mutability axis: read-only roles never receive
|
|
58
|
-
`edit` or `write`.
|
|
91
|
+
`edit` or `write`. A `bash` grant is not read-only — a shell can still write,
|
|
92
|
+
so "read-only" here bounds the tools, not the file system, and the completion
|
|
93
|
+
gate relies on the reviewer's instruction rather than enforcement.
|
|
59
94
|
- Every finding or claim carries evidence: exact paths, command output,
|
|
60
95
|
timestamps — never bare assertions.
|
|
61
96
|
- Scope is bounded: mutating roles state "do not touch files outside the
|
|
62
97
|
assigned scope"; read-only roles state the equivalent prohibition.
|
|
63
|
-
-
|
|
98
|
+
- Final results carry evidence; an ordinary final answer is a successful candidate,
|
|
99
|
+
not an independently verified result. Ungated completion is not independently verified.
|
|
100
|
+
- If requested work cannot be performed, including missing capabilities, explicitly use
|
|
101
|
+
`work({ action: "submit", outcome: "failed", result: "Blocker and unverified work" })`.
|
|
102
|
+
Do not return blocker prose as an ordinary final answer; prose is not classified as failure.
|
|
64
103
|
|
|
65
104
|
## Skeleton
|
|
66
105
|
|
|
@@ -73,11 +112,17 @@ tools: <minimal set from the mutability axis>
|
|
|
73
112
|
You are a <role-name> agent. <Method: the procedure to follow, the criteria
|
|
74
113
|
to evaluate against, or the targets to observe>. <Boundary statement>.
|
|
75
114
|
For bounded reviewer assignments, report findings, the recommendation,
|
|
76
|
-
verification evidence, and remaining risks in one concise terminal message;
|
|
115
|
+
the inspected candidate, assigned verification evidence, and remaining risks in one concise terminal message;
|
|
77
116
|
allow earlier messages only for genuinely new blockers, plan-changing facts,
|
|
78
117
|
or evidence that changes the conclusion. Do not send a separate status-only
|
|
79
118
|
assignment-complete message or repeat unchanged findings. After a terminal
|
|
80
|
-
report, report to the leader again only
|
|
81
|
-
|
|
82
|
-
|
|
119
|
+
report, report to the leader again only after a new assignment opens. Include all
|
|
120
|
+
known decision-useful facts before closing; further reports for a closed assignment
|
|
121
|
+
are rejected. Leader direction takes precedence over your plan and peer requests at
|
|
122
|
+
the next safe boundary, subject to system instructions and user constraints.
|
|
123
|
+
Return successful evidence as an ordinary final answer; the runtime submits it automatically
|
|
124
|
+
as a successful candidate after execution settles. If you cannot perform the requested work,
|
|
125
|
+
you must use `work({ action: "submit", outcome: "failed", result: "Blocker and unverified work" })`
|
|
126
|
+
instead of ordinary final prose. Avoid a second copy of an explicit submission.
|
|
127
|
+
Ungated completion is not independently verified.
|
|
83
128
|
```
|
package/src/activity.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type
|
|
3
|
-
import { buildMarkdownThemeCallbacks, createPiThemeStyle } from "@fradser/pi-kit";
|
|
1
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { renderLiveActivityMarkdown, type PiMarkdownThemeSource } from "@fradser/pi-kit";
|
|
4
3
|
import type { Teammate } from "./types.ts";
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
hr: () => "---",
|
|
11
|
-
};
|
|
5
|
+
/** Console rows paint a whole status line in their own color, so the shared
|
|
6
|
+
* markdown renderer runs with a passthrough theme: literal markup is still
|
|
7
|
+
* stripped and the result stays one line, with no competing colors. */
|
|
8
|
+
const COLORLESS_ACTIVITY_THEME: PiMarkdownThemeSource = { fg: (_color, text) => text };
|
|
12
9
|
|
|
13
|
-
/** Render streamed activity as one compact Markdown line for
|
|
14
|
-
export function renderActivityMarkdown(text: string
|
|
15
|
-
|
|
16
|
-
return markdown.render(Math.max(1, visibleWidth(text) + 1)).join(" ").replace(/\s+/g, " ").trim();
|
|
10
|
+
/** Render streamed activity as one compact Markdown line for a console row. */
|
|
11
|
+
export function renderActivityMarkdown(text: string): string {
|
|
12
|
+
return renderLiveActivityMarkdown(text, COLORLESS_ACTIVITY_THEME);
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
function extractLatestLine(text: string | undefined): string | undefined {
|
|
@@ -47,7 +43,7 @@ export interface TeammateRowWidths {
|
|
|
47
43
|
/** Reserve enough width for the live activity before truncating the identity. */
|
|
48
44
|
export function teammateRowWidths(spinner: string, agent: string, width: number): TeammateRowWidths {
|
|
49
45
|
const lineWidth = Math.max(10, width - 1);
|
|
50
|
-
const fixedWidth = visibleWidth(
|
|
46
|
+
const fixedWidth = visibleWidth(`${spinner} · `);
|
|
51
47
|
const availableWidth = Math.max(2, lineWidth - fixedWidth);
|
|
52
48
|
const nameWidth = Math.max(1, Math.min(visibleWidth(agent), Math.floor(availableWidth * 0.4)));
|
|
53
49
|
return {
|
|
@@ -57,30 +53,13 @@ export function teammateRowWidths(spinner: string, agent: string, width: number)
|
|
|
57
53
|
};
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
/** Fit one widget row to its terminal width while keeping it on one line. */
|
|
61
|
-
export function fitTeammateRow(
|
|
62
|
-
spinner: string,
|
|
63
|
-
agent: string,
|
|
64
|
-
activity: string,
|
|
65
|
-
width: number,
|
|
66
|
-
formatActivity: (text: string) => string = (text) => text,
|
|
67
|
-
markdownTheme: MarkdownTheme = ACTIVITY_THEME,
|
|
68
|
-
): string {
|
|
69
|
-
const sizes = teammateRowWidths(spinner, agent, width);
|
|
70
|
-
const nameText = truncateToWidth(agent, sizes.nameWidth);
|
|
71
|
-
const activityText = formatActivity(truncateToWidth(renderActivityMarkdown(activity, markdownTheme), sizes.activityWidth));
|
|
72
|
-
const line = ` ${spinner} ${nameText} · ${activityText}`;
|
|
73
|
-
return truncateToWidth(line, sizes.lineWidth);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
56
|
/** Fit a console status label to the requested activity width. */
|
|
77
57
|
export function formatTeammateLabel(
|
|
78
58
|
spinner: string,
|
|
79
59
|
activity: string,
|
|
80
60
|
maxActivityWidth?: number,
|
|
81
|
-
markdownTheme: MarkdownTheme = ACTIVITY_THEME,
|
|
82
61
|
): string {
|
|
83
|
-
if (maxActivityWidth === undefined) return `${spinner} ${renderActivityMarkdown(activity
|
|
62
|
+
if (maxActivityWidth === undefined) return `${spinner} ${renderActivityMarkdown(activity)}`;
|
|
84
63
|
if (maxActivityWidth <= 0) return spinner;
|
|
85
|
-
return `${spinner} ${truncateToWidth(renderActivityMarkdown(activity
|
|
64
|
+
return `${spinner} ${truncateToWidth(renderActivityMarkdown(activity), maxActivityWidth)}`;
|
|
86
65
|
}
|