1import { ProviderDriverKind, RuntimeRequestId, TurnId } from "@t3tools/contracts";
2import { describe, expect, it } from "vite-plus/test";
3
4import {
5 makeAcpAssistantItemEvent,
6 makeAcpContentDeltaEvent,
7 makeAcpPlanUpdatedEvent,
8 makeAcpRequestOpenedEvent,
9 makeAcpRequestResolvedEvent,
10 makeAcpToolCallEvent,
11} from "./coreRuntimeEvents.ts";
12
13describe("AcpCoreRuntimeEvents", () => {
14 it("maps ACP permission requests to canonical runtime events", () => {
15 const stamp = { eventId: "event-1" as never, createdAt: "2026-03-27T00:00:00.000Z" };
16 const turnId = TurnId.make("turn-1");
17 const permissionRequest = {
18 kind: "execute" as const,
19 detail: "cat package.json",
20 toolCall: {
21 toolCallId: "tool-1",
22 kind: "execute",
23 status: "pending" as const,
24 command: "cat package.json",
25 detail: "cat package.json",
26 data: { toolCallId: "tool-1", kind: "execute" },
27 },
28 };
29
30 const openedEvent = makeAcpRequestOpenedEvent({
31 stamp,
32 provider: ProviderDriverKind.make("cursor"),
33 threadId: "thread-1" as never,
34 turnId,
35 requestId: RuntimeRequestId.make("request-1"),
36 permissionRequest,
37 detail: "cat package.json",
38 args: { command: ["cat", "package.json"] },
39 source: "acp.jsonrpc",
40 method: "session/request_permission",
41 rawPayload: { sessionId: "session-1" },
42 });
43 expect(openedEvent).toMatchObject({
44 type: "request.opened",
45 payload: {
46 requestType: "exec_command_approval",
47 detail: "cat package.json",
48 },
49 });
50 expect(openedEvent).not.toHaveProperty("payload.options");
51
52 expect(
53 makeAcpRequestResolvedEvent({
54 stamp,
55 provider: ProviderDriverKind.make("cursor"),
56 threadId: "thread-1" as never,
57 turnId,
58 requestId: RuntimeRequestId.make("request-1"),
59 permissionRequest,
60 decision: "accept",
61 }),
62 ).toMatchObject({
63 type: "request.resolved",
64 payload: {
65 requestType: "exec_command_approval",
66 decision: "accept",
67 },
68 });
69 });
70
71 it("preserves a native file approval without a remembered-allow choice", () => {
72 const event = makeAcpRequestOpenedEvent({
73 stamp: { eventId: "approval-1" as never, createdAt: "2026-09-02T00:00:00.000Z" },
74 provider: ProviderDriverKind.make("antigravity"),
75 threadId: "thread-1" as never,
76 turnId: TurnId.make("turn-1"),
77 requestId: RuntimeRequestId.make("request-1"),
78 permissionRequest: { kind: "edit" },
79 approvalOptions: [
80 { decision: "accept", label: "Allow once" },
81 { decision: "decline", label: "Reject" },
82 ],
83 detail: "Edit package.json",
84 args: {},
85 source: "acp.jsonrpc",
86 method: "session/request_permission",
87 rawPayload: { sessionId: "session-1" },
88 });
89
90 expect(event.payload).toEqual({
91 requestType: "file_change_approval",
92 detail: "Edit package.json",
93 args: {},
94 options: [
95 { decision: "accept", label: "Allow once" },
96 { decision: "decline", label: "Reject" },
97 ],
98 });
99 });
100
101 it("maps generic ACP permission kinds to dynamic tool approvals", () => {
102 const stamp = { eventId: "event-1" as never, createdAt: "2026-03-27T00:00:00.000Z" };
103
104 for (const kind of ["search", "fetch", "other", "unknown", "future-tool-kind"]) {
105 const permissionRequest = { kind };
106 const request = {
107 stamp,
108 provider: ProviderDriverKind.make("cursor"),
109 threadId: "thread-1" as never,
110 turnId: TurnId.make("turn-1"),
111 requestId: RuntimeRequestId.make(`request-${kind}`),
112 permissionRequest,
113 };
114
115 expect(
116 makeAcpRequestOpenedEvent({
117 ...request,
118 detail: kind,
119 args: {},
120 source: "acp.jsonrpc",
121 method: "session/request_permission",
122 rawPayload: { sessionId: "session-1" },
123 }),
124 ).toMatchObject({
125 type: "request.opened",
126 payload: { requestType: "dynamic_tool_call" },
127 });
128
129 expect(
130 makeAcpRequestResolvedEvent({
131 ...request,
132 decision: "accept",
133 }),
134 ).toMatchObject({
135 type: "request.resolved",
136 payload: { requestType: "dynamic_tool_call" },
137 });
138 }
139 });
140
141 it("maps ACP core plan, tool-call, and content updates", () => {
142 const stamp = { eventId: "event-1" as never, createdAt: "2026-03-27T00:00:00.000Z" };
143 const turnId = TurnId.make("turn-1");
144
145 expect(
146 makeAcpPlanUpdatedEvent({
147 stamp,
148 provider: ProviderDriverKind.make("cursor"),
149 threadId: "thread-1" as never,
150 turnId,
151 payload: {
152 nativePlanId: "plan-1",
153 kind: "items",
154 plan: [{ step: "Inspect state", status: "inProgress" }],
155 },
156 source: "acp.cursor.extension",
157 method: "cursor/update_todos",
158 rawPayload: { todos: [] },
159 }),
160 ).toMatchObject({
161 type: "turn.plan.updated",
162 raw: {
163 method: "cursor/update_todos",
164 },
165 });
166
167 expect(
168 makeAcpToolCallEvent({
169 stamp,
170 provider: ProviderDriverKind.make("cursor"),
171 threadId: "thread-1" as never,
172 turnId,
173 toolCall: {
174 toolCallId: "tool-1",
175 kind: "execute",
176 status: "completed",
177 title: "Terminal",
178 detail: "bun run test",
179 data: { command: "bun run test" },
180 },
181 rawPayload: { sessionId: "session-1" },
182 }),
183 ).toMatchObject({
184 type: "item.completed",
185 payload: {
186 itemType: "command_execution",
187 status: "completed",
188 },
189 });
190
191 expect(
192 makeAcpContentDeltaEvent({
193 stamp,
194 provider: ProviderDriverKind.make("cursor"),
195 threadId: "thread-1" as never,
196 turnId,
197 itemId: "assistant:session-1:segment:0",
198 text: "hello",
199 rawPayload: { sessionId: "session-1" },
200 }),
201 ).toMatchObject({
202 type: "content.delta",
203 itemId: "assistant:session-1:segment:0",
204 payload: {
205 delta: "hello",
206 },
207 });
208
209 expect(
210 makeAcpAssistantItemEvent({
211 stamp,
212 provider: ProviderDriverKind.make("cursor"),
213 threadId: "thread-1" as never,
214 turnId,
215 itemId: "assistant:session-1:segment:0",
216 lifecycle: "item.started",
217 }),
218 ).toMatchObject({
219 type: "item.started",
220 itemId: "assistant:session-1:segment:0",
221 payload: {
222 itemType: "assistant_message",
223 status: "inProgress",
224 },
225 });
226 });
227
228 it("maps thoughts to the reasoning stream", () => {
229 expect(
230 makeAcpContentDeltaEvent({
231 stamp: { eventId: "thought-1" as never, createdAt: "2026-09-02T00:00:00.000Z" },
232 provider: ProviderDriverKind.make("cursor"),
233 threadId: "thread-1" as never,
234 turnId: TurnId.make("turn-1"),
235 streamKind: "reasoning_text",
236 text: "Inspect the current implementation first.",
237 rawPayload: { sessionId: "session-1" },
238 }),
239 ).toMatchObject({
240 type: "content.delta",
241 payload: {
242 streamKind: "reasoning_text",
243 delta: "Inspect the current implementation first.",
244 },
245 });
246 });
247});