gsd-pi 2.67.0-dev.a5b1d8f → 2.67.0-dev.fe39184

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 (191) hide show
  1. package/README.md +41 -31
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +121 -8
  3. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  4. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  5. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  6. package/dist/resources/extensions/gsd/auto-start.js +12 -0
  7. package/dist/resources/extensions/gsd/auto.js +27 -0
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  9. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  10. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  11. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  12. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
  14. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  15. package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
  16. package/dist/resources/extensions/gsd/guided-flow.js +16 -0
  17. package/dist/resources/extensions/gsd/init-wizard.js +37 -0
  18. package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
  19. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  20. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  21. package/dist/resources/extensions/gsd/workflow-mcp.js +261 -0
  22. package/dist/web/standalone/.next/BUILD_ID +1 -1
  23. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  24. package/dist/web/standalone/.next/build-manifest.json +3 -3
  25. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  26. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.html +1 -1
  44. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  51. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  54. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  55. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  56. package/dist/web/standalone/.next/static/chunks/6502.5dcdcf1e1432e20d.js +9 -0
  57. package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-42a66876b763aa26.js} +1 -1
  58. package/package.json +4 -2
  59. package/packages/mcp-server/README.md +38 -0
  60. package/packages/mcp-server/dist/cli.d.ts +9 -0
  61. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  62. package/packages/mcp-server/dist/cli.js +58 -0
  63. package/packages/mcp-server/dist/cli.js.map +1 -0
  64. package/packages/mcp-server/dist/index.d.ts +20 -0
  65. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  66. package/packages/mcp-server/dist/index.js +14 -0
  67. package/packages/mcp-server/dist/index.js.map +1 -0
  68. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  69. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  70. package/packages/mcp-server/dist/readers/captures.js +67 -0
  71. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  72. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  73. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  74. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  75. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  76. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  77. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  78. package/packages/mcp-server/dist/readers/index.js +10 -0
  79. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  80. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  81. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  82. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  83. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  84. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  85. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  86. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  87. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  88. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  89. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  90. package/packages/mcp-server/dist/readers/paths.js +199 -0
  91. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  92. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  93. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  94. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  95. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  96. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  97. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  98. package/packages/mcp-server/dist/readers/state.js +184 -0
  99. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  100. package/packages/mcp-server/dist/server.d.ts +28 -0
  101. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  102. package/packages/mcp-server/dist/server.js +319 -0
  103. package/packages/mcp-server/dist/server.js.map +1 -0
  104. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  105. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  106. package/packages/mcp-server/dist/session-manager.js +284 -0
  107. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  108. package/packages/mcp-server/dist/types.d.ts +61 -0
  109. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  110. package/packages/mcp-server/dist/types.js +11 -0
  111. package/packages/mcp-server/dist/types.js.map +1 -0
  112. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  113. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  114. package/packages/mcp-server/dist/workflow-tools.js +532 -0
  115. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  116. package/packages/mcp-server/src/server.ts +6 -2
  117. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  118. package/packages/mcp-server/src/workflow-tools.ts +997 -0
  119. package/packages/mcp-server/tsconfig.json +1 -1
  120. package/packages/pi-agent-core/dist/agent-loop.js +14 -6
  121. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  122. package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
  123. package/packages/pi-agent-core/src/agent-loop.ts +20 -6
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
  136. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
  137. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
  138. package/packages/rpc-client/dist/index.d.ts +10 -0
  139. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  140. package/packages/rpc-client/dist/index.js +9 -0
  141. package/packages/rpc-client/dist/index.js.map +1 -0
  142. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  143. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  144. package/packages/rpc-client/dist/jsonl.js +54 -0
  145. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  146. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  147. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  148. package/packages/rpc-client/dist/rpc-client.js +541 -0
  149. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  150. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  151. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  152. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  153. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  154. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  155. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  156. package/packages/rpc-client/dist/rpc-types.js +12 -0
  157. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  158. package/scripts/ensure-workspace-builds.cjs +2 -0
  159. package/scripts/link-workspace-packages.cjs +21 -14
  160. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +157 -8
  161. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +182 -0
  162. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  163. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  164. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  165. package/src/resources/extensions/gsd/auto-start.ts +15 -1
  166. package/src/resources/extensions/gsd/auto.ts +29 -1
  167. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  168. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  169. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  170. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  171. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  172. package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
  173. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  174. package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
  175. package/src/resources/extensions/gsd/guided-flow.ts +24 -0
  176. package/src/resources/extensions/gsd/init-wizard.ts +40 -0
  177. package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
  178. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  179. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  180. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  181. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
  182. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
  183. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  184. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -0
  185. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  186. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  187. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  188. package/src/resources/extensions/gsd/workflow-mcp.ts +320 -0
  189. package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
  190. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_buildManifest.js +0 -0
  191. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_ssgManifest.js +0 -0
@@ -0,0 +1,477 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { PassThrough } from "node:stream";
4
+ import { serializeJsonLine, attachJsonlLineReader } from "./jsonl.js";
5
+ import { RpcClient } from "./rpc-client.js";
6
+ // ============================================================================
7
+ // JSONL Tests
8
+ // ============================================================================
9
+ describe("serializeJsonLine", () => {
10
+ it("produces valid JSON terminated with LF", () => {
11
+ const result = serializeJsonLine({ type: "test", value: 42 });
12
+ assert.ok(result.endsWith("\n"), "must end with LF");
13
+ const parsed = JSON.parse(result.trim());
14
+ assert.equal(parsed.type, "test");
15
+ assert.equal(parsed.value, 42);
16
+ });
17
+ it("serializes strings with special characters", () => {
18
+ const result = serializeJsonLine({ msg: "hello\nworld" });
19
+ assert.ok(result.endsWith("\n"));
20
+ // The embedded \n must be escaped inside the JSON — only the trailing LF is the framing delimiter
21
+ const lines = result.split("\n");
22
+ // Should be exactly 2 parts: the JSON line and the empty string after trailing LF
23
+ assert.equal(lines.length, 2);
24
+ assert.equal(lines[1], "");
25
+ const parsed = JSON.parse(lines[0]);
26
+ assert.equal(parsed.msg, "hello\nworld");
27
+ });
28
+ it("handles empty objects", () => {
29
+ const result = serializeJsonLine({});
30
+ assert.equal(result, "{}\n");
31
+ });
32
+ });
33
+ describe("attachJsonlLineReader", () => {
34
+ it("splits on LF correctly", async () => {
35
+ const stream = new PassThrough();
36
+ const lines = [];
37
+ attachJsonlLineReader(stream, (line) => lines.push(line));
38
+ stream.write('{"a":1}\n{"b":2}\n');
39
+ stream.end();
40
+ // Let microtask queue flush
41
+ await new Promise((r) => setTimeout(r, 10));
42
+ assert.equal(lines.length, 2);
43
+ assert.equal(JSON.parse(lines[0]).a, 1);
44
+ assert.equal(JSON.parse(lines[1]).b, 2);
45
+ });
46
+ it("handles chunked data across boundaries", async () => {
47
+ const stream = new PassThrough();
48
+ const lines = [];
49
+ attachJsonlLineReader(stream, (line) => lines.push(line));
50
+ // Write in fragments that split mid-line
51
+ stream.write('{"type":"hel');
52
+ stream.write('lo"}\n{"type":"w');
53
+ stream.write('orld"}\n');
54
+ stream.end();
55
+ await new Promise((r) => setTimeout(r, 10));
56
+ assert.equal(lines.length, 2);
57
+ assert.equal(JSON.parse(lines[0]).type, "hello");
58
+ assert.equal(JSON.parse(lines[1]).type, "world");
59
+ });
60
+ it("emits trailing data on stream end", async () => {
61
+ const stream = new PassThrough();
62
+ const lines = [];
63
+ attachJsonlLineReader(stream, (line) => lines.push(line));
64
+ stream.write('{"final":true}');
65
+ stream.end();
66
+ await new Promise((r) => setTimeout(r, 10));
67
+ assert.equal(lines.length, 1);
68
+ assert.equal(JSON.parse(lines[0]).final, true);
69
+ });
70
+ it("returns a detach function that stops reading", async () => {
71
+ const stream = new PassThrough();
72
+ const lines = [];
73
+ const detach = attachJsonlLineReader(stream, (line) => lines.push(line));
74
+ stream.write('{"a":1}\n');
75
+ await new Promise((r) => setTimeout(r, 10));
76
+ assert.equal(lines.length, 1);
77
+ detach();
78
+ stream.write('{"b":2}\n');
79
+ stream.end();
80
+ await new Promise((r) => setTimeout(r, 10));
81
+ // Should still be 1 — detach removed listeners
82
+ assert.equal(lines.length, 1);
83
+ });
84
+ it("strips CR from CRLF line endings", async () => {
85
+ const stream = new PassThrough();
86
+ const lines = [];
87
+ attachJsonlLineReader(stream, (line) => lines.push(line));
88
+ stream.write('{"v":1}\r\n');
89
+ stream.end();
90
+ await new Promise((r) => setTimeout(r, 10));
91
+ assert.equal(lines.length, 1);
92
+ assert.equal(JSON.parse(lines[0]).v, 1);
93
+ });
94
+ });
95
+ // ============================================================================
96
+ // Type Shape Tests
97
+ // ============================================================================
98
+ describe("type shapes", () => {
99
+ it("RpcInitResult has protocolVersion, sessionId, capabilities", () => {
100
+ const init = {
101
+ protocolVersion: 2,
102
+ sessionId: "sess_123",
103
+ capabilities: {
104
+ events: ["execution_complete", "cost_update"],
105
+ commands: ["prompt", "steer"],
106
+ },
107
+ };
108
+ assert.equal(init.protocolVersion, 2);
109
+ assert.equal(init.sessionId, "sess_123");
110
+ assert.ok(Array.isArray(init.capabilities.events));
111
+ assert.ok(Array.isArray(init.capabilities.commands));
112
+ });
113
+ it("RpcExecutionCompleteEvent has required fields", () => {
114
+ const event = {
115
+ type: "execution_complete",
116
+ runId: "run_abc",
117
+ status: "completed",
118
+ stats: {
119
+ sessionFile: "/tmp/session.json",
120
+ sessionId: "sess_123",
121
+ userMessages: 5,
122
+ assistantMessages: 5,
123
+ toolCalls: 3,
124
+ toolResults: 3,
125
+ totalMessages: 10,
126
+ tokens: { input: 1000, output: 500, cacheRead: 200, cacheWrite: 100, total: 1800 },
127
+ cost: 0.05,
128
+ },
129
+ };
130
+ assert.equal(event.type, "execution_complete");
131
+ assert.equal(event.runId, "run_abc");
132
+ assert.equal(event.status, "completed");
133
+ assert.ok(event.stats);
134
+ assert.equal(event.stats.sessionId, "sess_123");
135
+ });
136
+ it("RpcCostUpdateEvent has required fields", () => {
137
+ const event = {
138
+ type: "cost_update",
139
+ runId: "run_abc",
140
+ turnCost: 0.01,
141
+ cumulativeCost: 0.05,
142
+ tokens: { input: 500, output: 200, cacheRead: 100, cacheWrite: 50 },
143
+ };
144
+ assert.equal(event.type, "cost_update");
145
+ assert.equal(event.runId, "run_abc");
146
+ assert.equal(event.turnCost, 0.01);
147
+ assert.equal(event.cumulativeCost, 0.05);
148
+ assert.ok(event.tokens);
149
+ });
150
+ it("SessionStats has all expected fields", () => {
151
+ const stats = {
152
+ sessionFile: "/tmp/session.json",
153
+ sessionId: "s1",
154
+ userMessages: 10,
155
+ assistantMessages: 10,
156
+ toolCalls: 5,
157
+ toolResults: 5,
158
+ totalMessages: 20,
159
+ tokens: { input: 2000, output: 1000, cacheRead: 500, cacheWrite: 200, total: 3700 },
160
+ cost: 0.10,
161
+ };
162
+ assert.equal(stats.sessionId, "s1");
163
+ assert.equal(stats.userMessages, 10);
164
+ assert.equal(stats.tokens.total, 3700);
165
+ assert.equal(stats.cost, 0.10);
166
+ });
167
+ it("RpcProtocolVersion accepts 1 and 2", () => {
168
+ const v1 = 1;
169
+ const v2 = 2;
170
+ assert.equal(v1, 1);
171
+ assert.equal(v2, 2);
172
+ });
173
+ it("RpcV2Event discriminated union covers both event types", () => {
174
+ const events = [
175
+ {
176
+ type: "execution_complete",
177
+ runId: "r1",
178
+ status: "completed",
179
+ stats: {
180
+ sessionFile: undefined,
181
+ sessionId: "s1",
182
+ userMessages: 1,
183
+ assistantMessages: 1,
184
+ toolCalls: 0,
185
+ toolResults: 0,
186
+ totalMessages: 2,
187
+ tokens: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, total: 150 },
188
+ cost: 0.001,
189
+ },
190
+ },
191
+ {
192
+ type: "cost_update",
193
+ runId: "r1",
194
+ turnCost: 0.001,
195
+ cumulativeCost: 0.001,
196
+ tokens: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0 },
197
+ },
198
+ ];
199
+ assert.equal(events.length, 2);
200
+ assert.equal(events[0].type, "execution_complete");
201
+ assert.equal(events[1].type, "cost_update");
202
+ });
203
+ });
204
+ // ============================================================================
205
+ // RpcClient Construction Tests
206
+ // ============================================================================
207
+ describe("RpcClient construction", () => {
208
+ it("creates with default options", () => {
209
+ const client = new RpcClient();
210
+ assert.ok(client);
211
+ });
212
+ it("creates with custom options", () => {
213
+ const client = new RpcClient({
214
+ cliPath: "/usr/local/bin/gsd",
215
+ cwd: "/tmp",
216
+ env: { NODE_ENV: "test" },
217
+ provider: "anthropic",
218
+ model: "claude-sonnet",
219
+ args: ["--verbose"],
220
+ });
221
+ assert.ok(client);
222
+ });
223
+ });
224
+ // ============================================================================
225
+ // events() Generator Tests
226
+ // ============================================================================
227
+ describe("events() async generator", () => {
228
+ it("yields events from a mock stream in order", async () => {
229
+ const client = new RpcClient();
230
+ // Reach into the client to set up a mock process with a PassThrough stdout
231
+ const mockStdout = new PassThrough();
232
+ const mockStderr = new PassThrough();
233
+ const mockStdin = new PassThrough();
234
+ // Simulate a started process by setting internal state
235
+ // We use Object.assign to set private fields for testing
236
+ const clientAny = client;
237
+ clientAny.process = {
238
+ stdout: mockStdout,
239
+ stderr: mockStderr,
240
+ stdin: mockStdin,
241
+ exitCode: null,
242
+ kill: () => { },
243
+ on: (event, handler) => {
244
+ if (event === "exit") {
245
+ // Store exit handler so we can trigger it
246
+ clientAny._testExitHandler = handler;
247
+ }
248
+ },
249
+ removeListener: () => { },
250
+ };
251
+ // Attach the JSONL reader like start() does
252
+ clientAny.stopReadingStdout = attachJsonlLineReader(mockStdout, (line) => {
253
+ clientAny.handleLine(line);
254
+ });
255
+ // Collect events from the generator
256
+ const received = [];
257
+ const genPromise = (async () => {
258
+ for await (const event of client.events()) {
259
+ received.push(event);
260
+ if (event.type === "done")
261
+ break;
262
+ }
263
+ })();
264
+ // Simulate server sending events
265
+ await new Promise((r) => setTimeout(r, 20));
266
+ mockStdout.write(serializeJsonLine({ type: "agent_start", runId: "r1" }));
267
+ await new Promise((r) => setTimeout(r, 20));
268
+ mockStdout.write(serializeJsonLine({ type: "token", text: "hello" }));
269
+ await new Promise((r) => setTimeout(r, 20));
270
+ mockStdout.write(serializeJsonLine({ type: "done" }));
271
+ await genPromise;
272
+ assert.equal(received.length, 3);
273
+ assert.equal(received[0].type, "agent_start");
274
+ assert.equal(received[1].type, "token");
275
+ assert.equal(received[2].type, "done");
276
+ });
277
+ it("terminates when process exits", async () => {
278
+ const client = new RpcClient();
279
+ const mockStdout = new PassThrough();
280
+ const mockStderr = new PassThrough();
281
+ const mockStdin = new PassThrough();
282
+ const exitHandlers = [];
283
+ const clientAny = client;
284
+ clientAny.process = {
285
+ stdout: mockStdout,
286
+ stderr: mockStderr,
287
+ stdin: mockStdin,
288
+ exitCode: null,
289
+ kill: () => { },
290
+ on: (event, handler) => {
291
+ if (event === "exit")
292
+ exitHandlers.push(handler);
293
+ },
294
+ removeListener: (event, handler) => {
295
+ const idx = exitHandlers.indexOf(handler);
296
+ if (idx !== -1)
297
+ exitHandlers.splice(idx, 1);
298
+ },
299
+ };
300
+ clientAny.stopReadingStdout = attachJsonlLineReader(mockStdout, (line) => {
301
+ clientAny.handleLine(line);
302
+ });
303
+ const received = [];
304
+ const genPromise = (async () => {
305
+ for await (const event of client.events()) {
306
+ received.push(event);
307
+ }
308
+ })();
309
+ // Send one event, then simulate process exit
310
+ await new Promise((r) => setTimeout(r, 20));
311
+ mockStdout.write(serializeJsonLine({ type: "agent_start" }));
312
+ await new Promise((r) => setTimeout(r, 20));
313
+ // Fire exit handlers
314
+ for (const h of exitHandlers)
315
+ h();
316
+ await genPromise;
317
+ assert.equal(received.length, 1);
318
+ assert.equal(received[0].type, "agent_start");
319
+ });
320
+ it("throws if client not started", async () => {
321
+ const client = new RpcClient();
322
+ await assert.rejects(async () => {
323
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
324
+ for await (const _event of client.events()) {
325
+ // should not reach
326
+ }
327
+ }, /Client not started/);
328
+ });
329
+ });
330
+ // ============================================================================
331
+ // sendUIResponse Serialization Test
332
+ // ============================================================================
333
+ describe("sendUIResponse serialization", () => {
334
+ it("writes correct JSONL to stdin", () => {
335
+ const client = new RpcClient();
336
+ const chunks = [];
337
+ const mockStdin = {
338
+ write: (data) => {
339
+ chunks.push(data);
340
+ return true;
341
+ },
342
+ };
343
+ const clientAny = client;
344
+ clientAny.process = { stdin: mockStdin };
345
+ client.sendUIResponse("ui_1", { value: "hello" });
346
+ assert.equal(chunks.length, 1);
347
+ const parsed = JSON.parse(chunks[0].trim());
348
+ assert.equal(parsed.type, "extension_ui_response");
349
+ assert.equal(parsed.id, "ui_1");
350
+ assert.equal(parsed.value, "hello");
351
+ });
352
+ it("serializes confirmed response", () => {
353
+ const client = new RpcClient();
354
+ const chunks = [];
355
+ const mockStdin = {
356
+ write: (data) => {
357
+ chunks.push(data);
358
+ return true;
359
+ },
360
+ };
361
+ const clientAny = client;
362
+ clientAny.process = { stdin: mockStdin };
363
+ client.sendUIResponse("ui_2", { confirmed: true });
364
+ const parsed = JSON.parse(chunks[0].trim());
365
+ assert.equal(parsed.confirmed, true);
366
+ assert.equal(parsed.id, "ui_2");
367
+ });
368
+ it("serializes cancelled response", () => {
369
+ const client = new RpcClient();
370
+ const chunks = [];
371
+ const mockStdin = {
372
+ write: (data) => {
373
+ chunks.push(data);
374
+ return true;
375
+ },
376
+ };
377
+ const clientAny = client;
378
+ clientAny.process = { stdin: mockStdin };
379
+ client.sendUIResponse("ui_3", { cancelled: true });
380
+ const parsed = JSON.parse(chunks[0].trim());
381
+ assert.equal(parsed.cancelled, true);
382
+ });
383
+ });
384
+ // ============================================================================
385
+ // init/shutdown/subscribe Serialization Tests
386
+ // ============================================================================
387
+ describe("v2 command serialization", () => {
388
+ // Helper: capture what the client sends to stdin
389
+ function createMockClient() {
390
+ const client = new RpcClient();
391
+ const sent = [];
392
+ let respondFn = null;
393
+ const clientAny = client;
394
+ clientAny.process = {
395
+ stdin: {
396
+ write: (data) => {
397
+ const parsed = JSON.parse(data.trim());
398
+ sent.push(parsed);
399
+ // Auto-respond with success after a tick
400
+ if (respondFn) {
401
+ setTimeout(() => respondFn(parsed), 5);
402
+ }
403
+ return true;
404
+ },
405
+ },
406
+ stderr: new PassThrough(),
407
+ exitCode: null,
408
+ kill: () => { },
409
+ on: () => { },
410
+ removeListener: () => { },
411
+ };
412
+ const respondNext = (overrides = {}) => {
413
+ respondFn = (parsed) => {
414
+ const response = {
415
+ type: "response",
416
+ id: parsed.id,
417
+ command: parsed.type,
418
+ success: true,
419
+ data: {},
420
+ ...overrides,
421
+ };
422
+ clientAny.handleLine(JSON.stringify(response));
423
+ };
424
+ };
425
+ return { client, sent, respondNext };
426
+ }
427
+ it("init sends correct v2 init command", async () => {
428
+ const { client, sent, respondNext } = createMockClient();
429
+ respondNext({ data: { protocolVersion: 2, sessionId: "s1", capabilities: { events: [], commands: [] } } });
430
+ const result = await client.init({ clientId: "test-app" });
431
+ assert.equal(sent.length, 1);
432
+ assert.equal(sent[0].type, "init");
433
+ assert.equal(sent[0].protocolVersion, 2);
434
+ assert.equal(sent[0].clientId, "test-app");
435
+ assert.equal(result.protocolVersion, 2);
436
+ assert.equal(result.sessionId, "s1");
437
+ });
438
+ it("shutdown sends shutdown command", async () => {
439
+ const { client, sent, respondNext } = createMockClient();
440
+ // Override the process exit wait
441
+ const clientAny = client;
442
+ const originalProcess = clientAny.process;
443
+ const exitHandlers = [];
444
+ clientAny.process = {
445
+ ...originalProcess,
446
+ on: (event, handler) => {
447
+ if (event === "exit")
448
+ exitHandlers.push(handler);
449
+ },
450
+ };
451
+ respondNext();
452
+ // Call shutdown and simulate process exit
453
+ const shutdownPromise = client.shutdown();
454
+ await new Promise((r) => setTimeout(r, 20));
455
+ for (const h of exitHandlers)
456
+ h(0);
457
+ await shutdownPromise;
458
+ assert.equal(sent.length, 1);
459
+ assert.equal(sent[0].type, "shutdown");
460
+ });
461
+ it("subscribe sends subscribe command with event list", async () => {
462
+ const { client, sent, respondNext } = createMockClient();
463
+ respondNext();
464
+ await client.subscribe(["execution_complete", "cost_update"]);
465
+ assert.equal(sent.length, 1);
466
+ assert.equal(sent[0].type, "subscribe");
467
+ assert.deepEqual(sent[0].events, ["execution_complete", "cost_update"]);
468
+ });
469
+ it("subscribe with wildcard", async () => {
470
+ const { client, sent, respondNext } = createMockClient();
471
+ respondNext();
472
+ await client.subscribe(["*"]);
473
+ assert.equal(sent[0].events.length, 1);
474
+ assert.equal(sent[0].events[0], "*");
475
+ });
476
+ });
477
+ //# sourceMappingURL=rpc-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-client.test.js","sourceRoot":"","sources":["../src/rpc-client.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, beforeEach, afterEach } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { PassThrough } from \"node:stream\";\nimport { serializeJsonLine, attachJsonlLineReader } from \"./jsonl.js\";\nimport type {\n\tRpcInitResult,\n\tRpcExecutionCompleteEvent,\n\tRpcCostUpdateEvent,\n\tRpcProtocolVersion,\n\tSessionStats,\n\tRpcV2Event,\n} from \"./rpc-types.js\";\nimport { RpcClient } from \"./rpc-client.js\";\nimport type { SdkAgentEvent } from \"./rpc-client.js\";\n\n// ============================================================================\n// JSONL Tests\n// ============================================================================\n\ndescribe(\"serializeJsonLine\", () => {\n\tit(\"produces valid JSON terminated with LF\", () => {\n\t\tconst result = serializeJsonLine({ type: \"test\", value: 42 });\n\t\tassert.ok(result.endsWith(\"\\n\"), \"must end with LF\");\n\t\tconst parsed = JSON.parse(result.trim());\n\t\tassert.equal(parsed.type, \"test\");\n\t\tassert.equal(parsed.value, 42);\n\t});\n\n\tit(\"serializes strings with special characters\", () => {\n\t\tconst result = serializeJsonLine({ msg: \"hello\\nworld\" });\n\t\tassert.ok(result.endsWith(\"\\n\"));\n\t\t// The embedded \\n must be escaped inside the JSON — only the trailing LF is the framing delimiter\n\t\tconst lines = result.split(\"\\n\");\n\t\t// Should be exactly 2 parts: the JSON line and the empty string after trailing LF\n\t\tassert.equal(lines.length, 2);\n\t\tassert.equal(lines[1], \"\");\n\t\tconst parsed = JSON.parse(lines[0]);\n\t\tassert.equal(parsed.msg, \"hello\\nworld\");\n\t});\n\n\tit(\"handles empty objects\", () => {\n\t\tconst result = serializeJsonLine({});\n\t\tassert.equal(result, \"{}\\n\");\n\t});\n});\n\ndescribe(\"attachJsonlLineReader\", () => {\n\tit(\"splits on LF correctly\", async () => {\n\t\tconst stream = new PassThrough();\n\t\tconst lines: string[] = [];\n\n\t\tattachJsonlLineReader(stream, (line) => lines.push(line));\n\n\t\tstream.write('{\"a\":1}\\n{\"b\":2}\\n');\n\t\tstream.end();\n\n\t\t// Let microtask queue flush\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\n\t\tassert.equal(lines.length, 2);\n\t\tassert.equal(JSON.parse(lines[0]).a, 1);\n\t\tassert.equal(JSON.parse(lines[1]).b, 2);\n\t});\n\n\tit(\"handles chunked data across boundaries\", async () => {\n\t\tconst stream = new PassThrough();\n\t\tconst lines: string[] = [];\n\n\t\tattachJsonlLineReader(stream, (line) => lines.push(line));\n\n\t\t// Write in fragments that split mid-line\n\t\tstream.write('{\"type\":\"hel');\n\t\tstream.write('lo\"}\\n{\"type\":\"w');\n\t\tstream.write('orld\"}\\n');\n\t\tstream.end();\n\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\n\t\tassert.equal(lines.length, 2);\n\t\tassert.equal(JSON.parse(lines[0]).type, \"hello\");\n\t\tassert.equal(JSON.parse(lines[1]).type, \"world\");\n\t});\n\n\tit(\"emits trailing data on stream end\", async () => {\n\t\tconst stream = new PassThrough();\n\t\tconst lines: string[] = [];\n\n\t\tattachJsonlLineReader(stream, (line) => lines.push(line));\n\n\t\tstream.write('{\"final\":true}');\n\t\tstream.end();\n\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\n\t\tassert.equal(lines.length, 1);\n\t\tassert.equal(JSON.parse(lines[0]).final, true);\n\t});\n\n\tit(\"returns a detach function that stops reading\", async () => {\n\t\tconst stream = new PassThrough();\n\t\tconst lines: string[] = [];\n\n\t\tconst detach = attachJsonlLineReader(stream, (line) => lines.push(line));\n\n\t\tstream.write('{\"a\":1}\\n');\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\t\tassert.equal(lines.length, 1);\n\n\t\tdetach();\n\n\t\tstream.write('{\"b\":2}\\n');\n\t\tstream.end();\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\n\t\t// Should still be 1 — detach removed listeners\n\t\tassert.equal(lines.length, 1);\n\t});\n\n\tit(\"strips CR from CRLF line endings\", async () => {\n\t\tconst stream = new PassThrough();\n\t\tconst lines: string[] = [];\n\n\t\tattachJsonlLineReader(stream, (line) => lines.push(line));\n\n\t\tstream.write('{\"v\":1}\\r\\n');\n\t\tstream.end();\n\n\t\tawait new Promise((r) => setTimeout(r, 10));\n\n\t\tassert.equal(lines.length, 1);\n\t\tassert.equal(JSON.parse(lines[0]).v, 1);\n\t});\n});\n\n// ============================================================================\n// Type Shape Tests\n// ============================================================================\n\ndescribe(\"type shapes\", () => {\n\tit(\"RpcInitResult has protocolVersion, sessionId, capabilities\", () => {\n\t\tconst init: RpcInitResult = {\n\t\t\tprotocolVersion: 2,\n\t\t\tsessionId: \"sess_123\",\n\t\t\tcapabilities: {\n\t\t\t\tevents: [\"execution_complete\", \"cost_update\"],\n\t\t\t\tcommands: [\"prompt\", \"steer\"],\n\t\t\t},\n\t\t};\n\t\tassert.equal(init.protocolVersion, 2);\n\t\tassert.equal(init.sessionId, \"sess_123\");\n\t\tassert.ok(Array.isArray(init.capabilities.events));\n\t\tassert.ok(Array.isArray(init.capabilities.commands));\n\t});\n\n\tit(\"RpcExecutionCompleteEvent has required fields\", () => {\n\t\tconst event: RpcExecutionCompleteEvent = {\n\t\t\ttype: \"execution_complete\",\n\t\t\trunId: \"run_abc\",\n\t\t\tstatus: \"completed\",\n\t\t\tstats: {\n\t\t\t\tsessionFile: \"/tmp/session.json\",\n\t\t\t\tsessionId: \"sess_123\",\n\t\t\t\tuserMessages: 5,\n\t\t\t\tassistantMessages: 5,\n\t\t\t\ttoolCalls: 3,\n\t\t\t\ttoolResults: 3,\n\t\t\t\ttotalMessages: 10,\n\t\t\t\ttokens: { input: 1000, output: 500, cacheRead: 200, cacheWrite: 100, total: 1800 },\n\t\t\t\tcost: 0.05,\n\t\t\t},\n\t\t};\n\t\tassert.equal(event.type, \"execution_complete\");\n\t\tassert.equal(event.runId, \"run_abc\");\n\t\tassert.equal(event.status, \"completed\");\n\t\tassert.ok(event.stats);\n\t\tassert.equal(event.stats.sessionId, \"sess_123\");\n\t});\n\n\tit(\"RpcCostUpdateEvent has required fields\", () => {\n\t\tconst event: RpcCostUpdateEvent = {\n\t\t\ttype: \"cost_update\",\n\t\t\trunId: \"run_abc\",\n\t\t\tturnCost: 0.01,\n\t\t\tcumulativeCost: 0.05,\n\t\t\ttokens: { input: 500, output: 200, cacheRead: 100, cacheWrite: 50 },\n\t\t};\n\t\tassert.equal(event.type, \"cost_update\");\n\t\tassert.equal(event.runId, \"run_abc\");\n\t\tassert.equal(event.turnCost, 0.01);\n\t\tassert.equal(event.cumulativeCost, 0.05);\n\t\tassert.ok(event.tokens);\n\t});\n\n\tit(\"SessionStats has all expected fields\", () => {\n\t\tconst stats: SessionStats = {\n\t\t\tsessionFile: \"/tmp/session.json\",\n\t\t\tsessionId: \"s1\",\n\t\t\tuserMessages: 10,\n\t\t\tassistantMessages: 10,\n\t\t\ttoolCalls: 5,\n\t\t\ttoolResults: 5,\n\t\t\ttotalMessages: 20,\n\t\t\ttokens: { input: 2000, output: 1000, cacheRead: 500, cacheWrite: 200, total: 3700 },\n\t\t\tcost: 0.10,\n\t\t};\n\t\tassert.equal(stats.sessionId, \"s1\");\n\t\tassert.equal(stats.userMessages, 10);\n\t\tassert.equal(stats.tokens.total, 3700);\n\t\tassert.equal(stats.cost, 0.10);\n\t});\n\n\tit(\"RpcProtocolVersion accepts 1 and 2\", () => {\n\t\tconst v1: RpcProtocolVersion = 1;\n\t\tconst v2: RpcProtocolVersion = 2;\n\t\tassert.equal(v1, 1);\n\t\tassert.equal(v2, 2);\n\t});\n\n\tit(\"RpcV2Event discriminated union covers both event types\", () => {\n\t\tconst events: RpcV2Event[] = [\n\t\t\t{\n\t\t\t\ttype: \"execution_complete\",\n\t\t\t\trunId: \"r1\",\n\t\t\t\tstatus: \"completed\",\n\t\t\t\tstats: {\n\t\t\t\t\tsessionFile: undefined,\n\t\t\t\t\tsessionId: \"s1\",\n\t\t\t\t\tuserMessages: 1,\n\t\t\t\t\tassistantMessages: 1,\n\t\t\t\t\ttoolCalls: 0,\n\t\t\t\t\ttoolResults: 0,\n\t\t\t\t\ttotalMessages: 2,\n\t\t\t\t\ttokens: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0, total: 150 },\n\t\t\t\t\tcost: 0.001,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: \"cost_update\",\n\t\t\t\trunId: \"r1\",\n\t\t\t\tturnCost: 0.001,\n\t\t\t\tcumulativeCost: 0.001,\n\t\t\t\ttokens: { input: 100, output: 50, cacheRead: 0, cacheWrite: 0 },\n\t\t\t},\n\t\t];\n\t\tassert.equal(events.length, 2);\n\t\tassert.equal(events[0].type, \"execution_complete\");\n\t\tassert.equal(events[1].type, \"cost_update\");\n\t});\n});\n\n// ============================================================================\n// RpcClient Construction Tests\n// ============================================================================\n\ndescribe(\"RpcClient construction\", () => {\n\tit(\"creates with default options\", () => {\n\t\tconst client = new RpcClient();\n\t\tassert.ok(client);\n\t});\n\n\tit(\"creates with custom options\", () => {\n\t\tconst client = new RpcClient({\n\t\t\tcliPath: \"/usr/local/bin/gsd\",\n\t\t\tcwd: \"/tmp\",\n\t\t\tenv: { NODE_ENV: \"test\" },\n\t\t\tprovider: \"anthropic\",\n\t\t\tmodel: \"claude-sonnet\",\n\t\t\targs: [\"--verbose\"],\n\t\t});\n\t\tassert.ok(client);\n\t});\n});\n\n// ============================================================================\n// events() Generator Tests\n// ============================================================================\n\ndescribe(\"events() async generator\", () => {\n\tit(\"yields events from a mock stream in order\", async () => {\n\t\tconst client = new RpcClient();\n\n\t\t// Reach into the client to set up a mock process with a PassThrough stdout\n\t\tconst mockStdout = new PassThrough();\n\t\tconst mockStderr = new PassThrough();\n\t\tconst mockStdin = new PassThrough();\n\n\t\t// Simulate a started process by setting internal state\n\t\t// We use Object.assign to set private fields for testing\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = {\n\t\t\tstdout: mockStdout,\n\t\t\tstderr: mockStderr,\n\t\t\tstdin: mockStdin,\n\t\t\texitCode: null,\n\t\t\tkill: () => {},\n\t\t\ton: (event: string, handler: (...args: any[]) => void) => {\n\t\t\t\tif (event === \"exit\") {\n\t\t\t\t\t// Store exit handler so we can trigger it\n\t\t\t\t\tclientAny._testExitHandler = handler;\n\t\t\t\t}\n\t\t\t},\n\t\t\tremoveListener: () => {},\n\t\t};\n\n\t\t// Attach the JSONL reader like start() does\n\t\tclientAny.stopReadingStdout = attachJsonlLineReader(mockStdout, (line: string) => {\n\t\t\tclientAny.handleLine(line);\n\t\t});\n\n\t\t// Collect events from the generator\n\t\tconst received: SdkAgentEvent[] = [];\n\t\tconst genPromise = (async () => {\n\t\t\tfor await (const event of client.events()) {\n\t\t\t\treceived.push(event);\n\t\t\t\tif (event.type === \"done\") break;\n\t\t\t}\n\t\t})();\n\n\t\t// Simulate server sending events\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\t\tmockStdout.write(serializeJsonLine({ type: \"agent_start\", runId: \"r1\" }));\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\t\tmockStdout.write(serializeJsonLine({ type: \"token\", text: \"hello\" }));\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\t\tmockStdout.write(serializeJsonLine({ type: \"done\" }));\n\n\t\tawait genPromise;\n\n\t\tassert.equal(received.length, 3);\n\t\tassert.equal(received[0].type, \"agent_start\");\n\t\tassert.equal(received[1].type, \"token\");\n\t\tassert.equal(received[2].type, \"done\");\n\t});\n\n\tit(\"terminates when process exits\", async () => {\n\t\tconst client = new RpcClient();\n\t\tconst mockStdout = new PassThrough();\n\t\tconst mockStderr = new PassThrough();\n\t\tconst mockStdin = new PassThrough();\n\n\t\tconst exitHandlers: Array<() => void> = [];\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = {\n\t\t\tstdout: mockStdout,\n\t\t\tstderr: mockStderr,\n\t\t\tstdin: mockStdin,\n\t\t\texitCode: null,\n\t\t\tkill: () => {},\n\t\t\ton: (event: string, handler: () => void) => {\n\t\t\t\tif (event === \"exit\") exitHandlers.push(handler);\n\t\t\t},\n\t\t\tremoveListener: (event: string, handler: () => void) => {\n\t\t\t\tconst idx = exitHandlers.indexOf(handler);\n\t\t\t\tif (idx !== -1) exitHandlers.splice(idx, 1);\n\t\t\t},\n\t\t};\n\n\t\tclientAny.stopReadingStdout = attachJsonlLineReader(mockStdout, (line: string) => {\n\t\t\tclientAny.handleLine(line);\n\t\t});\n\n\t\tconst received: SdkAgentEvent[] = [];\n\t\tconst genPromise = (async () => {\n\t\t\tfor await (const event of client.events()) {\n\t\t\t\treceived.push(event);\n\t\t\t}\n\t\t})();\n\n\t\t// Send one event, then simulate process exit\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\t\tmockStdout.write(serializeJsonLine({ type: \"agent_start\" }));\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\n\t\t// Fire exit handlers\n\t\tfor (const h of exitHandlers) h();\n\n\t\tawait genPromise;\n\n\t\tassert.equal(received.length, 1);\n\t\tassert.equal(received[0].type, \"agent_start\");\n\t});\n\n\tit(\"throws if client not started\", async () => {\n\t\tconst client = new RpcClient();\n\t\tawait assert.rejects(async () => {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\t\t\tfor await (const _event of client.events()) {\n\t\t\t\t// should not reach\n\t\t\t}\n\t\t}, /Client not started/);\n\t});\n});\n\n// ============================================================================\n// sendUIResponse Serialization Test\n// ============================================================================\n\ndescribe(\"sendUIResponse serialization\", () => {\n\tit(\"writes correct JSONL to stdin\", () => {\n\t\tconst client = new RpcClient();\n\t\tconst chunks: string[] = [];\n\t\tconst mockStdin = {\n\t\t\twrite: (data: string) => {\n\t\t\t\tchunks.push(data);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = { stdin: mockStdin };\n\n\t\tclient.sendUIResponse(\"ui_1\", { value: \"hello\" });\n\n\t\tassert.equal(chunks.length, 1);\n\t\tconst parsed = JSON.parse(chunks[0].trim());\n\t\tassert.equal(parsed.type, \"extension_ui_response\");\n\t\tassert.equal(parsed.id, \"ui_1\");\n\t\tassert.equal(parsed.value, \"hello\");\n\t});\n\n\tit(\"serializes confirmed response\", () => {\n\t\tconst client = new RpcClient();\n\t\tconst chunks: string[] = [];\n\t\tconst mockStdin = {\n\t\t\twrite: (data: string) => {\n\t\t\t\tchunks.push(data);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = { stdin: mockStdin };\n\n\t\tclient.sendUIResponse(\"ui_2\", { confirmed: true });\n\n\t\tconst parsed = JSON.parse(chunks[0].trim());\n\t\tassert.equal(parsed.confirmed, true);\n\t\tassert.equal(parsed.id, \"ui_2\");\n\t});\n\n\tit(\"serializes cancelled response\", () => {\n\t\tconst client = new RpcClient();\n\t\tconst chunks: string[] = [];\n\t\tconst mockStdin = {\n\t\t\twrite: (data: string) => {\n\t\t\t\tchunks.push(data);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = { stdin: mockStdin };\n\n\t\tclient.sendUIResponse(\"ui_3\", { cancelled: true });\n\n\t\tconst parsed = JSON.parse(chunks[0].trim());\n\t\tassert.equal(parsed.cancelled, true);\n\t});\n});\n\n// ============================================================================\n// init/shutdown/subscribe Serialization Tests\n// ============================================================================\n\ndescribe(\"v2 command serialization\", () => {\n\t// Helper: capture what the client sends to stdin\n\tfunction createMockClient(): { client: RpcClient; sent: any[]; respondNext: (data?: any) => void } {\n\t\tconst client = new RpcClient();\n\t\tconst sent: any[] = [];\n\t\tlet respondFn: ((data: any) => void) | null = null;\n\n\t\tconst clientAny = client as any;\n\t\tclientAny.process = {\n\t\t\tstdin: {\n\t\t\t\twrite: (data: string) => {\n\t\t\t\t\tconst parsed = JSON.parse(data.trim());\n\t\t\t\t\tsent.push(parsed);\n\t\t\t\t\t// Auto-respond with success after a tick\n\t\t\t\t\tif (respondFn) {\n\t\t\t\t\t\tsetTimeout(() => respondFn!(parsed), 5);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t},\n\t\t\tstderr: new PassThrough(),\n\t\t\texitCode: null,\n\t\t\tkill: () => {},\n\t\t\ton: () => {},\n\t\t\tremoveListener: () => {},\n\t\t};\n\n\t\tconst respondNext = (overrides: any = {}) => {\n\t\t\trespondFn = (parsed) => {\n\t\t\t\tconst response = {\n\t\t\t\t\ttype: \"response\",\n\t\t\t\t\tid: parsed.id,\n\t\t\t\t\tcommand: parsed.type,\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tdata: {},\n\t\t\t\t\t...overrides,\n\t\t\t\t};\n\t\t\t\tclientAny.handleLine(JSON.stringify(response));\n\t\t\t};\n\t\t};\n\n\t\treturn { client, sent, respondNext };\n\t}\n\n\tit(\"init sends correct v2 init command\", async () => {\n\t\tconst { client, sent, respondNext } = createMockClient();\n\t\trespondNext({ data: { protocolVersion: 2, sessionId: \"s1\", capabilities: { events: [], commands: [] } } });\n\n\t\tconst result = await client.init({ clientId: \"test-app\" });\n\n\t\tassert.equal(sent.length, 1);\n\t\tassert.equal(sent[0].type, \"init\");\n\t\tassert.equal(sent[0].protocolVersion, 2);\n\t\tassert.equal(sent[0].clientId, \"test-app\");\n\t\tassert.equal(result.protocolVersion, 2);\n\t\tassert.equal(result.sessionId, \"s1\");\n\t});\n\n\tit(\"shutdown sends shutdown command\", async () => {\n\t\tconst { client, sent, respondNext } = createMockClient();\n\n\t\t// Override the process exit wait\n\t\tconst clientAny = client as any;\n\t\tconst originalProcess = clientAny.process;\n\t\tconst exitHandlers: Array<(code: number) => void> = [];\n\t\tclientAny.process = {\n\t\t\t...originalProcess,\n\t\t\ton: (event: string, handler: (code: number) => void) => {\n\t\t\t\tif (event === \"exit\") exitHandlers.push(handler);\n\t\t\t},\n\t\t};\n\n\t\trespondNext();\n\n\t\t// Call shutdown and simulate process exit\n\t\tconst shutdownPromise = client.shutdown();\n\t\tawait new Promise((r) => setTimeout(r, 20));\n\t\tfor (const h of exitHandlers) h(0);\n\n\t\tawait shutdownPromise;\n\n\t\tassert.equal(sent.length, 1);\n\t\tassert.equal(sent[0].type, \"shutdown\");\n\t});\n\n\tit(\"subscribe sends subscribe command with event list\", async () => {\n\t\tconst { client, sent, respondNext } = createMockClient();\n\t\trespondNext();\n\n\t\tawait client.subscribe([\"execution_complete\", \"cost_update\"]);\n\n\t\tassert.equal(sent.length, 1);\n\t\tassert.equal(sent[0].type, \"subscribe\");\n\t\tassert.deepEqual(sent[0].events, [\"execution_complete\", \"cost_update\"]);\n\t});\n\n\tit(\"subscribe with wildcard\", async () => {\n\t\tconst { client, sent, respondNext } = createMockClient();\n\t\trespondNext();\n\n\t\tawait client.subscribe([\"*\"]);\n\n\t\tassert.equal(sent[0].events.length, 1);\n\t\tassert.equal(sent[0].events[0], \"*\");\n\t});\n});\n"]}