macro-agent 0.0.14 → 0.0.16

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 (154) hide show
  1. package/.claude/settings.local.json +59 -0
  2. package/dist/acp/index.d.ts +1 -1
  3. package/dist/acp/index.d.ts.map +1 -1
  4. package/dist/acp/index.js.map +1 -1
  5. package/dist/acp/macro-agent.d.ts +21 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +182 -0
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +31 -2
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts.map +1 -1
  13. package/dist/agent/agent-manager.js +10 -4
  14. package/dist/agent/agent-manager.js.map +1 -1
  15. package/dist/cli/acp.d.ts +6 -0
  16. package/dist/cli/acp.d.ts.map +1 -1
  17. package/dist/cli/acp.js +16 -2
  18. package/dist/cli/acp.js.map +1 -1
  19. package/dist/map/adapter/acp-over-map.d.ts +5 -0
  20. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  21. package/dist/map/adapter/acp-over-map.js +47 -4
  22. package/dist/map/adapter/acp-over-map.js.map +1 -1
  23. package/dist/map/utils/address-translation.d.ts +99 -0
  24. package/dist/map/utils/address-translation.d.ts.map +1 -0
  25. package/dist/map/utils/address-translation.js +285 -0
  26. package/dist/map/utils/address-translation.js.map +1 -0
  27. package/dist/map/utils/index.d.ts +7 -0
  28. package/dist/map/utils/index.d.ts.map +1 -0
  29. package/dist/map/utils/index.js +7 -0
  30. package/dist/map/utils/index.js.map +1 -0
  31. package/dist/store/event-store.js +9 -2
  32. package/dist/store/event-store.js.map +1 -1
  33. package/dist/store/types/agents.d.ts +2 -0
  34. package/dist/store/types/agents.d.ts.map +1 -1
  35. package/package.json +4 -4
  36. package/references/acp-factory-ref/CHANGELOG.md +33 -0
  37. package/references/acp-factory-ref/LICENSE +21 -0
  38. package/references/acp-factory-ref/README.md +341 -0
  39. package/references/acp-factory-ref/package-lock.json +3102 -0
  40. package/references/acp-factory-ref/package.json +96 -0
  41. package/references/acp-factory-ref/python/CHANGELOG.md +33 -0
  42. package/references/acp-factory-ref/python/LICENSE +21 -0
  43. package/references/acp-factory-ref/python/Makefile +57 -0
  44. package/references/acp-factory-ref/python/README.md +253 -0
  45. package/references/acp-factory-ref/python/pyproject.toml +73 -0
  46. package/references/acp-factory-ref/python/tests/__init__.py +0 -0
  47. package/references/acp-factory-ref/python/tests/e2e/__init__.py +1 -0
  48. package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +349 -0
  49. package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +165 -0
  50. package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +296 -0
  51. package/references/acp-factory-ref/python/tests/test_client_handler.py +543 -0
  52. package/references/acp-factory-ref/python/tests/test_pushable.py +199 -0
  53. package/references/claude-code-acp/.github/workflows/ci.yml +45 -0
  54. package/references/claude-code-acp/.github/workflows/publish.yml +34 -0
  55. package/references/claude-code-acp/.prettierrc.json +4 -0
  56. package/references/claude-code-acp/CHANGELOG.md +249 -0
  57. package/references/claude-code-acp/LICENSE +222 -0
  58. package/references/claude-code-acp/README.md +53 -0
  59. package/references/claude-code-acp/docs/RELEASES.md +24 -0
  60. package/references/claude-code-acp/eslint.config.js +48 -0
  61. package/references/claude-code-acp/package-lock.json +4570 -0
  62. package/references/claude-code-acp/package.json +88 -0
  63. package/references/claude-code-acp/scripts/release.sh +119 -0
  64. package/references/claude-code-acp/src/acp-agent.ts +2079 -0
  65. package/references/claude-code-acp/src/index.ts +26 -0
  66. package/references/claude-code-acp/src/lib.ts +38 -0
  67. package/references/claude-code-acp/src/mcp-server.ts +911 -0
  68. package/references/claude-code-acp/src/settings.ts +522 -0
  69. package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +5 -0
  70. package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +6 -0
  71. package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +479 -0
  72. package/references/claude-code-acp/src/tests/acp-agent.test.ts +1502 -0
  73. package/references/claude-code-acp/src/tests/extract-lines.test.ts +103 -0
  74. package/references/claude-code-acp/src/tests/fork-session.test.ts +335 -0
  75. package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +334 -0
  76. package/references/claude-code-acp/src/tests/settings.test.ts +617 -0
  77. package/references/claude-code-acp/src/tests/skills-options.test.ts +187 -0
  78. package/references/claude-code-acp/src/tests/tools.test.ts +318 -0
  79. package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +558 -0
  80. package/references/claude-code-acp/src/tools.ts +819 -0
  81. package/references/claude-code-acp/src/utils.ts +171 -0
  82. package/references/claude-code-acp/tsconfig.json +18 -0
  83. package/references/claude-code-acp/vitest.config.ts +19 -0
  84. package/references/multi-agent-protocol/.sudocode/issues.jsonl +111 -0
  85. package/references/multi-agent-protocol/.sudocode/specs.jsonl +13 -0
  86. package/references/multi-agent-protocol/LICENSE +21 -0
  87. package/references/multi-agent-protocol/README.md +113 -0
  88. package/references/multi-agent-protocol/docs/00-design-specification.md +496 -0
  89. package/references/multi-agent-protocol/docs/01-open-questions.md +1050 -0
  90. package/references/multi-agent-protocol/docs/02-wire-protocol.md +296 -0
  91. package/references/multi-agent-protocol/docs/03-streaming-semantics.md +252 -0
  92. package/references/multi-agent-protocol/docs/04-error-handling.md +231 -0
  93. package/references/multi-agent-protocol/docs/05-connection-model.md +244 -0
  94. package/references/multi-agent-protocol/docs/06-visibility-permissions.md +243 -0
  95. package/references/multi-agent-protocol/docs/07-federation.md +259 -0
  96. package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +253 -0
  97. package/references/multi-agent-protocol/docs/09-authentication.md +680 -0
  98. package/references/multi-agent-protocol/docs/10-mail-protocol.md +553 -0
  99. package/references/multi-agent-protocol/docs/agent-iam-integration.md +877 -0
  100. package/references/multi-agent-protocol/docs/agentic-mesh-integration-draft.md +459 -0
  101. package/references/multi-agent-protocol/docs/git-transport-draft.md +251 -0
  102. package/references/multi-agent-protocol/docs-site/Gemfile +22 -0
  103. package/references/multi-agent-protocol/docs-site/README.md +82 -0
  104. package/references/multi-agent-protocol/docs-site/_config.yml +91 -0
  105. package/references/multi-agent-protocol/docs-site/_includes/head_custom.html +20 -0
  106. package/references/multi-agent-protocol/docs-site/_sass/color_schemes/map.scss +42 -0
  107. package/references/multi-agent-protocol/docs-site/_sass/custom/custom.scss +34 -0
  108. package/references/multi-agent-protocol/docs-site/examples/full-integration.md +510 -0
  109. package/references/multi-agent-protocol/docs-site/examples/index.md +138 -0
  110. package/references/multi-agent-protocol/docs-site/examples/simple-chat.md +282 -0
  111. package/references/multi-agent-protocol/docs-site/examples/task-queue.md +399 -0
  112. package/references/multi-agent-protocol/docs-site/getting-started/index.md +98 -0
  113. package/references/multi-agent-protocol/docs-site/getting-started/installation.md +219 -0
  114. package/references/multi-agent-protocol/docs-site/getting-started/overview.md +172 -0
  115. package/references/multi-agent-protocol/docs-site/getting-started/quickstart.md +237 -0
  116. package/references/multi-agent-protocol/docs-site/index.md +136 -0
  117. package/references/multi-agent-protocol/docs-site/protocol/authentication.md +391 -0
  118. package/references/multi-agent-protocol/docs-site/protocol/connection-model.md +376 -0
  119. package/references/multi-agent-protocol/docs-site/protocol/design.md +284 -0
  120. package/references/multi-agent-protocol/docs-site/protocol/error-handling.md +312 -0
  121. package/references/multi-agent-protocol/docs-site/protocol/federation.md +449 -0
  122. package/references/multi-agent-protocol/docs-site/protocol/index.md +129 -0
  123. package/references/multi-agent-protocol/docs-site/protocol/permissions.md +398 -0
  124. package/references/multi-agent-protocol/docs-site/protocol/streaming.md +353 -0
  125. package/references/multi-agent-protocol/docs-site/protocol/wire-protocol.md +369 -0
  126. package/references/multi-agent-protocol/docs-site/sdk/api/agent.md +357 -0
  127. package/references/multi-agent-protocol/docs-site/sdk/api/client.md +380 -0
  128. package/references/multi-agent-protocol/docs-site/sdk/api/index.md +62 -0
  129. package/references/multi-agent-protocol/docs-site/sdk/api/server.md +453 -0
  130. package/references/multi-agent-protocol/docs-site/sdk/api/types.md +468 -0
  131. package/references/multi-agent-protocol/docs-site/sdk/guides/agent.md +375 -0
  132. package/references/multi-agent-protocol/docs-site/sdk/guides/authentication.md +405 -0
  133. package/references/multi-agent-protocol/docs-site/sdk/guides/client.md +352 -0
  134. package/references/multi-agent-protocol/docs-site/sdk/guides/index.md +89 -0
  135. package/references/multi-agent-protocol/docs-site/sdk/guides/server.md +360 -0
  136. package/references/multi-agent-protocol/docs-site/sdk/guides/testing.md +446 -0
  137. package/references/multi-agent-protocol/docs-site/sdk/guides/transports.md +363 -0
  138. package/references/multi-agent-protocol/docs-site/sdk/index.md +206 -0
  139. package/references/multi-agent-protocol/package-lock.json +3886 -0
  140. package/references/multi-agent-protocol/package.json +56 -0
  141. package/references/multi-agent-protocol/schema/meta.json +467 -0
  142. package/references/multi-agent-protocol/schema/schema.json +2558 -0
  143. package/src/acp/__tests__/history.test.ts +526 -0
  144. package/src/acp/__tests__/integration.test.ts +2 -1
  145. package/src/acp/index.ts +4 -0
  146. package/src/acp/macro-agent.ts +329 -85
  147. package/src/acp/types.ts +39 -2
  148. package/src/agent/__tests__/agent-manager.test.ts +67 -1
  149. package/src/agent/agent-manager.ts +10 -4
  150. package/src/cli/__tests__/stable-instance-id.test.ts +57 -0
  151. package/src/cli/acp.ts +17 -2
  152. package/src/map/adapter/acp-over-map.ts +57 -2
  153. package/src/store/event-store.ts +10 -3
  154. package/src/store/types/agents.ts +2 -0
@@ -63,6 +63,9 @@ import type {
63
63
  CancelPermissionResponse,
64
64
  ResumeAgentRequest,
65
65
  ResumeAgentResponse,
66
+ GetHistoryRequest,
67
+ GetHistoryResponse,
68
+ HistoryTurn,
66
69
  } from "./types.js";
67
70
  import { ACPError } from "./types.js";
68
71
  import type { PeerManager } from "../peer/peer-manager.js";
@@ -95,6 +98,7 @@ const SUPPORTED_EXTENSIONS: ACPExtensionMethod[] = [
95
98
  "_macro/respondToPermission",
96
99
  "_macro/cancelPermission",
97
100
  "_macro/resume",
101
+ "_macro/getHistory",
98
102
  ];
99
103
 
100
104
  // ─────────────────────────────────────────────────────────────────
@@ -150,6 +154,12 @@ export class MacroAgent implements Agent {
150
154
  private cancellationControllers: Map<ACPSessionId, AbortController> =
151
155
  new Map();
152
156
 
157
+ /** Accumulates assistant response parts during prompt streaming for history persistence */
158
+ private promptBuffers: Map<
159
+ ACPSessionId,
160
+ { textChunks: string[]; toolCalls: Record<string, unknown>[] }
161
+ > = new Map();
162
+
153
163
  constructor(connection: AgentSideConnection, config: MacroAgentConfig) {
154
164
  this.connection = connection;
155
165
  this.agentManager = config.agentManager;
@@ -165,7 +175,7 @@ export class MacroAgent implements Agent {
165
175
  const recovered = this.sessionMapper.recoverFromStore();
166
176
  if (recovered > 0) {
167
177
  console.log(
168
- `[MacroAgent] Recovered ${recovered} session(s) from EventStore`
178
+ `[MacroAgent] Recovered ${recovered} session(s) from EventStore`,
169
179
  );
170
180
  }
171
181
  }
@@ -231,6 +241,12 @@ export class MacroAgent implements Agent {
231
241
  // Create abort controller for cancellation
232
242
  this.cancellationControllers.set(acpSessionId, new AbortController());
233
243
 
244
+ // Create a conversation in EventStore for history tracking
245
+ this.ensureConversation(acpSessionId, spawned.id);
246
+
247
+ // Emit session_info_update so client has title/timestamps
248
+ await this.emitSessionInfo(acpSessionId);
249
+
234
250
  return {
235
251
  sessionId: acpSessionId,
236
252
  };
@@ -255,7 +271,7 @@ export class MacroAgent implements Agent {
255
271
  }
256
272
  acpSessionId = agent.session_id;
257
273
  console.log(
258
- `[MacroAgent] loadSession: Resolved agentId ${metaAgentId} to session ${acpSessionId}`
274
+ `[MacroAgent] loadSession: Resolved agentId ${metaAgentId} to session ${acpSessionId}`,
259
275
  );
260
276
  }
261
277
 
@@ -267,32 +283,36 @@ export class MacroAgent implements Agent {
267
283
  // Check if the agent already has an active session
268
284
  if (this.agentManager.hasActiveSession(existing.id)) {
269
285
  console.log(
270
- `[MacroAgent] loadSession: Agent ${existing.id} already has active session, reusing`
286
+ `[MacroAgent] loadSession: Agent ${existing.id} already has active session, reusing`,
271
287
  );
272
288
  // Reuse the existing active session - just update mappings
273
289
  this.sessionMapper.createMapping(acpSessionId, existing.id);
274
290
  if (!this.cancellationControllers.has(acpSessionId)) {
275
291
  this.cancellationControllers.set(acpSessionId, new AbortController());
276
292
  }
293
+ this.ensureConversation(acpSessionId, existing.id);
294
+ await this.emitSessionInfo(acpSessionId);
277
295
  return {};
278
296
  }
279
297
 
280
298
  // Agent exists but no active session - resume it
281
299
  console.log(
282
- `[MacroAgent] loadSession: Resuming stopped agent ${existing.id}`
300
+ `[MacroAgent] loadSession: Resuming stopped agent ${existing.id}`,
283
301
  );
284
302
  const spawned = await this.agentManager.resume(existing.id);
285
303
 
286
304
  // Create session mapping
287
305
  this.sessionMapper.createMapping(acpSessionId, spawned.id);
288
306
  this.cancellationControllers.set(acpSessionId, new AbortController());
307
+ this.ensureConversation(acpSessionId, spawned.id);
308
+ await this.emitSessionInfo(acpSessionId);
289
309
 
290
310
  return {};
291
311
  }
292
312
 
293
313
  // No existing agent found - try to get or create with the specific session ID
294
314
  console.log(
295
- `[MacroAgent] loadSession: No existing agent for session ${acpSessionId}, creating new`
315
+ `[MacroAgent] loadSession: No existing agent for session ${acpSessionId}, creating new`,
296
316
  );
297
317
  const spawned = await this.agentManager.getOrCreateHeadManager({
298
318
  cwd,
@@ -302,6 +322,8 @@ export class MacroAgent implements Agent {
302
322
  // Create session mapping
303
323
  this.sessionMapper.createMapping(acpSessionId, spawned.id);
304
324
  this.cancellationControllers.set(acpSessionId, new AbortController());
325
+ this.ensureConversation(acpSessionId, spawned.id);
326
+ await this.emitSessionInfo(acpSessionId);
305
327
 
306
328
  return {};
307
329
  }
@@ -310,7 +332,7 @@ export class MacroAgent implements Agent {
310
332
  * Authenticate - macro-agent doesn't require authentication
311
333
  */
312
334
  async authenticate(
313
- _params: AuthenticateRequest
335
+ _params: AuthenticateRequest,
314
336
  ): Promise<AuthenticateResponse> {
315
337
  // No authentication required
316
338
  return {};
@@ -338,11 +360,14 @@ export class MacroAgent implements Agent {
338
360
  // Mark session as processing (for health monitoring)
339
361
  this.sessionMapper.setProcessing(acpSessionId, true);
340
362
 
363
+ // Initialize prompt buffer for history accumulation
364
+ this.promptBuffers.set(acpSessionId, { textChunks: [], toolCalls: [] });
365
+
341
366
  try {
342
367
  // Stream responses from the agent
343
368
  for await (const update of this.agentManager.prompt(
344
369
  agentId,
345
- messageContent
370
+ messageContent,
346
371
  )) {
347
372
  // Check for cancellation
348
373
  if (abortController.signal.aborted) {
@@ -355,6 +380,12 @@ export class MacroAgent implements Agent {
355
380
  await this.forwardSessionUpdate(acpSessionId, update);
356
381
  }
357
382
 
383
+ // Persist conversation turns for history
384
+ this.recordPromptTurns(acpSessionId, agentId, messageContent);
385
+
386
+ // Emit updated session info after prompt completes
387
+ await this.emitSessionInfo(acpSessionId);
388
+
358
389
  return {
359
390
  stopReason: "end_turn",
360
391
  };
@@ -408,7 +439,7 @@ export class MacroAgent implements Agent {
408
439
  // Agent may already be stopped - log but don't throw
409
440
  console.warn(
410
441
  `[MacroAgent] Error terminating agent ${agentId}:`,
411
- error instanceof Error ? error.message : String(error)
442
+ error instanceof Error ? error.message : String(error),
412
443
  );
413
444
  }
414
445
 
@@ -426,97 +457,104 @@ export class MacroAgent implements Agent {
426
457
  */
427
458
  async extMethod(
428
459
  method: string,
429
- params: Record<string, unknown>
460
+ params: Record<string, unknown>,
430
461
  ): Promise<Record<string, unknown>> {
431
462
  // Method may come with or without underscore prefix depending on caller
432
463
  // SDK calls with full name (e.g., "_macro/spawnAgent"), direct calls may omit it
433
- const fullMethod = (method.startsWith("_") ? method : `_${method}`) as ACPExtensionMethod;
464
+ const fullMethod = (
465
+ method.startsWith("_") ? method : `_${method}`
466
+ ) as ACPExtensionMethod;
434
467
 
435
468
  switch (fullMethod) {
436
469
  case "_macro/spawnAgent":
437
470
  return this.handleSpawnAgent(
438
- params as unknown as SpawnAgentRequest
471
+ params as unknown as SpawnAgentRequest,
439
472
  ) as unknown as Record<string, unknown>;
440
473
 
441
474
  case "_macro/getHierarchy":
442
475
  return this.handleGetHierarchy(
443
- params as unknown as GetHierarchyRequest
476
+ params as unknown as GetHierarchyRequest,
444
477
  ) as unknown as Record<string, unknown>;
445
478
 
446
479
  case "_macro/getTask":
447
480
  return this.handleGetTask(
448
- params as unknown as GetTaskRequest
481
+ params as unknown as GetTaskRequest,
449
482
  ) as unknown as Record<string, unknown>;
450
483
 
451
484
  case "_macro/mountAgent":
452
485
  return this.handleMountAgent(
453
- params as unknown as MountAgentRequest
486
+ params as unknown as MountAgentRequest,
454
487
  ) as unknown as Record<string, unknown>;
455
488
 
456
489
  case "_macro/forkAgent":
457
490
  return this.handleForkAgent(
458
- params as unknown as ForkAgentRequest
491
+ params as unknown as ForkAgentRequest,
459
492
  ) as unknown as Record<string, unknown>;
460
493
 
461
494
  case "_macro/sendPeerMessage":
462
495
  return this.handleSendPeerMessage(
463
- params as unknown as SendPeerMessageACPRequest
496
+ params as unknown as SendPeerMessageACPRequest,
464
497
  ) as unknown as Record<string, unknown>;
465
498
 
466
499
  case "_macro/sendPeerRequest":
467
500
  return this.handleSendPeerRequest(
468
- params as unknown as SendPeerRequestACPRequest
501
+ params as unknown as SendPeerRequestACPRequest,
469
502
  ) as unknown as Record<string, unknown>;
470
503
 
471
504
  case "_macro/deliverPeerMessage":
472
505
  return this.handleDeliverPeerMessage(
473
- params as unknown as DeliverPeerMessageRequest
506
+ params as unknown as DeliverPeerMessageRequest,
474
507
  ) as unknown as Record<string, unknown>;
475
508
 
476
509
  case "_macro/deliverPeerRequest":
477
510
  return this.handleDeliverPeerRequest(
478
- params as unknown as DeliverPeerRequestRequest
511
+ params as unknown as DeliverPeerRequestRequest,
479
512
  ) as unknown as Record<string, unknown>;
480
513
 
481
514
  case "_macro/grantCapability":
482
515
  return this.handleGrantCapability(
483
- params as unknown as GrantCapabilityRequest
516
+ params as unknown as GrantCapabilityRequest,
484
517
  ) as unknown as Record<string, unknown>;
485
518
 
486
519
  case "_macro/revokeCapability":
487
520
  return this.handleRevokeCapability(
488
- params as unknown as RevokeCapabilityRequest
521
+ params as unknown as RevokeCapabilityRequest,
489
522
  ) as unknown as Record<string, unknown>;
490
523
 
491
524
  case "_macro/getCapabilities":
492
525
  return this.handleGetCapabilities(
493
- params as unknown as GetCapabilitiesRequest
526
+ params as unknown as GetCapabilitiesRequest,
494
527
  ) as unknown as Record<string, unknown>;
495
528
 
496
529
  case "_macro/checkCapability":
497
530
  return this.handleCheckCapability(
498
- params as unknown as CheckCapabilityRequest
531
+ params as unknown as CheckCapabilityRequest,
499
532
  ) as unknown as Record<string, unknown>;
500
533
 
501
534
  case "_macro/respondToPermission":
502
535
  return this.handleRespondToPermission(
503
- params as unknown as RespondToPermissionRequest
536
+ params as unknown as RespondToPermissionRequest,
504
537
  ) as unknown as Record<string, unknown>;
505
538
 
506
539
  case "_macro/cancelPermission":
507
540
  return this.handleCancelPermission(
508
- params as unknown as CancelPermissionRequest
541
+ params as unknown as CancelPermissionRequest,
509
542
  ) as unknown as Record<string, unknown>;
510
543
 
511
544
  case "_macro/resume":
512
545
  return this.handleResumeAgent(
513
- params as unknown as ResumeAgentRequest
546
+ params as unknown as ResumeAgentRequest,
547
+ ) as unknown as Record<string, unknown>;
548
+
549
+ case "_macro/getHistory":
550
+ return this.handleGetHistory(
551
+ params as unknown as GetHistoryRequest,
514
552
  ) as unknown as Record<string, unknown>;
515
553
 
516
554
  default:
517
555
  throw new ACPError(
518
556
  `Unknown extension method: ${method}`,
519
- "INVALID_EXTENSION"
557
+ "INVALID_EXTENSION",
520
558
  );
521
559
  }
522
560
  }
@@ -529,7 +567,7 @@ export class MacroAgent implements Agent {
529
567
  * Spawn a new child agent
530
568
  */
531
569
  private async handleSpawnAgent(
532
- params: SpawnAgentRequest
570
+ params: SpawnAgentRequest,
533
571
  ): Promise<SpawnAgentResponse> {
534
572
  // Determine parent - use provided parentId or fall back to a session's mapped agent
535
573
  let parentId = params.parentId;
@@ -563,7 +601,7 @@ export class MacroAgent implements Agent {
563
601
  parentRole,
564
602
  childRole,
565
603
  requiredCapability,
566
- }
604
+ },
567
605
  );
568
606
  }
569
607
  }
@@ -572,7 +610,7 @@ export class MacroAgent implements Agent {
572
610
  // Merge default config with per-spawn override
573
611
  const mergedConfig = this.mergeSubAgentConfig(
574
612
  this.initConfig.defaultSubAgentConfig,
575
- params.config
613
+ params.config,
576
614
  );
577
615
 
578
616
  // Spawn the agent with merged config
@@ -624,7 +662,7 @@ export class MacroAgent implements Agent {
624
662
  * Get the agent hierarchy tree
625
663
  */
626
664
  private async handleGetHierarchy(
627
- params: GetHierarchyRequest
665
+ params: GetHierarchyRequest,
628
666
  ): Promise<GetHierarchyResponse> {
629
667
  let rootAgentId = params.rootAgentId;
630
668
 
@@ -665,7 +703,7 @@ export class MacroAgent implements Agent {
665
703
  * Get task details by ID
666
704
  */
667
705
  private async handleGetTask(
668
- params: GetTaskRequest
706
+ params: GetTaskRequest,
669
707
  ): Promise<GetTaskResponse> {
670
708
  const task = this.taskManager.get(params.taskId);
671
709
 
@@ -687,7 +725,7 @@ export class MacroAgent implements Agent {
687
725
  * Subsequent prompts will go to the mounted agent.
688
726
  */
689
727
  private async handleMountAgent(
690
- params: MountAgentRequest
728
+ params: MountAgentRequest,
691
729
  ): Promise<MountAgentResponse> {
692
730
  const { sessionId, agentId } = params;
693
731
 
@@ -705,7 +743,7 @@ export class MacroAgent implements Agent {
705
743
  throw new ACPError(
706
744
  `Session not found: ${sessionId}`,
707
745
  "SESSION_NOT_FOUND",
708
- { sessionId }
746
+ { sessionId },
709
747
  );
710
748
  }
711
749
 
@@ -729,7 +767,7 @@ export class MacroAgent implements Agent {
729
767
  * Uses native fork if available, otherwise falls back to loadSession.
730
768
  */
731
769
  private async handleForkAgent(
732
- params: ForkAgentRequest
770
+ params: ForkAgentRequest,
733
771
  ): Promise<ForkAgentResponse> {
734
772
  const { agentId, name } = params;
735
773
 
@@ -747,7 +785,7 @@ export class MacroAgent implements Agent {
747
785
  throw new ACPError(
748
786
  `Agent has no active session to fork: ${agentId}`,
749
787
  "FORK_NOT_SUPPORTED",
750
- { agentId }
788
+ { agentId },
751
789
  );
752
790
  }
753
791
 
@@ -789,12 +827,12 @@ export class MacroAgent implements Agent {
789
827
  * Called by external clients to have an agent send a message to a peer.
790
828
  */
791
829
  private async handleSendPeerMessage(
792
- params: SendPeerMessageACPRequest
830
+ params: SendPeerMessageACPRequest,
793
831
  ): Promise<SendPeerMessageACPResponse> {
794
832
  if (!this.peerManager) {
795
833
  throw new ACPError(
796
834
  "PeerManager not configured for this macro-agent",
797
- "NO_PEER_MANAGER"
835
+ "NO_PEER_MANAGER",
798
836
  );
799
837
  }
800
838
 
@@ -803,7 +841,7 @@ export class MacroAgent implements Agent {
803
841
  if (headManagers.length === 0) {
804
842
  throw new ACPError(
805
843
  "No agents available to send peer message",
806
- "AGENT_NOT_FOUND"
844
+ "AGENT_NOT_FOUND",
807
845
  );
808
846
  }
809
847
  const sendingAgentId = headManagers[0].id;
@@ -825,7 +863,7 @@ export class MacroAgent implements Agent {
825
863
  throw new ACPError(
826
864
  `Failed to send peer message: ${error instanceof Error ? error.message : String(error)}`,
827
865
  "PEER_SEND_FAILED",
828
- { to: params.to, error }
866
+ { to: params.to, error },
829
867
  );
830
868
  }
831
869
  }
@@ -837,12 +875,12 @@ export class MacroAgent implements Agent {
837
875
  * and wait for a response.
838
876
  */
839
877
  private async handleSendPeerRequest(
840
- params: SendPeerRequestACPRequest
878
+ params: SendPeerRequestACPRequest,
841
879
  ): Promise<SendPeerRequestACPResponse> {
842
880
  if (!this.peerManager) {
843
881
  throw new ACPError(
844
882
  "PeerManager not configured for this macro-agent",
845
- "NO_PEER_MANAGER"
883
+ "NO_PEER_MANAGER",
846
884
  );
847
885
  }
848
886
 
@@ -851,7 +889,7 @@ export class MacroAgent implements Agent {
851
889
  if (headManagers.length === 0) {
852
890
  throw new ACPError(
853
891
  "No agents available to send peer request",
854
- "AGENT_NOT_FOUND"
892
+ "AGENT_NOT_FOUND",
855
893
  );
856
894
  }
857
895
  const sendingAgentId = headManagers[0].id;
@@ -864,7 +902,7 @@ export class MacroAgent implements Agent {
864
902
  method: params.method,
865
903
  params: params.params,
866
904
  timeout: params.timeout,
867
- }
905
+ },
868
906
  );
869
907
 
870
908
  return response;
@@ -872,7 +910,7 @@ export class MacroAgent implements Agent {
872
910
  throw new ACPError(
873
911
  `Failed to send peer request: ${error instanceof Error ? error.message : String(error)}`,
874
912
  "PEER_SEND_FAILED",
875
- { to: params.to, error }
913
+ { to: params.to, error },
876
914
  );
877
915
  }
878
916
  }
@@ -884,12 +922,12 @@ export class MacroAgent implements Agent {
884
922
  * to this macro-agent. The message is queued for the target agent.
885
923
  */
886
924
  private async handleDeliverPeerMessage(
887
- params: DeliverPeerMessageRequest
925
+ params: DeliverPeerMessageRequest,
888
926
  ): Promise<DeliverPeerMessageResponse> {
889
927
  if (!this.peerManager) {
890
928
  throw new ACPError(
891
929
  "PeerManager not configured for this macro-agent",
892
- "NO_PEER_MANAGER"
930
+ "NO_PEER_MANAGER",
893
931
  );
894
932
  }
895
933
 
@@ -903,7 +941,7 @@ export class MacroAgent implements Agent {
903
941
  ? { correlationId: params.correlationId }
904
942
  : undefined,
905
943
  },
906
- params.targetAgentId
944
+ params.targetAgentId,
907
945
  );
908
946
 
909
947
  return {
@@ -920,12 +958,12 @@ export class MacroAgent implements Agent {
920
958
  * internal agent to respond.
921
959
  */
922
960
  private async handleDeliverPeerRequest(
923
- params: DeliverPeerRequestRequest
961
+ params: DeliverPeerRequestRequest,
924
962
  ): Promise<DeliverPeerRequestResponse> {
925
963
  if (!this.peerManager) {
926
964
  throw new ACPError(
927
965
  "PeerManager not configured for this macro-agent",
928
- "NO_PEER_MANAGER"
966
+ "NO_PEER_MANAGER",
929
967
  );
930
968
  }
931
969
 
@@ -938,7 +976,7 @@ export class MacroAgent implements Agent {
938
976
  params: params.params,
939
977
  timeout: params.timeout,
940
978
  },
941
- params.targetAgentId
979
+ params.targetAgentId,
942
980
  );
943
981
 
944
982
  return response;
@@ -952,12 +990,12 @@ export class MacroAgent implements Agent {
952
990
  * Grant capabilities to a peer
953
991
  */
954
992
  private async handleGrantCapability(
955
- params: GrantCapabilityRequest
993
+ params: GrantCapabilityRequest,
956
994
  ): Promise<GrantCapabilityResponse> {
957
995
  if (!this.capabilityManager) {
958
996
  throw new ACPError(
959
997
  "CapabilityManager not configured for this macro-agent",
960
- "NO_PEER_MANAGER"
998
+ "NO_PEER_MANAGER",
961
999
  );
962
1000
  }
963
1001
 
@@ -967,7 +1005,7 @@ export class MacroAgent implements Agent {
967
1005
  {
968
1006
  expiresIn: params.expiresIn,
969
1007
  issuedBy: params.issuedBy,
970
- }
1008
+ },
971
1009
  );
972
1010
 
973
1011
  return { capabilities };
@@ -977,12 +1015,12 @@ export class MacroAgent implements Agent {
977
1015
  * Revoke capabilities from a peer
978
1016
  */
979
1017
  private async handleRevokeCapability(
980
- params: RevokeCapabilityRequest
1018
+ params: RevokeCapabilityRequest,
981
1019
  ): Promise<RevokeCapabilityResponse> {
982
1020
  if (!this.capabilityManager) {
983
1021
  throw new ACPError(
984
1022
  "CapabilityManager not configured for this macro-agent",
985
- "NO_PEER_MANAGER"
1023
+ "NO_PEER_MANAGER",
986
1024
  );
987
1025
  }
988
1026
 
@@ -991,7 +1029,7 @@ export class MacroAgent implements Agent {
991
1029
  return {
992
1030
  success: true,
993
1031
  remainingCapabilities: this.capabilityManager.getCapabilities(
994
- params.peerId
1032
+ params.peerId,
995
1033
  ),
996
1034
  };
997
1035
  }
@@ -1000,12 +1038,12 @@ export class MacroAgent implements Agent {
1000
1038
  * Get capabilities for a peer or list all authorized peers
1001
1039
  */
1002
1040
  private async handleGetCapabilities(
1003
- params: GetCapabilitiesRequest
1041
+ params: GetCapabilitiesRequest,
1004
1042
  ): Promise<GetCapabilitiesResponse> {
1005
1043
  if (!this.capabilityManager) {
1006
1044
  throw new ACPError(
1007
1045
  "CapabilityManager not configured for this macro-agent",
1008
- "NO_PEER_MANAGER"
1046
+ "NO_PEER_MANAGER",
1009
1047
  );
1010
1048
  }
1011
1049
 
@@ -1024,19 +1062,19 @@ export class MacroAgent implements Agent {
1024
1062
  * Check if a peer has a required capability
1025
1063
  */
1026
1064
  private async handleCheckCapability(
1027
- params: CheckCapabilityRequest
1065
+ params: CheckCapabilityRequest,
1028
1066
  ): Promise<CheckCapabilityResponse> {
1029
1067
  if (!this.capabilityManager) {
1030
1068
  throw new ACPError(
1031
1069
  "CapabilityManager not configured for this macro-agent",
1032
- "NO_PEER_MANAGER"
1070
+ "NO_PEER_MANAGER",
1033
1071
  );
1034
1072
  }
1035
1073
 
1036
1074
  return {
1037
1075
  hasCapability: this.capabilityManager.hasCapability(
1038
1076
  params.peerId,
1039
- params.required
1077
+ params.required,
1040
1078
  ),
1041
1079
  };
1042
1080
  }
@@ -1052,7 +1090,7 @@ export class MacroAgent implements Agent {
1052
1090
  * then calls the agent manager to resolve the pending permission.
1053
1091
  */
1054
1092
  private async handleRespondToPermission(
1055
- params: RespondToPermissionRequest
1093
+ params: RespondToPermissionRequest,
1056
1094
  ): Promise<RespondToPermissionResponse> {
1057
1095
  const { sessionId, requestId, optionId } = params;
1058
1096
 
@@ -1069,12 +1107,12 @@ export class MacroAgent implements Agent {
1069
1107
  const success = this.agentManager.respondToPermission(
1070
1108
  agentId,
1071
1109
  requestId,
1072
- optionId
1110
+ optionId,
1073
1111
  );
1074
1112
 
1075
1113
  if (success) {
1076
1114
  console.log(
1077
- `[MacroAgent] Responded to permission ${requestId} for session ${sessionId} with ${optionId}`
1115
+ `[MacroAgent] Responded to permission ${requestId} for session ${sessionId} with ${optionId}`,
1078
1116
  );
1079
1117
  return { success: true };
1080
1118
  } else {
@@ -1092,7 +1130,7 @@ export class MacroAgent implements Agent {
1092
1130
  * then calls the agent manager to cancel the pending permission.
1093
1131
  */
1094
1132
  private async handleCancelPermission(
1095
- params: CancelPermissionRequest
1133
+ params: CancelPermissionRequest,
1096
1134
  ): Promise<CancelPermissionResponse> {
1097
1135
  const { sessionId, requestId } = params;
1098
1136
 
@@ -1110,7 +1148,7 @@ export class MacroAgent implements Agent {
1110
1148
 
1111
1149
  if (success) {
1112
1150
  console.log(
1113
- `[MacroAgent] Cancelled permission ${requestId} for session ${sessionId}`
1151
+ `[MacroAgent] Cancelled permission ${requestId} for session ${sessionId}`,
1114
1152
  );
1115
1153
  return { success: true };
1116
1154
  } else {
@@ -1125,7 +1163,7 @@ export class MacroAgent implements Agent {
1125
1163
  * Resume a stopped/failed agent
1126
1164
  */
1127
1165
  private async handleResumeAgent(
1128
- params: ResumeAgentRequest
1166
+ params: ResumeAgentRequest,
1129
1167
  ): Promise<ResumeAgentResponse> {
1130
1168
  const { agentId } = params;
1131
1169
 
@@ -1143,7 +1181,7 @@ export class MacroAgent implements Agent {
1143
1181
  if (agent.state !== "stopped" && agent.state !== "failed") {
1144
1182
  throw new ACPError(
1145
1183
  `Agent ${agentId} is ${agent.state} — only stopped or failed agents can be resumed`,
1146
- "INVALID_EXTENSION"
1184
+ "INVALID_EXTENSION",
1147
1185
  );
1148
1186
  }
1149
1187
 
@@ -1195,12 +1233,13 @@ export class MacroAgent implements Agent {
1195
1233
  * - available_commands_update: Available slash commands
1196
1234
  * - current_mode_update: Mode changes (code/plan/etc)
1197
1235
  * - config_option_update: Configuration changes
1236
+ * - session_info_update: Session title and timestamps
1198
1237
  *
1199
1238
  * Permission requests are handled separately via the requestPermission RPC.
1200
1239
  */
1201
1240
  private async forwardSessionUpdate(
1202
1241
  acpSessionId: ACPSessionId,
1203
- update: unknown
1242
+ update: unknown,
1204
1243
  ): Promise<void> {
1205
1244
  const sessionUpdate = update as Record<string, unknown>;
1206
1245
 
@@ -1208,7 +1247,7 @@ export class MacroAgent implements Agent {
1208
1247
  if (!("sessionUpdate" in sessionUpdate)) {
1209
1248
  console.warn(
1210
1249
  `[MacroAgent] Received update without sessionUpdate field:`,
1211
- JSON.stringify(update).substring(0, 200)
1250
+ JSON.stringify(update).substring(0, 200),
1212
1251
  );
1213
1252
  return;
1214
1253
  }
@@ -1226,7 +1265,7 @@ export class MacroAgent implements Agent {
1226
1265
  const text = content?.text ?? "";
1227
1266
  if (text) {
1228
1267
  console.log(
1229
- `[MacroAgent] Forwarding ${updateType} (${text.length} chars): "${text.substring(0, 80)}${text.length > 80 ? "..." : ""}"`
1268
+ `[MacroAgent] Forwarding ${updateType} (${text.length} chars): "${text.substring(0, 80)}${text.length > 80 ? "..." : ""}"`,
1230
1269
  );
1231
1270
  }
1232
1271
  break;
@@ -1237,7 +1276,7 @@ export class MacroAgent implements Agent {
1237
1276
  const title = sessionUpdate.title as string;
1238
1277
  const status = sessionUpdate.status as string;
1239
1278
  console.log(
1240
- `[MacroAgent] Forwarding tool_call: id=${toolCallId}, title="${title}", status=${status}`
1279
+ `[MacroAgent] Forwarding tool_call: id=${toolCallId}, title="${title}", status=${status}`,
1241
1280
  );
1242
1281
  break;
1243
1282
  }
@@ -1246,7 +1285,7 @@ export class MacroAgent implements Agent {
1246
1285
  const toolCallId = sessionUpdate.toolCallId as string;
1247
1286
  const status = sessionUpdate.status as string;
1248
1287
  console.log(
1249
- `[MacroAgent] Forwarding tool_call_update: id=${toolCallId}, status=${status}`
1288
+ `[MacroAgent] Forwarding tool_call_update: id=${toolCallId}, status=${status}`,
1250
1289
  );
1251
1290
  break;
1252
1291
  }
@@ -1276,7 +1315,7 @@ export class MacroAgent implements Agent {
1276
1315
  const agentId = this.sessionMapper.getAgentId(permReq.sessionId);
1277
1316
  if (!agentId) {
1278
1317
  console.warn(
1279
- `[MacroAgent] No agent found for session ${permReq.sessionId}, cannot forward permission request`
1318
+ `[MacroAgent] No agent found for session ${permReq.sessionId}, cannot forward permission request`,
1280
1319
  );
1281
1320
  return;
1282
1321
  }
@@ -1289,11 +1328,20 @@ export class MacroAgent implements Agent {
1289
1328
  toolCall: {
1290
1329
  toolCallId: permReq.toolCall.toolCallId,
1291
1330
  title: permReq.toolCall.title,
1292
- status: permReq.toolCall.status as "pending" | "in_progress" | "completed" | "failed" | undefined,
1331
+ status: permReq.toolCall.status as
1332
+ | "pending"
1333
+ | "in_progress"
1334
+ | "completed"
1335
+ | "failed"
1336
+ | undefined,
1293
1337
  rawInput: permReq.toolCall.rawInput,
1294
1338
  },
1295
1339
  options: permReq.options as Array<{
1296
- kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
1340
+ kind:
1341
+ | "allow_once"
1342
+ | "allow_always"
1343
+ | "reject_once"
1344
+ | "reject_always";
1297
1345
  name: string;
1298
1346
  optionId: string;
1299
1347
  }>,
@@ -1308,21 +1356,21 @@ export class MacroAgent implements Agent {
1308
1356
  const success = this.agentManager.respondToPermission(
1309
1357
  agentId,
1310
1358
  permReq.requestId,
1311
- response.outcome.optionId
1359
+ response.outcome.optionId,
1312
1360
  );
1313
1361
  if (!success) {
1314
1362
  console.warn(
1315
- `[MacroAgent] Failed to forward permission response to agent ${agentId}`
1363
+ `[MacroAgent] Failed to forward permission response to agent ${agentId}`,
1316
1364
  );
1317
1365
  }
1318
1366
  } else if (response.outcome.outcome === "cancelled") {
1319
1367
  const success = this.agentManager.cancelPermission(
1320
1368
  agentId,
1321
- permReq.requestId
1369
+ permReq.requestId,
1322
1370
  );
1323
1371
  if (!success) {
1324
1372
  console.warn(
1325
- `[MacroAgent] Failed to cancel permission for agent ${agentId}`
1373
+ `[MacroAgent] Failed to cancel permission for agent ${agentId}`,
1326
1374
  );
1327
1375
  }
1328
1376
  }
@@ -1330,7 +1378,7 @@ export class MacroAgent implements Agent {
1330
1378
  } catch (err) {
1331
1379
  console.error(
1332
1380
  `[MacroAgent] Failed to forward permission_request:`,
1333
- err instanceof Error ? err.message : err
1381
+ err instanceof Error ? err.message : err,
1334
1382
  );
1335
1383
  // Cancel the permission request on error
1336
1384
  try {
@@ -1348,16 +1396,77 @@ export class MacroAgent implements Agent {
1348
1396
  console.log(`[MacroAgent] Forwarding ${updateType}`);
1349
1397
  }
1350
1398
 
1399
+ // Accumulate content for history persistence
1400
+ const buffer = this.promptBuffers.get(acpSessionId);
1401
+ if (buffer) {
1402
+ if (updateType === "agent_message_chunk") {
1403
+ const content = sessionUpdate.content as
1404
+ | { type?: string; text?: string }
1405
+ | undefined;
1406
+ if (content?.text) {
1407
+ buffer.textChunks.push(content.text);
1408
+ }
1409
+ } else if (
1410
+ updateType === "tool_call" ||
1411
+ updateType === "tool_call_update"
1412
+ ) {
1413
+ const status = sessionUpdate.status as string | undefined;
1414
+ if (
1415
+ status === "completed" ||
1416
+ (updateType === "tool_call" && status !== "running")
1417
+ ) {
1418
+ buffer.toolCalls.push({
1419
+ toolCallId: sessionUpdate.toolCallId,
1420
+ title: sessionUpdate.title,
1421
+ status: sessionUpdate.status,
1422
+ input: sessionUpdate.rawInput,
1423
+ output: sessionUpdate.output,
1424
+ });
1425
+ }
1426
+ }
1427
+ }
1428
+
1351
1429
  // Forward updates via sessionUpdate (except permission_request which is handled above)
1352
1430
  try {
1353
1431
  await this.connection.sessionUpdate({
1354
1432
  sessionId: acpSessionId,
1355
- update: sessionUpdate as Parameters<AgentSideConnection["sessionUpdate"]>[0]["update"],
1433
+ update: sessionUpdate as Parameters<
1434
+ AgentSideConnection["sessionUpdate"]
1435
+ >[0]["update"],
1356
1436
  });
1357
1437
  } catch (err) {
1358
1438
  console.error(
1359
1439
  `[MacroAgent] Failed to forward ${updateType}:`,
1360
- err instanceof Error ? err.message : err
1440
+ err instanceof Error ? err.message : err,
1441
+ );
1442
+ }
1443
+ }
1444
+
1445
+ /**
1446
+ * Emit a session_info_update with title and timestamps.
1447
+ * Uses agent task as title and session mapping timestamps.
1448
+ */
1449
+ private async emitSessionInfo(acpSessionId: ACPSessionId): Promise<void> {
1450
+ const mapping = this.sessionMapper.getMapping(acpSessionId);
1451
+ const agentId = mapping?.agentId;
1452
+ const agent = agentId ? this.eventStore.getAgent(agentId) : null;
1453
+
1454
+ const title = agent?.task ?? null;
1455
+ const updatedAt = new Date(mapping?.updatedAt ?? Date.now()).toISOString();
1456
+
1457
+ try {
1458
+ await this.connection.sessionUpdate({
1459
+ sessionId: acpSessionId,
1460
+ update: {
1461
+ sessionUpdate: "session_info_update",
1462
+ title,
1463
+ updatedAt,
1464
+ } as Parameters<AgentSideConnection["sessionUpdate"]>[0]["update"],
1465
+ });
1466
+ } catch (err) {
1467
+ console.warn(
1468
+ `[MacroAgent] Failed to send session_info_update:`,
1469
+ err instanceof Error ? err.message : err,
1361
1470
  );
1362
1471
  }
1363
1472
  }
@@ -1391,7 +1500,7 @@ export class MacroAgent implements Agent {
1391
1500
  */
1392
1501
  private mergeSubAgentConfig(
1393
1502
  defaults?: SubAgentConfig,
1394
- override?: SubAgentConfig
1503
+ override?: SubAgentConfig,
1395
1504
  ): SubAgentConfig | undefined {
1396
1505
  if (!defaults && !override) {
1397
1506
  return undefined;
@@ -1448,6 +1557,141 @@ export class MacroAgent implements Agent {
1448
1557
  };
1449
1558
  }
1450
1559
 
1560
+ // ─────────────────────────────────────────────────────────────────
1561
+ // History Persistence Helpers
1562
+ // ─────────────────────────────────────────────────────────────────
1563
+
1564
+ /**
1565
+ * Ensure a conversation exists in the EventStore for a session.
1566
+ * Uses the ACP session ID as the conversation ID for direct lookup.
1567
+ */
1568
+ private ensureConversation(
1569
+ acpSessionId: ACPSessionId,
1570
+ agentId: AgentId,
1571
+ ): void {
1572
+ // Guard: EventStore may not support conversations (e.g., in tests with mocks)
1573
+ if (typeof this.eventStore.getConversation !== "function") return;
1574
+
1575
+ // Check if conversation already exists
1576
+ const existing = this.eventStore.getConversation(acpSessionId);
1577
+ if (existing) return;
1578
+
1579
+ try {
1580
+ this.eventStore.emit({
1581
+ type: "conversation",
1582
+ source: { agent_id: agentId },
1583
+ payload: {
1584
+ action: "created",
1585
+ conversation_id: acpSessionId,
1586
+ conversation_type: "session",
1587
+ subject: `ACP session ${acpSessionId}`,
1588
+ },
1589
+ });
1590
+ } catch (error) {
1591
+ console.warn(
1592
+ `[MacroAgent] Failed to create conversation for session ${acpSessionId}:`,
1593
+ error instanceof Error ? error.message : String(error),
1594
+ );
1595
+ }
1596
+ }
1597
+
1598
+ /**
1599
+ * Record user and assistant turns after a prompt completes.
1600
+ */
1601
+ private recordPromptTurns(
1602
+ acpSessionId: ACPSessionId,
1603
+ agentId: AgentId,
1604
+ userMessage: string,
1605
+ ): void {
1606
+ const buffer = this.promptBuffers.get(acpSessionId);
1607
+ if (!buffer) return;
1608
+
1609
+ const now = Date.now();
1610
+
1611
+ try {
1612
+ // Record user turn
1613
+ if (userMessage) {
1614
+ this.eventStore.emit({
1615
+ type: "turn",
1616
+ source: { agent_id: agentId },
1617
+ payload: {
1618
+ action: "recorded",
1619
+ turn_id: `turn_user_${now}_${Math.random().toString(36).slice(2, 8)}`,
1620
+ conversation_id: acpSessionId,
1621
+ participant: "user",
1622
+ timestamp: now,
1623
+ content_type: "user_prompt",
1624
+ content: userMessage,
1625
+ source_type: "explicit",
1626
+ },
1627
+ });
1628
+ }
1629
+
1630
+ // Record assistant turn with accumulated content
1631
+ const assistantText = buffer.textChunks.join("");
1632
+ const parts: unknown[] = [];
1633
+
1634
+ if (assistantText) {
1635
+ parts.push({ type: "text", text: assistantText });
1636
+ }
1637
+
1638
+ for (const tool of buffer.toolCalls) {
1639
+ parts.push({ type: "tool", ...tool });
1640
+ }
1641
+
1642
+ if (parts.length > 0) {
1643
+ this.eventStore.emit({
1644
+ type: "turn",
1645
+ source: { agent_id: agentId },
1646
+ payload: {
1647
+ action: "recorded",
1648
+ turn_id: `turn_asst_${now}_${Math.random().toString(36).slice(2, 8)}`,
1649
+ conversation_id: acpSessionId,
1650
+ participant: agentId,
1651
+ timestamp: now + 1, // +1ms to ensure ordering after user turn
1652
+ content_type: "assistant_response",
1653
+ content: { parts },
1654
+ source_type: "explicit",
1655
+ },
1656
+ });
1657
+ }
1658
+ } catch (error) {
1659
+ console.warn(
1660
+ `[MacroAgent] Failed to record turns for session ${acpSessionId}:`,
1661
+ error instanceof Error ? error.message : String(error),
1662
+ );
1663
+ } finally {
1664
+ // Clean up the buffer
1665
+ this.promptBuffers.delete(acpSessionId);
1666
+ }
1667
+ }
1668
+
1669
+ /**
1670
+ * Handle _macro/getHistory extension — returns conversation turns for a session
1671
+ */
1672
+ private handleGetHistory(params: GetHistoryRequest): GetHistoryResponse {
1673
+ const { sessionId, limit } = params;
1674
+
1675
+ // Query turns from EventStore (conversation ID = session ID)
1676
+ const turns = this.eventStore.listTurns({
1677
+ conversationId: sessionId,
1678
+ order: "asc",
1679
+ limit: limit ?? 200,
1680
+ });
1681
+
1682
+ // Convert to HistoryTurn format
1683
+ const historyTurns: HistoryTurn[] = turns.map((turn) => ({
1684
+ role:
1685
+ turn.contentType === "user_prompt"
1686
+ ? ("user" as const)
1687
+ : ("assistant" as const),
1688
+ timestamp: turn.timestamp,
1689
+ content: turn.content,
1690
+ }));
1691
+
1692
+ return { turns: historyTurns };
1693
+ }
1694
+
1451
1695
  // ─────────────────────────────────────────────────────────────────
1452
1696
  // Accessors
1453
1697
  // ─────────────────────────────────────────────────────────────────