@automatalabs/acp-agents 0.5.1 → 0.7.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/README.md +1 -0
- package/dist/acp-client.d.ts +9 -1
- package/dist/acp-client.d.ts.map +1 -1
- package/dist/acp-client.js +90 -14
- package/dist/backend.d.ts +5 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backends/custom.d.ts +3 -0
- package/dist/backends/custom.d.ts.map +1 -1
- package/dist/backends/custom.js +23 -0
- package/dist/capabilities.d.ts +46 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +89 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +10 -7
- package/dist/registry.d.ts +8 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +18 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +15 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -99,6 +99,7 @@ Also exported: `AcpAgentPool` / `resolvePoolSize`, `PooledConnection` / `Session
|
|
|
99
99
|
| --- | --- |
|
|
100
100
|
| `AGENTPRISM_DEFAULT_BACKEND` | Backend when `model`/`tier` don't pick one (`codex` selects Codex; a registered custom name selects that backend; anything else is Claude). |
|
|
101
101
|
| `AGENTPRISM_BACKENDS` | Custom ACP backends as JSON: `{"<name>": {"command": "…", "args": […], "env": {…}, "sessionMeta": {…}}}`. |
|
|
102
|
+
| `AGENTPRISM_ACP_INIT_TIMEOUT_MS` | Deadline (default `60000`) for a backend's one-time ACP `initialize` handshake — a non-ACP command fails fast instead of hanging. |
|
|
102
103
|
| `AGENTPRISM_ACP_POOL_SIZE` | Long-lived processes to keep per backend (default `1`). |
|
|
103
104
|
| `AGENTPRISM_CLAUDE_ACP_CMD` / `AGENTPRISM_CLAUDE_ACP_ARGS` | Override the command (and args) used to spawn the Claude ACP server. |
|
|
104
105
|
| `AGENTPRISM_CODEX_ACP_CMD` / `AGENTPRISM_CODEX_ACP_ARGS` | Override the command (and args) used to spawn the Codex ACP server. |
|
package/dist/acp-client.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ClientSideConnection, type PromptResponse, type SessionConfigOption, ty
|
|
|
2
2
|
import type { TSchema } from "typebox";
|
|
3
3
|
import { type AgentHistoryEntry, type McpServerConfig } from "@automatalabs/shared-types";
|
|
4
4
|
import type { Backend, BackendId, StructuredSource } from "./backend.js";
|
|
5
|
+
import { type NegotiatedCapabilities } from "./capabilities.js";
|
|
5
6
|
import { type AcpEventSink } from "./events.js";
|
|
6
7
|
import { type ToolPolicy } from "./permissions.js";
|
|
7
8
|
import { UsageAccumulator } from "./usage.js";
|
|
@@ -83,7 +84,8 @@ export declare class PooledConnection {
|
|
|
83
84
|
private readonly whenDead;
|
|
84
85
|
private resolveDead;
|
|
85
86
|
private deathError;
|
|
86
|
-
|
|
87
|
+
/** Set from the one-time initialize handshake; undefined until it completes (or if it failed). */
|
|
88
|
+
private negotiated;
|
|
87
89
|
private _alive;
|
|
88
90
|
private _activeSessions;
|
|
89
91
|
private stderrTail;
|
|
@@ -93,6 +95,12 @@ export declare class PooledConnection {
|
|
|
93
95
|
static create(backend: Backend, deps: PooledConnectionDeps): PooledConnection;
|
|
94
96
|
get alive(): boolean;
|
|
95
97
|
get activeSessions(): number;
|
|
98
|
+
/** The capabilities negotiated on this connection's one-time initialize handshake, or undefined
|
|
99
|
+
* until it completes — derived-state-behind-a-getter, like `alive`/`activeSessions`. */
|
|
100
|
+
get capabilities(): NegotiatedCapabilities | undefined;
|
|
101
|
+
/** Drop the fork's custom bare `_meta` keys the connected agent did not advertise support for
|
|
102
|
+
* (see gateCustomMeta). Applied to BOTH session/new and session/prompt `_meta`. */
|
|
103
|
+
gateCustomMeta(meta: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
96
104
|
/** Mark this connection dead exactly once, then ask the pool to evict it. Idempotent. */
|
|
97
105
|
private die;
|
|
98
106
|
private stderrSuffix;
|
package/dist/acp-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acp-client.d.ts","sourceRoot":"","sources":["../src/acp-client.ts"],"names":[],"mappings":"AAwBA,OAAO,EACL,oBAAoB,EAOpB,KAAK,cAAc,EAGnB,KAAK,mBAAmB,EAGxB,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"acp-client.d.ts","sourceRoot":"","sources":["../src/acp-client.ts"],"names":[],"mappings":"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"}
|
package/dist/acp-client.js
CHANGED
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
import { spawn } from "node:child_process";
|
|
24
24
|
import { Readable, Writable } from "node:stream";
|
|
25
25
|
import { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
|
|
26
|
-
import { META_KEYS } from "@automatalabs/shared-types";
|
|
26
|
+
import { META_KEYS, WorkflowError, WorkflowErrorCode, } from "@automatalabs/shared-types";
|
|
27
|
+
import { gateCustomMeta, isSupportedProtocolVersion, negotiateCapabilities, unsupportedMcpServer, } from "./capabilities.js";
|
|
27
28
|
import { emitSessionUpdate } from "./events.js";
|
|
28
29
|
import { decidePermission } from "./permissions.js";
|
|
29
30
|
import { UsageAccumulator } from "./usage.js";
|
|
@@ -171,6 +172,18 @@ class MultiplexClient {
|
|
|
171
172
|
});
|
|
172
173
|
}
|
|
173
174
|
}
|
|
175
|
+
const DEFAULT_INIT_TIMEOUT_MS = 60_000;
|
|
176
|
+
/** Deadline for the one-time ACP `initialize` handshake per pooled process. Overridable via
|
|
177
|
+
* AGENTPRISM_ACP_INIT_TIMEOUT_MS (e.g. for slow cold-start backends). Clamped to >= 1s. */
|
|
178
|
+
function initializeTimeoutMs() {
|
|
179
|
+
const env = process.env.AGENTPRISM_ACP_INIT_TIMEOUT_MS;
|
|
180
|
+
if (env !== undefined) {
|
|
181
|
+
const parsed = Number.parseInt(env, 10);
|
|
182
|
+
if (Number.isFinite(parsed) && parsed >= 1000)
|
|
183
|
+
return parsed;
|
|
184
|
+
}
|
|
185
|
+
return DEFAULT_INIT_TIMEOUT_MS;
|
|
186
|
+
}
|
|
174
187
|
/** Shallow-merge `_meta` layers lowest-to-highest precedence, treating empty layers as absent
|
|
175
188
|
* so an unconfigured session keeps sending NO `_meta` at all. */
|
|
176
189
|
function layerMeta(...layers) {
|
|
@@ -220,7 +233,8 @@ export class PooledConnection {
|
|
|
220
233
|
whenDead;
|
|
221
234
|
resolveDead;
|
|
222
235
|
deathError;
|
|
223
|
-
|
|
236
|
+
/** Set from the one-time initialize handshake; undefined until it completes (or if it failed). */
|
|
237
|
+
negotiated;
|
|
224
238
|
_alive = true;
|
|
225
239
|
_activeSessions = 0;
|
|
226
240
|
stderrTail = "";
|
|
@@ -277,6 +291,16 @@ export class PooledConnection {
|
|
|
277
291
|
get activeSessions() {
|
|
278
292
|
return this._activeSessions;
|
|
279
293
|
}
|
|
294
|
+
/** The capabilities negotiated on this connection's one-time initialize handshake, or undefined
|
|
295
|
+
* until it completes — derived-state-behind-a-getter, like `alive`/`activeSessions`. */
|
|
296
|
+
get capabilities() {
|
|
297
|
+
return this.negotiated;
|
|
298
|
+
}
|
|
299
|
+
/** Drop the fork's custom bare `_meta` keys the connected agent did not advertise support for
|
|
300
|
+
* (see gateCustomMeta). Applied to BOTH session/new and session/prompt `_meta`. */
|
|
301
|
+
gateCustomMeta(meta) {
|
|
302
|
+
return gateCustomMeta(meta, this.negotiated?.customMetaSupport);
|
|
303
|
+
}
|
|
280
304
|
/** Mark this connection dead exactly once, then ask the pool to evict it. Idempotent. */
|
|
281
305
|
die(error) {
|
|
282
306
|
if (!this._alive)
|
|
@@ -307,12 +331,49 @@ export class PooledConnection {
|
|
|
307
331
|
return Promise.race([op, dead]);
|
|
308
332
|
}
|
|
309
333
|
async initialize() {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
// Handshake deadline: a command that is NOT an ACP server never answers `initialize`, and
|
|
335
|
+
// without a deadline the first openSession() would hang forever. On timeout the process is
|
|
336
|
+
// killed and a legible error surfaces (fail-fast hygiene — this is NOT a security gate;
|
|
337
|
+
// the process has already been spawned by then). Tunable for slow cold starts.
|
|
338
|
+
const timeoutMs = initializeTimeoutMs();
|
|
339
|
+
let timer;
|
|
340
|
+
const deadline = new Promise((_, reject) => {
|
|
341
|
+
timer = setTimeout(() => {
|
|
342
|
+
reject(new Error(`ACP agent (${this.backendId}) did not complete the ACP initialize handshake within ${timeoutMs}ms — ` +
|
|
343
|
+
`is the configured command an ACP server?${this.stderrSuffix()}`));
|
|
344
|
+
this.killNow();
|
|
345
|
+
}, timeoutMs);
|
|
346
|
+
timer.unref?.();
|
|
347
|
+
});
|
|
348
|
+
try {
|
|
349
|
+
const response = await Promise.race([
|
|
350
|
+
this.race(this.rpc.initialize({
|
|
351
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
352
|
+
// Truthful advertisement: MultiplexClient implements NONE of the client-side
|
|
353
|
+
// fs/terminal methods, so we advertise none (the ACP rule is "advertise only what you
|
|
354
|
+
// implement"). Everything omitted here is treated as unsupported by the agent.
|
|
355
|
+
clientCapabilities: {},
|
|
356
|
+
clientInfo: { ...CLIENT_INFO },
|
|
357
|
+
})),
|
|
358
|
+
deadline,
|
|
359
|
+
]);
|
|
360
|
+
const negotiated = negotiateCapabilities(response);
|
|
361
|
+
// Version negotiation: the agent replies with the version it chose (our requested version if
|
|
362
|
+
// it supports it, else its own latest). If this client cannot speak it, the ACP spec says
|
|
363
|
+
// CLOSE the connection and inform the user — kill the process (so the pool evicts it) and
|
|
364
|
+
// surface a legible error instead of proceeding on an unspoken protocol. Non-recoverable: a
|
|
365
|
+
// deterministic protocol incompatibility must fail fast, not be retried as a transient
|
|
366
|
+
// AGENT_EXECUTION_ERROR.
|
|
367
|
+
if (!isSupportedProtocolVersion(negotiated.protocolVersion)) {
|
|
368
|
+
this.killNow();
|
|
369
|
+
throw new WorkflowError(`ACP agent (${this.backendId}) selected protocol version ${negotiated.protocolVersion}, which ` +
|
|
370
|
+
`this client (protocol v${PROTOCOL_VERSION}) does not support — closing the connection.${this.stderrSuffix()}`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false });
|
|
371
|
+
}
|
|
372
|
+
this.negotiated = negotiated;
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
clearTimeout(timer);
|
|
376
|
+
}
|
|
316
377
|
}
|
|
317
378
|
/**
|
|
318
379
|
* Open a new per-agent session on this pooled connection: session/new { cwd }, register its
|
|
@@ -323,16 +384,28 @@ export class PooledConnection {
|
|
|
323
384
|
this._activeSessions += 1;
|
|
324
385
|
try {
|
|
325
386
|
await this.ready;
|
|
387
|
+
// Capability gate: reject a client-provided MCP server whose transport the connected agent
|
|
388
|
+
// does not advertise (http/sse gated on mcpCapabilities; stdio is always serviceable).
|
|
389
|
+
// Fail-fast and non-recoverable — re-running the same incompatible transport can never
|
|
390
|
+
// succeed. Lenient for agents that advertise no mcpCapabilities (the legacy passthrough).
|
|
391
|
+
const unsupported = this.negotiated
|
|
392
|
+
? unsupportedMcpServer(opts.mcpServers, this.negotiated.agent)
|
|
393
|
+
: undefined;
|
|
394
|
+
if (unsupported) {
|
|
395
|
+
throw new WorkflowError(`MCP server "${unsupported.name}" uses the "${unsupported.transport}" transport, which the ` +
|
|
396
|
+
`${this.backendId} agent does not support`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false, agentLabel: opts.label });
|
|
397
|
+
}
|
|
326
398
|
const state = new SessionState(opts.policy, opts.label, opts.runId);
|
|
327
399
|
// session/new `_meta`, layered lowest-to-highest precedence: the backend's static
|
|
328
400
|
// defaults (a custom registry entry's `sessionMeta`), then the generic user passthrough
|
|
329
401
|
// (opts.meta), then the backend's protocol-critical `_meta` (Claude schema channel;
|
|
330
|
-
// Codex base/developer instructions), then the engine runId correlation stamp.
|
|
331
|
-
//
|
|
332
|
-
|
|
402
|
+
// Codex base/developer instructions), then the engine runId correlation stamp. The result
|
|
403
|
+
// is gated against the agent's advertised custom capabilities (a Codex instruction key the
|
|
404
|
+
// agent said it does not honor is dropped). When no layer survives, no `_meta` is sent.
|
|
405
|
+
const meta = this.gateCustomMeta(stampRunId(layerMeta(this.backend.sessionMetaDefaults?.(), opts.meta, this.backend.sessionMeta(opts.schema, {
|
|
333
406
|
baseInstructions: opts.baseInstructions,
|
|
334
407
|
developerInstructions: opts.developerInstructions,
|
|
335
|
-
})), opts.runId);
|
|
408
|
+
})), opts.runId));
|
|
336
409
|
const request = {
|
|
337
410
|
cwd: opts.cwd,
|
|
338
411
|
// Client-provided MCP servers (additive run input), else the default empty list.
|
|
@@ -368,7 +441,7 @@ export class PooledConnection {
|
|
|
368
441
|
this.client.unregister(sessionId);
|
|
369
442
|
if (this._activeSessions > 0)
|
|
370
443
|
this._activeSessions -= 1;
|
|
371
|
-
if (!this.supportsClose || !this._alive)
|
|
444
|
+
if (!this.negotiated?.supportsClose || !this._alive)
|
|
372
445
|
return;
|
|
373
446
|
try {
|
|
374
447
|
await this.race(withTimeout(this.rpc.closeSession({ sessionId }), CLOSE_SESSION_TIMEOUT_MS));
|
|
@@ -554,10 +627,13 @@ export class SessionHandle {
|
|
|
554
627
|
this.opts.signal?.throwIfAborted();
|
|
555
628
|
this.state.beginTurn();
|
|
556
629
|
const prompt = [{ type: "text", text }];
|
|
630
|
+
// Gate the turn `_meta` against the agent's advertised custom capabilities: the Codex
|
|
631
|
+
// outputSchema forward is dropped when the connected agent said it does not honor it.
|
|
632
|
+
const gatedMeta = this.pooled.gateCustomMeta(promptMeta);
|
|
557
633
|
const request = {
|
|
558
634
|
sessionId: this.sessionId,
|
|
559
635
|
prompt,
|
|
560
|
-
...(
|
|
636
|
+
...(gatedMeta ? { _meta: gatedMeta } : {}),
|
|
561
637
|
};
|
|
562
638
|
const response = await this.pooled.race(this.pooled.rpc.prompt(request));
|
|
563
639
|
this.state.usage.recordPromptUsage(response.usage);
|
package/dist/backend.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export interface SessionMetaInputs {
|
|
|
26
26
|
}
|
|
27
27
|
export interface Backend {
|
|
28
28
|
readonly id: BackendId;
|
|
29
|
+
/** Pool identity for this backend's long-lived processes. Defaults to `id` — but a CUSTOM
|
|
30
|
+
* backend sets it to id + a spawn-config hash, because two runs may declare the SAME name
|
|
31
|
+
* with DIFFERENT commands (script-declared `meta.backends`): keying the pool by name alone
|
|
32
|
+
* would hand run B a pooled process spawned from run A's command. */
|
|
33
|
+
readonly poolKey?: string;
|
|
29
34
|
/** When true, the runner EMBEDS the JSON Schema in the prompt text on schema runs. For the
|
|
30
35
|
* built-ins the native constraint channel is authoritative and this stays unset; a CUSTOM
|
|
31
36
|
* backend sets it because its agent may ignore the `_meta.outputSchema` forward entirely —
|
package/dist/backend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,4FAA4F;AAC5F,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAClD;sFACsF;AACtF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,eAAe,IAAI,MAAM,CAAC;IAC1B,uGAAuG;IACvG,mBAAmB,IAAI,OAAO,CAAC;CAChC;AAED;+EAC+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sFAAsF;IACtF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB;;;;kGAI8F;IAC9F,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,0DAA0D;IAC1D,WAAW,IAAI,WAAW,CAAC;IAC3B;;sEAEkE;IAClE,mBAAmB,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5D;;;mGAG+F;IAC/F,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1G,kGAAkG;IAClG,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC7E,oGAAoG;IACpG,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;CACrD;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAE7D"}
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,4FAA4F;AAC5F,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAClD;sFACsF;AACtF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,eAAe,IAAI,MAAM,CAAC;IAC1B,uGAAuG;IACvG,mBAAmB,IAAI,OAAO,CAAC;CAChC;AAED;+EAC+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sFAAsF;IACtF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB;;;0EAGsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;kGAI8F;IAC9F,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,0DAA0D;IAC1D,WAAW,IAAI,WAAW,CAAC;IAC3B;;sEAEkE;IAClE,mBAAmB,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5D;;;mGAG+F;IAC/F,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1G,kGAAkG;IAClG,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC7E,oGAAoG;IACpG,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC;CACrD;AAED,6FAA6F;AAC7F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAE7D"}
|
|
@@ -4,6 +4,9 @@ import type { RegisteredBackend } from "../registry.js";
|
|
|
4
4
|
export declare class CustomAcpBackend implements Backend {
|
|
5
5
|
private readonly config;
|
|
6
6
|
readonly id: string;
|
|
7
|
+
/** id + spawn-config hash: two registries (e.g. two scripts' `meta.backends`) may declare
|
|
8
|
+
* the SAME name with DIFFERENT commands — the pool must never share a process across them. */
|
|
9
|
+
readonly poolKey: string;
|
|
7
10
|
/** The agent may ignore the `_meta.outputSchema` forward, so the runner must also state the
|
|
8
11
|
* schema in the prompt — otherwise the model returns JSON with keys it invented and the
|
|
9
12
|
* repair ladder can never converge on a contract the model was never shown. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/backends/custom.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/backends/custom.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,qBAAa,gBAAiB,YAAW,OAAO;IAUlC,OAAO,CAAC,QAAQ,CAAC,MAAM;IATnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;mGAC+F;IAC/F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;oFAEgF;IAChF,QAAQ,CAAC,mBAAmB,QAAQ;gBAEP,MAAM,EAAE,iBAAiB;IAUtD,WAAW,IAAI,WAAW;IAS1B,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAQ1D,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAM9E,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAQ5E,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO;CAGpD"}
|
package/dist/backends/custom.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
// CustomAcpBackend — drives ANY registered ACP agent (registry.ts), not just the built-in
|
|
2
|
+
// Claude/Codex pair. The generic dialect it speaks is the one this repo already publishes:
|
|
3
|
+
// - schema IN: the bare turn-level `_meta.outputSchema` (same key the @automatalabs/codex-acp
|
|
4
|
+
// fork reads), as a plain JSON Schema. An agent that honors it constrains its
|
|
5
|
+
// final message natively; an agent that ignores it still works — the runner's
|
|
6
|
+
// validate/re-prompt ladder repairs off the final text.
|
|
7
|
+
// - result OUT: JSON.parse of the final assistant message (with a balanced-block fallback),
|
|
8
|
+
// exactly like Codex — no vendor extension notification required.
|
|
9
|
+
// - config-level `_meta`: the registry entry's static `sessionMeta` rides every session/new
|
|
10
|
+
// (per-call RunOptions.meta merges over it in the ACP client; backend-computed
|
|
11
|
+
// keys and the runId stamp win over both).
|
|
12
|
+
// SessionMetaInputs (Codex base/developer instruction overrides) are IGNORED — they are a
|
|
13
|
+
// codex-acp vendor contract; a custom agent's knobs travel through the generic meta channels.
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
1
15
|
import { META_KEYS } from "@automatalabs/shared-types";
|
|
2
16
|
import { toJsonSchema } from "../schema-strict.js";
|
|
3
17
|
import { parseFinalJson } from "../structured-output.js";
|
|
4
18
|
export class CustomAcpBackend {
|
|
5
19
|
config;
|
|
6
20
|
id;
|
|
21
|
+
/** id + spawn-config hash: two registries (e.g. two scripts' `meta.backends`) may declare
|
|
22
|
+
* the SAME name with DIFFERENT commands — the pool must never share a process across them. */
|
|
23
|
+
poolKey;
|
|
7
24
|
/** The agent may ignore the `_meta.outputSchema` forward, so the runner must also state the
|
|
8
25
|
* schema in the prompt — otherwise the model returns JSON with keys it invented and the
|
|
9
26
|
* repair ladder can never converge on a contract the model was never shown. */
|
|
@@ -11,6 +28,12 @@ export class CustomAcpBackend {
|
|
|
11
28
|
constructor(config) {
|
|
12
29
|
this.config = config;
|
|
13
30
|
this.id = config.name;
|
|
31
|
+
const spawnIdentity = JSON.stringify({
|
|
32
|
+
command: config.command,
|
|
33
|
+
args: config.args ?? [],
|
|
34
|
+
env: config.env ?? {},
|
|
35
|
+
});
|
|
36
|
+
this.poolKey = `${config.name}#${createHash("sha256").update(spawnIdentity).digest("hex").slice(0, 12)}`;
|
|
14
37
|
}
|
|
15
38
|
spawnConfig() {
|
|
16
39
|
return {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type AgentCapabilities, type Implementation, type InitializeResponse } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { type McpServerConfig } from "@automatalabs/shared-types";
|
|
3
|
+
/** The bare `_meta` keys whose emission is gated by the fork's custom-capability advertisement.
|
|
4
|
+
* Each is named EXACTLY like the advertised flag that gates it, so `support[key] === true` is the
|
|
5
|
+
* whole test. session/new carries baseInstructions/developerInstructions and session/prompt carries
|
|
6
|
+
* outputSchema — one list covers both (a key absent from a given `_meta` is simply skipped). */
|
|
7
|
+
export declare const GATED_CUSTOM_META_KEYS: readonly string[];
|
|
8
|
+
/** The capability state a pooled connection derives from its initialize response. */
|
|
9
|
+
export interface NegotiatedCapabilities {
|
|
10
|
+
/** The protocol version the agent selected (echoes the client's when supported, else the agent's
|
|
11
|
+
* latest). Validated by isSupportedProtocolVersion before the connection is used. */
|
|
12
|
+
protocolVersion: number;
|
|
13
|
+
/** The agent's full advertised capabilities — an empty object when the agent sent none (every
|
|
14
|
+
* capability is then UNSUPPORTED per the ACP spec). */
|
|
15
|
+
agent: AgentCapabilities;
|
|
16
|
+
/** The agent's self-identification, when it sent agentInfo. */
|
|
17
|
+
agentInfo: Implementation | undefined;
|
|
18
|
+
/** Whether session/close is advertised (gates the best-effort release-time close). */
|
|
19
|
+
supportsClose: boolean;
|
|
20
|
+
/** The parsed @automatalabs/codex-acp custom-capability block (the namespaced `_meta` object), or
|
|
21
|
+
* undefined when the agent did not advertise the namespace at all — the legacy passthrough. */
|
|
22
|
+
customMetaSupport: Record<string, unknown> | undefined;
|
|
23
|
+
}
|
|
24
|
+
/** Parse an initialize response into the connection's derived capability state. */
|
|
25
|
+
export declare function negotiateCapabilities(response: InitializeResponse): NegotiatedCapabilities;
|
|
26
|
+
/** True only when the agent selected EXACTLY PROTOCOL_VERSION. This client implements that one wire
|
|
27
|
+
* version and adapts its behavior to no other, so any other selected version — older or newer —
|
|
28
|
+
* means close the connection per the ACP spec's SHOULD-close rule. Per the spec the agent echoes
|
|
29
|
+
* the requested version when it supports it, else its own latest; we do NOT accept older versions
|
|
30
|
+
* (we cannot speak them) — the equality is the whole test. */
|
|
31
|
+
export declare function isSupportedProtocolVersion(version: number): boolean;
|
|
32
|
+
/** Remove the fork's custom bare `_meta` keys the connected agent did NOT advertise support for.
|
|
33
|
+
* A no-op when the agent advertised no namespace (`support` undefined => legacy => every key
|
|
34
|
+
* passes) or the meta is empty/undefined. Never mutates its input; collapses to undefined if
|
|
35
|
+
* gating empties the object (so no `_meta` is sent at all). */
|
|
36
|
+
export declare function gateCustomMeta(meta: Record<string, unknown> | undefined, support: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
37
|
+
/** The first client-provided MCP server whose transport the agent did NOT advertise, or undefined
|
|
38
|
+
* when every server is serviceable. stdio is ALWAYS serviceable (the baseline transport); http/sse
|
|
39
|
+
* are gated on mcpCapabilities.{http,sse}. Lenient for legacy agents: when the agent advertised no
|
|
40
|
+
* mcpCapabilities at all we cannot know its transports, so we do not gate (preserving today's
|
|
41
|
+
* send-and-let-the-agent-decide behavior for minimal/custom servers). */
|
|
42
|
+
export declare function unsupportedMcpServer(servers: McpServerConfig[] | undefined, agent: AgentCapabilities): {
|
|
43
|
+
name: string;
|
|
44
|
+
transport: "http" | "sse";
|
|
45
|
+
} | undefined;
|
|
46
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAeA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAEpC;;;iGAGiG;AACjG,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAInD,CAAC;AAEF,qFAAqF;AACrF,MAAM,WAAW,sBAAsB;IACrC;0FACsF;IACtF,eAAe,EAAE,MAAM,CAAC;IACxB;4DACwD;IACxD,KAAK,EAAE,iBAAiB,CAAC;IACzB,+DAA+D;IAC/D,SAAS,EAAE,cAAc,GAAG,SAAS,CAAC;IACtC,sFAAsF;IACtF,aAAa,EAAE,OAAO,CAAC;IACvB;oGACgG;IAChG,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACxD;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,sBAAsB,CAS1F;AAUD;;;;+DAI+D;AAC/D,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED;;;gEAGgE;AAChE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAWrC;AAED;;;;0EAI0E;AAC1E,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,eAAe,EAAE,GAAG,SAAS,EACtC,KAAK,EAAE,iBAAiB,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,GAAG,SAAS,CAYzD"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Negotiated ACP capabilities for one pooled connection — the derived state the connection acts on
|
|
2
|
+
// after its ONE-TIME `initialize` handshake. Parses the InitializeResponse into: the agent's chosen
|
|
3
|
+
// protocolVersion (validated against what this client speaks), its full advertised agentCapabilities
|
|
4
|
+
// + agentInfo, whether session/close is supported, and the @automatalabs/codex-acp fork's custom
|
|
5
|
+
// capability advertisement (the namespaced `_meta` block gating which bare `_meta` inputs the client
|
|
6
|
+
// may send).
|
|
7
|
+
//
|
|
8
|
+
// GATING PHILOSOPHY — lenient for legacy agents. An agent that advertises NOTHING is treated as
|
|
9
|
+
// today's behavior (every gated key still sent), because the currently-published fork
|
|
10
|
+
// (@automatalabs/codex-acp ≤ 1.2.0) and arbitrary custom ACP servers honor these inputs WITHOUT
|
|
11
|
+
// advertising them; a strict "omitted => unsupported" reading would silently regress them. Only once
|
|
12
|
+
// an agent DOES advertise the fork namespace (opts into negotiation) is each bare key gated on its
|
|
13
|
+
// same-named flag — and, symmetrically, only once an agent advertises mcpCapabilities is an
|
|
14
|
+
// unsupported MCP transport rejected. Truthfully-advertised absence still gates (per spec) WITHIN an
|
|
15
|
+
// advertised capability; total silence is the legacy passthrough.
|
|
16
|
+
import { PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
|
|
17
|
+
import { CODEX_CUSTOM_CAPABILITY_NAMESPACE, CODEX_META_KEYS, META_KEYS, } from "@automatalabs/shared-types";
|
|
18
|
+
/** The bare `_meta` keys whose emission is gated by the fork's custom-capability advertisement.
|
|
19
|
+
* Each is named EXACTLY like the advertised flag that gates it, so `support[key] === true` is the
|
|
20
|
+
* whole test. session/new carries baseInstructions/developerInstructions and session/prompt carries
|
|
21
|
+
* outputSchema — one list covers both (a key absent from a given `_meta` is simply skipped). */
|
|
22
|
+
export const GATED_CUSTOM_META_KEYS = [
|
|
23
|
+
META_KEYS.outputSchema,
|
|
24
|
+
CODEX_META_KEYS.baseInstructions,
|
|
25
|
+
CODEX_META_KEYS.developerInstructions,
|
|
26
|
+
];
|
|
27
|
+
/** Parse an initialize response into the connection's derived capability state. */
|
|
28
|
+
export function negotiateCapabilities(response) {
|
|
29
|
+
const agent = response.agentCapabilities ?? {};
|
|
30
|
+
return {
|
|
31
|
+
protocolVersion: response.protocolVersion,
|
|
32
|
+
agent,
|
|
33
|
+
agentInfo: response.agentInfo ?? undefined,
|
|
34
|
+
supportsClose: Boolean(agent.sessionCapabilities?.close),
|
|
35
|
+
customMetaSupport: readCustomNamespace(agent._meta),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function readCustomNamespace(meta) {
|
|
39
|
+
if (!meta || typeof meta !== "object")
|
|
40
|
+
return undefined;
|
|
41
|
+
const block = meta[CODEX_CUSTOM_CAPABILITY_NAMESPACE];
|
|
42
|
+
return block && typeof block === "object" && !Array.isArray(block)
|
|
43
|
+
? block
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
46
|
+
/** True only when the agent selected EXACTLY PROTOCOL_VERSION. This client implements that one wire
|
|
47
|
+
* version and adapts its behavior to no other, so any other selected version — older or newer —
|
|
48
|
+
* means close the connection per the ACP spec's SHOULD-close rule. Per the spec the agent echoes
|
|
49
|
+
* the requested version when it supports it, else its own latest; we do NOT accept older versions
|
|
50
|
+
* (we cannot speak them) — the equality is the whole test. */
|
|
51
|
+
export function isSupportedProtocolVersion(version) {
|
|
52
|
+
return version === PROTOCOL_VERSION;
|
|
53
|
+
}
|
|
54
|
+
/** Remove the fork's custom bare `_meta` keys the connected agent did NOT advertise support for.
|
|
55
|
+
* A no-op when the agent advertised no namespace (`support` undefined => legacy => every key
|
|
56
|
+
* passes) or the meta is empty/undefined. Never mutates its input; collapses to undefined if
|
|
57
|
+
* gating empties the object (so no `_meta` is sent at all). */
|
|
58
|
+
export function gateCustomMeta(meta, support) {
|
|
59
|
+
if (!meta || !support)
|
|
60
|
+
return meta;
|
|
61
|
+
let gated;
|
|
62
|
+
for (const key of GATED_CUSTOM_META_KEYS) {
|
|
63
|
+
if (key in meta && support[key] !== true) {
|
|
64
|
+
gated ??= { ...meta };
|
|
65
|
+
delete gated[key];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const result = gated ?? meta;
|
|
69
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
70
|
+
}
|
|
71
|
+
/** The first client-provided MCP server whose transport the agent did NOT advertise, or undefined
|
|
72
|
+
* when every server is serviceable. stdio is ALWAYS serviceable (the baseline transport); http/sse
|
|
73
|
+
* are gated on mcpCapabilities.{http,sse}. Lenient for legacy agents: when the agent advertised no
|
|
74
|
+
* mcpCapabilities at all we cannot know its transports, so we do not gate (preserving today's
|
|
75
|
+
* send-and-let-the-agent-decide behavior for minimal/custom servers). */
|
|
76
|
+
export function unsupportedMcpServer(servers, agent) {
|
|
77
|
+
const mcp = agent.mcpCapabilities;
|
|
78
|
+
if (!mcp || !servers)
|
|
79
|
+
return undefined;
|
|
80
|
+
for (const server of servers) {
|
|
81
|
+
if ("type" in server && server.type === "http" && mcp.http !== true) {
|
|
82
|
+
return { name: server.name, transport: "http" };
|
|
83
|
+
}
|
|
84
|
+
if ("type" in server && server.type === "sse" && mcp.sse !== true) {
|
|
85
|
+
return { name: server.name, transport: "sse" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export { AcpAgentRunner, createAcpRunner, selectBackend } from "./runner.js";
|
|
2
2
|
export type { AcpRunnerOptions } from "./runner.js";
|
|
3
|
-
export { BACKENDS_ENV, resolveBackendRegistry } from "./registry.js";
|
|
3
|
+
export { BACKENDS_ENV, registryWithRunBackends, resolveBackendRegistry } from "./registry.js";
|
|
4
4
|
export type { BackendRegistry, CustomBackendConfig, RegisteredBackend } from "./registry.js";
|
|
5
5
|
export { PooledConnection, SessionHandle } from "./acp-client.js";
|
|
6
6
|
export type { AcpSessionOptions, PooledConnectionDeps } from "./acp-client.js";
|
|
7
|
+
export { GATED_CUSTOM_META_KEYS, gateCustomMeta, isSupportedProtocolVersion, negotiateCapabilities, unsupportedMcpServer, } from "./capabilities.js";
|
|
8
|
+
export type { NegotiatedCapabilities } from "./capabilities.js";
|
|
7
9
|
export { AcpAgentPool, resolvePoolSize } from "./pool.js";
|
|
8
10
|
export type { AcpPoolOptions, AcpPoolDeps } from "./pool.js";
|
|
9
11
|
export { TypedEventEmitter, emitSessionUpdate } from "./events.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC9F,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAI/E,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7D,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEtE,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
// @automatalabs/workflows facade (which mcp-server builds on) via createAcpRunner().
|
|
7
7
|
export { AcpAgentRunner, createAcpRunner, selectBackend } from "./runner.js";
|
|
8
8
|
// The custom-backend registry: run ANY ACP agent as an agent() target.
|
|
9
|
-
export { BACKENDS_ENV, resolveBackendRegistry } from "./registry.js";
|
|
9
|
+
export { BACKENDS_ENV, registryWithRunBackends, resolveBackendRegistry } from "./registry.js";
|
|
10
10
|
export { PooledConnection, SessionHandle } from "./acp-client.js";
|
|
11
|
+
// ACP capability negotiation: parse/validate the initialize response and gate what the client
|
|
12
|
+
// sends (custom `_meta` keys, MCP transports) on what the connected agent advertised.
|
|
13
|
+
export { GATED_CUSTOM_META_KEYS, gateCustomMeta, isSupportedProtocolVersion, negotiateCapabilities, unsupportedMcpServer, } from "./capabilities.js";
|
|
11
14
|
export { AcpAgentPool, resolvePoolSize } from "./pool.js";
|
|
12
15
|
// The typed ACP event bus surfaced on AcpAgentRunner (`runner.on(name, evt => …)`).
|
|
13
16
|
export { TypedEventEmitter, emitSessionUpdate } from "./events.js";
|
package/dist/pool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AACvD,OAAO,EAAoB,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,cAAc;IAC7B,mGAAmG;IACnG,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;kGACkG;AAClG,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,kGAAkG;AAClG,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,qBAAa,YAAY;IASrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IARvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IACtE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAS;gBAGvB,OAAO,GAAE,cAAmB,EACX,IAAI,GAAE,WAAgB;IAKzC,8FAA8F;IACxF,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAMhF;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AACvD,OAAO,EAAoB,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,cAAc;IAC7B,mGAAmG;IACnG,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;kGACkG;AAClG,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,kGAAkG;AAClG,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,qBAAa,YAAY;IASrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IARvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IACtE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAS;gBAGvB,OAAO,GAAE,cAAmB,EACX,IAAI,GAAE,WAAgB;IAKzC,8FAA8F;IACxF,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAMhF;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,cAAc;IAStB,+DAA+D;IAC/D,OAAO,CAAC,IAAI;IAOZ,iEAAiE;IAC3D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,cAAc;IAMtB,gGAAgG;IAChG,OAAO,CAAC,WAAW;CAGpB"}
|
package/dist/pool.js
CHANGED
|
@@ -38,7 +38,10 @@ export class AcpAgentPool {
|
|
|
38
38
|
* double-book a connection.
|
|
39
39
|
*/
|
|
40
40
|
selectConnection(backend) {
|
|
41
|
-
|
|
41
|
+
// Pool identity: poolKey (id + spawn-config hash for custom backends) over bare id, so two
|
|
42
|
+
// runs declaring the same NAME with different COMMANDS never share a process.
|
|
43
|
+
const key = backend.poolKey ?? backend.id;
|
|
44
|
+
const connections = this.connectionsFor(key);
|
|
42
45
|
const live = connections.filter((c) => c.alive);
|
|
43
46
|
const idle = live.find((c) => c.activeSessions === 0);
|
|
44
47
|
if (idle)
|
|
@@ -46,7 +49,7 @@ export class AcpAgentPool {
|
|
|
46
49
|
if (live.length < this.size) {
|
|
47
50
|
this.installExitHook();
|
|
48
51
|
const connection = PooledConnection.create(backend, {
|
|
49
|
-
onDead: (dead) => this.drop(
|
|
52
|
+
onDead: (dead) => this.drop(key, dead),
|
|
50
53
|
onEvent: this.deps.onEvent,
|
|
51
54
|
});
|
|
52
55
|
connections.push(connection);
|
|
@@ -55,17 +58,17 @@ export class AcpAgentPool {
|
|
|
55
58
|
// At capacity with every connection busy: multiplex onto the least-loaded one.
|
|
56
59
|
return live.reduce((least, c) => (c.activeSessions < least.activeSessions ? c : least));
|
|
57
60
|
}
|
|
58
|
-
connectionsFor(
|
|
59
|
-
let arr = this.byBackend.get(
|
|
61
|
+
connectionsFor(key) {
|
|
62
|
+
let arr = this.byBackend.get(key);
|
|
60
63
|
if (!arr) {
|
|
61
64
|
arr = [];
|
|
62
|
-
this.byBackend.set(
|
|
65
|
+
this.byBackend.set(key, arr);
|
|
63
66
|
}
|
|
64
67
|
return arr;
|
|
65
68
|
}
|
|
66
69
|
/** Evict a dead connection so it is never handed out again. */
|
|
67
|
-
drop(
|
|
68
|
-
const arr = this.byBackend.get(
|
|
70
|
+
drop(key, connection) {
|
|
71
|
+
const arr = this.byBackend.get(key);
|
|
69
72
|
if (!arr)
|
|
70
73
|
return;
|
|
71
74
|
const index = arr.indexOf(connection);
|
package/dist/registry.d.ts
CHANGED
|
@@ -23,4 +23,12 @@ export type BackendRegistry = ReadonlyMap<string, RegisteredBackend>;
|
|
|
23
23
|
* misroute at run time.
|
|
24
24
|
*/
|
|
25
25
|
export declare function resolveBackendRegistry(option?: Record<string, CustomBackendConfig>, env?: NodeJS.ProcessEnv): BackendRegistry;
|
|
26
|
+
/**
|
|
27
|
+
* Layer a RUN-SCOPED registry (an approved script-declared `meta.backends`, arriving via
|
|
28
|
+
* RunOptions.backends) UNDER the host registry: run entries are validated with the same rules
|
|
29
|
+
* (reserved names rejected), and a host-registered name always wins on conflict — a script can
|
|
30
|
+
* never hijack a name the operator configured. Returns the host registry unchanged when the
|
|
31
|
+
* run declares nothing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function registryWithRunBackends(host: BackendRegistry, run?: Record<string, CustomBackendConfig>): BackendRegistry;
|
|
26
34
|
//# sourceMappingURL=registry.d.ts.map
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAKlD,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;2FACuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,eAAe,CAwBjB"}
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAKlD,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;2FACuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,yEAAyE;AACzE,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAErE;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAC5C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,eAAe,CAwBjB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,EACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GACxC,eAAe,CAQjB"}
|
package/dist/registry.js
CHANGED
|
@@ -39,6 +39,24 @@ export function resolveBackendRegistry(option, env = process.env) {
|
|
|
39
39
|
}
|
|
40
40
|
return registry;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Layer a RUN-SCOPED registry (an approved script-declared `meta.backends`, arriving via
|
|
44
|
+
* RunOptions.backends) UNDER the host registry: run entries are validated with the same rules
|
|
45
|
+
* (reserved names rejected), and a host-registered name always wins on conflict — a script can
|
|
46
|
+
* never hijack a name the operator configured. Returns the host registry unchanged when the
|
|
47
|
+
* run declares nothing.
|
|
48
|
+
*/
|
|
49
|
+
export function registryWithRunBackends(host, run) {
|
|
50
|
+
if (!run || Object.keys(run).length === 0)
|
|
51
|
+
return host;
|
|
52
|
+
const merged = new Map();
|
|
53
|
+
for (const [name, config] of Object.entries(run)) {
|
|
54
|
+
merged.set(...validateEntry(name, config, "script backends (meta.backends)"));
|
|
55
|
+
}
|
|
56
|
+
for (const [name, entry] of host)
|
|
57
|
+
merged.set(name, entry); // host wins on conflict
|
|
58
|
+
return merged;
|
|
59
|
+
}
|
|
42
60
|
function validateEntry(rawName, config, source) {
|
|
43
61
|
const name = rawName.toLowerCase();
|
|
44
62
|
if (!NAME_PATTERN.test(name)) {
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAkBA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C,OAAO,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAkBA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAI5C,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AAQvB;0FAC0F;AAC1F,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD;6FACyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAChD;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C;uGACmG;IACnG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8C;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgE;gBAE9E,OAAO,GAAE,gBAAqB;IAK1C;;;;;;;;OAQG;IACH,EAAE,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAI9E,+EAA+E;IAC/E,IAAI,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIhF,GAAG,CAAC,CAAC,SAAS,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIzE,kBAAkB,CAAC,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI;IAI7C,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM;IAInC,GAAG,CAAC,CAAC,SAAS,OAAO,GAAG,SAAS,GAAG,SAAS,EACjD,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,UAAU,CAAC,CAAC,CAAM,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IA2G1B;qGACiG;IAC3F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAI/B;AAED;;;qDAGqD;AACrD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,cAAc,CAE1E;AAmFD;;;;oCAIoC;AACpC,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,OAAO,CAM1G"}
|
package/dist/runner.js
CHANGED
|
@@ -22,7 +22,7 @@ import { TypedEventEmitter, } from "./events.js";
|
|
|
22
22
|
import { ClaudeBackend } from "./backends/claude.js";
|
|
23
23
|
import { CodexBackend } from "./backends/codex.js";
|
|
24
24
|
import { CustomAcpBackend } from "./backends/custom.js";
|
|
25
|
-
import { resolveBackendRegistry } from "./registry.js";
|
|
25
|
+
import { registryWithRunBackends, resolveBackendRegistry, } from "./registry.js";
|
|
26
26
|
import { mapThrownError } from "./errors-map.js";
|
|
27
27
|
import { toJsonSchema } from "./schema-strict.js";
|
|
28
28
|
import { resolveStructuredOutput } from "./structured-output.js";
|
|
@@ -66,7 +66,20 @@ export class AcpAgentRunner {
|
|
|
66
66
|
async run(prompt, options = {}) {
|
|
67
67
|
const opts = options;
|
|
68
68
|
const schema = opts.schema;
|
|
69
|
-
|
|
69
|
+
// Layer any run-scoped backends (an APPROVED script-declared meta.backends) under the
|
|
70
|
+
// host registry. Malformed/reserved entries fail the call loudly and are NOT retried —
|
|
71
|
+
// re-running a misdeclared registry can never succeed.
|
|
72
|
+
let registry;
|
|
73
|
+
try {
|
|
74
|
+
registry = registryWithRunBackends(this.backends, opts.backends);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
throw new WorkflowError(error.message, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, {
|
|
78
|
+
recoverable: false,
|
|
79
|
+
agentLabel: opts.label,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const backend = selectBackend(opts, registry);
|
|
70
83
|
const policy = { allow: opts.toolNames, deny: opts.disallowedToolNames };
|
|
71
84
|
const cwd = opts.cwd ?? process.cwd();
|
|
72
85
|
const session = await this.pool.acquire(backend, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automatalabs/acp-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@agentclientprotocol/sdk": "1.
|
|
28
|
-
"@agentclientprotocol/claude-agent-acp": "0.
|
|
29
|
-
"@automatalabs/codex-acp": "1.
|
|
27
|
+
"@agentclientprotocol/sdk": "1.1.0",
|
|
28
|
+
"@agentclientprotocol/claude-agent-acp": "0.55.0",
|
|
29
|
+
"@automatalabs/codex-acp": "1.3.0",
|
|
30
30
|
"typebox": "1.3.2",
|
|
31
|
-
"@automatalabs/shared-types": "0.
|
|
31
|
+
"@automatalabs/shared-types": "0.6.0"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc -b",
|