@wrongstack/acp 0.260.0 → 0.264.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,492 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { writeErr, expectDefined } from '@wrongstack/core';
3
+
4
+ // src/agent/wrongstack-acp-agent.ts
5
+
6
+ // src/types/acp-v1.ts
7
+ var ACP_PROTOCOL_VERSION = 1;
8
+
9
+ // src/agent/protocol-handler.ts
10
+ function toWire(msg) {
11
+ return msg;
12
+ }
13
+ var WRONGSTACK_VERSION = "0.263.0";
14
+ var DEFAULT_MODE_ID = "code";
15
+ var DEFAULT_MODES = [
16
+ {
17
+ id: DEFAULT_MODE_ID,
18
+ name: "Code",
19
+ description: "Default agent mode for code-generation tasks."
20
+ }
21
+ ];
22
+ var ACPProtocolHandler = class {
23
+ transport;
24
+ defaultCwd;
25
+ runTurn;
26
+ onSessionNew;
27
+ modes;
28
+ configOptions;
29
+ agentName;
30
+ initialized = false;
31
+ sessions = /* @__PURE__ */ new Map();
32
+ nextId = 1;
33
+ constructor(opts) {
34
+ this.transport = opts.transport;
35
+ this.defaultCwd = opts.defaultCwd;
36
+ this.runTurn = opts.runTurn;
37
+ this.onSessionNew = opts.onSessionNew ?? (() => {
38
+ });
39
+ this.modes = opts.modes ?? DEFAULT_MODES;
40
+ this.configOptions = opts.configOptions ?? [];
41
+ this.agentName = opts.agentName ?? "wrongstack";
42
+ }
43
+ /**
44
+ * Process one inbound message. Returns true if this was a terminal
45
+ * message (rare; reserved for future use by the server's own
46
+ * shutdown signal).
47
+ */
48
+ async handleMessage(msg) {
49
+ if (typeof msg !== "object" || msg === null) return false;
50
+ const m = msg;
51
+ if (m.id !== void 0 && (m.result !== void 0 || m.error !== void 0)) {
52
+ return false;
53
+ }
54
+ if (m.id !== void 0 && typeof m.method === "string") {
55
+ return this.handleRequest(m.id, m.method, m.params);
56
+ }
57
+ if (typeof m.method === "string") {
58
+ return this.handleNotification(m.method, m.params);
59
+ }
60
+ return false;
61
+ }
62
+ /** Abort all active turns and drop session state. */
63
+ close() {
64
+ for (const [, session] of this.sessions) {
65
+ session.abort.abort();
66
+ }
67
+ this.sessions.clear();
68
+ }
69
+ // ────────────────────────────────────────────────────────────────────
70
+ // Requests
71
+ // ────────────────────────────────────────────────────────────────────
72
+ async handleRequest(id, method, params) {
73
+ if (method !== "initialize" && !this.initialized) {
74
+ await this.sendError(id, -32e3, "Not initialized");
75
+ return false;
76
+ }
77
+ try {
78
+ switch (method) {
79
+ case "initialize":
80
+ return await this.handleInitialize(id, params);
81
+ case "authenticate":
82
+ return await this.handleAuthenticate(id, params);
83
+ case "session/new":
84
+ return await this.handleSessionNew(id, params);
85
+ case "session/load":
86
+ return await this.handleSessionLoad(id, params);
87
+ case "session/prompt":
88
+ return await this.handleSessionPrompt(id, params);
89
+ case "session/set_mode":
90
+ return await this.handleSetMode(id, params);
91
+ case "session/set_config_option":
92
+ return await this.handleSetConfigOption(id, params);
93
+ case "session/list":
94
+ return await this.handleSessionList(id);
95
+ default:
96
+ await this.sendError(id, -32601, `Unknown method: ${method}`);
97
+ return false;
98
+ }
99
+ } catch (err) {
100
+ const { code, message, data } = errorToJsonRpc(err);
101
+ await this.sendError(id, code, message, data);
102
+ return false;
103
+ }
104
+ }
105
+ async handleInitialize(id, params) {
106
+ const p = params ?? {};
107
+ const requested = typeof p.protocolVersion === "number" ? p.protocolVersion : 1;
108
+ if (requested !== ACP_PROTOCOL_VERSION) {
109
+ await this.sendError(
110
+ id,
111
+ -32e3,
112
+ `server speaks protocolVersion=${ACP_PROTOCOL_VERSION}, client requested ${requested}`
113
+ );
114
+ return false;
115
+ }
116
+ this.initialized = true;
117
+ await this.transport.send(toWire({
118
+ jsonrpc: "2.0",
119
+ id,
120
+ result: {
121
+ protocolVersion: ACP_PROTOCOL_VERSION,
122
+ agentCapabilities: {
123
+ loadSession: true,
124
+ promptCapabilities: {
125
+ image: false,
126
+ audio: false,
127
+ embeddedContext: true
128
+ }
129
+ },
130
+ agentInfo: {
131
+ name: this.agentName,
132
+ title: "WrongStack",
133
+ version: WRONGSTACK_VERSION
134
+ },
135
+ // Static options advertised at handshake. They are also
136
+ // re-sent on every `current_mode_update` / `config_option_update`
137
+ // notification so late-joining clients see them.
138
+ authMethods: [],
139
+ modes: this.modes,
140
+ configOptions: this.configOptions
141
+ }
142
+ }));
143
+ return false;
144
+ }
145
+ async handleAuthenticate(id, _params) {
146
+ await this.transport.send(toWire({
147
+ jsonrpc: "2.0",
148
+ id,
149
+ result: { outcome: "unauthenticated" }
150
+ }));
151
+ return false;
152
+ }
153
+ async handleSessionNew(id, params) {
154
+ const p = params ?? {};
155
+ const cwd = typeof p.cwd === "string" ? p.cwd : this.defaultCwd;
156
+ const sessionId = `sess_${this.allocId()}`;
157
+ const now = (/* @__PURE__ */ new Date()).toISOString();
158
+ const state = {
159
+ id: sessionId,
160
+ cwd,
161
+ abort: new AbortController(),
162
+ modeId: DEFAULT_MODE_ID,
163
+ createdAt: now,
164
+ updatedAt: now
165
+ };
166
+ this.sessions.set(sessionId, state);
167
+ this.onSessionNew(state);
168
+ await this.sendNotification({
169
+ sessionId,
170
+ update: {
171
+ sessionUpdate: "current_mode_update",
172
+ modeId: this.modes[0]?.id ?? DEFAULT_MODE_ID
173
+ }
174
+ });
175
+ if (this.configOptions.length > 0) {
176
+ await this.sendNotification({
177
+ sessionId,
178
+ update: {
179
+ sessionUpdate: "config_option_update",
180
+ configOptions: [...this.configOptions]
181
+ }
182
+ });
183
+ }
184
+ await this.transport.send(toWire({
185
+ jsonrpc: "2.0",
186
+ id,
187
+ result: {
188
+ sessionId,
189
+ modes: this.modes,
190
+ configOptions: this.configOptions
191
+ }
192
+ }));
193
+ return false;
194
+ }
195
+ async handleSessionLoad(id, params) {
196
+ return this.handleSessionNew(id, params);
197
+ }
198
+ async handleSessionPrompt(id, params) {
199
+ const p = params ?? {};
200
+ const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
201
+ if (!sessionId || !this.sessions.has(sessionId)) {
202
+ await this.sendError(id, -32e3, "unknown or missing sessionId");
203
+ return false;
204
+ }
205
+ if (!Array.isArray(p.prompt)) {
206
+ await this.sendError(id, -32602, "prompt must be an array of content blocks");
207
+ return false;
208
+ }
209
+ const session = this.sessions.get(sessionId);
210
+ if (session.abort.signal.aborted) {
211
+ session.abort = new AbortController();
212
+ }
213
+ const turnSignal = new AbortController();
214
+ const onCancel = () => turnSignal.abort();
215
+ session.abort.signal.addEventListener("abort", onCancel, { once: true });
216
+ let result;
217
+ try {
218
+ result = await this.runTurn(
219
+ { sessionId, prompt: p.prompt, signal: turnSignal.signal },
220
+ (update) => this.sendNotification({ sessionId, update })
221
+ );
222
+ } catch (err) {
223
+ session.abort.signal.removeEventListener("abort", onCancel);
224
+ const { code, message, data } = errorToJsonRpc(err);
225
+ await this.sendError(id, code, message, data);
226
+ return false;
227
+ }
228
+ session.abort.signal.removeEventListener("abort", onCancel);
229
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
230
+ await this.transport.send(toWire({
231
+ jsonrpc: "2.0",
232
+ id,
233
+ result: { stopReason: result.stopReason }
234
+ }));
235
+ return false;
236
+ }
237
+ async handleSetMode(id, params) {
238
+ const p = params ?? {};
239
+ const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
240
+ const modeId = typeof p.modeId === "string" ? p.modeId : null;
241
+ const session = sessionId ? this.sessions.get(sessionId) : void 0;
242
+ if (!session || !modeId || !this.modes.some((m) => m.id === modeId)) {
243
+ await this.sendError(id, -32602, "invalid sessionId or modeId");
244
+ return false;
245
+ }
246
+ session.modeId = modeId;
247
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
248
+ await this.sendNotification({
249
+ sessionId,
250
+ update: { sessionUpdate: "current_mode_update", modeId }
251
+ });
252
+ await this.transport.send(toWire({ jsonrpc: "2.0", id, result: {} }));
253
+ return false;
254
+ }
255
+ async handleSetConfigOption(id, params) {
256
+ const p = params ?? {};
257
+ const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
258
+ const optionId = typeof p.configOptionId === "string" ? p.configOptionId : null;
259
+ const value = typeof p.value === "string" ? p.value : null;
260
+ const session = sessionId ? this.sessions.get(sessionId) : void 0;
261
+ const option = optionId ? this.configOptions.find((o) => o.id === optionId) : void 0;
262
+ if (!session || !option || value === null || !option.options.some((o) => o.value === value)) {
263
+ await this.sendError(id, -32602, "invalid sessionId, configOptionId, or value");
264
+ return false;
265
+ }
266
+ option.currentValue = value;
267
+ session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
268
+ await this.sendNotification({
269
+ sessionId,
270
+ update: {
271
+ sessionUpdate: "config_option_update",
272
+ configOptions: [...this.configOptions]
273
+ }
274
+ });
275
+ await this.transport.send(toWire({ jsonrpc: "2.0", id, result: {} }));
276
+ return false;
277
+ }
278
+ async handleSessionList(id) {
279
+ const sessions = Array.from(this.sessions.values()).map((s) => {
280
+ const out = {
281
+ sessionId: s.id,
282
+ cwd: s.cwd,
283
+ updatedAt: s.updatedAt
284
+ };
285
+ if (s.title !== void 0) out.title = s.title;
286
+ return out;
287
+ });
288
+ await this.transport.send(toWire({
289
+ jsonrpc: "2.0",
290
+ id,
291
+ result: { sessions }
292
+ }));
293
+ return false;
294
+ }
295
+ // ────────────────────────────────────────────────────────────────────
296
+ // Notifications
297
+ // ────────────────────────────────────────────────────────────────────
298
+ async handleNotification(method, params) {
299
+ switch (method) {
300
+ case "session/cancel": {
301
+ const p = params ?? {};
302
+ const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
303
+ const session = sessionId ? this.sessions.get(sessionId) : void 0;
304
+ if (session) {
305
+ session.abort.abort();
306
+ }
307
+ return false;
308
+ }
309
+ case "exit":
310
+ this.close();
311
+ return true;
312
+ default:
313
+ return false;
314
+ }
315
+ }
316
+ // ────────────────────────────────────────────────────────────────────
317
+ // Wire helpers
318
+ // ────────────────────────────────────────────────────────────────────
319
+ async sendNotification(params) {
320
+ await this.transport.send(toWire({ jsonrpc: "2.0", method: "session/update", params }));
321
+ }
322
+ async sendError(id, code, message, data) {
323
+ const error = { code, message };
324
+ if (data !== void 0) error.data = data;
325
+ await this.transport.send(toWire({ jsonrpc: "2.0", id, error }));
326
+ }
327
+ allocId() {
328
+ return this.nextId++;
329
+ }
330
+ };
331
+ function errorToJsonRpc(err) {
332
+ if (err && typeof err === "object") {
333
+ const e = err;
334
+ if (typeof e.code === "number" && typeof e.message === "string") {
335
+ const result = {
336
+ code: e.code,
337
+ message: e.message
338
+ };
339
+ if (e.data !== void 0) result.data = e.data;
340
+ return result;
341
+ }
342
+ }
343
+ const message = err instanceof Error ? err.message : String(err);
344
+ return { code: -32603, message };
345
+ }
346
+ var StdioTransport = class {
347
+ stdin = process.stdin;
348
+ stdout = process.stdout;
349
+ stderr = process.stderr;
350
+ buffer = "";
351
+ handlers = /* @__PURE__ */ new Set();
352
+ closed = false;
353
+ resolveRead = null;
354
+ messageQueue = [];
355
+ constructor() {
356
+ this.stdin.resume();
357
+ this.stdin.setEncoding("utf8");
358
+ this.stdin.on("data", (chunk) => this.onData(chunk));
359
+ this.stdin.on("end", () => this.handleClose());
360
+ this.stdin.on("error", (err) => this.failAll(err));
361
+ }
362
+ sendStartupMarker() {
363
+ this.stdout.write("[wstack-acp]\n", "utf8");
364
+ }
365
+ send(msg) {
366
+ if (this.closed) return Promise.resolve();
367
+ return new Promise((resolve) => {
368
+ const line = JSON.stringify(msg) + "\n";
369
+ this.stdout.write(line, "utf8", () => resolve());
370
+ });
371
+ }
372
+ sendRaw(chunk) {
373
+ this.stdout.write(chunk, "utf8");
374
+ }
375
+ read() {
376
+ if (this.messageQueue.length > 0) return Promise.resolve(expectDefined(this.messageQueue.shift()));
377
+ if (this.closed) return Promise.resolve(null);
378
+ return new Promise((resolve) => {
379
+ this.resolveRead = resolve;
380
+ });
381
+ }
382
+ onMessage(handler) {
383
+ this.handlers.add(handler);
384
+ return () => this.handlers.delete(handler);
385
+ }
386
+ close() {
387
+ this.closed = true;
388
+ this.stdin.pause();
389
+ this.resolveRead?.(null);
390
+ this.resolveRead = null;
391
+ }
392
+ onData(chunk) {
393
+ this.buffer += chunk;
394
+ const lines = this.buffer.split("\n");
395
+ this.buffer = lines.pop() ?? "";
396
+ for (const raw of lines) {
397
+ if (!raw.trim()) continue;
398
+ try {
399
+ this.dispatch(JSON.parse(raw));
400
+ } catch (err) {
401
+ this.stderr.write(`[wstack-acp parse error] ${err}
402
+ `, "utf8");
403
+ }
404
+ }
405
+ }
406
+ dispatch(msg) {
407
+ if (this.resolveRead) {
408
+ const resolve = this.resolveRead;
409
+ this.resolveRead = null;
410
+ resolve(msg);
411
+ } else {
412
+ this.messageQueue.push(msg);
413
+ }
414
+ for (const handler of this.handlers) {
415
+ try {
416
+ handler(msg);
417
+ } catch (err) {
418
+ this.stderr.write(`[wstack-acp handler error] ${err}
419
+ `, "utf8");
420
+ }
421
+ }
422
+ }
423
+ handleClose() {
424
+ this.closed = true;
425
+ this.resolveRead?.(null);
426
+ this.resolveRead = null;
427
+ }
428
+ failAll(err) {
429
+ this.stderr.write(`[wstack-acp stdin error] ${err.message}
430
+ `, "utf8");
431
+ this.close();
432
+ }
433
+ };
434
+
435
+ // src/agent/wrongstack-acp-agent.ts
436
+ var WrongStackACPServer = class {
437
+ transport;
438
+ handler;
439
+ running = false;
440
+ constructor(opts = {}) {
441
+ this.transport = new StdioTransport();
442
+ const runTurn = opts.runTurn ?? defaultEchoRunTurn;
443
+ this.handler = new ACPProtocolHandler({
444
+ transport: this.transport,
445
+ defaultCwd: opts.defaultCwd ?? process.cwd(),
446
+ runTurn,
447
+ agentName: opts.agentName
448
+ });
449
+ }
450
+ /**
451
+ * Start the server. Blocks until the client disconnects.
452
+ *
453
+ * 1. Print the legacy `[wstack-acp]\n` marker so the client knows the
454
+ * process is the ACP server (the old `StdioTransport` handshake).
455
+ * 2. Loop: read messages, dispatch to the handler, until EOF / error.
456
+ */
457
+ async start() {
458
+ this.transport.sendStartupMarker();
459
+ this.running = true;
460
+ while (this.running) {
461
+ const msg = await this.transport.read();
462
+ if (!msg) break;
463
+ const terminal = await this.handler.handleMessage(msg);
464
+ if (terminal) break;
465
+ }
466
+ this.transport.close();
467
+ }
468
+ /** Stop the server. */
469
+ stop() {
470
+ this.running = false;
471
+ this.transport.close();
472
+ }
473
+ };
474
+ var defaultEchoRunTurn = async (_input, _emit) => {
475
+ return { stopReason: "end_turn" };
476
+ };
477
+ async function main() {
478
+ const server = new WrongStackACPServer();
479
+ await server.start();
480
+ }
481
+ var isEntrypoint = process.argv[1] !== void 0 && fileURLToPath(import.meta.url) === process.argv[1];
482
+ if (isEntrypoint) {
483
+ main().catch((err) => {
484
+ writeErr(`[wstack-acp fatal] ${err}
485
+ `);
486
+ process.exit(1);
487
+ });
488
+ }
489
+
490
+ export { WrongStackACPServer };
491
+ //# sourceMappingURL=wrongstack-acp-agent.js.map
492
+ //# sourceMappingURL=wrongstack-acp-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/acp-v1.ts","../src/agent/protocol-handler.ts","../src/agent/stdio-transport.ts","../src/agent/wrongstack-acp-agent.ts"],"names":["writeErr"],"mappings":"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","file":"wrongstack-acp-agent.js","sourcesContent":["/**\n * ACP v1 type definitions — Agent Client Protocol, stable v1 spec.\n *\n * Scope: discriminated union for the `session/update` notification payload\n * (the `update` field of a `session/update` JSON-RPC notification), plus the\n * subset of supporting types it depends on.\n *\n * Spec: https://agentclientprotocol.com/protocol/v1/overview\n *\n * Design notes\n * ────────────\n * • The stable v1 spec defines 11 `sessionUpdate` discriminator values. We\n * type exactly those 11, plus an `_unstable_*` escape hatch for v2-RFD\n * kinds (e.g. `next_edit_suggestions`, `elicitation`) that real agents\n * may emit before the spec stabilises them, and an `unknown` fallback for\n * everything else. We do NOT synthesise 32 fake variants to match a\n * number cited in passing — the union is honest about the surface.\n *\n * • Per the spec's conventions, discriminator values use snake_case. The\n * property keys inside each variant are camelCase (the JSON-RPC envelope\n * is JSON-RPC 2.0, everything else is camelCase unless the spec says\n * otherwise).\n *\n * • Optional fields that the spec marks optional are marked `?:`. Required\n * fields have no `?`. We do not include spec fields the spec marks\n * \"SHOULD NOT\" or \"reserved\".\n *\n * • The existing `acp-messages.ts` types describe an older draft of the\n * protocol (string `protocolVersion: '2024-11'`, fake `tools/call`\n * method, etc.). Do NOT import from it here — `acp-v1.ts` is\n * self-contained so the new code path can be reviewed in isolation and\n * deleted wholesale if the rewrite is ever reverted.\n */\n\n// ────────────────────────────────────────────────────────────────────────────\n// Shared building blocks\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Stable protocol version (integer per the spec, not a date-string). */\nexport const ACP_PROTOCOL_VERSION = 1 as const;\nexport type ACPProtocolVersion = typeof ACP_PROTOCOL_VERSION;\n\n/** Per the spec: opaque, unique id. We type as branded string. */\nexport type SessionId = string & { readonly __acpSessionId: unique symbol };\nexport type ToolCallId = string & { readonly __acpToolCallId: unique symbol };\nexport type MessageId = string & { readonly __acpMessageId: unique symbol };\nexport type TerminalId = string & { readonly __acpTerminalId: unique symbol };\nexport type PlanEntryId = string & { readonly __acpPlanEntryId: unique symbol };\n\n// ────────────────────────────────────────────────────────────────────────────\n// Content blocks — reused from MCP per the spec\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Annotations attached to a content block. Optional, agent-supplied hint\n * about audience/priority. Spec leaves shape open; we mirror the fields\n * the spec shows in its examples.\n */\nexport interface ContentAnnotations {\n audience?: ('user' | 'assistant')[] | undefined;\n priority?: number | undefined;\n [key: string]: unknown;\n}\n\nexport interface TextContent {\n type: 'text';\n text: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ImageContent {\n type: 'image';\n mimeType: string;\n /** Base64-encoded image data. */\n data: string;\n uri?: string | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface AudioContent {\n type: 'audio';\n mimeType: string;\n /** Base64-encoded audio data. */\n data: string;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface TextResourceContents {\n uri: string;\n mimeType?: string | undefined;\n text: string;\n}\n\nexport interface BlobResourceContents {\n uri: string;\n mimeType?: string | undefined;\n /** Base64-encoded binary. */\n blob: string;\n}\n\nexport type EmbeddedResourceContents = TextResourceContents | BlobResourceContents;\n\nexport interface EmbeddedResourceContent {\n type: 'resource';\n resource: EmbeddedResourceContents;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport interface ResourceLinkContent {\n type: 'resource_link';\n uri: string;\n name: string;\n mimeType?: string | undefined;\n title?: string | undefined;\n description?: string | undefined;\n size?: number | undefined;\n annotations?: ContentAnnotations | undefined;\n}\n\nexport type ContentBlock =\n | TextContent\n | ImageContent\n | AudioContent\n | EmbeddedResourceContent\n | ResourceLinkContent;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Tool calls\n// ────────────────────────────────────────────────────────────────────────────\n\nexport type ToolKind =\n | 'read'\n | 'edit'\n | 'delete'\n | 'move'\n | 'search'\n | 'execute'\n | 'think'\n | 'fetch'\n | 'switch_mode'\n | 'other';\n\nexport type ToolCallStatus = 'pending' | 'in_progress' | 'completed' | 'failed';\n\n/** A single concrete content payload attached to a tool call. */\nexport type ToolCallContent =\n | { type: 'content'; content: ContentBlock }\n | {\n type: 'diff';\n path: string;\n oldText: string | null;\n newText: string;\n }\n | { type: 'terminal'; terminalId: TerminalId };\n\nexport interface ToolCallLocation {\n path: string;\n /** 1-based per the spec's argument requirements. */\n line?: number | undefined;\n}\n\nexport interface ToolCall {\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n/**\n * Partial update of a previously-emitted tool call. All fields except\n * `toolCallId` are optional — only the changed fields are included.\n * Declared standalone (not `extends ToolCall`) because `title` is required\n * on `ToolCall` but optional here; the structural variance is the point.\n */\nexport interface ToolCallUpdateFields {\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Plan\n// ────────────────────────────────────────────────────────────────────────────\n\nexport type PlanEntryPriority = 'high' | 'medium' | 'low';\nexport type PlanEntryStatus = 'pending' | 'in_progress' | 'completed';\n\nexport interface PlanEntry {\n /** Required by the spec for the array shape, but per-entry id is optional. */\n content: string;\n priority: PlanEntryPriority;\n status: PlanEntryStatus;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Slash commands\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface AvailableCommandInput {\n hint: string;\n}\n\nexport interface AvailableCommand {\n name: string;\n description: string;\n input?: AvailableCommandInput | undefined;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Session modes\n// ────────────────────────────────────────────────────────────────────────────\n\nexport type SessionModeId = string & { readonly __acpModeId: unique symbol };\n\nexport interface SessionMode {\n id: SessionModeId;\n name: string;\n description?: string | undefined;\n}\n\nexport interface SessionModeState {\n currentModeId: SessionModeId;\n availableModes: SessionMode[];\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Config options\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Reserved spec categories. Underscore-prefixed names are free for custom use. */\nexport type ConfigOptionCategory =\n | 'mode'\n | 'model'\n | 'thought_level'\n | `_${string}`;\n\nexport type ConfigOptionType = 'select' | string;\n\nexport interface ConfigOptionValue {\n value: string;\n name: string;\n description?: string | undefined;\n}\n\nexport interface ConfigOption {\n id: string;\n name: string;\n description?: string | undefined;\n category?: ConfigOptionCategory | undefined;\n type: ConfigOptionType;\n currentValue: string;\n options: ConfigOptionValue[];\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Session info\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface SessionInfo {\n sessionId: SessionId;\n cwd: string;\n title?: string | undefined;\n updatedAt?: string | undefined;\n /** Agent-supplied extension metadata; opaque to clients. */\n _meta?: Record<string, unknown> | undefined;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Usage (token / cost) updates\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface UsageCost {\n amount: number;\n /** ISO 4217 currency code, e.g. \"USD\". */\n currency: string;\n}\n\nexport interface UsageUpdate {\n /** Tokens used in the current session context. Required, non-null. */\n used: number;\n /** Total context window size in tokens. Required, non-null. */\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Permission requests\n// ────────────────────────────────────────────────────────────────────────────\n\nexport type PermissionOptionKind =\n | 'allow_once'\n | 'allow_always'\n | 'reject_once'\n | 'reject_always';\n\nexport interface PermissionOption {\n optionId: string;\n name: string;\n kind: PermissionOptionKind;\n}\n\nexport type RequestPermissionOutcome =\n | { outcome: 'cancelled' }\n | { outcome: 'selected'; optionId: string };\n\n// ────────────────────────────────────────────────────────────────────────────\n// Stop reasons\n// ────────────────────────────────────────────────────────────────────────────\n\nexport type StopReason =\n | 'end_turn'\n | 'max_tokens'\n | 'max_turn_requests'\n | 'refusal'\n | 'cancelled'\n | string;\n\n// ────────────────────────────────────────────────────────────────────────────\n// SessionUpdate — the discriminated union\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Stable v1 variants. The spec currently defines exactly 11. */\nexport type SessionUpdate =\n | UserMessageChunkUpdate\n | AgentMessageChunkUpdate\n | ThoughtChunkUpdate\n | ToolCallUpdateUpdate\n | ToolCallUpdateNotification\n | PlanUpdate\n | AvailableCommandsUpdate\n | CurrentModeUpdate\n | ConfigOptionUpdate\n | SessionInfoUpdate\n | UsageUpdateUpdate;\n\n// --- Streaming message chunks -----------------------------------------------\n\nexport interface UserMessageChunkUpdate {\n sessionUpdate: 'user_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface AgentMessageChunkUpdate {\n sessionUpdate: 'agent_message_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\nexport interface ThoughtChunkUpdate {\n sessionUpdate: 'thought_chunk';\n messageId?: MessageId | undefined;\n content: ContentBlock;\n}\n\n// --- Tool calls ------------------------------------------------------------\n\n/** First notification for a new tool call. */\nexport interface ToolCallUpdateUpdate {\n sessionUpdate: 'tool_call';\n toolCallId: ToolCallId;\n title: string;\n kind?: ToolKind | undefined;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n}\n\n/** Subsequent updates to a previously-emitted tool call. */\nexport interface ToolCallUpdateNotification {\n sessionUpdate: 'tool_call_update';\n toolCallId: ToolCallId;\n status?: ToolCallStatus | undefined;\n content?: ToolCallContent[] | undefined;\n title?: string | undefined;\n kind?: ToolKind | undefined;\n locations?: ToolCallLocation[] | undefined;\n rawInput?: Record<string, unknown> | undefined;\n rawOutput?: Record<string, unknown> | undefined;\n}\n\n// --- Plan ------------------------------------------------------------------\n\nexport interface PlanUpdate {\n sessionUpdate: 'plan';\n entries: PlanEntry[];\n}\n\n// --- Commands / modes / config ---------------------------------------------\n\nexport interface AvailableCommandsUpdate {\n sessionUpdate: 'available_commands_update';\n availableCommands: AvailableCommand[];\n}\n\nexport interface CurrentModeUpdate {\n sessionUpdate: 'current_mode_update';\n modeId: SessionModeId;\n}\n\nexport interface ConfigOptionUpdate {\n sessionUpdate: 'config_option_update';\n configOptions: ConfigOption[];\n}\n\n// --- Session metadata ------------------------------------------------------\n\nexport interface SessionInfoUpdate {\n sessionUpdate: 'session_info_update';\n title?: string | null | undefined;\n updatedAt?: string | null | undefined;\n _meta?: Record<string, unknown> | undefined;\n}\n\n// --- Usage -----------------------------------------------------------------\n\nexport interface UsageUpdateUpdate {\n sessionUpdate: 'usage_update';\n used: number;\n size: number;\n cost?: UsageCost | undefined;\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Escape hatches: unknown / unstable\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Escape hatch for v2-RFD `sessionUpdate` kinds that have been published\n * but are not yet stabilised in the v1 spec. Examples seen in the wild:\n * `next_edit_suggestions`, `elicitation`, `proxy_extension`. We surface\n * the raw payload so forward-compat code can switch on\n * `kind === 'next_edit_suggestions'` etc. without losing the data.\n */\nexport interface UnstableSessionUpdate {\n sessionUpdate: `_unstable_${string}`;\n [key: string]: unknown;\n}\n\n/**\n * Last-resort variant: the agent sent a discriminator string we don't\n * recognise at all. The full payload is preserved as a record so consumers\n * can still log/inspect it. Prefer matching the known variants first.\n */\nexport interface UnknownSessionUpdate {\n sessionUpdate: string;\n [key: string]: unknown;\n}\n\n/** The full union, including escape hatches. */\nexport type AnySessionUpdate = SessionUpdate | UnstableSessionUpdate | UnknownSessionUpdate;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Top-level notification envelope\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface SessionUpdateNotification {\n jsonrpc?: '2.0' | undefined;\n method: 'session/update';\n params: {\n sessionId: SessionId;\n update: AnySessionUpdate;\n };\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Type guards\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Exhaustiveness helper. Call from the `default:` branch of a switch on\n * `sessionUpdate` to get a compile-time error when a new variant is added\n * without updating the consumer.\n */\nexport function assertNeverSessionUpdate(x: never): never {\n throw new Error(\n `Unhandled sessionUpdate: ${JSON.stringify(x)}`,\n );\n}\n","/**\n * ACP v1 server-side protocol handler.\n *\n * Receives JSON-RPC requests from an external ACP client (Zed, JetBrains\n * Junie, VS Code ACP extension, etc.) over stdio and answers them per the\n * v1 spec. See https://agentclientprotocol.com/protocol/v1/overview.\n *\n * Supported methods\n * ─────────────────\n * - initialize — handshake\n * - authenticate — optional, no-op when auth isn't required\n * - session/new — create a session\n * - session/load — restore a session by id\n * - session/prompt — run one turn, stream session/update\n * notifications, return stopReason\n * - session/cancel — notification (no response); cancels the\n * in-flight turn on the target session\n * - session/set_mode — change the active mode for a session\n * - session/set_config_option — change a config option value\n * - session/list — list known sessions\n *\n * Method execution\n * ────────────────\n * The handler is transport-agnostic; it sends responses via the\n * `AgentServerTransport` injected at construction. The actual\n * agent-loop work for a `session/prompt` turn is delegated to the\n * caller-provided `runTurn` callback, which receives the prompt\n * blocks and the per-turn AbortSignal and resolves with the final\n * stopReason. Updates are streamed via the `emit` callback passed\n * to `runTurn`; the handler wraps each as a `session/update`\n * notification.\n *\n * This separation keeps the handler unit-testable: tests can supply\n * a fake `runTurn` that yields a canned sequence of updates, and\n * assert on the JSON-RPC traffic the handler produces. A real\n * production caller wires `runTurn` to a core `Agent` instance.\n *\n * Concurrency\n * ───────────\n * Each session is single-threaded (one active turn at a time). The\n * handler keeps a per-session AbortController so a `session/cancel`\n * notification can stop the running turn mid-stream without tearing\n * down the session. Multiple sessions can be active concurrently.\n */\nimport { ACP_PROTOCOL_VERSION, type StopReason, type ContentBlock, type PlanEntry, type UsageCost } from '../types/acp-v1.js';\nimport type { AgentServerTransport } from './stdio-transport.js';\nimport type { ACPMessage } from '../types/acp-messages.js';\n\n// Transport's `send` is typed `ACPMessage` which predates v1 and\n// doesn't carry a `jsonrpc` field. The runtime is fine — the\n// transport just `JSON.stringify`s the message — so cast at the\n// boundary.\ntype WireMessage = { jsonrpc?: '2.0'; id?: string | number; method?: string; params?: unknown; result?: unknown; error?: unknown };\nfunction toWire(msg: WireMessage): ACPMessage {\n return msg as unknown as ACPMessage;\n}\n\nexport const WRONGSTACK_VERSION = '0.263.0';\n\n/** What kinds of content the agent accepts in a prompt. */\nexport interface PromptCapabilities {\n image: boolean;\n audio: boolean;\n embeddedContext: boolean;\n}\n\nexport interface AgentCapabilities {\n loadSession: boolean;\n promptCapabilities: PromptCapabilities;\n}\n\nexport interface RunTurnInput {\n sessionId: string;\n /** Content blocks the client sent. */\n prompt: readonly ContentBlock[];\n /** Cancelled when the client sends `session/cancel` for this session. */\n signal: AbortSignal;\n}\n\nexport interface RunTurnResult {\n stopReason: StopReason;\n /** Optional summary text the agent produced. */\n text?: string;\n plan?: PlanEntry[];\n usage?: { used: number; size: number; cost?: UsageCost | undefined };\n}\n\n/**\n * The agent's per-turn work. Streams `SessionUpdate` notifications to\n * `emit` and resolves with the final stopReason. Errors thrown from\n * this iterable are converted to a `prompt_failed` JSON-RPC error.\n */\nexport type RunTurn = (\n input: RunTurnInput,\n emit: (update: unknown) => void,\n) => Promise<RunTurnResult>;\n\nexport interface SessionState {\n id: string;\n cwd: string;\n /** Per-turn abort signal — aborted when the session is cancelled or closed. */\n abort: AbortController;\n /** Active mode, advertised to the client in current_mode_update. */\n modeId: string;\n /** Created at, for session/list ordering. */\n createdAt: string;\n /** Last activity timestamp, for session/info_update. */\n updatedAt: string;\n /** Optional human title. */\n title?: string;\n}\n\n/** MCP-style session mode advertised in current_mode_update. */\nexport interface SessionMode {\n id: string;\n name: string;\n description?: string | undefined;\n}\n\nexport interface SessionConfigOption {\n id: string;\n name: string;\n type: 'select' | string;\n currentValue: string;\n options: { value: string; name: string; description?: string | undefined }[];\n}\n\nexport interface ProtocolHandlerOptions {\n transport: AgentServerTransport;\n /** Where the server is running; used for new sessions' default cwd. */\n defaultCwd: string;\n /** Agent's per-turn implementation. */\n runTurn: RunTurn;\n /**\n * Optional callbacks for the lifecycle events the server should\n * surface to the client. All default to no-ops.\n */\n onSessionNew?: ((state: SessionState) => void) | undefined;\n /** Static list of available modes (advertised to clients). */\n modes?: readonly SessionMode[] | undefined;\n /** Static list of config options. */\n configOptions?: readonly SessionConfigOption[] | undefined;\n /** Agent name advertised in initialize. */\n agentName?: string | undefined;\n}\n\n/** Single global mode id, sufficient for v1. */\nconst DEFAULT_MODE_ID = 'code';\n\nconst DEFAULT_MODES: readonly SessionMode[] = [\n {\n id: DEFAULT_MODE_ID,\n name: 'Code',\n description: 'Default agent mode for code-generation tasks.',\n },\n];\n\nexport class ACPProtocolHandler {\n private readonly transport: AgentServerTransport;\n private readonly defaultCwd: string;\n private readonly runTurn: RunTurn;\n private readonly onSessionNew: (state: SessionState) => void;\n private readonly modes: readonly SessionMode[];\n private readonly configOptions: readonly SessionConfigOption[];\n private readonly agentName: string;\n\n private initialized = false;\n private readonly sessions = new Map<string, SessionState>();\n private nextId = 1;\n\n constructor(opts: ProtocolHandlerOptions) {\n this.transport = opts.transport;\n this.defaultCwd = opts.defaultCwd;\n this.runTurn = opts.runTurn;\n this.onSessionNew = opts.onSessionNew ?? (() => {});\n this.modes = opts.modes ?? DEFAULT_MODES;\n this.configOptions = opts.configOptions ?? [];\n this.agentName = opts.agentName ?? 'wrongstack';\n }\n\n /**\n * Process one inbound message. Returns true if this was a terminal\n * message (rare; reserved for future use by the server's own\n * shutdown signal).\n */\n async handleMessage(msg: unknown): Promise<boolean> {\n if (typeof msg !== 'object' || msg === null) return false;\n const m = msg as { id?: unknown; method?: unknown; params?: unknown; result?: unknown; error?: unknown };\n\n // Response (we never initiate requests, but be defensive).\n if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {\n return false;\n }\n\n // Request (has id, has method, no result/error)\n if (m.id !== undefined && typeof m.method === 'string') {\n return this.handleRequest(m.id as string | number, m.method, m.params);\n }\n\n // Notification (no id, has method)\n if (typeof m.method === 'string') {\n return this.handleNotification(m.method, m.params);\n }\n\n return false;\n }\n\n /** Abort all active turns and drop session state. */\n close(): void {\n for (const [, session] of this.sessions) {\n session.abort.abort();\n }\n this.sessions.clear();\n }\n\n // ────────────────────────────────────────────────────────────────────\n // Requests\n // ────────────────────────────────────────────────────────────────────\n\n private async handleRequest(\n id: string | number,\n method: string,\n params: unknown,\n ): Promise<boolean> {\n // The only method allowed before initialize is `initialize` itself.\n if (method !== 'initialize' && !this.initialized) {\n await this.sendError(id, -32000, 'Not initialized');\n return false;\n }\n\n try {\n switch (method) {\n case 'initialize':\n return await this.handleInitialize(id, params);\n case 'authenticate':\n return await this.handleAuthenticate(id, params);\n case 'session/new':\n return await this.handleSessionNew(id, params);\n case 'session/load':\n return await this.handleSessionLoad(id, params);\n case 'session/prompt':\n return await this.handleSessionPrompt(id, params);\n case 'session/set_mode':\n return await this.handleSetMode(id, params);\n case 'session/set_config_option':\n return await this.handleSetConfigOption(id, params);\n case 'session/list':\n return await this.handleSessionList(id);\n default:\n await this.sendError(id, -32601, `Unknown method: ${method}`);\n return false;\n }\n } catch (err) {\n const { code, message, data } = errorToJsonRpc(err);\n await this.sendError(id, code, message, data);\n return false;\n }\n }\n\n private async handleInitialize(id: string | number, params: unknown): Promise<boolean> {\n const p = (params ?? {}) as { protocolVersion?: unknown };\n const requested = typeof p.protocolVersion === 'number' ? p.protocolVersion : 1;\n if (requested !== ACP_PROTOCOL_VERSION) {\n // v1 spec: \"If the client requests a different protocol version, the\n // agent SHOULD respond with an error and the version it supports.\"\n await this.sendError(\n id,\n -32000,\n `server speaks protocolVersion=${ACP_PROTOCOL_VERSION}, client requested ${requested}`,\n );\n return false;\n }\n this.initialized = true;\n await this.transport.send(toWire({\n jsonrpc: '2.0',\n id,\n result: {\n protocolVersion: ACP_PROTOCOL_VERSION,\n agentCapabilities: {\n loadSession: true,\n promptCapabilities: {\n image: false,\n audio: false,\n embeddedContext: true,\n },\n },\n agentInfo: {\n name: this.agentName,\n title: 'WrongStack',\n version: WRONGSTACK_VERSION,\n },\n // Static options advertised at handshake. They are also\n // re-sent on every `current_mode_update` / `config_option_update`\n // notification so late-joining clients see them.\n authMethods: [],\n modes: this.modes,\n configOptions: this.configOptions,\n },\n }));\n return false;\n }\n\n private async handleAuthenticate(id: string | number, _params: unknown): Promise<boolean> {\n // WrongStack doesn't currently require auth. Per spec, a server\n // MAY respond with an unauthenticated outcome to tell the client\n // to proceed without credentials.\n await this.transport.send(toWire({\n jsonrpc: '2.0',\n id,\n result: { outcome: 'unauthenticated' },\n }));\n return false;\n }\n\n private async handleSessionNew(id: string | number, params: unknown): Promise<boolean> {\n const p = (params ?? {}) as { cwd?: unknown; mcpServers?: unknown };\n const cwd = typeof p.cwd === 'string' ? p.cwd : this.defaultCwd;\n const sessionId = `sess_${this.allocId()}`;\n const now = new Date().toISOString();\n const state: SessionState = {\n id: sessionId,\n cwd,\n abort: new AbortController(),\n modeId: DEFAULT_MODE_ID,\n createdAt: now,\n updatedAt: now,\n };\n this.sessions.set(sessionId, state);\n this.onSessionNew(state);\n\n // Per spec, the server MAY emit current_mode_update /\n // config_option_update / available_commands_update notifications\n // immediately after session/new to populate the client UI. We do.\n await this.sendNotification({\n sessionId,\n update: {\n sessionUpdate: 'current_mode_update',\n modeId: this.modes[0]?.id ?? DEFAULT_MODE_ID,\n },\n });\n if (this.configOptions.length > 0) {\n await this.sendNotification({\n sessionId,\n update: {\n sessionUpdate: 'config_option_update',\n configOptions: [...this.configOptions],\n },\n });\n }\n\n await this.transport.send(toWire({\n jsonrpc: '2.0',\n id,\n result: {\n sessionId,\n modes: this.modes,\n configOptions: this.configOptions,\n },\n }));\n return false;\n }\n\n private async handleSessionLoad(id: string | number, params: unknown): Promise<boolean> {\n // v1 spec: \"If `loadSession: true` is not in the agent's\n // capabilities, the client SHOULD NOT call this method.\" We\n // declared loadSession: true in initialize, so we accept it.\n // We don't persist sessions across restarts yet — for now,\n // session/load is a no-op alias of session/new.\n return this.handleSessionNew(id, params);\n }\n\n private async handleSessionPrompt(id: string | number, params: unknown): Promise<boolean> {\n const p = (params ?? {}) as { sessionId?: unknown; prompt?: unknown };\n const sessionId = typeof p.sessionId === 'string' ? p.sessionId : null;\n if (!sessionId || !this.sessions.has(sessionId)) {\n await this.sendError(id, -32000, 'unknown or missing sessionId');\n return false;\n }\n if (!Array.isArray(p.prompt)) {\n await this.sendError(id, -32602, 'prompt must be an array of content blocks');\n return false;\n }\n const session = this.sessions.get(sessionId)!;\n\n // If the previous turn was cancelled, recreate the AbortController\n // so a stale signal doesn't cancel the new turn.\n if (session.abort.signal.aborted) {\n session.abort = new AbortController();\n }\n\n const turnSignal = new AbortController();\n // Forward session/cancel notifications to the turn's signal.\n const onCancel = (): void => turnSignal.abort();\n session.abort.signal.addEventListener('abort', onCancel, { once: true });\n\n let result: RunTurnResult;\n try {\n result = await this.runTurn(\n { sessionId, prompt: p.prompt as ContentBlock[], signal: turnSignal.signal },\n (update) => this.sendNotification({ sessionId, update }),\n );\n } catch (err) {\n session.abort.signal.removeEventListener('abort', onCancel);\n const { code, message, data } = errorToJsonRpc(err);\n await this.sendError(id, code, message, data);\n return false;\n }\n session.abort.signal.removeEventListener('abort', onCancel);\n session.updatedAt = new Date().toISOString();\n\n await this.transport.send(toWire({\n jsonrpc: '2.0',\n id,\n result: { stopReason: result.stopReason },\n }));\n return false;\n }\n\n private async handleSetMode(id: string | number, params: unknown): Promise<boolean> {\n const p = (params ?? {}) as { sessionId?: unknown; modeId?: unknown };\n const sessionId = typeof p.sessionId === 'string' ? p.sessionId : null;\n const modeId = typeof p.modeId === 'string' ? p.modeId : null;\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n if (!session || !modeId || !this.modes.some((m) => m.id === modeId)) {\n await this.sendError(id, -32602, 'invalid sessionId or modeId');\n return false;\n }\n session.modeId = modeId;\n session.updatedAt = new Date().toISOString();\n await this.sendNotification({\n sessionId,\n update: { sessionUpdate: 'current_mode_update', modeId },\n });\n await this.transport.send(toWire({ jsonrpc: '2.0', id, result: {} }));\n return false;\n }\n\n private async handleSetConfigOption(id: string | number, params: unknown): Promise<boolean> {\n const p = (params ?? {}) as { sessionId?: unknown; configOptionId?: unknown; value?: unknown };\n const sessionId = typeof p.sessionId === 'string' ? p.sessionId : null;\n const optionId = typeof p.configOptionId === 'string' ? p.configOptionId : null;\n const value = typeof p.value === 'string' ? p.value : null;\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n const option = optionId ? this.configOptions.find((o) => o.id === optionId) : undefined;\n if (!session || !option || value === null || !option.options.some((o) => o.value === value)) {\n await this.sendError(id, -32602, 'invalid sessionId, configOptionId, or value');\n return false;\n }\n option.currentValue = value;\n session.updatedAt = new Date().toISOString();\n await this.sendNotification({\n sessionId,\n update: {\n sessionUpdate: 'config_option_update',\n configOptions: [...this.configOptions],\n },\n });\n await this.transport.send(toWire({ jsonrpc: '2.0', id, result: {} }));\n return false;\n }\n\n private async handleSessionList(id: string | number): Promise<boolean> {\n const sessions = Array.from(this.sessions.values()).map((s) => {\n const out: { sessionId: string; cwd: string; updatedAt: string; title?: string } = {\n sessionId: s.id,\n cwd: s.cwd,\n updatedAt: s.updatedAt,\n };\n if (s.title !== undefined) out.title = s.title;\n return out;\n });\n await this.transport.send(toWire({\n jsonrpc: '2.0',\n id,\n result: { sessions },\n }));\n return false;\n }\n\n // ────────────────────────────────────────────────────────────────────\n // Notifications\n // ────────────────────────────────────────────────────────────────────\n\n private async handleNotification(method: string, params: unknown): Promise<boolean> {\n switch (method) {\n case 'session/cancel': {\n const p = (params ?? {}) as { sessionId?: unknown };\n const sessionId = typeof p.sessionId === 'string' ? p.sessionId : null;\n const session = sessionId ? this.sessions.get(sessionId) : undefined;\n if (session) {\n session.abort.abort();\n }\n return false;\n }\n case 'exit':\n // Client is shutting down. Best-effort: abort all sessions.\n this.close();\n return true;\n default:\n // Unknown notification — log and ignore.\n return false;\n }\n }\n\n // ────────────────────────────────────────────────────────────────────\n // Wire helpers\n // ────────────────────────────────────────────────────────────────────\n\n private async sendNotification(params: unknown): Promise<void> {\n await this.transport.send(toWire({ jsonrpc: '2.0', method: 'session/update', params }));\n }\n\n private async sendError(\n id: string | number,\n code: number,\n message: string,\n data?: unknown,\n ): Promise<void> {\n const error: { code: number; message: string; data?: unknown } = { code, message };\n if (data !== undefined) error.data = data;\n await this.transport.send(toWire({ jsonrpc: '2.0', id, error }));\n }\n\n private allocId(): number {\n return this.nextId++;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Error mapping\n// ─────────────────────────────────────────────────────────────────────────\n\nfunction errorToJsonRpc(err: unknown): { code: number; message: string; data?: unknown } {\n if (err && typeof err === 'object') {\n const e = err as { code?: unknown; message?: unknown; data?: unknown };\n if (typeof e.code === 'number' && typeof e.message === 'string') {\n const result: { code: number; message: string; data?: unknown } = {\n code: e.code,\n message: e.message,\n };\n if (e.data !== undefined) result.data = e.data;\n return result;\n }\n }\n const message = err instanceof Error ? err.message : String(err);\n return { code: -32603, message };\n}\n","/**\n * StdioTransport — bidirectional stdin/stdout communication for ACP.\n *\n * ACP uses newline-delimited JSON-RPC 2.0 messages over stdio:\n * client → agent: JSON-RPC request/notification on stdin\n * agent → client: JSON-RPC response/notification on stdout\n *\n * Start message: clients look for the `[wstack-acp]` marker on stdout before\n * treating subsequent lines as protocol messages.\n */\nimport { expectDefined, writeErr } from '@wrongstack/core';\nimport type { ACPMessage } from '../types/acp-messages.js';\nexport interface AgentServerTransport {\n send(msg: ACPMessage): Promise<void>;\n sendRaw(chunk: string): void;\n read(): Promise<ACPMessage | null>;\n close(): void;\n onMessage(handler: (msg: ACPMessage) => void): () => void;\n}\n\nexport class StdioTransport implements AgentServerTransport {\n private readonly stdin = process.stdin;\n private readonly stdout = process.stdout;\n private readonly stderr = process.stderr;\n\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n\n constructor() {\n this.stdin.resume();\n this.stdin.setEncoding('utf8');\n this.stdin.on('data', (chunk: string) => this.onData(chunk));\n this.stdin.on('end', () => this.handleClose());\n this.stdin.on('error', (err: Error) => this.failAll(err));\n }\n\n sendStartupMarker(): void {\n this.stdout.write('[wstack-acp]\\n', 'utf8');\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (this.closed) return Promise.resolve();\n return new Promise((resolve) => {\n const line = JSON.stringify(msg) + '\\n';\n this.stdout.write(line, 'utf8', () => resolve());\n });\n }\n\n sendRaw(chunk: string): void {\n this.stdout.write(chunk, 'utf8');\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0) return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n close(): void {\n this.closed = true;\n this.stdin.pause();\n this.resolveRead?.(null);\n this.resolveRead = null;\n }\n\n private onData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields ≥1 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch (err) {\n this.stderr.write(`[wstack-acp parse error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else {\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch (err) {\n this.stderr.write(`[wstack-acp handler error] ${err}\\n`, 'utf8');\n }\n }\n }\n\n private handleClose(): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n }\n\n private failAll(err: Error): void {\n this.stderr.write(`[wstack-acp stdin error] ${err.message}\\n`, 'utf8');\n this.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// ClientTransport — spawns a child ACP agent process (DIR-1)\n// ---------------------------------------------------------------------------\n\nimport type { EventEmitter } from 'node:events';\n\nexport interface ClientTransportOptions {\n command: string;\n args?: string[] | undefined;\n env?: Record<string, string>;\n cwd?: string | undefined;\n handshakeTimeoutMs?: number | undefined;\n /**\n * Set to true when the child is an external ACP agent (Claude Code,\n * Gemini CLI, Codex CLI, …) that does NOT emit a `[wstack-acp]\\n`\n * marker on startup. The v1 client (`ACPSession`) sets this; the\n * server-side transport (the default) keeps the marker check.\n */\n skipHandshakeMarker?: boolean | undefined;\n}\n\nexport interface ACPChildProcess extends EventEmitter {\n stdout: NodeJS.ReadableStream;\n stdin: NodeJS.WritableStream;\n stderr: NodeJS.ReadableStream;\n pid: number | undefined;\n kill(): void;\n}\n\nexport class ClientTransport {\n private child: ACPChildProcess | null = null;\n private buffer = '';\n private readonly handlers = new Set<(msg: ACPMessage) => void>();\n private closed = false;\n private resolveRead: ((msg: ACPMessage | null) => void) | null = null;\n private messageQueue: ACPMessage[] = [];\n private readonly opts: Required<Pick<ClientTransportOptions, 'handshakeTimeoutMs'>> &\n ClientTransportOptions;\n\n constructor(options: ClientTransportOptions) {\n this.opts = {\n handshakeTimeoutMs: 30_000,\n ...options,\n };\n }\n\n async start(): Promise<void> {\n if (this.child) return;\n const [{ spawn }, { buildChildEnv }] = await Promise.all([\n import('node:child_process'),\n import('@wrongstack/core'),\n ]);\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(`ACP child process failed to start within ${this.opts.handshakeTimeoutMs}ms`),\n );\n }, this.opts.handshakeTimeoutMs);\n\n try {\n this.child = spawn(this.opts.command, this.opts.args ?? [], {\n env: { ...buildChildEnv(), ...this.opts.env },\n cwd: this.opts.cwd,\n stdio: ['pipe', 'pipe', 'pipe'],\n windowsHide: true,\n // On Windows, most ACP-supporting tools (claude, gemini, codex,\n // qwen, copilot) are installed as `.cmd` shims under\n // AppData\\Roaming\\npm\\. Node's spawn won't find them via\n // `shell: false` because the .cmd extension is not in the\n // default PATHEXT lookup. The argv here is always from our\n // own static catalog or from a hardcoded spec, never from\n // user input, so shell-expansion is bounded.\n shell: process.platform === 'win32',\n }) as unknown as ACPChildProcess;\n /* v8 ignore start -- spawn() throwing synchronously is a defensive guard (e.g. argv0 type errors); the realistic async failure path is the child 'error' event, covered by tests. */\n } catch (err) {\n clearTimeout(timeout);\n reject(err);\n return;\n }\n /* v8 ignore stop */\n\n const child = this.child;\n\n child.stdout.setEncoding('utf8');\n\n const onReady = (): void => {\n child.stdout.on('data', (c: string) => this.onChildData(c));\n child.stderr.on('data', (c: string) => this.onChildError(c));\n child.on('close', (code: number | null) => this.onChildClose(code));\n clearTimeout(timeout);\n resolve();\n };\n\n if (this.opts.skipHandshakeMarker) {\n // External ACP agents don't emit a startup marker — they're\n // ready to accept JSON-RPC immediately. Resolve as soon as\n // the child process is spawned and stdout is flowing.\n onReady();\n return;\n }\n\n const waitForMarker = (chunk: string) => {\n this.buffer += chunk;\n const idx = this.buffer.indexOf('[wstack-acp]\\n');\n if (idx !== -1) {\n this.buffer = this.buffer.slice(idx + '[wstack-acp]\\n'.length);\n child.stdout.removeListener('data', waitForMarker);\n onReady();\n }\n };\n\n child.stdout.on('data', waitForMarker);\n child.stdout.on('error', (err: Error) => {\n clearTimeout(timeout);\n reject(err);\n });\n child.on('error', (err: Error) => {\n clearTimeout(timeout);\n reject(err);\n });\n });\n }\n\n send(msg: ACPMessage): Promise<void> {\n if (!this.child) return Promise.reject(new Error('ClientTransport not started'));\n return new Promise((resolve, reject) => {\n const line = JSON.stringify(msg) + '\\n';\n this.child?.stdin.write(line, 'utf8', (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n read(): Promise<ACPMessage | null> {\n if (this.messageQueue.length > 0) return Promise.resolve(expectDefined(this.messageQueue.shift()));\n if (this.closed) return Promise.resolve(null);\n return new Promise((resolve) => {\n this.resolveRead = resolve;\n });\n }\n\n onMessage(handler: (msg: ACPMessage) => void): () => void {\n this.handlers.add(handler);\n return () => this.handlers.delete(handler);\n }\n\n stop(): void {\n if (!this.child) return;\n this.closed = true;\n try {\n this.child.kill();\n } catch {\n // already dead\n }\n this.child = null;\n }\n\n private onChildData(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n /* v8 ignore next -- split() always yields ≥1 element, so pop() is never undefined; the ?? '' is defensive. */\n this.buffer = lines.pop() ?? '';\n\n for (const raw of lines) {\n if (!raw.trim()) continue;\n try {\n this.dispatch(JSON.parse(raw) as ACPMessage);\n } catch {\n // skip malformed\n }\n }\n }\n\n private onChildError(chunk: string): void {\n writeErr(`[acp-child stderr] ${chunk}`);\n }\n\n private onChildClose(code: number | null): void {\n this.closed = true;\n this.resolveRead?.(null);\n this.resolveRead = null;\n if (code !== 0 && code !== null) {\n writeErr(`[acp-child exited with code ${code}]\\n`);\n }\n }\n\n private dispatch(msg: ACPMessage): void {\n if (this.resolveRead) {\n const resolve = this.resolveRead;\n this.resolveRead = null;\n resolve(msg);\n } else {\n this.messageQueue.push(msg);\n }\n for (const handler of this.handlers) {\n try {\n handler(msg);\n } catch {\n // non-fatal\n }\n }\n }\n}\n","/**\n * WrongStackACPServer — ACP v1 server-side entry point.\n *\n * Exposes WrongStack as an ACP-compatible agent. ACP clients (Zed, JetBrains\n * Junie, VS Code ACP extension) spawn this as a subprocess, send JSON-RPC\n * messages over stdio, and receive v1-protocol responses.\n *\n * Usage:\n * node dist/agent/wrongstack-acp-agent.js\n *\n * Or via the CLI:\n * wstack acp-server\n *\n * Wiring a real agent: this class is the surface; the bootstrap\n * binary uses a no-op echo by default so the binary is a useful\n * connectivity smoke test. For a real server, instantiate\n * `WrongStackACPServer` programmatically and pass a `runTurn`\n * produced by `makeACPServerAgentTurn({ agentFor: ... })` from\n * `./server-agent-turn.js`. The factory is responsible for building\n * a real core `Agent` (with the right provider, model, system prompt,\n * etc.) per session.\n *\n * Startup: prints the legacy `[wstack-acp]\\n` marker (kept for backward\n * compatibility with the old `StdioTransport` handshake) so the client\n * knows the protocol boundary. v1 initialize is then sent by the client\n * and answered by `ACPProtocolHandler`.\n */\nimport { fileURLToPath } from 'node:url';\nimport { writeErr } from '@wrongstack/core';\nimport {\n ACPProtocolHandler,\n type RunTurn,\n type RunTurnResult,\n} from './protocol-handler.js';\nimport { StdioTransport } from './stdio-transport.js';\n\nexport interface WrongStackACPServerOptions {\n /**\n * Per-turn implementation. If omitted, the server runs a no-op turn\n * that just resolves with `end_turn`. The real production usage\n * passes the result of `makeACPServerAgentTurn({ agentFor: ... })`\n * from `./server-agent-turn.js` so each session gets a real\n * `Agent` instance.\n */\n runTurn?: RunTurn | undefined;\n /** Default cwd for new sessions. Defaults to the current process cwd. */\n defaultCwd?: string | undefined;\n /** Agent name advertised in initialize. */\n agentName?: string | undefined;\n}\n\nexport class WrongStackACPServer {\n private readonly transport: StdioTransport;\n private readonly handler: ACPProtocolHandler;\n private running = false;\n\n constructor(opts: WrongStackACPServerOptions = {}) {\n this.transport = new StdioTransport();\n const runTurn: RunTurn = opts.runTurn ?? defaultEchoRunTurn;\n this.handler = new ACPProtocolHandler({\n transport: this.transport,\n defaultCwd: opts.defaultCwd ?? process.cwd(),\n runTurn,\n agentName: opts.agentName,\n });\n }\n\n /**\n * Start the server. Blocks until the client disconnects.\n *\n * 1. Print the legacy `[wstack-acp]\\n` marker so the client knows the\n * process is the ACP server (the old `StdioTransport` handshake).\n * 2. Loop: read messages, dispatch to the handler, until EOF / error.\n */\n async start(): Promise<void> {\n this.transport.sendStartupMarker();\n this.running = true;\n while (this.running) {\n const msg = await this.transport.read();\n if (!msg) break; // EOF\n const terminal = await this.handler.handleMessage(msg);\n if (terminal) break;\n }\n this.transport.close();\n }\n\n /** Stop the server. */\n stop(): void {\n this.running = false;\n this.transport.close();\n }\n}\n\n/**\n * Default per-turn implementation: a no-op that echoes nothing useful\n * and returns `end_turn`. Lets the server boot end-to-end without\n * needing the core Agent factory (which would couple this entrypoint\n * to a long-lived model provider). The real implementation is\n * `ACPServerAgentTurn` (follow-up PR) that wires a core `Agent`.\n */\nconst defaultEchoRunTurn: RunTurn = async (_input, _emit): Promise<RunTurnResult> => {\n return { stopReason: 'end_turn' };\n};\n\n/**\n * Bootstrap function for `node dist/agent/wrongstack-acp-agent.js`.\n * Instantiates the server with the default (no-op) runTurn so the\n * binary is useful as a connectivity smoke test.\n *\n * In practice the CLI will instantiate and run `WrongStackACPServer`\n * directly, passing a real `runTurn` wired to a core `Agent`.\n */\n/* v8 ignore start -- process entrypoint: bootstrap + auto-start only run when launched as `node wrongstack-acp-agent.js`, never on import (which the CLI does to reuse the class). */\nasync function main(): Promise<void> {\n const server = new WrongStackACPServer();\n await server.start();\n}\n\nconst isEntrypoint =\n process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1];\nif (isEntrypoint) {\n main().catch((err) => {\n writeErr(`[wstack-acp fatal] ${err}\\n`);\n process.exit(1);\n });\n}\n/* v8 ignore stop */\n"]}