opencode-swarm-plugin 0.12.31 → 0.13.1
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/.beads/issues.jsonl +204 -10
- package/.opencode/skills/tdd/SKILL.md +182 -0
- package/README.md +165 -17
- package/bun.lock +23 -0
- package/dist/index.js +4082 -457
- package/dist/pglite.data +0 -0
- package/dist/pglite.wasm +0 -0
- package/dist/plugin.js +4070 -533
- package/examples/commands/swarm.md +100 -28
- package/examples/skills/beads-workflow/SKILL.md +75 -28
- package/examples/skills/swarm-coordination/SKILL.md +165 -21
- package/global-skills/swarm-coordination/SKILL.md +116 -58
- package/global-skills/testing-patterns/SKILL.md +430 -0
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +586 -0
- package/package.json +11 -5
- package/src/index.ts +44 -5
- package/src/streams/agent-mail.test.ts +777 -0
- package/src/streams/agent-mail.ts +535 -0
- package/src/streams/debug.test.ts +500 -0
- package/src/streams/debug.ts +629 -0
- package/src/streams/effect/ask.integration.test.ts +314 -0
- package/src/streams/effect/ask.ts +202 -0
- package/src/streams/effect/cursor.integration.test.ts +418 -0
- package/src/streams/effect/cursor.ts +288 -0
- package/src/streams/effect/deferred.test.ts +357 -0
- package/src/streams/effect/deferred.ts +445 -0
- package/src/streams/effect/index.ts +17 -0
- package/src/streams/effect/layers.ts +73 -0
- package/src/streams/effect/lock.test.ts +385 -0
- package/src/streams/effect/lock.ts +399 -0
- package/src/streams/effect/mailbox.test.ts +260 -0
- package/src/streams/effect/mailbox.ts +318 -0
- package/src/streams/events.test.ts +628 -0
- package/src/streams/events.ts +214 -0
- package/src/streams/index.test.ts +229 -0
- package/src/streams/index.ts +492 -0
- package/src/streams/migrations.test.ts +355 -0
- package/src/streams/migrations.ts +269 -0
- package/src/streams/projections.test.ts +611 -0
- package/src/streams/projections.ts +302 -0
- package/src/streams/store.integration.test.ts +548 -0
- package/src/streams/store.ts +546 -0
- package/src/streams/swarm-mail.ts +552 -0
- package/src/swarm-mail.integration.test.ts +970 -0
- package/src/swarm-mail.ts +739 -0
- package/src/swarm.integration.test.ts +16 -10
- package/src/swarm.ts +146 -78
- package/src/tool-availability.ts +35 -2
- package/global-skills/mcp-tool-authoring/SKILL.md +0 -695
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: "Test-Driven Development workflow with RED-GREEN-REFACTOR, lore from Kent Beck, Michael Feathers, and Ousterhout's counterpoint"
|
|
4
|
+
tags:
|
|
5
|
+
- testing
|
|
6
|
+
- workflow
|
|
7
|
+
- methodology
|
|
8
|
+
- tdd
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Test-Driven Development (TDD)
|
|
12
|
+
|
|
13
|
+
## The Rhythm: RED-GREEN-REFACTOR
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. RED - Write failing test first (define expected behavior)
|
|
17
|
+
2. GREEN - Minimal implementation to pass (don't over-engineer)
|
|
18
|
+
3. REFACTOR - Clean up, remove duplication, run tests again
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Why TDD Works (The Lore)
|
|
22
|
+
|
|
23
|
+
### Kent Beck (Test-Driven Development by Example)
|
|
24
|
+
|
|
25
|
+
> "The act of writing a unit test is more an act of design than verification."
|
|
26
|
+
|
|
27
|
+
- Tests become executable documentation of intent
|
|
28
|
+
- "Fake it til you make it" - start with hardcoded values, generalize
|
|
29
|
+
- Small steps reduce debugging time
|
|
30
|
+
- Confidence to refactor comes from test coverage
|
|
31
|
+
|
|
32
|
+
### Michael Feathers (Working Effectively with Legacy Code)
|
|
33
|
+
|
|
34
|
+
> "The most powerful feature-addition technique I know of is test-driven development."
|
|
35
|
+
|
|
36
|
+
- TDD works in both OO and procedural code
|
|
37
|
+
- Writing tests first forces you to think about interfaces
|
|
38
|
+
- Tests are the safety net that enables aggressive refactoring
|
|
39
|
+
- Legacy code = code without tests
|
|
40
|
+
|
|
41
|
+
### Martin Fowler (Refactoring)
|
|
42
|
+
|
|
43
|
+
> "Kent Beck baked this habit of writing the test first into a technique called Test-Driven Development."
|
|
44
|
+
|
|
45
|
+
- TDD relies on short cycles
|
|
46
|
+
- Tests enable refactoring
|
|
47
|
+
- Refactoring becomes safe - tests catch regressions instantly
|
|
48
|
+
|
|
49
|
+
## The Counterpoint: Know When to Break the Rule
|
|
50
|
+
|
|
51
|
+
### John Ousterhout (A Philosophy of Software Design)
|
|
52
|
+
|
|
53
|
+
> "The problem with test-driven development is that it focuses attention on getting specific features working, rather than finding the best design."
|
|
54
|
+
|
|
55
|
+
**When TDD can hurt:**
|
|
56
|
+
|
|
57
|
+
- Can lead to tactical programming (feature-focused, not design-focused)
|
|
58
|
+
- May produce code that's easy to test but hard to understand
|
|
59
|
+
- Risk of over-testing implementation details
|
|
60
|
+
|
|
61
|
+
**The balance:**
|
|
62
|
+
|
|
63
|
+
- For exploratory/architectural work, design first, then add tests
|
|
64
|
+
- Don't let tests drive you into a corner
|
|
65
|
+
- Step back periodically to evaluate overall design
|
|
66
|
+
|
|
67
|
+
## When to Use TDD
|
|
68
|
+
|
|
69
|
+
✅ **Use TDD for:**
|
|
70
|
+
|
|
71
|
+
- New features with clear requirements
|
|
72
|
+
- Bug fixes (write test that reproduces bug first)
|
|
73
|
+
- Refactoring existing code (add characterization tests first)
|
|
74
|
+
- API design (tests reveal ergonomics)
|
|
75
|
+
- Any code that will be maintained long-term
|
|
76
|
+
|
|
77
|
+
❌ **Skip TDD for:**
|
|
78
|
+
|
|
79
|
+
- Exploratory spikes (but add tests after if keeping the code)
|
|
80
|
+
- Emergency hotfixes (but add tests immediately after)
|
|
81
|
+
- Pure UI/styling changes
|
|
82
|
+
- One-off scripts
|
|
83
|
+
- Throwaway prototypes
|
|
84
|
+
|
|
85
|
+
## The TDD Workflow
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# 1. Write test, watch it fail
|
|
89
|
+
bun test src/thing.test.ts # RED - test fails
|
|
90
|
+
|
|
91
|
+
# 2. Implement minimal code to pass
|
|
92
|
+
bun test src/thing.test.ts # GREEN - test passes
|
|
93
|
+
|
|
94
|
+
# 3. Refactor, tests still pass
|
|
95
|
+
bun test src/thing.test.ts # GREEN - still passing
|
|
96
|
+
|
|
97
|
+
# 4. Repeat for next behavior
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## TDD Patterns
|
|
101
|
+
|
|
102
|
+
### Start with the Assertion
|
|
103
|
+
|
|
104
|
+
Write the assertion first, then work backwards:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Start here
|
|
108
|
+
expect(result).toBe(42);
|
|
109
|
+
|
|
110
|
+
// Then figure out what 'result' is
|
|
111
|
+
const result = calculate(input);
|
|
112
|
+
|
|
113
|
+
// Then figure out what 'input' is
|
|
114
|
+
const input = { value: 21 };
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Triangulation
|
|
118
|
+
|
|
119
|
+
Use multiple examples to drive generalization:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
it("doubles 2", () => expect(double(2)).toBe(4));
|
|
123
|
+
it("doubles 3", () => expect(double(3)).toBe(6));
|
|
124
|
+
// Now you MUST implement the general solution
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Obvious Implementation
|
|
128
|
+
|
|
129
|
+
When the solution is obvious, just write it:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
function add(a: number, b: number): number {
|
|
133
|
+
return a + b; // Don't fake this
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Fake It Til You Make It
|
|
138
|
+
|
|
139
|
+
When unsure, start with hardcoded values:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// First pass
|
|
143
|
+
function fibonacci(n: number): number {
|
|
144
|
+
return 1; // Passes for n=1
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add test for n=2, then generalize
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Testing Pyramid
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
/\
|
|
154
|
+
/ \ E2E (few)
|
|
155
|
+
/----\
|
|
156
|
+
/ \ Integration (some)
|
|
157
|
+
/--------\
|
|
158
|
+
/ \ Unit (many)
|
|
159
|
+
--------------
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- **Unit tests**: Fast, isolated, test one thing
|
|
163
|
+
- **Integration tests**: Test component interactions
|
|
164
|
+
- **E2E tests**: Test full user flows (expensive, use sparingly)
|
|
165
|
+
|
|
166
|
+
## Common TDD Mistakes
|
|
167
|
+
|
|
168
|
+
1. **Writing too many tests at once** - One failing test at a time
|
|
169
|
+
2. **Testing implementation, not behavior** - Test what, not how
|
|
170
|
+
3. **Skipping the refactor step** - Technical debt accumulates
|
|
171
|
+
4. **Over-mocking** - Don't mock what you don't own
|
|
172
|
+
5. **Testing private methods** - Test through public interface
|
|
173
|
+
|
|
174
|
+
## Integration with Beads
|
|
175
|
+
|
|
176
|
+
When working on a bead:
|
|
177
|
+
|
|
178
|
+
1. Start bead: `beads_start(id="bd-123")`
|
|
179
|
+
2. Write failing test for the requirement
|
|
180
|
+
3. Implement to pass
|
|
181
|
+
4. Refactor
|
|
182
|
+
5. Close bead: `beads_close(id="bd-123", reason="Done: tests passing")`
|
package/README.md
CHANGED
|
@@ -36,11 +36,11 @@ The setup wizard handles everything:
|
|
|
36
36
|
◆ OpenCode
|
|
37
37
|
◆ Beads
|
|
38
38
|
◆ Go
|
|
39
|
-
▲
|
|
39
|
+
▲ Swarm Mail (optional)
|
|
40
40
|
▲ Redis (optional)
|
|
41
41
|
│
|
|
42
42
|
◆ Install optional dependencies?
|
|
43
|
-
│ ◻
|
|
43
|
+
│ ◻ Swarm Mail - Multi-agent coordination
|
|
44
44
|
│ ◻ Redis - Rate limiting
|
|
45
45
|
│
|
|
46
46
|
◇ Setting up OpenCode integration...
|
|
@@ -271,16 +271,16 @@ What NOT to do...
|
|
|
271
271
|
|
|
272
272
|
## Dependencies
|
|
273
273
|
|
|
274
|
-
| Dependency | Purpose
|
|
275
|
-
| ------------------------------------------------------------------------------------------------------ |
|
|
276
|
-
| [OpenCode](https://opencode.ai) | Plugin host
|
|
277
|
-
| [Beads](https://github.com/steveyegge/beads) | Git-backed issue tracking
|
|
278
|
-
| [Go](https://go.dev) | Required for
|
|
279
|
-
| [MCP Agent Mail](https://github.com/Dicklesworthstone/mcp_agent_mail)
|
|
280
|
-
| [CASS (Coding Agent Session Search)](https://github.com/Dicklesworthstone/coding_agent_session_search) | Historical context from past sessions
|
|
281
|
-
| [UBS (Ultimate Bug Scanner)](https://github.com/Dicklesworthstone/ultimate_bug_scanner) | Pre-completion bug scanning using AI-powered static analysis
|
|
282
|
-
| [semantic-memory](https://github.com/joelhooks/semantic-memory) | Learning persistence
|
|
283
|
-
| [Redis](https://redis.io) | Rate limiting (SQLite fallback available)
|
|
274
|
+
| Dependency | Purpose | Required |
|
|
275
|
+
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------- |
|
|
276
|
+
| [OpenCode](https://opencode.ai) | Plugin host | Yes |
|
|
277
|
+
| [Beads](https://github.com/steveyegge/beads) | Git-backed issue tracking | Yes |
|
|
278
|
+
| [Go](https://go.dev) | Required for Swarm Mail | No |
|
|
279
|
+
| [MCP Agent Mail](https://github.com/Dicklesworthstone/mcp_agent_mail) ⭐ | **The original** - Multi-agent coordination, file reservations. Swarm Mail is built on these patterns. | No |
|
|
280
|
+
| [CASS (Coding Agent Session Search)](https://github.com/Dicklesworthstone/coding_agent_session_search) | Historical context from past sessions | No |
|
|
281
|
+
| [UBS (Ultimate Bug Scanner)](https://github.com/Dicklesworthstone/ultimate_bug_scanner) | Pre-completion bug scanning using AI-powered static analysis | No |
|
|
282
|
+
| [semantic-memory](https://github.com/joelhooks/semantic-memory) | Learning persistence | No |
|
|
283
|
+
| [Redis](https://redis.io) | Rate limiting (SQLite fallback available) | No |
|
|
284
284
|
|
|
285
285
|
All dependencies are checked and can be installed via `swarm setup`.
|
|
286
286
|
|
|
@@ -298,12 +298,14 @@ curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/ultimate_bug_sca
|
|
|
298
298
|
curl -fsSL https://raw.githubusercontent.com/Dicklesworthstone/coding_agent_session_search/main/install.sh | bash -s -- --easy-mode
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
-
**MCP Agent Mail** -
|
|
301
|
+
**MCP Agent Mail** ⭐ - The original multi-agent coordination system. Swarm Mail's embedded implementation is inspired by and compatible with MCP Agent Mail's protocol:
|
|
302
302
|
|
|
303
303
|
```bash
|
|
304
304
|
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/main/scripts/install.sh" | bash -s -- --yes
|
|
305
305
|
```
|
|
306
306
|
|
|
307
|
+
> **Note:** The embedded Swarm Mail (PGLite in-process) is now the primary option and works out of the box with no external dependencies. MCP Agent Mail (external Go server) is still supported for advanced use cases requiring a separate server process.
|
|
308
|
+
|
|
307
309
|
## Tools Reference
|
|
308
310
|
|
|
309
311
|
### Swarm
|
|
@@ -315,7 +317,7 @@ curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/m
|
|
|
315
317
|
| `swarm_plan_prompt` | Generate strategy-specific planning prompt with CASS history |
|
|
316
318
|
| `swarm_decompose` | Generate decomposition prompt |
|
|
317
319
|
| `swarm_validate_decomposition` | Validate response, detect file conflicts |
|
|
318
|
-
| `swarm_spawn_subtask` | Generate worker agent prompt with
|
|
320
|
+
| `swarm_spawn_subtask` | Generate worker agent prompt with Swarm Mail/beads instructions |
|
|
319
321
|
| `swarm_status` | Get swarm progress by epic ID |
|
|
320
322
|
| `swarm_progress` | Report subtask progress to coordinator |
|
|
321
323
|
| `swarm_complete` | Complete subtask - runs UBS (Ultimate Bug Scanner), releases reservations |
|
|
@@ -333,9 +335,22 @@ curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/m
|
|
|
333
335
|
| `beads_start` | Mark bead as in-progress |
|
|
334
336
|
| `beads_ready` | Get next unblocked bead |
|
|
335
337
|
| `beads_sync` | Sync to git and push |
|
|
336
|
-
| `beads_link_thread` | Link bead to
|
|
338
|
+
| `beads_link_thread` | Link bead to Swarm Mail thread |
|
|
339
|
+
|
|
340
|
+
### Swarm Mail (Embedded - Primary)
|
|
341
|
+
|
|
342
|
+
| Tool | Description |
|
|
343
|
+
| ------------------------ | --------------------------------------------- |
|
|
344
|
+
| `swarmmail_init` | Initialize session, register agent |
|
|
345
|
+
| `swarmmail_send` | Send message to agents |
|
|
346
|
+
| `swarmmail_inbox` | Fetch inbox (max 5, no bodies - context safe) |
|
|
347
|
+
| `swarmmail_read_message` | Fetch single message body by ID |
|
|
348
|
+
| `swarmmail_reserve` | Reserve file paths for exclusive editing |
|
|
349
|
+
| `swarmmail_release` | Release file reservations |
|
|
350
|
+
| `swarmmail_ack` | Acknowledge message |
|
|
351
|
+
| `swarmmail_health` | Check embedded database health |
|
|
337
352
|
|
|
338
|
-
### Agent Mail
|
|
353
|
+
### Agent Mail (Legacy MCP - Optional)
|
|
339
354
|
|
|
340
355
|
| Tool | Description |
|
|
341
356
|
| ---------------------------- | ---------------------------------------------- |
|
|
@@ -348,7 +363,140 @@ curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/m
|
|
|
348
363
|
| `agentmail_release` | Release file reservations |
|
|
349
364
|
| `agentmail_ack` | Acknowledge message |
|
|
350
365
|
| `agentmail_search` | Search messages by keyword |
|
|
351
|
-
| `agentmail_health` | Check if Agent Mail server is running
|
|
366
|
+
| `agentmail_health` | Check if MCP Agent Mail server is running |
|
|
367
|
+
|
|
368
|
+
## Event-Sourced Architecture (Embedded)
|
|
369
|
+
|
|
370
|
+
> **🙏 Built on the shoulders of giants**
|
|
371
|
+
>
|
|
372
|
+
> The Swarm Mail system is deeply inspired by and builds upon [**MCP Agent Mail**](https://github.com/Dicklesworthstone/mcp_agent_mail) by [@Dicklesworthstone](https://github.com/Dicklesworthstone). The original MCP Agent Mail pioneered multi-agent coordination patterns including file reservations, thread-based messaging, and agent registration - concepts that form the foundation of this embedded implementation.
|
|
373
|
+
>
|
|
374
|
+
> If you need a production-ready, battle-tested solution with a full Go server, **use MCP Agent Mail directly**. This embedded version is an experimental alternative that trades the external server for in-process PGLite, optimized for single-machine development workflows.
|
|
375
|
+
>
|
|
376
|
+
> **Key contributions from MCP Agent Mail:**
|
|
377
|
+
>
|
|
378
|
+
> - File reservation protocol with conflict detection
|
|
379
|
+
> - Agent registration and heartbeat patterns
|
|
380
|
+
> - Thread-based message organization
|
|
381
|
+
> - Importance levels and acknowledgment tracking
|
|
382
|
+
>
|
|
383
|
+
> Thank you to the MCP Agent Mail team for open-sourcing such a well-designed system.
|
|
384
|
+
|
|
385
|
+
The plugin includes an embedded event-sourced Swarm Mail implementation as an alternative to the external MCP server. This provides the same multi-agent coordination capabilities without requiring a separate server process.
|
|
386
|
+
|
|
387
|
+
### Architecture Comparison
|
|
388
|
+
|
|
389
|
+
**MCP-based (external):**
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
plugin tools → HTTP → MCP Server (Go process) → SQLite
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Event-sourced (embedded):**
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
plugin tools → streams/agent-mail.ts → streams/store.ts → PGLite (in-process)
|
|
399
|
+
↓
|
|
400
|
+
streams/projections.ts
|
|
401
|
+
↓
|
|
402
|
+
Materialized views (agents, messages, reservations)
|
|
403
|
+
↓
|
|
404
|
+
Fast reads
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Architecture Flow
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
411
|
+
│ Plugin Tools Layer │
|
|
412
|
+
│ (agentmail_init, agentmail_send, agentmail_reserve, etc.) │
|
|
413
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
414
|
+
│
|
|
415
|
+
▼
|
|
416
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
417
|
+
│ streams/agent-mail.ts │
|
|
418
|
+
│ (High-level API wrapper) │
|
|
419
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
420
|
+
│
|
|
421
|
+
▼
|
|
422
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
423
|
+
│ streams/events.ts │
|
|
424
|
+
│ 11 event types: agent_registered, message_sent, │
|
|
425
|
+
│ file_reserved, message_read, message_acked, etc. │
|
|
426
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
427
|
+
│
|
|
428
|
+
▼
|
|
429
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
430
|
+
│ streams/store.ts │
|
|
431
|
+
│ Append-only event log (PGLite storage) │
|
|
432
|
+
│ appendEvent() • readEvents() • replayEvents() │
|
|
433
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
434
|
+
│
|
|
435
|
+
▼
|
|
436
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
437
|
+
│ streams/projections.ts │
|
|
438
|
+
│ Build materialized views from events │
|
|
439
|
+
│ getAgents() • getInbox() • getActiveReservations() │
|
|
440
|
+
│ checkConflicts() • threadStats() │
|
|
441
|
+
└─────────────────────────────┬────────────────────────────────────┘
|
|
442
|
+
│
|
|
443
|
+
▼
|
|
444
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
445
|
+
│ Materialized Tables (Derived State) │
|
|
446
|
+
│ agents • messages • reservations • message_reads │
|
|
447
|
+
│ (Rebuilt by replaying event log) │
|
|
448
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Module Descriptions
|
|
452
|
+
|
|
453
|
+
| Module | Responsibility |
|
|
454
|
+
| -------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
455
|
+
| **streams/events.ts** | Zod schemas for 11 event types (agent_registered, message_sent, file_reserved, task_progress, etc) |
|
|
456
|
+
| **streams/store.ts** | Append-only event log with PGLite backend (appendEvent, readEvents, replayEvents) |
|
|
457
|
+
| **streams/projections.ts** | Materialize views from events (getAgents, getInbox, checkConflicts, threadStats) |
|
|
458
|
+
| **streams/agent-mail.ts** | High-level API matching MCP interface (initAgent, sendAgentMessage, reserveAgentFiles) |
|
|
459
|
+
| **streams/debug.ts** | Debugging utilities (debugEvents, debugAgent, debugMessage, inspectState) |
|
|
460
|
+
|
|
461
|
+
### Key Benefits
|
|
462
|
+
|
|
463
|
+
- **No external dependencies** - Runs in-process with PGLite (Postgres compiled to WASM)
|
|
464
|
+
- **Full audit trail** - Every state change is an immutable event
|
|
465
|
+
- **Crash recovery** - Rebuild state by replaying events from log
|
|
466
|
+
- **Time-travel debugging** - Replay events up to any point in time
|
|
467
|
+
- **Testability** - 127 tests passing across streams module
|
|
468
|
+
- **Durable Streams protocol** - Inspired by Electric SQL's event sourcing patterns
|
|
469
|
+
|
|
470
|
+
### Event Types
|
|
471
|
+
|
|
472
|
+
The system emits 11 event types tracked in `streams/events.ts`:
|
|
473
|
+
|
|
474
|
+
| Event | Triggered By |
|
|
475
|
+
| ------------------ | ------------------------------------- |
|
|
476
|
+
| `agent_registered` | Agent initialization |
|
|
477
|
+
| `message_sent` | Sending inter-agent message |
|
|
478
|
+
| `file_reserved` | Reserving files for exclusive editing |
|
|
479
|
+
| `file_released` | Releasing file reservations |
|
|
480
|
+
| `message_read` | Reading a message |
|
|
481
|
+
| `message_acked` | Acknowledging a message |
|
|
482
|
+
| `task_started` | Starting work on a bead |
|
|
483
|
+
| `task_progress` | Reporting progress update |
|
|
484
|
+
| `task_completed` | Completing a bead |
|
|
485
|
+
| `task_blocked` | Marking a task as blocked |
|
|
486
|
+
| `agent_active` | Agent heartbeat/keep-alive |
|
|
487
|
+
|
|
488
|
+
### Materialized Views
|
|
489
|
+
|
|
490
|
+
Projections build these derived tables from the event log:
|
|
491
|
+
|
|
492
|
+
| View | Contains |
|
|
493
|
+
| --------------- | ------------------------------------------------------ |
|
|
494
|
+
| `agents` | Registered agents with metadata and last activity |
|
|
495
|
+
| `messages` | All inter-agent messages with thread/importance |
|
|
496
|
+
| `reservations` | Active file reservations with TTL and exclusivity flag |
|
|
497
|
+
| `message_reads` | Read receipts for message tracking |
|
|
498
|
+
|
|
499
|
+
State is always derived - delete the tables and replay events to rebuild.
|
|
352
500
|
|
|
353
501
|
### Structured Output
|
|
354
502
|
|
package/bun.lock
CHANGED
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
"name": "opencode-swarm-plugin",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@clack/prompts": "^0.11.0",
|
|
9
|
+
"@effect/schema": "^0.75.5",
|
|
10
|
+
"@electric-sql/pglite": "0.3.14",
|
|
9
11
|
"@opencode-ai/plugin": "^1.0.134",
|
|
12
|
+
"effect": "^3.19.12",
|
|
10
13
|
"gray-matter": "^4.0.3",
|
|
11
14
|
"ioredis": "^5.4.1",
|
|
15
|
+
"minimatch": "^10.1.1",
|
|
12
16
|
"zod": "4.1.8",
|
|
13
17
|
},
|
|
14
18
|
"devDependencies": {
|
|
15
19
|
"@types/bun": "latest",
|
|
20
|
+
"@types/minimatch": "^6.0.0",
|
|
16
21
|
"typescript": "^5.7.0",
|
|
17
22
|
"vitest": "^4.0.15",
|
|
18
23
|
},
|
|
@@ -26,6 +31,10 @@
|
|
|
26
31
|
|
|
27
32
|
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
|
28
33
|
|
|
34
|
+
"@effect/schema": ["@effect/schema@0.75.5", "", { "dependencies": { "fast-check": "^3.21.0" }, "peerDependencies": { "effect": "^3.9.2" } }, "sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw=="],
|
|
35
|
+
|
|
36
|
+
"@electric-sql/pglite": ["@electric-sql/pglite@0.3.14", "", {}, "sha512-3DB258dhqdsArOI1fIt7cb9RpUOgcDg5hXWVgVHAeqVQ/qxtFy605QKs4gx6mFq3jWsSPqDN8TgSEsqC3OfV9Q=="],
|
|
37
|
+
|
|
29
38
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
|
30
39
|
|
|
31
40
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
|
@@ -80,6 +89,10 @@
|
|
|
80
89
|
|
|
81
90
|
"@ioredis/commands": ["@ioredis/commands@1.4.0", "", {}, "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ=="],
|
|
82
91
|
|
|
92
|
+
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
|
|
93
|
+
|
|
94
|
+
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
|
95
|
+
|
|
83
96
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
|
84
97
|
|
|
85
98
|
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.0.134", "", { "dependencies": { "@opencode-ai/sdk": "1.0.134", "zod": "4.1.8" } }, "sha512-W5305/s7nVOtM4ha+qZ3iafY2h52D4zvPSylzq/ge0VVxowB/Ge1Tu+3mMUcwnoEcOOwGok7267SP9+4ulaK2w=="],
|
|
@@ -140,6 +153,8 @@
|
|
|
140
153
|
|
|
141
154
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
|
142
155
|
|
|
156
|
+
"@types/minimatch": ["@types/minimatch@6.0.0", "", { "dependencies": { "minimatch": "*" } }, "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA=="],
|
|
157
|
+
|
|
143
158
|
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
|
144
159
|
|
|
145
160
|
"@vitest/expect": ["@vitest/expect@4.0.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.15", "@vitest/utils": "4.0.15", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w=="],
|
|
@@ -170,6 +185,8 @@
|
|
|
170
185
|
|
|
171
186
|
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
|
172
187
|
|
|
188
|
+
"effect": ["effect@3.19.12", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-7F9RGTrCTC3D7nh9Zw+3VlJWwZgo5k33KA+476BAaD0rKIXKZsY/jQ+ipyhR/Avo239Fi6GqAVFs1mqM1IJ7yg=="],
|
|
189
|
+
|
|
173
190
|
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
|
174
191
|
|
|
175
192
|
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
|
@@ -182,6 +199,8 @@
|
|
|
182
199
|
|
|
183
200
|
"extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
|
184
201
|
|
|
202
|
+
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
|
203
|
+
|
|
185
204
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
|
186
205
|
|
|
187
206
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
|
@@ -202,6 +221,8 @@
|
|
|
202
221
|
|
|
203
222
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
|
204
223
|
|
|
224
|
+
"minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
|
225
|
+
|
|
205
226
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
206
227
|
|
|
207
228
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
|
@@ -216,6 +237,8 @@
|
|
|
216
237
|
|
|
217
238
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
|
218
239
|
|
|
240
|
+
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
|
241
|
+
|
|
219
242
|
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
|
220
243
|
|
|
221
244
|
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
|