macro-agent 0.0.16 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/acp/macro-agent.d.ts +2 -0
  2. package/dist/acp/macro-agent.d.ts.map +1 -1
  3. package/dist/acp/macro-agent.js +52 -20
  4. package/dist/acp/macro-agent.js.map +1 -1
  5. package/dist/agent/agent-manager.d.ts.map +1 -1
  6. package/dist/agent/agent-manager.js +23 -5
  7. package/dist/agent/agent-manager.js.map +1 -1
  8. package/dist/map/adapter/acp-over-map.d.ts +16 -0
  9. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  10. package/dist/map/adapter/acp-over-map.js +242 -24
  11. package/dist/map/adapter/acp-over-map.js.map +1 -1
  12. package/dist/map/adapter/map-adapter.d.ts +1 -0
  13. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  14. package/dist/map/adapter/map-adapter.js +57 -8
  15. package/dist/map/adapter/map-adapter.js.map +1 -1
  16. package/dist/store/event-store.d.ts +5 -0
  17. package/dist/store/event-store.d.ts.map +1 -1
  18. package/dist/store/event-store.js +126 -53
  19. package/dist/store/event-store.js.map +1 -1
  20. package/dist/store/instance.d.ts +0 -2
  21. package/dist/store/instance.d.ts.map +1 -1
  22. package/dist/store/instance.js +1 -24
  23. package/dist/store/instance.js.map +1 -1
  24. package/dist/store/types/agents.d.ts +5 -0
  25. package/dist/store/types/agents.d.ts.map +1 -1
  26. package/package.json +3 -3
  27. package/references/acp-factory-ref/package-lock.json +2 -2
  28. package/references/acp-factory-ref/package.json +2 -2
  29. package/references/claude-code-acp/package-lock.json +2 -2
  30. package/references/claude-code-acp/package.json +1 -1
  31. package/references/claude-code-acp/src/acp-agent.ts +3 -6
  32. package/src/acp/__tests__/history.test.ts +8 -4
  33. package/src/acp/macro-agent.ts +60 -26
  34. package/src/agent/__tests__/agent-manager.test.ts +4 -6
  35. package/src/agent/agent-manager.ts +24 -5
  36. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +802 -0
  37. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +1123 -0
  38. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +440 -0
  39. package/src/map/adapter/acp-over-map.ts +282 -25
  40. package/src/map/adapter/map-adapter.ts +79 -9
  41. package/src/store/__tests__/event-store.test.ts +44 -12
  42. package/src/store/__tests__/instance.test.ts +5 -7
  43. package/src/store/event-store.ts +157 -57
  44. package/src/store/instance.ts +1 -29
  45. package/src/store/types/agents.ts +1 -0
@@ -75,6 +75,22 @@ import type { RoleRegistry, Capability } from "../roles/types.js";
75
75
  import { AGENT_CAPABILITIES } from "../roles/capabilities.js";
76
76
  import { DefaultRoleRegistry } from "../roles/registry.js";
77
77
 
78
+ // ─────────────────────────────────────────────────────────────────
79
+ // Helpers
80
+ // ─────────────────────────────────────────────────────────────────
81
+
82
+ /** Extract a plain-text output string from `rawOutput` (string | ContentBlock[] | undefined). */
83
+ function extractToolOutput(rawOutput: unknown): string | undefined {
84
+ if (typeof rawOutput === "string") return rawOutput;
85
+ if (Array.isArray(rawOutput)) {
86
+ return rawOutput
87
+ .filter((item: any) => item.type === "text" && typeof item.text === "string")
88
+ .map((item: any) => item.text as string)
89
+ .join("\n") || undefined;
90
+ }
91
+ return undefined;
92
+ }
93
+
78
94
  // ─────────────────────────────────────────────────────────────────
79
95
  // Protocol Constants
80
96
  // ─────────────────────────────────────────────────────────────────
@@ -157,7 +173,13 @@ export class MacroAgent implements Agent {
157
173
  /** Accumulates assistant response parts during prompt streaming for history persistence */
158
174
  private promptBuffers: Map<
159
175
  ACPSessionId,
160
- { textChunks: string[]; toolCalls: Record<string, unknown>[] }
176
+ { parts: Array<{ type: "text"; text: string } | ({ type: "tool" } & Record<string, unknown>)> }
177
+ > = new Map();
178
+
179
+ /** Caches tool info (title, name, input) from initial tool_call events per session */
180
+ private toolInfoCaches: Map<
181
+ ACPSessionId,
182
+ Map<string, { title?: string; name?: string; input?: unknown }>
161
183
  > = new Map();
162
184
 
163
185
  constructor(connection: AgentSideConnection, config: MacroAgentConfig) {
@@ -360,8 +382,9 @@ export class MacroAgent implements Agent {
360
382
  // Mark session as processing (for health monitoring)
361
383
  this.sessionMapper.setProcessing(acpSessionId, true);
362
384
 
363
- // Initialize prompt buffer for history accumulation
364
- this.promptBuffers.set(acpSessionId, { textChunks: [], toolCalls: [] });
385
+ // Initialize prompt buffer and tool info cache for history accumulation
386
+ this.promptBuffers.set(acpSessionId, { parts: [] });
387
+ this.toolInfoCaches.set(acpSessionId, new Map());
365
388
 
366
389
  try {
367
390
  // Stream responses from the agent
@@ -1396,7 +1419,7 @@ export class MacroAgent implements Agent {
1396
1419
  console.log(`[MacroAgent] Forwarding ${updateType}`);
1397
1420
  }
1398
1421
 
1399
- // Accumulate content for history persistence
1422
+ // Accumulate content for history persistence (preserving text/tool interleaving order)
1400
1423
  const buffer = this.promptBuffers.get(acpSessionId);
1401
1424
  if (buffer) {
1402
1425
  if (updateType === "agent_message_chunk") {
@@ -1404,23 +1427,42 @@ export class MacroAgent implements Agent {
1404
1427
  | { type?: string; text?: string }
1405
1428
  | undefined;
1406
1429
  if (content?.text) {
1407
- buffer.textChunks.push(content.text);
1430
+ const last = buffer.parts[buffer.parts.length - 1];
1431
+ if (last && last.type === "text") {
1432
+ last.text += content.text;
1433
+ } else {
1434
+ buffer.parts.push({ type: "text", text: content.text });
1435
+ }
1408
1436
  }
1409
1437
  } else if (
1410
1438
  updateType === "tool_call" ||
1411
1439
  updateType === "tool_call_update"
1412
1440
  ) {
1441
+ const toolCallId = sessionUpdate.toolCallId as string | undefined;
1413
1442
  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,
1443
+ const meta = sessionUpdate._meta as { claudeCode?: { toolName?: string } } | undefined;
1444
+ const toolInfoCache = this.toolInfoCaches.get(acpSessionId);
1445
+
1446
+ // Cache tool info from initial tool_call events
1447
+ if (updateType === "tool_call" && toolCallId && toolInfoCache) {
1448
+ toolInfoCache.set(toolCallId, {
1449
+ title: sessionUpdate.title as string | undefined,
1450
+ name: meta?.claudeCode?.toolName,
1422
1451
  input: sessionUpdate.rawInput,
1423
- output: sessionUpdate.output,
1452
+ });
1453
+ }
1454
+
1455
+ if (status === "completed" || status === "failed") {
1456
+ // Merge cached info for tool_call_update events that lack title/input
1457
+ const cached = toolCallId ? toolInfoCache?.get(toolCallId) : undefined;
1458
+ buffer.parts.push({
1459
+ type: "tool",
1460
+ toolCallId,
1461
+ title: sessionUpdate.title ?? cached?.title,
1462
+ name: meta?.claudeCode?.toolName ?? cached?.name,
1463
+ status: sessionUpdate.status,
1464
+ input: sessionUpdate.rawInput ?? cached?.input,
1465
+ output: extractToolOutput(sessionUpdate.rawOutput),
1424
1466
  });
1425
1467
  }
1426
1468
  }
@@ -1627,17 +1669,8 @@ export class MacroAgent implements Agent {
1627
1669
  });
1628
1670
  }
1629
1671
 
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
- }
1672
+ // Record assistant turn with accumulated content (parts already in order)
1673
+ const parts = buffer.parts;
1641
1674
 
1642
1675
  if (parts.length > 0) {
1643
1676
  this.eventStore.emit({
@@ -1661,8 +1694,9 @@ export class MacroAgent implements Agent {
1661
1694
  error instanceof Error ? error.message : String(error),
1662
1695
  );
1663
1696
  } finally {
1664
- // Clean up the buffer
1697
+ // Clean up the buffer and tool info cache
1665
1698
  this.promptBuffers.delete(acpSessionId);
1699
+ this.toolInfoCaches.delete(acpSessionId);
1666
1700
  }
1667
1701
  }
1668
1702
 
@@ -720,7 +720,7 @@ describe("AgentManager Integration (with mocked acp-factory)", () => {
720
720
  );
721
721
  });
722
722
 
723
- it("should fall back to session_id if provider_session_id is not set", async () => {
723
+ it("should create new session if provider_session_id is not set", async () => {
724
724
  // Create agent directly without provider_session_id
725
725
  const agentId = "agent_no_provider";
726
726
  const sessionId = "session_no_provider";
@@ -744,11 +744,9 @@ describe("AgentManager Integration (with mocked acp-factory)", () => {
744
744
 
745
745
  await agentManager.resume(agentId);
746
746
 
747
- // Should fall back to the macro-agent session_id
748
- expect(mockHandle.loadSession).toHaveBeenCalledWith(
749
- sessionId,
750
- "/tmp",
751
- );
747
+ // Should create a new session instead of loading with invalid macro-agent session_id
748
+ expect(mockHandle.createSession).toHaveBeenCalledWith("/tmp");
749
+ expect(mockHandle.loadSession).not.toHaveBeenCalled();
752
750
  });
753
751
  });
754
752
 
@@ -901,16 +901,34 @@ export function createAgentManager(
901
901
  );
902
902
  }
903
903
 
904
- // Spawn new process and load existing session
904
+ // Spawn new process
905
905
  const handle = await AgentFactory.spawn(defaultAgentType, {
906
906
  permissionMode: defaultPermissionMode,
907
907
  });
908
908
 
909
- // Load the existing session using the provider's session ID (e.g., Claude Code UUID)
910
- // Falls back to macro-agent session_id for backwards compatibility
911
- const loadSessionId = agent.provider_session_id ?? agent.session_id;
912
909
  const agentCwd = agent.cwd ?? defaultCwd;
913
- const session = await handle.loadSession(loadSessionId, agentCwd);
910
+ let session;
911
+
912
+ if (agent.provider_session_id) {
913
+ // Load existing session using the provider's session ID (e.g., Claude Code UUID)
914
+ session = await handle.loadSession(agent.provider_session_id, agentCwd);
915
+ } else {
916
+ // No provider session ID available (agent predates this feature or wasn't persisted).
917
+ // Create a new session instead of loading with the macro-agent session_id
918
+ // which is not a valid provider session ID (e.g., Claude Code expects UUIDs).
919
+ session = await handle.createSession(agentCwd);
920
+
921
+ // Store the provider session ID for future resumes
922
+ eventStore.emit({
923
+ type: "status",
924
+ source: { agent_id: agentId },
925
+ payload: {
926
+ status_type: "started",
927
+ summary: "Agent session created (no provider session to resume)",
928
+ provider_session_id: session.id,
929
+ },
930
+ });
931
+ }
914
932
 
915
933
  // Track active session
916
934
  const activeSession: ActiveSession = {
@@ -929,6 +947,7 @@ export function createAgentManager(
929
947
  payload: {
930
948
  status_type: "started",
931
949
  summary: "Agent session resumed",
950
+ provider_session_id: session.id,
932
951
  },
933
952
  });
934
953