@claudexor/acp-server 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 joi-lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @claudexor/acp-server
2
+
3
+ Internal package of [Claudexor](https://github.com/razzant/claudexor) — Minimal ACP server (stdio JSON-RPC) exposing Claudexor as a meta-agent to editors. Runner injected.
4
+
5
+ Published as part of the Claudexor toolchain; it follows the monorepo's
6
+ lockstep version and has no separate semver contract. Use the `claudexor`
7
+ CLI (or `@claudexor/cli`) as the supported entry point.
@@ -0,0 +1,76 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ export declare const ACP_PROTOCOL_VERSION = 1;
3
+ export interface RunnerHooks {
4
+ /** Live RunEvent sink (mirrors events.jsonl) for session/update streaming. */
5
+ onEvent?: (event: any) => void;
6
+ /** Interactive question surface; resolve with answers or null to decline. */
7
+ onInteraction?: (ctx: any) => Promise<any | null>;
8
+ /** Cooperative cancellation (session/cancel aborts the underlying run). */
9
+ signal?: AbortSignal;
10
+ }
11
+ export type RunnerFn = (params: any, hooks?: RunnerHooks) => Promise<unknown>;
12
+ export interface AcpServerOptions {
13
+ runner: RunnerFn;
14
+ transport: {
15
+ read: Readable;
16
+ write: Writable;
17
+ };
18
+ name?: string;
19
+ version?: string;
20
+ }
21
+ /**
22
+ * Minimal Agent Client Protocol server (JSON-RPC over stdio). Exposes Claudexor as
23
+ * a meta-agent: editors can talk to Claudexor instead of a single harness.
24
+ * Implements initialize / session/new / session/prompt / session/cancel, streams
25
+ * live run events as session/update notifications, and forwards interactive
26
+ * harness questions as session/request_permission round-trips.
27
+ */
28
+ export declare class AcpServer {
29
+ private readonly opts;
30
+ private sessions;
31
+ /** Editor-provided project root per session (anchors runs to the user's project). */
32
+ private readonly sessionCwds;
33
+ private nextRequestId;
34
+ private readonly pendingRequests;
35
+ /** Active run per session: lets session/cancel abort and keeps prompts serial. */
36
+ private readonly activeRuns;
37
+ /** Fallback tool_call ids keyed by `${sessionId}:${toolName}` — a FIFO QUEUE of
38
+ * synthetic ids awaiting completion by a tool_result that arrives WITHOUT a
39
+ * use_id. A queue (not a single slot) so two in-flight same-name use_id-less
40
+ * calls don't clobber each other: each PUSHes on tool_call, the matching
41
+ * tool_result SHIFTs one (oldest-first). use_id-bearing calls match directly
42
+ * by id and never touch this. */
43
+ private readonly openToolCalls;
44
+ constructor(opts: AcpServerOptions);
45
+ serve(): Promise<void>;
46
+ private write;
47
+ private reply;
48
+ /** Spec-coded JSON-RPC error response ({code, message}) — never an ad-hoc shape. */
49
+ private error;
50
+ private notify;
51
+ /**
52
+ * Server->client JSON-RPC request (e.g. session/request_permission),
53
+ * bounded by a deadline: an editor that never answers must not leave the
54
+ * request pending forever (the engine's own interaction timeout would
55
+ * eventually fire, but the ACP surface owns its half of the contract —
56
+ * every request resolves, and the pending slot is always reclaimed).
57
+ * Resolves null on deadline.
58
+ */
59
+ private request;
60
+ handle(msg: any): Promise<void>;
61
+ /**
62
+ * JSON-RPC notifications (no `id`) get NO response. ACP `session/cancel` is a
63
+ * notification: it aborts the underlying run (cooperative cancellation) and the
64
+ * in-flight session/prompt then resolves with stopReason "cancelled".
65
+ */
66
+ private handleNotification;
67
+ /** Thin RunEvent -> session/update projection (no business logic). */
68
+ private forwardRunEvent;
69
+ /**
70
+ * Interactive question -> one session/request_permission round-trip per
71
+ * question (ACP's permission options carry a flat choice list). A cancelled
72
+ * or unanswered request resolves null and the engine declines benignly.
73
+ */
74
+ private requestAnswers;
75
+ }
76
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKtD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAClD,2EAA2E;IAC3E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAA;KAAE,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAAa,SAAS;IAgBR,OAAO,CAAC,QAAQ,CAAC,IAAI;IAfjC,OAAO,CAAC,QAAQ,CAAqB;IACrC,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4C;IAC5E,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsC;IACjE;;;;;qCAKiC;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+B;gBAEhC,IAAI,EAAE,gBAAgB;IAE7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,KAAK;IAIb,oFAAoF;IACpF,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,MAAM;IAId;;;;;;;OAOG;IACH,OAAO,CAAC,OAAO;IAkBT,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAwIrC;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAU1B,sEAAsE;IACtE,OAAO,CAAC,eAAe;IAuFvB;;;;OAIG;YACW,cAAc;CA8E7B"}
package/dist/index.js ADDED
@@ -0,0 +1,435 @@
1
+ import { existsSync, lstatSync } from "node:fs";
2
+ import { createInterface } from "node:readline";
3
+ import { isAbsolute } from "node:path";
4
+ import { extractPromptText, summarizeResult } from "./prompt.js";
5
+ import { validateRunControls } from "./validate.js";
6
+ export const ACP_PROTOCOL_VERSION = 1;
7
+ /**
8
+ * Minimal Agent Client Protocol server (JSON-RPC over stdio). Exposes Claudexor as
9
+ * a meta-agent: editors can talk to Claudexor instead of a single harness.
10
+ * Implements initialize / session/new / session/prompt / session/cancel, streams
11
+ * live run events as session/update notifications, and forwards interactive
12
+ * harness questions as session/request_permission round-trips.
13
+ */
14
+ export class AcpServer {
15
+ opts;
16
+ sessions = new Set();
17
+ /** Editor-provided project root per session (anchors runs to the user's project). */
18
+ sessionCwds = new Map();
19
+ nextRequestId = 1;
20
+ pendingRequests = new Map();
21
+ /** Active run per session: lets session/cancel abort and keeps prompts serial. */
22
+ activeRuns = new Map();
23
+ /** Fallback tool_call ids keyed by `${sessionId}:${toolName}` — a FIFO QUEUE of
24
+ * synthetic ids awaiting completion by a tool_result that arrives WITHOUT a
25
+ * use_id. A queue (not a single slot) so two in-flight same-name use_id-less
26
+ * calls don't clobber each other: each PUSHes on tool_call, the matching
27
+ * tool_result SHIFTs one (oldest-first). use_id-bearing calls match directly
28
+ * by id and never touch this. */
29
+ openToolCalls = new Map();
30
+ constructor(opts) {
31
+ this.opts = opts;
32
+ }
33
+ async serve() {
34
+ const rl = createInterface({ input: this.opts.transport.read });
35
+ for await (const line of rl) {
36
+ const trimmed = line.trim();
37
+ if (!trimmed)
38
+ continue;
39
+ let msg;
40
+ try {
41
+ msg = JSON.parse(trimmed);
42
+ }
43
+ catch {
44
+ // JSON-RPC 2.0 prescribes a Parse Error response with id null —
45
+ // a malformed frame must never vanish silently (fail-loud surface).
46
+ this.write({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error: invalid JSON frame" } });
47
+ continue;
48
+ }
49
+ // Responses to OUR outgoing requests carry an id and no method.
50
+ if (msg.id !== undefined && msg.method === undefined) {
51
+ const pending = this.pendingRequests.get(String(msg.id));
52
+ if (pending) {
53
+ this.pendingRequests.delete(String(msg.id));
54
+ pending(msg.result ?? msg.error ?? null);
55
+ }
56
+ continue;
57
+ }
58
+ // A JSON-RPC NOTIFICATION (method present, no id) must be handled WITHOUT
59
+ // a response — replying to a notification violates JSON-RPC. session/cancel
60
+ // arrives this way (id-less) and aborts the active run silently.
61
+ if (msg.id === undefined || msg.id === null) {
62
+ this.handleNotification(msg);
63
+ continue;
64
+ }
65
+ // NEVER block the read loop on a handler: session/prompt runs for
66
+ // minutes and the loop must keep consuming session/request_permission
67
+ // responses and session/cancel while the run is active. Handler errors
68
+ // are reported per-request, not thrown into the loop.
69
+ void this.handle(msg).catch((err) => {
70
+ this.write({ jsonrpc: "2.0", id: msg.id, error: { code: -32603, message: err instanceof Error ? err.message : String(err) } });
71
+ });
72
+ }
73
+ }
74
+ write(obj) {
75
+ this.opts.transport.write.write(JSON.stringify(obj) + "\n");
76
+ }
77
+ reply(id, result) {
78
+ this.write({ jsonrpc: "2.0", id, result });
79
+ }
80
+ /** Spec-coded JSON-RPC error response ({code, message}) — never an ad-hoc shape. */
81
+ error(id, code, message, data) {
82
+ this.write({ jsonrpc: "2.0", id, error: { code, message, ...(data ? { data } : {}) } });
83
+ }
84
+ notify(method, params) {
85
+ this.write({ jsonrpc: "2.0", method, params });
86
+ }
87
+ /**
88
+ * Server->client JSON-RPC request (e.g. session/request_permission),
89
+ * bounded by a deadline: an editor that never answers must not leave the
90
+ * request pending forever (the engine's own interaction timeout would
91
+ * eventually fire, but the ACP surface owns its half of the contract —
92
+ * every request resolves, and the pending slot is always reclaimed).
93
+ * Resolves null on deadline.
94
+ */
95
+ request(method, params, deadlineMs) {
96
+ const id = `srv-${this.nextRequestId++}`;
97
+ return new Promise((resolve) => {
98
+ let timer;
99
+ if (deadlineMs !== undefined && Number.isFinite(deadlineMs)) {
100
+ timer = setTimeout(() => {
101
+ if (this.pendingRequests.delete(id))
102
+ resolve(null);
103
+ }, Math.max(0, deadlineMs));
104
+ timer.unref?.();
105
+ }
106
+ this.pendingRequests.set(id, (result) => {
107
+ if (timer)
108
+ clearTimeout(timer);
109
+ resolve(result);
110
+ });
111
+ this.write({ jsonrpc: "2.0", id, method, params });
112
+ });
113
+ }
114
+ async handle(msg) {
115
+ const { id, method, params } = msg;
116
+ switch (method) {
117
+ case "initialize":
118
+ this.reply(id, {
119
+ protocolVersion: ACP_PROTOCOL_VERSION,
120
+ agentInfo: { name: this.opts.name ?? "claudexor", version: this.opts.version ?? "dev" },
121
+ agentCapabilities: { promptCapabilities: { image: false, audio: false, embeddedContext: true } },
122
+ // ACP v1 InitializeResponse carries authMethods; strict clients
123
+ // deserialize it. Claudexor needs no editor-side auth: empty list.
124
+ authMethods: [],
125
+ });
126
+ return;
127
+ case "session/new": {
128
+ const sessionId = `acp-${Math.random().toString(36).slice(2, 10)}`;
129
+ // The editor's cwd anchors all of this session's runs to the project the
130
+ // user is actually in (previously ignored -> runs hit the server's cwd).
131
+ if (!params || !Object.prototype.hasOwnProperty.call(params, "cwd")) {
132
+ this.error(id, -32600, "session/new cwd is required");
133
+ return;
134
+ }
135
+ if (typeof params.cwd !== "string" || !params.cwd.trim()) {
136
+ this.error(id, -32600, "session/new cwd must be a non-empty absolute path string");
137
+ return;
138
+ }
139
+ const cwd = params.cwd.trim();
140
+ if (!isAbsolute(cwd)) {
141
+ this.error(id, -32600, "session/new cwd must be an absolute path");
142
+ return;
143
+ }
144
+ if (!existsSync(cwd) || !lstatSync(cwd).isDirectory()) {
145
+ this.error(id, -32600, "session/new cwd must be an existing directory");
146
+ return;
147
+ }
148
+ this.sessions.add(sessionId);
149
+ this.sessionCwds.set(sessionId, cwd);
150
+ this.reply(id, { sessionId });
151
+ return;
152
+ }
153
+ case "session/prompt": {
154
+ const sessionId = params?.sessionId;
155
+ const text = extractPromptText(params?.prompt);
156
+ if (!text.trim()) {
157
+ this.error(id, -32600, "prompt must be a non-empty string");
158
+ return;
159
+ }
160
+ if (!sessionId || !this.sessions.has(sessionId) || !this.sessionCwds.has(sessionId)) {
161
+ this.error(id, -32600, "session/prompt requires a known session created with session/new cwd");
162
+ return;
163
+ }
164
+ const runControlError = validateRunControls(params);
165
+ if (runControlError) {
166
+ // The machine-readable class (e.g. inline_secret_rejected) rides
167
+ // JSON-RPC error.data so ACP hosts can branch without parsing prose.
168
+ this.error(id, -32600, runControlError.message, runControlError.code ? { code: runControlError.code } : undefined);
169
+ return;
170
+ }
171
+ // One active run per session: a second prompt while one is running is
172
+ // a protocol misuse. ACP StopReason has no "error" member, so fail loudly
173
+ // as a JSON-RPC error (-32600 Invalid Request) rather than inventing one.
174
+ if (this.activeRuns.has(sessionId)) {
175
+ this.error(id, -32600, `session ${sessionId} already has an active prompt`);
176
+ return;
177
+ }
178
+ const controller = new AbortController();
179
+ this.activeRuns.set(sessionId, controller);
180
+ try {
181
+ const hooks = {
182
+ signal: controller.signal,
183
+ onEvent: (event) => this.forwardRunEvent(sessionId, event),
184
+ onInteraction: (ctx) => this.requestAnswers(sessionId, ctx),
185
+ };
186
+ const result = await this.opts.runner({
187
+ prompt: text,
188
+ mode: params?.mode ?? "agent",
189
+ repoPath: this.sessionCwds.get(sessionId),
190
+ ...(params?.harness !== undefined ? { harness: params.harness } : {}),
191
+ ...(params?.primaryHarness !== undefined
192
+ ? { primaryHarness: params.primaryHarness }
193
+ : {}),
194
+ ...(params?.web !== undefined ? { web: params.web } : {}),
195
+ ...(params?.externalContextPolicy !== undefined
196
+ ? { externalContextPolicy: params.externalContextPolicy }
197
+ : {}),
198
+ ...(params?.model !== undefined ? { model: params.model } : {}),
199
+ ...(params?.effort !== undefined ? { effort: params.effort } : {}),
200
+ ...(params?.n !== undefined ? { n: params.n } : {}),
201
+ ...(params?.race === true ? { race: true } : {}),
202
+ ...(params?.untilClean === true ? { untilClean: true } : {}),
203
+ ...(params?.swarm === true ? { swarm: true } : {}),
204
+ ...(params?.create === true ? { create: true } : {}),
205
+ ...(params?.tests !== undefined ? { tests: params.tests } : {}),
206
+ ...(params?.maxUsd !== undefined ? { maxUsd: params.maxUsd } : {}),
207
+ ...(params?.access !== undefined ? { access: params.access } : {}),
208
+ ...(params?.protectedPathApprovals !== undefined
209
+ ? { protectedPathApprovals: params.protectedPathApprovals }
210
+ : {}),
211
+ ...(params?.reviewerPanel !== undefined ? { reviewerPanel: params.reviewerPanel } : {}),
212
+ ...(params?.reviewerModels !== undefined
213
+ ? { reviewerModels: params.reviewerModels }
214
+ : {}),
215
+ ...(params?.reviewerEfforts !== undefined
216
+ ? { reviewerEfforts: params.reviewerEfforts }
217
+ : {}),
218
+ }, hooks);
219
+ // The turn result is the human-readable summary/answer (the run's
220
+ // primary output), not a raw dumped JSON object the editor can't show.
221
+ const summary = summarizeResult(result);
222
+ if (summary) {
223
+ this.notify("session/update", {
224
+ sessionId,
225
+ update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: summary } },
226
+ });
227
+ }
228
+ this.reply(id, { stopReason: controller.signal.aborted ? "cancelled" : "end_turn" });
229
+ }
230
+ catch (err) {
231
+ if (controller.signal.aborted) {
232
+ this.reply(id, { stopReason: "cancelled" });
233
+ }
234
+ else {
235
+ // A failed turn is a JSON-RPC error (-32603 internal), not an invented
236
+ // StopReason — the ACP StopReason enum has no "error" member.
237
+ this.error(id, -32603, err instanceof Error ? err.message : String(err));
238
+ }
239
+ }
240
+ finally {
241
+ if (this.activeRuns.get(sessionId) === controller)
242
+ this.activeRuns.delete(sessionId);
243
+ }
244
+ return;
245
+ }
246
+ default:
247
+ this.error(id, -32601, `method not found: ${method}`);
248
+ }
249
+ }
250
+ /**
251
+ * JSON-RPC notifications (no `id`) get NO response. ACP `session/cancel` is a
252
+ * notification: it aborts the underlying run (cooperative cancellation) and the
253
+ * in-flight session/prompt then resolves with stopReason "cancelled".
254
+ */
255
+ handleNotification(msg) {
256
+ const { method, params } = msg;
257
+ if (method === "session/cancel") {
258
+ const sessionId = params?.sessionId;
259
+ const active = sessionId ? this.activeRuns.get(sessionId) : undefined;
260
+ if (active)
261
+ active.abort();
262
+ }
263
+ // Unknown notifications are silently ignored — JSON-RPC forbids replying.
264
+ }
265
+ /** Thin RunEvent -> session/update projection (no business logic). */
266
+ forwardRunEvent(sessionId, event) {
267
+ const type = String(event?.type ?? "");
268
+ const p = (event?.payload ?? {});
269
+ if (type === "harness.event") {
270
+ const sub = String(p["type"] ?? "");
271
+ if (sub === "message" && typeof p["text"] === "string" && p["text"].trim()) {
272
+ this.notify("session/update", {
273
+ sessionId,
274
+ update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: p["text"] } },
275
+ });
276
+ return;
277
+ }
278
+ if (sub === "thinking" && typeof p["text"] === "string" && p["text"].trim()) {
279
+ this.notify("session/update", {
280
+ sessionId,
281
+ update: { sessionUpdate: "agent_thought_chunk", content: { type: "text", text: p["text"] } },
282
+ });
283
+ return;
284
+ }
285
+ if (sub === "tool_call" && p["tool"] && typeof p["tool"] === "object") {
286
+ const tool = p["tool"];
287
+ const toolCallId = String(tool["use_id"] ?? `tc-${Math.random().toString(36).slice(2, 8)}`);
288
+ // Fallback matching for tool_results that lack a native use_id: only those
289
+ // need it (a use_id-bearing result matches directly by id). Without this the
290
+ // synthetic tc-* call never completes and the client hangs. Key by
291
+ // sessionId+name (the only discriminator the result side also carries) and
292
+ // PUSH onto a FIFO queue so two concurrent same-name use_id-less calls each
293
+ // get their own slot — the matching results SHIFT them oldest-first.
294
+ if (typeof tool["use_id"] !== "string") {
295
+ const fallbackKey = `${sessionId}:${String(tool["name"] ?? "")}`;
296
+ const queue = this.openToolCalls.get(fallbackKey);
297
+ if (queue)
298
+ queue.push(toolCallId);
299
+ else
300
+ this.openToolCalls.set(fallbackKey, [toolCallId]);
301
+ }
302
+ this.notify("session/update", {
303
+ sessionId,
304
+ update: {
305
+ sessionUpdate: "tool_call",
306
+ toolCallId,
307
+ title: String(tool["name"] ?? "tool"),
308
+ status: "in_progress",
309
+ },
310
+ });
311
+ return;
312
+ }
313
+ if (sub === "tool_result" && p["tool"] && typeof p["tool"] === "object") {
314
+ const tool = p["tool"];
315
+ // Terminal completion for the started tool_call. status: "ok"->completed,
316
+ // "error"->failed; an unknown/missing status still completes (never hang).
317
+ const hasUseId = typeof tool["use_id"] === "string" && tool["use_id"];
318
+ const fallbackKey = `${sessionId}:${String(tool["name"] ?? "")}`;
319
+ // use_id-bearing result -> match directly; otherwise SHIFT the oldest
320
+ // queued synthetic id (FIFO) so concurrent same-name calls each complete.
321
+ const queue = hasUseId ? undefined : this.openToolCalls.get(fallbackKey);
322
+ const toolCallId = hasUseId ? String(tool["use_id"]) : queue?.shift();
323
+ if (toolCallId) {
324
+ // Drop the queue once drained so empty keys don't accumulate.
325
+ if (queue && queue.length === 0)
326
+ this.openToolCalls.delete(fallbackKey);
327
+ this.notify("session/update", {
328
+ sessionId,
329
+ update: {
330
+ sessionUpdate: "tool_call_update",
331
+ toolCallId,
332
+ status: tool["status"] === "error" ? "failed" : "completed",
333
+ },
334
+ });
335
+ }
336
+ return;
337
+ }
338
+ return;
339
+ }
340
+ if (type === "run.completed" || type === "run.failed" || type === "run.blocked") {
341
+ // Human sentences, not raw event ids — this text lands in the editor's
342
+ // chat timeline as-is.
343
+ const text = type === "run.completed"
344
+ ? "[claudexor] run completed."
345
+ : type === "run.failed"
346
+ ? "[claudexor] run failed — inspect the run artifacts for diagnostics."
347
+ : "[claudexor] run blocked — a typed operator decision is required before this result can be applied.";
348
+ this.notify("session/update", {
349
+ sessionId,
350
+ update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text } },
351
+ });
352
+ }
353
+ }
354
+ /**
355
+ * Interactive question -> one session/request_permission round-trip per
356
+ * question (ACP's permission options carry a flat choice list). A cancelled
357
+ * or unanswered request resolves null and the engine declines benignly.
358
+ */
359
+ async requestAnswers(sessionId, ctx) {
360
+ const request = ctx?.request;
361
+ const questions = Array.isArray(request?.questions) ? request.questions : [];
362
+ if (questions.length === 0)
363
+ return null;
364
+ const answers = [];
365
+ let declinedFreeText = false;
366
+ for (const [qIdx, q] of questions.entries()) {
367
+ const options = (Array.isArray(q?.options) ? q.options : []).map((o, idx) => ({
368
+ optionId: `opt-${idx + 1}`,
369
+ name: String(o?.label ?? `option ${idx + 1}`),
370
+ kind: "allow_once",
371
+ }));
372
+ if (options.length === 0) {
373
+ // A free-text question has no answer channel over ACP: session/request_permission
374
+ // returns a chosen optionId, NOT arbitrary text. Faking an "Answer in chat"
375
+ // affordance would advertise a capability this surface cannot honor. Decline
376
+ // benignly (like the --json path) so the run continues with assumptions, and
377
+ // note it honestly to the client below.
378
+ declinedFreeText = true;
379
+ continue;
380
+ }
381
+ // Announce the tool_call the permission request will reference —
382
+ // clients that JOIN permissions to tool calls otherwise render an
383
+ // orphan (the id must exist in the session's tool-call timeline).
384
+ // UNIQUE per question: a multi-question interaction must not reuse one
385
+ // id across frames (joining clients would clobber/mis-associate).
386
+ const toolCallId = `${String(request?.interaction_id ?? "interaction")}:${String(q?.id ?? qIdx)}`;
387
+ this.notify("session/update", {
388
+ sessionId,
389
+ update: {
390
+ sessionUpdate: "tool_call",
391
+ toolCallId,
392
+ title: String(q?.question ?? "Question"),
393
+ kind: "other",
394
+ status: "pending",
395
+ },
396
+ });
397
+ // Bound the wait by the interaction's own deadline (the engine stamps
398
+ // timeoutAt on every interaction request); a silent editor then yields
399
+ // a null response instead of a forever-pending server request.
400
+ const deadlineMs = Date.parse(String(request?.timeout_at ?? ctx?.timeoutAt ?? "")) - Date.now();
401
+ const response = await this.request("session/request_permission", {
402
+ sessionId,
403
+ toolCall: { toolCallId, title: String(q?.question ?? "Question") },
404
+ options,
405
+ }, Number.isFinite(deadlineMs) ? deadlineMs : 900_000);
406
+ const optionId = response?.outcome?.optionId ?? response?.optionId;
407
+ const picked = typeof optionId === "string" ? options.find((o) => o.optionId === optionId) : undefined;
408
+ // Terminal status for the announced call either way: completed on an
409
+ // answer, failed on decline/timeout — a started tool_call never hangs.
410
+ this.notify("session/update", {
411
+ sessionId,
412
+ update: { sessionUpdate: "tool_call_update", toolCallId, status: picked ? "completed" : "failed" },
413
+ });
414
+ if (picked) {
415
+ answers.push({ question_id: String(q?.id ?? ""), selected_labels: [picked.name], free_text: null });
416
+ }
417
+ }
418
+ if (declinedFreeText) {
419
+ this.notify("session/update", {
420
+ sessionId,
421
+ update: {
422
+ sessionUpdate: "agent_message_chunk",
423
+ content: {
424
+ type: "text",
425
+ text: "[claudexor] a free-text question could not be answered over ACP; the run continues with assumptions.",
426
+ },
427
+ },
428
+ });
429
+ }
430
+ // Returning answers only for the choice questions; an empty set is a benign
431
+ // decline (orchestrator/adapter then continue with assumptions).
432
+ return answers.length > 0 ? { interaction_id: String(request?.interaction_id ?? ""), answers } : null;
433
+ }
434
+ }
435
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ACP prompt/result projection helpers — pure functions, no server state.
3
+ */
4
+ /**
5
+ * Flatten ACP prompt content blocks into the engine prompt. The declared
6
+ * `embeddedContext: true` capability is honored here: `resource` blocks
7
+ * (embedded file text) are inlined as fenced context sections and
8
+ * `resource_link` blocks surface as references — editor-supplied context is
9
+ * never silently dropped.
10
+ */
11
+ export declare function extractPromptText(prompt: unknown): string;
12
+ /**
13
+ * Reduce a run result to the human-readable text the editor should show. The
14
+ * orchestrator returns an OrchestratorResult whose `summary` is the primary
15
+ * output; prefer it over dumping the whole internal object. Falls back to a
16
+ * compact JSON string only when no summary/text field is present.
17
+ */
18
+ export declare function summarizeResult(result: unknown): string;
19
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CA2BzD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAWvD"}
package/dist/prompt.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * ACP prompt/result projection helpers — pure functions, no server state.
3
+ */
4
+ /**
5
+ * Flatten ACP prompt content blocks into the engine prompt. The declared
6
+ * `embeddedContext: true` capability is honored here: `resource` blocks
7
+ * (embedded file text) are inlined as fenced context sections and
8
+ * `resource_link` blocks surface as references — editor-supplied context is
9
+ * never silently dropped.
10
+ */
11
+ export function extractPromptText(prompt) {
12
+ if (typeof prompt === "string")
13
+ return prompt;
14
+ const blockText = (p) => {
15
+ if (typeof p === "string")
16
+ return p;
17
+ if (!p || typeof p !== "object")
18
+ return "";
19
+ if (typeof p.text === "string" && p.type !== "resource")
20
+ return p.text;
21
+ if (p.type === "resource" && p.resource && typeof p.resource === "object") {
22
+ const res = p.resource;
23
+ if (typeof res["text"] === "string" && res["text"].trim()) {
24
+ const uri = typeof res["uri"] === "string" ? res["uri"] : "embedded resource";
25
+ return `Context from ${uri}:\n\`\`\`\n${res["text"]}\n\`\`\``;
26
+ }
27
+ // Binary blobs have no text channel into a CLI prompt — reference only.
28
+ const uri = typeof res["uri"] === "string" ? res["uri"] : "";
29
+ return uri ? `Context resource (binary, not inlined): ${uri}` : "";
30
+ }
31
+ if (p.type === "resource_link") {
32
+ const uri = typeof p.uri === "string" ? p.uri : "";
33
+ const name = typeof p.name === "string" && p.name ? ` (${p.name})` : "";
34
+ return uri ? `Referenced resource: ${uri}${name}` : "";
35
+ }
36
+ return "";
37
+ };
38
+ if (Array.isArray(prompt)) {
39
+ return prompt.map(blockText).filter(Boolean).join("\n\n");
40
+ }
41
+ return blockText(prompt);
42
+ }
43
+ /**
44
+ * Reduce a run result to the human-readable text the editor should show. The
45
+ * orchestrator returns an OrchestratorResult whose `summary` is the primary
46
+ * output; prefer it over dumping the whole internal object. Falls back to a
47
+ * compact JSON string only when no summary/text field is present.
48
+ */
49
+ export function summarizeResult(result) {
50
+ if (typeof result === "string")
51
+ return result.trim();
52
+ if (result && typeof result === "object") {
53
+ const r = result;
54
+ for (const key of ["summary", "answer", "text"]) {
55
+ const v = r[key];
56
+ if (typeof v === "string" && v.trim())
57
+ return v.trim();
58
+ }
59
+ return JSON.stringify(result);
60
+ }
61
+ return result === undefined || result === null ? "" : String(result);
62
+ }
63
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Typed validation verdict: message for humans, optional machine code. */
2
+ export interface RunControlError {
3
+ message: string;
4
+ code?: string;
5
+ }
6
+ export declare function validateRunControls(params: unknown): RunControlError | null;
7
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAYA,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAoD3E"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ACP session/prompt run-control validation — ACP-specific rules first
3
+ * (mode/harness/n/booleans), then the SHARED semantic run-control rules
4
+ * (one owner in @claudexor/schema). Pure functions, no server state.
5
+ */
6
+ import { ModeKind, validateOptionalNonEmptyString, validateSurfaceRunControls } from "@claudexor/schema";
7
+ import { assertNoInlineSecretValues, errorCode } from "@claudexor/util";
8
+ function isPlainRecord(value) {
9
+ return !!value && typeof value === "object" && !Array.isArray(value);
10
+ }
11
+ const msg = (message) => ({ message });
12
+ export function validateRunControls(params) {
13
+ if (!isPlainRecord(params))
14
+ return null;
15
+ const allowedKeys = new Set([
16
+ "sessionId",
17
+ "prompt",
18
+ "mode",
19
+ "harness",
20
+ "primaryHarness",
21
+ "web",
22
+ "externalContextPolicy",
23
+ "model",
24
+ "effort",
25
+ "n",
26
+ "race",
27
+ "untilClean",
28
+ "swarm",
29
+ "create",
30
+ "tests",
31
+ "maxUsd",
32
+ "access",
33
+ "protectedPathApprovals",
34
+ "reviewerPanel",
35
+ "reviewerModels",
36
+ "reviewerEfforts",
37
+ ]);
38
+ for (const key of Object.keys(params)) {
39
+ // `_meta` is the PROTOCOL's forward-compat envelope (other parties'
40
+ // standard field), not a Claudexor knob — tolerate it, reject the rest.
41
+ // Unknown CLAUDEXOR fields still fail loudly (typo'd knobs never no-op).
42
+ if (key === "_meta")
43
+ continue;
44
+ if (!allowedKeys.has(key))
45
+ return msg(`unknown session/prompt field: ${key}`);
46
+ }
47
+ if (params.mode !== undefined && (typeof params.mode !== "string" || !ModeKind.safeParse(params.mode).success)) {
48
+ return msg("mode must be a valid mode");
49
+ }
50
+ const harnessError = validateOptionalNonEmptyString(params.harness, "harness");
51
+ if (harnessError)
52
+ return msg(harnessError);
53
+ for (const flag of ["race", "untilClean", "swarm", "create"]) {
54
+ if (params[flag] !== undefined && typeof params[flag] !== "boolean") {
55
+ return msg(`${flag} must be a boolean`);
56
+ }
57
+ }
58
+ if (params.n !== undefined) {
59
+ // A race needs >= 2 candidates; other routes accept any positive width.
60
+ const min = params.race === true ? 2 : 1;
61
+ if (!Number.isInteger(params.n) || params.n < min) {
62
+ return msg(params.race === true ? "race n must be an integer >= 2" : "n must be a positive integer");
63
+ }
64
+ }
65
+ const shared = validateSurfaceRunControls(params);
66
+ if (shared)
67
+ return msg(shared);
68
+ return validateNoInlineSecrets(params, "ACP session/prompt params");
69
+ }
70
+ function validateNoInlineSecrets(value, context) {
71
+ try {
72
+ assertNoInlineSecretValues(value, "$", context);
73
+ return null;
74
+ }
75
+ catch (err) {
76
+ const code = errorCode(err);
77
+ return { message: err instanceof Error ? err.message : String(err), ...(code ? { code } : {}) };
78
+ }
79
+ }
80
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@claudexor/acp-server",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "description": "Minimal ACP server (stdio JSON-RPC) exposing Claudexor as a meta-agent to editors. Runner injected.",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@claudexor/schema": "1.0.0",
20
+ "@claudexor/util": "1.0.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/razzant/claudexor.git",
25
+ "directory": "packages/acp-server"
26
+ },
27
+ "homepage": "https://github.com/razzant/claudexor#readme",
28
+ "bugs": {
29
+ "url": "https://github.com/razzant/claudexor/issues"
30
+ },
31
+ "engines": {
32
+ "node": ">=20.19"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "typecheck": "tsc --noEmit"
40
+ }
41
+ }