Skip to content

Commit 8195d37

Browse files
authored
Merge pull request #22 from reoring/roboppi/loop-20260227-121443
feat(agents): rename swarm and add resident team workflows
2 parents baf7e1b + 0cf7a3d commit 8195d37

77 files changed

Lines changed: 7382 additions & 1506 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,43 @@ The format is based on Keep a Changelog, and this project follows SemVer where p
1313
### Fixed
1414

1515

16+
## [0.1.9] - 2026-03-06
17+
18+
### Added
19+
20+
- `roboppi agents chat` interactive REPL, `message reply`, and resident-agent dispatch loops so teammates can keep working across the full workflow run.
21+
- Runner-owned lead inbox summaries at `_agents/inbox-summary.json`, dynamic membership reconciliation, and new example workflows for live team reconfiguration.
22+
- TUI agent overview/chat surfaces for inline messaging and richer agent activity inspection.
23+
24+
### Changed
25+
26+
- Renamed the swarm-facing CLI, docs, examples, tests, and runtime modules to the `agents` surface so the feature matches the shipped vocabulary.
27+
- Agent coordination now keeps long-lived LLM teammates resident and routes lead communication through mailbox-backed workflow services instead of prompt-level polling.
28+
29+
### Fixed
30+
31+
- Added reply-token handling and expanded unit/acceptance coverage around agent mailbox, broker, and TUI chat behavior.
32+
33+
1634
## [0.1.8] - 2026-03-02
1735

1836
### Added
1937

20-
- Swarm v1: file-backed mailbox + task store for agent teams under `<context_dir>/_swarm/`.
21-
- New `roboppi swarm` CLI (JSON-only stdout) for members, messaging, tasks, and housekeeping (incl. `message recv --wait-ms`, claim-token ack, stale recovery).
22-
- Workflow YAML `swarm:` config for defining members + seeding tasks; inject `ROBOPPI_SWARM_*` env vars into steps.
23-
- Workflow TUI Swarm tab + `swarm_*` exec events for metadata-only activity visibility.
24-
- New worker capabilities `MAILBOX` and `TASKS` to gate Swarm tooling (Claude Code allowlist support).
38+
- Agents v1: file-backed mailbox + task store for agent teams under `<context_dir>/_agents/`.
39+
- New `roboppi agents` CLI (JSON-only stdout) for members, messaging, tasks, and housekeeping (incl. `message recv --wait-ms`, claim-token ack, stale recovery).
40+
- Workflow YAML `agents:` config for defining members + seeding tasks; inject `ROBOPPI_AGENTS_*` env vars into steps.
41+
- Workflow TUI Agents tab + `agent_*` exec events for metadata-only activity visibility.
42+
- New worker capabilities `MAILBOX` and `TASKS` to gate Agents tooling (Claude Code allowlist support).
43+
- Runner-owned `LeadInboxBroker` for auto-consuming lead inbox and maintaining `_agents/inbox-summary.json`.
44+
- Dynamic membership via `roboppi agents members set|upsert|remove` with coordinator reconcile loop.
2545

2646
### Changed
2747

28-
- Swarm conformance checklist is captured in `docs/spec/swarm.md` and enforced by the self-dev Swarm impl loop.
48+
- Agents conformance checklist is captured in `docs/spec/agents.md` and enforced by the self-dev Agents impl loop.
2949

3050
### Fixed
3151

32-
- Harden `roboppi swarm` tool-facing inputs with explicit path-safety validation and JSON-safe error output.
52+
- Harden `roboppi agents` tool-facing inputs with explicit path-safety validation and JSON-safe error output.
3353

3454
## [0.1.7] - 2026-03-01
3555

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# Agents: Resident Lead Inbox + Dynamic Members (Workflow-Scoped)
2+
3+
Status: implemented
4+
5+
Related:
6+
- Issue 21: "Agents: make lead a persistent inbox actor (auto-consume messages)"
7+
- `docs/features/agents.md`
8+
9+
This document extends the Agents design with two capabilities:
10+
11+
1) A runner-owned **resident lead inbox broker** that continuously consumes the lead's mailbox and maintains a stable "latest agents context" artifact.
12+
2) **Dynamic team membership** during a workflow run, driven by a file-backed desired-state config and reconciled by the runner.
13+
14+
This design intentionally keeps these behaviors **workflow-scoped** (only while a workflow is executing).
15+
16+
---
17+
18+
## 1. Problem
19+
20+
Agents teammates can be long-lived workers that continuously send findings to the lead. Today, the lead (the workflow itself) is not a resident actor, so receiving/triaging messages requires embedding explicit polling into step prompts (brittle, easy to forget).
21+
22+
Additionally, some workflows need to **change the team** based on runtime evidence (e.g. spawn a k8s-focused investigator when cluster-level symptoms appear). We want to allow the lead to do this directly, even if it is operationally risky.
23+
24+
---
25+
26+
## 2. Goals / Non-goals
27+
28+
Goals:
29+
- With Agents enabled, lead inbox messages are processed even when no lead step is currently executing.
30+
- Provide a stable artifact under `context_dir` for "latest agents context".
31+
- Allow the lead to modify team membership during workflow execution via `roboppi agents members ...`.
32+
- Deterministic shutdown: broker + dynamically spawned teammates stop cleanly when the workflow ends.
33+
34+
Non-goals (v1 of this doc):
35+
- LLM-based triage/decision making for inbox messages (no automatic management hook trigger).
36+
- Cross-workflow persistence (daemon-style always-on lead/teammates).
37+
- Distributed agents across multiple hosts.
38+
39+
---
40+
41+
## 3. Architecture Overview
42+
43+
We introduce two runner-owned components and one store/CLI extension.
44+
45+
### 3.1 LeadInboxBroker (runner-owned)
46+
47+
New component:
48+
- `LeadInboxBroker` (suggested path: `src/agents/lead-inbox-broker.ts`)
49+
50+
Responsibilities:
51+
- Poll/consume the lead's mailbox using Agent Store primitives (`recvMessages` + claim tokens + ack).
52+
- Update a bounded, secret-safe summary artifact.
53+
- Optionally emit lightweight `ExecEventSink` warnings/notifications when new actionable messages arrive.
54+
55+
Key property:
56+
- It is runner-owned and runs independently of step execution, so the lead behaves like a resident actor for inbox processing.
57+
58+
### 3.2 Dynamic membership reconcile loop (runner-owned)
59+
60+
Extend `AgentCoordinator` (or add a `AgentsTeamController`) to reconcile "desired members" from disk:
61+
62+
- The lead updates desired membership via CLI (see 3.3).
63+
- The coordinator periodically reads desired state and applies diffs:
64+
- spawn missing teammates
65+
- request shutdown for removed teammates
66+
67+
This keeps process management centralized and ensures deterministic workflow shutdown.
68+
69+
### 3.3 CLI + store extensions for membership mutation
70+
71+
Extend the Agents CLI (`src/agents/cli.ts`) with membership mutation commands that update the desired-state file atomically.
72+
73+
The lead can call these commands directly (danger accepted) to reshape the team at runtime.
74+
75+
---
76+
77+
## 4. Disk Artifacts
78+
79+
### 4.1 Lead inbox summary artifact
80+
81+
New stable artifact:
82+
83+
- `context/_agents/inbox-summary.json`
84+
85+
Design constraints:
86+
- Bounded size (both message count and per-entry size).
87+
- Secret-safe by default: do not copy full message bodies.
88+
- Provide pointers so a lead step can open the original message file if needed.
89+
90+
Suggested schema:
91+
92+
```json
93+
{
94+
"version": "1",
95+
"team_id": "uuid",
96+
"lead_member_id": "lead",
97+
"updated_at": 1710000000000,
98+
"unread_count": 3,
99+
"entries": [
100+
{
101+
"message_id": "uuid",
102+
"ts": 1710000000000,
103+
"from": "researcher",
104+
"topic": "findings",
105+
"kind": "text",
106+
"body_preview": "First 200 bytes...",
107+
"mailbox_path": "_agents/mailbox/inbox/lead/cur/<ts>-<id>.json"
108+
}
109+
]
110+
}
111+
```
112+
113+
Notes:
114+
- `mailbox_path` is workspace-relative (context-relative) so it can be opened via READ.
115+
- `body_preview` is optional; if present, it MUST be short and safe.
116+
117+
### 4.2 Desired membership state
118+
119+
We need a runner-readable desired-state file that the lead can update.
120+
121+
Option (preferred): extend `_agents/members.json` entries with an optional `agent` field.
122+
123+
- Existing type: `src/agents/types.ts` `MemberEntry`
124+
- Proposed addition: `agent?: string` (agent catalog id)
125+
126+
This keeps one canonical membership file and avoids adding another config file.
127+
128+
---
129+
130+
## 5. Runtime Flows
131+
132+
### 5.1 Workflow start
133+
134+
When `agents.enabled: true`:
135+
- `WorkflowExecutor` initializes agents context (`initAgentsContext`) and starts `AgentCoordinator`.
136+
- `WorkflowExecutor` starts `LeadInboxBroker` with the resolved `contextDir`, `teamId`, and `leadMemberId`.
137+
138+
### 5.2 Inbox consumption loop
139+
140+
The broker loops until workflow abort/shutdown:
141+
142+
1) `recvMessages({ memberId: leadMemberId, claim: true, max: N })`
143+
2) For each claimed message:
144+
- append/update `inbox-summary.json`
145+
- `ackMessageByClaimToken(...)`
146+
3) Sleep (poll interval) and repeat
147+
148+
Backpressure defaults:
149+
- `N` small (e.g. 10)
150+
- poll interval small but bounded (e.g. 1-2s)
151+
- summary bounded (e.g. keep last 100 entries; truncate previews)
152+
153+
Crash/interrupt safety:
154+
- Claimed-but-not-acked messages remain in `processing/` and will be requeued by existing housekeeping.
155+
156+
### 5.3 Dynamic membership mutation
157+
158+
The lead updates desired membership by writing to the Agent Store via CLI.
159+
160+
Example (conceptual):
161+
- `roboppi agents members upsert --member k8s --agent k8s-investigator`
162+
163+
Coordinator reconciliation loop:
164+
- On an interval (e.g. 2s), read desired membership from disk.
165+
- Compute diff against currently running teammate handles.
166+
- Apply:
167+
- spawn: create `_agent:<memberId>` long-lived worker task
168+
- remove: send `shutdown_request` message and then abort after a wait
169+
170+
Determinism:
171+
- Reconcile is idempotent.
172+
- Member ordering is deterministic (sort by `member_id`).
173+
174+
---
175+
176+
## 6. CLI Surface (Additions)
177+
178+
Add new subcommands under `roboppi agents members`:
179+
180+
- `roboppi agents members set --context <dir> --json-stdin`
181+
- Replaces the full desired member list (atomic).
182+
183+
- `roboppi agents members upsert --context <dir> --member <id> --agent <agentId> [--name <n>] [--role <r>]`
184+
- Adds or updates a member entry.
185+
186+
- `roboppi agents members remove --context <dir> --member <id>`
187+
- Removes a member entry.
188+
189+
Behavior constraints:
190+
- All stdout remains JSON-only.
191+
- Member mutation is restricted to the lead identity by default:
192+
- compare `team.json.lead_member_id` and `ROBOPPI_AGENTS_MEMBER_ID`
193+
- if mismatch: error
194+
195+
---
196+
197+
## 7. Safety and Guardrails (Explicitly Risky Feature)
198+
199+
Dynamic membership is operationally risky (can spawn many long-lived workers with `RUN_COMMANDS`). We still add a few guardrails to prevent accidental runaway:
200+
201+
- Explicit opt-in (recommended): `agents.dynamic_members.enabled: true` (DSL extension) OR env flag `ROBOPPI_AGENTS_DYNAMIC_MEMBERS=1`.
202+
- Hard caps:
203+
- `max_teammates` (default small)
204+
- spawn rate limit (e.g. N per minute)
205+
- Deterministic shutdown always wins:
206+
- on workflow end, coordinator stops all teammates regardless of desired state
207+
208+
---
209+
210+
## 8. Acceptance Criteria
211+
212+
- With `agents.enabled: true`, teammate messages to the lead are consumed without embedding `roboppi agents message recv ...` into step prompts.
213+
- `context/_agents/inbox-summary.json` exists and is updated as messages arrive.
214+
- Lead can modify team membership during workflow execution via `roboppi agents members ...`.
215+
- Coordinator spawns/removes teammates to match desired state within a bounded time.
216+
- Workflow shutdown stops broker + teammates deterministically.
217+
218+
---
219+
220+
## 9. Testing Strategy
221+
222+
Unit:
223+
- LeadInboxBroker summarization bounds (max entries, preview truncation).
224+
- Broker claim/ack behavior (ack by claim token).
225+
- Store writes for membership mutation are atomic.
226+
227+
Integration:
228+
- Spawn agents with 1 teammate, send messages, verify summary artifact updates without lead polling.
229+
- During run, mutate membership to add/remove a teammate; verify coordinator spawns/stops.
230+
- Shutdown behavior: workflow end stops broker and all teammates; no lingering timers.
231+
232+
---
233+
234+
## 10. Implementation Notes (Where Changes Landed)
235+
236+
Touch points:
237+
- `src/agents/lead-inbox-broker.ts`: new `LeadInboxBroker` class — runner-owned, polls lead inbox via claim+ack, writes bounded `inbox-summary.json`.
238+
- `src/workflow/executor.ts`: starts/stops `LeadInboxBroker` alongside `AgentCoordinator`; writes `MemberEntry.agent` from `WorkflowDefinition.agents.members`.
239+
- `src/agents/coordinator.ts`: reconcile loop periodically reads desired membership from `members.json`, spawns missing non-lead members, shuts down removed members; runaway guards (max teammates cap + spawn rate limit).
240+
- `src/agents/store.ts`: atomic membership mutation helpers — `writeMembersConfig()`, `upsertMember()`, `removeMember()`.
241+
- `src/agents/cli.ts`: new `members set|upsert|remove` subcommands with lead identity restriction.
242+
- `src/agents/types.ts`: `MemberEntry.agent?: string` for desired-state membership.
243+
- `src/agents/paths.ts`: `inboxSummaryPath()` for `_agents/inbox-summary.json`.
244+
- `src/agents/constants.ts`: broker and reconcile defaults (poll intervals, summary bounds, max teammates, spawn rate limit).
245+
246+
Core (`src/core/*`) remains unchanged: Agents is runner-owned policy orchestration built on mechanism-grade file operations.

0 commit comments

Comments
 (0)