@agen-ai/provider-acp 0.4.3 → 0.4.4

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 CHANGED
@@ -1,17 +1,20 @@
1
1
  # @agen-ai/provider-acp
2
2
 
3
3
  An optional provider for the AgenAI coding-agent runtime. It translates the stable
4
- Agent Client Protocol wire version 1 into the existing Agent Protocol V12 session
5
- API. The coordinated release is `0.4.3`; published `0.4.1` qualification remains
6
- historical evidence for its exact V11 artifacts.
4
+ Agent Client Protocol wire version 1 into the Agent Protocol V13 session API. Current source is
5
+ the unpublished coordinated eight-package `0.4.4` candidate. Published `0.4.3` is the seven-package
6
+ V12 release; published `0.4.1` qualification remains historical evidence for its exact V11 artifacts.
7
7
  The package is ESM, MIT licensed, side-effect-free on import and requires Node.js
8
8
  22 or newer. It depends only on the public transport, runtime and protocol packages;
9
9
  the core packages remain independent of ACP.
10
10
 
11
11
  The provider pins the transport's `schema-v1.23.0` stable profile. It translates
12
- ACP into current V12 capabilities and rejects unsupported semantics explicitly.
12
+ ACP into current V13 capabilities and rejects unsupported semantics explicitly.
13
13
  Connection negotiation, configured host facilities, and mapping policy fix one
14
14
  capability envelope per instance; every later connection must match it.
15
+ The [0.4.4 upgrade guide](../../UPGRADING-TO-0.4.4.md) describes the coordinated candidate.
16
+ Optional `@agen-ai/agent-history` browsing remains independent of this execution provider; native
17
+ transcript JSON and ACP metadata alone establish no structured-generation capability.
15
18
 
16
19
  Tool approvals default to once-only scoped consequences. Both constructors accept an explicit
17
20
  `permissionMapping` selection for completely described provider-managed remembered choices.
@@ -53,9 +56,11 @@ under a different policy.
53
56
  | Surface | Supported behavior and requirements |
54
57
  | --- | --- |
55
58
  | Turns and requests | Default text turns, streamed assistant/reasoning text, tool progress, cancellation and once-only exact-action permissions by default, with opt-in complete remembered-choice mappings. One execution retains the original native prompt across request waits and answers. |
59
+ | Execution input | Unsupported for every public ACP/Grok composition. There is no input mapping selection or native interjection dispatch; peer labels, metadata and extension advertisements cannot enable it. |
60
+ | Structured final output | Unsupported for generic ACP. Explicit `grok-build-1.0.62-structured-output-v1` mapping supports native turn-scoped `agenai.output-schema.v1` requests and independently validated terminal objects, within the bounds below. |
56
61
  | Resume | Exact persisted binding using advertised `session/resume`, otherwise advertised `session/load`. Load history is bounded and suppressed from new-turn output. No automatic recreation after failure. |
57
62
  | Configuration | Managed by default. Explicit `configuration: 'selectable'` maps observed boolean/select catalogs, with a session-mode fallback when config options are absent. Unknown metadata differs from observed empty metadata. |
58
- | Forms | Canonical boolean/single-select/multi-select forms that fit V12 constraints. An intent stage preserves decline separately from cancel, followed by answers on the same native waiter. Explicit host form facilities can handle broader native forms. |
63
+ | Forms | Canonical boolean/single-select/multi-select forms that fit V13 constraints. An intent stage preserves decline separately from cancel, followed by answers on the same native waiter. Explicit host form facilities can handle broader native forms. |
59
64
  | Images | Inline base64 PNG/JPEG/WebP only when the peer advertises image input, within image and total turn budgets. No implicit local-file read or URL fetch. |
60
65
  | Plans | Complete replacements of observed turn plans. A mode called “plan” does not establish enforced planning behavior or approval authority. |
61
66
  | File changes | Partial structured observations from native tool diffs. Canonical paths contained by the working directory become sorted relative display paths without filesystem reads. Unfinished tools remain unknown at ordinary turn completion. No final workspace diff or Review artifact is inferred. |
@@ -69,7 +74,7 @@ Through-turn branching, same-turn steering, enforced plan mode, persistent
69
74
  SDK-owned tool-approval grants, context usage/compaction, account quota, effective skill/rule
70
75
  inventory, collaboration, generated resources and artifact publication remain
71
76
  unsupported. ACP usage counters can decrease without confirmed compaction and
72
- cannot satisfy V12's context continuity contract. Unsupported media output fails
77
+ cannot satisfy V13's context continuity contract. Unsupported media output fails
73
78
  explicitly rather than disappearing from an otherwise successful transcript.
74
79
  Embedded terminal references also fail explicitly: a mutable, possibly truncated
75
80
  terminal snapshot does not provide an ordered append-only transcript. Agents can
@@ -80,9 +85,121 @@ this release. Host-owned integrations may use the transport directly with their
80
85
  own authorization and native lifecycle policy. No capability grants authority
81
86
  over other conversations or account state.
82
87
 
88
+ ## Input during an execution
89
+
90
+ The optional runtime `execution.input` contract is available in the unpublished `0.4.4`
91
+ candidate, but this provider exposes no supported execution-input port. Consumers must narrow
92
+ `execution.input?.kind === 'supported'` before sending. Absence and `{ kind: 'unsupported' }`
93
+ both mean unsupported; use ordinary `startTurn()` for new work when idle. Strict `turns.steer`
94
+ semantics are unchanged and remain unsupported here. The
95
+ [runtime README](../agent-runtime/README.md) explains receipt, consumption and output ownership.
96
+
97
+ Stable ACP `schema-v1.23.0` negotiation and standard unknown-extension handling do not grant
98
+ native input support. Grok Build `1.0.67` has native interjection facilities, but the inspected
99
+ behavior did not establish an authoritative input-to-command link, ownership of autonomous wake
100
+ work, or reusable-session producer quiescence. Queued acknowledgements, matching text and empty
101
+ queue snapshots cannot establish those guarantees. No Grok input mapping is shipped. The
102
+ independent Grok `1.0.62` structured-output mapping and history qualification retain their own
103
+ versions and scope.
104
+
105
+ Installed public-package proof exercises six unsupported cases: generic ACP, Grok labels and
106
+ input advertisements, and the selected Grok `1.0.62` structured-output mapping, each with borrowed
107
+ and owned connections. The actual packed provider constructors use a synthetic serialized peer;
108
+ all six retain one original turn and reader, dispatch zero native input requests, and join cleanup.
109
+ An unsolicited input extension request receives `-32601`. This is public unsupported-boundary
110
+ proof, separate from private controlled Claude qualification; no public Claude execution constructor ships.
111
+
112
+ ## Structured final output
113
+
114
+ ACP schema `1.23.0` does not define a portable final-output-schema capability. Generic provider
115
+ instances declare `turns.structuredOutput: { kind: 'unsupported' }`. Standard unknown-extension
116
+ handling, a peer accepting metadata, or JSON-looking assistant text cannot enable it.
117
+
118
+ Both constructors accept one exact opt-in native mapping:
119
+
120
+ ```ts
121
+ import { parseAgentProviderKey } from '@agen-ai/agent-protocol';
122
+ import { createNodeAcpProviderDriver } from '@agen-ai/provider-acp/node';
123
+
124
+ const driver = createNodeAcpProviderDriver({
125
+ providerKey: parseAgentProviderKey('grok-build'),
126
+ structuredOutputMapping: { mapping: 'grok-build-1.0.62-structured-output-v1' },
127
+ launch: {
128
+ executable: approvedExecutable,
129
+ args: ['agent', '--no-leader', 'stdio'],
130
+ cwd: workingDirectory,
131
+ env: explicitNativeEnvironment,
132
+ },
133
+ });
134
+ ```
135
+
136
+ The host supplies and verifies the executable, environment, credentials and confinement policy.
137
+ For injected connections, pass the same `structuredOutputMapping` to `createAcpProviderDriver`.
138
+ The selection is captured at construction; each connection must negotiate the pinned stable ACP
139
+ profile and initialize with Grok's exact `grokShell: true` / `agentVersion: '1.0.62'` identity.
140
+ Initialization identity is an admission check, not executable provenance or inference proof.
141
+ Changing a mapping needs a new driver/materialization; it is absent from serialized empty
142
+ `driverConfiguration`.
143
+
144
+ The supported declaration has `mechanism: 'native'`,
145
+ `schemaProfile: 'agenai.output-schema.v1'`, `scope: 'turn'`, `maxSchemaBytes: 16_384`,
146
+ `maxOutputBytes: 16_384`, `resume: false`, and `branch: false`. Supply the schema on `startTurn`,
147
+ or use [`generateObject`](../agent-runtime/README.md#generate-a-typed-object), which borrows the
148
+ selected instance and owns a fresh session through cleanup. Session-opening output is rejected.
149
+ A later ordinary turn omits `output` and sends no schema. Ordinary exact-binding resume remains
150
+ separate; structured requests cannot restore schema state through resume or branch.
151
+
152
+ The mapping sends the admitted schema through prompt `_meta.outputSchema` and reads only the
153
+ correlated authoritative prompt response's `_meta.structuredOutput`. A successful `end_turn`
154
+ and a complete schema-valid object are both required for terminal `structuredOutput.kind: 'json'`.
155
+ Text deltas are not parsed as output, and the SDK never extracts a JSON substring or repairs it.
156
+ The shared runtime validates the candidate independently against the exact admitted schema.
157
+
158
+ | Native observation | Portable structured result |
159
+ | --- | --- |
160
+ | `refusal` | `unavailable/refused`, preserving native completed outcome |
161
+ | Non-`end_turn` stop, including token/request limit or cancellation | `unavailable/incomplete`, preserving native outcome |
162
+ | Successful `end_turn` without structured metadata | `unavailable/missing_output` |
163
+ | Bounded native `structuredOutputError` | `unavailable/provider_error`; Grok's free text cannot distinguish exhausted retries from other native validation errors |
164
+ | Oversized metadata/value | `unavailable/output_limit_exceeded` |
165
+ | Complete object that fails the admitted schema | `unavailable/schema_mismatch` with bounded issues |
166
+
167
+ Native structured metadata is capped at 32,768 UTF-8 bytes and its error string at 4,096 bytes.
168
+ The canonical schema/value ceilings are each 16,384 serialized bytes, with the additional
169
+ [portable shape limits](../agent-protocol/README.md#the-agenaioutput-schemav1-profile). Ambiguous
170
+ value-plus-error metadata, a wrong native session identity, or malformed error metadata is a
171
+ contract failure. Ordinary turns report `structuredOutput: { kind: 'not_requested' }`.
172
+ Only `outcome: 'completed'` can carry `json`; a native finished turn does not guarantee generation
173
+ success. There is no SDK generation retry, fallback model or tool replay. Native validation/fallback
174
+ attempts may occur inside the provider's one prompt and are not described as SDK retries.
175
+
176
+ Selecting this mapping does not disable native tools or alter host permission policy. A generation
177
+ that requires a human response needs an explicit handler; the helper fails with
178
+ `interaction_required` when one is absent. Retain cleanup errors and their callable retry owners
179
+ as described by the [runtime generation lifecycle](../agent-runtime/README.md#deadlines-requests-and-cleanup).
180
+
181
+ ### Qualified installed native scope
182
+
183
+ The 2026-10-22 qualification used all eight installed public `0.4.4` candidate tarballs, only public
184
+ entrypoints, stable ACP schema `1.23.0`, Linux x86_64 / Node `24.18.0`, Grok Build `1.0.62`
185
+ (`1474f9cbdd91`), `grok-4.3`, low reasoning and explicit xAI API-key authentication. The executable
186
+ SHA-256 was `2a46d9e4d7ebd7a9cff7f95716714b19dad5f21dd2a497822400b1fb128969aa`.
187
+ The shipped title example returned a typed title and saved it after session/reader closure. Further
188
+ native turns produced an independently validated rich nested object, a changed `{ count: 7 }`
189
+ schema, and ordinary `Ready` text with no schema. Sessions/readers closed, both observed native
190
+ process groups were absent, the caller-owned registry was disposed and owned scratch removed.
191
+
192
+ This exact local tuple establishes the public mapping's installed native behavior. It does not
193
+ qualify generic ACP, other Grok releases/models/authentication modes, OpenCode, Cursor or arbitrary
194
+ harnesses. Process-group evidence does not establish escaped-descendant or cgroup containment;
195
+ the Node launcher grants no production execution authority or backend billing guarantee. The
196
+ separate private Codex and Claude diagnostic mappings are not public provider packages and cannot
197
+ be imported through this package. Existing history/delegation/approval qualifications below keep
198
+ their original version-specific scope. The `0.4.4` candidate remains unpublished.
199
+
83
200
  ## Operational failures and uncertainty
84
201
 
85
- The provider maps supported standard `schema-v1.23.0` prompt errors into V12 `AgentError` values.
202
+ The provider maps supported standard `schema-v1.23.0` prompt errors into V13 `AgentError` values.
86
203
  Standard `-32000` reports `authentication` with an `authenticate` user-action hint; `-32602` reports
87
204
  `invalid_request`; `-32603` reports `provider`; standard parse/request/method errors report `protocol`.
88
205
  Other remote codes remain `unknown` with unknown recovery. Mapping uses the standard error code,
@@ -414,7 +531,7 @@ unknown-extension handling never enables it implicitly.
414
531
  The selected launch arguments are exactly `['agent', '--no-leader', 'stdio']`.
415
532
  The adapter exposes the exact `sessionInteractions.mappings` tuple
416
533
  `{ openingOperation: 'create', requestKind: 'decision' }`, exclusive producer
417
- ownership and, in current 0.4.3 source, `humanWait: ['bounded', 'until_withdrawn_or_closed']`. The handler receives a neutral workspace
534
+ ownership and, in current source, `humanWait: ['bounded', 'until_withdrawn_or_closed']`. The handler receives a neutral workspace
418
535
  decision and can keep its response capability after opening returns, before
419
536
  any model turn. Use the generic
420
537
  [runtime lifecycle](../agent-runtime/README.md#session-lifetime-interactions)
@@ -479,7 +596,7 @@ other builds/platforms, browser login, native durable recovery, SDK-managed
479
596
  remembered permissions or private execution support. The earlier failed native
480
597
  ownership attempt remains unqualified.
481
598
 
482
- The current V12 decision descriptor omits `expiresAt` because this native mapping reports no
599
+ The current V13 decision descriptor omits `expiresAt` because this native mapping reports no
483
600
  provider-owned expiry. The bounded host timer remains a separate policy boundary;
484
601
  `humanWait: { kind: 'until_withdrawn_or_closed' }` omits that human timer and
485
602
  rejects an explicitly supplied `limits.requestTimeoutMs`. Initialization,
@@ -494,7 +611,7 @@ validated consumer facade. Session forms, plan reviews and native continuations
494
611
  remain unsupported by `grok-folder-trust-v1`. The retained SDK02 native evidence
495
612
  above remains specific to its original finite waiting configuration.
496
613
 
497
- ## Restored plan requests in current 0.4.3 source
614
+ ## Restored plan requests
498
615
 
499
616
  The Node constructor accepts a separate explicit selection:
500
617
  `sessionInteractions: { mapping: 'grok-build-1.0.60-session-requests-v1' }`.
@@ -921,8 +1038,8 @@ close ends that handle's ownership. The host retains control over its process po
921
1038
  Canonical forms preflight the whole schema before presenting a request. They
922
1039
  support at most 16 ordinary fields and 100 choices per selection. Free-text fields,
923
1040
  nested objects, numeric fields, defaults, patterns/formats, and constraints without
924
- a V12 representation are rejected. Native free text permits blank/whitespace values
925
- and measures length in Unicode code points; V12 text answers use nonblank/UTF-16
1041
+ a V13 representation are rejected. Native free text permits blank/whitespace values
1042
+ and measures length in Unicode code points; V13 text answers use nonblank/UTF-16
926
1043
  bounds. The provider does not narrow those native semantics silently. Enumerated
927
1044
  string values can map through opaque canonical selection IDs.
928
1045
  A required multi-select must require exactly one minimum selection; an optional
@@ -943,7 +1060,7 @@ binding grants no interaction authority. Their host callbacks receive a turn
943
1060
  lifetime signal that ends on cancellation, settlement or close.
944
1061
 
945
1062
  Image input allows at most 16 images, 375,000 decoded bytes per image and 750,000
946
- decoded bytes total, subject to the stricter 1 MiB V12 turn-content bound. Declared
1063
+ decoded bytes total, subject to the stricter 1 MiB V13 turn-content bound. Declared
947
1064
  media type, dimensions, base64 encoding and byte counts are validated. The provider
948
1065
  does not decode pixels or publish generated bytes; hosts needing image-content
949
1066
  inspection must perform it before submitting input.
@@ -952,18 +1069,40 @@ inspection must perform it before submitting input.
952
1069
 
953
1070
  `limits` can override `maxQueuedMessages`, `maxQueuedBytes`,
954
1071
  `maxPendingRequests`, `maxHostTerminals`, `hostOperationTimeoutMs`,
955
- `authenticationTimeoutMs`, `initializationTimeoutMs`, `promptTimeoutMs`, and
1072
+ `authenticationTimeoutMs`, `initializationTimeoutMs`, `terminalWaitTimeoutMs`, and
956
1073
  `cancellationTimeoutMs`. Defaults share a 128-entry, 8 MiB budget across queued
957
1074
  sources and retained current-turn item state, with output/control headroom
958
1075
  reserved inside that budget. Large bursts and turns with many retained items
959
1076
  can exhaust the same budget. Defaults allow 16 pending requests, 16 host terminals,
960
1077
  30 seconds for initialization or a host operation, 15 minutes for authentication,
961
- 30 minutes for a prompt, and 5 seconds for cancellation. Admission
1078
+ 30 minutes for an individual host-terminal exit wait, and 5 seconds for cancellation. Admission
962
1079
  reserves finite source capacity synchronously. Response and cancellation
963
1080
  processing remain independent of slow output consumers. Overflow, malformed
964
1081
  messages, process loss, or unconfirmed cancellation fail closed and require
965
1082
  closing the unusable session before rematerialization.
966
1083
 
1084
+ Ordinary prompts have no implicit execution deadline. Omit turn `deadlineAt` to
1085
+ retain the original prompt, sole output reader and connection until native
1086
+ completion, explicit cancellation or close. Startup, authentication, host APIs,
1087
+ human-decision policies, output capacity and cleanup keep their independent bounds.
1088
+ Selected native continuations also retain their original execution and reader;
1089
+ their preparation, promotion and response observations remain separately bounded.
1090
+
1091
+ Set `deadlineAt` on each `startTurn` for a caller-selected finite execution budget,
1092
+ for example `new Date(Date.now() + 86_400_000).toISOString()` for one day. Distant
1093
+ dates use timer slices without an SDK business-duration ceiling or premature Node
1094
+ timer overflow. Expiry sends native `session/cancel` while retaining the original
1095
+ prompt RPC until its native terminal. A confirmed canceled response produces
1096
+ `expired`; an unconfirmed cancellation fails the session and starts owned cleanup.
1097
+ Neither expiry nor a cancellation request invents a completed native receipt.
1098
+
1099
+ The former provider `limits.promptTimeoutMs` is rejected. Migrate its numeric
1100
+ budget to each turn's `deadlineAt`; use `terminalWaitTimeoutMs` only for host-terminal
1101
+ exit observation. That setting preserves the prior finite 30-minute default and
1102
+ maximum. A host-terminal wait timeout leaves its terminal lease owned for explicit
1103
+ kill/release or connection cleanup. Applications retain live owners and close them;
1104
+ unbounded prompt waiting does not provide controller-crash survival.
1105
+
967
1106
  Readiness reports completed initialization and the reported agent version when
968
1107
  available. It does not prove authentication or inference. The Node launcher
969
1108
  does not install agents, load credentials, apply a sandbox, or grant production
@@ -2,5 +2,6 @@ import { type AgentAuthenticationFlow, type AgentCapabilities, type AgentProvide
2
2
  import type { AcpNegotiatedAgent } from './connection/contracts.js';
3
3
  import type { AcpSessionInteractionSelection } from './sessionInteractions/contracts.js';
4
4
  import type { AcpPermissionMappingSelection } from './permissions/contracts.js';
5
- export declare function acpProviderCapabilities(providerKey: AgentProviderKey, negotiated: AcpNegotiatedAgent, configuration?: 'managed' | 'selectable', authenticationFlows?: readonly AgentAuthenticationFlow[], sessionInteractions?: AcpSessionInteractionSelection, permissionMapping?: AcpPermissionMappingSelection): AgentCapabilities;
5
+ import { type AcpStructuredOutputMappingSelection } from './structuredOutput/mapping.js';
6
+ export declare function acpProviderCapabilities(providerKey: AgentProviderKey, negotiated: AcpNegotiatedAgent, configuration?: 'managed' | 'selectable', authenticationFlows?: readonly AgentAuthenticationFlow[], sessionInteractions?: AcpSessionInteractionSelection, permissionMapping?: AcpPermissionMappingSelection, structuredOutputMapping?: AcpStructuredOutputMappingSelection): AgentCapabilities;
6
7
  //# sourceMappingURL=capabilities.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { parseAgentCapabilities } from "@agen-ai/agent-protocol";
2
2
  import { acpPermissionCapability } from "./permissions/selection.js";
3
+ import { acpStructuredOutputCapability } from "./structuredOutput/mapping.js";
3
4
  function freezeCapabilities(value) {
4
5
  if (value && typeof value === "object") {
5
6
  for (const child of Object.values(value)) freezeCapabilities(child);
@@ -7,12 +8,12 @@ function freezeCapabilities(value) {
7
8
  }
8
9
  return value;
9
10
  }
10
- function acpProviderCapabilities(providerKey, negotiated, configuration = "managed", authenticationFlows = [], sessionInteractions, permissionMapping = { mapping: "once-only-v1" }) {
11
+ function acpProviderCapabilities(providerKey, negotiated, configuration = "managed", authenticationFlows = [], sessionInteractions, permissionMapping = { mapping: "once-only-v1" }, structuredOutputMapping) {
11
12
  return freezeCapabilities(parseAgentCapabilities({
12
- protocolVersion: 12,
13
+ protocolVersion: 13,
13
14
  providerKey,
14
15
  sessions: { create: true, resume: sessionInteractions?.mapping !== "grok-folder-trust-v1" && (negotiated.agentCapabilities.loadSession === true || negotiated.agentCapabilities.sessionCapabilities?.resume != null), branch: { kind: "unsupported" } },
15
- turns: { interactionModes: ["default"], interrupt: true, steer: { kind: "unsupported" } },
16
+ turns: { structuredOutput: acpStructuredOutputCapability(structuredOutputMapping), interactionModes: ["default"], interrupt: true, steer: { kind: "unsupported" } },
16
17
  requests: {
17
18
  approval: acpPermissionCapability(permissionMapping),
18
19
  elicitation: { kind: "supported", fieldKinds: ["single_select", "multi_select", "boolean"], maxFields: 16, sensitiveFields: false }
@@ -24,7 +24,7 @@ export interface AcpNegotiatedAgent {
24
24
  name: string;
25
25
  version: string;
26
26
  }>;
27
- /** Bounded compatibility observation retained only by the selected Grok permission profile. */
27
+ /** Bounded native version observation retained by explicitly selected Grok mappings. */
28
28
  readonly agentVersion?: string;
29
29
  readonly agentCapabilities: AcpJsonObject;
30
30
  readonly authMethods: readonly AcpJsonValue[];
@@ -5,6 +5,7 @@ import type { AcpConnectionAcquisition, AcpInitializedConnection } from './contr
5
5
  import type { AcpAuthenticationMethods, AcpHostFacilities } from '../host/contracts.js';
6
6
  import type { AcpSessionInteractionSelection } from '../sessionInteractions/contracts.js';
7
7
  import type { AcpPermissionMappingSelection } from '../permissions/contracts.js';
8
+ import type { AcpStructuredOutputMappingSelection } from '../structuredOutput/mapping.js';
8
9
  export declare function initializeAcpConnection(input: {
9
10
  readonly source: AcpConnectionAcquisition;
10
11
  readonly unknownExtensionPolicy: AcpUnknownExtensionPolicy;
@@ -16,5 +17,6 @@ export declare function initializeAcpConnection(input: {
16
17
  readonly configuration?: 'managed' | 'selectable';
17
18
  readonly sessionInteractions?: AcpSessionInteractionSelection;
18
19
  readonly permissionMapping?: AcpPermissionMappingSelection;
20
+ readonly structuredOutputMapping?: AcpStructuredOutputMappingSelection;
19
21
  }): Promise<AcpInitializedConnection>;
20
22
  //# sourceMappingURL=initialize.d.ts.map
@@ -208,7 +208,7 @@ async function initializeAcpConnection(input) {
208
208
  signal: initialization.signal
209
209
  });
210
210
  if (stopped.signal.aborted) throw stopped.signal.reason;
211
- const negotiated = readNegotiatedAgent(response, input.permissionMapping, input.sessionInteractions);
211
+ const negotiated = readNegotiatedAgent(response, input.permissionMapping, input.sessionInteractions, input.structuredOutputMapping);
212
212
  initialization.signal.removeEventListener("abort", onAbort);
213
213
  return Object.freeze({
214
214
  client,
@@ -2,7 +2,8 @@ import type { AcpJsonValue } from '@agen-ai/acp-transport';
2
2
  import type { AcpNegotiatedAgent } from './contracts.js';
3
3
  import type { AcpPermissionMappingSelection } from '../permissions/contracts.js';
4
4
  import type { AcpSessionInteractionSelection } from '../sessionInteractions/contracts.js';
5
+ import { type AcpStructuredOutputMappingSelection } from '../structuredOutput/mapping.js';
5
6
  /** The transport has already validated the complete pinned initialize response. */
6
- export declare function readNegotiatedAgent(response: AcpJsonValue, permissionMapping?: AcpPermissionMappingSelection, sessionInteractions?: AcpSessionInteractionSelection): AcpNegotiatedAgent;
7
+ export declare function readNegotiatedAgent(response: AcpJsonValue, permissionMapping?: AcpPermissionMappingSelection, sessionInteractions?: AcpSessionInteractionSelection, structuredOutputMapping?: AcpStructuredOutputMappingSelection): AcpNegotiatedAgent;
7
8
  export declare function negotiatedEnvelope(negotiated: AcpNegotiatedAgent): string;
8
9
  //# sourceMappingURL=negotiation.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { AcpProviderConnectionError } from "./errors.js";
2
+ import { assertAcpStructuredOutputIdentity } from "../structuredOutput/mapping.js";
2
3
  function immutableProtocolData(value) {
3
4
  if (Array.isArray(value)) {
4
5
  const items = value.map(immutableProtocolData);
@@ -11,7 +12,7 @@ function immutableProtocolData(value) {
11
12
  }
12
13
  return value;
13
14
  }
14
- function readNegotiatedAgent(response, permissionMapping = { mapping: "once-only-v1" }, sessionInteractions) {
15
+ function readNegotiatedAgent(response, permissionMapping = { mapping: "once-only-v1" }, sessionInteractions, structuredOutputMapping) {
15
16
  const value = response;
16
17
  if (value.protocolVersion !== 1) {
17
18
  throw new AcpProviderConnectionError("acp_provider_version_unsupported", "ACP wire version 1 is required.");
@@ -28,6 +29,8 @@ function readNegotiatedAgent(response, permissionMapping = { mapping: "once-only
28
29
  agentInfo = Object.freeze({ name: info.name, version: info.version });
29
30
  }
30
31
  let agentVersion;
32
+ assertAcpStructuredOutputIdentity(value, structuredOutputMapping);
33
+ if (structuredOutputMapping) agentVersion = "1.0.62";
31
34
  if (sessionInteractions?.mapping === "grok-build-1.0.60-session-requests-v1") {
32
35
  const meta = value._meta;
33
36
  if (meta === null || typeof meta !== "object" || Array.isArray(meta) || meta.agentVersion !== "1.0.60") {
@@ -5,6 +5,7 @@ import type { AcpProviderLimits } from './limits.js';
5
5
  import type { AcpStandardMcpServer, AcpUnknownExtensionPolicy } from '@agen-ai/acp-transport';
6
6
  import type { AcpAuthenticationMethods, AcpHostFacilities } from './host/contracts.js';
7
7
  import type { AcpPermissionMappingSelection } from './permissions/contracts.js';
8
+ import type { AcpStructuredOutputMappingSelection } from './structuredOutput/mapping.js';
8
9
  export type { AcpProviderLimits } from './limits.js';
9
10
  export interface AcpConnectionStreams {
10
11
  readonly stdout: Readable;
@@ -54,6 +55,8 @@ export interface CreateAcpProviderDriverOptions {
54
55
  readonly unknownExtensionPolicy?: AcpUnknownExtensionPolicy;
55
56
  /** Defaults to once-only choices; complete profiles require independently qualified native semantics. */
56
57
  readonly permissionMapping?: AcpPermissionMappingSelection;
58
+ /** Opts into the exact qualified native schema contract; absent means unsupported. */
59
+ readonly structuredOutputMapping?: AcpStructuredOutputMappingSelection;
57
60
  readonly limits?: Partial<AcpProviderLimits>;
58
61
  readonly configuration?: 'managed' | 'selectable';
59
62
  readonly authentication?: AcpAuthenticationMethods;
@@ -7,6 +7,7 @@ import { resolveAuthenticationMapping } from "./auth/methods.js";
7
7
  import { resolveHostFacilities } from "./host/validation.js";
8
8
  import { resolveAcpMcpServers, resolveAcpSessionMcpBinding } from "./mcp.js";
9
9
  import { resolveAcpPermissionMapping } from "./permissions/selection.js";
10
+ import { resolveAcpStructuredOutputMapping } from "./structuredOutput/mapping.js";
10
11
  function createAcpProviderDriverInternal(options, sessionInteractions) {
11
12
  const providerKey = parseAgentProviderKey(options.providerKey);
12
13
  const connectionSource = options.connectionSource;
@@ -21,6 +22,7 @@ function createAcpProviderDriverInternal(options, sessionInteractions) {
21
22
  const mcpServers = resolveAcpMcpServers(options.mcpServers);
22
23
  const sessionMcpBinding = resolveAcpSessionMcpBinding(options.sessionMcpBinding);
23
24
  const permissionMapping = resolveAcpPermissionMapping(options.permissionMapping);
25
+ const structuredOutputMapping = resolveAcpStructuredOutputMapping(options.structuredOutputMapping);
24
26
  const usedStreams = /* @__PURE__ */ new WeakSet();
25
27
  return defineAgentProviderDriver({
26
28
  providerKey,
@@ -38,6 +40,7 @@ function createAcpProviderDriverInternal(options, sessionInteractions) {
38
40
  host,
39
41
  mcpServers,
40
42
  permissionMapping,
43
+ ...structuredOutputMapping ? { structuredOutputMapping } : {},
41
44
  ...sessionMcpBinding ? { sessionMcpBinding } : {},
42
45
  ...sessionInteractions ? { sessionInteractions } : {}
43
46
  })
@@ -31,9 +31,19 @@ export declare class AcpHostRouter {
31
31
  readonly requestId: AcpRequestId;
32
32
  }) => void;
33
33
  onResponseReceived: (response: import("@agen-ai/acp-transport").AcpResponseReceived) => void;
34
+ signal?: AbortSignal;
35
+ lifetime?: "bounded";
34
36
  timeoutMs?: number;
35
37
  deadlineAt?: number | Date;
38
+ } | {
39
+ onRequestDispatch: (context: {
40
+ readonly requestId: AcpRequestId;
41
+ }) => void;
42
+ onResponseReceived: (response: import("@agen-ai/acp-transport").AcpResponseReceived) => void;
36
43
  signal?: AbortSignal;
44
+ lifetime: "until_settled";
45
+ timeoutMs?: never;
46
+ deadlineAt?: never;
37
47
  };
38
48
  close: () => Promise<void>;
39
49
  };
@@ -95,7 +95,7 @@ class AcpHostRouter {
95
95
  if (!ingress) throw invalidHostRequest();
96
96
  return ingress.onPermissionRequest(context);
97
97
  }
98
- const timeout = context.method === "terminal/wait_for_exit" ? this.input.limits.promptTimeoutMs : this.input.limits.hostOperationTimeoutMs;
98
+ const timeout = context.method === "terminal/wait_for_exit" ? this.input.limits.terminalWaitTimeoutMs : this.input.limits.hostOperationTimeoutMs;
99
99
  const deadline = hostDeadline(session.signal, timeout);
100
100
  return this.hostRequest(context.method, params, { ...session, signal: deadline.signal }).finally(() => deadline.close());
101
101
  }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { createAcpProviderDriver } from './driver.js';
2
2
  export { DEFAULT_ACP_PROVIDER_LIMITS, type AcpProviderLimits } from './limits.js';
3
3
  export { AcpProviderConnectionError } from './connection/errors.js';
4
4
  export { AcpProviderSessionError } from './session/errors.js';
5
+ export type { AcpStructuredOutputMappingSelection } from './structuredOutput/mapping.js';
5
6
  export type { AcpPermissionOptionSignature, AcpPermissionMenuSignature, AcpPermissionMappingSelection, } from './permissions/contracts.js';
6
7
  export type { AcpConnectionStreams, AcpConnectionLease, AcpConnectionFactoryContext, AcpConnectionFactory, AcpSessionMcpBindingContext, AcpSessionMcpBindingLease, AcpSessionMcpBindingFactory, AcpSessionMcpBindingOptions, CreateAcpProviderDriverOptions, } from './contracts.js';
7
8
  export type { AcpAuthenticationMethods, AcpHostFacilities, AcpHostInteractionScope, AcpHostInteractionInput, AcpHostFormInput, AcpHostFormResponse, AcpHostUrlInput, AcpHostUrlDecision, AcpHostUrlInteractionLease, AcpHostSessionInput, AcpHostFileReadInput, AcpHostFileWriteInput, AcpHostTerminalCreateInput, AcpHostTerminalLease, AcpHostTerminalOutput, AcpHostTerminalExitStatus, AcpHostAuthenticationTerminalInput, AcpHostAuthenticationTerminalLease, } from './host/contracts.js';
@@ -7,6 +7,7 @@ import type { AcpProviderLimits } from './limits.js';
7
7
  import type { AcpConnectionSource } from './connection/contracts.js';
8
8
  import type { AcpSessionInteractionSelection } from './sessionInteractions/contracts.js';
9
9
  import type { AcpPermissionMappingSelection } from './permissions/contracts.js';
10
+ import type { AcpStructuredOutputMappingSelection } from './structuredOutput/mapping.js';
10
11
  export declare function createAcpProviderInstance(input: {
11
12
  readonly providerKey: AgentProviderKey;
12
13
  readonly instanceId: AgentInstanceId;
@@ -19,6 +20,7 @@ export declare function createAcpProviderInstance(input: {
19
20
  readonly host: AcpHostFacilities;
20
21
  readonly mcpServers: readonly AcpStandardMcpServer[];
21
22
  readonly permissionMapping: AcpPermissionMappingSelection;
23
+ readonly structuredOutputMapping?: AcpStructuredOutputMappingSelection;
22
24
  readonly sessionMcpBinding?: AcpSessionMcpBindingOptions;
23
25
  readonly sessionInteractions?: AcpSessionInteractionSelection;
24
26
  }): Promise<MaterializedAgentProviderInstance>;
package/dist/instance.js CHANGED
@@ -43,6 +43,7 @@ async function createAcpProviderInstance(input) {
43
43
  authentication: input.authentication,
44
44
  host: input.host,
45
45
  permissionMapping: input.permissionMapping,
46
+ ...input.structuredOutputMapping ? { structuredOutputMapping: input.structuredOutputMapping } : {},
46
47
  ...input.sessionInteractions ? { sessionInteractions: input.sessionInteractions } : {}
47
48
  });
48
49
  };
@@ -53,7 +54,15 @@ async function createAcpProviderInstance(input) {
53
54
  try {
54
55
  assertAcpMcpSupport(input.mcpServers, negotiated);
55
56
  methods = selectedAuthenticationMethods(negotiated, input.authentication, input.host);
56
- capabilities = acpProviderCapabilities(input.providerKey, negotiated, input.configuration, [...methods.keys()], input.sessionInteractions, input.permissionMapping);
57
+ capabilities = acpProviderCapabilities(
58
+ input.providerKey,
59
+ negotiated,
60
+ input.configuration,
61
+ [...methods.keys()],
62
+ input.sessionInteractions,
63
+ input.permissionMapping,
64
+ input.structuredOutputMapping
65
+ );
57
66
  } catch (cause) {
58
67
  if (eager.execution?.cleanupFailure) throw eager.execution.retainedError(cause, eager.execution.cleanupFailure.error);
59
68
  try {
@@ -133,6 +142,11 @@ async function createAcpProviderInstance(input) {
133
142
  const open = async (context, resume) => {
134
143
  throwIfAgentOperationAborted(context.signal);
135
144
  throwIfAgentOperationAborted(lifetime.signal);
145
+ if ("output" in context && context.output !== void 0) throw new AgentProviderContractError(
146
+ input.providerKey,
147
+ capabilities.turns.structuredOutput.kind === "unsupported" ? "structured_output_unsupported" : "structured_output_scope_mismatch",
148
+ "ACP structured output is accepted only when starting a turn."
149
+ );
136
150
  if (input.sessionInteractions?.mapping === "grok-folder-trust-v1" && resume) throw new AgentProviderContractError(
137
151
  input.providerKey,
138
152
  "session_interactions_unsupported",
package/dist/limits.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface AcpProviderLimits {
7
7
  readonly authenticationTimeoutMs: number;
8
8
  readonly cancellationTimeoutMs: number;
9
9
  readonly initializationTimeoutMs: number;
10
- readonly promptTimeoutMs: number;
10
+ readonly terminalWaitTimeoutMs: number;
11
11
  }
12
12
  export declare const DEFAULT_ACP_PROVIDER_LIMITS: AcpProviderLimits;
13
13
  export declare function resolveAcpProviderLimits(overrides?: Partial<AcpProviderLimits>): AcpProviderLimits;
package/dist/limits.js CHANGED
@@ -7,7 +7,7 @@ const DEFAULT_ACP_PROVIDER_LIMITS = Object.freeze({
7
7
  authenticationTimeoutMs: 15 * 6e4,
8
8
  cancellationTimeoutMs: 5e3,
9
9
  initializationTimeoutMs: 3e4,
10
- promptTimeoutMs: 30 * 6e4
10
+ terminalWaitTimeoutMs: 30 * 6e4
11
11
  });
12
12
  const BOUNDS = {
13
13
  maxQueuedMessages: [4, 1024],
@@ -18,7 +18,7 @@ const BOUNDS = {
18
18
  authenticationTimeoutMs: [10, 30 * 6e4],
19
19
  cancellationTimeoutMs: [10, 6e4],
20
20
  initializationTimeoutMs: [10, 30 * 6e4],
21
- promptTimeoutMs: [10, 30 * 6e4]
21
+ terminalWaitTimeoutMs: [10, 30 * 6e4]
22
22
  };
23
23
  function resolveAcpProviderLimits(overrides = {}) {
24
24
  if (Object.keys(overrides).some((key) => !Object.hasOwn(BOUNDS, key))) {
@@ -15,6 +15,7 @@ function createNodeAcpProviderDriver(options) {
15
15
  connectionSource: reserve ? { kind: "reserved", reserve } : { kind: "factory", create: (context) => spawnAcpProcess(launch, context) },
16
16
  ...options.unknownExtensionPolicy !== void 0 ? { unknownExtensionPolicy: options.unknownExtensionPolicy } : {},
17
17
  ...options.permissionMapping !== void 0 ? { permissionMapping: options.permissionMapping } : {},
18
+ ...options.structuredOutputMapping !== void 0 ? { structuredOutputMapping: options.structuredOutputMapping } : {},
18
19
  ...options.limits ? { limits: options.limits } : {},
19
20
  ...options.configuration ? { configuration: options.configuration } : {},
20
21
  ...options.authentication ? { authentication: options.authentication } : {},
@@ -2,15 +2,16 @@ import { AcpProviderSessionError } from "./errors.js";
2
2
  import { nativeObject, nativeString } from "./native.js";
3
3
  function acpTurnCompletion(response, cancelReason) {
4
4
  if (response.kind === "error") return {
5
+ structuredOutput: { kind: "not_requested" },
5
6
  outcome: "failed",
6
7
  error: promptFailure(response.error.code)
7
8
  };
8
9
  const reason = nativeString(nativeObject(response.result, "prompt response").stopReason, "prompt stop reason");
9
- if (reason === "cancelled") return { outcome: cancelReason === "timeout" ? "expired" : "canceled" };
10
- if (reason === "end_turn") return { outcome: "completed" };
11
- if (reason === "max_tokens") return { outcome: "completed", reason: "The agent reached its token limit." };
12
- if (reason === "max_turn_requests") return { outcome: "completed", reason: "The agent reached its request limit." };
13
- if (reason === "refusal") return { outcome: "completed", reason: "The agent declined the request." };
10
+ if (reason === "cancelled") return { structuredOutput: { kind: "not_requested" }, outcome: cancelReason === "timeout" ? "expired" : "canceled" };
11
+ if (reason === "end_turn") return { structuredOutput: { kind: "not_requested" }, outcome: "completed" };
12
+ if (reason === "max_tokens") return { structuredOutput: { kind: "not_requested" }, outcome: "completed", reason: "The agent reached its token limit." };
13
+ if (reason === "max_turn_requests") return { structuredOutput: { kind: "not_requested" }, outcome: "completed", reason: "The agent reached its request limit." };
14
+ if (reason === "refusal") return { structuredOutput: { kind: "not_requested" }, outcome: "completed", reason: "The agent declined the request." };
14
15
  throw new AcpProviderSessionError("acp_stop_reason_invalid", "ACP returned an unsupported prompt stop reason.");
15
16
  }
16
17
  function promptFailure(code) {
@@ -0,0 +1,2 @@
1
+ export declare function scheduleTurnDeadline(deadlineAt: string, expire: () => void): () => void;
2
+ //# sourceMappingURL=deadline.d.ts.map
@@ -0,0 +1,18 @@
1
+ const MAX_TIMER_DELAY_MS = 2147483647;
2
+ function scheduleTurnDeadline(deadlineAt, expire) {
3
+ const expiresAt = Date.parse(deadlineAt);
4
+ let timer;
5
+ const check = () => {
6
+ const remaining = expiresAt - Date.now();
7
+ if (remaining <= 0) {
8
+ expire();
9
+ return;
10
+ }
11
+ timer = setTimeout(check, Math.min(remaining, MAX_TIMER_DELAY_MS));
12
+ };
13
+ timer = setTimeout(check, Math.min(Math.max(0, expiresAt - Date.now()), MAX_TIMER_DELAY_MS));
14
+ return () => clearTimeout(timer);
15
+ }
16
+ export {
17
+ scheduleTurnDeadline
18
+ };
@@ -20,7 +20,7 @@ class TurnMapping {
20
20
  planRevision = 0;
21
21
  event(type, payload) {
22
22
  return createAgentEventOutput({
23
- protocolVersion: 12,
23
+ protocolVersion: 13,
24
24
  sessionId: this.context.sessionId,
25
25
  turnId: this.context.turnId,
26
26
  occurredAt: this.context.now(),
@@ -25,6 +25,9 @@ import { prepareAcpSessionElicitation } from "./requests/elicitation.js";
25
25
  import { canceledRequestReply, PendingSessionRequests } from "./requests/pending.js";
26
26
  import { nativeBytes, nativeObject, nativeString, SESSION_METADATA_UPDATES } from "./native.js";
27
27
  import { deferred, SessionQueue } from "./queue.js";
28
+ import { prepareAcpStructuredOutput } from "../structuredOutput/request.js";
29
+ import { acpStructuredOutputCompletion } from "../structuredOutput/completion.js";
30
+ import { scheduleTurnDeadline } from "./deadline.js";
28
31
  class SessionState {
29
32
  constructor(input) {
30
33
  this.input = input;
@@ -286,16 +289,18 @@ class SessionState {
286
289
  throw new AcpProviderSessionError("acp_input_unsupported", "ACP supports only the default interaction mode.");
287
290
  }
288
291
  const prompt = prepareAcpPrompt(input.parts, this.input.capabilities);
289
- const turn = this.createNativeTurn(input.turnId);
290
- return this.execution(turn, () => this.streamTurn(turn, input, prompt));
292
+ const output = prepareAcpStructuredOutput(input.output, this.input.capabilities);
293
+ const turn = this.createNativeTurn(input.turnId, output?.schema);
294
+ return this.execution(turn, () => this.streamTurn(turn, input, prompt, output?.promptMeta));
291
295
  }
292
- createNativeTurn(turnId) {
296
+ createNativeTurn(turnId, outputSchema) {
293
297
  this.requireUsable();
294
298
  if (this.turn && !this.turn.sourceSettled) throw new AcpProviderSessionError("acp_turn_active", "The ACP session already has an active or waiting turn.");
295
299
  const settled = deferred();
296
300
  void settled.promise.catch(() => void 0);
297
301
  const turn = {
298
302
  turnId,
303
+ ...outputSchema ? { outputSchema } : {},
299
304
  mapping: new TurnMapping({
300
305
  sessionId: this.input.context.sessionId,
301
306
  turnId,
@@ -348,7 +353,7 @@ class SessionState {
348
353
  this.queue.admit({ kind: "terminal", payload }, nativeBytes(payload), true);
349
354
  turn.settled.resolve(payload);
350
355
  }
351
- launchTurn(turn, input, prompt) {
356
+ launchTurn(turn, input, prompt, promptMeta) {
352
357
  this.requireUsable();
353
358
  throwIfAgentOperationAborted(input.signal);
354
359
  if (turn.nativeSettled) return;
@@ -356,11 +361,17 @@ class SessionState {
356
361
  turn.nativeStarted = true;
357
362
  turn.prompt = this.operations.prompt({
358
363
  sessionId: this.nativeSessionId,
359
- prompt
364
+ prompt,
365
+ ...promptMeta ? { _meta: promptMeta } : {}
360
366
  }, {
361
- timeoutMs: this.input.limits.promptTimeoutMs,
367
+ lifetime: "until_settled",
362
368
  onResponseReceived: (response) => {
363
- const payload = acpTurnCompletion(response, turn.cancelReason);
369
+ const payload = acpStructuredOutputCompletion(
370
+ acpTurnCompletion(response, turn.cancelReason),
371
+ response,
372
+ turn.outputSchema,
373
+ this.nativeSessionId
374
+ );
364
375
  this.completeNativeTurn(turn, payload);
365
376
  }
366
377
  }).then(() => void 0, (error) => {
@@ -376,8 +387,7 @@ class SessionState {
376
387
  if (input.signal.aborted) abort();
377
388
  }
378
389
  if (input.deadlineAt !== void 0) {
379
- const timer = setTimeout(() => cancel("timeout"), Math.min(2147483647, Math.max(0, Date.parse(input.deadlineAt) - Date.now())));
380
- turn.cleanup.push(() => clearTimeout(timer));
390
+ turn.cleanup.push(scheduleTurnDeadline(input.deadlineAt, () => cancel("timeout")));
381
391
  }
382
392
  }
383
393
  async *emit(output) {
@@ -499,10 +509,10 @@ class SessionState {
499
509
  if (turn.terminal) return;
500
510
  yield* this.emit(turn.mapping.event("turn.state_changed", { state: "waiting_for_request", requestId: canonical.requestId }));
501
511
  }
502
- async *streamTurn(turn, input, prompt) {
512
+ async *streamTurn(turn, input, prompt, promptMeta) {
503
513
  let stable = false;
504
514
  try {
505
- this.launchTurn(turn, input, prompt);
515
+ this.launchTurn(turn, input, prompt, promptMeta);
506
516
  yield* this.emit(turn.mapping.event("turn.started", {}));
507
517
  yield* this.drain(turn);
508
518
  await this.requests.join();
@@ -534,7 +544,7 @@ class SessionState {
534
544
  this.requests.cancelAll();
535
545
  turn.hostInteractions.abort(new DOMException("The ACP turn was canceled.", "AbortError"));
536
546
  if (!turn.nativeStarted) {
537
- const payload = { outcome: reason === "timeout" ? "expired" : "canceled" };
547
+ const payload = { structuredOutput: turn.outputSchema ? { kind: "unavailable", reason: "incomplete" } : { kind: "not_requested" }, outcome: reason === "timeout" ? "expired" : "canceled" };
538
548
  turn.nativeSettled = true;
539
549
  this.queue.admit({ kind: "terminal", payload }, nativeBytes(payload), true);
540
550
  turn.settled.resolve(payload);
@@ -1,5 +1,5 @@
1
1
  import type { AcpJsonObject } from '@agen-ai/acp-transport';
2
- import type { AgentRequestId, AgentTurnCompletedPayload, AgentTurnId } from '@agen-ai/agent-protocol';
2
+ import type { AgentOutputSchema, AgentRequestId, AgentTurnCompletedPayload, AgentTurnId } from '@agen-ai/agent-protocol';
3
3
  import type { TurnMapping } from './mapping.js';
4
4
  import type { deferred } from './queue.js';
5
5
  import type { PendingSessionRequest } from './requests/pending.js';
@@ -19,6 +19,7 @@ export type SessionMessage = Readonly<{
19
19
  }>;
20
20
  export interface NativeTurn {
21
21
  readonly turnId: AgentTurnId;
22
+ readonly outputSchema?: AgentOutputSchema;
22
23
  readonly mapping: TurnMapping;
23
24
  readonly settled: ReturnType<typeof deferred<AgentTurnCompletedPayload>>;
24
25
  readonly cleanup: (() => void)[];
@@ -0,0 +1,4 @@
1
+ import type { AcpResponseReceived } from '@agen-ai/acp-transport';
2
+ import type { AgentOutputSchema, AgentTurnCompletedPayload } from '@agen-ai/agent-protocol';
3
+ export declare function acpStructuredOutputCompletion(completion: AgentTurnCompletedPayload, response: AcpResponseReceived, schema: AgentOutputSchema | undefined, nativeSessionId: string): AgentTurnCompletedPayload;
4
+ //# sourceMappingURL=completion.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { createAgentStructuredOutputCandidate } from "@agen-ai/agent-runtime";
2
+ import { AcpProviderSessionError } from "../session/errors.js";
3
+ import { nativeBytes, nativeObject } from "../session/native.js";
4
+ const GROK_STRUCTURED_METADATA_BYTES_LIMIT = 32768;
5
+ const GROK_STRUCTURED_ERROR_BYTES_LIMIT = 4096;
6
+ function structuredOutputResult(response, schema, nativeSessionId) {
7
+ if (!schema) return { kind: "not_requested" };
8
+ const unavailable = (reason) => createAgentStructuredOutputCandidate({
9
+ schema,
10
+ candidate: { kind: "unavailable", reason }
11
+ });
12
+ if (response.kind === "error") return unavailable("provider_error");
13
+ const result = nativeObject(response.result, "structured prompt response");
14
+ const meta = result._meta === void 0 ? void 0 : nativeObject(result._meta, "structured response metadata");
15
+ if (meta && nativeBytes(meta) > GROK_STRUCTURED_METADATA_BYTES_LIMIT) return unavailable("output_limit_exceeded");
16
+ if (meta?.sessionId !== void 0 && meta.sessionId !== nativeSessionId) {
17
+ throw new AcpProviderSessionError(
18
+ "acp_structured_output_identity_mismatch",
19
+ "Structured output metadata identifies a different native session."
20
+ );
21
+ }
22
+ const hasOutput = meta !== void 0 && Object.hasOwn(meta, "structuredOutput");
23
+ const hasError = meta !== void 0 && Object.hasOwn(meta, "structuredOutputError");
24
+ if (hasOutput && hasError) {
25
+ throw new AcpProviderSessionError(
26
+ "acp_structured_output_ambiguous",
27
+ "Structured output response contains both a value and an error."
28
+ );
29
+ }
30
+ if (hasError && (typeof meta?.structuredOutputError !== "string" || !meta.structuredOutputError.trim() || Buffer.byteLength(meta.structuredOutputError, "utf8") > GROK_STRUCTURED_ERROR_BYTES_LIMIT)) {
31
+ throw new AcpProviderSessionError(
32
+ "acp_structured_output_error_invalid",
33
+ "Structured output error must be a bounded nonempty native error string."
34
+ );
35
+ }
36
+ if (result.stopReason === "refusal") return unavailable("refused");
37
+ if (result.stopReason !== "end_turn") return unavailable("incomplete");
38
+ if (hasError) return unavailable("provider_error");
39
+ if (!hasOutput) return unavailable("missing_output");
40
+ return createAgentStructuredOutputCandidate({ schema, candidate: { kind: "json_value", value: meta?.structuredOutput } });
41
+ }
42
+ function acpStructuredOutputCompletion(completion, response, schema, nativeSessionId) {
43
+ const structuredOutput = structuredOutputResult(response, schema, nativeSessionId);
44
+ if (completion.outcome === "completed") return { ...completion, structuredOutput };
45
+ if (structuredOutput.kind === "json") {
46
+ throw new AcpProviderSessionError(
47
+ "acp_structured_output_terminal_invalid",
48
+ "A structured output value requires successful native completion."
49
+ );
50
+ }
51
+ return { ...completion, structuredOutput };
52
+ }
53
+ export {
54
+ acpStructuredOutputCompletion
55
+ };
@@ -0,0 +1,10 @@
1
+ import { type AgentStructuredOutputCapability } from '@agen-ai/agent-protocol';
2
+ import type { AcpJsonObject } from '@agen-ai/acp-transport';
3
+ /** Explicit native contract; generic ACP metadata support does not imply this feature. */
4
+ export interface AcpStructuredOutputMappingSelection {
5
+ readonly mapping: 'grok-build-1.0.62-structured-output-v1';
6
+ }
7
+ export declare function resolveAcpStructuredOutputMapping(input: unknown): AcpStructuredOutputMappingSelection | undefined;
8
+ export declare function assertAcpStructuredOutputIdentity(initialized: AcpJsonObject, selected: AcpStructuredOutputMappingSelection | undefined): void;
9
+ export declare function acpStructuredOutputCapability(selected: AcpStructuredOutputMappingSelection | undefined): AgentStructuredOutputCapability;
10
+ //# sourceMappingURL=mapping.d.ts.map
@@ -0,0 +1,45 @@
1
+ import {
2
+ AGENT_OUTPUT_SCHEMA_BYTES_LIMIT,
3
+ AGENT_OUTPUT_SCHEMA_PROFILE,
4
+ AGENT_OUTPUT_VALUE_BYTES_LIMIT
5
+ } from "@agen-ai/agent-protocol";
6
+ import { AcpProviderConnectionError } from "../connection/errors.js";
7
+ function resolveAcpStructuredOutputMapping(input) {
8
+ if (input === void 0) return void 0;
9
+ if (input === null || typeof input !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(input))) {
10
+ throw new TypeError("ACP structured output mapping must be an explicit selection.");
11
+ }
12
+ const keys = Reflect.ownKeys(input);
13
+ const descriptor = Object.getOwnPropertyDescriptor(input, "mapping");
14
+ if (keys.length !== 1 || keys[0] !== "mapping" || !descriptor || !("value" in descriptor) || !descriptor.enumerable || descriptor.value !== "grok-build-1.0.62-structured-output-v1") {
15
+ throw new TypeError("ACP structured output mapping is unsupported.");
16
+ }
17
+ return Object.freeze({ mapping: "grok-build-1.0.62-structured-output-v1" });
18
+ }
19
+ function assertAcpStructuredOutputIdentity(initialized, selected) {
20
+ if (!selected) return;
21
+ const meta = initialized._meta;
22
+ if (meta === null || typeof meta !== "object" || Array.isArray(meta) || meta.grokShell !== true || meta.agentVersion !== "1.0.62") {
23
+ throw new AcpProviderConnectionError(
24
+ "acp_structured_output_profile_mismatch",
25
+ "The selected structured output mapping requires exact Grok Build agentVersion 1.0.62."
26
+ );
27
+ }
28
+ }
29
+ function acpStructuredOutputCapability(selected) {
30
+ return selected ? {
31
+ kind: "supported",
32
+ mechanism: "native",
33
+ schemaProfile: AGENT_OUTPUT_SCHEMA_PROFILE,
34
+ scope: "turn",
35
+ maxSchemaBytes: AGENT_OUTPUT_SCHEMA_BYTES_LIMIT,
36
+ maxOutputBytes: AGENT_OUTPUT_VALUE_BYTES_LIMIT,
37
+ resume: false,
38
+ branch: false
39
+ } : { kind: "unsupported" };
40
+ }
41
+ export {
42
+ acpStructuredOutputCapability,
43
+ assertAcpStructuredOutputIdentity,
44
+ resolveAcpStructuredOutputMapping
45
+ };
@@ -0,0 +1,7 @@
1
+ import { type AcpJsonObject } from '@agen-ai/acp-transport';
2
+ import { type AgentCapabilities, type AgentOutputSchema, type AgentStructuredOutputRequest } from '@agen-ai/agent-protocol';
3
+ export declare function prepareAcpStructuredOutput(request: AgentStructuredOutputRequest | undefined, capabilities: AgentCapabilities): Readonly<{
4
+ schema: AgentOutputSchema;
5
+ promptMeta: AcpJsonObject;
6
+ }> | undefined;
7
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { assertBoundedAcpJson, DEFAULT_ACP_PROTOCOL_LIMITS } from "@agen-ai/acp-transport";
2
+ import { parseAgentOutputSchema } from "@agen-ai/agent-protocol";
3
+ import { AgentProviderContractError } from "@agen-ai/agent-runtime";
4
+ function prepareAcpStructuredOutput(request, capabilities) {
5
+ if (request === void 0) return void 0;
6
+ if (capabilities.turns.structuredOutput.kind === "unsupported") {
7
+ throw new AgentProviderContractError(
8
+ capabilities.providerKey,
9
+ "structured_output_unsupported",
10
+ "The ACP provider has no selected native structured output mapping."
11
+ );
12
+ }
13
+ const schema = parseAgentOutputSchema(request.schema);
14
+ assertBoundedAcpJson(schema, DEFAULT_ACP_PROTOCOL_LIMITS);
15
+ return { schema, promptMeta: { outputSchema: schema } };
16
+ }
17
+ export {
18
+ prepareAcpStructuredOutput
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agen-ai/provider-acp",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "private": false,
5
5
  "description": "Optional Agent Client Protocol provider for the AgenAI coding-agent runtime.",
6
6
  "type": "module",
@@ -27,9 +27,9 @@
27
27
  "./package.json": "./package.json"
28
28
  },
29
29
  "dependencies": {
30
- "@agen-ai/acp-transport": "^0.4.3",
31
- "@agen-ai/agent-protocol": "^0.4.3",
32
- "@agen-ai/agent-runtime": "^0.4.3"
30
+ "@agen-ai/acp-transport": "^0.4.4",
31
+ "@agen-ai/agent-runtime": "^0.4.4",
32
+ "@agen-ai/agent-protocol": "^0.4.4"
33
33
  },
34
34
  "devDependencies": {
35
35
  "tsup": "^8.5.0"