@agen-ai/agent-runtime 0.4.2 → 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.
Files changed (58) hide show
  1. package/README.md +316 -10
  2. package/dist/adapterValidation.js +38 -4
  3. package/dist/contractErrors.d.ts +2 -2
  4. package/dist/contractErrors.js +8 -1
  5. package/dist/executionInput/index.d.ts +3 -0
  6. package/dist/executionInput/index.js +4 -0
  7. package/dist/executionInput/lifetime.d.ts +47 -0
  8. package/dist/executionInput/lifetime.js +241 -0
  9. package/dist/executionInput/types.d.ts +61 -0
  10. package/dist/executionInput/types.js +0 -0
  11. package/dist/executionInput/validation.d.ts +25 -0
  12. package/dist/executionInput/validation.js +178 -0
  13. package/dist/generation/cleanup.d.ts +15 -0
  14. package/dist/generation/cleanup.js +108 -0
  15. package/dist/generation/deadline.d.ts +16 -0
  16. package/dist/generation/deadline.js +81 -0
  17. package/dist/generation/decoding.d.ts +4 -0
  18. package/dist/generation/decoding.js +59 -0
  19. package/dist/generation/errors.d.ts +29 -0
  20. package/dist/generation/errors.js +39 -0
  21. package/dist/generation/generateObject.d.ts +3 -0
  22. package/dist/generation/generateObject.js +148 -0
  23. package/dist/generation/index.d.ts +5 -0
  24. package/dist/generation/index.js +8 -0
  25. package/dist/generation/preflight.d.ts +25 -0
  26. package/dist/generation/preflight.js +48 -0
  27. package/dist/generation/requests.d.ts +17 -0
  28. package/dist/generation/requests.js +117 -0
  29. package/dist/generation/types.d.ts +22 -0
  30. package/dist/generation/types.js +0 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.js +7 -1
  33. package/dist/nativeContinuations/admission.js +1 -0
  34. package/dist/outputValidation.d.ts +2 -0
  35. package/dist/outputValidation.js +29 -1
  36. package/dist/sessionAdmission.d.ts +2 -0
  37. package/dist/sessionAdmission.js +13 -0
  38. package/dist/sessionInteractions/lifetime.d.ts +1 -0
  39. package/dist/sessionInteractions/lifetime.js +4 -0
  40. package/dist/sessionInteractions/request.d.ts +1 -0
  41. package/dist/sessionInteractions/request.js +3 -0
  42. package/dist/sessionValidation.d.ts +15 -3
  43. package/dist/sessionValidation.js +215 -127
  44. package/dist/sessions.d.ts +7 -1
  45. package/dist/structuredOutput/admission.d.ts +16 -0
  46. package/dist/structuredOutput/admission.js +89 -0
  47. package/dist/structuredOutput/candidate.d.ts +22 -0
  48. package/dist/structuredOutput/candidate.js +64 -0
  49. package/dist/structuredOutput/validation.d.ts +8 -0
  50. package/dist/structuredOutput/validation.js +26 -0
  51. package/dist/testing/conformance.d.ts +20 -1
  52. package/dist/testing/conformance.js +103 -3
  53. package/dist/testing/fakeProvider.js +5 -4
  54. package/dist/turnExecution/execution.d.ts +9 -1
  55. package/dist/turnExecution/execution.js +88 -7
  56. package/dist/turnExecution/terminalBuffer.d.ts +9 -0
  57. package/dist/turnExecution/terminalBuffer.js +38 -0
  58. package/package.json +6 -2
@@ -1,4 +1,4 @@
1
- import type { AgentAuthenticationFlow, AgentCollaborationControlInput, AgentCollaborationNode, AgentCollaborationSpawnInput, AgentConfigurationCatalog, AgentConfigurationSelectionInput, AgentError, AgentGeneratedResourceDescriptor, AgentGeneratedResourceId, AgentManagedContentCatalog, AgentOperationCatalog, AgentOperationInvocation, AgentOperationResult, AgentRequestResolution, AgentSessionBinding, AgentSessionBranchSource, AgentSessionConfiguration, AgentSessionId, AgentTurnId, AgentTurnInputContent, AgentTurnInteractionMode, AgentTurnInputPart, AgentTurnInterruptionReason } from "@agen-ai/agent-protocol";
1
+ import type { AgentAuthenticationFlow, AgentCollaborationControlInput, AgentCollaborationNode, AgentCollaborationSpawnInput, AgentConfigurationCatalog, AgentConfigurationSelectionInput, AgentError, AgentGeneratedResourceDescriptor, AgentGeneratedResourceId, AgentManagedContentCatalog, AgentOperationCatalog, AgentOperationInvocation, AgentOperationResult, AgentRequestResolution, AgentSessionBinding, AgentSessionBranchSource, AgentSessionConfiguration, AgentSessionId, AgentStructuredOutputRequest, AgentTurnId, AgentTurnInputContent, AgentTurnInteractionMode, AgentTurnInputPart, AgentTurnInterruptionReason } from "@agen-ai/agent-protocol";
2
2
  import type { AgentNativeContinuationContext, AgentNativeContinuationSupport } from "./nativeContinuations/types.js";
3
3
  import type { MaybePromise } from "./foundation.js";
4
4
  import type { AgentSessionHostBinding, AgentSessionHostBindingSupport } from "./hostBindings/types.js";
@@ -6,6 +6,7 @@ import type { AgentProviderOutput } from "./outputs.js";
6
6
  import type { AgentArtifactCandidate } from "./artifacts.js";
7
7
  import type { AgentProviderSessionInteractionContext, AgentSessionInteractionContext, AgentSessionInteractionSupport } from "./sessionInteractions/types.js";
8
8
  import type { AgentEnvironmentObservationPort } from "./environment/types.js";
9
+ import type { AgentExecutionInput, AgentExecutionInputSupport } from "./executionInput/types.js";
9
10
  export interface AgentProviderSessionContext {
10
11
  readonly sessionId: AgentSessionId;
11
12
  readonly workingDirectory: string;
@@ -17,6 +18,7 @@ export interface AgentProviderSessionContext {
17
18
  export type AgentSessionBindingCreatedObserver = (binding: AgentSessionBinding) => void;
18
19
  export interface AgentProviderCreateSessionInput extends AgentProviderSessionContext {
19
20
  readonly onBindingCreated: AgentSessionBindingCreatedObserver;
21
+ readonly output?: AgentStructuredOutputRequest;
20
22
  }
21
23
  export interface AgentProviderResumeSessionInput extends AgentProviderSessionContext {
22
24
  readonly binding: AgentSessionBinding;
@@ -33,6 +35,7 @@ export interface AgentProviderStartTurnInput {
33
35
  readonly parts: readonly AgentTurnInputPart[];
34
36
  readonly summary?: string;
35
37
  readonly deadlineAt?: string;
38
+ readonly output?: AgentStructuredOutputRequest;
36
39
  readonly signal?: AbortSignal;
37
40
  readonly onProviderExecutionStarted?: AgentProviderExecutionStartedObserver;
38
41
  }
@@ -55,6 +58,7 @@ export type AgentRequestResolutionResult = Readonly<{
55
58
  export interface AgentProviderTurnExecution {
56
59
  readonly turnId: AgentTurnId;
57
60
  readonly outputs: AsyncIterable<AgentProviderOutput>;
61
+ readonly input?: AgentExecutionInput;
58
62
  readonly resolveRequest: (input: AgentProviderResolveRequestInput) => Promise<AgentRequestResolutionResult>;
59
63
  }
60
64
  export interface AgentProviderInterruptTurnInput {
@@ -215,6 +219,7 @@ export type AgentProviderAuthentication = Readonly<{
215
219
  cancel: (input: AgentProviderAuthenticationCancelInput) => MaybePromise<AgentProviderOperationResult>;
216
220
  }>;
217
221
  export interface AgentProviderAdapter {
222
+ readonly executionInput?: AgentExecutionInputSupport;
218
223
  readonly sessionInteractions?: AgentSessionInteractionSupport;
219
224
  readonly hostBinding?: AgentSessionHostBindingSupport;
220
225
  readonly nativeContinuations?: AgentNativeContinuationSupport;
@@ -229,6 +234,7 @@ export type AgentSessionOpenContext = Omit<AgentProviderSessionContext, "interac
229
234
  }>;
230
235
  export type AgentCreateSessionInput = AgentSessionOpenContext & Readonly<{
231
236
  onBindingCreated: AgentSessionBindingCreatedObserver;
237
+ output?: AgentStructuredOutputRequest;
232
238
  }>;
233
239
  export type AgentResumeSessionInput = AgentSessionOpenContext & Readonly<{
234
240
  binding: AgentSessionBinding;
@@ -0,0 +1,16 @@
1
+ import { type AgentCapabilities, type AgentStructuredOutputRequest } from "@agen-ai/agent-protocol";
2
+ export interface AgentStructuredOutputBinding {
3
+ readonly request: AgentStructuredOutputRequest;
4
+ readonly maxOutputBytes: number;
5
+ }
6
+ export declare function admitAgentStructuredOutputOpening(input: Readonly<{
7
+ capabilities: AgentCapabilities;
8
+ operation: "create" | "resume" | "branch";
9
+ output: unknown;
10
+ }>): AgentStructuredOutputBinding | undefined;
11
+ export declare function admitAgentStructuredOutputTurn(input: Readonly<{
12
+ capabilities: AgentCapabilities;
13
+ binding?: AgentStructuredOutputBinding;
14
+ output: unknown;
15
+ }>): AgentStructuredOutputBinding | undefined;
16
+ //# sourceMappingURL=admission.d.ts.map
@@ -0,0 +1,89 @@
1
+ import {
2
+ AgentProtocolValidationError,
3
+ agentProtocolSerializedJsonBytes,
4
+ parseAgentOutputSchema
5
+ } from "@agen-ai/agent-protocol";
6
+ import { throwAgentProviderContractError } from "../contractErrors.js";
7
+ import { hasExactOwnKeys } from "../internal/objectShape.js";
8
+ function parseOutputRequest(input, maxOutputBytes) {
9
+ const invalid = () => {
10
+ throw new AgentProtocolValidationError([{
11
+ code: "invalid_structured_output_request",
12
+ message: "Structured output must be a plain json_schema request with only kind and schema data properties.",
13
+ path: ["output"]
14
+ }]);
15
+ };
16
+ if (input === null || typeof input !== "object") return invalid();
17
+ let kind;
18
+ let schema;
19
+ try {
20
+ const prototype = Object.getPrototypeOf(input);
21
+ if (prototype !== Object.prototype && prototype !== null || !hasExactOwnKeys(input, ["kind", "schema"])) return invalid();
22
+ const kindDescriptor = Object.getOwnPropertyDescriptor(input, "kind");
23
+ const schemaDescriptor = Object.getOwnPropertyDescriptor(input, "schema");
24
+ if (!kindDescriptor || !("value" in kindDescriptor) || !kindDescriptor.enumerable || !schemaDescriptor || !("value" in schemaDescriptor) || !schemaDescriptor.enumerable) return invalid();
25
+ kind = kindDescriptor.value;
26
+ schema = schemaDescriptor.value;
27
+ } catch (error) {
28
+ if (error instanceof AgentProtocolValidationError) throw error;
29
+ return invalid();
30
+ }
31
+ if (kind !== "json_schema") return invalid();
32
+ return Object.freeze({ kind, schema: parseAgentOutputSchema(schema, { maxOutputBytes }) });
33
+ }
34
+ function admitRequest(capabilities, output) {
35
+ const capability = capabilities.turns.structuredOutput;
36
+ if (capability.kind === "unsupported") {
37
+ throwAgentProviderContractError(
38
+ capabilities.providerKey,
39
+ "structured_output_unsupported",
40
+ "Provider does not support structured output."
41
+ );
42
+ }
43
+ const request = parseOutputRequest(output, capability.maxOutputBytes);
44
+ if (agentProtocolSerializedJsonBytes(request.schema) > capability.maxSchemaBytes) {
45
+ throwAgentProviderContractError(
46
+ capabilities.providerKey,
47
+ "invalid_structured_output",
48
+ "Structured output schema exceeds the provider's advertised byte limit."
49
+ );
50
+ }
51
+ return Object.freeze({ request, maxOutputBytes: capability.maxOutputBytes });
52
+ }
53
+ function admitAgentStructuredOutputOpening(input) {
54
+ if (input.output === void 0) return void 0;
55
+ const { capabilities } = input;
56
+ if (input.operation !== "create") {
57
+ throwAgentProviderContractError(
58
+ capabilities.providerKey,
59
+ "structured_output_unsupported",
60
+ "Structured output cannot be restored through resume or branch."
61
+ );
62
+ }
63
+ const capability = capabilities.turns.structuredOutput;
64
+ if (capability.kind === "supported" && capability.scope !== "session") {
65
+ throwAgentProviderContractError(
66
+ capabilities.providerKey,
67
+ "structured_output_scope_mismatch",
68
+ "Provider accepts structured output only when starting a turn."
69
+ );
70
+ }
71
+ return admitRequest(capabilities, input.output);
72
+ }
73
+ function admitAgentStructuredOutputTurn(input) {
74
+ if (input.output === void 0) return input.binding;
75
+ const { capabilities } = input;
76
+ const capability = capabilities.turns.structuredOutput;
77
+ if (input.binding || capability.kind === "supported" && capability.scope !== "turn") {
78
+ throwAgentProviderContractError(
79
+ capabilities.providerKey,
80
+ "structured_output_scope_mismatch",
81
+ "Session-scoped structured output cannot be overridden or supplied on a turn."
82
+ );
83
+ }
84
+ return admitRequest(capabilities, input.output);
85
+ }
86
+ export {
87
+ admitAgentStructuredOutputOpening,
88
+ admitAgentStructuredOutputTurn
89
+ };
@@ -0,0 +1,22 @@
1
+ import { type AgentOutputSchema, type AgentStructuredOutputResult, type AgentStructuredOutputUnavailableReason } from "@agen-ai/agent-protocol";
2
+ export type AgentStructuredOutputCandidate = Readonly<{
3
+ kind: "json_text";
4
+ text: string;
5
+ }> | Readonly<{
6
+ kind: "json_value";
7
+ value: unknown;
8
+ }> | Readonly<{
9
+ kind: "unavailable";
10
+ reason: AgentStructuredOutputUnavailableReason;
11
+ }>;
12
+ export interface CreateAgentStructuredOutputCandidateInput {
13
+ readonly schema: AgentOutputSchema;
14
+ readonly maxOutputBytes?: number;
15
+ readonly candidate: AgentStructuredOutputCandidate;
16
+ }
17
+ export type AgentStructuredOutputCandidateResult = Exclude<AgentStructuredOutputResult, Readonly<{
18
+ kind: "not_requested";
19
+ }>>;
20
+ export declare function validateAgentStructuredOutputCandidate(schema: AgentOutputSchema, value: unknown, maxOutputBytes: number): AgentStructuredOutputCandidateResult;
21
+ export declare function createAgentStructuredOutputCandidate(input: CreateAgentStructuredOutputCandidateInput): AgentStructuredOutputCandidateResult;
22
+ //# sourceMappingURL=candidate.d.ts.map
@@ -0,0 +1,64 @@
1
+ import {
2
+ AGENT_OUTPUT_RAW_JSON_BYTES_LIMIT,
3
+ AGENT_OUTPUT_VALUE_BYTES_LIMIT,
4
+ AGENT_STRUCTURED_OUTPUT_UNAVAILABLE_REASONS,
5
+ parseAgentOutputSchema,
6
+ validateAgentOutputValue
7
+ } from "@agen-ai/agent-protocol";
8
+ function freezeJson(value) {
9
+ if (value === null || typeof value !== "object") return;
10
+ for (const child of Object.values(value)) freezeJson(child);
11
+ Object.freeze(value);
12
+ }
13
+ function validateAgentStructuredOutputCandidate(schema, value, maxOutputBytes) {
14
+ const parsed = validateAgentOutputValue(schema, value, { maxOutputBytes });
15
+ if (!parsed.success) {
16
+ if (parsed.issues.some((issue) => issue.code === "output_limit_exceeded")) {
17
+ return Object.freeze({ kind: "unavailable", reason: "output_limit_exceeded" });
18
+ }
19
+ if (parsed.issues.some((issue) => issue.code === "invalid_json_data")) {
20
+ return Object.freeze({ kind: "unavailable", reason: "invalid_json" });
21
+ }
22
+ return Object.freeze({ kind: "unavailable", reason: "schema_mismatch", issues: Object.freeze(parsed.issues.map((issue) => Object.freeze({
23
+ ...issue,
24
+ path: Object.freeze([...issue.path])
25
+ }))) });
26
+ }
27
+ freezeJson(parsed.data);
28
+ return Object.freeze({ kind: "json", value: parsed.data });
29
+ }
30
+ function createAgentStructuredOutputCandidate(input) {
31
+ const schema = parseAgentOutputSchema(input.schema);
32
+ const maxOutputBytes = input.maxOutputBytes ?? AGENT_OUTPUT_VALUE_BYTES_LIMIT;
33
+ if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes <= 0 || maxOutputBytes > AGENT_OUTPUT_VALUE_BYTES_LIMIT) {
34
+ throw new RangeError("Structured output byte limit must be a positive integer within the portable profile ceiling.");
35
+ }
36
+ const { candidate } = input;
37
+ switch (candidate.kind) {
38
+ case "unavailable":
39
+ if (!AGENT_STRUCTURED_OUTPUT_UNAVAILABLE_REASONS.includes(candidate.reason)) {
40
+ throw new TypeError("Structured output candidate has an unsupported unavailable reason.");
41
+ }
42
+ return Object.freeze({ kind: "unavailable", reason: candidate.reason });
43
+ case "json_text": {
44
+ if (typeof candidate.text !== "string") throw new TypeError("Structured JSON text candidate must contain a string.");
45
+ if (candidate.text.length > AGENT_OUTPUT_RAW_JSON_BYTES_LIMIT || Buffer.byteLength(candidate.text, "utf8") > AGENT_OUTPUT_RAW_JSON_BYTES_LIMIT) {
46
+ return Object.freeze({ kind: "unavailable", reason: "output_limit_exceeded" });
47
+ }
48
+ let value;
49
+ try {
50
+ value = JSON.parse(candidate.text);
51
+ } catch {
52
+ return Object.freeze({ kind: "unavailable", reason: "invalid_json" });
53
+ }
54
+ return validateAgentStructuredOutputCandidate(schema, value, maxOutputBytes);
55
+ }
56
+ case "json_value":
57
+ return validateAgentStructuredOutputCandidate(schema, candidate.value, maxOutputBytes);
58
+ }
59
+ throw new TypeError("Structured output candidate kind is unsupported.");
60
+ }
61
+ export {
62
+ createAgentStructuredOutputCandidate,
63
+ validateAgentStructuredOutputCandidate
64
+ };
@@ -0,0 +1,8 @@
1
+ import type { AgentProviderKey, AgentStructuredOutputResult } from "@agen-ai/agent-protocol";
2
+ import type { AgentStructuredOutputBinding } from "./admission.js";
3
+ export declare function validateAgentStructuredOutputResult(input: Readonly<{
4
+ providerKey: AgentProviderKey;
5
+ binding?: AgentStructuredOutputBinding;
6
+ result: AgentStructuredOutputResult;
7
+ }>): AgentStructuredOutputResult;
8
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { throwAgentProviderContractError } from "../contractErrors.js";
2
+ import { validateAgentStructuredOutputCandidate } from "./candidate.js";
3
+ function validateAgentStructuredOutputResult(input) {
4
+ const { binding, result } = input;
5
+ if (!binding) {
6
+ if (result.kind !== "not_requested") {
7
+ throwAgentProviderContractError(
8
+ input.providerKey,
9
+ "invalid_structured_output",
10
+ "Provider reported structured output for a turn without an admitted schema."
11
+ );
12
+ }
13
+ return result;
14
+ }
15
+ if (result.kind === "not_requested") {
16
+ throwAgentProviderContractError(
17
+ input.providerKey,
18
+ "invalid_structured_output",
19
+ "Provider omitted the admitted structured-output contract."
20
+ );
21
+ }
22
+ return result.kind === "json" ? validateAgentStructuredOutputCandidate(binding.request.schema, result.value, binding.maxOutputBytes) : result;
23
+ }
24
+ export {
25
+ validateAgentStructuredOutputResult
26
+ };
@@ -1,4 +1,4 @@
1
- import { type AgentCollaborationSpawnInput, type AgentConfigurationSelectionInput, type AgentEnvironmentId, type AgentGeneratedResourceId, type AgentOperationInvocation, type AgentRequest, type AgentRequestResolution, type AgentSessionBinding, type AgentSessionBranchSource, type AgentSessionConfiguration, type AgentSessionId, type AgentTurnId, type AgentTurnInputContent } from "@agen-ai/agent-protocol";
1
+ import { type AgentCollaborationSpawnInput, type AgentJsonObject, type AgentOutputSchema, type AgentConfigurationSelectionInput, type AgentEnvironmentId, type AgentGeneratedResourceId, type AgentOperationInvocation, type AgentRequest, type AgentRequestResolution, type AgentSessionBinding, type AgentSessionBranchSource, type AgentSessionConfiguration, type AgentSessionId, type AgentTurnId, type AgentTurnInputContent } from "@agen-ai/agent-protocol";
2
2
  import type { AgentProviderDriver, AgentProviderInstanceDefinition } from "../providerDriver.js";
3
3
  import type { AgentProviderStartTurnInput } from "../sessions.js";
4
4
  export interface AgentProviderConformanceScenario {
@@ -23,6 +23,25 @@ export interface AgentProviderConformanceScenario {
23
23
  }>) => AgentRequestResolution | undefined | Promise<AgentRequestResolution | undefined>;
24
24
  readonly branchSource?: (binding: AgentSessionBinding, turnId: AgentTurnId) => AgentSessionBranchSource;
25
25
  readonly steeringInput?: AgentTurnInputContent;
26
+ readonly structuredOutput?: AgentStructuredOutputConformanceScenario;
27
+ }
28
+ export interface AgentStructuredOutputConformanceScenario {
29
+ readonly sessionId: AgentSessionId;
30
+ readonly plainSessionId: AgentSessionId;
31
+ readonly schema: AgentOutputSchema;
32
+ readonly alternateSchema: AgentOutputSchema;
33
+ /** Turn scope uses schema then alternateSchema; session scope uses schema for both turns. */
34
+ readonly turns: readonly [
35
+ Readonly<{
36
+ turn: Omit<AgentProviderStartTurnInput, "output">;
37
+ expectedValue: AgentJsonObject;
38
+ }>,
39
+ Readonly<{
40
+ turn: Omit<AgentProviderStartTurnInput, "output">;
41
+ expectedValue: AgentJsonObject;
42
+ }>
43
+ ];
44
+ readonly plainTurn: Omit<AgentProviderStartTurnInput, "output">;
26
45
  }
27
46
  export interface AgentProviderConformanceReport {
28
47
  readonly providerKey: string;
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from "node:util";
1
2
  import {
2
3
  matchesAgentSessionBinding,
3
4
  parseAgentEnvironmentId,
@@ -5,6 +6,7 @@ import {
5
6
  parseAgentTurnId
6
7
  } from "@agen-ai/agent-protocol";
7
8
  import { isAgentOperationAbortError } from "../foundation.js";
9
+ import { AgentProviderContractError } from "../contractErrors.js";
8
10
  import {
9
11
  createAgentProviderRegistry,
10
12
  AgentProviderRegistryError
@@ -61,7 +63,7 @@ async function advanceToStartedTurn(session, input) {
61
63
  throw error;
62
64
  }
63
65
  }
64
- async function collectRemainingOutputs(started, resolutionFor, beforeClose) {
66
+ async function collectRemainingOutputs(started, resolutionFor, beforeClose, closeOnSuccess = true) {
65
67
  const requests = /* @__PURE__ */ new Map();
66
68
  const tasks = [];
67
69
  let acceptingRequests = true;
@@ -119,7 +121,6 @@ async function collectRemainingOutputs(started, resolutionFor, beforeClose) {
119
121
  requests.delete(event.payload.requestId);
120
122
  } else if (event.type === "turn.completed") {
121
123
  abortRequests();
122
- return;
123
124
  }
124
125
  }
125
126
  })();
@@ -134,7 +135,7 @@ async function collectRemainingOutputs(started, resolutionFor, beforeClose) {
134
135
  }
135
136
  acceptingRequests = false;
136
137
  abortRequests();
137
- const closing = started.session.close({ reason: failures.size > 0 ? "contract_rejected" : "idle" });
138
+ const closing = failures.size > 0 || closeOnSuccess ? started.session.close({ reason: failures.size > 0 ? "contract_rejected" : "idle" }) : Promise.resolve();
138
139
  const [closed, read, inspected, returned] = await Promise.allSettled([closing, reader, checked, started.iterator.return?.()]);
139
140
  if (closed?.status === "rejected") failures.add(closed.reason);
140
141
  for (const result of [read, inspected]) {
@@ -163,6 +164,99 @@ function hasStableTurnBoundary(events) {
163
164
  );
164
165
  return boundary?.type === "turn.completed" || boundary?.type === "turn.state_changed" && boundary.payload.state === "waiting_for_request";
165
166
  }
167
+ async function requireStructuredRejection(operation, code) {
168
+ let rejected = false;
169
+ try {
170
+ await operation();
171
+ } catch (error) {
172
+ requireCheck(
173
+ error instanceof AgentProviderContractError && error.code === code,
174
+ "structured_output",
175
+ "Structured output rejection did not preserve its admission classification."
176
+ );
177
+ rejected = true;
178
+ }
179
+ requireCheck(rejected, "structured_output", "Unsupported structured output was admitted.");
180
+ }
181
+ async function checkStructuredOutput(input) {
182
+ const { adapter, capability, scenario } = input;
183
+ const schema = scenario.structuredOutput?.schema ?? {
184
+ type: "object",
185
+ properties: { title: { type: "string" } },
186
+ required: ["title"],
187
+ additionalProperties: false
188
+ };
189
+ const request = { kind: "json_schema", schema };
190
+ if (capability.kind === "unsupported") {
191
+ await requireStructuredRejection(async () => {
192
+ const unexpected = await adapter.createSession({
193
+ sessionId: scenario.abortedSessionId,
194
+ workingDirectory: scenario.workingDirectory,
195
+ configuration: scenario.configuration,
196
+ onBindingCreated() {
197
+ },
198
+ output: request
199
+ });
200
+ await unexpected.close({ reason: "contract_rejected" });
201
+ }, "structured_output_unsupported");
202
+ await requireStructuredRejection(() => input.plainSession.startTurn({ ...scenario.turn, output: request }), "structured_output_unsupported");
203
+ return ["structured_output_unsupported"];
204
+ }
205
+ const fixture = scenario.structuredOutput;
206
+ requireCheck(fixture !== void 0, "structured_output", "Supported structured output requires two schema/result fixtures and an ordinary turn.");
207
+ let bound;
208
+ let plain;
209
+ let failure;
210
+ const open = (sessionId, bind) => adapter.createSession({
211
+ sessionId,
212
+ workingDirectory: scenario.workingDirectory,
213
+ configuration: scenario.configuration,
214
+ onBindingCreated() {
215
+ },
216
+ ...bind ? { output: request } : {}
217
+ });
218
+ try {
219
+ bound = await open(fixture.sessionId, capability.scope === "session");
220
+ for (const [index, turnFixture] of fixture.turns.entries()) {
221
+ const selected = { kind: "json_schema", schema: index === 0 ? schema : fixture.alternateSchema };
222
+ if (capability.scope === "session") {
223
+ await requireStructuredRejection(() => bound.startTurn({ ...turnFixture.turn, output: selected }), "structured_output_scope_mismatch");
224
+ }
225
+ const started2 = await advanceToStartedTurn(bound, { ...turnFixture.turn, ...capability.scope === "turn" ? { output: selected } : {} });
226
+ const observed2 = await collectRemainingOutputs(started2, scenario.resolutionFor, void 0, false);
227
+ const terminal2 = semanticEvents(observed2.outputs).at(-1);
228
+ requireCheck(
229
+ terminal2?.type === "turn.completed" && terminal2.payload.outcome === "completed" && terminal2.payload.structuredOutput.kind === "json" && isDeepStrictEqual(terminal2.payload.structuredOutput.value, turnFixture.expectedValue),
230
+ "structured_output",
231
+ "Structured turn did not finish with the expected schema-valid native value."
232
+ );
233
+ }
234
+ if (capability.scope === "session") {
235
+ await bound.close({ reason: "idle" });
236
+ plain = await open(fixture.plainSessionId, false);
237
+ } else plain = bound;
238
+ const started = await advanceToStartedTurn(plain, fixture.plainTurn);
239
+ const observed = await collectRemainingOutputs(started, scenario.resolutionFor, void 0, false);
240
+ const terminal = semanticEvents(observed.outputs).at(-1);
241
+ requireCheck(
242
+ terminal?.type === "turn.completed" && terminal.payload.outcome === "completed" && terminal.payload.structuredOutput.kind === "not_requested",
243
+ "structured_output_lifetime",
244
+ "An ordinary turn inherited a structured output binding."
245
+ );
246
+ return ["structured_output", "structured_output_lifetime"];
247
+ } catch (error) {
248
+ failure = { error };
249
+ throw error;
250
+ } finally {
251
+ const settled = await Promise.allSettled([bound?.close({ reason: "idle" }), plain === bound ? void 0 : plain?.close({ reason: "idle" })]);
252
+ const failures = settled.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
253
+ if (failures.length > 0) throw new AggregateError(
254
+ failure ? [failure.error, ...failures] : failures,
255
+ "Structured conformance cleanup failed.",
256
+ failure ? { cause: failure.error } : void 0
257
+ );
258
+ }
259
+ }
166
260
  async function runAgentProviderConformance(scenario) {
167
261
  const checks = [];
168
262
  try {
@@ -337,6 +431,12 @@ async function runAgentProviderConformance(scenario) {
337
431
  }
338
432
  }
339
433
  checks.push("environment_session");
434
+ checks.push(...await checkStructuredOutput({
435
+ adapter: instance.adapter,
436
+ capability: instance.capabilities.turns.structuredOutput,
437
+ plainSession: created,
438
+ scenario
439
+ }));
340
440
  const aborted = new AbortController();
341
441
  aborted.abort(new DOMException("Conformance abort.", "AbortError"));
342
442
  try {
@@ -44,10 +44,11 @@ function defaultCapabilities(providerKey) {
44
44
  }
45
45
  };
46
46
  return parseAgentCapabilities({
47
- protocolVersion: 12,
47
+ protocolVersion: 13,
48
48
  providerKey,
49
49
  sessions: { create: true, resume: true, branch: { kind: "through_turn" } },
50
50
  turns: {
51
+ structuredOutput: { kind: "unsupported" },
51
52
  interactionModes: ["default"],
52
53
  interrupt: true,
53
54
  steer: { kind: "supported", input }
@@ -166,7 +167,7 @@ function sessionBinding(state, sessionId) {
166
167
  };
167
168
  }
168
169
  function eventBase(sessionId, turnId, occurredAt) {
169
- return { protocolVersion: 12, sessionId, turnId, occurredAt };
170
+ return { protocolVersion: 13, sessionId, turnId, occurredAt };
170
171
  }
171
172
  function fakeEnvironmentSnapshot(environmentId) {
172
173
  return parseAgentEnvironmentSnapshot({
@@ -293,7 +294,7 @@ function fakeSession(input) {
293
294
  yield createAgentEventOutput({
294
295
  ...eventBase(input.context.sessionId, turnInput.turnId, input.now()),
295
296
  type: "turn.completed",
296
- payload: { outcome: "canceled", reason: "Fake provider interrupted." }
297
+ payload: { structuredOutput: { kind: "not_requested" }, outcome: "canceled", reason: "Fake provider interrupted." }
297
298
  });
298
299
  return;
299
300
  }
@@ -353,7 +354,7 @@ function fakeSession(input) {
353
354
  yield createAgentEventOutput({
354
355
  ...eventBase(input.context.sessionId, turnInput.turnId, occurredAt),
355
356
  type: "turn.completed",
356
- payload: { outcome: interruptedTurns.has(turnInput.turnId) ? "canceled" : "completed", reason: "Fake provider completed." }
357
+ payload: { structuredOutput: { kind: "not_requested" }, outcome: interruptedTurns.has(turnInput.turnId) ? "canceled" : "completed", reason: "Fake provider completed." }
357
358
  });
358
359
  };
359
360
  const close = async () => {
@@ -1,4 +1,6 @@
1
1
  import { type AgentProviderKey, type AgentTurnId } from "@agen-ai/agent-protocol";
2
+ import type { AgentExecutionInputSupport } from "../executionInput/types.js";
3
+ import { type createExecutionInputCensus } from "../executionInput/lifetime.js";
2
4
  import type { AgentProviderOutput } from "../outputs.js";
3
5
  import type { AgentSessionAdmission } from "../sessionAdmission.js";
4
6
  import type { AgentProviderStartTurnInput, AgentProviderTurnExecution } from "../sessions.js";
@@ -6,13 +8,19 @@ import { type AgentTurnRequestAdmission, type PendingAgentRequest, type Suspende
6
8
  export interface ValidatedAgentTurnExecution {
7
9
  readonly execution: AgentProviderTurnExecution;
8
10
  readonly hasEntered: () => boolean;
9
- readonly stop: (reason: Error) => void;
11
+ readonly stop: (reason: Error, inputReason?: "closed" | "interrupted" | "native_failed") => void;
12
+ readonly closeInputAdmission: () => void;
10
13
  readonly join: () => Promise<void>;
11
14
  readonly suspendRequests: () => SuspendedAgentTurnRequests;
12
15
  readonly completed: Promise<void>;
13
16
  }
14
17
  type ExecutionOptions = Readonly<{
15
18
  providerKey: AgentProviderKey;
19
+ executionInputSupport?: AgentExecutionInputSupport;
20
+ executionInputCensus: ReturnType<typeof createExecutionInputCensus>;
21
+ ownsExecution: () => boolean;
22
+ requireInputContext: () => void;
23
+ structuredOutputBound: boolean;
16
24
  pendingRequests: Map<string, PendingAgentRequest>;
17
25
  sessionAdmission: AgentSessionAdmission;
18
26
  inFlightOperations: Set<Promise<void>>;