opencode-swarm-plugin 0.28.1 → 0.29.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +69 -0
- package/dist/compaction-hook.d.ts +20 -2
- package/dist/compaction-hook.d.ts.map +1 -1
- package/dist/hive.d.ts +8 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +181 -78
- package/dist/plugin.js +70 -69
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/tool-availability.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/compaction-hook.test.ts +110 -0
- package/src/compaction-hook.ts +183 -29
- package/src/hive.integration.test.ts +5 -5
- package/src/hive.ts +7 -7
- package/src/learning.integration.test.ts +11 -3
- package/src/swarm-orchestrate.ts +76 -79
- package/src/swarm.integration.test.ts +88 -2
- package/src/tool-availability.ts +6 -24
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
$ bun build ./src/index.ts --outdir ./dist --target node --external @electric-sql/pglite --external swarm-mail && bun build ./src/plugin.ts --outfile ./dist/plugin.js --target node --external @electric-sql/pglite --external swarm-mail && tsc
|
|
2
|
-
Bundled 200 modules in
|
|
2
|
+
Bundled 200 modules in 35ms
|
|
3
3
|
|
|
4
4
|
index.js 1.20 MB (entry point)
|
|
5
5
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# opencode-swarm-plugin
|
|
2
2
|
|
|
3
|
+
## 0.29.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`a2ff1f4`](https://github.com/joelhooks/swarm-tools/commit/a2ff1f4257a2e9857f63abe4e9b941a573f44380) Thanks [@joelhooks](https://github.com/joelhooks)! - ## 🐝 Cell IDs Now Wear Their Project Colors
|
|
8
|
+
|
|
9
|
+
> _"We may fantasize about being International Men of Mystery, but our code needs to be mundane and clear. One of the most important parts of clear code is good names."_
|
|
10
|
+
> — Martin Fowler, _Refactoring_
|
|
11
|
+
|
|
12
|
+
Cell IDs finally know where they came from. Instead of anonymous `bd-xxx` prefixes,
|
|
13
|
+
new cells proudly display their project name: `swarm-mail-lf2p4u-abc123`.
|
|
14
|
+
|
|
15
|
+
### What Changed
|
|
16
|
+
|
|
17
|
+
**swarm-mail:**
|
|
18
|
+
|
|
19
|
+
- `generateBeadId()` now reads `package.json` name field from project directory
|
|
20
|
+
- Added `slugifyProjectName()` for safe ID generation (lowercase, special chars → dashes)
|
|
21
|
+
- Falls back to `cell-` prefix if no package.json or no name field
|
|
22
|
+
|
|
23
|
+
**opencode-swarm-plugin:**
|
|
24
|
+
|
|
25
|
+
- Removed all `bd` CLI usage from `swarm-orchestrate.ts` - now uses HiveAdapter
|
|
26
|
+
- Improved compaction hook swarm detection with confidence levels (high/medium/low)
|
|
27
|
+
- Added fallback detection prompt for uncertain swarm states
|
|
28
|
+
|
|
29
|
+
### Examples
|
|
30
|
+
|
|
31
|
+
| Before | After |
|
|
32
|
+
| ----------------------- | ------------------------------- |
|
|
33
|
+
| `bd-lf2p4u-mjbneh7mqah` | `swarm-mail-lf2p4u-mjbneh7mqah` |
|
|
34
|
+
| `bd-abc123-xyz` | `my-cool-app-abc123-xyz` |
|
|
35
|
+
| (no package.json) | `cell-abc123-xyz` |
|
|
36
|
+
|
|
37
|
+
### Why It Matters
|
|
38
|
+
|
|
39
|
+
- **Identifiable at a glance** - Know which project a cell belongs to without looking it up
|
|
40
|
+
- **Multi-project workspaces** - Filter/search cells by project prefix
|
|
41
|
+
- **Terminology cleanup** - Removes legacy "bead" (`bd-`) from user-facing IDs
|
|
42
|
+
|
|
43
|
+
### Backward Compatible
|
|
44
|
+
|
|
45
|
+
Existing `bd-*` IDs still work fine. No migration needed - only NEW cells get project prefixes.
|
|
46
|
+
|
|
47
|
+
### Compaction: Keeping the Swarm Alive
|
|
48
|
+
|
|
49
|
+
> _"Intelligent and structured group dynamics that emerge not from a leader, but from the local interactions of the elements themselves."_
|
|
50
|
+
> — Daniel Shiffman, _The Nature of Code_
|
|
51
|
+
|
|
52
|
+
The compaction hook now uses multi-signal detection to keep swarms cooking through context compression:
|
|
53
|
+
|
|
54
|
+
- **HIGH confidence:** Active reservations, in_progress cells → full swarm context
|
|
55
|
+
- **MEDIUM confidence:** Open subtasks, unclosed epics → full swarm context
|
|
56
|
+
- **LOW confidence:** Any cells exist → fallback detection prompt
|
|
57
|
+
|
|
58
|
+
Philosophy: Err on the side of continuation. A false positive costs context space. A false negative loses the swarm.
|
|
59
|
+
|
|
60
|
+
### Patch Changes
|
|
61
|
+
|
|
62
|
+
- Updated dependencies [[`a2ff1f4`](https://github.com/joelhooks/swarm-tools/commit/a2ff1f4257a2e9857f63abe4e9b941a573f44380)]:
|
|
63
|
+
- swarm-mail@0.4.0
|
|
64
|
+
|
|
65
|
+
## 0.28.2
|
|
66
|
+
|
|
67
|
+
### Patch Changes
|
|
68
|
+
|
|
69
|
+
- Updated dependencies [[`90409ef`](https://github.com/joelhooks/swarm-tools/commit/90409ef4f353844b25fe04221bc80d6f930eced2)]:
|
|
70
|
+
- swarm-mail@0.3.4
|
|
71
|
+
|
|
3
72
|
## 0.28.1
|
|
4
73
|
|
|
5
74
|
### Patch Changes
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* When context is compacted, this hook injects instructions for the summarizer
|
|
6
6
|
* to preserve swarm coordination state and enable seamless resumption.
|
|
7
7
|
*
|
|
8
|
+
* ## Philosophy: Err on the Side of Continuation
|
|
9
|
+
*
|
|
10
|
+
* It's better to inject swarm context unnecessarily than to lose an active swarm.
|
|
11
|
+
* The cost of a false positive (extra context) is low.
|
|
12
|
+
* The cost of a false negative (lost swarm) is high - wasted work, confused agents.
|
|
13
|
+
*
|
|
8
14
|
* Hook signature (from @opencode-ai/plugin):
|
|
9
15
|
* ```typescript
|
|
10
16
|
* "experimental.session.compacting"?: (
|
|
@@ -33,11 +39,23 @@
|
|
|
33
39
|
* autonomously after context compression.
|
|
34
40
|
*/
|
|
35
41
|
export declare const SWARM_COMPACTION_CONTEXT = "## \uD83D\uDC1D SWARM ACTIVE - Keep Cooking\n\nYou are the **COORDINATOR** of an active swarm. Context was compacted but the swarm is still running.\n\n**YOUR JOB:** Keep orchestrating. Spawn agents. Monitor progress. Unblock work. Ship it.\n\n### Preserve in Summary\n\nExtract from session context:\n\n1. **Epic & Subtasks** - IDs, titles, status, file assignments\n2. **What's Running** - Which agents are active, what they're working on \n3. **What's Blocked** - Blockers and what's needed to unblock\n4. **What's Done** - Completed work and any follow-ups needed\n5. **What's Next** - Pending subtasks ready to spawn\n\n### Summary Format\n\n```\n## \uD83D\uDC1D Swarm State\n\n**Epic:** <bd-xxx> - <title>\n**Project:** <path>\n**Progress:** X/Y subtasks complete\n\n**Active:**\n- <bd-xxx>: <title> [in_progress] \u2192 <agent> working on <files>\n\n**Blocked:**\n- <bd-xxx>: <title> - BLOCKED: <reason>\n\n**Completed:**\n- <bd-xxx>: <title> \u2713\n\n**Ready to Spawn:**\n- <bd-xxx>: <title> (files: <...>)\n```\n\n### On Resume - IMMEDIATELY\n\n1. `swarm_status(epic_id=\"<epic>\", project_key=\"<path>\")` - Get current state\n2. `swarmmail_inbox(limit=5)` - Check for agent messages\n3. **Spawn ready subtasks** - Don't wait, fire them off\n4. **Unblock blocked work** - Resolve dependencies, reassign if needed\n5. **Collect completed work** - Close done subtasks, verify quality\n\n### Keep the Swarm Cooking\n\n- **Spawn aggressively** - If a subtask is ready and unblocked, spawn an agent\n- **Monitor actively** - Check status, read messages, respond to blockers\n- **Close the loop** - When all subtasks done, verify and close the epic\n- **Don't stop** - The swarm runs until the epic is closed\n\n**You are not waiting for instructions. You are the coordinator. Coordinate.**\n";
|
|
42
|
+
/**
|
|
43
|
+
* Fallback detection prompt - tells the compactor what to look for
|
|
44
|
+
*
|
|
45
|
+
* Used when we can't definitively detect a swarm but want to be safe.
|
|
46
|
+
* The compactor can check the conversation context for these patterns.
|
|
47
|
+
*/
|
|
48
|
+
export declare const SWARM_DETECTION_FALLBACK = "## \uD83D\uDC1D Swarm Detection - Check Your Context\n\n**IMPORTANT:** Before summarizing, check if this session involves an active swarm.\n\nLook for ANY of these patterns in the conversation:\n\n### Tool Calls (definite swarm sign)\n- `swarm_decompose`, `swarm_spawn_subtask`, `swarm_status`, `swarm_complete`\n- `swarmmail_init`, `swarmmail_reserve`, `swarmmail_send`\n- `hive_create_epic`, `hive_start`, `hive_close`\n\n### IDs and Names\n- Cell IDs: `bd-xxx`, `bd-xxx.N` (subtask format)\n- Agent names: BlueLake, RedMountain, GreenValley, etc.\n- Epic references: \"epic\", \"subtask\", \"parent\"\n\n### Coordination Language\n- \"spawn\", \"worker\", \"coordinator\"\n- \"reserve\", \"reservation\", \"files\"\n- \"blocked\", \"unblock\", \"dependency\"\n- \"progress\", \"complete\", \"in_progress\"\n\n### If You Find Swarm Evidence\n\nInclude this in your summary:\n1. Epic ID and title\n2. Project path\n3. Subtask status (running/blocked/done/pending)\n4. Any blockers or issues\n5. What should happen next\n\n**Then tell the resumed session:**\n\"This is an active swarm. Check swarm_status and swarmmail_inbox immediately.\"\n";
|
|
36
49
|
/**
|
|
37
50
|
* Create the compaction hook for use in plugin registration
|
|
38
51
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
52
|
+
* Injects swarm context based on detection confidence:
|
|
53
|
+
* - HIGH/MEDIUM: Full swarm context (definitely/probably a swarm)
|
|
54
|
+
* - LOW: Fallback detection prompt (let compactor check context)
|
|
55
|
+
* - NONE: No injection (probably not a swarm)
|
|
56
|
+
*
|
|
57
|
+
* Philosophy: Err on the side of continuation. A false positive costs
|
|
58
|
+
* a bit of context space. A false negative loses the swarm.
|
|
41
59
|
*
|
|
42
60
|
* @example
|
|
43
61
|
* ```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../src/compaction-hook.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"compaction-hook.d.ts","sourceRoot":"","sources":["../src/compaction-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AASH;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,2wDAsDpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,0nCAiCpC,CAAC;AAoIF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,KAEhC,QAAQ;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,KAC5B,OAAO,CAAC,IAAI,CAAC,CAcjB"}
|
package/dist/hive.d.ts
CHANGED
|
@@ -301,11 +301,11 @@ export declare const hive_sync: {
|
|
|
301
301
|
export declare const hive_link_thread: {
|
|
302
302
|
description: string;
|
|
303
303
|
args: {
|
|
304
|
-
|
|
304
|
+
bead_id: z.ZodString;
|
|
305
305
|
thread_id: z.ZodString;
|
|
306
306
|
};
|
|
307
307
|
execute(args: {
|
|
308
|
-
|
|
308
|
+
bead_id: string;
|
|
309
309
|
thread_id: string;
|
|
310
310
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
311
311
|
};
|
|
@@ -461,11 +461,11 @@ export declare const hiveTools: {
|
|
|
461
461
|
hive_link_thread: {
|
|
462
462
|
description: string;
|
|
463
463
|
args: {
|
|
464
|
-
|
|
464
|
+
bead_id: z.ZodString;
|
|
465
465
|
thread_id: z.ZodString;
|
|
466
466
|
};
|
|
467
467
|
execute(args: {
|
|
468
|
-
|
|
468
|
+
bead_id: string;
|
|
469
469
|
thread_id: string;
|
|
470
470
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
471
471
|
};
|
|
@@ -648,11 +648,11 @@ export declare const beads_sync: {
|
|
|
648
648
|
export declare const beads_link_thread: {
|
|
649
649
|
description: string;
|
|
650
650
|
args: {
|
|
651
|
-
|
|
651
|
+
bead_id: z.ZodString;
|
|
652
652
|
thread_id: z.ZodString;
|
|
653
653
|
};
|
|
654
654
|
execute(args: {
|
|
655
|
-
|
|
655
|
+
bead_id: string;
|
|
656
656
|
thread_id: string;
|
|
657
657
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
658
658
|
};
|
|
@@ -811,11 +811,11 @@ export declare const beadsTools: {
|
|
|
811
811
|
beads_link_thread: {
|
|
812
812
|
description: string;
|
|
813
813
|
args: {
|
|
814
|
-
|
|
814
|
+
bead_id: z.ZodString;
|
|
815
815
|
thread_id: z.ZodString;
|
|
816
816
|
};
|
|
817
817
|
execute(args: {
|
|
818
|
-
|
|
818
|
+
bead_id: string;
|
|
819
819
|
thread_id: string;
|
|
820
820
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
821
821
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1287,11 +1287,11 @@ export declare const allTools: {
|
|
|
1287
1287
|
readonly hive_link_thread: {
|
|
1288
1288
|
description: string;
|
|
1289
1289
|
args: {
|
|
1290
|
-
|
|
1290
|
+
bead_id: import("zod").ZodString;
|
|
1291
1291
|
thread_id: import("zod").ZodString;
|
|
1292
1292
|
};
|
|
1293
1293
|
execute(args: {
|
|
1294
|
-
|
|
1294
|
+
bead_id: string;
|
|
1295
1295
|
thread_id: string;
|
|
1296
1296
|
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
1297
1297
|
};
|
package/dist/index.js
CHANGED
|
@@ -28258,30 +28258,30 @@ var hive_sync = tool({
|
|
|
28258
28258
|
var hive_link_thread = tool({
|
|
28259
28259
|
description: "Add metadata linking cell to Agent Mail thread",
|
|
28260
28260
|
args: {
|
|
28261
|
-
|
|
28261
|
+
bead_id: tool.schema.string().describe("Cell ID"),
|
|
28262
28262
|
thread_id: tool.schema.string().describe("Agent Mail thread ID")
|
|
28263
28263
|
},
|
|
28264
28264
|
async execute(args, ctx) {
|
|
28265
28265
|
const projectKey = getHiveWorkingDirectory();
|
|
28266
28266
|
const adapter = await getHiveAdapter(projectKey);
|
|
28267
28267
|
try {
|
|
28268
|
-
const cell = await adapter.getCell(projectKey, args.
|
|
28268
|
+
const cell = await adapter.getCell(projectKey, args.bead_id);
|
|
28269
28269
|
if (!cell) {
|
|
28270
|
-
throw new HiveError(`Cell not found: ${args.
|
|
28270
|
+
throw new HiveError(`Cell not found: ${args.bead_id}`, "hive_link_thread");
|
|
28271
28271
|
}
|
|
28272
28272
|
const existingDesc = cell.description || "";
|
|
28273
28273
|
const threadMarker = `[thread:${args.thread_id}]`;
|
|
28274
28274
|
if (existingDesc.includes(threadMarker)) {
|
|
28275
|
-
return `Cell ${args.
|
|
28275
|
+
return `Cell ${args.bead_id} already linked to thread ${args.thread_id}`;
|
|
28276
28276
|
}
|
|
28277
28277
|
const newDesc = existingDesc ? `${existingDesc}
|
|
28278
28278
|
|
|
28279
28279
|
${threadMarker}` : threadMarker;
|
|
28280
|
-
await adapter.updateCell(projectKey, args.
|
|
28280
|
+
await adapter.updateCell(projectKey, args.bead_id, {
|
|
28281
28281
|
description: newDesc
|
|
28282
28282
|
});
|
|
28283
|
-
await adapter.markDirty(projectKey, args.
|
|
28284
|
-
return `Linked cell ${args.
|
|
28283
|
+
await adapter.markDirty(projectKey, args.bead_id);
|
|
28284
|
+
return `Linked cell ${args.bead_id} to thread ${args.thread_id}`;
|
|
28285
28285
|
} catch (error45) {
|
|
28286
28286
|
const message = error45 instanceof Error ? error45.message : String(error45);
|
|
28287
28287
|
throw new HiveError(`Failed to link thread: ${message}`, "hive_link_thread");
|
|
@@ -28523,27 +28523,11 @@ var toolCheckers = {
|
|
|
28523
28523
|
}
|
|
28524
28524
|
},
|
|
28525
28525
|
beads: async () => {
|
|
28526
|
-
|
|
28527
|
-
|
|
28528
|
-
|
|
28529
|
-
|
|
28530
|
-
|
|
28531
|
-
error: "bd command not found"
|
|
28532
|
-
};
|
|
28533
|
-
}
|
|
28534
|
-
try {
|
|
28535
|
-
const result = await Bun.$`bd --version`.quiet().nothrow();
|
|
28536
|
-
return {
|
|
28537
|
-
available: result.exitCode === 0,
|
|
28538
|
-
checkedAt: new Date().toISOString()
|
|
28539
|
-
};
|
|
28540
|
-
} catch (e) {
|
|
28541
|
-
return {
|
|
28542
|
-
available: false,
|
|
28543
|
-
checkedAt: new Date().toISOString(),
|
|
28544
|
-
error: String(e)
|
|
28545
|
-
};
|
|
28546
|
-
}
|
|
28526
|
+
return {
|
|
28527
|
+
available: false,
|
|
28528
|
+
checkedAt: new Date().toISOString(),
|
|
28529
|
+
error: "bd CLI is deprecated - use hive_* tools with HiveAdapter instead"
|
|
28530
|
+
};
|
|
28547
28531
|
},
|
|
28548
28532
|
"swarm-mail": async () => {
|
|
28549
28533
|
try {
|
|
@@ -32663,27 +32647,25 @@ var reviewTools = {
|
|
|
32663
32647
|
};
|
|
32664
32648
|
|
|
32665
32649
|
// src/swarm-orchestrate.ts
|
|
32666
|
-
async function queryEpicSubtasks(epicId) {
|
|
32667
|
-
const beadsAvailable = await isToolAvailable("beads");
|
|
32668
|
-
if (!beadsAvailable) {
|
|
32669
|
-
warnMissingTool("beads");
|
|
32670
|
-
return [];
|
|
32671
|
-
}
|
|
32672
|
-
const result = await Bun.$`bd list --parent ${epicId} --json`.quiet().nothrow();
|
|
32673
|
-
if (result.exitCode !== 0) {
|
|
32674
|
-
console.error(`[swarm] ERROR: Failed to query subtasks for epic ${epicId}:`, result.stderr.toString());
|
|
32675
|
-
return [];
|
|
32676
|
-
}
|
|
32650
|
+
async function queryEpicSubtasks(projectKey, epicId) {
|
|
32677
32651
|
try {
|
|
32678
|
-
const
|
|
32679
|
-
|
|
32652
|
+
const adapter = await getHiveAdapter(projectKey);
|
|
32653
|
+
const cells = await adapter.queryCells(projectKey, { parent_id: epicId });
|
|
32654
|
+
return cells.filter((cell) => cell.status !== "tombstone").map((cell) => ({
|
|
32655
|
+
id: cell.id,
|
|
32656
|
+
title: cell.title,
|
|
32657
|
+
description: cell.description || "",
|
|
32658
|
+
status: cell.status,
|
|
32659
|
+
priority: cell.priority,
|
|
32660
|
+
issue_type: cell.type,
|
|
32661
|
+
created_at: new Date(cell.created_at).toISOString(),
|
|
32662
|
+
updated_at: cell.updated_at ? new Date(cell.updated_at).toISOString() : undefined,
|
|
32663
|
+
dependencies: [],
|
|
32664
|
+
metadata: {}
|
|
32665
|
+
}));
|
|
32680
32666
|
} catch (error45) {
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
return [];
|
|
32684
|
-
}
|
|
32685
|
-
console.error(`[swarm] ERROR: Failed to parse beads for epic ${epicId}:`, error45);
|
|
32686
|
-
throw error45;
|
|
32667
|
+
console.error(`[swarm] ERROR: Failed to query subtasks for epic ${epicId}:`, error45 instanceof Error ? error45.message : String(error45));
|
|
32668
|
+
return [];
|
|
32687
32669
|
}
|
|
32688
32670
|
}
|
|
32689
32671
|
async function querySwarmMessages(projectKey, threadId) {
|
|
@@ -33035,7 +33017,7 @@ var swarm_status = tool({
|
|
|
33035
33017
|
project_key: tool.schema.string().describe("Project path (for Agent Mail queries)")
|
|
33036
33018
|
},
|
|
33037
33019
|
async execute(args) {
|
|
33038
|
-
const subtasks = await queryEpicSubtasks(args.epic_id);
|
|
33020
|
+
const subtasks = await queryEpicSubtasks(args.project_key, args.epic_id);
|
|
33039
33021
|
const statusCounts = {
|
|
33040
33022
|
running: 0,
|
|
33041
33023
|
completed: 0,
|
|
@@ -33110,8 +33092,13 @@ var swarm_progress = tool({
|
|
|
33110
33092
|
};
|
|
33111
33093
|
const validated = AgentProgressSchema.parse(progress);
|
|
33112
33094
|
if (args.status === "blocked" || args.status === "in_progress") {
|
|
33113
|
-
|
|
33114
|
-
|
|
33095
|
+
try {
|
|
33096
|
+
const adapter = await getHiveAdapter(args.project_key);
|
|
33097
|
+
const newStatus = args.status === "blocked" ? "blocked" : "in_progress";
|
|
33098
|
+
await adapter.changeCellStatus(args.project_key, args.bead_id, newStatus);
|
|
33099
|
+
} catch (error45) {
|
|
33100
|
+
console.error(`[swarm] Failed to update cell status: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
33101
|
+
}
|
|
33115
33102
|
}
|
|
33116
33103
|
const epicId = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
|
|
33117
33104
|
await sendSwarmMessage3({
|
|
@@ -33255,6 +33242,22 @@ Or use skip_review=true to bypass (not recommended for production work).`
|
|
|
33255
33242
|
}
|
|
33256
33243
|
try {
|
|
33257
33244
|
const projectKey = args.project_key.replace(/\//g, "-").replace(/\\/g, "-");
|
|
33245
|
+
const adapter = await getHiveAdapter(args.project_key);
|
|
33246
|
+
const cell = await adapter.getCell(projectKey, args.bead_id);
|
|
33247
|
+
if (!cell) {
|
|
33248
|
+
return JSON.stringify({
|
|
33249
|
+
success: false,
|
|
33250
|
+
error: `Bead not found: ${args.bead_id}`,
|
|
33251
|
+
hint: "Check the bead ID is correct. Use hive_query to list open cells."
|
|
33252
|
+
});
|
|
33253
|
+
}
|
|
33254
|
+
if (cell.status === "closed") {
|
|
33255
|
+
return JSON.stringify({
|
|
33256
|
+
success: false,
|
|
33257
|
+
error: `Bead already closed: ${args.bead_id}`,
|
|
33258
|
+
hint: "This bead was already completed. No action needed."
|
|
33259
|
+
});
|
|
33260
|
+
}
|
|
33258
33261
|
let agentRegistered = false;
|
|
33259
33262
|
let registrationWarning = "";
|
|
33260
33263
|
try {
|
|
@@ -33339,32 +33342,25 @@ Continuing with completion, but this should be fixed for future subtasks.`;
|
|
|
33339
33342
|
}, null, 2);
|
|
33340
33343
|
}
|
|
33341
33344
|
}
|
|
33342
|
-
|
|
33343
|
-
|
|
33344
|
-
|
|
33345
|
-
const
|
|
33346
|
-
const isNoDatabaseError = stderrOutput.includes("no beads database found");
|
|
33347
|
-
const isNotFoundError = stderrOutput.includes("not found") || stderrOutput.includes("does not exist");
|
|
33345
|
+
try {
|
|
33346
|
+
await adapter.closeCell(args.project_key, args.bead_id, args.summary);
|
|
33347
|
+
} catch (closeError) {
|
|
33348
|
+
const errorMessage = closeError instanceof Error ? closeError.message : String(closeError);
|
|
33348
33349
|
return JSON.stringify({
|
|
33349
33350
|
success: false,
|
|
33350
33351
|
error: "Failed to close cell",
|
|
33351
|
-
failed_step: "
|
|
33352
|
-
details:
|
|
33352
|
+
failed_step: "closeCell",
|
|
33353
|
+
details: errorMessage,
|
|
33353
33354
|
bead_id: args.bead_id,
|
|
33354
33355
|
project_key: args.project_key,
|
|
33355
33356
|
recovery: {
|
|
33356
|
-
steps:
|
|
33357
|
-
`1.
|
|
33358
|
-
`2. Check
|
|
33359
|
-
`3. Cell ID prefix "${args.bead_id.split("-")[0]}" should match project`,
|
|
33360
|
-
`4. Try: hive_close(id="${args.bead_id}", reason="...")`
|
|
33361
|
-
] : [
|
|
33362
|
-
`1. Check cell exists: bd show ${args.bead_id}`,
|
|
33363
|
-
`2. Check cell status (might already be closed): hive_query()`,
|
|
33357
|
+
steps: [
|
|
33358
|
+
`1. Check cell exists: hive_query()`,
|
|
33359
|
+
`2. Check cell status (might already be closed)`,
|
|
33364
33360
|
`3. If cell is blocked, unblock first: hive_update(id="${args.bead_id}", status="in_progress")`,
|
|
33365
33361
|
`4. Try closing directly: hive_close(id="${args.bead_id}", reason="...")`
|
|
33366
33362
|
],
|
|
33367
|
-
hint:
|
|
33363
|
+
hint: "Cell may already be closed, or the ID is incorrect."
|
|
33368
33364
|
}
|
|
33369
33365
|
}, null, 2);
|
|
33370
33366
|
}
|
|
@@ -33565,12 +33561,13 @@ ${errorStack.slice(0, 1000)}
|
|
|
33565
33561
|
}
|
|
33566
33562
|
return JSON.stringify({
|
|
33567
33563
|
success: false,
|
|
33568
|
-
error: errorMessage
|
|
33564
|
+
error: `swarm_complete failed: ${errorMessage}`,
|
|
33569
33565
|
failed_step: failedStep,
|
|
33570
33566
|
bead_id: args.bead_id,
|
|
33571
33567
|
agent_name: args.agent_name,
|
|
33572
33568
|
coordinator_notified: notificationSent,
|
|
33573
33569
|
stack_trace: errorStack?.slice(0, 500),
|
|
33570
|
+
hint: "Check the error message above. Common issues: bead not found, session not initialized.",
|
|
33574
33571
|
context: {
|
|
33575
33572
|
summary: args.summary,
|
|
33576
33573
|
files_touched: args.files_touched || [],
|
|
@@ -35890,6 +35887,7 @@ async function resetStorage() {
|
|
|
35890
35887
|
init_skills();
|
|
35891
35888
|
|
|
35892
35889
|
// src/compaction-hook.ts
|
|
35890
|
+
import { checkSwarmHealth as checkSwarmHealth3 } from "swarm-mail";
|
|
35893
35891
|
var SWARM_COMPACTION_CONTEXT = `## \uD83D\uDC1D SWARM ACTIVE - Keep Cooking
|
|
35894
35892
|
|
|
35895
35893
|
You are the **COORDINATOR** of an active swarm. Context was compacted but the swarm is still running.
|
|
@@ -35945,23 +35943,128 @@ Extract from session context:
|
|
|
35945
35943
|
|
|
35946
35944
|
**You are not waiting for instructions. You are the coordinator. Coordinate.**
|
|
35947
35945
|
`;
|
|
35948
|
-
|
|
35946
|
+
var SWARM_DETECTION_FALLBACK = `## \uD83D\uDC1D Swarm Detection - Check Your Context
|
|
35947
|
+
|
|
35948
|
+
**IMPORTANT:** Before summarizing, check if this session involves an active swarm.
|
|
35949
|
+
|
|
35950
|
+
Look for ANY of these patterns in the conversation:
|
|
35951
|
+
|
|
35952
|
+
### Tool Calls (definite swarm sign)
|
|
35953
|
+
- \`swarm_decompose\`, \`swarm_spawn_subtask\`, \`swarm_status\`, \`swarm_complete\`
|
|
35954
|
+
- \`swarmmail_init\`, \`swarmmail_reserve\`, \`swarmmail_send\`
|
|
35955
|
+
- \`hive_create_epic\`, \`hive_start\`, \`hive_close\`
|
|
35956
|
+
|
|
35957
|
+
### IDs and Names
|
|
35958
|
+
- Cell IDs: \`bd-xxx\`, \`bd-xxx.N\` (subtask format)
|
|
35959
|
+
- Agent names: BlueLake, RedMountain, GreenValley, etc.
|
|
35960
|
+
- Epic references: "epic", "subtask", "parent"
|
|
35961
|
+
|
|
35962
|
+
### Coordination Language
|
|
35963
|
+
- "spawn", "worker", "coordinator"
|
|
35964
|
+
- "reserve", "reservation", "files"
|
|
35965
|
+
- "blocked", "unblock", "dependency"
|
|
35966
|
+
- "progress", "complete", "in_progress"
|
|
35967
|
+
|
|
35968
|
+
### If You Find Swarm Evidence
|
|
35969
|
+
|
|
35970
|
+
Include this in your summary:
|
|
35971
|
+
1. Epic ID and title
|
|
35972
|
+
2. Project path
|
|
35973
|
+
3. Subtask status (running/blocked/done/pending)
|
|
35974
|
+
4. Any blockers or issues
|
|
35975
|
+
5. What should happen next
|
|
35976
|
+
|
|
35977
|
+
**Then tell the resumed session:**
|
|
35978
|
+
"This is an active swarm. Check swarm_status and swarmmail_inbox immediately."
|
|
35979
|
+
`;
|
|
35980
|
+
async function detectSwarm() {
|
|
35981
|
+
const reasons = [];
|
|
35982
|
+
let highConfidence = false;
|
|
35983
|
+
let mediumConfidence = false;
|
|
35984
|
+
let lowConfidence = false;
|
|
35949
35985
|
try {
|
|
35950
35986
|
const projectKey = getHiveWorkingDirectory();
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35987
|
+
try {
|
|
35988
|
+
const health = await checkSwarmHealth3(projectKey);
|
|
35989
|
+
if (health.healthy && health.stats) {
|
|
35990
|
+
if (health.stats.reservations > 0) {
|
|
35991
|
+
highConfidence = true;
|
|
35992
|
+
reasons.push(`${health.stats.reservations} active file reservations`);
|
|
35993
|
+
}
|
|
35994
|
+
if (health.stats.agents > 0) {
|
|
35995
|
+
mediumConfidence = true;
|
|
35996
|
+
reasons.push(`${health.stats.agents} registered agents`);
|
|
35997
|
+
}
|
|
35998
|
+
if (health.stats.messages > 0) {
|
|
35999
|
+
lowConfidence = true;
|
|
36000
|
+
reasons.push(`${health.stats.messages} swarm messages`);
|
|
36001
|
+
}
|
|
36002
|
+
}
|
|
36003
|
+
} catch {}
|
|
36004
|
+
try {
|
|
36005
|
+
const adapter = await getHiveAdapter(projectKey);
|
|
36006
|
+
const cells = await adapter.queryCells(projectKey, {});
|
|
36007
|
+
if (Array.isArray(cells) && cells.length > 0) {
|
|
36008
|
+
const inProgress = cells.filter((c) => c.status === "in_progress");
|
|
36009
|
+
if (inProgress.length > 0) {
|
|
36010
|
+
highConfidence = true;
|
|
36011
|
+
reasons.push(`${inProgress.length} cells in_progress`);
|
|
36012
|
+
}
|
|
36013
|
+
const subtasks = cells.filter((c) => c.status === "open" && c.parent_id);
|
|
36014
|
+
if (subtasks.length > 0) {
|
|
36015
|
+
mediumConfidence = true;
|
|
36016
|
+
reasons.push(`${subtasks.length} open subtasks`);
|
|
36017
|
+
}
|
|
36018
|
+
const openEpics = cells.filter((c) => c.type === "epic" && c.status !== "closed");
|
|
36019
|
+
if (openEpics.length > 0) {
|
|
36020
|
+
mediumConfidence = true;
|
|
36021
|
+
reasons.push(`${openEpics.length} unclosed epics`);
|
|
36022
|
+
}
|
|
36023
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
36024
|
+
const recentCells = cells.filter((c) => c.updated_at > oneHourAgo);
|
|
36025
|
+
if (recentCells.length > 0) {
|
|
36026
|
+
mediumConfidence = true;
|
|
36027
|
+
reasons.push(`${recentCells.length} cells updated in last hour`);
|
|
36028
|
+
}
|
|
36029
|
+
if (cells.length > 0) {
|
|
36030
|
+
lowConfidence = true;
|
|
36031
|
+
reasons.push(`${cells.length} total cells in hive`);
|
|
36032
|
+
}
|
|
36033
|
+
}
|
|
36034
|
+
} catch {}
|
|
35956
36035
|
} catch {
|
|
35957
|
-
|
|
36036
|
+
lowConfidence = true;
|
|
36037
|
+
reasons.push("Could not detect project, using fallback");
|
|
36038
|
+
}
|
|
36039
|
+
let confidence;
|
|
36040
|
+
if (highConfidence) {
|
|
36041
|
+
confidence = "high";
|
|
36042
|
+
} else if (mediumConfidence) {
|
|
36043
|
+
confidence = "medium";
|
|
36044
|
+
} else if (lowConfidence) {
|
|
36045
|
+
confidence = "low";
|
|
36046
|
+
} else {
|
|
36047
|
+
confidence = "none";
|
|
35958
36048
|
}
|
|
36049
|
+
return {
|
|
36050
|
+
detected: confidence !== "none",
|
|
36051
|
+
confidence,
|
|
36052
|
+
reasons
|
|
36053
|
+
};
|
|
35959
36054
|
}
|
|
35960
36055
|
function createCompactionHook() {
|
|
35961
36056
|
return async (_input, output) => {
|
|
35962
|
-
const
|
|
35963
|
-
if (
|
|
35964
|
-
|
|
36057
|
+
const detection = await detectSwarm();
|
|
36058
|
+
if (detection.confidence === "high" || detection.confidence === "medium") {
|
|
36059
|
+
const header = `[Swarm detected: ${detection.reasons.join(", ")}]
|
|
36060
|
+
|
|
36061
|
+
`;
|
|
36062
|
+
output.context.push(header + SWARM_COMPACTION_CONTEXT);
|
|
36063
|
+
} else if (detection.confidence === "low") {
|
|
36064
|
+
const header = `[Possible swarm: ${detection.reasons.join(", ")}]
|
|
36065
|
+
|
|
36066
|
+
`;
|
|
36067
|
+
output.context.push(header + SWARM_DETECTION_FALLBACK);
|
|
35965
36068
|
}
|
|
35966
36069
|
};
|
|
35967
36070
|
}
|