leduo-patrol 2.2.1 → 2.2.4

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 (179) hide show
  1. package/README.md +11 -2
  2. package/dist/server/__tests__/acp-session.test.js +115 -0
  3. package/dist/server/__tests__/activity-monitor.test.js +13 -1
  4. package/dist/server/__tests__/session-manager.test.js +380 -1
  5. package/dist/server/acp-session.js +476 -0
  6. package/dist/server/activity-monitor.js +22 -7
  7. package/dist/server/index.js +57 -1
  8. package/dist/server/session-manager.js +1301 -121
  9. package/dist/web/assets/index-Bll9nc_X.js +21 -0
  10. package/dist/web/assets/index-y1qgSOLv.css +1 -0
  11. package/dist/web/index.html +2 -2
  12. package/package.json +3 -1
  13. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/LICENSE +191 -0
  14. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/README.md +53 -0
  15. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.d.ts +823 -0
  16. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.js +965 -0
  17. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.js.map +1 -0
  18. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.test.d.ts +1 -0
  19. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.test.js +839 -0
  20. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/acp.test.js.map +1 -0
  21. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/agent.d.ts +2 -0
  22. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js +225 -0
  23. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/agent.js.map +1 -0
  24. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/client.d.ts +2 -0
  25. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/client.js +130 -0
  26. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/examples/client.js.map +1 -0
  27. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.d.ts +35 -0
  28. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js +5 -0
  29. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/jsonrpc.js.map +1 -0
  30. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/index.d.ts +27 -0
  31. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/index.js +28 -0
  32. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/index.js.map +1 -0
  33. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.d.ts +2870 -0
  34. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js +3 -0
  35. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/types.gen.js.map +1 -0
  36. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.d.ts +5333 -0
  37. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js +1554 -0
  38. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/schema/zod.gen.js.map +1 -0
  39. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/stream.d.ts +24 -0
  40. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/stream.js +64 -0
  41. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/dist/stream.js.map +1 -0
  42. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/package.json +66 -0
  43. package/vendor/claude-code-acp/node_modules/@agentclientprotocol/sdk/schema/schema.json +4125 -0
  44. package/vendor/claude-code-acp/node_modules/@types/node/LICENSE +21 -0
  45. package/vendor/claude-code-acp/node_modules/@types/node/README.md +15 -0
  46. package/vendor/claude-code-acp/node_modules/@types/node/assert/strict.d.ts +105 -0
  47. package/vendor/claude-code-acp/node_modules/@types/node/assert.d.ts +955 -0
  48. package/vendor/claude-code-acp/node_modules/@types/node/async_hooks.d.ts +623 -0
  49. package/vendor/claude-code-acp/node_modules/@types/node/buffer.buffer.d.ts +466 -0
  50. package/vendor/claude-code-acp/node_modules/@types/node/buffer.d.ts +1810 -0
  51. package/vendor/claude-code-acp/node_modules/@types/node/child_process.d.ts +1428 -0
  52. package/vendor/claude-code-acp/node_modules/@types/node/cluster.d.ts +486 -0
  53. package/vendor/claude-code-acp/node_modules/@types/node/compatibility/iterators.d.ts +21 -0
  54. package/vendor/claude-code-acp/node_modules/@types/node/console.d.ts +151 -0
  55. package/vendor/claude-code-acp/node_modules/@types/node/constants.d.ts +20 -0
  56. package/vendor/claude-code-acp/node_modules/@types/node/crypto.d.ts +4065 -0
  57. package/vendor/claude-code-acp/node_modules/@types/node/dgram.d.ts +564 -0
  58. package/vendor/claude-code-acp/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
  59. package/vendor/claude-code-acp/node_modules/@types/node/dns/promises.d.ts +503 -0
  60. package/vendor/claude-code-acp/node_modules/@types/node/dns.d.ts +922 -0
  61. package/vendor/claude-code-acp/node_modules/@types/node/domain.d.ts +166 -0
  62. package/vendor/claude-code-acp/node_modules/@types/node/events.d.ts +1054 -0
  63. package/vendor/claude-code-acp/node_modules/@types/node/fs/promises.d.ts +1329 -0
  64. package/vendor/claude-code-acp/node_modules/@types/node/fs.d.ts +4676 -0
  65. package/vendor/claude-code-acp/node_modules/@types/node/globals.d.ts +150 -0
  66. package/vendor/claude-code-acp/node_modules/@types/node/globals.typedarray.d.ts +101 -0
  67. package/vendor/claude-code-acp/node_modules/@types/node/http.d.ts +2167 -0
  68. package/vendor/claude-code-acp/node_modules/@types/node/http2.d.ts +2480 -0
  69. package/vendor/claude-code-acp/node_modules/@types/node/https.d.ts +405 -0
  70. package/vendor/claude-code-acp/node_modules/@types/node/index.d.ts +115 -0
  71. package/vendor/claude-code-acp/node_modules/@types/node/inspector/promises.d.ts +41 -0
  72. package/vendor/claude-code-acp/node_modules/@types/node/inspector.d.ts +224 -0
  73. package/vendor/claude-code-acp/node_modules/@types/node/inspector.generated.d.ts +4226 -0
  74. package/vendor/claude-code-acp/node_modules/@types/node/module.d.ts +819 -0
  75. package/vendor/claude-code-acp/node_modules/@types/node/net.d.ts +933 -0
  76. package/vendor/claude-code-acp/node_modules/@types/node/os.d.ts +507 -0
  77. package/vendor/claude-code-acp/node_modules/@types/node/package.json +155 -0
  78. package/vendor/claude-code-acp/node_modules/@types/node/path/posix.d.ts +8 -0
  79. package/vendor/claude-code-acp/node_modules/@types/node/path/win32.d.ts +8 -0
  80. package/vendor/claude-code-acp/node_modules/@types/node/path.d.ts +187 -0
  81. package/vendor/claude-code-acp/node_modules/@types/node/perf_hooks.d.ts +643 -0
  82. package/vendor/claude-code-acp/node_modules/@types/node/process.d.ts +2161 -0
  83. package/vendor/claude-code-acp/node_modules/@types/node/punycode.d.ts +117 -0
  84. package/vendor/claude-code-acp/node_modules/@types/node/querystring.d.ts +152 -0
  85. package/vendor/claude-code-acp/node_modules/@types/node/quic.d.ts +910 -0
  86. package/vendor/claude-code-acp/node_modules/@types/node/readline/promises.d.ts +161 -0
  87. package/vendor/claude-code-acp/node_modules/@types/node/readline.d.ts +541 -0
  88. package/vendor/claude-code-acp/node_modules/@types/node/repl.d.ts +415 -0
  89. package/vendor/claude-code-acp/node_modules/@types/node/sea.d.ts +162 -0
  90. package/vendor/claude-code-acp/node_modules/@types/node/sqlite.d.ts +955 -0
  91. package/vendor/claude-code-acp/node_modules/@types/node/stream/consumers.d.ts +38 -0
  92. package/vendor/claude-code-acp/node_modules/@types/node/stream/promises.d.ts +211 -0
  93. package/vendor/claude-code-acp/node_modules/@types/node/stream/web.d.ts +296 -0
  94. package/vendor/claude-code-acp/node_modules/@types/node/stream.d.ts +1760 -0
  95. package/vendor/claude-code-acp/node_modules/@types/node/string_decoder.d.ts +67 -0
  96. package/vendor/claude-code-acp/node_modules/@types/node/test/reporters.d.ts +96 -0
  97. package/vendor/claude-code-acp/node_modules/@types/node/test.d.ts +2240 -0
  98. package/vendor/claude-code-acp/node_modules/@types/node/timers/promises.d.ts +108 -0
  99. package/vendor/claude-code-acp/node_modules/@types/node/timers.d.ts +159 -0
  100. package/vendor/claude-code-acp/node_modules/@types/node/tls.d.ts +1198 -0
  101. package/vendor/claude-code-acp/node_modules/@types/node/trace_events.d.ts +197 -0
  102. package/vendor/claude-code-acp/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +462 -0
  103. package/vendor/claude-code-acp/node_modules/@types/node/ts5.6/compatibility/float16array.d.ts +71 -0
  104. package/vendor/claude-code-acp/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +36 -0
  105. package/vendor/claude-code-acp/node_modules/@types/node/ts5.6/index.d.ts +117 -0
  106. package/vendor/claude-code-acp/node_modules/@types/node/ts5.7/compatibility/float16array.d.ts +72 -0
  107. package/vendor/claude-code-acp/node_modules/@types/node/ts5.7/index.d.ts +117 -0
  108. package/vendor/claude-code-acp/node_modules/@types/node/tty.d.ts +250 -0
  109. package/vendor/claude-code-acp/node_modules/@types/node/url.d.ts +519 -0
  110. package/vendor/claude-code-acp/node_modules/@types/node/util/types.d.ts +558 -0
  111. package/vendor/claude-code-acp/node_modules/@types/node/util.d.ts +1662 -0
  112. package/vendor/claude-code-acp/node_modules/@types/node/v8.d.ts +983 -0
  113. package/vendor/claude-code-acp/node_modules/@types/node/vm.d.ts +1208 -0
  114. package/vendor/claude-code-acp/node_modules/@types/node/wasi.d.ts +202 -0
  115. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/abortcontroller.d.ts +59 -0
  116. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/blob.d.ts +23 -0
  117. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/console.d.ts +9 -0
  118. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/crypto.d.ts +39 -0
  119. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
  120. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/encoding.d.ts +11 -0
  121. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/events.d.ts +106 -0
  122. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/fetch.d.ts +69 -0
  123. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/importmeta.d.ts +13 -0
  124. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/messaging.d.ts +23 -0
  125. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/navigator.d.ts +25 -0
  126. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/performance.d.ts +45 -0
  127. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/storage.d.ts +24 -0
  128. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/streams.d.ts +115 -0
  129. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/timers.d.ts +44 -0
  130. package/vendor/claude-code-acp/node_modules/@types/node/web-globals/url.d.ts +24 -0
  131. package/vendor/claude-code-acp/node_modules/@types/node/worker_threads.d.ts +717 -0
  132. package/vendor/claude-code-acp/node_modules/@types/node/zlib.d.ts +618 -0
  133. package/vendor/claude-code-acp/node_modules/undici-types/LICENSE +21 -0
  134. package/vendor/claude-code-acp/node_modules/undici-types/README.md +6 -0
  135. package/vendor/claude-code-acp/node_modules/undici-types/agent.d.ts +32 -0
  136. package/vendor/claude-code-acp/node_modules/undici-types/api.d.ts +43 -0
  137. package/vendor/claude-code-acp/node_modules/undici-types/balanced-pool.d.ts +29 -0
  138. package/vendor/claude-code-acp/node_modules/undici-types/cache-interceptor.d.ts +172 -0
  139. package/vendor/claude-code-acp/node_modules/undici-types/cache.d.ts +36 -0
  140. package/vendor/claude-code-acp/node_modules/undici-types/client-stats.d.ts +15 -0
  141. package/vendor/claude-code-acp/node_modules/undici-types/client.d.ts +108 -0
  142. package/vendor/claude-code-acp/node_modules/undici-types/connector.d.ts +34 -0
  143. package/vendor/claude-code-acp/node_modules/undici-types/content-type.d.ts +21 -0
  144. package/vendor/claude-code-acp/node_modules/undici-types/cookies.d.ts +30 -0
  145. package/vendor/claude-code-acp/node_modules/undici-types/diagnostics-channel.d.ts +74 -0
  146. package/vendor/claude-code-acp/node_modules/undici-types/dispatcher.d.ts +276 -0
  147. package/vendor/claude-code-acp/node_modules/undici-types/env-http-proxy-agent.d.ts +22 -0
  148. package/vendor/claude-code-acp/node_modules/undici-types/errors.d.ts +161 -0
  149. package/vendor/claude-code-acp/node_modules/undici-types/eventsource.d.ts +66 -0
  150. package/vendor/claude-code-acp/node_modules/undici-types/fetch.d.ts +211 -0
  151. package/vendor/claude-code-acp/node_modules/undici-types/formdata.d.ts +108 -0
  152. package/vendor/claude-code-acp/node_modules/undici-types/global-dispatcher.d.ts +9 -0
  153. package/vendor/claude-code-acp/node_modules/undici-types/global-origin.d.ts +7 -0
  154. package/vendor/claude-code-acp/node_modules/undici-types/h2c-client.d.ts +73 -0
  155. package/vendor/claude-code-acp/node_modules/undici-types/handlers.d.ts +15 -0
  156. package/vendor/claude-code-acp/node_modules/undici-types/header.d.ts +160 -0
  157. package/vendor/claude-code-acp/node_modules/undici-types/index.d.ts +80 -0
  158. package/vendor/claude-code-acp/node_modules/undici-types/interceptors.d.ts +39 -0
  159. package/vendor/claude-code-acp/node_modules/undici-types/mock-agent.d.ts +68 -0
  160. package/vendor/claude-code-acp/node_modules/undici-types/mock-call-history.d.ts +111 -0
  161. package/vendor/claude-code-acp/node_modules/undici-types/mock-client.d.ts +27 -0
  162. package/vendor/claude-code-acp/node_modules/undici-types/mock-errors.d.ts +12 -0
  163. package/vendor/claude-code-acp/node_modules/undici-types/mock-interceptor.d.ts +94 -0
  164. package/vendor/claude-code-acp/node_modules/undici-types/mock-pool.d.ts +27 -0
  165. package/vendor/claude-code-acp/node_modules/undici-types/package.json +55 -0
  166. package/vendor/claude-code-acp/node_modules/undici-types/patch.d.ts +29 -0
  167. package/vendor/claude-code-acp/node_modules/undici-types/pool-stats.d.ts +19 -0
  168. package/vendor/claude-code-acp/node_modules/undici-types/pool.d.ts +41 -0
  169. package/vendor/claude-code-acp/node_modules/undici-types/proxy-agent.d.ts +29 -0
  170. package/vendor/claude-code-acp/node_modules/undici-types/readable.d.ts +68 -0
  171. package/vendor/claude-code-acp/node_modules/undici-types/retry-agent.d.ts +8 -0
  172. package/vendor/claude-code-acp/node_modules/undici-types/retry-handler.d.ts +125 -0
  173. package/vendor/claude-code-acp/node_modules/undici-types/snapshot-agent.d.ts +109 -0
  174. package/vendor/claude-code-acp/node_modules/undici-types/util.d.ts +18 -0
  175. package/vendor/claude-code-acp/node_modules/undici-types/utility.d.ts +7 -0
  176. package/vendor/claude-code-acp/node_modules/undici-types/webidl.d.ts +341 -0
  177. package/vendor/claude-code-acp/node_modules/undici-types/websocket.d.ts +186 -0
  178. package/dist/web/assets/index-B5Dh2E8j.css +0 -1
  179. package/dist/web/assets/index-xPPPaEde.js +0 -13
@@ -0,0 +1,839 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { ClientSideConnection, AgentSideConnection, PROTOCOL_VERSION, ndJsonStream, } from "./acp.js";
3
+ describe("Connection", () => {
4
+ let clientToAgent;
5
+ let agentToClient;
6
+ beforeEach(() => {
7
+ clientToAgent = new TransformStream();
8
+ agentToClient = new TransformStream();
9
+ });
10
+ it("handles errors in bidirectional communication", async () => {
11
+ // Create client that throws errors
12
+ class TestClient {
13
+ async writeTextFile(_) {
14
+ throw new Error("Write failed");
15
+ }
16
+ async readTextFile(_) {
17
+ throw new Error("Read failed");
18
+ }
19
+ async requestPermission(_) {
20
+ throw new Error("Permission denied");
21
+ }
22
+ async sessionUpdate(_) {
23
+ // no-op
24
+ }
25
+ }
26
+ // Create agent that throws errors
27
+ class TestAgent {
28
+ async initialize(_) {
29
+ throw new Error("Failed to initialize");
30
+ }
31
+ async newSession(_) {
32
+ throw new Error("Failed to create session");
33
+ }
34
+ async loadSession(_) {
35
+ throw new Error("Failed to load session");
36
+ }
37
+ async authenticate(_) {
38
+ throw new Error("Authentication failed");
39
+ }
40
+ async prompt(_) {
41
+ throw new Error("Prompt failed");
42
+ }
43
+ async cancel(_) {
44
+ // no-op
45
+ }
46
+ }
47
+ // Set up connections
48
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
49
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
50
+ // Test error handling in client->agent direction
51
+ await expect(clientConnection.writeTextFile({
52
+ path: "/test.txt",
53
+ content: "test",
54
+ sessionId: "test-session",
55
+ })).rejects.toThrow();
56
+ // Test error handling in agent->client direction
57
+ await expect(agentConnection.newSession({
58
+ cwd: "/test",
59
+ mcpServers: [],
60
+ })).rejects.toThrow();
61
+ });
62
+ it("handles concurrent requests", async () => {
63
+ let requestCount = 0;
64
+ // Create client
65
+ class TestClient {
66
+ async writeTextFile(_) {
67
+ requestCount++;
68
+ const currentCount = requestCount;
69
+ await new Promise((resolve) => setTimeout(resolve, 40));
70
+ console.log(`Write request ${currentCount} completed`);
71
+ return {};
72
+ }
73
+ async readTextFile(params) {
74
+ return { content: `Content of ${params.path}` };
75
+ }
76
+ async requestPermission(_) {
77
+ return {
78
+ outcome: {
79
+ outcome: "selected",
80
+ optionId: "allow",
81
+ },
82
+ };
83
+ }
84
+ async sessionUpdate(_) {
85
+ // no-op
86
+ }
87
+ }
88
+ // Create agent
89
+ class TestAgent {
90
+ async initialize(_) {
91
+ return {
92
+ protocolVersion: 1,
93
+ agentCapabilities: { loadSession: false },
94
+ authMethods: [],
95
+ };
96
+ }
97
+ async newSession(_) {
98
+ return {
99
+ sessionId: "test-session",
100
+ };
101
+ }
102
+ async loadSession(_) {
103
+ return {};
104
+ }
105
+ async authenticate(_) {
106
+ // no-op
107
+ }
108
+ async prompt(_) {
109
+ return { stopReason: "end_turn" };
110
+ }
111
+ async cancel(_) {
112
+ // no-op
113
+ }
114
+ }
115
+ // Set up connections
116
+ new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
117
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
118
+ // Send multiple concurrent requests
119
+ const promises = [
120
+ clientConnection.writeTextFile({
121
+ path: "/file1.txt",
122
+ content: "content1",
123
+ sessionId: "session1",
124
+ }),
125
+ clientConnection.writeTextFile({
126
+ path: "/file2.txt",
127
+ content: "content2",
128
+ sessionId: "session1",
129
+ }),
130
+ clientConnection.writeTextFile({
131
+ path: "/file3.txt",
132
+ content: "content3",
133
+ sessionId: "session1",
134
+ }),
135
+ ];
136
+ const results = await Promise.all(promises);
137
+ // Verify all requests completed successfully
138
+ expect(results).toHaveLength(3);
139
+ expect(results[0]).toEqual({});
140
+ expect(results[1]).toEqual({});
141
+ expect(results[2]).toEqual({});
142
+ expect(requestCount).toBe(3);
143
+ });
144
+ it("handles message ordering correctly", async () => {
145
+ const messageLog = [];
146
+ // Create client
147
+ class TestClient {
148
+ async writeTextFile(params) {
149
+ messageLog.push(`writeTextFile called: ${params.path}`);
150
+ return {};
151
+ }
152
+ async readTextFile(params) {
153
+ messageLog.push(`readTextFile called: ${params.path}`);
154
+ return { content: "test content" };
155
+ }
156
+ async requestPermission(params) {
157
+ messageLog.push(`requestPermission called: ${params.toolCall.title}`);
158
+ return {
159
+ outcome: {
160
+ outcome: "selected",
161
+ optionId: "allow",
162
+ },
163
+ };
164
+ }
165
+ async sessionUpdate(_params) {
166
+ messageLog.push("sessionUpdate called");
167
+ }
168
+ }
169
+ // Create agent
170
+ class TestAgent {
171
+ async initialize(_) {
172
+ return {
173
+ protocolVersion: 1,
174
+ agentCapabilities: { loadSession: false },
175
+ authMethods: [],
176
+ };
177
+ }
178
+ async newSession(request) {
179
+ messageLog.push(`newSession called: ${request.cwd}`);
180
+ return {
181
+ sessionId: "test-session",
182
+ };
183
+ }
184
+ async loadSession(params) {
185
+ messageLog.push(`loadSession called: ${params.sessionId}`);
186
+ return {};
187
+ }
188
+ async authenticate(params) {
189
+ messageLog.push(`authenticate called: ${params.methodId}`);
190
+ }
191
+ async prompt(params) {
192
+ messageLog.push(`prompt called: ${params.sessionId}`);
193
+ return { stopReason: "end_turn" };
194
+ }
195
+ async cancel(params) {
196
+ messageLog.push(`cancelled called: ${params.sessionId}`);
197
+ }
198
+ }
199
+ // Set up connections
200
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
201
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
202
+ // Send requests in specific order
203
+ await agentConnection.newSession({
204
+ cwd: "/test",
205
+ mcpServers: [],
206
+ });
207
+ await clientConnection.writeTextFile({
208
+ path: "/test.txt",
209
+ content: "test",
210
+ sessionId: "test-session",
211
+ });
212
+ await clientConnection.readTextFile({
213
+ path: "/test.txt",
214
+ sessionId: "test-session",
215
+ });
216
+ await clientConnection.requestPermission({
217
+ sessionId: "test-session",
218
+ toolCall: {
219
+ title: "Execute command",
220
+ kind: "execute",
221
+ status: "pending",
222
+ toolCallId: "tool-123",
223
+ content: [
224
+ {
225
+ type: "content",
226
+ content: {
227
+ type: "text",
228
+ text: "ls -la",
229
+ },
230
+ },
231
+ ],
232
+ },
233
+ options: [
234
+ {
235
+ kind: "allow_once",
236
+ name: "Allow",
237
+ optionId: "allow",
238
+ },
239
+ {
240
+ kind: "reject_once",
241
+ name: "Reject",
242
+ optionId: "reject",
243
+ },
244
+ ],
245
+ });
246
+ // Verify order
247
+ expect(messageLog).toEqual([
248
+ "newSession called: /test",
249
+ "writeTextFile called: /test.txt",
250
+ "readTextFile called: /test.txt",
251
+ "requestPermission called: Execute command",
252
+ ]);
253
+ });
254
+ it("handles notifications correctly", async () => {
255
+ const notificationLog = [];
256
+ // Create client
257
+ class TestClient {
258
+ async writeTextFile(_) {
259
+ return {};
260
+ }
261
+ async readTextFile(_) {
262
+ return { content: "test" };
263
+ }
264
+ async requestPermission(_) {
265
+ return {
266
+ outcome: {
267
+ outcome: "selected",
268
+ optionId: "allow",
269
+ },
270
+ };
271
+ }
272
+ async sessionUpdate(notification) {
273
+ if (notification.update &&
274
+ "sessionUpdate" in notification.update &&
275
+ notification.update.sessionUpdate === "agent_message_chunk") {
276
+ notificationLog.push(`agent message: ${notification.update.content.text}`);
277
+ }
278
+ }
279
+ }
280
+ // Create agent
281
+ class TestAgent {
282
+ async initialize(_) {
283
+ return {
284
+ protocolVersion: 1,
285
+ agentCapabilities: { loadSession: false },
286
+ authMethods: [],
287
+ };
288
+ }
289
+ async newSession(_) {
290
+ return {
291
+ sessionId: "test-session",
292
+ };
293
+ }
294
+ async loadSession(_) {
295
+ return {};
296
+ }
297
+ async authenticate(_) {
298
+ // no-op
299
+ }
300
+ async prompt(_) {
301
+ return { stopReason: "end_turn" };
302
+ }
303
+ async cancel(params) {
304
+ notificationLog.push(`cancelled: ${params.sessionId}`);
305
+ }
306
+ }
307
+ // Create shared instances
308
+ const testClient = () => new TestClient();
309
+ const testAgent = () => new TestAgent();
310
+ // Set up connections
311
+ const agentConnection = new ClientSideConnection(testClient, ndJsonStream(clientToAgent.writable, agentToClient.readable));
312
+ const clientConnection = new AgentSideConnection(testAgent, ndJsonStream(agentToClient.writable, clientToAgent.readable));
313
+ // Send notifications
314
+ await clientConnection.sessionUpdate({
315
+ sessionId: "test-session",
316
+ update: {
317
+ sessionUpdate: "agent_message_chunk",
318
+ content: {
319
+ type: "text",
320
+ text: "Hello from agent",
321
+ },
322
+ },
323
+ });
324
+ await agentConnection.cancel({
325
+ sessionId: "test-session",
326
+ });
327
+ // Wait a bit for async handlers
328
+ await new Promise((resolve) => setTimeout(resolve, 50));
329
+ // Verify notifications were received
330
+ expect(notificationLog).toContain("agent message: Hello from agent");
331
+ expect(notificationLog).toContain("cancelled: test-session");
332
+ });
333
+ it("handles initialize method", async () => {
334
+ // Create client
335
+ class TestClient {
336
+ async writeTextFile(_) {
337
+ return {};
338
+ }
339
+ async readTextFile(_) {
340
+ return { content: "test" };
341
+ }
342
+ async requestPermission(_) {
343
+ return {
344
+ outcome: {
345
+ outcome: "selected",
346
+ optionId: "allow",
347
+ },
348
+ };
349
+ }
350
+ async sessionUpdate(_) {
351
+ // no-op
352
+ }
353
+ }
354
+ // Create agent
355
+ class TestAgent {
356
+ async initialize(params) {
357
+ return {
358
+ protocolVersion: params.protocolVersion,
359
+ agentCapabilities: { loadSession: true },
360
+ authMethods: [
361
+ {
362
+ id: "oauth",
363
+ name: "OAuth",
364
+ description: "Authenticate with OAuth",
365
+ },
366
+ ],
367
+ };
368
+ }
369
+ async newSession(_) {
370
+ return { sessionId: "test-session" };
371
+ }
372
+ async loadSession(_) {
373
+ return {};
374
+ }
375
+ async authenticate(_) {
376
+ // no-op
377
+ }
378
+ async prompt(_) {
379
+ return { stopReason: "end_turn" };
380
+ }
381
+ async cancel(_) {
382
+ // no-op
383
+ }
384
+ }
385
+ // Set up connections
386
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
387
+ new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
388
+ // Test initialize request
389
+ const response = await agentConnection.initialize({
390
+ protocolVersion: PROTOCOL_VERSION,
391
+ clientCapabilities: {
392
+ fs: {
393
+ readTextFile: false,
394
+ writeTextFile: false,
395
+ },
396
+ },
397
+ });
398
+ expect(response.protocolVersion).toBe(PROTOCOL_VERSION);
399
+ expect(response.agentCapabilities?.loadSession).toBe(true);
400
+ expect(response.authMethods).toHaveLength(1);
401
+ expect(response.authMethods?.[0].id).toBe("oauth");
402
+ });
403
+ it("handles extension methods and notifications", async () => {
404
+ const extensionLog = [];
405
+ // Create client with extension method support
406
+ class TestClient {
407
+ async writeTextFile(_) {
408
+ return {};
409
+ }
410
+ async readTextFile(_) {
411
+ return { content: "test" };
412
+ }
413
+ async requestPermission(_) {
414
+ return {
415
+ outcome: {
416
+ outcome: "selected",
417
+ optionId: "allow",
418
+ },
419
+ };
420
+ }
421
+ async sessionUpdate(_) {
422
+ // no-op
423
+ }
424
+ async extMethod(method, params) {
425
+ if (method === "example.com/ping") {
426
+ return { response: "pong", params };
427
+ }
428
+ throw new Error(`Unknown method: ${method}`);
429
+ }
430
+ async extNotification(method, _params) {
431
+ extensionLog.push(`client extNotification: ${method}`);
432
+ }
433
+ }
434
+ // Create agent with extension method support
435
+ class TestAgent {
436
+ async initialize(_) {
437
+ return {
438
+ protocolVersion: PROTOCOL_VERSION,
439
+ agentCapabilities: { loadSession: false },
440
+ };
441
+ }
442
+ async newSession(_) {
443
+ return { sessionId: "test-session" };
444
+ }
445
+ async authenticate(_) {
446
+ // no-op
447
+ }
448
+ async prompt(_) {
449
+ return { stopReason: "end_turn" };
450
+ }
451
+ async cancel(_) {
452
+ // no-op
453
+ }
454
+ async extMethod(method, params) {
455
+ if (method === "example.com/echo") {
456
+ return { echo: params };
457
+ }
458
+ throw new Error(`Unknown method: ${method}`);
459
+ }
460
+ async extNotification(method, _params) {
461
+ extensionLog.push(`agent extNotification: ${method}`);
462
+ }
463
+ }
464
+ // Set up connections
465
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
466
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
467
+ // Test agent calling client extension method
468
+ const clientResponse = await clientConnection.extMethod("example.com/ping", {
469
+ data: "test",
470
+ });
471
+ expect(clientResponse).toEqual({
472
+ response: "pong",
473
+ params: { data: "test" },
474
+ });
475
+ // Test client calling agent extension method
476
+ const agentResponse = await agentConnection.extMethod("example.com/echo", {
477
+ message: "hello",
478
+ });
479
+ expect(agentResponse).toEqual({ echo: { message: "hello" } });
480
+ // Test extension notifications
481
+ await clientConnection.extNotification("example.com/client/notify", {
482
+ info: "client notification",
483
+ });
484
+ await agentConnection.extNotification("example.com/agent/notify", {
485
+ info: "agent notification",
486
+ });
487
+ // Wait a bit for async handlers
488
+ await new Promise((resolve) => setTimeout(resolve, 50));
489
+ // Verify notifications were logged
490
+ expect(extensionLog).toContain("client extNotification: example.com/client/notify");
491
+ expect(extensionLog).toContain("agent extNotification: example.com/agent/notify");
492
+ });
493
+ it("handles optional extension methods correctly", async () => {
494
+ // Create client WITHOUT extension methods
495
+ class TestClientWithoutExtensions {
496
+ async writeTextFile(_) {
497
+ return {};
498
+ }
499
+ async readTextFile(_) {
500
+ return { content: "test" };
501
+ }
502
+ async requestPermission(_) {
503
+ return {
504
+ outcome: {
505
+ outcome: "selected",
506
+ optionId: "allow",
507
+ },
508
+ };
509
+ }
510
+ async sessionUpdate(_) {
511
+ // no-op
512
+ }
513
+ }
514
+ // Create agent WITHOUT extension methods
515
+ class TestAgentWithoutExtensions {
516
+ async initialize(_) {
517
+ return {
518
+ protocolVersion: PROTOCOL_VERSION,
519
+ agentCapabilities: { loadSession: false },
520
+ };
521
+ }
522
+ async newSession(_) {
523
+ return { sessionId: "test-session" };
524
+ }
525
+ async authenticate(_) {
526
+ // no-op
527
+ }
528
+ async prompt(_) {
529
+ return { stopReason: "end_turn" };
530
+ }
531
+ async cancel(_) {
532
+ // no-op
533
+ }
534
+ }
535
+ // Set up connections
536
+ const agentConnection = new ClientSideConnection(() => new TestClientWithoutExtensions(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
537
+ const clientConnection = new AgentSideConnection(() => new TestAgentWithoutExtensions(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
538
+ // Test that calling extension methods on connections without them throws method not found
539
+ try {
540
+ await clientConnection.extMethod("_example.com/ping", { data: "test" });
541
+ expect.fail("Should have thrown method not found error");
542
+ }
543
+ catch (error) {
544
+ expect(error.code).toBe(-32601); // Method not found
545
+ expect(error.data.method).toBe("_example.com/ping");
546
+ }
547
+ try {
548
+ await agentConnection.extMethod("_example.com/echo", {
549
+ message: "hello",
550
+ });
551
+ expect.fail("Should have thrown method not found error");
552
+ }
553
+ catch (error) {
554
+ expect(error.code).toBe(-32601); // Method not found
555
+ expect(error.data.method).toBe("_example.com/echo");
556
+ }
557
+ // Notifications should be ignored when not implemented (no error thrown)
558
+ await clientConnection.extNotification("example.com/notify", {
559
+ info: "test",
560
+ });
561
+ await agentConnection.extNotification("example.com/notify", {
562
+ info: "test",
563
+ });
564
+ });
565
+ it("resolves closed promise when stream ends", async () => {
566
+ const closeLog = [];
567
+ // Create simple client and agent
568
+ class TestClient {
569
+ async writeTextFile(_) {
570
+ return {};
571
+ }
572
+ async readTextFile(_) {
573
+ return { content: "test" };
574
+ }
575
+ async requestPermission(_) {
576
+ return {
577
+ outcome: {
578
+ outcome: "selected",
579
+ optionId: "allow",
580
+ },
581
+ };
582
+ }
583
+ async sessionUpdate(_) {
584
+ // no-op
585
+ }
586
+ }
587
+ class TestAgent {
588
+ async initialize(_) {
589
+ return {
590
+ protocolVersion: PROTOCOL_VERSION,
591
+ agentCapabilities: { loadSession: false },
592
+ };
593
+ }
594
+ async newSession(_) {
595
+ return { sessionId: "test-session" };
596
+ }
597
+ async authenticate(_) {
598
+ // no-op
599
+ }
600
+ async prompt(_) {
601
+ return { stopReason: "end_turn" };
602
+ }
603
+ async cancel(_) {
604
+ // no-op
605
+ }
606
+ }
607
+ // Set up connections
608
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
609
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
610
+ // Listen for close via signal
611
+ agentConnection.signal.addEventListener("abort", () => {
612
+ closeLog.push("agent connection closed (signal)");
613
+ });
614
+ clientConnection.signal.addEventListener("abort", () => {
615
+ closeLog.push("client connection closed (signal)");
616
+ });
617
+ // Verify connections are not closed yet
618
+ expect(agentConnection.signal.aborted).toBe(false);
619
+ expect(clientConnection.signal.aborted).toBe(false);
620
+ expect(closeLog).toHaveLength(0);
621
+ // Close the streams by closing the writable ends
622
+ await clientToAgent.writable.close();
623
+ await agentToClient.writable.close();
624
+ // Wait for closed promises to resolve
625
+ await agentConnection.closed;
626
+ await clientConnection.closed;
627
+ // Verify connections are now closed
628
+ expect(agentConnection.signal.aborted).toBe(true);
629
+ expect(clientConnection.signal.aborted).toBe(true);
630
+ expect(closeLog).toContain("agent connection closed (signal)");
631
+ expect(closeLog).toContain("client connection closed (signal)");
632
+ });
633
+ it("supports removing signal event listeners", async () => {
634
+ const closeLog = [];
635
+ // Create simple client and agent
636
+ class TestClient {
637
+ async writeTextFile(_) {
638
+ return {};
639
+ }
640
+ async readTextFile(_) {
641
+ return { content: "test" };
642
+ }
643
+ async requestPermission(_) {
644
+ return {
645
+ outcome: {
646
+ outcome: "selected",
647
+ optionId: "allow",
648
+ },
649
+ };
650
+ }
651
+ async sessionUpdate(_) {
652
+ // no-op
653
+ }
654
+ }
655
+ class TestAgent {
656
+ async initialize(_) {
657
+ return {
658
+ protocolVersion: PROTOCOL_VERSION,
659
+ agentCapabilities: { loadSession: false },
660
+ };
661
+ }
662
+ async newSession(_) {
663
+ return { sessionId: "test-session" };
664
+ }
665
+ async authenticate(_) {
666
+ // no-op
667
+ }
668
+ async prompt(_) {
669
+ return { stopReason: "end_turn" };
670
+ }
671
+ async cancel(_) {
672
+ // no-op
673
+ }
674
+ }
675
+ // Set up connections
676
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
677
+ new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
678
+ // Register and then remove a listener
679
+ const listener = () => {
680
+ closeLog.push("this should not be called");
681
+ };
682
+ agentConnection.signal.addEventListener("abort", listener);
683
+ agentConnection.signal.removeEventListener("abort", listener);
684
+ // Register another listener that should be called
685
+ agentConnection.signal.addEventListener("abort", () => {
686
+ closeLog.push("agent connection closed");
687
+ });
688
+ // Close the streams
689
+ await clientToAgent.writable.close();
690
+ await agentToClient.writable.close();
691
+ // Wait for closed promise
692
+ await agentConnection.closed;
693
+ // Verify only the non-removed listener was called
694
+ expect(closeLog).toEqual(["agent connection closed"]);
695
+ expect(closeLog).not.toContain("this should not be called");
696
+ });
697
+ it("handles methods returning response objects with _meta or void", async () => {
698
+ // Create client that returns both response objects and void
699
+ class TestClient {
700
+ async writeTextFile(_params) {
701
+ // Return response object with _meta
702
+ return {
703
+ _meta: {
704
+ timestamp: new Date().toISOString(),
705
+ version: "1.0.0",
706
+ },
707
+ };
708
+ }
709
+ async readTextFile(_params) {
710
+ return {
711
+ content: "test content",
712
+ _meta: {
713
+ encoding: "utf-8",
714
+ },
715
+ };
716
+ }
717
+ async requestPermission(_params) {
718
+ return {
719
+ outcome: {
720
+ outcome: "selected",
721
+ optionId: "allow",
722
+ },
723
+ _meta: {
724
+ userId: "test-user",
725
+ },
726
+ };
727
+ }
728
+ async sessionUpdate(_params) {
729
+ // Returns void
730
+ }
731
+ }
732
+ // Create agent that returns both response objects and void
733
+ class TestAgent {
734
+ async initialize(params) {
735
+ return {
736
+ protocolVersion: params.protocolVersion,
737
+ agentCapabilities: { loadSession: true },
738
+ _meta: {
739
+ agentVersion: "2.0.0",
740
+ },
741
+ };
742
+ }
743
+ async newSession(_params) {
744
+ return {
745
+ sessionId: "test-session",
746
+ _meta: {
747
+ sessionType: "ephemeral",
748
+ },
749
+ };
750
+ }
751
+ async loadSession(_params) {
752
+ // Test returning minimal response
753
+ return {};
754
+ }
755
+ async authenticate(params) {
756
+ if (params.methodId === "none") {
757
+ // Test returning void
758
+ return;
759
+ }
760
+ // Test returning response with _meta
761
+ return {
762
+ _meta: {
763
+ authenticated: true,
764
+ method: params.methodId,
765
+ },
766
+ };
767
+ }
768
+ async prompt(_params) {
769
+ return { stopReason: "end_turn" };
770
+ }
771
+ async cancel(_params) {
772
+ // Returns void
773
+ }
774
+ }
775
+ // Set up connections
776
+ const agentConnection = new ClientSideConnection(() => new TestClient(), ndJsonStream(clientToAgent.writable, agentToClient.readable));
777
+ const clientConnection = new AgentSideConnection(() => new TestAgent(), ndJsonStream(agentToClient.writable, clientToAgent.readable));
778
+ // Test writeTextFile returns response with _meta
779
+ const writeResponse = await clientConnection.writeTextFile({
780
+ path: "/test.txt",
781
+ content: "test",
782
+ sessionId: "test-session",
783
+ });
784
+ expect(writeResponse).toEqual({
785
+ _meta: {
786
+ timestamp: expect.any(String),
787
+ version: "1.0.0",
788
+ },
789
+ });
790
+ // Test readTextFile returns response with content and _meta
791
+ const readResponse = await clientConnection.readTextFile({
792
+ path: "/test.txt",
793
+ sessionId: "test-session",
794
+ });
795
+ expect(readResponse.content).toBe("test content");
796
+ expect(readResponse._meta).toEqual({
797
+ encoding: "utf-8",
798
+ });
799
+ // Test initialize with _meta
800
+ const initResponse = await agentConnection.initialize({
801
+ protocolVersion: PROTOCOL_VERSION,
802
+ clientCapabilities: {},
803
+ });
804
+ expect(initResponse._meta).toEqual({
805
+ agentVersion: "2.0.0",
806
+ });
807
+ // Test authenticate returning void
808
+ const authResponseVoid = await agentConnection.authenticate({
809
+ methodId: "none",
810
+ });
811
+ expect(authResponseVoid).toEqual({});
812
+ // Test authenticate returning response with _meta
813
+ const authResponse = await agentConnection.authenticate({
814
+ methodId: "oauth",
815
+ });
816
+ expect(authResponse).toEqual({
817
+ _meta: {
818
+ authenticated: true,
819
+ method: "oauth",
820
+ },
821
+ });
822
+ // Test newSession with _meta
823
+ const sessionResponse = await agentConnection.newSession({
824
+ cwd: "/test",
825
+ mcpServers: [],
826
+ });
827
+ expect(sessionResponse._meta).toEqual({
828
+ sessionType: "ephemeral",
829
+ });
830
+ // Test loadSession returning minimal response
831
+ const loadResponse = await agentConnection.loadSession({
832
+ sessionId: "test-session",
833
+ mcpServers: [],
834
+ cwd: "/test",
835
+ });
836
+ expect(loadResponse).toEqual({});
837
+ });
838
+ });
839
+ //# sourceMappingURL=acp.test.js.map