@mclawnet/swarm 0.1.3 → 0.1.5

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.
Files changed (224) hide show
  1. package/README.md +118 -0
  2. package/dist/__tests__/action-parser.test.js +29 -82
  3. package/dist/__tests__/action-parser.test.js.map +1 -1
  4. package/dist/__tests__/coordinator-create-tx.test.d.ts +2 -0
  5. package/dist/__tests__/coordinator-create-tx.test.d.ts.map +1 -0
  6. package/dist/__tests__/coordinator-create-tx.test.js +114 -0
  7. package/dist/__tests__/coordinator-create-tx.test.js.map +1 -0
  8. package/dist/__tests__/coordinator-inbox-migration.test.d.ts +2 -0
  9. package/dist/__tests__/coordinator-inbox-migration.test.d.ts.map +1 -0
  10. package/dist/__tests__/coordinator-inbox-migration.test.js +56 -0
  11. package/dist/__tests__/coordinator-inbox-migration.test.js.map +1 -0
  12. package/dist/__tests__/inbox-integration.test.d.ts +2 -0
  13. package/dist/__tests__/inbox-integration.test.d.ts.map +1 -0
  14. package/dist/__tests__/inbox-integration.test.js +120 -0
  15. package/dist/__tests__/inbox-integration.test.js.map +1 -0
  16. package/dist/__tests__/inbox-persistence-recovery.test.d.ts +2 -0
  17. package/dist/__tests__/inbox-persistence-recovery.test.d.ts.map +1 -0
  18. package/dist/__tests__/inbox-persistence-recovery.test.js +139 -0
  19. package/dist/__tests__/inbox-persistence-recovery.test.js.map +1 -0
  20. package/dist/__tests__/inbox-relay-interceptor.test.d.ts +2 -0
  21. package/dist/__tests__/inbox-relay-interceptor.test.d.ts.map +1 -0
  22. package/dist/__tests__/inbox-relay-interceptor.test.js +156 -0
  23. package/dist/__tests__/inbox-relay-interceptor.test.js.map +1 -0
  24. package/dist/__tests__/inbox-relay.test.d.ts +2 -0
  25. package/dist/__tests__/inbox-relay.test.d.ts.map +1 -0
  26. package/dist/__tests__/inbox-relay.test.js +318 -0
  27. package/dist/__tests__/inbox-relay.test.js.map +1 -0
  28. package/dist/__tests__/inbox-store.test.d.ts +2 -0
  29. package/dist/__tests__/inbox-store.test.d.ts.map +1 -0
  30. package/dist/__tests__/inbox-store.test.js +129 -0
  31. package/dist/__tests__/inbox-store.test.js.map +1 -0
  32. package/dist/__tests__/inbox-watcher.test.d.ts +2 -0
  33. package/dist/__tests__/inbox-watcher.test.d.ts.map +1 -0
  34. package/dist/__tests__/inbox-watcher.test.js +104 -0
  35. package/dist/__tests__/inbox-watcher.test.js.map +1 -0
  36. package/dist/__tests__/persistence-path.test.d.ts +2 -0
  37. package/dist/__tests__/persistence-path.test.d.ts.map +1 -0
  38. package/dist/__tests__/persistence-path.test.js +79 -0
  39. package/dist/__tests__/persistence-path.test.js.map +1 -0
  40. package/dist/__tests__/persistence-robust.test.d.ts +2 -0
  41. package/dist/__tests__/persistence-robust.test.d.ts.map +1 -0
  42. package/dist/__tests__/persistence-robust.test.js +125 -0
  43. package/dist/__tests__/persistence-robust.test.js.map +1 -0
  44. package/dist/__tests__/persistence.test.d.ts +2 -0
  45. package/dist/__tests__/persistence.test.d.ts.map +1 -0
  46. package/dist/__tests__/persistence.test.js +105 -0
  47. package/dist/__tests__/persistence.test.js.map +1 -0
  48. package/dist/__tests__/phase4-5-e2e.test.d.ts +2 -0
  49. package/dist/__tests__/phase4-5-e2e.test.d.ts.map +1 -0
  50. package/dist/__tests__/phase4-5-e2e.test.js +203 -0
  51. package/dist/__tests__/phase4-5-e2e.test.js.map +1 -0
  52. package/dist/__tests__/phase6-7-e2e.test.d.ts +2 -0
  53. package/dist/__tests__/phase6-7-e2e.test.d.ts.map +1 -0
  54. package/dist/__tests__/phase6-7-e2e.test.js +93 -0
  55. package/dist/__tests__/phase6-7-e2e.test.js.map +1 -0
  56. package/dist/__tests__/recovery-cross-project.test.d.ts +2 -0
  57. package/dist/__tests__/recovery-cross-project.test.d.ts.map +1 -0
  58. package/dist/__tests__/recovery-cross-project.test.js +87 -0
  59. package/dist/__tests__/recovery-cross-project.test.js.map +1 -0
  60. package/dist/__tests__/recovery-forwards-to-coordinator.test.d.ts +2 -0
  61. package/dist/__tests__/recovery-forwards-to-coordinator.test.d.ts.map +1 -0
  62. package/dist/__tests__/recovery-forwards-to-coordinator.test.js +59 -0
  63. package/dist/__tests__/recovery-forwards-to-coordinator.test.js.map +1 -0
  64. package/dist/__tests__/recovery-resume.test.d.ts +2 -0
  65. package/dist/__tests__/recovery-resume.test.d.ts.map +1 -0
  66. package/dist/__tests__/recovery-resume.test.js +132 -0
  67. package/dist/__tests__/recovery-resume.test.js.map +1 -0
  68. package/dist/__tests__/retrospective.test.js +1 -0
  69. package/dist/__tests__/retrospective.test.js.map +1 -1
  70. package/dist/__tests__/role-loader-preamble-all.test.d.ts +2 -0
  71. package/dist/__tests__/role-loader-preamble-all.test.d.ts.map +1 -0
  72. package/dist/__tests__/role-loader-preamble-all.test.js +38 -0
  73. package/dist/__tests__/role-loader-preamble-all.test.js.map +1 -0
  74. package/dist/__tests__/role-loader-tools.test.d.ts +2 -0
  75. package/dist/__tests__/role-loader-tools.test.d.ts.map +1 -0
  76. package/dist/__tests__/role-loader-tools.test.js +39 -0
  77. package/dist/__tests__/role-loader-tools.test.js.map +1 -0
  78. package/dist/__tests__/role-loader.test.js +116 -1
  79. package/dist/__tests__/role-loader.test.js.map +1 -1
  80. package/dist/__tests__/role-prompt-no-legacy-protocol.test.d.ts +2 -0
  81. package/dist/__tests__/role-prompt-no-legacy-protocol.test.d.ts.map +1 -0
  82. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js +37 -0
  83. package/dist/__tests__/role-prompt-no-legacy-protocol.test.js.map +1 -0
  84. package/dist/__tests__/role-tools.test.d.ts +2 -0
  85. package/dist/__tests__/role-tools.test.d.ts.map +1 -0
  86. package/dist/__tests__/role-tools.test.js +80 -0
  87. package/dist/__tests__/role-tools.test.js.map +1 -0
  88. package/dist/__tests__/spawn-role-injects-briefings.test.d.ts +2 -0
  89. package/dist/__tests__/spawn-role-injects-briefings.test.d.ts.map +1 -0
  90. package/dist/__tests__/spawn-role-injects-briefings.test.js +182 -0
  91. package/dist/__tests__/spawn-role-injects-briefings.test.js.map +1 -0
  92. package/dist/__tests__/spawn-role-tool-policy.test.d.ts +2 -0
  93. package/dist/__tests__/spawn-role-tool-policy.test.d.ts.map +1 -0
  94. package/dist/__tests__/spawn-role-tool-policy.test.js +96 -0
  95. package/dist/__tests__/spawn-role-tool-policy.test.js.map +1 -0
  96. package/dist/__tests__/swarm-coordinator-inbox-watcher.test.d.ts +2 -0
  97. package/dist/__tests__/swarm-coordinator-inbox-watcher.test.d.ts.map +1 -0
  98. package/dist/__tests__/swarm-coordinator-inbox-watcher.test.js +61 -0
  99. package/dist/__tests__/swarm-coordinator-inbox-watcher.test.js.map +1 -0
  100. package/dist/__tests__/swarm-coordinator-inbox.test.d.ts +2 -0
  101. package/dist/__tests__/swarm-coordinator-inbox.test.d.ts.map +1 -0
  102. package/dist/__tests__/swarm-coordinator-inbox.test.js +182 -0
  103. package/dist/__tests__/swarm-coordinator-inbox.test.js.map +1 -0
  104. package/dist/__tests__/swarm-coordinator-init.test.js +36 -8
  105. package/dist/__tests__/swarm-coordinator-init.test.js.map +1 -1
  106. package/dist/__tests__/swarm-coordinator-legacy-plan-review-warn.test.d.ts +2 -0
  107. package/dist/__tests__/swarm-coordinator-legacy-plan-review-warn.test.d.ts.map +1 -0
  108. package/dist/__tests__/swarm-coordinator-legacy-plan-review-warn.test.js +113 -0
  109. package/dist/__tests__/swarm-coordinator-legacy-plan-review-warn.test.js.map +1 -0
  110. package/dist/__tests__/swarm-coordinator-plan-review-intercept.test.d.ts +2 -0
  111. package/dist/__tests__/swarm-coordinator-plan-review-intercept.test.d.ts.map +1 -0
  112. package/dist/__tests__/swarm-coordinator-plan-review-intercept.test.js +465 -0
  113. package/dist/__tests__/swarm-coordinator-plan-review-intercept.test.js.map +1 -0
  114. package/dist/__tests__/swarm-coordinator-plan-review-recovery.test.d.ts +2 -0
  115. package/dist/__tests__/swarm-coordinator-plan-review-recovery.test.d.ts.map +1 -0
  116. package/dist/__tests__/swarm-coordinator-plan-review-recovery.test.js +284 -0
  117. package/dist/__tests__/swarm-coordinator-plan-review-recovery.test.js.map +1 -0
  118. package/dist/__tests__/swarm-coordinator-plan-review.test.d.ts +2 -0
  119. package/dist/__tests__/swarm-coordinator-plan-review.test.d.ts.map +1 -0
  120. package/dist/__tests__/swarm-coordinator-plan-review.test.js +294 -0
  121. package/dist/__tests__/swarm-coordinator-plan-review.test.js.map +1 -0
  122. package/dist/__tests__/swarm-coordinator-resume.test.d.ts +2 -0
  123. package/dist/__tests__/swarm-coordinator-resume.test.d.ts.map +1 -0
  124. package/dist/__tests__/swarm-coordinator-resume.test.js +93 -0
  125. package/dist/__tests__/swarm-coordinator-resume.test.js.map +1 -0
  126. package/dist/__tests__/swarm-coordinator-roleId.test.js +2 -2
  127. package/dist/__tests__/swarm-coordinator-roleId.test.js.map +1 -1
  128. package/dist/__tests__/swarm-destroy-detach.test.d.ts +2 -0
  129. package/dist/__tests__/swarm-destroy-detach.test.d.ts.map +1 -0
  130. package/dist/__tests__/swarm-destroy-detach.test.js +135 -0
  131. package/dist/__tests__/swarm-destroy-detach.test.js.map +1 -0
  132. package/dist/action-parser.d.ts +0 -9
  133. package/dist/action-parser.d.ts.map +1 -1
  134. package/dist/action-parser.js +0 -114
  135. package/dist/action-parser.js.map +1 -1
  136. package/dist/inbox-relay.d.ts +50 -0
  137. package/dist/inbox-relay.d.ts.map +1 -0
  138. package/dist/inbox-relay.js +168 -0
  139. package/dist/inbox-relay.js.map +1 -0
  140. package/dist/inbox-store.d.ts +25 -0
  141. package/dist/inbox-store.d.ts.map +1 -0
  142. package/dist/inbox-store.js +95 -0
  143. package/dist/inbox-store.js.map +1 -0
  144. package/dist/inbox-watcher.d.ts +13 -0
  145. package/dist/inbox-watcher.d.ts.map +1 -0
  146. package/dist/inbox-watcher.js +89 -0
  147. package/dist/inbox-watcher.js.map +1 -0
  148. package/dist/index.d.ts +4 -3
  149. package/dist/index.d.ts.map +1 -1
  150. package/dist/index.js +3 -2
  151. package/dist/index.js.map +1 -1
  152. package/dist/persistence.d.ts +19 -5
  153. package/dist/persistence.d.ts.map +1 -1
  154. package/dist/persistence.js +97 -22
  155. package/dist/persistence.js.map +1 -1
  156. package/dist/recovery.d.ts +12 -0
  157. package/dist/recovery.d.ts.map +1 -1
  158. package/dist/recovery.js +14 -19
  159. package/dist/recovery.js.map +1 -1
  160. package/dist/roles/role-loader.d.ts +28 -1
  161. package/dist/roles/role-loader.d.ts.map +1 -1
  162. package/dist/roles/role-loader.js +73 -1
  163. package/dist/roles/role-loader.js.map +1 -1
  164. package/dist/roles/role-tools.d.ts +16 -0
  165. package/dist/roles/role-tools.d.ts.map +1 -0
  166. package/dist/roles/role-tools.js +25 -0
  167. package/dist/roles/role-tools.js.map +1 -0
  168. package/dist/roles/types.d.ts +4 -0
  169. package/dist/roles/types.d.ts.map +1 -1
  170. package/dist/swarm-coordinator.d.ts +176 -12
  171. package/dist/swarm-coordinator.d.ts.map +1 -1
  172. package/dist/swarm-coordinator.js +863 -370
  173. package/dist/swarm-coordinator.js.map +1 -1
  174. package/dist/types.d.ts +26 -0
  175. package/dist/types.d.ts.map +1 -1
  176. package/package.json +9 -6
  177. package/roles/analyst-livermore.md +6 -30
  178. package/roles/designer-rams.md +2 -30
  179. package/roles/dev-torvalds.md +8 -44
  180. package/roles/developer.md +5 -21
  181. package/roles/director-jia.md +20 -49
  182. package/roles/editor-boyong.md +8 -40
  183. package/roles/macro-dalio.md +6 -30
  184. package/roles/planner-maoni.md +24 -53
  185. package/roles/pm-jobs.md +20 -71
  186. package/roles/preset-analyst-simons.md +2 -18
  187. package/roles/preset-architect-knuth.md +2 -18
  188. package/roles/preset-designer-norman.md +2 -18
  189. package/roles/preset-designer.md +2 -18
  190. package/roles/preset-dev-carmack.md +2 -18
  191. package/roles/preset-dev-gosling.md +2 -18
  192. package/roles/preset-developer.md +7 -23
  193. package/roles/preset-manager-grove.md +2 -18
  194. package/roles/preset-manager-musk.md +2 -18
  195. package/roles/preset-pm.md +7 -34
  196. package/roles/preset-researcher-feynman.md +2 -18
  197. package/roles/preset-reviewer.md +5 -21
  198. package/roles/preset-strategist-buffett.md +2 -18
  199. package/roles/preset-strategist-munger.md +2 -18
  200. package/roles/preset-strategist-sunzi.md +2 -18
  201. package/roles/preset-tester-beck.md +2 -18
  202. package/roles/preset-tester.md +5 -21
  203. package/roles/preset-writer-orwell.md +2 -18
  204. package/roles/preset-writer.md +2 -18
  205. package/roles/quant-simons.md +5 -32
  206. package/roles/queen.md +25 -41
  207. package/roles/reviewer-martin.md +11 -37
  208. package/roles/reviewer.md +20 -21
  209. package/roles/rhythm-tangsan.md +5 -29
  210. package/roles/risk-taleb.md +4 -32
  211. package/roles/script-shitiesheng.md +8 -31
  212. package/roles/storyboard-xuke.md +9 -29
  213. package/roles/strategist-soros.md +16 -73
  214. package/roles/tester-beck.md +4 -40
  215. package/roles/tester.md +5 -21
  216. package/roles/trader-jones.md +4 -32
  217. package/roles/vfx-guchangwei.md +8 -27
  218. package/roles/writer-zhouzi.md +7 -39
  219. package/templates/dev-team-pro.md +4 -1
  220. package/templates/dev-team.md +3 -1
  221. package/templates/minimal.md +2 -1
  222. package/templates/trading-team.md +6 -1
  223. package/templates/video-team.md +4 -1
  224. package/templates/writing-team.md +4 -1
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # @mclawnet/swarm
2
+
3
+ Swarm coordination, message routing, and role management. Phase 2 adds an
4
+ **Inbox layer** (`InboxStore` + `InboxRelay`) on top of the existing
5
+ `SwarmCoordinator`, giving the host a push-based path for delivering
6
+ messages to agent sessions.
7
+
8
+ ## BREAKING CHANGE — persistence path migrated
9
+
10
+ Swarm state has moved under the per-project directory shared with
11
+ `@mclawnet/task`:
12
+
13
+ ```
14
+ OLD: ~/.clawnet/swarms/<swarmId>/...
15
+ NEW: ~/.clawnet/projects/<encoded-cwd>/swarms/<swarmId>/...
16
+ ```
17
+
18
+ There is **no backward-compatibility shim**. Snapshots, message logs, and
19
+ inbox files written by 0.1.4 and earlier will not be picked up by 0.1.5+.
20
+ Any in-flight swarms should be drained before upgrading.
21
+
22
+ `<encoded-cwd>` is computed by `encodeCwd()` from `@mclawnet/task` (absolute
23
+ path with `/` replaced by `-`).
24
+
25
+ ## Modules
26
+
27
+ ### Coordinator (existing)
28
+
29
+ `SwarmCoordinator`, action parsing, role/template
30
+ loading, persistence, and recovery — see exports in `src/index.ts`.
31
+ Inter-role messaging is delivered via `InboxStore` + `InboxRelay` (see below).
32
+
33
+ ### Inbox (new)
34
+
35
+ ```ts
36
+ interface InboxMessage {
37
+ id: string;
38
+ from: string;
39
+ type: string;
40
+ data: string;
41
+ taskId?: string;
42
+ timestamp: number;
43
+ delivered: boolean;
44
+ deliveredAt?: number;
45
+ }
46
+
47
+ class InboxStore {
48
+ constructor(workDir: string, swarmId: string);
49
+ append(instanceId: string, msg: InboxMessage): Promise<void>;
50
+ readAll(instanceId: string): Promise<InboxMessage[]>;
51
+ readUndelivered(instanceId: string): Promise<InboxMessage[]>;
52
+ markDelivered(instanceId: string, ids: string[]): Promise<void>;
53
+ }
54
+
55
+ class InboxRelay {
56
+ constructor(
57
+ sessionAdapter: SessionAdapter,
58
+ getSwarm: (swarmId: string) => SwarmInstance | undefined,
59
+ );
60
+ deliver(swarmId: string, instanceId: string): Promise<void>;
61
+ onAgentTurnSettled(swarmId: string, instanceId: string): Promise<void>;
62
+ }
63
+ ```
64
+
65
+ `InboxStore` writes one JSON file per recipient under
66
+ `~/.clawnet/projects/<encoded-cwd>/swarms/<swarmId>/inboxes/<instanceId>.json`,
67
+ guarded by `proper-lockfile` for concurrent appenders.
68
+
69
+ `InboxRelay` is the push side. It wraps undelivered messages in a single
70
+ `<info_for_agent>` envelope and feeds them to the session through
71
+ `SessionAdapter.sendInput`. Four gates guard the push:
72
+
73
+ 1. **single-flight** — at most one in-flight `deliver()` per `(swarmId, instanceId)`.
74
+ 2. **liveness** — swarm and role must exist with a `workDir`.
75
+ 3. **lifecycle** — agents in `spawning` or `stopped` are skipped.
76
+ 4. **pending-echo** — messages already pushed but not yet ack'd by a turn
77
+ completion are not re-pushed.
78
+
79
+ `onAgentTurnSettled()` is called when the host sees a `result` event for a
80
+ turn; it marks pending messages `delivered=true` and triggers another
81
+ deliver pass to flush messages that arrived mid-turn.
82
+
83
+ ## Wiring points
84
+
85
+ The host connects the relay to the coordinator at three places:
86
+
87
+ - **spawn** — after `SwarmCoordinator` creates a role and the session is
88
+ ready, call `relay.deliver(swarmId, instanceId)` to flush any inbox
89
+ messages buffered before the agent existed.
90
+ - **resume** — on snapshot recovery, call `deliver()` for each restored role
91
+ so agents receive what queued up while offline.
92
+ - **turn-complete** — on the session adapter's turn-settled signal, call
93
+ `relay.onAgentTurnSettled(swarmId, instanceId)`.
94
+
95
+ Producers append to the inbox via `InboxStore.append()` (e.g. router
96
+ fan-out, task notifications); the relay handles delivery semantics.
97
+
98
+ ## Example
99
+
100
+ ```ts
101
+ import { InboxStore, InboxRelay } from "@mclawnet/swarm";
102
+
103
+ const store = new InboxStore(process.cwd(), swarmId);
104
+ await store.append("worker-1", {
105
+ id: crypto.randomUUID(),
106
+ from: "host",
107
+ type: "task-assigned",
108
+ data: "please pick up TASK-42",
109
+ taskId: "TASK-42",
110
+ timestamp: Date.now(),
111
+ delivered: false,
112
+ });
113
+
114
+ const relay = new InboxRelay(sessionAdapter, (id) => coordinator.get(id));
115
+ await relay.deliver(swarmId, "worker-1");
116
+ // later, when the agent's turn completes:
117
+ await relay.onAgentTurnSettled(swarmId, "worker-1");
118
+ ```
@@ -1,91 +1,38 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { parseSwarmActions } from "../action-parser.js";
3
- describe("parseSwarmActions", () => {
4
- it("should parse a single swarm block", () => {
5
- const text = `一些文字
6
- \`\`\`swarm
7
- {"action":"send","to":"dev-0","type":"task","data":"执行任务"}
2
+ import { parsePlanFromText } from "../action-parser.js";
3
+ describe("parsePlanFromText", () => {
4
+ it("should parse plan from ```json code block", () => {
5
+ const text = `Queen 给出方案:
6
+ \`\`\`json
7
+ {"plan":{"goal":"实现登录","tasks":[{"id":"t1","desc":"写表单"}]}}
8
8
  \`\`\`
9
- 后续文字`;
10
- const { actions, cleanText } = parseSwarmActions(text);
11
- expect(actions).toHaveLength(1);
12
- expect(actions[0]).toMatchObject({ action: "send", to: "dev-0", data: "执行任务" });
13
- expect(cleanText).not.toContain("```swarm");
14
- expect(cleanText).toContain("一些文字");
15
- expect(cleanText).toContain("后续文字");
9
+ 请审核`;
10
+ const plan = parsePlanFromText(text);
11
+ expect(plan).not.toBeNull();
12
+ expect(plan.goal).toBe("实现登录");
13
+ expect(plan.tasks).toHaveLength(1);
16
14
  });
17
- it("should parse swarm block with nested code blocks in data field", () => {
18
- const text = `\`\`\`swarm
19
- {"action":"send","to":"dev-0","type":"task","data":"请实现:\\n\`\`\`typescript\\nconst x = 1;\\n\`\`\`\\n完成后汇报"}
20
- \`\`\``;
21
- const { actions } = parseSwarmActions(text);
22
- expect(actions).toHaveLength(1);
23
- expect(actions[0]).toMatchObject({ action: "send", to: "dev-0" });
24
- expect(actions[0].data).toContain("```typescript");
25
- expect(actions[0].data).toContain("const x = 1;");
15
+ it("should parse inline JSON containing plan key", () => {
16
+ const text = `这里直接给出 {"plan":{"goal":"x","tasks":[]}} 内联 JSON`;
17
+ const plan = parsePlanFromText(text);
18
+ expect(plan).not.toBeNull();
19
+ expect(plan.goal).toBe("x");
26
20
  });
27
- it("should parse multiple swarm blocks", () => {
28
- const text = `\`\`\`swarm
29
- {"action":"send","to":"dev-0","type":"task","data":"任务1"}
30
- \`\`\`
31
- 中间文字
32
- \`\`\`swarm
33
- {"action":"report","status":"completed","output":"完成"}
34
- \`\`\``;
35
- const { actions, cleanText } = parseSwarmActions(text);
36
- expect(actions).toHaveLength(2);
37
- expect(actions[0]).toMatchObject({ action: "send" });
38
- expect(actions[1]).toMatchObject({ action: "report", status: "completed" });
39
- expect(cleanText).toContain("中间文字");
40
- expect(cleanText).not.toContain("```swarm");
41
- });
42
- it("should skip malformed JSON", () => {
43
- const text = `\`\`\`swarm
44
- {"action":"send","to":"dev-0"
45
- \`\`\``;
46
- const { actions } = parseSwarmActions(text);
47
- expect(actions).toHaveLength(0);
48
- });
49
- it("should skip JSON without action field", () => {
50
- const text = `\`\`\`swarm
51
- {"foo":"bar"}
52
- \`\`\``;
53
- const { actions } = parseSwarmActions(text);
54
- expect(actions).toHaveLength(0);
55
- });
56
- it("should handle nested braces in data field", () => {
57
- const text = `\`\`\`swarm
58
- {"action":"send","to":"dev-0","type":"task","data":"{\\"nested\\":{\\"deep\\":true}}"}
59
- \`\`\``;
60
- const { actions } = parseSwarmActions(text);
61
- expect(actions).toHaveLength(1);
62
- expect(actions[0].data).toBe('{"nested":{"deep":true}}');
21
+ it("should return null when there is no plan", () => {
22
+ expect(parsePlanFromText("普通文字,不含 plan")).toBeNull();
63
23
  });
64
- it("should return empty actions for text without swarm blocks", () => {
65
- const text = "普通文字,没有任何指令";
66
- const { actions, cleanText } = parseSwarmActions(text);
67
- expect(actions).toHaveLength(0);
68
- expect(cleanText).toBe(text);
24
+ it("should return null for malformed JSON", () => {
25
+ const text = "```json\n{\"plan\": { broken }\n```";
26
+ expect(parsePlanFromText(text)).toBeNull();
69
27
  });
70
- it("should produce clean text with swarm blocks removed", () => {
71
- const text = `前文
72
- \`\`\`swarm
73
- {"action":"report","status":"completed","output":"done"}
74
- \`\`\`
75
- 后文`;
76
- const { cleanText } = parseSwarmActions(text);
77
- expect(cleanText).not.toContain("```swarm");
78
- expect(cleanText).not.toContain('"action"');
79
- expect(cleanText).toContain("前文");
80
- expect(cleanText).toContain("后文");
81
- });
82
- it("should handle escaped quotes in data field", () => {
83
- const text = `\`\`\`swarm
84
- {"action":"send","to":"dev-0","type":"task","data":"他说:\\"你好\\""}
85
- \`\`\``;
86
- const { actions } = parseSwarmActions(text);
87
- expect(actions).toHaveLength(1);
88
- expect(actions[0].data).toBe('他说:"你好"');
28
+ });
29
+ describe("action-parser exports (hard cut)", () => {
30
+ it("should not export parseSwarmActions / extractSwarmJsonBlocks / tryExtractPlan", async () => {
31
+ const mod = await import("../action-parser.js");
32
+ expect("parseSwarmActions" in mod).toBe(false);
33
+ expect("extractSwarmJsonBlocks" in mod).toBe(false);
34
+ expect("tryExtractPlan" in mod).toBe(false);
35
+ expect("parsePlanFromText" in mod).toBe(true);
89
36
  });
90
37
  });
91
38
  //# sourceMappingURL=action-parser.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/action-parser.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/action-parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,IAAI,GAAG;;;;IAIb,CAAC;QACD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAQ,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,IAAI,GAAG,iDAAiD,CAAC;QAC/D,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAQ,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,qCAAqC,CAAC;QACnD,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC7F,MAAM,GAAG,GAA4B,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACzE,MAAM,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,CAAC,wBAAwB,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,gBAAgB,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coordinator-create-tx.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-create-tx.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordinator-create-tx.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ const { warnFn } = vi.hoisted(() => ({ warnFn: vi.fn() }));
3
+ vi.mock("@mclawnet/logger", () => ({
4
+ createLogger: () => ({ info: vi.fn(), warn: warnFn, error: vi.fn(), debug: vi.fn() }),
5
+ }));
6
+ vi.mock("../retrospective.js", () => ({ runRetrospective: vi.fn() }));
7
+ import { mkdtempSync, rmSync, existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ // keep persistence real so we can observe snapshot cleanup
11
+ import { SwarmCoordinator } from "../swarm-coordinator.js";
12
+ import { projectRoot } from "@mclawnet/task";
13
+ let tempHome;
14
+ let workDir;
15
+ let prevHome;
16
+ beforeEach(() => {
17
+ tempHome = mkdtempSync(join(tmpdir(), "create-tx-"));
18
+ workDir = mkdtempSync(join(tmpdir(), "create-tx-work-"));
19
+ prevHome = process.env.CLAWNET_HOME;
20
+ process.env.CLAWNET_HOME = tempHome;
21
+ warnFn.mockClear();
22
+ });
23
+ afterEach(() => {
24
+ if (prevHome === undefined)
25
+ delete process.env.CLAWNET_HOME;
26
+ else
27
+ process.env.CLAWNET_HOME = prevHome;
28
+ rmSync(tempHome, { recursive: true, force: true });
29
+ rmSync(workDir, { recursive: true, force: true });
30
+ });
31
+ function makeCoordinator(opts) {
32
+ let openCount = 0;
33
+ const sessionAdapter = {
34
+ createSession: vi.fn(async (options) => {
35
+ openCount++;
36
+ if (opts.failAtRole && openCount === opts.failAtRole) {
37
+ throw new Error(`simulated openSession failure on call ${openCount}`);
38
+ }
39
+ return options.sessionId;
40
+ }),
41
+ closeSession: vi.fn(async (id) => { opts.closed.push(id); }),
42
+ sendInput: vi.fn(),
43
+ };
44
+ const hub = { send: vi.fn() };
45
+ return new SwarmCoordinator(sessionAdapter, hub);
46
+ }
47
+ describe("SwarmCoordinator.create — transactional cleanup", () => {
48
+ it("on spawnRole failure, retry with same swarmId succeeds (state was cleaned up)", async () => {
49
+ const closed = [];
50
+ const coord1 = makeCoordinator({ failAtRole: 1, closed });
51
+ await expect(coord1.create("swarm-tx-1", {
52
+ workDir,
53
+ roles: [{ roleName: "queen", eager: true }],
54
+ task: "hello",
55
+ })).rejects.toThrow(/simulated openSession failure/);
56
+ // Memory registry is empty — same id can be reused
57
+ expect(coord1.hasSwarm("swarm-tx-1")).toBe(false);
58
+ // No persisted recovery.json left behind
59
+ const recoveryPath = join(projectRoot(workDir, tempHome), "swarms", "swarm-tx-1", "recovery.json");
60
+ expect(existsSync(recoveryPath)).toBe(false);
61
+ // Retry must NOT throw "already exists"
62
+ const closed2 = [];
63
+ const coord2 = makeCoordinator({ closed: closed2 }); // no failure this time
64
+ await expect(coord2.create("swarm-tx-1", {
65
+ workDir,
66
+ roles: [{ roleName: "queen", eager: true }],
67
+ task: "hello",
68
+ })).resolves.toBeUndefined();
69
+ });
70
+ it("on spawnRole failure mid-loop, all previously opened sessions are closed and timer cleared", async () => {
71
+ const closed = [];
72
+ // Two roles requested; fail on the SECOND openSession
73
+ const coord = makeCoordinator({ failAtRole: 2, closed });
74
+ await expect(coord.create("swarm-tx-2", {
75
+ workDir,
76
+ roles: [
77
+ { roleName: "queen", eager: true },
78
+ { roleName: "developer", eager: true },
79
+ ],
80
+ task: "hello",
81
+ })).rejects.toThrow(/simulated openSession failure on call 2/);
82
+ // The successfully-opened first role's session must have been closed.
83
+ expect(closed.length).toBeGreaterThanOrEqual(1);
84
+ // No swarm in the map → its checkTimer cannot fire.
85
+ expect(coord.hasSwarm("swarm-tx-2")).toBe(false);
86
+ // Cleanup is logged for visibility (operators want to see this).
87
+ const cleanupLogged = warnFn.mock.calls.some(([ctx, msg]) => {
88
+ const ctxStr = JSON.stringify(ctx ?? {});
89
+ return ctxStr.includes("swarm-tx-2") &&
90
+ /partial.*create|cleanup|rollback/i.test(String(msg ?? ""));
91
+ });
92
+ expect(cleanupLogged).toBe(true);
93
+ });
94
+ it("on spawnRole failure, a pre-existing recovery.json from a prior run is preserved", async () => {
95
+ // Simulate the continuation / restart-after-crash path: recovery.json
96
+ // already exists on disk before create() runs.
97
+ const swarmDir = join(projectRoot(workDir, tempHome), "swarms", "swarm-tx-3");
98
+ mkdirSync(swarmDir, { recursive: true });
99
+ const recoveryPath = join(swarmDir, "recovery.json");
100
+ const priorContent = JSON.stringify({ id: "swarm-tx-3", marker: "PRIOR" });
101
+ writeFileSync(recoveryPath, priorContent);
102
+ const closed = [];
103
+ const coord = makeCoordinator({ failAtRole: 1, closed });
104
+ await expect(coord.create("swarm-tx-3", {
105
+ workDir,
106
+ roles: [{ roleName: "queen", eager: true }],
107
+ task: "hello",
108
+ })).rejects.toThrow(/simulated openSession failure/);
109
+ // The prior snapshot must NOT be deleted by cleanup.
110
+ expect(existsSync(recoveryPath)).toBe(true);
111
+ expect(readFileSync(recoveryPath, "utf-8")).toBe(priorContent);
112
+ });
113
+ });
114
+ //# sourceMappingURL=coordinator-create-tx.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-create-tx.test.js","sourceRoot":"","sources":["../../src/__tests__/coordinator-create-tx.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coordinator-inbox-migration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-inbox-migration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/coordinator-inbox-migration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ vi.mock("@mclawnet/logger", () => ({
6
+ createLogger: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }),
7
+ }));
8
+ vi.mock("../persistence.js", () => ({
9
+ saveSwarmSnapshot: vi.fn(),
10
+ deleteSwarmSnapshot: vi.fn(),
11
+ appendMessageLog: vi.fn(),
12
+ loadSwarmSnapshot: vi.fn(),
13
+ readMessageLog: vi.fn(() => []),
14
+ }));
15
+ vi.mock("../retrospective.js", () => ({
16
+ runRetrospective: vi.fn(async () => ({})),
17
+ }));
18
+ import { SwarmCoordinator } from "../swarm-coordinator.js";
19
+ import { InboxStore } from "../inbox-store.js";
20
+ let HOME;
21
+ let WORK;
22
+ beforeEach(() => {
23
+ HOME = mkdtempSync(join(tmpdir(), "coord-mig-home-"));
24
+ WORK = mkdtempSync(join(tmpdir(), "coord-mig-work-"));
25
+ process.env.CLAWNET_HOME = HOME;
26
+ });
27
+ afterEach(() => {
28
+ rmSync(HOME, { recursive: true, force: true });
29
+ rmSync(WORK, { recursive: true, force: true });
30
+ delete process.env.CLAWNET_HOME;
31
+ });
32
+ describe("Task 6 — coordinator routes through InboxStore", () => {
33
+ it("handleUserMessage writes the user task into the queen's inbox file", async () => {
34
+ const sessionAdapter = {
35
+ createSession: vi.fn(async (o) => `proc-${o.sessionId}`),
36
+ sendInput: vi.fn(),
37
+ closeSession: vi.fn(async () => { }),
38
+ };
39
+ const hub = { send: vi.fn() };
40
+ const coordinator = new SwarmCoordinator(sessionAdapter, hub);
41
+ const swarmId = "sw-mig-1";
42
+ await coordinator.create(swarmId, {
43
+ workDir: WORK,
44
+ roles: [{ roleName: "queen", count: 1, eager: true }],
45
+ });
46
+ await coordinator.handleUserMessage(swarmId, "make me a sandwich");
47
+ // Allow the fire-and-forget deliver in handleUserMessage to settle.
48
+ await new Promise((r) => setTimeout(r, 30));
49
+ const store = new InboxStore(WORK, swarmId);
50
+ const all = await store.readAll("queen-0");
51
+ const userMsg = all.find((m) => m.from === "user" && m.data === "make me a sandwich");
52
+ expect(userMsg).toBeDefined();
53
+ expect(userMsg.type).toBe("task");
54
+ });
55
+ });
56
+ //# sourceMappingURL=coordinator-inbox-migration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator-inbox-migration.test.js","sourceRoot":"","sources":["../../src/__tests__/coordinator-inbox-migration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;CACvF,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE;IAC1B,mBAAmB,EAAE,EAAE,CAAC,EAAE,EAAE;IAC5B,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE;IACzB,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE;IAC1B,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;CAChC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE,CAAC,CAAC;IACpC,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;CAC1C,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/C,IAAI,IAAY,CAAC;AACjB,IAAI,IAAY,CAAC;AAEjB,UAAU,CAAC,GAAG,EAAE;IACd,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACtD,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,cAAc,GAAmB;YACrC,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;YACxD,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;YAClB,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;SACpC,CAAC;QACF,MAAM,GAAG,GAAe,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG,UAAU,CAAC;QAC3B,MAAM,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE;YAChC,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;SACtD,CAAC,CAAC;QAEH,MAAM,WAAW,CAAC,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACnE,oEAAoE;QACpE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAE5C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE3C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC;QACtF,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inbox-integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/inbox-integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync, readFileSync, existsSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ // Mock logger
6
+ vi.mock("@mclawnet/logger", () => ({
7
+ createLogger: () => ({
8
+ info: vi.fn(),
9
+ warn: vi.fn(),
10
+ error: vi.fn(),
11
+ debug: vi.fn(),
12
+ }),
13
+ }));
14
+ // Mock persistence
15
+ vi.mock("../persistence.js", () => ({
16
+ saveSwarmSnapshot: vi.fn(),
17
+ deleteSwarmSnapshot: vi.fn(),
18
+ appendMessageLog: vi.fn(),
19
+ loadSwarmSnapshot: vi.fn(),
20
+ readMessageLog: vi.fn(() => []),
21
+ }));
22
+ vi.mock("../retrospective.js", () => ({
23
+ runRetrospective: vi.fn(async () => ({})),
24
+ }));
25
+ import { SwarmCoordinator, InboxStore, InboxRelay, } from "../index.js";
26
+ function createMockSessionAdapter() {
27
+ const inputs = [];
28
+ const adapter = {
29
+ _inputs: inputs,
30
+ createSession: vi.fn(async (options) => `proc-${options.sessionId}`),
31
+ sendInput: vi.fn((sessionId, input) => {
32
+ inputs.push({ sessionId, input });
33
+ }),
34
+ closeSession: vi.fn(async () => { }),
35
+ };
36
+ return adapter;
37
+ }
38
+ function createMockHubAdapter() {
39
+ const adapter = { send: vi.fn() };
40
+ return adapter;
41
+ }
42
+ const baseMsg = {
43
+ from: "queen",
44
+ type: "task",
45
+ data: "implement feature X",
46
+ timestamp: 1,
47
+ delivered: false,
48
+ };
49
+ let HOME;
50
+ let WORK;
51
+ beforeEach(() => {
52
+ HOME = mkdtempSync(join(tmpdir(), "swarm-inbox-int-"));
53
+ WORK = mkdtempSync(join(tmpdir(), "swarm-inbox-int-work-"));
54
+ process.env.CLAWNET_HOME = HOME;
55
+ });
56
+ afterEach(() => {
57
+ rmSync(HOME, { recursive: true, force: true });
58
+ rmSync(WORK, { recursive: true, force: true });
59
+ delete process.env.CLAWNET_HOME;
60
+ });
61
+ describe("Inbox end-to-end integration", () => {
62
+ it("re-exports InboxStore, InboxRelay, and InboxMessage type from package index", () => {
63
+ expect(typeof InboxStore).toBe("function");
64
+ expect(typeof InboxRelay).toBe("function");
65
+ // InboxMessage is a type-only export — verified at compile time.
66
+ const msg = { ...baseMsg, id: "x" };
67
+ expect(msg.id).toBe("x");
68
+ });
69
+ it("delivers inbox message via inboxRelay.deliver and marks it delivered after turn-complete", async () => {
70
+ const sessionAdapter = createMockSessionAdapter();
71
+ const hubAdapter = createMockHubAdapter();
72
+ const coordinator = new SwarmCoordinator(sessionAdapter, hubAdapter);
73
+ const swarmId = "sw-int-1";
74
+ // Spec: pre-populate the developer's inbox BEFORE create() is fine, but
75
+ // the spec also mentions "directly use coordinator.inboxRelay.deliver(...)".
76
+ // We do both: create the swarm, then append to dev-0's inbox, then call
77
+ // inboxRelay.deliver directly.
78
+ await coordinator.create(swarmId, {
79
+ workDir: WORK,
80
+ roles: [
81
+ { roleName: "queen", count: 1, eager: true },
82
+ { roleName: "developer", count: 1, eager: true },
83
+ ],
84
+ task: "Outer task",
85
+ });
86
+ // Wait for spawn-time deliver passes to settle.
87
+ await new Promise((r) => setTimeout(r, 30));
88
+ // Append a fresh inbox message addressed to dev-0.
89
+ const store = new InboxStore(WORK, swarmId);
90
+ await store.append("dev-0", { ...baseMsg, id: "int-msg-1", data: "do the thing" });
91
+ // Directly trigger delivery via the public relay.
92
+ expect(coordinator.inboxRelay).toBeInstanceOf(InboxRelay);
93
+ await coordinator.inboxRelay.deliver(swarmId, "dev-0");
94
+ // sendInput should have been called with the wrapped envelope to dev-0.
95
+ const devInputs = sessionAdapter._inputs.filter((i) => i.sessionId === `${swarmId}::dev-0` && i.input.includes("<info_for_agent>"));
96
+ expect(devInputs.length).toBeGreaterThanOrEqual(1);
97
+ const lastDev = devInputs[devInputs.length - 1];
98
+ expect(lastDev.input).toEqual(expect.stringContaining("<info_for_agent>"));
99
+ expect(lastDev.input).toEqual(expect.stringMatching(/<message [^>]*id="int-msg-1"/));
100
+ expect(lastDev.input).toContain("do the thing");
101
+ // The message is still pendingEcho — not yet marked delivered.
102
+ let all = await store.readAll("dev-0");
103
+ let m = all.find((x) => x.id === "int-msg-1");
104
+ expect(m.delivered).toBe(false);
105
+ // Trigger turn-complete, which should call onAgentTurnSettled and mark delivered.
106
+ const handled = coordinator.handleRoleTurnComplete(`${swarmId}::dev-0`, {});
107
+ expect(handled).toBe(true);
108
+ // Wait for the async markDelivered.
109
+ await new Promise((r) => setTimeout(r, 80));
110
+ // Read the inbox file directly and assert delivered flag + deliveredAt.
111
+ const inboxFile = store.inboxFile("dev-0");
112
+ expect(existsSync(inboxFile)).toBe(true);
113
+ const raw = JSON.parse(readFileSync(inboxFile, "utf-8"));
114
+ const persisted = raw.find((x) => x.id === "int-msg-1");
115
+ expect(persisted.delivered).toBe(true);
116
+ expect(typeof persisted.deliveredAt).toBe("number");
117
+ expect(persisted.deliveredAt).toBeGreaterThan(0);
118
+ });
119
+ });
120
+ //# sourceMappingURL=inbox-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/inbox-integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inbox-persistence-recovery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox-persistence-recovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/inbox-persistence-recovery.test.ts"],"names":[],"mappings":""}