@automatalabs/acp-agents 1.2.7 → 2.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/README.md +84 -11
- package/dist/acp-client.d.ts +26 -6
- package/dist/acp-client.d.ts.map +1 -1
- package/dist/acp-client.js +30 -7
- package/dist/agent/acp-agent.d.ts +141 -0
- package/dist/agent/acp-agent.d.ts.map +1 -0
- package/dist/agent/acp-agent.js +837 -0
- package/dist/agent/errors.d.ts +17 -0
- package/dist/agent/errors.d.ts.map +1 -0
- package/dist/agent/errors.js +37 -0
- package/dist/agent/events.d.ts +26 -0
- package/dist/agent/events.d.ts.map +1 -0
- package/dist/agent/events.js +165 -0
- package/dist/agent/fork.d.ts +27 -0
- package/dist/agent/fork.d.ts.map +1 -0
- package/dist/agent/fork.js +28 -0
- package/dist/agent/probe.d.ts +14 -0
- package/dist/agent/probe.d.ts.map +1 -0
- package/dist/agent/probe.js +87 -0
- package/dist/agent/process-registry.d.ts +9 -0
- package/dist/agent/process-registry.d.ts.map +1 -0
- package/dist/agent/process-registry.js +28 -0
- package/dist/agent/queue.d.ts +19 -0
- package/dist/agent/queue.d.ts.map +1 -0
- package/dist/agent/queue.js +90 -0
- package/dist/agent/routing.d.ts +27 -0
- package/dist/agent/routing.d.ts.map +1 -0
- package/dist/agent/routing.js +87 -0
- package/dist/agent/structured.d.ts +47 -0
- package/dist/agent/structured.d.ts.map +1 -0
- package/dist/agent/structured.js +90 -0
- package/dist/agent/turn.d.ts +65 -0
- package/dist/agent/turn.d.ts.map +1 -0
- package/dist/agent/turn.js +187 -0
- package/dist/agent/types.d.ts +226 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +9 -0
- package/dist/backend.d.ts +18 -5
- package/dist/backend.d.ts.map +1 -1
- package/dist/backends/claude.d.ts +5 -0
- package/dist/backends/claude.d.ts.map +1 -1
- package/dist/backends/claude.js +45 -17
- package/dist/backends/codex.d.ts +4 -0
- package/dist/backends/codex.d.ts.map +1 -1
- package/dist/backends/codex.js +14 -9
- package/dist/backends/custom.d.ts.map +1 -1
- package/dist/backends/custom.js +3 -2
- package/dist/backends/opencode.d.ts +4 -0
- package/dist/backends/opencode.d.ts.map +1 -1
- package/dist/backends/opencode.js +5 -1
- package/dist/backends/pi.d.ts +5 -2
- package/dist/backends/pi.d.ts.map +1 -1
- package/dist/backends/pi.js +15 -3
- package/dist/config-catalog.d.ts +173 -0
- package/dist/config-catalog.d.ts.map +1 -0
- package/dist/config-catalog.js +408 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/interactive.d.ts +4 -5
- package/dist/interactive.d.ts.map +1 -1
- package/dist/protocol-coverage.d.ts +82 -0
- package/dist/protocol-coverage.d.ts.map +1 -1
- package/dist/protocol-coverage.js +76 -0
- package/dist/registry.d.ts +12 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +22 -0
- package/dist/routing.d.ts +14 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +53 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +9 -68
- package/dist/session-ref.d.ts +8 -0
- package/dist/session-ref.d.ts.map +1 -0
- package/dist/session-ref.js +21 -0
- package/dist/structured-tool.d.ts +4 -0
- package/dist/structured-tool.d.ts.map +1 -1
- package/dist/structured-tool.js +5 -0
- package/dist/system-prompt.d.ts +13 -0
- package/dist/system-prompt.d.ts.map +1 -0
- package/dist/system-prompt.js +67 -0
- package/package.json +4 -4
package/dist/runner.js
CHANGED
|
@@ -21,10 +21,12 @@ import { PooledConnection, ReattachCapabilityUnavailable, isChildCleanupError, }
|
|
|
21
21
|
import { AcpAgentPool } from "./pool.js";
|
|
22
22
|
import { TypedEventEmitter, } from "./events.js";
|
|
23
23
|
import { InteractiveSession } from "./interactive.js";
|
|
24
|
-
import {
|
|
25
|
-
import { CustomAcpBackend } from "./backends/custom.js";
|
|
24
|
+
import { BUILTIN_BACKEND_IDS } from "./backends/builtins.js";
|
|
26
25
|
import { registryWithRunBackends, resolveBackendRegistry, } from "./registry.js";
|
|
27
26
|
import { mapThrownError } from "./errors-map.js";
|
|
27
|
+
import { assertNoModelConfigOption, resolveModelRoute } from "./routing.js";
|
|
28
|
+
import { assertSystemPromptSupported } from "./system-prompt.js";
|
|
29
|
+
import { sessionRefFor } from "./session-ref.js";
|
|
28
30
|
import { buildAuthDescriptor, } from "./auth/auth-types.js";
|
|
29
31
|
import { AuthStore, classifyCredential, } from "./auth/auth-store.js";
|
|
30
32
|
import { ProviderStore, providerVertexMeta } from "./provider-store.js";
|
|
@@ -1028,6 +1030,8 @@ export class AcpAgentRunner {
|
|
|
1028
1030
|
prepareSession(opts, config) {
|
|
1029
1031
|
const route = resolveModelRoute(opts.model ?? opts.tier, config.registry);
|
|
1030
1032
|
const backend = route.backend;
|
|
1033
|
+
// Refuse instructions the routed backend cannot carry BEFORE anything is acquired or sent.
|
|
1034
|
+
const systemPrompt = assertSystemPromptSupported(backend, opts.systemPrompt, opts.label);
|
|
1031
1035
|
const policy = {
|
|
1032
1036
|
allow: opts.toolNames,
|
|
1033
1037
|
deny: opts.disallowedToolNames,
|
|
@@ -1053,10 +1057,9 @@ export class AcpAgentRunner {
|
|
|
1053
1057
|
label: opts.label,
|
|
1054
1058
|
// Direct engine-call correlation on emitted events only; never sent on the ACP wire.
|
|
1055
1059
|
callIndex: opts.callIndex,
|
|
1056
|
-
//
|
|
1057
|
-
//
|
|
1058
|
-
|
|
1059
|
-
developerInstructions: opts.developerInstructions,
|
|
1060
|
+
// Backend-neutral system prompt instructions -> the backend's own session `_meta` keys
|
|
1061
|
+
// (validated above). Additive; never hashed.
|
|
1062
|
+
systemPrompt,
|
|
1060
1063
|
retainSessionLog: config.retainSessionLog,
|
|
1061
1064
|
},
|
|
1062
1065
|
};
|
|
@@ -1139,54 +1142,12 @@ async function applyModelSelection(session, spec, opts) {
|
|
|
1139
1142
|
await session.selectModel(spec);
|
|
1140
1143
|
opts.onModelResolved?.(spec);
|
|
1141
1144
|
}
|
|
1142
|
-
function assertNoModelConfigOption(configOptions, label) {
|
|
1143
|
-
if (!configOptions || !("model" in configOptions))
|
|
1144
|
-
return;
|
|
1145
|
-
throw new WorkflowError(`Agent call${label ? ` "${label}"` : ""} configOptions must not contain reserved option id "model"; use the model field instead`, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, { recoverable: false, agentLabel: label });
|
|
1146
|
-
}
|
|
1147
1145
|
/** Pick the backend for the effective model spec (`model` wins over `tier`). The first segment
|
|
1148
1146
|
* routes only when it is a registered custom or built-in harness name; everything else goes to
|
|
1149
1147
|
* the configured default backend without interpretation. */
|
|
1150
1148
|
export function selectBackend(opts, registry) {
|
|
1151
1149
|
return resolveModelRoute(opts.model ?? opts.tier, registry).backend;
|
|
1152
1150
|
}
|
|
1153
|
-
/** Resolve routing and the verbatim model value together so backend choice and prefix stripping
|
|
1154
|
-
* cannot drift. A registered custom name has priority over a built-in on collision. */
|
|
1155
|
-
function resolveModelRoute(spec, registry) {
|
|
1156
|
-
if (spec === undefined)
|
|
1157
|
-
return { backend: defaultBackend(registry), modelSpec: undefined };
|
|
1158
|
-
const slash = spec.indexOf("/");
|
|
1159
|
-
const firstSegment = asciiLowercase(slash >= 0 ? spec.slice(0, slash) : spec);
|
|
1160
|
-
const inner = slash >= 0 ? spec.slice(slash + 1) : undefined;
|
|
1161
|
-
const custom = registry?.get(firstSegment);
|
|
1162
|
-
if (custom)
|
|
1163
|
-
return { backend: new CustomAcpBackend(custom), modelSpec: inner };
|
|
1164
|
-
const builtIn = builtinBackend(firstSegment);
|
|
1165
|
-
if (builtIn)
|
|
1166
|
-
return { backend: builtIn, modelSpec: inner };
|
|
1167
|
-
return { backend: defaultBackend(registry), modelSpec: spec };
|
|
1168
|
-
}
|
|
1169
|
-
/** The re-attach handle for an open session: id + backend routing name + cwd + the
|
|
1170
|
-
* agent-advertised reopen surface. Contains no secrets; JSON-round-trippable. `backendId`
|
|
1171
|
-
* doubles as the `model` routing spec for loadSession/resumeSession/listSessions. */
|
|
1172
|
-
function sessionRefFor(session, backend, cwd) {
|
|
1173
|
-
const caps = session.capabilities;
|
|
1174
|
-
return {
|
|
1175
|
-
sessionId: session.sessionId,
|
|
1176
|
-
backendId: backend.id,
|
|
1177
|
-
poolKey: backend.poolKey ?? backend.id,
|
|
1178
|
-
...(session.initializeMeta !== undefined
|
|
1179
|
-
? { initializeMeta: session.initializeMeta }
|
|
1180
|
-
: {}),
|
|
1181
|
-
cwd,
|
|
1182
|
-
reopen: {
|
|
1183
|
-
load: caps?.supportsLoadSession === true,
|
|
1184
|
-
resume: caps?.supportsResumeSession === true,
|
|
1185
|
-
list: caps?.supportsListSessions === true,
|
|
1186
|
-
fork: caps?.supportsForkSession === true,
|
|
1187
|
-
},
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
1151
|
/** Interactive sessions are public and long-lived, so fail before spawning a dedicated process
|
|
1191
1152
|
* when the required worktree root is absent or not absolute. */
|
|
1192
1153
|
function validateInteractiveCwd(cwd, label, methodName) {
|
|
@@ -1267,23 +1228,3 @@ async function disposeBestEffort(connection) {
|
|
|
1267
1228
|
// Dedicated lifecycle processes are already no longer useful; never mask the request result.
|
|
1268
1229
|
}
|
|
1269
1230
|
}
|
|
1270
|
-
/** Resolve the default backend: a registered custom name wins (returned as a Backend), else
|
|
1271
|
-
* the built-in id. An unknown/unset value falls back to "claude" (the historical default). */
|
|
1272
|
-
function defaultBackend(registry) {
|
|
1273
|
-
const configured = process.env.AGENTPRISM_DEFAULT_BACKEND;
|
|
1274
|
-
const name = configured === undefined ? undefined : asciiLowercase(configured);
|
|
1275
|
-
if (name && registry) {
|
|
1276
|
-
const config = registry.get(name);
|
|
1277
|
-
if (config)
|
|
1278
|
-
return new CustomAcpBackend(config);
|
|
1279
|
-
}
|
|
1280
|
-
if (name) {
|
|
1281
|
-
const builtIn = builtinBackend(name);
|
|
1282
|
-
if (builtIn)
|
|
1283
|
-
return builtIn;
|
|
1284
|
-
}
|
|
1285
|
-
return BUILTIN_BACKENDS.claude.create();
|
|
1286
|
-
}
|
|
1287
|
-
function asciiLowercase(value) {
|
|
1288
|
-
return value.replace(/[A-Z]/g, (character) => String.fromCharCode(character.charCodeAt(0) + 32));
|
|
1289
|
-
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentSessionRef } from "@automatalabs/shared-types";
|
|
2
|
+
import type { SessionHandle } from "./acp-client.js";
|
|
3
|
+
import type { Backend } from "./backend.js";
|
|
4
|
+
/** The re-attach handle for an open session: id + backend routing name + cwd + the
|
|
5
|
+
* agent-advertised reopen surface. Contains no secrets; JSON-round-trippable. `backendId`
|
|
6
|
+
* doubles as the `model` routing spec for loadSession/resumeSession/listSessions. */
|
|
7
|
+
export declare function sessionRefFor(session: SessionHandle, backend: Backend, cwd: string): AgentSessionRef;
|
|
8
|
+
//# sourceMappingURL=session-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-ref.d.ts","sourceRoot":"","sources":["../src/session-ref.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;sFAEsF;AACtF,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,CAiBpG"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** The re-attach handle for an open session: id + backend routing name + cwd + the
|
|
2
|
+
* agent-advertised reopen surface. Contains no secrets; JSON-round-trippable. `backendId`
|
|
3
|
+
* doubles as the `model` routing spec for loadSession/resumeSession/listSessions. */
|
|
4
|
+
export function sessionRefFor(session, backend, cwd) {
|
|
5
|
+
const caps = session.capabilities;
|
|
6
|
+
return {
|
|
7
|
+
sessionId: session.sessionId,
|
|
8
|
+
backendId: backend.id,
|
|
9
|
+
poolKey: backend.poolKey ?? backend.id,
|
|
10
|
+
...(session.initializeMeta !== undefined
|
|
11
|
+
? { initializeMeta: session.initializeMeta }
|
|
12
|
+
: {}),
|
|
13
|
+
cwd,
|
|
14
|
+
reopen: {
|
|
15
|
+
load: caps?.supportsLoadSession === true,
|
|
16
|
+
resume: caps?.supportsResumeSession === true,
|
|
17
|
+
list: caps?.supportsListSessions === true,
|
|
18
|
+
fork: caps?.supportsForkSession === true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -5,7 +5,11 @@ export declare const STRUCTURED_OUTPUT_SERVER_NAME = "structured_output";
|
|
|
5
5
|
export declare const STRUCTURED_OUTPUT_TOOL_DESCRIPTION: string;
|
|
6
6
|
export interface StructuredOutputToolRegistration {
|
|
7
7
|
readonly url: string;
|
|
8
|
+
/** Peek at the last valid capture without consuming it (the runner's single-turn read). */
|
|
8
9
|
tryCaptured(): unknown | undefined;
|
|
10
|
+
/** Return the captured value and clear the slot — a capture belongs to exactly one turn, so a
|
|
11
|
+
* long-lived registration never hands turn N's object to turn N+1. */
|
|
12
|
+
takeCaptured(): unknown | undefined;
|
|
9
13
|
release(): void;
|
|
10
14
|
}
|
|
11
15
|
/** Runner-scoped localhost MCP host. It binds only once a schema run actually needs injection. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structured-tool.d.ts","sourceRoot":"","sources":["../src/structured-tool.ts"],"names":[],"mappings":"AASA,OAAO,EAOL,KAAK,IAAI,EACV,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAC9D,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAEjE,eAAO,MAAM,kCAAkC,QAM2C,CAAC;AAW3F,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,WAAW,IAAI,OAAO,GAAG,SAAS,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,kGAAkG;AAClG,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,IAAI,CAAqB;IAEjC,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,MAAM,GAAG,SAAS;IAI7B,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"structured-tool.d.ts","sourceRoot":"","sources":["../src/structured-tool.ts"],"names":[],"mappings":"AASA,OAAO,EAOL,KAAK,IAAI,EACV,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAKvC,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAC9D,eAAO,MAAM,6BAA6B,sBAAsB,CAAC;AAEjE,eAAO,MAAM,kCAAkC,QAM2C,CAAC;AAW3F,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,WAAW,IAAI,OAAO,GAAG,SAAS,CAAC;IACnC;2EACuE;IACvE,YAAY,IAAI,OAAO,GAAG,SAAS,CAAC;IACpC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,kGAAkG;AAClG,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,IAAI,CAAqB;IAEjC,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,MAAM,GAAG,SAAS;IAI7B,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,gCAAgC,CAAC;IA6BpE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAYhB,eAAe;YAoCf,aAAa;IAyB3B,OAAO,CAAC,OAAO;CAahB;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,CAI9E"}
|
package/dist/structured-tool.js
CHANGED
|
@@ -41,6 +41,11 @@ export class StructuredOutputToolHost {
|
|
|
41
41
|
return {
|
|
42
42
|
url: `http://${HOST}:${port}/${token}`,
|
|
43
43
|
tryCaptured: () => slot.captured,
|
|
44
|
+
takeCaptured: () => {
|
|
45
|
+
const captured = slot.captured;
|
|
46
|
+
slot.captured = undefined;
|
|
47
|
+
return captured;
|
|
48
|
+
},
|
|
44
49
|
release: once(() => {
|
|
45
50
|
this.slots.delete(token);
|
|
46
51
|
}),
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SystemPromptOptions } from "@automatalabs/shared-types";
|
|
2
|
+
import type { Backend } from "./backend.js";
|
|
3
|
+
import { type SystemPromptSupport } from "./protocol-coverage.js";
|
|
4
|
+
/** Human summary of a support row for error messages: `replace and append`, `append only`, `none`. */
|
|
5
|
+
export declare function describeSystemPromptSupport(support: SystemPromptSupport): string;
|
|
6
|
+
/**
|
|
7
|
+
* Validate `systemPrompt` for `backend` and return it normalized (only the defined fields, or
|
|
8
|
+
* undefined when nothing was requested). Throws SCRIPT_VALIDATION_ERROR when the value is not a
|
|
9
|
+
* plain object, a field is not a non-blank string, or the backend does not carry a requested
|
|
10
|
+
* field — an unsupported instruction is refused, never silently ignored.
|
|
11
|
+
*/
|
|
12
|
+
export declare function assertSystemPromptSupported(backend: Pick<Backend, "id" | "systemPrompt">, systemPrompt: SystemPromptOptions | undefined, label: string | undefined): SystemPromptOptions | undefined;
|
|
13
|
+
//# sourceMappingURL=system-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../src/system-prompt.ts"],"names":[],"mappings":"AAMA,OAAO,EAAoC,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAe7F,sGAAsG;AACtG,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAKhF;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,cAAc,CAAC,EAC7C,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAC7C,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,mBAAmB,GAAG,SAAS,CAoCjC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Backend-neutral system prompt instructions (`SystemPromptOptions`): the one validator every
|
|
2
|
+
// front door runs BEFORE a session opens — `AcpAgentRunner.prepareSession` (run / interactive /
|
|
3
|
+
// reattach), the `AcpAgent` constructor, `agent.fork()`, and the cold statics. The backend maps a
|
|
4
|
+
// validated value onto its own `_meta` dialect in `Backend.sessionMeta`; a backend whose
|
|
5
|
+
// `Backend.systemPrompt` row does not carry a field never receives it, and the caller learns why
|
|
6
|
+
// from a SCRIPT_VALIDATION_ERROR naming the backend, the field, and the escape hatch (`meta`).
|
|
7
|
+
import { WorkflowError, WorkflowErrorCode } from "@automatalabs/shared-types";
|
|
8
|
+
import { SYSTEM_PROMPT_UNSUPPORTED } from "./protocol-coverage.js";
|
|
9
|
+
const SYSTEM_PROMPT_FIELDS = ["replace", "append"];
|
|
10
|
+
function validationError(message, label) {
|
|
11
|
+
return new WorkflowError(message, WorkflowErrorCode.SCRIPT_VALIDATION_ERROR, {
|
|
12
|
+
recoverable: false,
|
|
13
|
+
agentLabel: label,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
/** Human summary of a support row for error messages: `replace and append`, `append only`, `none`. */
|
|
20
|
+
export function describeSystemPromptSupport(support) {
|
|
21
|
+
const supported = SYSTEM_PROMPT_FIELDS.filter((field) => support[field]);
|
|
22
|
+
if (supported.length === 0)
|
|
23
|
+
return "none";
|
|
24
|
+
if (supported.length === SYSTEM_PROMPT_FIELDS.length)
|
|
25
|
+
return supported.join(" and ");
|
|
26
|
+
return `${supported.join(", ")} only`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validate `systemPrompt` for `backend` and return it normalized (only the defined fields, or
|
|
30
|
+
* undefined when nothing was requested). Throws SCRIPT_VALIDATION_ERROR when the value is not a
|
|
31
|
+
* plain object, a field is not a non-blank string, or the backend does not carry a requested
|
|
32
|
+
* field — an unsupported instruction is refused, never silently ignored.
|
|
33
|
+
*/
|
|
34
|
+
export function assertSystemPromptSupported(backend, systemPrompt, label) {
|
|
35
|
+
if (systemPrompt === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
const prefix = `Agent call${label ? ` "${label}"` : ""}`;
|
|
38
|
+
if (!isRecord(systemPrompt)) {
|
|
39
|
+
throw validationError(`${prefix} systemPrompt must be an object with optional string fields replace / append`, label);
|
|
40
|
+
}
|
|
41
|
+
const unknown = Object.keys(systemPrompt).filter((key) => !SYSTEM_PROMPT_FIELDS.includes(key));
|
|
42
|
+
if (unknown.length > 0) {
|
|
43
|
+
throw validationError(`${prefix} systemPrompt has unknown field${unknown.length > 1 ? "s" : ""} ${unknown.map((key) => `"${key}"`).join(", ")}; only replace / append are accepted (a backend's own extras go through meta)`, label);
|
|
44
|
+
}
|
|
45
|
+
const normalized = {};
|
|
46
|
+
for (const field of SYSTEM_PROMPT_FIELDS) {
|
|
47
|
+
const value = systemPrompt[field];
|
|
48
|
+
if (value === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
51
|
+
throw validationError(`${prefix} systemPrompt.${field} must be a non-empty string`, label);
|
|
52
|
+
}
|
|
53
|
+
normalized[field] = value;
|
|
54
|
+
}
|
|
55
|
+
const requested = SYSTEM_PROMPT_FIELDS.filter((field) => normalized[field] !== undefined);
|
|
56
|
+
if (requested.length === 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
const support = backend.systemPrompt ?? SYSTEM_PROMPT_UNSUPPORTED;
|
|
59
|
+
const refused = requested.filter((field) => !support[field]);
|
|
60
|
+
if (refused.length > 0) {
|
|
61
|
+
throw validationError(`${prefix} systemPrompt.${refused.join(" / ")} is not supported by backend "${backend.id}" (supported: ${describeSystemPromptSupport(support)}); ` +
|
|
62
|
+
(support.replace || support.append
|
|
63
|
+
? "use a supported field"
|
|
64
|
+
: "this backend exposes no ACP system-prompt channel — drive its own configuration, or send its vendor `_meta` keys through the meta passthrough"), label);
|
|
65
|
+
}
|
|
66
|
+
return normalized;
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automatalabs/acp-agents",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22"
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.30",
|
|
33
33
|
"typebox": "1.3.2",
|
|
34
34
|
"@automatalabs/codex-acp": "2.6.0",
|
|
35
|
-
"@automatalabs/pi-acp": "0.
|
|
36
|
-
"@automatalabs/shared-types": "
|
|
35
|
+
"@automatalabs/pi-acp": "0.9.0",
|
|
36
|
+
"@automatalabs/shared-types": "3.0.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc -b",
|
|
40
|
-
"typecheck": "tsc --noEmit",
|
|
40
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit",
|
|
41
41
|
"test": "tsx --test \"test/**/*.test.ts\""
|
|
42
42
|
}
|
|
43
43
|
}
|