ac-teams 0.0.7 → 0.0.9
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 +1 -1
- package/docs/access.md +4 -0
- package/docs/design/session-roles/README.md +343 -0
- package/docs/design/session-roles/coordinator.yaml +44 -0
- package/docs/design/session-roles/design.yaml +70 -0
- package/docs/design/session-roles/herdr.yaml +17 -0
- package/docs/design/session-roles/journey.md +139 -0
- package/docs/design/session-roles/main-session.yaml +34 -0
- package/docs/design/session-roles/member.yaml +28 -0
- package/docs/design/session-roles/read-guard.yaml +17 -0
- package/docs/design/session-roles/team-store.yaml +13 -0
- package/docs/design/session-roles/transcripts.yaml +23 -0
- package/docs/design/session-roles/user.yaml +17 -0
- package/docs/operations.md +13 -2
- package/docs/usage.md +31 -9
- package/package.json +1 -1
- package/src/commands.ts +1 -1
- package/src/coordinator.ts +242 -31
- package/src/index.ts +243 -61
- package/src/mains.ts +41 -0
- package/src/panel.ts +9 -1
- package/src/schemas.ts +2 -2
- package/src/store.ts +32 -9
- package/src/terminal-agent.ts +1 -1
- package/src/terminal-ui.ts +6 -3
- package/src/types.ts +19 -4
- package/src/ui-types.ts +4 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
id: main-session
|
|
2
|
+
name: Main session
|
|
3
|
+
summary: >-
|
|
4
|
+
A Pi session the user started, joined to a team, or promoted. Every main
|
|
5
|
+
session controls every member and other main sessions, shares the team's
|
|
6
|
+
tasks and messages, and belongs to one team at a time.
|
|
7
|
+
kind: component
|
|
8
|
+
channels: [cli]
|
|
9
|
+
relationships:
|
|
10
|
+
- id: user-drives-main
|
|
11
|
+
from: user
|
|
12
|
+
to: main-session
|
|
13
|
+
direction: one-way
|
|
14
|
+
label: Promotes, joins, and leaves through /teams in
|
|
15
|
+
- id: main-uses-coordinator
|
|
16
|
+
from: main-session
|
|
17
|
+
to: coordinator
|
|
18
|
+
direction: one-way
|
|
19
|
+
label: Hosts as owner, or connects to as a client
|
|
20
|
+
- id: main-writes-transcript
|
|
21
|
+
from: main-session
|
|
22
|
+
to: transcripts
|
|
23
|
+
direction: one-way
|
|
24
|
+
label: Writes its conversation and team entry to
|
|
25
|
+
- id: guard-reads-main
|
|
26
|
+
from: read-guard
|
|
27
|
+
to: main-session
|
|
28
|
+
direction: one-way
|
|
29
|
+
label: Finds no member role in
|
|
30
|
+
- id: coordinator-launches-main
|
|
31
|
+
from: coordinator
|
|
32
|
+
to: main-session
|
|
33
|
+
direction: one-way
|
|
34
|
+
label: Relaunches promoted members as
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
id: member
|
|
2
|
+
name: Member
|
|
3
|
+
summary: >-
|
|
4
|
+
An agent-launched Pi process with ACSTACK_AGENT_ROLE=member and AC_TEAMS_*
|
|
5
|
+
variables. It controls only its descendants, and its transcript stays under
|
|
6
|
+
ac-teams/<team>/<member>/ until promotion.
|
|
7
|
+
kind: component
|
|
8
|
+
relationships:
|
|
9
|
+
- id: coordinator-runs-member
|
|
10
|
+
from: coordinator
|
|
11
|
+
to: member
|
|
12
|
+
direction: one-way
|
|
13
|
+
label: Launches, stops, and resumes
|
|
14
|
+
- id: member-uses-coordinator
|
|
15
|
+
from: member
|
|
16
|
+
to: coordinator
|
|
17
|
+
direction: one-way
|
|
18
|
+
label: Calls team tools through
|
|
19
|
+
- id: member-writes-transcript
|
|
20
|
+
from: member
|
|
21
|
+
to: transcripts
|
|
22
|
+
direction: one-way
|
|
23
|
+
label: Writes its conversation to
|
|
24
|
+
- id: guard-reads-member
|
|
25
|
+
from: read-guard
|
|
26
|
+
to: member
|
|
27
|
+
direction: one-way
|
|
28
|
+
label: Reads ACSTACK_AGENT_ROLE=member in
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
id: read-guard
|
|
2
|
+
name: ACStack read guard
|
|
3
|
+
summary: >-
|
|
4
|
+
ACStack's large-read hook. It blocks a large untargeted read once in main
|
|
5
|
+
agents only, and reads ACSTACK_AGENT_ROLE on every call to decide.
|
|
6
|
+
kind: external-system
|
|
7
|
+
relationships:
|
|
8
|
+
- id: guard-reads-member
|
|
9
|
+
from: read-guard
|
|
10
|
+
to: member
|
|
11
|
+
direction: one-way
|
|
12
|
+
label: Reads ACSTACK_AGENT_ROLE=member in
|
|
13
|
+
- id: guard-reads-main
|
|
14
|
+
from: read-guard
|
|
15
|
+
to: main-session
|
|
16
|
+
direction: one-way
|
|
17
|
+
label: Finds no member role in
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
id: team-store
|
|
2
|
+
name: Team store
|
|
3
|
+
summary: >-
|
|
4
|
+
Private team state under ~/.pi/ac-teams/teams/<team>/: members with their
|
|
5
|
+
role, tasks, messages, owner.lock, and each main session's connection
|
|
6
|
+
token. Sandboxed members cannot read it.
|
|
7
|
+
kind: store
|
|
8
|
+
relationships:
|
|
9
|
+
- id: coordinator-writes-store
|
|
10
|
+
from: coordinator
|
|
11
|
+
to: team-store
|
|
12
|
+
direction: one-way
|
|
13
|
+
label: Holds owner.lock and records roles in
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
id: transcripts
|
|
2
|
+
name: Pi transcripts
|
|
3
|
+
summary: >-
|
|
4
|
+
Standard Pi session files. Main sessions sit at the top level of the
|
|
5
|
+
project's session folder, where /resume finds them. Members sit under
|
|
6
|
+
ac-teams/<team>/<member>/, where only /teams finds them.
|
|
7
|
+
kind: store
|
|
8
|
+
relationships:
|
|
9
|
+
- id: main-writes-transcript
|
|
10
|
+
from: main-session
|
|
11
|
+
to: transcripts
|
|
12
|
+
direction: one-way
|
|
13
|
+
label: Writes its conversation and team entry to
|
|
14
|
+
- id: member-writes-transcript
|
|
15
|
+
from: member
|
|
16
|
+
to: transcripts
|
|
17
|
+
direction: one-way
|
|
18
|
+
label: Writes its conversation to
|
|
19
|
+
- id: coordinator-moves-transcript
|
|
20
|
+
from: coordinator
|
|
21
|
+
to: transcripts
|
|
22
|
+
direction: one-way
|
|
23
|
+
label: Moves promoted transcripts within
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
id: user
|
|
2
|
+
name: User
|
|
3
|
+
summary: >-
|
|
4
|
+
Starts main sessions and is the only party who can promote a member, join a
|
|
5
|
+
session to a team, or approve a sandboxed member's full access.
|
|
6
|
+
kind: actor
|
|
7
|
+
relationships:
|
|
8
|
+
- id: user-drives-main
|
|
9
|
+
from: user
|
|
10
|
+
to: main-session
|
|
11
|
+
direction: one-way
|
|
12
|
+
label: Promotes, joins, and leaves through /teams in
|
|
13
|
+
- id: user-uses-herdr
|
|
14
|
+
from: user
|
|
15
|
+
to: herdr
|
|
16
|
+
direction: one-way
|
|
17
|
+
label: Switches between main sessions in
|
package/docs/operations.md
CHANGED
|
@@ -51,6 +51,8 @@ This is snapshot-based cleanup, not OS containment. Detached or uncaptured desce
|
|
|
51
51
|
|
|
52
52
|
The interactive panel can instead quietly resume a saved member session without inventing a new brief. Feedback then arrives through the durable team mailbox.
|
|
53
53
|
|
|
54
|
+
Stopping a main session ends its Pi process; an exiting coordinator hands over first. Resuming another main session relaunches it in its own terminal.
|
|
55
|
+
|
|
54
56
|
Root session switching, forking, and tree traversal are blocked while members are live. A member cannot switch away from its team binding. Stop members explicitly before replacing the root session.
|
|
55
57
|
|
|
56
58
|
### Exit confirmation needs a Pi hook
|
|
@@ -59,6 +61,12 @@ Exit confirmation requires Pi's `session_before_exit` hook. The prerequisite is
|
|
|
59
61
|
|
|
60
62
|
With that hook, native exit asks before stopping live members; cancellation leaves them running. Without it, shutdown performs cleanup but cannot ask first. Signals bypass confirmation.
|
|
61
63
|
|
|
64
|
+
### Coordinator handover
|
|
65
|
+
|
|
66
|
+
The main session holding `owner.lock` runs the coordinator. Before it exits it records whether each member was busy, idle, or waiting for input, then stops the members. Another connected main session takes the lock within seconds and runs the resume pass: busy members resume and are told their process restarted during a handover, idle members resume idle, and members waiting for input resume and ask again. The same pass runs when a main session opens a team with no coordinator running.
|
|
67
|
+
|
|
68
|
+
A main session that crashes is marked interrupted; any main session can resume it. Raising the member limit above 10 requires the coordinator's own session, because the confirmation prompt runs there.
|
|
69
|
+
|
|
62
70
|
### Crash recovery is best effort
|
|
63
71
|
|
|
64
72
|
If the root crashes, closing its owned pipes and losing the coordinator connection makes ordinary RPC children shut down. Native members also monitor the authenticated coordinator connection and exit after it is lost. This passed SIGTERM and SIGKILL probes with native child and grandchild processes.
|
|
@@ -79,7 +87,8 @@ Each root session uses `~/.pi/ac-teams/teams/<session-id>/`:
|
|
|
79
87
|
|
|
80
88
|
- `state.json` contains membership, policy, tasks, messages, claims, and notification cursors
|
|
81
89
|
- Member transcripts live under Main's session directory in `ac-teams/<team>/<member>/`, normally `~/.pi/agent/sessions/<project>/ac-teams/...`; Pi's `/resume` lists only top-level sessions, so use `/teams` to find them
|
|
82
|
-
- `owner.lock` and `coordinator.sock` coordinate the running
|
|
90
|
+
- `owner.lock` and `coordinator.sock` coordinate the main session running the coordinator; other main sessions authenticate with tokens stored in `state.json`
|
|
91
|
+
- Promoted members' transcripts move to `<session folder>/` beside other top-level sessions, and their `~/.pi/ac-teams/agents/<id>` folder is removed
|
|
83
92
|
- Interactive teams also own `t.sock`, the private tmux socket, and per-member terminal environment files; shutdown removes those files
|
|
84
93
|
|
|
85
94
|
New members also store their descriptive names and optional model/context reports. When an existing team opens, unnamed members receive stable, collision-free names such as `teammate-1`; their IDs and sessions do not change.
|
|
@@ -88,7 +97,9 @@ State schema 3 adds stable task numbers and short labels. Opening a schema 1 or
|
|
|
88
97
|
|
|
89
98
|
Schema 1 also migrates progress: `pending` becomes `todo`, `claimed` becomes `in_progress`, and `completed` becomes `done`. Migration writes atomically and rejects invalid snapshots without replacement. Older ac-teams code cannot reopen schema 3; stop old sessions before updating.
|
|
90
99
|
|
|
91
|
-
|
|
100
|
+
State schema 4 adds a role to each record: `main` or `member`. Opening a schema 3 team makes the root record a main session and every other record a member. A record without a role has left the team. A promotion records each step, so a failure rolls the member back to a stopped member with its original transcript path.
|
|
101
|
+
|
|
102
|
+
Only the coordinator writes state. Mutations validate a copy, flush a temporary snapshot, atomically rename it, and flush the directory before returning success. An exclusive process-identity lock prevents concurrent owners. Private team directories use 0700; state files and the authenticated Unix socket use 0600. Uncertain locks fail closed.
|
|
92
103
|
|
|
93
104
|
This is a small-team store: each mutation rewrites the snapshot, and search scans local history. There is no database, compaction, retention policy, automatic deletion, or deduplication of repeated mutation requests. Inspect state before retrying an ambiguous failed request.
|
|
94
105
|
|
package/docs/usage.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Use ac-teams
|
|
2
2
|
|
|
3
|
-
ac-teams provides three tools
|
|
3
|
+
ac-teams provides three tools with lightweight guidance for tracking delegated work. Your Pi session is a main session; the agents it launches are members. The first main session opens coordination state on the first team request. See [main sessions](#main-sessions-share-one-team) for adding more.
|
|
4
4
|
|
|
5
5
|
| Tool | Actions | Main rule |
|
|
6
6
|
| --- | --- | --- |
|
|
@@ -8,7 +8,7 @@ ac-teams provides three tools without prescribing a workflow. Your Pi session is
|
|
|
8
8
|
| `team_task` | `create`, `list`, `read`, `edit`, `claim`, `start`, `release`, `complete`, `reopen` | Claim reserves work; dependencies gate starting, not ownership |
|
|
9
9
|
| `team_message` | `send`, `history`, `search` | Recipients select who receives notification, not who can read |
|
|
10
10
|
|
|
11
|
-
In interactive Pi, `/teams` or `/teams panel` opens the team panel. `/teams teammate <name-or-id>` opens a live teammate directly; autocomplete suggests subcommands and teammate names. You can also enter an exact teammate ID. The older `/teams open <id>` form remains supported. Headless roots retain the status listing. `/teams stop` stops every
|
|
11
|
+
In interactive Pi, `/teams` or `/teams panel` opens the team panel. `/teams teammate <name-or-id>` opens a live teammate directly; autocomplete suggests subcommands and teammate names. You can also enter an exact teammate ID. The older `/teams open <id>` form remains supported. Headless roots retain the status listing. `/teams stop` stops every member; main sessions keep running. `/teams join` and `/teams leave` change which team this session belongs to. See [operations](operations.md) for limits, recovery, and worktree cleanup.
|
|
12
12
|
|
|
13
13
|
## Navigate without stopping members
|
|
14
14
|
|
|
@@ -38,6 +38,17 @@ The `+N lines` hint shows the hidden detail count. Ctrl+O expands the full reque
|
|
|
38
38
|
|
|
39
39
|
Team notification messages remain compact; Ctrl+O expands their labeled fields. UI state colors do not replace the underlying progress, ownership, or dependency fields.
|
|
40
40
|
|
|
41
|
+
## Main sessions share one team
|
|
42
|
+
|
|
43
|
+
A team can have several main sessions, each a top-level Pi session a person drives. Members are the agents they launch. Every main session controls every member and can stop or resume other main sessions; a member controls only its own descendants and never a main session.
|
|
44
|
+
|
|
45
|
+
- **Promote:** On the panel's Teammates tab, `P` stops a member and relaunches it as a main session. It keeps its session ID and history, its transcript moves to the normal Pi session folder so `/resume` lists it, and it stays in the team. Under Herdr it opens in a new tab of the workspace for its worktree; otherwise it runs in its own tmux server and ac-teams prints the attach command. A member with enforced access asks first, because the promoted session runs without the container.
|
|
46
|
+
- **Join:** `/teams join` lists the teams in this project and adds this session to the chosen one as a main session, then posts a summary of open tasks, owners, and recent messages. It refuses a session already in that team, or one already in a team with other members.
|
|
47
|
+
- **Leave:** `/teams leave` returns this session's in-progress tasks to `todo`, clears their ownership, and drops its main-session role; its record keeps its history. The last main session cannot leave while members are live.
|
|
48
|
+
- **Resume:** A session remembers its team in its transcript. Resuming it reconnects to the team; if no coordinator is running, ac-teams offers to run it here. Declining turns the team tools off for this session.
|
|
49
|
+
|
|
50
|
+
One main session runs the coordinator; the others connect to it. When that session exits, another main session takes over automatically and resumes the members that were working. See [operations](operations.md#coordinator-handover).
|
|
51
|
+
|
|
41
52
|
## Show tasks and configure the displays
|
|
42
53
|
|
|
43
54
|
The task summary sits above the input editor; the agent selector sits below the editor. The task summary shows completed/total counts and up to five numbered task labels, ordered by:
|
|
@@ -57,12 +68,27 @@ Hiding a display does not stop agents, change tasks, or disable `/teams`. With t
|
|
|
57
68
|
|
|
58
69
|
## Give members bounded assignments
|
|
59
70
|
|
|
60
|
-
|
|
71
|
+
Before delegating a deliverable, create or reuse a shared task and include its ID in the member's brief. Track independently owned outcomes, not every agent, tool call, or procedural step. For example:
|
|
72
|
+
|
|
73
|
+
- A fix and an independently owned test assignment need separate tasks.
|
|
74
|
+
- A diagnosis others need for follow-up needs a task, even if the investigation is read-only.
|
|
75
|
+
- A quick lookup, clarification, or brief second opinion can skip a task unless its result needs coordination or follow-up.
|
|
76
|
+
- Running tests as part of a fix usually belongs to the fix's existing task.
|
|
77
|
+
|
|
78
|
+
Review the current task list and relevant details first. Reuse an existing task when it covers the assignment. If none does, create one with acceptance criteria. Example `team_task` arguments:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{"action":"create","label":"Parser review","title":"Review parser errors and malformed input","description":"Read-only review of src/parser.ts. Acceptance: report concrete malformed-input failures with reproduction steps, or report that none were found."}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Put the returned task UUID in the brief in place of `<task-id>`. Example `team_agent` arguments:
|
|
61
85
|
|
|
62
86
|
```json
|
|
63
|
-
{"action":"launch","name":"parser-review","brief":"
|
|
87
|
+
{"action":"launch","name":"parser-review","brief":"Task <task-id>: claim and start it, then review src/parser.ts. Read only; do not delegate or edit. Send findings to root through team_message. Complete the task after checking its acceptance criteria, or report blockers."}
|
|
64
88
|
```
|
|
65
89
|
|
|
90
|
+
These are prompt guidelines, not launch requirements. ac-teams neither creates tasks automatically nor blocks launches without tasks.
|
|
91
|
+
|
|
66
92
|
Every launch needs a unique descriptive `name`: one or two lowercase words, hyphenated if two, at most 40 characters. For example, `parser-review` or `research`. `main`, `root`, and names matching an existing member's ID are reserved. Names remain stable across stop/resume; IDs remain the API references.
|
|
67
93
|
|
|
68
94
|
`cwd` defaults to the requesting member's directory. `model` (`provider/model`) and `thinking` default to that member's current selection. An explicit override wins. Native Pi selects the model; provider/account rejections remain visible in member status and notify the parent. There is no automatic model retry.
|
|
@@ -75,11 +101,7 @@ Children can delegate through the same coordinator, subject to depth and capacit
|
|
|
75
101
|
|
|
76
102
|
## Track progress separately from ownership
|
|
77
103
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```json
|
|
81
|
-
{"action":"create","label":"Parser review","title":"Review parser errors and malformed input","description":"Check malformed input and report concrete failures; no edits."}
|
|
82
|
-
```
|
|
104
|
+
Task owners claim and start their work, then complete it after checking acceptance criteria or report blockers. Claiming reserves a task; starting also requires its dependencies to be done.
|
|
83
105
|
|
|
84
106
|
Each task has a stable team-local `number` and a 1–3-word `label` (at most 60 characters). The graph and list show `#3 Parser review`; details retain the full title, description, and UUID.
|
|
85
107
|
|
package/package.json
CHANGED
package/src/commands.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { memberName, type Member } from "./types.ts";
|
|
|
3
3
|
import { cleanText } from "./render.ts";
|
|
4
4
|
|
|
5
5
|
type NamedMember = Pick<Member, "id" | "name" | "status">;
|
|
6
|
-
const actions = ["panel", "teammate", "config", "back", "stop", "status"];
|
|
6
|
+
const actions = ["panel", "teammate", "config", "back", "stop", "status", "join", "leave"];
|
|
7
7
|
|
|
8
8
|
export function resolveTeammate(members: NamedMember[], value: string): string | undefined {
|
|
9
9
|
const key = value === "main" ? "root" : value;
|