@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.
Files changed (82) hide show
  1. package/README.md +84 -11
  2. package/dist/acp-client.d.ts +26 -6
  3. package/dist/acp-client.d.ts.map +1 -1
  4. package/dist/acp-client.js +30 -7
  5. package/dist/agent/acp-agent.d.ts +141 -0
  6. package/dist/agent/acp-agent.d.ts.map +1 -0
  7. package/dist/agent/acp-agent.js +837 -0
  8. package/dist/agent/errors.d.ts +17 -0
  9. package/dist/agent/errors.d.ts.map +1 -0
  10. package/dist/agent/errors.js +37 -0
  11. package/dist/agent/events.d.ts +26 -0
  12. package/dist/agent/events.d.ts.map +1 -0
  13. package/dist/agent/events.js +165 -0
  14. package/dist/agent/fork.d.ts +27 -0
  15. package/dist/agent/fork.d.ts.map +1 -0
  16. package/dist/agent/fork.js +28 -0
  17. package/dist/agent/probe.d.ts +14 -0
  18. package/dist/agent/probe.d.ts.map +1 -0
  19. package/dist/agent/probe.js +87 -0
  20. package/dist/agent/process-registry.d.ts +9 -0
  21. package/dist/agent/process-registry.d.ts.map +1 -0
  22. package/dist/agent/process-registry.js +28 -0
  23. package/dist/agent/queue.d.ts +19 -0
  24. package/dist/agent/queue.d.ts.map +1 -0
  25. package/dist/agent/queue.js +90 -0
  26. package/dist/agent/routing.d.ts +27 -0
  27. package/dist/agent/routing.d.ts.map +1 -0
  28. package/dist/agent/routing.js +87 -0
  29. package/dist/agent/structured.d.ts +47 -0
  30. package/dist/agent/structured.d.ts.map +1 -0
  31. package/dist/agent/structured.js +90 -0
  32. package/dist/agent/turn.d.ts +65 -0
  33. package/dist/agent/turn.d.ts.map +1 -0
  34. package/dist/agent/turn.js +187 -0
  35. package/dist/agent/types.d.ts +226 -0
  36. package/dist/agent/types.d.ts.map +1 -0
  37. package/dist/agent/types.js +9 -0
  38. package/dist/backend.d.ts +18 -5
  39. package/dist/backend.d.ts.map +1 -1
  40. package/dist/backends/claude.d.ts +5 -0
  41. package/dist/backends/claude.d.ts.map +1 -1
  42. package/dist/backends/claude.js +45 -17
  43. package/dist/backends/codex.d.ts +4 -0
  44. package/dist/backends/codex.d.ts.map +1 -1
  45. package/dist/backends/codex.js +14 -9
  46. package/dist/backends/custom.d.ts.map +1 -1
  47. package/dist/backends/custom.js +3 -2
  48. package/dist/backends/opencode.d.ts +4 -0
  49. package/dist/backends/opencode.d.ts.map +1 -1
  50. package/dist/backends/opencode.js +5 -1
  51. package/dist/backends/pi.d.ts +5 -2
  52. package/dist/backends/pi.d.ts.map +1 -1
  53. package/dist/backends/pi.js +15 -3
  54. package/dist/config-catalog.d.ts +173 -0
  55. package/dist/config-catalog.d.ts.map +1 -0
  56. package/dist/config-catalog.js +408 -0
  57. package/dist/index.d.ts +11 -3
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +12 -1
  60. package/dist/interactive.d.ts +4 -5
  61. package/dist/interactive.d.ts.map +1 -1
  62. package/dist/protocol-coverage.d.ts +82 -0
  63. package/dist/protocol-coverage.d.ts.map +1 -1
  64. package/dist/protocol-coverage.js +76 -0
  65. package/dist/registry.d.ts +12 -0
  66. package/dist/registry.d.ts.map +1 -1
  67. package/dist/registry.js +22 -0
  68. package/dist/routing.d.ts +14 -0
  69. package/dist/routing.d.ts.map +1 -0
  70. package/dist/routing.js +53 -0
  71. package/dist/runner.d.ts.map +1 -1
  72. package/dist/runner.js +9 -68
  73. package/dist/session-ref.d.ts +8 -0
  74. package/dist/session-ref.d.ts.map +1 -0
  75. package/dist/session-ref.js +21 -0
  76. package/dist/structured-tool.d.ts +4 -0
  77. package/dist/structured-tool.d.ts.map +1 -1
  78. package/dist/structured-tool.js +5 -0
  79. package/dist/system-prompt.d.ts +13 -0
  80. package/dist/system-prompt.d.ts.map +1 -0
  81. package/dist/system-prompt.js +67 -0
  82. 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 { BUILTIN_BACKENDS, BUILTIN_BACKEND_IDS, builtinBackend, } from "./backends/builtins.js";
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
- // CODEX-ONLY session instruction overrides -> session/new _meta bare keys. Additive;
1057
- // never hashed. The Claude backend ignores them.
1058
- baseInstructions: opts.baseInstructions,
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;IAwBpE,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"}
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"}
@@ -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": "1.2.7",
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.8.0",
36
- "@automatalabs/shared-types": "2.2.0"
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
  }