macro-agent 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.
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Tests for Trajectory Reporter — checkpoint building & reporting.
2
+ * Tests for Trajectory Reporter — checkpoint reporting & content serving.
3
3
  */
4
4
 
5
- import { describe, it, expect, beforeEach, vi } from "vitest";
5
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
6
+ import * as fs from "node:fs";
7
+ import * as path from "node:path";
8
+ import * as os from "node:os";
6
9
  import {
7
10
  createTrajectoryReporter,
8
11
  type TrajectoryConnection,
@@ -171,3 +174,252 @@ describe("TrajectoryReporter", () => {
171
174
  );
172
175
  });
173
176
  });
177
+
178
+ // =============================================================================
179
+ // Tests — Content Serving via sessionlog
180
+ // =============================================================================
181
+
182
+ describe("TrajectoryReporter — content serving", () => {
183
+ let conn: ReturnType<typeof mockConnection>;
184
+ let tmpDir: string;
185
+
186
+ beforeEach(() => {
187
+ conn = mockConnection();
188
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "trajectory-content-test-"));
189
+ });
190
+
191
+ afterEach(() => {
192
+ fs.rmSync(tmpDir, { recursive: true, force: true });
193
+ });
194
+
195
+ /** Write a sessionlog-compatible flat state file: <sessionsDir>/<sessionId>.json */
196
+ function writeSessionState(
197
+ sessionsDir: string,
198
+ sessionId: string,
199
+ state: Record<string, unknown>,
200
+ transcript?: string,
201
+ ): string {
202
+ fs.mkdirSync(sessionsDir, { recursive: true });
203
+
204
+ const transcriptPath = path.join(sessionsDir, `${sessionId}.jsonl`);
205
+ if (transcript) {
206
+ fs.writeFileSync(transcriptPath, transcript);
207
+ }
208
+
209
+ fs.writeFileSync(
210
+ path.join(sessionsDir, `${sessionId}.json`),
211
+ JSON.stringify({
212
+ sessionID: sessionId,
213
+ phase: "active",
214
+ baseCommit: "abc123",
215
+ startedAt: new Date().toISOString(),
216
+ agentType: "claude",
217
+ transcriptPath: transcript ? transcriptPath : undefined,
218
+ ...state,
219
+ }),
220
+ );
221
+
222
+ return transcriptPath;
223
+ }
224
+
225
+ it("serves transcript from live session matching session ID", async () => {
226
+ const sessionsDir = path.join(tmpDir, "sessions");
227
+ const transcript = [
228
+ JSON.stringify({ type: "user", message: "Fix the bug" }),
229
+ JSON.stringify({ type: "assistant", message: "I'll look into it" }),
230
+ ].join("\n");
231
+
232
+ writeSessionState(sessionsDir, "sess-abc", {
233
+ stepCount: 3,
234
+ filesTouched: ["src/main.ts"],
235
+ firstPrompt: "Fix the bug",
236
+ }, transcript);
237
+
238
+ createTrajectoryReporter(conn, {
239
+ trajectorySyncLevel: "full",
240
+ sessionDirs: [sessionsDir],
241
+ });
242
+
243
+ const handler = conn.onNotification.mock.calls[0][1];
244
+ await handler({ request_id: "req-1", checkpoint_id: "sess-abc-step2" });
245
+
246
+ expect(conn.sendNotification).toHaveBeenCalledWith(
247
+ "trajectory/content.response",
248
+ expect.objectContaining({
249
+ request_id: "req-1",
250
+ transcript: expect.stringContaining("Fix the bug"),
251
+ prompts: "Fix the bug",
252
+ metadata: expect.objectContaining({
253
+ sessionID: "sess-abc",
254
+ source: "live",
255
+ }),
256
+ }),
257
+ );
258
+ });
259
+
260
+ it("serves transcript matching checkpoint ID in turnCheckpointIDs", async () => {
261
+ const sessionsDir = path.join(tmpDir, "sessions");
262
+ const transcript = JSON.stringify({ type: "user", message: "Deploy it" }) + "\n";
263
+
264
+ writeSessionState(sessionsDir, "sess-xyz", {
265
+ turnCheckpointIDs: ["sess-xyz-step1", "sess-xyz-step2"],
266
+ firstPrompt: "Deploy it",
267
+ }, transcript);
268
+
269
+ createTrajectoryReporter(conn, {
270
+ trajectorySyncLevel: "full",
271
+ sessionDirs: [sessionsDir],
272
+ });
273
+
274
+ const handler = conn.onNotification.mock.calls[0][1];
275
+ await handler({ request_id: "req-2", checkpoint_id: "sess-xyz-step2" });
276
+
277
+ expect(conn.sendNotification).toHaveBeenCalledWith(
278
+ "trajectory/content.response",
279
+ expect.objectContaining({
280
+ request_id: "req-2",
281
+ transcript: expect.stringContaining("Deploy it"),
282
+ }),
283
+ );
284
+ });
285
+
286
+ it("uses promptAttributions for multi-prompt sessions", async () => {
287
+ const sessionsDir = path.join(tmpDir, "sessions");
288
+ const transcript = JSON.stringify({ type: "user", message: "First" }) + "\n"
289
+ + JSON.stringify({ type: "user", message: "Second" }) + "\n";
290
+
291
+ writeSessionState(sessionsDir, "sess-multi", {
292
+ firstPrompt: "First",
293
+ promptAttributions: [
294
+ { prompt: "First", timestamp: "2026-01-01T00:00:00Z", agentLines: 10 },
295
+ { prompt: "Second", timestamp: "2026-01-01T00:01:00Z", agentLines: 5 },
296
+ ],
297
+ }, transcript);
298
+
299
+ createTrajectoryReporter(conn, {
300
+ trajectorySyncLevel: "full",
301
+ sessionDirs: [sessionsDir],
302
+ });
303
+
304
+ const handler = conn.onNotification.mock.calls[0][1];
305
+ await handler({ request_id: "req-3", checkpoint_id: "sess-multi-step1" });
306
+
307
+ expect(conn.sendNotification).toHaveBeenCalledWith(
308
+ "trajectory/content.response",
309
+ expect.objectContaining({
310
+ prompts: "First\n---\nSecond",
311
+ }),
312
+ );
313
+ });
314
+
315
+ it("returns empty response when no session found", async () => {
316
+ createTrajectoryReporter(conn, {
317
+ trajectorySyncLevel: "full",
318
+ sessionDirs: [path.join(tmpDir, "nonexistent")],
319
+ });
320
+
321
+ const handler = conn.onNotification.mock.calls[0][1];
322
+ await handler({ request_id: "req-4", checkpoint_id: "unknown-session-step1" });
323
+
324
+ expect(conn.sendNotification).toHaveBeenCalledWith(
325
+ "trajectory/content.response",
326
+ expect.objectContaining({
327
+ request_id: "req-4",
328
+ transcript: "",
329
+ metadata: expect.objectContaining({ source: "macro-agent" }),
330
+ }),
331
+ );
332
+ });
333
+
334
+ it("serves transcripts from ended sessions (content is still valid)", async () => {
335
+ const sessionsDir = path.join(tmpDir, "sessions");
336
+ const transcript = JSON.stringify({ type: "user", message: "Old session" }) + "\n";
337
+
338
+ writeSessionState(sessionsDir, "sess-ended", {
339
+ phase: "ended",
340
+ }, transcript);
341
+
342
+ createTrajectoryReporter(conn, {
343
+ trajectorySyncLevel: "full",
344
+ sessionDirs: [sessionsDir],
345
+ });
346
+
347
+ const handler = conn.onNotification.mock.calls[0][1];
348
+ await handler({ request_id: "req-5", checkpoint_id: "sess-ended-step1" });
349
+
350
+ expect(conn.sendNotification).toHaveBeenCalledWith(
351
+ "trajectory/content.response",
352
+ expect.objectContaining({
353
+ request_id: "req-5",
354
+ transcript: expect.stringContaining("Old session"),
355
+ }),
356
+ );
357
+ });
358
+
359
+ it("skips sessions with missing transcript path", async () => {
360
+ const sessionsDir = path.join(tmpDir, "sessions");
361
+
362
+ // Write state without transcript file
363
+ writeSessionState(sessionsDir, "sess-no-file", {});
364
+
365
+ createTrajectoryReporter(conn, {
366
+ trajectorySyncLevel: "full",
367
+ sessionDirs: [sessionsDir],
368
+ });
369
+
370
+ const handler = conn.onNotification.mock.calls[0][1];
371
+ await handler({ request_id: "req-6", checkpoint_id: "sess-no-file-step1" });
372
+
373
+ const call = conn.sendNotification.mock.calls[0];
374
+ expect(call[1]).toHaveProperty("transcript", "");
375
+ });
376
+
377
+ it("sends error response when content handler throws", async () => {
378
+ conn.sendNotification
379
+ .mockRejectedValueOnce(new Error("network"))
380
+ .mockResolvedValueOnce(undefined);
381
+
382
+ createTrajectoryReporter(conn, {
383
+ trajectorySyncLevel: "full",
384
+ sessionDirs: [path.join(tmpDir, "nonexistent")],
385
+ });
386
+
387
+ const handler = conn.onNotification.mock.calls[0][1];
388
+ await handler({ request_id: "req-7", checkpoint_id: "any" });
389
+
390
+ // First call fails, second call sends error response
391
+ expect(conn.sendNotification).toHaveBeenCalledTimes(2);
392
+ expect(conn.sendNotification).toHaveBeenLastCalledWith(
393
+ "trajectory/content.response",
394
+ expect.objectContaining({
395
+ request_id: "req-7",
396
+ error: "Content serving failed",
397
+ }),
398
+ );
399
+ });
400
+
401
+ it("searches multiple session directories", async () => {
402
+ const dir1 = path.join(tmpDir, "dir1");
403
+ const dir2 = path.join(tmpDir, "dir2");
404
+ const transcript = JSON.stringify({ type: "user", message: "Found in dir2" }) + "\n";
405
+
406
+ // Only dir2 has the session
407
+ fs.mkdirSync(dir1, { recursive: true });
408
+ writeSessionState(dir2, "sess-multi-dir", {}, transcript);
409
+
410
+ createTrajectoryReporter(conn, {
411
+ trajectorySyncLevel: "full",
412
+ sessionDirs: [dir1, dir2],
413
+ });
414
+
415
+ const handler = conn.onNotification.mock.calls[0][1];
416
+ await handler({ request_id: "req-8", checkpoint_id: "sess-multi-dir-step1" });
417
+
418
+ expect(conn.sendNotification).toHaveBeenCalledWith(
419
+ "trajectory/content.response",
420
+ expect.objectContaining({
421
+ transcript: expect.stringContaining("Found in dir2"),
422
+ }),
423
+ );
424
+ });
425
+ });
@@ -1,21 +1,26 @@
1
1
  /**
2
2
  * Coordination Handler — dispatches inbound coordination messages from the MAP hub.
3
3
  *
4
- * Handles x-openhive/* JSON-RPC notifications for task assignment, status updates,
5
- * context sharing, messaging, and workspace execution.
4
+ * Task operations use generic MAP scope messages (task.created, task.assigned,
5
+ * task.status) matching the wire format used by cc-swarm and opentasks.
6
+ * Context sharing and messaging use agent-inbox (not MAP).
7
+ * Workspace execution uses x-workspace/* notifications.
6
8
  *
7
9
  * @module map/coordination-handler
8
10
  */
9
11
 
10
- import type { AgentManager } from "../agent/agent-manager.js";
12
+ import { WORKSPACE_METHODS, WORKSPACE_METHODS_LEGACY } from "agent-workspace";
11
13
  import type { InboxAdapter, TasksAdapter } from "../adapters/types.js";
12
- import type {
13
- CoordinationTaskAssign,
14
- CoordinationTaskStatus,
15
- CoordinationContextShare,
16
- CoordinationMessage,
17
- TrajectoryReporter,
18
- } from "./types.js";
14
+
15
+ /** MAP Message shape (subset of @multi-agent-protocol/sdk Message) */
16
+ export interface MAPMessage {
17
+ id: string;
18
+ from: string;
19
+ to: string | { scope: string };
20
+ timestamp: string;
21
+ payload?: Record<string, unknown>;
22
+ meta?: Record<string, unknown>;
23
+ }
19
24
 
20
25
  export interface CoordinationConnection {
21
26
  onNotification(
@@ -27,14 +32,15 @@ export interface CoordinationConnection {
27
32
  handler: (params: unknown) => void | Promise<void>,
28
33
  ): void;
29
34
  sendNotification(method: string, params: unknown): Promise<void>;
35
+ onMessage(handler: (message: MAPMessage) => void | Promise<void>): void;
36
+ offMessage(handler: (message: MAPMessage) => void | Promise<void>): void;
30
37
  }
31
38
 
32
39
  export interface CoordinationDeps {
33
40
  connection: CoordinationConnection;
34
- agentManager: AgentManager;
41
+ /** Used by task handlers to notify assignees */
35
42
  inboxAdapter: InboxAdapter;
36
43
  tasksAdapter: TasksAdapter;
37
- trajectoryReporter?: TrajectoryReporter;
38
44
  /** Workspace handler from cognitive module (if available) */
39
45
  workspaceHandler?: {
40
46
  handleWorkspaceExecute(params: unknown): Promise<void>;
@@ -42,164 +48,138 @@ export interface CoordinationDeps {
42
48
  };
43
49
  }
44
50
 
45
- /** Coordination method constants */
51
+ /** Notification method constants (workspace only — task/context/message use MAP messages) */
46
52
  const METHODS = {
47
- TASK_ASSIGN: "x-openhive/task.assign",
48
- TASK_STATUS: "x-openhive/task.status",
49
- CONTEXT_SHARE: "x-openhive/context.share",
50
- MESSAGE_SEND: "x-openhive/message.send",
51
- WORKSPACE_EXECUTE: "x-openhive/learning.workspace.execute",
53
+ WORKSPACE_EXECUTE: WORKSPACE_METHODS.EXECUTE,
54
+ WORKSPACE_EXECUTE_LEGACY: WORKSPACE_METHODS_LEGACY.EXECUTE,
52
55
  } as const;
53
56
 
54
57
  /**
55
- * Register coordination notification handlers on the MAP connection.
58
+ * Register coordination handlers on the MAP connection.
59
+ *
60
+ * Task operations are handled via MAP scope messages (onMessage).
61
+ * Context sharing and messaging are handled by agent-inbox (not here).
62
+ * Workspace execution uses x-workspace/* notifications.
56
63
  * Returns a cleanup function that removes all handlers.
57
64
  */
58
65
  export function setupCoordinationHandlers(
59
66
  deps: CoordinationDeps,
60
67
  ): () => void {
61
- const { connection, agentManager, inboxAdapter, tasksAdapter } = deps;
62
- const handlers: Array<{ method: string; handler: (params: unknown) => void | Promise<void> }> = [];
68
+ const { connection, inboxAdapter, tasksAdapter } = deps;
69
+ const notificationHandlers: Array<{ method: string; handler: (params: unknown) => void | Promise<void> }> = [];
63
70
 
64
71
  const register = (
65
72
  method: string,
66
73
  handler: (params: unknown) => void | Promise<void>,
67
74
  ): void => {
68
75
  connection.onNotification(method, handler);
69
- handlers.push({ method, handler });
76
+ notificationHandlers.push({ method, handler });
70
77
  };
71
78
 
72
- // --- Task Assignment ---
73
- register(METHODS.TASK_ASSIGN, async (params: unknown) => {
74
- const p = params as CoordinationTaskAssign;
75
- if (!p?.title) return;
79
+ // =========================================================================
80
+ // Task operations generic MAP scope messages
81
+ // Wire format matches cc-swarm / opentasks MAP Event Bridge:
82
+ // { type: "task.created", task: { id, title, status, assignee } }
83
+ // { type: "task.assigned", taskId, assignee }
84
+ // { type: "task.status", taskId, previous, current }
85
+ // =========================================================================
86
+
87
+ const messageHandler = async (message: MAPMessage): Promise<void> => {
88
+ const payload = message.payload;
89
+ if (!payload || typeof payload.type !== "string") return;
90
+
91
+ // Skip messages we originated (echo prevention)
92
+ const origin = payload._origin as string | undefined;
93
+ if (origin === "macro-agent") return;
76
94
 
77
95
  try {
78
- // Create task in opentasks
79
- const taskId = await tasksAdapter.createTask({
80
- title: p.title,
81
- content: p.description,
82
- assignee: p.assigned_to,
83
- priority: p.priority === "critical" ? 1 : p.priority === "high" ? 2 : p.priority === "low" ? 4 : 3,
84
- });
85
-
86
- // Optionally spawn an agent to work on the task
87
- if (p.assigned_to) {
88
- try {
89
- await inboxAdapter.send("system", p.assigned_to, {
90
- type: "event",
91
- event: "TASK_ASSIGNED",
92
- data: { taskId, title: p.title, description: p.description },
96
+ switch (payload.type) {
97
+ case "task.created": {
98
+ const task = payload.task as
99
+ | { id?: string; title?: string; status?: string; assignee?: string }
100
+ | undefined;
101
+ if (!task?.title) return;
102
+
103
+ const taskId = await tasksAdapter.createTask({
104
+ title: task.title,
105
+ content: (task as Record<string, unknown>).description as string | undefined,
106
+ assignee: task.assignee,
93
107
  });
94
- } catch {
95
- // Agent may not exist locally — that's fine
108
+
109
+ if (task.assignee) {
110
+ await inboxAdapter
111
+ .send("system", task.assignee, {
112
+ type: "event",
113
+ event: "TASK_ASSIGNED",
114
+ data: { taskId, title: task.title },
115
+ })
116
+ .catch(() => {});
117
+ }
118
+ break;
96
119
  }
97
- }
98
- } catch (err) {
99
- console.warn(
100
- `[map-sidecar] Failed to handle task.assign: ${(err as Error).message}`,
101
- );
102
- }
103
- });
104
120
 
105
- // --- Task Status ---
106
- register(METHODS.TASK_STATUS, async (params: unknown) => {
107
- const p = params as CoordinationTaskStatus;
108
- if (!p?.task_id || !p?.status) return;
121
+ case "task.assigned": {
122
+ const taskId = payload.taskId as string | undefined;
123
+ const assignee = payload.assignee as string | undefined;
124
+ if (!taskId || !assignee) return;
125
+
126
+ await tasksAdapter.assignTask(taskId, assignee);
127
+
128
+ await inboxAdapter
129
+ .send("system", assignee, {
130
+ type: "event",
131
+ event: "TASK_ASSIGNED",
132
+ data: { taskId },
133
+ })
134
+ .catch(() => {});
135
+ break;
136
+ }
109
137
 
110
- try {
111
- const actionMap: Record<string, string> = {
112
- in_progress: "start",
113
- completed: "complete",
114
- closed: "complete",
115
- failed: "fail",
116
- blocked: "block",
117
- open: "reopen",
118
- };
119
- const action = actionMap[p.status];
120
- if (action) {
121
- await tasksAdapter.transitionTask(p.task_id, action as any);
122
- }
123
- } catch (err) {
124
- console.warn(
125
- `[map-sidecar] Failed to handle task.status: ${(err as Error).message}`,
126
- );
127
- }
128
- });
138
+ case "task.status": {
139
+ const taskId = payload.taskId as string | undefined;
140
+ const current = payload.current as string | undefined;
141
+ if (!taskId || !current) return;
142
+
143
+ const actionMap: Record<string, string> = {
144
+ in_progress: "start",
145
+ completed: "complete",
146
+ closed: "complete",
147
+ failed: "fail",
148
+ blocked: "block",
149
+ open: "reopen",
150
+ };
151
+ const action = actionMap[current];
152
+ if (action) {
153
+ await tasksAdapter.transitionTask(taskId, action as any);
154
+ }
155
+ break;
156
+ }
129
157
 
130
- // --- Context Share ---
131
- register(METHODS.CONTEXT_SHARE, async (params: unknown) => {
132
- const p = params as CoordinationContextShare;
133
- if (!p?.context_type || !p?.data) return;
158
+ // Context sharing and messaging are handled by agent-inbox directly
159
+ // (not through MAP scope messages). See InboxAdapter for broadcast
160
+ // scope delivery and agent-to-agent messaging.
134
161
 
135
- try {
136
- // Deliver context to all running agents via inbox
137
- const agents = agentManager.list()
138
- .filter((a: any) => a.state === "running");
139
- for (const agent of agents) {
140
- await inboxAdapter
141
- .send("system", agent.id, {
142
- type: "event",
143
- event: "CONTEXT_SHARED",
144
- data: {
145
- context_type: p.context_type,
146
- data: p.data,
147
- source: p.source_swarm_id,
148
- },
149
- })
150
- .catch(() => {});
162
+ // Ignore other message types (e.g., task.completed is informational)
163
+ default:
164
+ break;
151
165
  }
152
166
  } catch (err) {
153
167
  console.warn(
154
- `[map-sidecar] Failed to handle context.share: ${(err as Error).message}`,
168
+ `[map-sidecar] Failed to handle ${payload.type}: ${(err as Error).message}`,
155
169
  );
156
170
  }
157
- });
171
+ };
158
172
 
159
- // --- Message Send ---
160
- register(METHODS.MESSAGE_SEND, async (params: unknown) => {
161
- const p = params as CoordinationMessage;
162
- if (!p?.content) return;
173
+ connection.onMessage(messageHandler);
163
174
 
164
- try {
165
- const agents = agentManager.list()
166
- .filter((a: any) => a.state === "running");
167
- if (agents.length === 0) return;
168
-
169
- // Route to the best target:
170
- // 1. If to_swarm_id matches a local agent ID, send directly
171
- // 2. If metadata has a target_agent hint, use it
172
- // 3. Otherwise, send to the coordinator/head manager (parentless agent)
173
- // 4. Fallback: first running agent
174
- const targetId = p.to_swarm_id;
175
- const directTarget = targetId
176
- ? agents.find((a: any) => a.id === targetId)
177
- : undefined;
178
- const coordinator = agents.find((a: any) => !a.parent);
179
- const target = directTarget ?? coordinator ?? agents[0];
180
-
181
- await inboxAdapter.send("system", target.id, {
182
- type: "event",
183
- event: "EXTERNAL_MESSAGE",
184
- data: {
185
- from: p.from_swarm_id,
186
- content_type: p.content_type,
187
- content: p.content,
188
- reply_to: p.reply_to,
189
- metadata: p.metadata,
190
- },
191
- });
192
- } catch (err) {
193
- console.warn(
194
- `[map-sidecar] Failed to handle message.send: ${(err as Error).message}`,
195
- );
196
- }
197
- });
175
+ // =========================================================================
176
+ // Workspace JSON-RPC notifications (x-workspace protocol)
177
+ // =========================================================================
198
178
 
199
179
  // --- Workspace Execute (delegate to cognitive module) ---
200
180
  if (deps.workspaceHandler) {
201
181
  const wh = deps.workspaceHandler;
202
- register(METHODS.WORKSPACE_EXECUTE, async (params: unknown) => {
182
+ const workspaceHandler = async (params: unknown) => {
203
183
  try {
204
184
  await wh.handleWorkspaceExecute(params);
205
185
  } catch (err) {
@@ -207,14 +187,17 @@ export function setupCoordinationHandlers(
207
187
  `[map-sidecar] Failed to handle workspace.execute: ${(err as Error).message}`,
208
188
  );
209
189
  }
210
- });
190
+ };
191
+ register(METHODS.WORKSPACE_EXECUTE, workspaceHandler);
192
+ register(METHODS.WORKSPACE_EXECUTE_LEGACY, workspaceHandler);
211
193
  }
212
194
 
213
195
  // Return cleanup function
214
196
  return () => {
215
- for (const { method, handler } of handlers) {
197
+ connection.offMessage(messageHandler);
198
+ for (const { method, handler } of notificationHandlers) {
216
199
  connection.offNotification(method, handler);
217
200
  }
218
- handlers.length = 0;
201
+ notificationHandlers.length = 0;
219
202
  };
220
203
  }
@@ -253,14 +253,31 @@ export function createMAPSidecar(
253
253
  const { createTrajectoryReporter } = await import("./trajectory-reporter.js");
254
254
  trajectoryReporter = createTrajectoryReporter(connection, config);
255
255
 
256
- // 4. Coordination Handler
256
+ // 4. Workspace Handler (for x-workspace/task.execute)
257
+ const { MacroAgentBackend } = await import("../cognitive/macro-agent-backend.js");
258
+ const { handleWorkspaceExecute, isWorkspaceExecuteMessage } = await import("../cognitive/workspace-handler.js");
259
+ const workspaceBackend = new MacroAgentBackend(agentManager, {});
260
+ const sendToHub = (msg: { method?: string; params?: unknown }) => {
261
+ if (msg.method && msg.params) {
262
+ connection.sendNotification(msg.method, msg.params as Record<string, unknown>);
263
+ }
264
+ };
265
+ const workspaceHandler = {
266
+ handleWorkspaceExecute: (params: unknown) =>
267
+ handleWorkspaceExecute(
268
+ { backend: workspaceBackend, sendToHub },
269
+ params as import("agent-workspace").WorkspaceExecuteParams,
270
+ ),
271
+ isWorkspaceExecuteMessage,
272
+ };
273
+
274
+ // 5. Coordination Handler
257
275
  const { setupCoordinationHandlers } = await import("./coordination-handler.js");
258
276
  coordinationCleanup = setupCoordinationHandlers({
259
277
  connection,
260
- agentManager,
261
278
  inboxAdapter,
262
279
  tasksAdapter,
263
- trajectoryReporter,
280
+ workspaceHandler,
264
281
  });
265
282
  }
266
283