claude-code-swarm 0.3.20 → 0.3.22
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CLAUDE.md +9 -0
- package/package.json +1 -1
- package/scripts/map-sidecar.mjs +2 -0
- package/src/__tests__/opentasks-connector.test.mjs +47 -2
- package/src/map-connection.mjs +7 -1
- package/src/opentasks-connector.mjs +7 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-swarm",
|
|
3
3
|
"description": "Spin up Claude Code agent teams from openteams YAML topologies with optional MAP (Multi-Agent Protocol) observability and coordination. Provides hooks for session lifecycle, agent spawn/complete tracking, and a /swarm skill to launch team configurations.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.22",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "alexngai"
|
|
7
7
|
},
|
package/CLAUDE.md
CHANGED
|
@@ -398,6 +398,15 @@ Both modes:
|
|
|
398
398
|
- Report trajectory checkpoints via `trajectory/checkpoint` (with broadcast fallback)
|
|
399
399
|
- Self-terminate after 30 minutes of inactivity (session mode)
|
|
400
400
|
|
|
401
|
+
**MAP capabilities declared** (in `src/map-connection.mjs`):
|
|
402
|
+
- `messaging: { canSend: true, canReceive: true }` — can exchange MAP scope messages
|
|
403
|
+
- `mail: { canCreate: true, canJoin: true, canViewHistory: true }` — supports agent-inbox conversations (enables Mail chat mode in OpenHive session view)
|
|
404
|
+
- `trajectory: { canReport: true, canServeContent: true }` — reports checkpoints, serves transcript content on demand
|
|
405
|
+
- `tasks: { canCreate, canAssign, canUpdate, canList }` — task management
|
|
406
|
+
- `opentasks: { canQuery, canLink, canAnnotate, canTask }` — conditional, when task_graph configured
|
|
407
|
+
|
|
408
|
+
Message delivery is **pull-based**: the `UserPromptSubmit` hook reads the inbox on each turn and injects messages into Claude Code's prompt context. No real-time push delivery.
|
|
409
|
+
|
|
401
410
|
The hook helper (`scripts/map-hook.mjs`) includes best-effort auto-recovery: if the sidecar is down, it attempts to restart it, with a fire-and-forget fallback (mesh or direct WebSocket) if recovery fails.
|
|
402
411
|
|
|
403
412
|
### OpenTasks integration
|
package/package.json
CHANGED
package/scripts/map-sidecar.mjs
CHANGED
|
@@ -224,6 +224,7 @@ function startSlowReconnectLoop() {
|
|
|
224
224
|
systemId: SYSTEM_ID,
|
|
225
225
|
credential: AUTH_CREDENTIAL || undefined,
|
|
226
226
|
projectContext: PROJECT_CONTEXT,
|
|
227
|
+
inboxEnabled: !!INBOX_CONFIG || MESH_ENABLED,
|
|
227
228
|
onMessage: () => resetInactivityTimer(),
|
|
228
229
|
});
|
|
229
230
|
|
|
@@ -409,6 +410,7 @@ async function startWebSocketTransport() {
|
|
|
409
410
|
systemId: SYSTEM_ID,
|
|
410
411
|
credential: AUTH_CREDENTIAL || undefined,
|
|
411
412
|
projectContext: PROJECT_CONTEXT,
|
|
413
|
+
inboxEnabled: !!INBOX_CONFIG || MESH_ENABLED,
|
|
412
414
|
onMessage: () => {
|
|
413
415
|
resetInactivityTimer();
|
|
414
416
|
},
|
|
@@ -5,9 +5,17 @@ import { registerOpenTasksHandler } from "../opentasks-connector.mjs";
|
|
|
5
5
|
|
|
6
6
|
const MOCK_METHODS = {
|
|
7
7
|
QUERY_REQUEST: "opentasks/query.request",
|
|
8
|
+
QUERY_RESPONSE: "opentasks/query.response",
|
|
8
9
|
LINK_REQUEST: "opentasks/link.request",
|
|
10
|
+
LINK_RESPONSE: "opentasks/link.response",
|
|
9
11
|
ANNOTATE_REQUEST: "opentasks/annotate.request",
|
|
12
|
+
ANNOTATE_RESPONSE: "opentasks/annotate.response",
|
|
10
13
|
TASK_REQUEST: "opentasks/task.request",
|
|
14
|
+
TASK_RESPONSE: "opentasks/task.response",
|
|
15
|
+
GRAPH_CREATE_REQUEST: "opentasks/graph.create.request",
|
|
16
|
+
GRAPH_CREATE_RESPONSE: "opentasks/graph.create.response",
|
|
17
|
+
GRAPH_UPDATE_REQUEST: "opentasks/graph.update.request",
|
|
18
|
+
GRAPH_UPDATE_RESPONSE: "opentasks/graph.update.response",
|
|
11
19
|
};
|
|
12
20
|
|
|
13
21
|
function createMockConnection() {
|
|
@@ -89,16 +97,53 @@ describe("registerOpenTasksHandler", () => {
|
|
|
89
97
|
expect(callArgs.agentId).toBe("swarm:test-sidecar");
|
|
90
98
|
});
|
|
91
99
|
|
|
92
|
-
it("registers onNotification for
|
|
100
|
+
it("registers onNotification for every *.request method exported by opentasks", async () => {
|
|
93
101
|
await callRegister();
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
const expectedRequestMethods = Object.values(MOCK_METHODS).filter((m) =>
|
|
104
|
+
m.endsWith(".request"),
|
|
105
|
+
);
|
|
106
|
+
expect(mockConn.onNotification).toHaveBeenCalledTimes(expectedRequestMethods.length);
|
|
96
107
|
|
|
97
108
|
const registeredMethods = mockConn.onNotification.mock.calls.map((c) => c[0]);
|
|
98
109
|
expect(registeredMethods).toContain(MOCK_METHODS.QUERY_REQUEST);
|
|
99
110
|
expect(registeredMethods).toContain(MOCK_METHODS.LINK_REQUEST);
|
|
100
111
|
expect(registeredMethods).toContain(MOCK_METHODS.ANNOTATE_REQUEST);
|
|
101
112
|
expect(registeredMethods).toContain(MOCK_METHODS.TASK_REQUEST);
|
|
113
|
+
expect(registeredMethods).toContain(MOCK_METHODS.GRAPH_CREATE_REQUEST);
|
|
114
|
+
expect(registeredMethods).toContain(MOCK_METHODS.GRAPH_UPDATE_REQUEST);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("does not register response methods (only .request is subscribed)", async () => {
|
|
118
|
+
await callRegister();
|
|
119
|
+
|
|
120
|
+
const registeredMethods = mockConn.onNotification.mock.calls.map((c) => c[0]);
|
|
121
|
+
const responseMethods = registeredMethods.filter((m) => m.endsWith(".response"));
|
|
122
|
+
expect(responseMethods).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("forwards graph.create.request to the connector", async () => {
|
|
126
|
+
await callRegister();
|
|
127
|
+
|
|
128
|
+
const params = { request_id: "req-c1", create: { type: "task", title: "New" } };
|
|
129
|
+
await mockConn._fireNotification(MOCK_METHODS.GRAPH_CREATE_REQUEST, params);
|
|
130
|
+
|
|
131
|
+
expect(mockOpentasks._connector.handleNotification).toHaveBeenCalledWith(
|
|
132
|
+
MOCK_METHODS.GRAPH_CREATE_REQUEST,
|
|
133
|
+
params,
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("forwards graph.update.request to the connector", async () => {
|
|
138
|
+
await callRegister();
|
|
139
|
+
|
|
140
|
+
const params = { request_id: "req-u1", update: { id: "n-1", title: "Renamed" } };
|
|
141
|
+
await mockConn._fireNotification(MOCK_METHODS.GRAPH_UPDATE_REQUEST, params);
|
|
142
|
+
|
|
143
|
+
expect(mockOpentasks._connector.handleNotification).toHaveBeenCalledWith(
|
|
144
|
+
MOCK_METHODS.GRAPH_UPDATE_REQUEST,
|
|
145
|
+
params,
|
|
146
|
+
);
|
|
102
147
|
});
|
|
103
148
|
|
|
104
149
|
it("forwards notifications to connector.handleNotification", async () => {
|
package/src/map-connection.mjs
CHANGED
|
@@ -24,7 +24,7 @@ const log = createLogger("map");
|
|
|
24
24
|
* authRequired challenge with the server's preferred method + this credential.
|
|
25
25
|
* When absent, uses the standard SDK connect() for open mode servers.
|
|
26
26
|
*/
|
|
27
|
-
export async function connectToMAP({ server, scope, systemId, onMessage, credential, projectContext }) {
|
|
27
|
+
export async function connectToMAP({ server, scope, systemId, onMessage, credential, projectContext, inboxEnabled }) {
|
|
28
28
|
try {
|
|
29
29
|
const mapSdk = await resolvePackage("@multi-agent-protocol/sdk");
|
|
30
30
|
if (!mapSdk) throw new Error("@multi-agent-protocol/sdk not available");
|
|
@@ -38,6 +38,12 @@ export async function connectToMAP({ server, scope, systemId, onMessage, credent
|
|
|
38
38
|
role: "sidecar",
|
|
39
39
|
scopes: [scope],
|
|
40
40
|
capabilities: {
|
|
41
|
+
// Messaging and mail capabilities are conditional on agent-inbox being available.
|
|
42
|
+
// Without inbox, the sidecar can't receive messages or participate in conversations.
|
|
43
|
+
...(inboxEnabled ? {
|
|
44
|
+
messaging: { canSend: true, canReceive: true },
|
|
45
|
+
mail: { canCreate: true, canJoin: true, canViewHistory: true },
|
|
46
|
+
} : {}),
|
|
41
47
|
trajectory: { canReport: true, canServeContent: true },
|
|
42
48
|
tasks: { canCreate: true, canAssign: true, canUpdate: true, canList: true },
|
|
43
49
|
...(projectContext?.task_graph ? {
|
|
@@ -63,13 +63,13 @@ export async function registerOpenTasksHandler(conn, options = {}) {
|
|
|
63
63
|
agentId: `${scope}-sidecar`,
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
// Subscribe to every `opentasks/*.request` method the opentasks package
|
|
67
|
+
// exports. Iterating MAP_CONNECTOR_METHODS (rather than hardcoding a list)
|
|
68
|
+
// means new request methods added upstream — e.g. graph.create.request,
|
|
69
|
+
// graph.update.request — are wired up automatically.
|
|
70
|
+
const requestMethods = Object.values(MAP_CONNECTOR_METHODS).filter(
|
|
71
|
+
(m) => typeof m === "string" && m.endsWith(".request"),
|
|
72
|
+
);
|
|
73
73
|
|
|
74
74
|
for (const method of requestMethods) {
|
|
75
75
|
conn.onNotification(method, async (params) => {
|