opencode-swarm-plugin 0.9.0 → 0.10.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/README.md CHANGED
@@ -3,7 +3,21 @@
3
3
  [![npm version](https://img.shields.io/npm/v/opencode-swarm-plugin.svg)](https://www.npmjs.com/package/opencode-swarm-plugin)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Multi-agent swarm coordination for [OpenCode](https://opencode.ai). Break complex tasks into parallel subtasks, spawn agents, coordinate via messaging. The plugin learns from outcomes to improve future decompositions.
6
+ ```
7
+ ███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗
8
+ ██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║
9
+ ███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║
10
+ ╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║
11
+ ███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║
12
+ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
13
+
14
+ \ ` - ' /
15
+ - .(o o). -
16
+ ( >.< ) Multi-agent coordination for OpenCode
17
+ /| |\ Break complex tasks into parallel subtasks,
18
+ (_| |_) spawn agents, coordinate via messaging.
19
+ bzzzz... The plugin learns from outcomes.
20
+ ```
7
21
 
8
22
  ## Install
9
23
 
@@ -12,10 +26,10 @@ npm install -g opencode-swarm-plugin
12
26
  swarm setup
13
27
  ```
14
28
 
15
- That's it. The setup wizard handles everything:
29
+ The setup wizard handles everything:
16
30
 
17
31
  ```
18
- ┌ opencode-swarm-plugin v0.9.0
32
+ ┌ opencode-swarm-plugin v0.10.0
19
33
 
20
34
  ◇ Checking dependencies...
21
35
 
@@ -51,72 +65,138 @@ swarm init
51
65
  swarm setup Interactive installer - checks and installs all dependencies
52
66
  swarm doctor Health check - shows status of all dependencies
53
67
  swarm init Initialize beads in current project
54
- swarm version Show version
68
+ swarm config Show paths to generated config files
69
+ swarm version Show version and banner
55
70
  swarm help Show help
56
71
  ```
57
72
 
58
- ### swarm doctor
73
+ ## Usage
74
+
75
+ In OpenCode:
59
76
 
60
77
  ```
61
- swarm doctor v0.9.0
62
-
63
- ◇ Required dependencies:
64
-
65
- ◆ OpenCode v1.0.134
66
- ◆ Beads v0.29.0
67
-
68
- ◇ Optional dependencies:
69
-
70
- ◆ Go v1.25.2 - Required for Agent Mail
71
- ▲ Agent Mail - not found
72
- ◆ Redis - Rate limiting
73
-
74
- └ All required dependencies installed. 1 optional missing.
78
+ /swarm "Add user authentication with OAuth"
75
79
  ```
76
80
 
77
- ### swarm init
81
+ Or invoke the planner directly:
78
82
 
79
83
  ```
80
- swarm init v0.9.0
81
-
82
- ◇ Initializing beads...
83
- ◆ Created .beads/ directory
84
-
85
- ◆ Create your first bead?
86
- │ ● Yes / ○ No
87
-
88
- ◇ Bead title: Implement user authentication
89
- ◇ Type: Feature
90
-
91
- └ Project initialized!
84
+ @swarm-planner "Refactor all components to use hooks"
92
85
  ```
93
86
 
94
- ## Usage
87
+ ## Customization
95
88
 
96
- In OpenCode:
89
+ Run `swarm config` to see your config file paths:
97
90
 
98
91
  ```
99
- /swarm "Add user authentication with OAuth"
92
+ 🔌 Plugin loader
93
+ ~/.config/opencode/plugins/swarm.ts
94
+
95
+ 📜 /swarm command prompt
96
+ ~/.config/opencode/commands/swarm.md
97
+
98
+ 🤖 @swarm-planner agent
99
+ ~/.config/opencode/agents/swarm-planner.md
100
100
  ```
101
101
 
102
- Or invoke the planner directly:
102
+ ### /swarm Command
103
+
104
+ The `/swarm` command is defined in `~/.config/opencode/commands/swarm.md`:
105
+
106
+ ```markdown
107
+ ---
108
+ description: Decompose task into parallel subtasks and coordinate agents
109
+ ---
110
+
111
+ You are a swarm coordinator. Take a complex task, break it into beads,
112
+ and unleash parallel agents.
113
+
114
+ ## Usage
115
+
116
+ /swarm <task description or bead-id>
117
+
118
+ ## Workflow
103
119
 
120
+ 1. **Initialize**: `agentmail_init` with project_path and task_description
121
+ 2. **Decompose**: Use `swarm_select_strategy` then `swarm_plan_prompt`
122
+ 3. **Create beads**: `beads_create_epic` with subtasks and file assignments
123
+ 4. **Reserve files**: `agentmail_reserve` for each subtask's files
124
+ 5. **Spawn agents**: Use Task tool with `swarm_spawn_subtask` prompts
125
+ 6. **Monitor**: Check `agentmail_inbox` for progress
126
+ 7. **Complete**: `swarm_complete` when done, then `beads_sync` to push
127
+
128
+ ## Strategy Selection
129
+
130
+ | Strategy | Best For | Keywords |
131
+ | ------------- | ----------------------- | ------------------------------------- |
132
+ | file-based | Refactoring, migrations | refactor, migrate, rename, update all |
133
+ | feature-based | New features | add, implement, build, create |
134
+ | risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
135
+
136
+ Begin decomposition now.
104
137
  ```
105
- @swarm-planner "Refactor all components to use hooks"
138
+
139
+ ### @swarm-planner Agent
140
+
141
+ The `@swarm-planner` agent is defined in `~/.config/opencode/agents/swarm-planner.md`:
142
+
143
+ ````markdown
144
+ ---
145
+ name: swarm-planner
146
+ description: Strategic task decomposition for swarm coordination
147
+ model: claude-sonnet-4-5
148
+ ---
149
+
150
+ You are a swarm planner. Decompose tasks into optimal parallel subtasks.
151
+
152
+ ## Workflow
153
+
154
+ 1. Call `swarm_select_strategy` to analyze the task
155
+ 2. Call `swarm_plan_prompt` to get strategy-specific guidance
156
+ 3. Create a BeadTree following the guidelines
157
+ 4. Return ONLY valid JSON - no markdown, no explanation
158
+
159
+ ## Output Format
160
+
161
+ ```json
162
+ {
163
+ "epic": { "title": "...", "description": "..." },
164
+ "subtasks": [
165
+ {
166
+ "title": "...",
167
+ "description": "...",
168
+ "files": ["src/..."],
169
+ "dependencies": [],
170
+ "estimated_complexity": 2
171
+ }
172
+ ]
173
+ }
106
174
  ```
175
+ ````
176
+
177
+ ## Rules
178
+
179
+ - 2-7 subtasks (too few = not parallel, too many = overhead)
180
+ - No file overlap between subtasks
181
+ - Include tests with the code they test
182
+ - Order by dependency (if B needs A, A comes first)
183
+
184
+ ````
185
+
186
+ Edit these files to customize behavior. Run `swarm setup` to regenerate defaults.
107
187
 
108
188
  ## Dependencies
109
189
 
110
- | Dependency | Purpose | Required |
111
- | --------------------------------------------------------------- | ------------------------------------------- | -------- |
112
- | [OpenCode](https://opencode.ai) | Plugin host | Yes |
113
- | [Beads](https://github.com/steveyegge/beads) | Git-backed issue tracking | Yes |
114
- | [Go](https://go.dev) | Required for Agent Mail | No |
115
- | [Agent Mail](https://github.com/joelhooks/agent-mail) | Multi-agent coordination, file reservations | No |
116
- | [CASS](https://github.com/Dicklesworthstone/cass) | Historical context from past sessions | No |
117
- | [UBS](https://github.com/joelhooks/ubs) | Pre-completion bug scanning | No |
118
- | [semantic-memory](https://github.com/joelhooks/semantic-memory) | Learning persistence | No |
119
- | [Redis](https://redis.io) | Rate limiting (SQLite fallback available) | No |
190
+ | Dependency | Purpose | Required |
191
+ |------------|---------|----------|
192
+ | [OpenCode](https://opencode.ai) | Plugin host | Yes |
193
+ | [Beads](https://github.com/steveyegge/beads) | Git-backed issue tracking | Yes |
194
+ | [Go](https://go.dev) | Required for Agent Mail | No |
195
+ | [Agent Mail](https://github.com/joelhooks/agent-mail) | Multi-agent coordination, file reservations | No |
196
+ | [CASS](https://github.com/Dicklesworthstone/cass) | Historical context from past sessions | No |
197
+ | [UBS](https://github.com/joelhooks/ubs) | Pre-completion bug scanning | No |
198
+ | [semantic-memory](https://github.com/joelhooks/semantic-memory) | Learning persistence | No |
199
+ | [Redis](https://redis.io) | Rate limiting (SQLite fallback available) | No |
120
200
 
121
201
  All dependencies are checked and can be installed via `swarm setup`.
122
202
 
@@ -124,58 +204,57 @@ All dependencies are checked and can be installed via `swarm setup`.
124
204
 
125
205
  ### Swarm
126
206
 
127
- | Tool | Description |
128
- | ------------------------------ | ------------------------------------------------------------------------ |
129
- | `swarm_init` | Initialize swarm session |
130
- | `swarm_select_strategy` | Analyze task, recommend decomposition strategy (file/feature/risk-based) |
131
- | `swarm_plan_prompt` | Generate strategy-specific planning prompt with CASS history |
132
- | `swarm_decompose` | Generate decomposition prompt |
133
- | `swarm_validate_decomposition` | Validate response, detect file conflicts |
134
- | `swarm_spawn_subtask` | Generate worker agent prompt with Agent Mail/beads instructions |
135
- | `swarm_subtask_prompt` | Alias for spawn_subtask |
136
- | `swarm_status` | Get swarm progress by epic ID |
137
- | `swarm_progress` | Report subtask progress to coordinator |
138
- | `swarm_complete` | Complete subtask - runs UBS scan, releases reservations |
139
- | `swarm_record_outcome` | Record outcome for learning (duration, errors, retries) |
207
+ | Tool | Description |
208
+ |------|-------------|
209
+ | `swarm_init` | Initialize swarm session |
210
+ | `swarm_select_strategy` | Analyze task, recommend decomposition strategy (file/feature/risk-based) |
211
+ | `swarm_plan_prompt` | Generate strategy-specific planning prompt with CASS history |
212
+ | `swarm_decompose` | Generate decomposition prompt |
213
+ | `swarm_validate_decomposition` | Validate response, detect file conflicts |
214
+ | `swarm_spawn_subtask` | Generate worker agent prompt with Agent Mail/beads instructions |
215
+ | `swarm_status` | Get swarm progress by epic ID |
216
+ | `swarm_progress` | Report subtask progress to coordinator |
217
+ | `swarm_complete` | Complete subtask - runs UBS scan, releases reservations |
218
+ | `swarm_record_outcome` | Record outcome for learning (duration, errors, retries) |
140
219
 
141
220
  ### Beads
142
221
 
143
- | Tool | Description |
144
- | ------------------- | ---------------------------------------------- |
145
- | `beads_create` | Create bead with type-safe validation |
146
- | `beads_create_epic` | Create epic + subtasks atomically |
147
- | `beads_query` | Query beads with filters (status, type, ready) |
148
- | `beads_update` | Update status/description/priority |
149
- | `beads_close` | Close bead with reason |
150
- | `beads_start` | Mark bead as in-progress |
151
- | `beads_ready` | Get next unblocked bead |
152
- | `beads_sync` | Sync to git and push |
153
- | `beads_link_thread` | Link bead to Agent Mail thread |
222
+ | Tool | Description |
223
+ |------|-------------|
224
+ | `beads_create` | Create bead with type-safe validation |
225
+ | `beads_create_epic` | Create epic + subtasks atomically |
226
+ | `beads_query` | Query beads with filters (status, type, ready) |
227
+ | `beads_update` | Update status/description/priority |
228
+ | `beads_close` | Close bead with reason |
229
+ | `beads_start` | Mark bead as in-progress |
230
+ | `beads_ready` | Get next unblocked bead |
231
+ | `beads_sync` | Sync to git and push |
232
+ | `beads_link_thread` | Link bead to Agent Mail thread |
154
233
 
155
234
  ### Agent Mail
156
235
 
157
- | Tool | Description |
158
- | ---------------------------- | ---------------------------------------------- |
159
- | `agentmail_init` | Initialize session, register agent |
160
- | `agentmail_send` | Send message to agents |
161
- | `agentmail_inbox` | Fetch inbox (max 5, no bodies - context safe) |
162
- | `agentmail_read_message` | Fetch single message body by ID |
236
+ | Tool | Description |
237
+ |------|-------------|
238
+ | `agentmail_init` | Initialize session, register agent |
239
+ | `agentmail_send` | Send message to agents |
240
+ | `agentmail_inbox` | Fetch inbox (max 5, no bodies - context safe) |
241
+ | `agentmail_read_message` | Fetch single message body by ID |
163
242
  | `agentmail_summarize_thread` | Summarize thread (preferred over fetching all) |
164
- | `agentmail_reserve` | Reserve file paths for exclusive editing |
165
- | `agentmail_release` | Release file reservations |
166
- | `agentmail_ack` | Acknowledge message |
167
- | `agentmail_search` | Search messages by keyword |
168
- | `agentmail_health` | Check if Agent Mail server is running |
243
+ | `agentmail_reserve` | Reserve file paths for exclusive editing |
244
+ | `agentmail_release` | Release file reservations |
245
+ | `agentmail_ack` | Acknowledge message |
246
+ | `agentmail_search` | Search messages by keyword |
247
+ | `agentmail_health` | Check if Agent Mail server is running |
169
248
 
170
249
  ### Structured Output
171
250
 
172
- | Tool | Description |
173
- | -------------------------------- | ----------------------------------------------------- |
174
- | `structured_extract_json` | Extract JSON from markdown/text (multiple strategies) |
175
- | `structured_validate` | Validate response against schema |
176
- | `structured_parse_evaluation` | Parse self-evaluation response |
177
- | `structured_parse_decomposition` | Parse task decomposition response |
178
- | `structured_parse_bead_tree` | Parse bead tree for epic creation |
251
+ | Tool | Description |
252
+ |------|-------------|
253
+ | `structured_extract_json` | Extract JSON from markdown/text (multiple strategies) |
254
+ | `structured_validate` | Validate response against schema |
255
+ | `structured_parse_evaluation` | Parse self-evaluation response |
256
+ | `structured_parse_decomposition` | Parse task decomposition response |
257
+ | `structured_parse_bead_tree` | Parse bead tree for epic creation |
179
258
 
180
259
  ## Decomposition Strategies
181
260
 
@@ -213,32 +292,32 @@ Best for: bug fixes, security issues
213
292
 
214
293
  The plugin learns from outcomes:
215
294
 
216
- | Mechanism | How It Works |
217
- | ----------------- | ----------------------------------------------------------- |
218
- | Confidence decay | Criteria weights fade unless revalidated (90-day half-life) |
219
- | Implicit feedback | Fast + success = helpful signal, slow + errors = harmful |
220
- | Pattern maturity | candidate → established → proven (or deprecated) |
221
- | Anti-patterns | Patterns with >60% failure rate auto-invert |
295
+ | Mechanism | How It Works |
296
+ |-----------|--------------|
297
+ | Confidence decay | Criteria weights fade unless revalidated (90-day half-life) |
298
+ | Implicit feedback | Fast + success = helpful signal, slow + errors = harmful |
299
+ | Pattern maturity | candidate → established → proven (or deprecated) |
300
+ | Anti-patterns | Patterns with >60% failure rate auto-invert |
222
301
 
223
302
  ## Context Preservation
224
303
 
225
304
  Hard limits to prevent context exhaustion:
226
305
 
227
- | Constraint | Default | Reason |
228
- | ------------------- | ---------- | ------------------------------ |
229
- | Inbox limit | 5 messages | Prevents token burn |
230
- | Bodies excluded | Always | Fetch individually when needed |
231
- | Summarize preferred | Yes | Key points, not raw dump |
306
+ | Constraint | Default | Reason |
307
+ |------------|---------|--------|
308
+ | Inbox limit | 5 messages | Prevents token burn |
309
+ | Bodies excluded | Always | Fetch individually when needed |
310
+ | Summarize preferred | Yes | Key points, not raw dump |
232
311
 
233
312
  ## Rate Limiting
234
313
 
235
314
  Client-side limits (Redis primary, SQLite fallback):
236
315
 
237
316
  | Endpoint | Per Minute | Per Hour |
238
- | -------- | ---------- | -------- |
239
- | send | 20 | 200 |
240
- | reserve | 10 | 100 |
241
- | inbox | 60 | 600 |
317
+ |----------|------------|----------|
318
+ | send | 20 | 200 |
319
+ | reserve | 10 | 100 |
320
+ | inbox | 60 | 600 |
242
321
 
243
322
  Configure via `OPENCODE_RATE_LIMIT_{ENDPOINT}_PER_MIN` env vars.
244
323
 
@@ -249,7 +328,7 @@ bun install
249
328
  bun run typecheck
250
329
  bun test
251
330
  bun run build
252
- ```
331
+ ````
253
332
 
254
333
  ## License
255
334
 
package/bin/swarm.ts CHANGED
@@ -17,7 +17,34 @@ import { existsSync, mkdirSync, writeFileSync } from "fs";
17
17
  import { homedir } from "os";
18
18
  import { join } from "path";
19
19
 
20
- const VERSION = "0.9.0";
20
+ const VERSION = "0.10.0";
21
+
22
+ // ============================================================================
23
+ // ASCII Art & Branding
24
+ // ============================================================================
25
+
26
+ const BEE = `
27
+ \\ \` - ' /
28
+ - .(o o). -
29
+ ( >.< )
30
+ /| |\\
31
+ (_| |_) bzzzz...
32
+ `;
33
+
34
+ const BANNER = `
35
+ ███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗
36
+ ██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║
37
+ ███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║
38
+ ╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║
39
+ ███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║
40
+ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
41
+ `;
42
+
43
+ const TAGLINE = "Multi-agent coordination for OpenCode";
44
+
45
+ const dim = (s: string) => `\x1b[2m${s}\x1b[0m`;
46
+ const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`;
47
+ const cyan = (s: string) => `\x1b[36m${s}\x1b[0m`;
21
48
 
22
49
  // ============================================================================
23
50
  // Types
@@ -560,27 +587,70 @@ async function init() {
560
587
  }
561
588
 
562
589
  function version() {
563
- console.log("opencode-swarm-plugin v" + VERSION);
590
+ console.log(yellow(BANNER));
591
+ console.log(dim(" " + TAGLINE));
592
+ console.log();
593
+ console.log(" Version: " + VERSION);
594
+ console.log(" Docs: https://github.com/joelhooks/opencode-swarm-plugin");
595
+ console.log();
564
596
  }
565
597
 
566
- function help() {
567
- console.log(`
568
- opencode-swarm-plugin v${VERSION}
569
-
570
- Multi-agent swarm coordination for OpenCode.
598
+ function config() {
599
+ const configDir = join(homedir(), ".config", "opencode");
600
+ const pluginPath = join(configDir, "plugins", "swarm.ts");
601
+ const commandPath = join(configDir, "commands", "swarm.md");
602
+ const agentPath = join(configDir, "agents", "swarm-planner.md");
603
+
604
+ console.log(yellow(BANNER));
605
+ console.log(dim(" " + TAGLINE + " v" + VERSION));
606
+ console.log();
607
+ console.log(cyan("Config Files:"));
608
+ console.log();
609
+
610
+ const files = [
611
+ { path: pluginPath, desc: "Plugin loader", emoji: "🔌" },
612
+ { path: commandPath, desc: "/swarm command prompt", emoji: "📜" },
613
+ { path: agentPath, desc: "@swarm-planner agent", emoji: "🤖" },
614
+ ];
615
+
616
+ for (const { path, desc, emoji } of files) {
617
+ const exists = existsSync(path);
618
+ const status = exists ? "✓" : "✗";
619
+ const color = exists ? "\x1b[32m" : "\x1b[31m";
620
+ console.log(` ${emoji} ${desc}`);
621
+ console.log(` ${color}${status}\x1b[0m ${dim(path)}`);
622
+ console.log();
623
+ }
571
624
 
572
- Commands:
573
- swarm setup Interactive installer - checks and installs dependencies
574
- swarm doctor Health check - shows status of all dependencies
575
- swarm init Initialize beads in current project
576
- swarm version Show version
577
- swarm help Show this help
625
+ console.log(dim("Edit these files to customize swarm behavior."));
626
+ console.log(dim("Run 'swarm setup' to regenerate defaults."));
627
+ console.log();
628
+ }
578
629
 
579
- After setup, use in OpenCode:
630
+ function help() {
631
+ console.log(yellow(BANNER));
632
+ console.log(dim(" " + TAGLINE + " v" + VERSION));
633
+ console.log(cyan(BEE));
634
+ console.log(`
635
+ ${cyan("Commands:")}
636
+ swarm setup Interactive installer - checks and installs dependencies
637
+ swarm doctor Health check - shows status of all dependencies
638
+ swarm init Initialize beads in current project
639
+ swarm config Show paths to generated config files
640
+ swarm version Show version and banner
641
+ swarm help Show this help
642
+
643
+ ${cyan("Usage in OpenCode:")}
580
644
  /swarm "Add user authentication with OAuth"
581
- @swarm-planner "Refactor components to use hooks"
645
+ @swarm-planner "Refactor all components to use hooks"
582
646
 
583
- Documentation: https://github.com/joelhooks/opencode-swarm-plugin
647
+ ${cyan("Customization:")}
648
+ Edit the generated files to customize behavior:
649
+ ${dim("~/.config/opencode/commands/swarm.md")} - /swarm command prompt
650
+ ${dim("~/.config/opencode/agents/swarm-planner.md")} - @swarm-planner agent
651
+ ${dim("~/.config/opencode/plugins/swarm.ts")} - Plugin loader
652
+
653
+ ${dim("Docs: https://github.com/joelhooks/opencode-swarm-plugin")}
584
654
  `);
585
655
  }
586
656
 
@@ -600,6 +670,9 @@ switch (command) {
600
670
  case "init":
601
671
  await init();
602
672
  break;
673
+ case "config":
674
+ config();
675
+ break;
603
676
  case "version":
604
677
  case "--version":
605
678
  case "-v":
package/dist/index.js CHANGED
@@ -22593,6 +22593,18 @@ async function getRateLimiter() {
22593
22593
  var AGENT_MAIL_URL = "http://127.0.0.1:8765";
22594
22594
  var DEFAULT_TTL_SECONDS = 3600;
22595
22595
  var MAX_INBOX_LIMIT = 5;
22596
+ var RETRY_CONFIG = {
22597
+ maxRetries: parseInt(process.env.OPENCODE_AGENT_MAIL_MAX_RETRIES || "3"),
22598
+ baseDelayMs: parseInt(process.env.OPENCODE_AGENT_MAIL_BASE_DELAY_MS || "100"),
22599
+ maxDelayMs: parseInt(process.env.OPENCODE_AGENT_MAIL_MAX_DELAY_MS || "5000"),
22600
+ timeoutMs: parseInt(process.env.OPENCODE_AGENT_MAIL_TIMEOUT_MS || "10000"),
22601
+ jitterPercent: 20
22602
+ };
22603
+ var RECOVERY_CONFIG = {
22604
+ failureThreshold: 2,
22605
+ restartCooldownMs: 30000,
22606
+ enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
22607
+ };
22596
22608
  var sessionStates = new Map;
22597
22609
 
22598
22610
  class AgentMailError extends Error {
@@ -22639,6 +22651,151 @@ class RateLimitExceededError extends Error {
22639
22651
  this.name = "RateLimitExceededError";
22640
22652
  }
22641
22653
  }
22654
+ var consecutiveFailures = 0;
22655
+ var lastRestartAttempt = 0;
22656
+ var isRestarting = false;
22657
+ async function isServerHealthy() {
22658
+ try {
22659
+ const controller = new AbortController;
22660
+ const timeout = setTimeout(() => controller.abort(), 3000);
22661
+ const response = await fetch(`${AGENT_MAIL_URL}/health/liveness`, {
22662
+ signal: controller.signal
22663
+ });
22664
+ clearTimeout(timeout);
22665
+ return response.ok;
22666
+ } catch {
22667
+ return false;
22668
+ }
22669
+ }
22670
+ async function isServerFunctional() {
22671
+ try {
22672
+ const controller = new AbortController;
22673
+ const timeout = setTimeout(() => controller.abort(), 5000);
22674
+ const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22675
+ method: "POST",
22676
+ headers: { "Content-Type": "application/json" },
22677
+ body: JSON.stringify({
22678
+ jsonrpc: "2.0",
22679
+ id: "health-test",
22680
+ method: "tools/call",
22681
+ params: { name: "health_check", arguments: {} }
22682
+ }),
22683
+ signal: controller.signal
22684
+ });
22685
+ clearTimeout(timeout);
22686
+ if (!response.ok)
22687
+ return false;
22688
+ const json2 = await response.json();
22689
+ if (json2.result?.isError)
22690
+ return false;
22691
+ return true;
22692
+ } catch {
22693
+ return false;
22694
+ }
22695
+ }
22696
+ async function restartServer() {
22697
+ if (!RECOVERY_CONFIG.enabled) {
22698
+ console.warn("[agent-mail] Auto-restart disabled via OPENCODE_AGENT_MAIL_AUTO_RESTART=false");
22699
+ return false;
22700
+ }
22701
+ if (isRestarting) {
22702
+ console.warn("[agent-mail] Restart already in progress");
22703
+ return false;
22704
+ }
22705
+ const now = Date.now();
22706
+ if (now - lastRestartAttempt < RECOVERY_CONFIG.restartCooldownMs) {
22707
+ const waitSec = Math.ceil((RECOVERY_CONFIG.restartCooldownMs - (now - lastRestartAttempt)) / 1000);
22708
+ console.warn(`[agent-mail] Restart cooldown active, wait ${waitSec}s`);
22709
+ return false;
22710
+ }
22711
+ isRestarting = true;
22712
+ lastRestartAttempt = now;
22713
+ try {
22714
+ console.warn("[agent-mail] Attempting server restart...");
22715
+ const findProc = Bun.spawn(["lsof", "-i", ":8765", "-t"], {
22716
+ stdout: "pipe",
22717
+ stderr: "pipe"
22718
+ });
22719
+ const findOutput = await new Response(findProc.stdout).text();
22720
+ await findProc.exited;
22721
+ const pids = findOutput.trim().split(`
22722
+ `).filter(Boolean);
22723
+ if (pids.length > 0) {
22724
+ for (const pid of pids) {
22725
+ console.warn(`[agent-mail] Killing process ${pid}`);
22726
+ Bun.spawn(["kill", pid]);
22727
+ }
22728
+ await new Promise((resolve) => setTimeout(resolve, 2000));
22729
+ }
22730
+ const possiblePaths = [
22731
+ `${process.env.HOME}/Code/Dicklesworthstone/mcp_agent_mail`,
22732
+ `${process.env.HOME}/.local/share/agent-mail`,
22733
+ `${process.env.HOME}/mcp_agent_mail`
22734
+ ];
22735
+ let serverDir = null;
22736
+ for (const path of possiblePaths) {
22737
+ try {
22738
+ const stat = await Bun.file(`${path}/pyproject.toml`).exists();
22739
+ if (stat) {
22740
+ serverDir = path;
22741
+ break;
22742
+ }
22743
+ } catch {
22744
+ continue;
22745
+ }
22746
+ }
22747
+ if (!serverDir) {
22748
+ console.error("[agent-mail] Could not find agent-mail installation directory");
22749
+ return false;
22750
+ }
22751
+ console.warn(`[agent-mail] Starting server from ${serverDir}`);
22752
+ Bun.spawn(["python", "-m", "mcp_agent_mail.cli", "serve-http"], {
22753
+ cwd: serverDir,
22754
+ stdout: "ignore",
22755
+ stderr: "ignore",
22756
+ detached: true
22757
+ });
22758
+ for (let i = 0;i < 10; i++) {
22759
+ await new Promise((resolve) => setTimeout(resolve, 1000));
22760
+ if (await isServerHealthy()) {
22761
+ console.warn("[agent-mail] Server restarted successfully");
22762
+ consecutiveFailures = 0;
22763
+ return true;
22764
+ }
22765
+ }
22766
+ console.error("[agent-mail] Server failed to start after restart");
22767
+ return false;
22768
+ } catch (error45) {
22769
+ console.error("[agent-mail] Restart failed:", error45);
22770
+ return false;
22771
+ } finally {
22772
+ isRestarting = false;
22773
+ }
22774
+ }
22775
+ function calculateBackoffDelay(attempt) {
22776
+ if (attempt === 0)
22777
+ return 0;
22778
+ const exponentialDelay = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt - 1);
22779
+ const cappedDelay = Math.min(exponentialDelay, RETRY_CONFIG.maxDelayMs);
22780
+ const jitterRange = cappedDelay * (RETRY_CONFIG.jitterPercent / 100);
22781
+ const jitter = (Math.random() * 2 - 1) * jitterRange;
22782
+ return Math.round(cappedDelay + jitter);
22783
+ }
22784
+ function isRetryableError(error45) {
22785
+ if (error45 instanceof Error) {
22786
+ const message = error45.message.toLowerCase();
22787
+ if (message.includes("econnrefused") || message.includes("econnreset") || message.includes("socket") || message.includes("network") || message.includes("timeout") || message.includes("aborted")) {
22788
+ return true;
22789
+ }
22790
+ if (error45 instanceof AgentMailError && error45.code) {
22791
+ return error45.code === 502 || error45.code === 503 || error45.code === 504;
22792
+ }
22793
+ if (message.includes("unexpected error")) {
22794
+ return true;
22795
+ }
22796
+ }
22797
+ return false;
22798
+ }
22642
22799
  var agentMailAvailable = null;
22643
22800
  async function checkAgentMailAvailable() {
22644
22801
  if (agentMailAvailable !== null) {
@@ -22670,36 +22827,85 @@ async function recordRateLimitedRequest(agentName, endpoint) {
22670
22827
  }
22671
22828
  await rateLimiter.recordRequest(agentName, endpoint);
22672
22829
  }
22673
- async function mcpCall(toolName, args) {
22674
- const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22675
- method: "POST",
22676
- headers: { "Content-Type": "application/json" },
22677
- body: JSON.stringify({
22678
- jsonrpc: "2.0",
22679
- id: crypto.randomUUID(),
22680
- method: "tools/call",
22681
- params: { name: toolName, arguments: args }
22682
- })
22683
- });
22684
- if (!response.ok) {
22685
- throw new AgentMailError(`HTTP ${response.status}: ${response.statusText}`, toolName);
22686
- }
22687
- const json2 = await response.json();
22688
- if (json2.error) {
22689
- throw new AgentMailError(json2.error.message, toolName, json2.error.code, json2.error.data);
22830
+ async function mcpCallOnce(toolName, args) {
22831
+ const controller = new AbortController;
22832
+ const timeout = setTimeout(() => controller.abort(), RETRY_CONFIG.timeoutMs);
22833
+ try {
22834
+ const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22835
+ method: "POST",
22836
+ headers: { "Content-Type": "application/json" },
22837
+ body: JSON.stringify({
22838
+ jsonrpc: "2.0",
22839
+ id: crypto.randomUUID(),
22840
+ method: "tools/call",
22841
+ params: { name: toolName, arguments: args }
22842
+ }),
22843
+ signal: controller.signal
22844
+ });
22845
+ clearTimeout(timeout);
22846
+ if (!response.ok) {
22847
+ throw new AgentMailError(`HTTP ${response.status}: ${response.statusText}`, toolName, response.status);
22848
+ }
22849
+ const json2 = await response.json();
22850
+ if (json2.error) {
22851
+ throw new AgentMailError(json2.error.message, toolName, json2.error.code, json2.error.data);
22852
+ }
22853
+ const result = json2.result;
22854
+ if (result && typeof result === "object") {
22855
+ const wrapped = result;
22856
+ if (wrapped.isError) {
22857
+ const errorText = wrapped.content?.[0]?.text || "Unknown error";
22858
+ throw new AgentMailError(errorText, toolName);
22859
+ }
22860
+ if ("structuredContent" in wrapped) {
22861
+ return wrapped.structuredContent;
22862
+ }
22863
+ }
22864
+ return result;
22865
+ } catch (error45) {
22866
+ clearTimeout(timeout);
22867
+ throw error45;
22690
22868
  }
22691
- const result = json2.result;
22692
- if (result && typeof result === "object") {
22693
- const wrapped = result;
22694
- if (wrapped.isError) {
22695
- const errorText = wrapped.content?.[0]?.text || "Unknown error";
22696
- throw new AgentMailError(errorText, toolName);
22869
+ }
22870
+ async function mcpCall(toolName, args) {
22871
+ let lastError = null;
22872
+ for (let attempt = 0;attempt <= RETRY_CONFIG.maxRetries; attempt++) {
22873
+ if (attempt > 0) {
22874
+ const delay = calculateBackoffDelay(attempt);
22875
+ console.warn(`[agent-mail] Retry ${attempt}/${RETRY_CONFIG.maxRetries} for ${toolName} after ${delay}ms`);
22876
+ await new Promise((resolve) => setTimeout(resolve, delay));
22697
22877
  }
22698
- if ("structuredContent" in wrapped) {
22699
- return wrapped.structuredContent;
22878
+ try {
22879
+ const result = await mcpCallOnce(toolName, args);
22880
+ consecutiveFailures = 0;
22881
+ return result;
22882
+ } catch (error45) {
22883
+ lastError = error45 instanceof Error ? error45 : new Error(String(error45));
22884
+ consecutiveFailures++;
22885
+ if (consecutiveFailures >= RECOVERY_CONFIG.failureThreshold && RECOVERY_CONFIG.enabled) {
22886
+ console.warn(`[agent-mail] ${consecutiveFailures} consecutive failures, checking server health...`);
22887
+ const healthy = await isServerFunctional();
22888
+ if (!healthy) {
22889
+ console.warn("[agent-mail] Server unhealthy, attempting restart...");
22890
+ const restarted = await restartServer();
22891
+ if (restarted) {
22892
+ agentMailAvailable = null;
22893
+ attempt--;
22894
+ continue;
22895
+ }
22896
+ }
22897
+ }
22898
+ if (!isRetryableError(error45)) {
22899
+ console.warn(`[agent-mail] Non-retryable error for ${toolName}: ${lastError.message}`);
22900
+ throw lastError;
22901
+ }
22902
+ if (attempt === RETRY_CONFIG.maxRetries) {
22903
+ console.error(`[agent-mail] All ${RETRY_CONFIG.maxRetries} retries exhausted for ${toolName}`);
22904
+ throw lastError;
22905
+ }
22700
22906
  }
22701
22907
  }
22702
- return result;
22908
+ throw lastError || new Error("Unknown error in mcpCall");
22703
22909
  }
22704
22910
  function requireState(sessionID) {
22705
22911
  const state = sessionStates.get(sessionID);
package/dist/plugin.js CHANGED
@@ -22567,6 +22567,18 @@ async function getRateLimiter() {
22567
22567
  var AGENT_MAIL_URL = "http://127.0.0.1:8765";
22568
22568
  var DEFAULT_TTL_SECONDS = 3600;
22569
22569
  var MAX_INBOX_LIMIT = 5;
22570
+ var RETRY_CONFIG = {
22571
+ maxRetries: parseInt(process.env.OPENCODE_AGENT_MAIL_MAX_RETRIES || "3"),
22572
+ baseDelayMs: parseInt(process.env.OPENCODE_AGENT_MAIL_BASE_DELAY_MS || "100"),
22573
+ maxDelayMs: parseInt(process.env.OPENCODE_AGENT_MAIL_MAX_DELAY_MS || "5000"),
22574
+ timeoutMs: parseInt(process.env.OPENCODE_AGENT_MAIL_TIMEOUT_MS || "10000"),
22575
+ jitterPercent: 20
22576
+ };
22577
+ var RECOVERY_CONFIG = {
22578
+ failureThreshold: 2,
22579
+ restartCooldownMs: 30000,
22580
+ enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
22581
+ };
22570
22582
  var sessionStates = new Map;
22571
22583
 
22572
22584
  class AgentMailError extends Error {
@@ -22613,6 +22625,151 @@ class RateLimitExceededError extends Error {
22613
22625
  this.name = "RateLimitExceededError";
22614
22626
  }
22615
22627
  }
22628
+ var consecutiveFailures = 0;
22629
+ var lastRestartAttempt = 0;
22630
+ var isRestarting = false;
22631
+ async function isServerHealthy() {
22632
+ try {
22633
+ const controller = new AbortController;
22634
+ const timeout = setTimeout(() => controller.abort(), 3000);
22635
+ const response = await fetch(`${AGENT_MAIL_URL}/health/liveness`, {
22636
+ signal: controller.signal
22637
+ });
22638
+ clearTimeout(timeout);
22639
+ return response.ok;
22640
+ } catch {
22641
+ return false;
22642
+ }
22643
+ }
22644
+ async function isServerFunctional() {
22645
+ try {
22646
+ const controller = new AbortController;
22647
+ const timeout = setTimeout(() => controller.abort(), 5000);
22648
+ const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22649
+ method: "POST",
22650
+ headers: { "Content-Type": "application/json" },
22651
+ body: JSON.stringify({
22652
+ jsonrpc: "2.0",
22653
+ id: "health-test",
22654
+ method: "tools/call",
22655
+ params: { name: "health_check", arguments: {} }
22656
+ }),
22657
+ signal: controller.signal
22658
+ });
22659
+ clearTimeout(timeout);
22660
+ if (!response.ok)
22661
+ return false;
22662
+ const json2 = await response.json();
22663
+ if (json2.result?.isError)
22664
+ return false;
22665
+ return true;
22666
+ } catch {
22667
+ return false;
22668
+ }
22669
+ }
22670
+ async function restartServer() {
22671
+ if (!RECOVERY_CONFIG.enabled) {
22672
+ console.warn("[agent-mail] Auto-restart disabled via OPENCODE_AGENT_MAIL_AUTO_RESTART=false");
22673
+ return false;
22674
+ }
22675
+ if (isRestarting) {
22676
+ console.warn("[agent-mail] Restart already in progress");
22677
+ return false;
22678
+ }
22679
+ const now = Date.now();
22680
+ if (now - lastRestartAttempt < RECOVERY_CONFIG.restartCooldownMs) {
22681
+ const waitSec = Math.ceil((RECOVERY_CONFIG.restartCooldownMs - (now - lastRestartAttempt)) / 1000);
22682
+ console.warn(`[agent-mail] Restart cooldown active, wait ${waitSec}s`);
22683
+ return false;
22684
+ }
22685
+ isRestarting = true;
22686
+ lastRestartAttempt = now;
22687
+ try {
22688
+ console.warn("[agent-mail] Attempting server restart...");
22689
+ const findProc = Bun.spawn(["lsof", "-i", ":8765", "-t"], {
22690
+ stdout: "pipe",
22691
+ stderr: "pipe"
22692
+ });
22693
+ const findOutput = await new Response(findProc.stdout).text();
22694
+ await findProc.exited;
22695
+ const pids = findOutput.trim().split(`
22696
+ `).filter(Boolean);
22697
+ if (pids.length > 0) {
22698
+ for (const pid of pids) {
22699
+ console.warn(`[agent-mail] Killing process ${pid}`);
22700
+ Bun.spawn(["kill", pid]);
22701
+ }
22702
+ await new Promise((resolve) => setTimeout(resolve, 2000));
22703
+ }
22704
+ const possiblePaths = [
22705
+ `${process.env.HOME}/Code/Dicklesworthstone/mcp_agent_mail`,
22706
+ `${process.env.HOME}/.local/share/agent-mail`,
22707
+ `${process.env.HOME}/mcp_agent_mail`
22708
+ ];
22709
+ let serverDir = null;
22710
+ for (const path of possiblePaths) {
22711
+ try {
22712
+ const stat = await Bun.file(`${path}/pyproject.toml`).exists();
22713
+ if (stat) {
22714
+ serverDir = path;
22715
+ break;
22716
+ }
22717
+ } catch {
22718
+ continue;
22719
+ }
22720
+ }
22721
+ if (!serverDir) {
22722
+ console.error("[agent-mail] Could not find agent-mail installation directory");
22723
+ return false;
22724
+ }
22725
+ console.warn(`[agent-mail] Starting server from ${serverDir}`);
22726
+ Bun.spawn(["python", "-m", "mcp_agent_mail.cli", "serve-http"], {
22727
+ cwd: serverDir,
22728
+ stdout: "ignore",
22729
+ stderr: "ignore",
22730
+ detached: true
22731
+ });
22732
+ for (let i = 0;i < 10; i++) {
22733
+ await new Promise((resolve) => setTimeout(resolve, 1000));
22734
+ if (await isServerHealthy()) {
22735
+ console.warn("[agent-mail] Server restarted successfully");
22736
+ consecutiveFailures = 0;
22737
+ return true;
22738
+ }
22739
+ }
22740
+ console.error("[agent-mail] Server failed to start after restart");
22741
+ return false;
22742
+ } catch (error45) {
22743
+ console.error("[agent-mail] Restart failed:", error45);
22744
+ return false;
22745
+ } finally {
22746
+ isRestarting = false;
22747
+ }
22748
+ }
22749
+ function calculateBackoffDelay(attempt) {
22750
+ if (attempt === 0)
22751
+ return 0;
22752
+ const exponentialDelay = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt - 1);
22753
+ const cappedDelay = Math.min(exponentialDelay, RETRY_CONFIG.maxDelayMs);
22754
+ const jitterRange = cappedDelay * (RETRY_CONFIG.jitterPercent / 100);
22755
+ const jitter = (Math.random() * 2 - 1) * jitterRange;
22756
+ return Math.round(cappedDelay + jitter);
22757
+ }
22758
+ function isRetryableError(error45) {
22759
+ if (error45 instanceof Error) {
22760
+ const message = error45.message.toLowerCase();
22761
+ if (message.includes("econnrefused") || message.includes("econnreset") || message.includes("socket") || message.includes("network") || message.includes("timeout") || message.includes("aborted")) {
22762
+ return true;
22763
+ }
22764
+ if (error45 instanceof AgentMailError && error45.code) {
22765
+ return error45.code === 502 || error45.code === 503 || error45.code === 504;
22766
+ }
22767
+ if (message.includes("unexpected error")) {
22768
+ return true;
22769
+ }
22770
+ }
22771
+ return false;
22772
+ }
22616
22773
  var agentMailAvailable = null;
22617
22774
  async function checkAgentMailAvailable() {
22618
22775
  if (agentMailAvailable !== null) {
@@ -22644,36 +22801,85 @@ async function recordRateLimitedRequest(agentName, endpoint) {
22644
22801
  }
22645
22802
  await rateLimiter.recordRequest(agentName, endpoint);
22646
22803
  }
22647
- async function mcpCall(toolName, args) {
22648
- const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22649
- method: "POST",
22650
- headers: { "Content-Type": "application/json" },
22651
- body: JSON.stringify({
22652
- jsonrpc: "2.0",
22653
- id: crypto.randomUUID(),
22654
- method: "tools/call",
22655
- params: { name: toolName, arguments: args }
22656
- })
22657
- });
22658
- if (!response.ok) {
22659
- throw new AgentMailError(`HTTP ${response.status}: ${response.statusText}`, toolName);
22660
- }
22661
- const json2 = await response.json();
22662
- if (json2.error) {
22663
- throw new AgentMailError(json2.error.message, toolName, json2.error.code, json2.error.data);
22804
+ async function mcpCallOnce(toolName, args) {
22805
+ const controller = new AbortController;
22806
+ const timeout = setTimeout(() => controller.abort(), RETRY_CONFIG.timeoutMs);
22807
+ try {
22808
+ const response = await fetch(`${AGENT_MAIL_URL}/mcp/`, {
22809
+ method: "POST",
22810
+ headers: { "Content-Type": "application/json" },
22811
+ body: JSON.stringify({
22812
+ jsonrpc: "2.0",
22813
+ id: crypto.randomUUID(),
22814
+ method: "tools/call",
22815
+ params: { name: toolName, arguments: args }
22816
+ }),
22817
+ signal: controller.signal
22818
+ });
22819
+ clearTimeout(timeout);
22820
+ if (!response.ok) {
22821
+ throw new AgentMailError(`HTTP ${response.status}: ${response.statusText}`, toolName, response.status);
22822
+ }
22823
+ const json2 = await response.json();
22824
+ if (json2.error) {
22825
+ throw new AgentMailError(json2.error.message, toolName, json2.error.code, json2.error.data);
22826
+ }
22827
+ const result = json2.result;
22828
+ if (result && typeof result === "object") {
22829
+ const wrapped = result;
22830
+ if (wrapped.isError) {
22831
+ const errorText = wrapped.content?.[0]?.text || "Unknown error";
22832
+ throw new AgentMailError(errorText, toolName);
22833
+ }
22834
+ if ("structuredContent" in wrapped) {
22835
+ return wrapped.structuredContent;
22836
+ }
22837
+ }
22838
+ return result;
22839
+ } catch (error45) {
22840
+ clearTimeout(timeout);
22841
+ throw error45;
22664
22842
  }
22665
- const result = json2.result;
22666
- if (result && typeof result === "object") {
22667
- const wrapped = result;
22668
- if (wrapped.isError) {
22669
- const errorText = wrapped.content?.[0]?.text || "Unknown error";
22670
- throw new AgentMailError(errorText, toolName);
22843
+ }
22844
+ async function mcpCall(toolName, args) {
22845
+ let lastError = null;
22846
+ for (let attempt = 0;attempt <= RETRY_CONFIG.maxRetries; attempt++) {
22847
+ if (attempt > 0) {
22848
+ const delay = calculateBackoffDelay(attempt);
22849
+ console.warn(`[agent-mail] Retry ${attempt}/${RETRY_CONFIG.maxRetries} for ${toolName} after ${delay}ms`);
22850
+ await new Promise((resolve) => setTimeout(resolve, delay));
22671
22851
  }
22672
- if ("structuredContent" in wrapped) {
22673
- return wrapped.structuredContent;
22852
+ try {
22853
+ const result = await mcpCallOnce(toolName, args);
22854
+ consecutiveFailures = 0;
22855
+ return result;
22856
+ } catch (error45) {
22857
+ lastError = error45 instanceof Error ? error45 : new Error(String(error45));
22858
+ consecutiveFailures++;
22859
+ if (consecutiveFailures >= RECOVERY_CONFIG.failureThreshold && RECOVERY_CONFIG.enabled) {
22860
+ console.warn(`[agent-mail] ${consecutiveFailures} consecutive failures, checking server health...`);
22861
+ const healthy = await isServerFunctional();
22862
+ if (!healthy) {
22863
+ console.warn("[agent-mail] Server unhealthy, attempting restart...");
22864
+ const restarted = await restartServer();
22865
+ if (restarted) {
22866
+ agentMailAvailable = null;
22867
+ attempt--;
22868
+ continue;
22869
+ }
22870
+ }
22871
+ }
22872
+ if (!isRetryableError(error45)) {
22873
+ console.warn(`[agent-mail] Non-retryable error for ${toolName}: ${lastError.message}`);
22874
+ throw lastError;
22875
+ }
22876
+ if (attempt === RETRY_CONFIG.maxRetries) {
22877
+ console.error(`[agent-mail] All ${RETRY_CONFIG.maxRetries} retries exhausted for ${toolName}`);
22878
+ throw lastError;
22879
+ }
22674
22880
  }
22675
22881
  }
22676
- return result;
22882
+ throw lastError || new Error("Unknown error in mcpCall");
22677
22883
  }
22678
22884
  function requireState(sessionID) {
22679
22885
  const state = sessionStates.get(sessionID);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Multi-agent swarm coordination for OpenCode with learning capabilities, beads integration, and Agent Mail",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",