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.
- package/.claude/settings.local.json +59 -0
- package/dist/acp/index.d.ts +1 -1
- package/dist/acp/index.d.ts.map +1 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/macro-agent.d.ts +21 -0
- package/dist/acp/macro-agent.d.ts.map +1 -1
- package/dist/acp/macro-agent.js +182 -0
- package/dist/acp/macro-agent.js.map +1 -1
- package/dist/acp/types.d.ts +31 -2
- package/dist/acp/types.d.ts.map +1 -1
- package/dist/acp/types.js.map +1 -1
- package/dist/agent/agent-manager.d.ts.map +1 -1
- package/dist/agent/agent-manager.js +10 -4
- package/dist/agent/agent-manager.js.map +1 -1
- package/dist/cli/acp.d.ts +6 -0
- package/dist/cli/acp.d.ts.map +1 -1
- package/dist/cli/acp.js +16 -2
- package/dist/cli/acp.js.map +1 -1
- package/dist/map/adapter/acp-over-map.d.ts +5 -0
- package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
- package/dist/map/adapter/acp-over-map.js +47 -4
- package/dist/map/adapter/acp-over-map.js.map +1 -1
- package/dist/map/utils/address-translation.d.ts +99 -0
- package/dist/map/utils/address-translation.d.ts.map +1 -0
- package/dist/map/utils/address-translation.js +285 -0
- package/dist/map/utils/address-translation.js.map +1 -0
- package/dist/map/utils/index.d.ts +7 -0
- package/dist/map/utils/index.d.ts.map +1 -0
- package/dist/map/utils/index.js +7 -0
- package/dist/map/utils/index.js.map +1 -0
- package/dist/store/event-store.js +9 -2
- package/dist/store/event-store.js.map +1 -1
- package/dist/store/types/agents.d.ts +2 -0
- package/dist/store/types/agents.d.ts.map +1 -1
- package/package.json +4 -4
- package/references/acp-factory-ref/CHANGELOG.md +33 -0
- package/references/acp-factory-ref/LICENSE +21 -0
- package/references/acp-factory-ref/README.md +341 -0
- package/references/acp-factory-ref/package-lock.json +3102 -0
- package/references/acp-factory-ref/package.json +96 -0
- package/references/acp-factory-ref/python/CHANGELOG.md +33 -0
- package/references/acp-factory-ref/python/LICENSE +21 -0
- package/references/acp-factory-ref/python/Makefile +57 -0
- package/references/acp-factory-ref/python/README.md +253 -0
- package/references/acp-factory-ref/python/pyproject.toml +73 -0
- package/references/acp-factory-ref/python/tests/__init__.py +0 -0
- package/references/acp-factory-ref/python/tests/e2e/__init__.py +1 -0
- package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +349 -0
- package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +165 -0
- package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +296 -0
- package/references/acp-factory-ref/python/tests/test_client_handler.py +543 -0
- package/references/acp-factory-ref/python/tests/test_pushable.py +199 -0
- package/references/claude-code-acp/.github/workflows/ci.yml +45 -0
- package/references/claude-code-acp/.github/workflows/publish.yml +34 -0
- package/references/claude-code-acp/.prettierrc.json +4 -0
- package/references/claude-code-acp/CHANGELOG.md +249 -0
- package/references/claude-code-acp/LICENSE +222 -0
- package/references/claude-code-acp/README.md +53 -0
- package/references/claude-code-acp/docs/RELEASES.md +24 -0
- package/references/claude-code-acp/eslint.config.js +48 -0
- package/references/claude-code-acp/package-lock.json +4570 -0
- package/references/claude-code-acp/package.json +88 -0
- package/references/claude-code-acp/scripts/release.sh +119 -0
- package/references/claude-code-acp/src/acp-agent.ts +2079 -0
- package/references/claude-code-acp/src/index.ts +26 -0
- package/references/claude-code-acp/src/lib.ts +38 -0
- package/references/claude-code-acp/src/mcp-server.ts +911 -0
- package/references/claude-code-acp/src/settings.ts +522 -0
- package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +5 -0
- package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +6 -0
- package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +479 -0
- package/references/claude-code-acp/src/tests/acp-agent.test.ts +1502 -0
- package/references/claude-code-acp/src/tests/extract-lines.test.ts +103 -0
- package/references/claude-code-acp/src/tests/fork-session.test.ts +335 -0
- package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +334 -0
- package/references/claude-code-acp/src/tests/settings.test.ts +617 -0
- package/references/claude-code-acp/src/tests/skills-options.test.ts +187 -0
- package/references/claude-code-acp/src/tests/tools.test.ts +318 -0
- package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +558 -0
- package/references/claude-code-acp/src/tools.ts +819 -0
- package/references/claude-code-acp/src/utils.ts +171 -0
- package/references/claude-code-acp/tsconfig.json +18 -0
- package/references/claude-code-acp/vitest.config.ts +19 -0
- package/references/multi-agent-protocol/.sudocode/issues.jsonl +111 -0
- package/references/multi-agent-protocol/.sudocode/specs.jsonl +13 -0
- package/references/multi-agent-protocol/LICENSE +21 -0
- package/references/multi-agent-protocol/README.md +113 -0
- package/references/multi-agent-protocol/docs/00-design-specification.md +496 -0
- package/references/multi-agent-protocol/docs/01-open-questions.md +1050 -0
- package/references/multi-agent-protocol/docs/02-wire-protocol.md +296 -0
- package/references/multi-agent-protocol/docs/03-streaming-semantics.md +252 -0
- package/references/multi-agent-protocol/docs/04-error-handling.md +231 -0
- package/references/multi-agent-protocol/docs/05-connection-model.md +244 -0
- package/references/multi-agent-protocol/docs/06-visibility-permissions.md +243 -0
- package/references/multi-agent-protocol/docs/07-federation.md +259 -0
- package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +253 -0
- package/references/multi-agent-protocol/docs/09-authentication.md +680 -0
- package/references/multi-agent-protocol/docs/10-mail-protocol.md +553 -0
- package/references/multi-agent-protocol/docs/agent-iam-integration.md +877 -0
- package/references/multi-agent-protocol/docs/agentic-mesh-integration-draft.md +459 -0
- package/references/multi-agent-protocol/docs/git-transport-draft.md +251 -0
- package/references/multi-agent-protocol/docs-site/Gemfile +22 -0
- package/references/multi-agent-protocol/docs-site/README.md +82 -0
- package/references/multi-agent-protocol/docs-site/_config.yml +91 -0
- package/references/multi-agent-protocol/docs-site/_includes/head_custom.html +20 -0
- package/references/multi-agent-protocol/docs-site/_sass/color_schemes/map.scss +42 -0
- package/references/multi-agent-protocol/docs-site/_sass/custom/custom.scss +34 -0
- package/references/multi-agent-protocol/docs-site/examples/full-integration.md +510 -0
- package/references/multi-agent-protocol/docs-site/examples/index.md +138 -0
- package/references/multi-agent-protocol/docs-site/examples/simple-chat.md +282 -0
- package/references/multi-agent-protocol/docs-site/examples/task-queue.md +399 -0
- package/references/multi-agent-protocol/docs-site/getting-started/index.md +98 -0
- package/references/multi-agent-protocol/docs-site/getting-started/installation.md +219 -0
- package/references/multi-agent-protocol/docs-site/getting-started/overview.md +172 -0
- package/references/multi-agent-protocol/docs-site/getting-started/quickstart.md +237 -0
- package/references/multi-agent-protocol/docs-site/index.md +136 -0
- package/references/multi-agent-protocol/docs-site/protocol/authentication.md +391 -0
- package/references/multi-agent-protocol/docs-site/protocol/connection-model.md +376 -0
- package/references/multi-agent-protocol/docs-site/protocol/design.md +284 -0
- package/references/multi-agent-protocol/docs-site/protocol/error-handling.md +312 -0
- package/references/multi-agent-protocol/docs-site/protocol/federation.md +449 -0
- package/references/multi-agent-protocol/docs-site/protocol/index.md +129 -0
- package/references/multi-agent-protocol/docs-site/protocol/permissions.md +398 -0
- package/references/multi-agent-protocol/docs-site/protocol/streaming.md +353 -0
- package/references/multi-agent-protocol/docs-site/protocol/wire-protocol.md +369 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/agent.md +357 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/client.md +380 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/index.md +62 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/server.md +453 -0
- package/references/multi-agent-protocol/docs-site/sdk/api/types.md +468 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/agent.md +375 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/authentication.md +405 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/client.md +352 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/index.md +89 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/server.md +360 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/testing.md +446 -0
- package/references/multi-agent-protocol/docs-site/sdk/guides/transports.md +363 -0
- package/references/multi-agent-protocol/docs-site/sdk/index.md +206 -0
- package/references/multi-agent-protocol/package-lock.json +3886 -0
- package/references/multi-agent-protocol/package.json +56 -0
- package/references/multi-agent-protocol/schema/meta.json +467 -0
- package/references/multi-agent-protocol/schema/schema.json +2558 -0
- package/src/acp/__tests__/history.test.ts +526 -0
- package/src/acp/__tests__/integration.test.ts +2 -1
- package/src/acp/index.ts +4 -0
- package/src/acp/macro-agent.ts +329 -85
- package/src/acp/types.ts +39 -2
- package/src/agent/__tests__/agent-manager.test.ts +67 -1
- package/src/agent/agent-manager.ts +10 -4
- package/src/cli/__tests__/stable-instance-id.test.ts +57 -0
- package/src/cli/acp.ts +17 -2
- package/src/map/adapter/acp-over-map.ts +57 -2
- package/src/store/event-store.ts +10 -3
- package/src/store/types/agents.ts +2 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History persistence and retrieval tests
|
|
3
|
+
*
|
|
4
|
+
* Tests the _macro/getHistory extension method and the underlying
|
|
5
|
+
* conversation/turn persistence that records prompt interactions.
|
|
6
|
+
*
|
|
7
|
+
* Uses a real in-memory EventStore to verify the full flow:
|
|
8
|
+
* ensureConversation → recordPromptTurns → handleGetHistory
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
12
|
+
import { MacroAgent } from "../macro-agent.js";
|
|
13
|
+
import { createEventStore, type EventStore } from "../../store/event-store.js";
|
|
14
|
+
import type { AgentSideConnection } from "@agentclientprotocol/sdk";
|
|
15
|
+
import type { AgentManager } from "../../agent/agent-manager.js";
|
|
16
|
+
import type { TaskManager } from "../../task/task-manager.js";
|
|
17
|
+
import type { Agent, Task } from "../../store/types/index.js";
|
|
18
|
+
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────
|
|
20
|
+
// Helpers
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
function createMockAgent(overrides: Partial<Agent> = {}): Agent {
|
|
24
|
+
return {
|
|
25
|
+
id: "agent-1",
|
|
26
|
+
session_id: "session-1",
|
|
27
|
+
state: "running",
|
|
28
|
+
task: "Test task",
|
|
29
|
+
task_id: "task-1",
|
|
30
|
+
parent: null,
|
|
31
|
+
lineage: [],
|
|
32
|
+
config: {},
|
|
33
|
+
cwd: "/test/cwd",
|
|
34
|
+
created_at: Date.now(),
|
|
35
|
+
started_at: Date.now(),
|
|
36
|
+
...overrides,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createMockTask(overrides: Partial<Task> = {}): Task {
|
|
41
|
+
return {
|
|
42
|
+
id: "task-1",
|
|
43
|
+
description: "Test task",
|
|
44
|
+
status: "in_progress",
|
|
45
|
+
created_by: "agent-1",
|
|
46
|
+
created_at: Date.now(),
|
|
47
|
+
...overrides,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createMockConnection(): AgentSideConnection {
|
|
52
|
+
return {
|
|
53
|
+
sessionUpdate: vi.fn().mockResolvedValue(undefined),
|
|
54
|
+
requestPermission: vi.fn().mockResolvedValue({ outcome: "allow_once" }),
|
|
55
|
+
closed: Promise.resolve(),
|
|
56
|
+
} as unknown as AgentSideConnection;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a mock AgentManager that yields the given streaming updates
|
|
61
|
+
* from its `prompt()` method.
|
|
62
|
+
*/
|
|
63
|
+
function createMockAgentManager(
|
|
64
|
+
promptUpdates: unknown[] = []
|
|
65
|
+
): AgentManager {
|
|
66
|
+
const mockAgent = createMockAgent();
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
spawn: vi.fn().mockResolvedValue({
|
|
70
|
+
id: "agent-new",
|
|
71
|
+
session_id: "session-new",
|
|
72
|
+
agent: createMockAgent({ id: "agent-new", session_id: "session-new" }),
|
|
73
|
+
session: {},
|
|
74
|
+
}),
|
|
75
|
+
get: vi.fn().mockReturnValue(mockAgent),
|
|
76
|
+
list: vi.fn().mockReturnValue([mockAgent]),
|
|
77
|
+
listHeadManagers: vi.fn().mockReturnValue([mockAgent]),
|
|
78
|
+
getChildren: vi.fn().mockReturnValue([]),
|
|
79
|
+
getHierarchy: vi.fn().mockReturnValue({
|
|
80
|
+
root: { agent: mockAgent, children: [] },
|
|
81
|
+
depth: 1,
|
|
82
|
+
totalAgents: 1,
|
|
83
|
+
}),
|
|
84
|
+
getOrCreateHeadManager: vi.fn().mockResolvedValue({
|
|
85
|
+
id: "agent-1",
|
|
86
|
+
session_id: "session-1",
|
|
87
|
+
agent: mockAgent,
|
|
88
|
+
session: {},
|
|
89
|
+
}),
|
|
90
|
+
hasActiveSession: vi.fn().mockReturnValue(true),
|
|
91
|
+
resume: vi.fn().mockResolvedValue({
|
|
92
|
+
id: "agent-1",
|
|
93
|
+
session_id: "session-1",
|
|
94
|
+
agent: mockAgent,
|
|
95
|
+
session: {},
|
|
96
|
+
}),
|
|
97
|
+
terminate: vi.fn().mockResolvedValue(undefined),
|
|
98
|
+
prompt: vi.fn().mockReturnValue({
|
|
99
|
+
[Symbol.asyncIterator]: async function* () {
|
|
100
|
+
for (const update of promptUpdates) {
|
|
101
|
+
yield update;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
getSession: vi.fn().mockReturnValue(null),
|
|
106
|
+
onLifecycleEvent: vi.fn().mockReturnValue(() => {}),
|
|
107
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
108
|
+
respondToPermission: vi.fn().mockReturnValue(true),
|
|
109
|
+
cancelPermission: vi.fn().mockReturnValue(true),
|
|
110
|
+
} as unknown as AgentManager;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createMockTaskManager(): TaskManager {
|
|
114
|
+
return {
|
|
115
|
+
get: vi.fn().mockReturnValue(createMockTask()),
|
|
116
|
+
list: vi.fn().mockReturnValue([createMockTask()]),
|
|
117
|
+
create: vi.fn().mockReturnValue(createMockTask()),
|
|
118
|
+
} as unknown as TaskManager;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─────────────────────────────────────────────────────────────────
|
|
122
|
+
// Tests
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
describe("_macro/getHistory", () => {
|
|
126
|
+
let eventStore: EventStore;
|
|
127
|
+
let macroAgent: MacroAgent;
|
|
128
|
+
let mockConnection: AgentSideConnection;
|
|
129
|
+
|
|
130
|
+
afterEach(async () => {
|
|
131
|
+
await eventStore.close();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Helper to set up a MacroAgent with given prompt streaming updates.
|
|
136
|
+
*/
|
|
137
|
+
async function setup(promptUpdates: unknown[] = []) {
|
|
138
|
+
eventStore = await createEventStore({ inMemory: true });
|
|
139
|
+
mockConnection = createMockConnection();
|
|
140
|
+
|
|
141
|
+
const agentManager = createMockAgentManager(promptUpdates);
|
|
142
|
+
const taskManager = createMockTaskManager();
|
|
143
|
+
|
|
144
|
+
macroAgent = new MacroAgent(mockConnection, {
|
|
145
|
+
agentManager,
|
|
146
|
+
eventStore,
|
|
147
|
+
taskManager,
|
|
148
|
+
defaultCwd: "/test/cwd",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await macroAgent.initialize({
|
|
152
|
+
protocolVersion: 1,
|
|
153
|
+
clientCapabilities: {},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return { agentManager, taskManager };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
it("should return empty turns for a session with no history", async () => {
|
|
160
|
+
await setup();
|
|
161
|
+
|
|
162
|
+
// Create a session so it has a conversation
|
|
163
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
164
|
+
const sessionId =
|
|
165
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
166
|
+
|
|
167
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
168
|
+
sessionId,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(response).toHaveProperty("turns");
|
|
172
|
+
expect((response as { turns: unknown[] }).turns).toEqual([]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should record and return user + assistant text turns after prompt", async () => {
|
|
176
|
+
await setup([
|
|
177
|
+
{
|
|
178
|
+
sessionUpdate: "agent_message_chunk",
|
|
179
|
+
content: { type: "text", text: "Hello " },
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
sessionUpdate: "agent_message_chunk",
|
|
183
|
+
content: { type: "text", text: "world!" },
|
|
184
|
+
},
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
// Create session
|
|
188
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
189
|
+
const sessionId =
|
|
190
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
191
|
+
|
|
192
|
+
// Send a prompt — this triggers recording
|
|
193
|
+
await macroAgent.prompt({
|
|
194
|
+
sessionId,
|
|
195
|
+
prompt: [{ type: "text", text: "Say hello" }],
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Retrieve history
|
|
199
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
200
|
+
sessionId,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const turns = (response as { turns: { role: string; content: unknown }[] })
|
|
204
|
+
.turns;
|
|
205
|
+
|
|
206
|
+
expect(turns).toHaveLength(2);
|
|
207
|
+
|
|
208
|
+
// User turn
|
|
209
|
+
expect(turns[0].role).toBe("user");
|
|
210
|
+
expect(turns[0].content).toBe("Say hello");
|
|
211
|
+
|
|
212
|
+
// Assistant turn — accumulated text chunks
|
|
213
|
+
expect(turns[1].role).toBe("assistant");
|
|
214
|
+
const content = turns[1].content as { parts: { type: string; text?: string }[] };
|
|
215
|
+
expect(content.parts[0].type).toBe("text");
|
|
216
|
+
expect(content.parts[0].text).toBe("Hello world!");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should record tool calls in assistant turns", async () => {
|
|
220
|
+
await setup([
|
|
221
|
+
{
|
|
222
|
+
sessionUpdate: "agent_message_chunk",
|
|
223
|
+
content: { type: "text", text: "Let me check that." },
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
sessionUpdate: "tool_call",
|
|
227
|
+
toolCallId: "tc-1",
|
|
228
|
+
title: "Read file",
|
|
229
|
+
status: "completed",
|
|
230
|
+
rawInput: { path: "/test.txt" },
|
|
231
|
+
output: "file contents here",
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
sessionUpdate: "agent_message_chunk",
|
|
235
|
+
content: { type: "text", text: " Done!" },
|
|
236
|
+
},
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
240
|
+
const sessionId =
|
|
241
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
242
|
+
|
|
243
|
+
await macroAgent.prompt({
|
|
244
|
+
sessionId,
|
|
245
|
+
prompt: [{ type: "text", text: "Read test.txt" }],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
249
|
+
sessionId,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const turns = (response as { turns: { role: string; content: unknown }[] })
|
|
253
|
+
.turns;
|
|
254
|
+
|
|
255
|
+
expect(turns).toHaveLength(2);
|
|
256
|
+
|
|
257
|
+
// Assistant turn should have text + tool parts
|
|
258
|
+
const assistantContent = turns[1].content as {
|
|
259
|
+
parts: { type: string; text?: string; toolCallId?: string; title?: string; output?: unknown }[];
|
|
260
|
+
};
|
|
261
|
+
expect(assistantContent.parts).toHaveLength(2);
|
|
262
|
+
expect(assistantContent.parts[0]).toEqual({
|
|
263
|
+
type: "text",
|
|
264
|
+
text: "Let me check that. Done!",
|
|
265
|
+
});
|
|
266
|
+
expect(assistantContent.parts[1]).toMatchObject({
|
|
267
|
+
type: "tool",
|
|
268
|
+
toolCallId: "tc-1",
|
|
269
|
+
title: "Read file",
|
|
270
|
+
status: "completed",
|
|
271
|
+
output: "file contents here",
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should accumulate history across multiple prompts", async () => {
|
|
276
|
+
// First prompt returns "Hello"
|
|
277
|
+
const { agentManager } = await setup([
|
|
278
|
+
{
|
|
279
|
+
sessionUpdate: "agent_message_chunk",
|
|
280
|
+
content: { type: "text", text: "Hello" },
|
|
281
|
+
},
|
|
282
|
+
]);
|
|
283
|
+
|
|
284
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
285
|
+
const sessionId =
|
|
286
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
287
|
+
|
|
288
|
+
// First prompt
|
|
289
|
+
await macroAgent.prompt({
|
|
290
|
+
sessionId,
|
|
291
|
+
prompt: [{ type: "text", text: "Hi" }],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Second prompt — update mock to return different content
|
|
295
|
+
vi.mocked(agentManager.prompt).mockReturnValue({
|
|
296
|
+
[Symbol.asyncIterator]: async function* () {
|
|
297
|
+
yield {
|
|
298
|
+
sessionUpdate: "agent_message_chunk",
|
|
299
|
+
content: { type: "text", text: "Goodbye" },
|
|
300
|
+
};
|
|
301
|
+
},
|
|
302
|
+
} as any);
|
|
303
|
+
|
|
304
|
+
await macroAgent.prompt({
|
|
305
|
+
sessionId,
|
|
306
|
+
prompt: [{ type: "text", text: "Bye" }],
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
310
|
+
sessionId,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const turns = (response as { turns: { role: string; content: unknown }[] })
|
|
314
|
+
.turns;
|
|
315
|
+
|
|
316
|
+
// 2 prompts × 2 turns each = 4 turns total
|
|
317
|
+
expect(turns).toHaveLength(4);
|
|
318
|
+
expect(turns[0].role).toBe("user");
|
|
319
|
+
expect(turns[0].content).toBe("Hi");
|
|
320
|
+
expect(turns[1].role).toBe("assistant");
|
|
321
|
+
expect(turns[2].role).toBe("user");
|
|
322
|
+
expect(turns[2].content).toBe("Bye");
|
|
323
|
+
expect(turns[3].role).toBe("assistant");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("should respect the limit parameter", async () => {
|
|
327
|
+
const { agentManager } = await setup([
|
|
328
|
+
{
|
|
329
|
+
sessionUpdate: "agent_message_chunk",
|
|
330
|
+
content: { type: "text", text: "Response 1" },
|
|
331
|
+
},
|
|
332
|
+
]);
|
|
333
|
+
|
|
334
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
335
|
+
const sessionId =
|
|
336
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
337
|
+
|
|
338
|
+
// Send 3 prompts
|
|
339
|
+
for (let i = 0; i < 3; i++) {
|
|
340
|
+
vi.mocked(agentManager.prompt).mockReturnValue({
|
|
341
|
+
[Symbol.asyncIterator]: async function* () {
|
|
342
|
+
yield {
|
|
343
|
+
sessionUpdate: "agent_message_chunk",
|
|
344
|
+
content: { type: "text", text: `Response ${i + 1}` },
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
} as any);
|
|
348
|
+
|
|
349
|
+
await macroAgent.prompt({
|
|
350
|
+
sessionId,
|
|
351
|
+
prompt: [{ type: "text", text: `Message ${i + 1}` }],
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Request only 2 turns
|
|
356
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
357
|
+
sessionId,
|
|
358
|
+
limit: 2,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const turns = (response as { turns: unknown[] }).turns;
|
|
362
|
+
expect(turns).toHaveLength(2);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("should not record turns when prompt has no text content", async () => {
|
|
366
|
+
await setup([
|
|
367
|
+
{
|
|
368
|
+
sessionUpdate: "agent_message_chunk",
|
|
369
|
+
content: { type: "text", text: "Response" },
|
|
370
|
+
},
|
|
371
|
+
]);
|
|
372
|
+
|
|
373
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
374
|
+
const sessionId =
|
|
375
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
376
|
+
|
|
377
|
+
// Empty prompt — no text blocks
|
|
378
|
+
await macroAgent.prompt({
|
|
379
|
+
sessionId,
|
|
380
|
+
prompt: [],
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
384
|
+
sessionId,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const turns = (response as { turns: { role: string }[] }).turns;
|
|
388
|
+
|
|
389
|
+
// Should only have the assistant turn (no user turn since message was empty)
|
|
390
|
+
expect(turns).toHaveLength(1);
|
|
391
|
+
expect(turns[0].role).toBe("assistant");
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("should only record completed tool calls, not running ones", async () => {
|
|
395
|
+
await setup([
|
|
396
|
+
{
|
|
397
|
+
sessionUpdate: "tool_call",
|
|
398
|
+
toolCallId: "tc-running",
|
|
399
|
+
title: "Running tool",
|
|
400
|
+
status: "running",
|
|
401
|
+
rawInput: {},
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
sessionUpdate: "tool_call",
|
|
405
|
+
toolCallId: "tc-done",
|
|
406
|
+
title: "Done tool",
|
|
407
|
+
status: "completed",
|
|
408
|
+
rawInput: { x: 1 },
|
|
409
|
+
output: "result",
|
|
410
|
+
},
|
|
411
|
+
]);
|
|
412
|
+
|
|
413
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
414
|
+
const sessionId =
|
|
415
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
416
|
+
|
|
417
|
+
await macroAgent.prompt({
|
|
418
|
+
sessionId,
|
|
419
|
+
prompt: [{ type: "text", text: "Run tools" }],
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
423
|
+
sessionId,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const turns = (response as { turns: { role: string; content: unknown }[] })
|
|
427
|
+
.turns;
|
|
428
|
+
|
|
429
|
+
const assistantContent = turns[1].content as {
|
|
430
|
+
parts: { type: string; toolCallId?: string }[];
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// Only the completed tool call should be recorded
|
|
434
|
+
const toolParts = assistantContent.parts.filter((p) => p.type === "tool");
|
|
435
|
+
expect(toolParts).toHaveLength(1);
|
|
436
|
+
expect(toolParts[0].toolCallId).toBe("tc-done");
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("should return turns ordered by timestamp (ascending)", async () => {
|
|
440
|
+
const { agentManager } = await setup([
|
|
441
|
+
{
|
|
442
|
+
sessionUpdate: "agent_message_chunk",
|
|
443
|
+
content: { type: "text", text: "First" },
|
|
444
|
+
},
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
448
|
+
const sessionId =
|
|
449
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
450
|
+
|
|
451
|
+
await macroAgent.prompt({
|
|
452
|
+
sessionId,
|
|
453
|
+
prompt: [{ type: "text", text: "Q1" }],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Small delay to ensure distinct timestamps
|
|
457
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
458
|
+
|
|
459
|
+
vi.mocked(agentManager.prompt).mockReturnValue({
|
|
460
|
+
[Symbol.asyncIterator]: async function* () {
|
|
461
|
+
yield {
|
|
462
|
+
sessionUpdate: "agent_message_chunk",
|
|
463
|
+
content: { type: "text", text: "Second" },
|
|
464
|
+
};
|
|
465
|
+
},
|
|
466
|
+
} as any);
|
|
467
|
+
|
|
468
|
+
await macroAgent.prompt({
|
|
469
|
+
sessionId,
|
|
470
|
+
prompt: [{ type: "text", text: "Q2" }],
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
474
|
+
sessionId,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const turns = (
|
|
478
|
+
response as { turns: { timestamp: number; content: unknown }[] }
|
|
479
|
+
).turns;
|
|
480
|
+
|
|
481
|
+
// Verify timestamps are in ascending order
|
|
482
|
+
for (let i = 1; i < turns.length; i++) {
|
|
483
|
+
expect(turns[i].timestamp).toBeGreaterThanOrEqual(turns[i - 1].timestamp);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("should isolate history between different sessions", async () => {
|
|
488
|
+
await setup([
|
|
489
|
+
{
|
|
490
|
+
sessionUpdate: "agent_message_chunk",
|
|
491
|
+
content: { type: "text", text: "Session 1 response" },
|
|
492
|
+
},
|
|
493
|
+
]);
|
|
494
|
+
|
|
495
|
+
// Create first session and prompt
|
|
496
|
+
await macroAgent.newSession({ cwd: "/test" });
|
|
497
|
+
const session1Id =
|
|
498
|
+
macroAgent.getSessionMapper().getAllMappings()[0]?.acpSessionId;
|
|
499
|
+
|
|
500
|
+
await macroAgent.prompt({
|
|
501
|
+
sessionId: session1Id,
|
|
502
|
+
prompt: [{ type: "text", text: "Session 1 message" }],
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Create second session
|
|
506
|
+
await macroAgent.newSession({ cwd: "/test2" });
|
|
507
|
+
const allMappings = macroAgent.getSessionMapper().getAllMappings();
|
|
508
|
+
const session2Id = allMappings.find(
|
|
509
|
+
(m) => m.acpSessionId !== session1Id
|
|
510
|
+
)?.acpSessionId;
|
|
511
|
+
|
|
512
|
+
// Session 2 should have no history
|
|
513
|
+
const response = await macroAgent.extMethod("_macro/getHistory", {
|
|
514
|
+
sessionId: session2Id,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
const turns = (response as { turns: unknown[] }).turns;
|
|
518
|
+
expect(turns).toHaveLength(0);
|
|
519
|
+
|
|
520
|
+
// Session 1 should still have its history
|
|
521
|
+
const response1 = await macroAgent.extMethod("_macro/getHistory", {
|
|
522
|
+
sessionId: session1Id,
|
|
523
|
+
});
|
|
524
|
+
expect((response1 as { turns: unknown[] }).turns).toHaveLength(2);
|
|
525
|
+
});
|
|
526
|
+
});
|
|
@@ -372,7 +372,8 @@ describe("ACP Mode Integration", () => {
|
|
|
372
372
|
expect(extensions).toContain("_macro/respondToPermission");
|
|
373
373
|
expect(extensions).toContain("_macro/cancelPermission");
|
|
374
374
|
expect(extensions).toContain("_macro/resume");
|
|
375
|
-
expect(extensions
|
|
375
|
+
expect(extensions).toContain("_macro/getHistory");
|
|
376
|
+
expect(extensions?.length).toBe(17);
|
|
376
377
|
|
|
377
378
|
expect(initResponse.agentCapabilities?._meta?.agentType).toBe(
|
|
378
379
|
"macro-agent"
|
package/src/acp/index.ts
CHANGED