The four team tools (tool/team_tools.ts) on the rules of team/team.ts, driven from real turns by team/runtime.ts: - Members run on the subagent job machinery (app.ts spawn with `member`): background, unattended, their own prompt from blocks/team_member.md (the whole of team_lead.md for one with lead: true), team_send and team_read (team_add and team_stop for one that leads). Not jobs to the model: job_list and the job_* tools skip them; a client over the link still sees them as subagent jobs named "<name>: <role>". - The room: messages and reports with their marks, delivered at the next step boundary or waking a finished member or the idle lead; report posts to the manager. - A manager's work calls answer the refusal before the permission check, by risk class (an MCP tool by its readOnlyHint); the tool list is unchanged. job_stop and reading bash stay. - Slots: the lead waits for one before its turn (Esc stops the wait); members wait in order; task helpers in a team count toward the size and hold a slot; job_send restarting a helper is refused when full. - The offer is decided with the prompt and kept with it: a resumed session takes it from its kept tool list. - The size: agents.min/max settings, refused outside 1 <= min <= max <= cap, kept across /new and resume; said in a note on the first message, after a change, after a compaction and a rewind. - TUI: /team (tree and room, size, stop a member, dialogs for both), an "agents" row beside the effort in /effort, "team n/max" in the status bar. /team works over ACP too. tests/team.test.ts runs a lead, a member and its report through the app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
21 files changed
+834
-45
No files matched your search
@@ -52,7 +52,8 @@ test("no clock in the prompt: two requests across a date change send the same sy
|
||||
expect(system(1)).toBe(system(0))
|
||||
expect(system(2)).toBe(system(0))
|
||||
expect(fake!.requests[2].tools).toEqual(fake!.requests[0].tools)
|
||||
expect(lastUser(0)).toBe(`first\n${NOTE.open}\nToday is 2026-11-04.\n${NOTE.close}`)
|
||||
// A model with no capacity said (a hosted API) offers a team: its size is said once, last (spec 3.2.0).
|
||||
expect(lastUser(0)).toBe(`first\n${NOTE.open}\nToday is 2026-11-04.\n\nThe team may have 1 to 4 agents, you included.\n${NOTE.close}`)
|
||||
expect(lastUser(1)).toBe("second")
|
||||
expect(lastUser(2)).toBe(`third\n${NOTE.open}\nToday is 2026-11-05.\n${NOTE.close}`)
|
||||
// A note is part of its message from then on: the next request sends the first one as it was.
|
||||
@@ -140,7 +141,8 @@ test("compaction: the same system and tools, every message, then the compaction
|
||||
expect(store.requests(sid).map((r) => r.kind)).toEqual(["step", "compaction"])
|
||||
// After it the prompt is made again, and the next user message says the date again.
|
||||
await app.engine.prompt("go on")
|
||||
expect(lastUser(2)).toMatch(/^go on\n<session-note>\nToday is \d{4}-\d{2}-\d{2}\.\n<\/session-note>$/)
|
||||
// The team's size too (spec 3.2.0): like the date, it is not in the prompt.
|
||||
expect(lastUser(2)).toMatch(/^go on\n<session-note>\nToday is \d{4}-\d{2}-\d{2}\.\n\nThe team may have 1 to 4 agents, you included\.\n<\/session-note>$/)
|
||||
})
|
||||
|
||||
test("a resumed session sends the prompt it kept, byte for byte; what changed meanwhile is a note", async () => {
|
||||
|
||||
Reference in new issue
Block a user