@agen-ai/agent-runtime 0.2.5 → 0.3.1

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.
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  AGENT_COLLABORATION_GRAPH_LIMITS,
3
+ AGENT_CONTENT_REFERENCES_MAX_LENGTH,
4
+ findAgentContentReferenceTextIssue,
3
5
  matchesAgentSessionBinding,
4
6
  parseAgentCollaborationControlInput,
5
7
  parseAgentCollaborationSpawnInput,
@@ -18,11 +20,11 @@ import {
18
20
  import {
19
21
  createAgentArtifactCandidate
20
22
  } from "./artifacts.js";
23
+ import { validateAgentEnvironmentObservationPort } from "./environment/validation.js";
21
24
  import {
22
25
  validateAgentCollaborationNodeForCapabilities,
23
26
  validateAgentConfigurationCatalogForCapabilities,
24
27
  validateAgentGeneratedResourceForCapabilities,
25
- validateAgentIntegrationCatalogForCapabilities,
26
28
  validateAgentManagedContentCatalogForCapabilities,
27
29
  validateAgentOperationCatalogForCapabilities,
28
30
  validateAgentOperationResultForInvocation,
@@ -57,11 +59,11 @@ function validateSessionPorts(capabilities, session) {
57
59
  "configuration",
58
60
  "operations",
59
61
  "managedContent",
60
- "integrations",
62
+ "environment",
61
63
  "collaboration",
62
64
  "generatedResources",
63
65
  "close"
64
- ]) || typeof session.runTurn !== "function" || typeof session.resolveRequest !== "function" || typeof session.close !== "function" || session.interruption === null || typeof session.interruption !== "object" || !["supported", "unsupported"].includes(session.interruption.kind) || session.steering === null || typeof session.steering !== "object" || !["supported", "unsupported"].includes(session.steering.kind) || session.configuration === null || typeof session.configuration !== "object" || !["managed", "selectable"].includes(session.configuration.kind) || session.operations === null || typeof session.operations !== "object" || !["supported", "unsupported"].includes(session.operations.kind) || session.managedContent === null || typeof session.managedContent !== "object" || !["supported", "unsupported"].includes(session.managedContent.kind) || session.integrations === null || typeof session.integrations !== "object" || !["supported", "unsupported"].includes(session.integrations.kind) || session.collaboration === null || typeof session.collaboration !== "object" || !["supported", "unsupported"].includes(session.collaboration.kind) || session.generatedResources === null || typeof session.generatedResources !== "object" || !["supported", "unsupported"].includes(session.generatedResources.kind)) {
66
+ ]) || typeof session.runTurn !== "function" || typeof session.resolveRequest !== "function" || typeof session.close !== "function" || session.interruption === null || typeof session.interruption !== "object" || !["supported", "unsupported"].includes(session.interruption.kind) || session.steering === null || typeof session.steering !== "object" || !["supported", "unsupported"].includes(session.steering.kind) || session.configuration === null || typeof session.configuration !== "object" || !["managed", "selectable"].includes(session.configuration.kind) || session.operations === null || typeof session.operations !== "object" || !["supported", "unsupported"].includes(session.operations.kind) || session.managedContent === null || typeof session.managedContent !== "object" || !["supported", "unsupported"].includes(session.managedContent.kind) || session.environment === null || typeof session.environment !== "object" || !["read", "read_and_watch", "unsupported"].includes(session.environment.kind) || session.collaboration === null || typeof session.collaboration !== "object" || !["supported", "unsupported"].includes(session.collaboration.kind) || session.generatedResources === null || typeof session.generatedResources !== "object" || !["supported", "unsupported"].includes(session.generatedResources.kind)) {
65
67
  throwAgentProviderContractError(
66
68
  capabilities.providerKey,
67
69
  "invalid_session",
@@ -84,15 +86,15 @@ function validateSessionPorts(capabilities, session) {
84
86
  session.managedContent,
85
87
  session.managedContent.kind === "supported" ? ["kind", "listManagedContent"] : ["kind"]
86
88
  ) || !hasExactOwnKeys(
87
- session.integrations,
88
- session.integrations.kind === "supported" ? ["kind", "observeIntegrations"] : ["kind"]
89
+ session.environment,
90
+ session.environment.kind === "unsupported" ? ["kind"] : session.environment.kind === "read" ? ["kind", "domains", "readEnvironment"] : ["kind", "domains", "readEnvironment", "watchEnvironment"]
89
91
  ) || !hasExactOwnKeys(
90
92
  session.collaboration,
91
93
  session.collaboration.kind === "supported" ? ["kind", "spawnCollaboration", "controlCollaboration"] : ["kind"]
92
94
  ) || !hasExactOwnKeys(
93
95
  session.generatedResources,
94
96
  session.generatedResources.kind === "supported" ? ["kind", "getGeneratedResource"] : ["kind"]
95
- ) || session.interruption.kind === "supported" !== capabilities.turns.interrupt || session.steering.kind === "supported" !== (capabilities.turns.steer.kind === "supported") || session.configuration.kind !== capabilities.configuration.kind || session.interruption.kind === "supported" && typeof session.interruption.interruptTurn !== "function" || session.steering.kind === "supported" && typeof session.steering.steerTurn !== "function" || session.configuration.kind === "selectable" && (typeof session.configuration.listConfiguration !== "function" || typeof session.configuration.applyConfigurationSelection !== "function") || session.operations.kind === "supported" !== (capabilities.operations.kind === "supported") || session.operations.kind === "supported" && (typeof session.operations.listOperations !== "function" || typeof session.operations.invokeOperation !== "function") || session.managedContent.kind === "supported" !== (capabilities.managedContent.kind === "supported") || session.managedContent.kind === "supported" && typeof session.managedContent.listManagedContent !== "function" || session.integrations.kind === "supported" !== (capabilities.integrations.kind === "supported") || session.integrations.kind === "supported" && typeof session.integrations.observeIntegrations !== "function" || session.collaboration.kind === "supported" !== (capabilities.collaboration.kind === "supported") || session.collaboration.kind === "supported" && (typeof session.collaboration.spawnCollaboration !== "function" || typeof session.collaboration.controlCollaboration !== "function") || session.generatedResources.kind === "supported" !== (capabilities.generatedResources.kind === "supported") || session.generatedResources.kind === "supported" && typeof session.generatedResources.getGeneratedResource !== "function") {
97
+ ) || session.interruption.kind === "supported" !== capabilities.turns.interrupt || session.steering.kind === "supported" !== (capabilities.turns.steer.kind === "supported") || session.configuration.kind !== capabilities.configuration.kind || session.interruption.kind === "supported" && typeof session.interruption.interruptTurn !== "function" || session.steering.kind === "supported" && typeof session.steering.steerTurn !== "function" || session.configuration.kind === "selectable" && (typeof session.configuration.listConfiguration !== "function" || typeof session.configuration.applyConfigurationSelection !== "function") || session.operations.kind === "supported" !== (capabilities.operations.kind === "supported") || session.operations.kind === "supported" && (typeof session.operations.listOperations !== "function" || typeof session.operations.invokeOperation !== "function") || session.managedContent.kind === "supported" !== (capabilities.managedContent.kind === "supported") || session.managedContent.kind === "supported" && typeof session.managedContent.listManagedContent !== "function" || session.environment.kind !== capabilities.environment.session.kind || session.environment.kind !== "unsupported" && (typeof session.environment.readEnvironment !== "function" || session.environment.kind === "read_and_watch" && typeof session.environment.watchEnvironment !== "function") || session.collaboration.kind === "supported" !== (capabilities.collaboration.kind === "supported") || session.collaboration.kind === "supported" && (typeof session.collaboration.spawnCollaboration !== "function" || typeof session.collaboration.controlCollaboration !== "function") || session.generatedResources.kind === "supported" !== (capabilities.generatedResources.kind === "supported") || session.generatedResources.kind === "supported" && typeof session.generatedResources.getGeneratedResource !== "function") {
96
98
  throwAgentProviderContractError(
97
99
  capabilities.providerKey,
98
100
  "capability_port_mismatch",
@@ -612,46 +614,75 @@ function clearPendingRequestsForTurn(pendingRequests, turnId) {
612
614
  }
613
615
  function assertAgentTurnInputCapability(input) {
614
616
  const imageParts = input.parts.filter((part) => part.type === "image");
615
- if (imageParts.length === 0) return;
616
- const imageCapability = input.capability.images;
617
- if (imageCapability.kind === "unsupported") {
618
- throwAgentProviderContractError(
617
+ if (imageParts.length > 0) {
618
+ const imageCapability = input.capability.images;
619
+ if (imageCapability.kind === "unsupported") {
620
+ throwAgentProviderContractError(
621
+ input.providerKey,
622
+ "input_capability_mismatch",
623
+ `Provider ${input.providerKey} does not accept image input.`
624
+ );
625
+ }
626
+ const reject = (reason) => throwAgentProviderContractError(
619
627
  input.providerKey,
620
628
  "input_capability_mismatch",
621
- `Provider ${input.providerKey} does not accept image input.`
629
+ `Provider ${input.providerKey} cannot accept this image input: ${reason}.`
622
630
  );
631
+ if (imageParts.length > imageCapability.maxImages) {
632
+ reject("image count exceeds the declared limit");
633
+ }
634
+ if (!imageCapability.supportsImageOnly && !input.parts.some((part) => part.type === "text" && part.text.trim().length > 0)) {
635
+ reject("image-only input is unsupported");
636
+ }
637
+ let totalBytes = 0;
638
+ for (const part of imageParts) {
639
+ const { source } = part;
640
+ if (!imageCapability.sourceKinds.includes(source.type)) {
641
+ reject(`source kind ${source.type} is unsupported`);
642
+ }
643
+ if (!imageCapability.mediaTypes.includes(source.mediaType)) {
644
+ reject(`media type ${source.mediaType} is unsupported`);
645
+ }
646
+ if (source.byteSize > imageCapability.maxBytesPerImage) {
647
+ reject("an image exceeds the declared byte limit");
648
+ }
649
+ if (source.widthPixels > imageCapability.maxWidthPixels || source.heightPixels > imageCapability.maxHeightPixels || source.widthPixels * source.heightPixels > imageCapability.maxPixelsPerImage) {
650
+ reject("an image exceeds the declared dimension or pixel limit");
651
+ }
652
+ totalBytes += source.byteSize;
653
+ }
654
+ if (totalBytes > imageCapability.maxTotalBytes) {
655
+ reject("aggregate image bytes exceed the declared limit");
656
+ }
623
657
  }
624
- const reject = (reason) => throwAgentProviderContractError(
658
+ const contentReferenceParts = input.parts.filter(
659
+ (part) => part.type === "content_reference"
660
+ );
661
+ if (contentReferenceParts.length === 0) return;
662
+ const contentReferenceCapability = input.capability.contentReferences;
663
+ const rejectReference = (reason) => throwAgentProviderContractError(
625
664
  input.providerKey,
626
665
  "input_capability_mismatch",
627
- `Provider ${input.providerKey} cannot accept this image input: ${reason}.`
666
+ `Provider ${input.providerKey} cannot accept this content reference input: ${reason}.`
628
667
  );
629
- if (imageParts.length > imageCapability.maxImages) {
630
- reject("image count exceeds the declared limit");
668
+ if (contentReferenceParts.length > AGENT_CONTENT_REFERENCES_MAX_LENGTH) {
669
+ rejectReference("content reference count exceeds the protocol limit");
631
670
  }
632
- if (!imageCapability.supportsImageOnly && !input.parts.some((part) => part.type === "text" && part.text.trim().length > 0)) {
633
- reject("image-only input is unsupported");
671
+ if (contentReferenceCapability.kind === "unsupported") {
672
+ return rejectReference("content references are unsupported");
634
673
  }
635
- let totalBytes = 0;
636
- for (const part of imageParts) {
637
- const { source } = part;
638
- if (!imageCapability.sourceKinds.includes(source.type)) {
639
- reject(`source kind ${source.type} is unsupported`);
640
- }
641
- if (!imageCapability.mediaTypes.includes(source.mediaType)) {
642
- reject(`media type ${source.mediaType} is unsupported`);
643
- }
644
- if (source.byteSize > imageCapability.maxBytesPerImage) {
645
- reject("an image exceeds the declared byte limit");
646
- }
647
- if (source.widthPixels > imageCapability.maxWidthPixels || source.heightPixels > imageCapability.maxHeightPixels || source.widthPixels * source.heightPixels > imageCapability.maxPixelsPerImage) {
648
- reject("an image exceeds the declared dimension or pixel limit");
649
- }
650
- totalBytes += source.byteSize;
674
+ if (contentReferenceParts.length > contentReferenceCapability.maxReferences) {
675
+ rejectReference("content reference count exceeds the declared limit");
651
676
  }
652
- if (totalBytes > imageCapability.maxTotalBytes) {
653
- reject("aggregate image bytes exceed the declared limit");
677
+ if (!contentReferenceCapability.arguments && contentReferenceParts.some((part) => part.arguments !== void 0)) {
678
+ rejectReference("reference arguments are unsupported");
654
679
  }
680
+ const textIssue = findAgentContentReferenceTextIssue({
681
+ prompt: input.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n"),
682
+ arguments: contentReferenceParts.map((part) => part.arguments ?? ""),
683
+ textFormats: contentReferenceCapability.textFormats
684
+ });
685
+ if (textIssue) rejectReference(`${textIssue.target} must use ${textIssue.format} text`);
655
686
  }
656
687
  function validateAgentProviderSession(input) {
657
688
  const { capabilities, sessionId } = input;
@@ -688,6 +719,13 @@ function validateAgentProviderSession(input) {
688
719
  );
689
720
  }
690
721
  validateSessionPorts(capabilities, input.candidate);
722
+ const sessionLifetime = new AbortController();
723
+ const environment = validateAgentEnvironmentObservationPort({
724
+ providerKey,
725
+ capability: capabilities.environment.session,
726
+ port: input.candidate.environment,
727
+ signal: sessionLifetime.signal
728
+ });
691
729
  const pendingRequests = /* @__PURE__ */ new Map();
692
730
  const waitingTurnStates = /* @__PURE__ */ new Map();
693
731
  const openedRequestIds = /* @__PURE__ */ new Set();
@@ -1331,20 +1369,6 @@ function validateAgentProviderSession(input) {
1331
1369
  );
1332
1370
  }
1333
1371
  });
1334
- const declaredIntegrations = input.candidate.integrations;
1335
- const integrations = declaredIntegrations.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
1336
- kind: "supported",
1337
- observeIntegrations: async (listInput = {}) => {
1338
- requireUsable();
1339
- throwIfAgentOperationAborted(listInput.signal);
1340
- return validateAgentIntegrationCatalogForCapabilities(
1341
- capabilities,
1342
- await declaredIntegrations.observeIntegrations(
1343
- listInput.signal === void 0 ? {} : { signal: listInput.signal }
1344
- )
1345
- );
1346
- }
1347
- });
1348
1372
  const declaredCollaboration = input.candidate.collaboration;
1349
1373
  const collaboration = declaredCollaboration.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
1350
1374
  kind: "supported",
@@ -1592,6 +1616,7 @@ function validateAgentProviderSession(input) {
1592
1616
  throw new TypeError("Provider session close reason is unsupported.");
1593
1617
  }
1594
1618
  unusable = true;
1619
+ sessionLifetime.abort(new DOMException("The provider session was closed.", "AbortError"));
1595
1620
  closePromise = Promise.resolve().then(() => input.candidate.close(closeInput)).then(() => {
1596
1621
  closed = true;
1597
1622
  }).finally(() => {
@@ -1608,7 +1633,7 @@ function validateAgentProviderSession(input) {
1608
1633
  configuration,
1609
1634
  operations,
1610
1635
  managedContent,
1611
- integrations,
1636
+ environment,
1612
1637
  collaboration,
1613
1638
  generatedResources,
1614
1639
  close
@@ -1,7 +1,8 @@
1
- import type { AgentAuthenticationFlow, AgentCollaborationControlInput, AgentCollaborationNode, AgentCollaborationSpawnInput, AgentConfigurationCatalog, AgentConfigurationSelectionInput, AgentError, AgentGeneratedResourceDescriptor, AgentGeneratedResourceId, AgentIntegrationCatalog, 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, AgentTurnId, AgentTurnInputContent, AgentTurnInteractionMode, AgentTurnInputPart, AgentTurnInterruptionReason } from "@agen-ai/agent-protocol";
2
2
  import type { MaybePromise } from "./foundation.js";
3
3
  import type { AgentProviderOutput } from "./outputs.js";
4
4
  import type { AgentArtifactCandidate } from "./artifacts.js";
5
+ import type { AgentEnvironmentObservationPort } from "./environment/types.js";
5
6
  export interface AgentProviderSessionContext {
6
7
  readonly sessionId: AgentSessionId;
7
8
  readonly workingDirectory: string;
@@ -129,12 +130,6 @@ export type AgentManagedContentInventory = Readonly<{
129
130
  kind: "supported";
130
131
  listManagedContent: (input?: AgentProviderListInput) => MaybePromise<AgentManagedContentCatalog>;
131
132
  }>;
132
- export type AgentIntegrationObservation = Readonly<{
133
- kind: "unsupported";
134
- }> | Readonly<{
135
- kind: "supported";
136
- observeIntegrations: (input?: AgentProviderListInput) => MaybePromise<AgentIntegrationCatalog>;
137
- }>;
138
133
  export type AgentCollaborationControl = Readonly<{
139
134
  kind: "unsupported";
140
135
  }> | Readonly<{
@@ -157,7 +152,7 @@ export interface AgentProviderSession {
157
152
  readonly configuration: AgentSessionConfigurationControl;
158
153
  readonly operations: AgentOperationControl;
159
154
  readonly managedContent: AgentManagedContentInventory;
160
- readonly integrations: AgentIntegrationObservation;
155
+ readonly environment: AgentEnvironmentObservationPort;
161
156
  readonly collaboration: AgentCollaborationControl;
162
157
  readonly generatedResources: AgentGeneratedResourceAccess;
163
158
  readonly close: (input: AgentProviderCloseSessionInput) => MaybePromise<void>;
@@ -1,4 +1,4 @@
1
- import { type AgentCollaborationSpawnInput, type AgentConfigurationSelectionInput, 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 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 { AgentProviderRunTurnInput } from "../sessions.js";
4
4
  export interface AgentProviderConformanceScenario {
@@ -10,6 +10,7 @@ export interface AgentProviderConformanceScenario {
10
10
  readonly operationInvocation?: AgentOperationInvocation;
11
11
  readonly collaborationSpawn?: AgentCollaborationSpawnInput;
12
12
  readonly generatedResourceId?: AgentGeneratedResourceId;
13
+ readonly environmentId?: AgentEnvironmentId;
13
14
  readonly createSessionId: AgentSessionId;
14
15
  readonly resumeSessionId: AgentSessionId;
15
16
  readonly branchSessionId?: AgentSessionId;
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  matchesAgentSessionBinding,
3
+ parseAgentEnvironmentId,
3
4
  parseAgentRequestResolutionFor,
4
5
  parseAgentTurnId
5
6
  } from "@agen-ai/agent-protocol";
@@ -19,6 +20,23 @@ class AgentProviderConformanceError extends Error {
19
20
  function requireCheck(condition, check, message) {
20
21
  if (!condition) throw new AgentProviderConformanceError(check, message);
21
22
  }
23
+ async function checkEnvironmentWatch(input) {
24
+ const pending = input.iterator.next();
25
+ await Promise.resolve();
26
+ input.controller.abort(new DOMException("Conformance watch complete.", "AbortError"));
27
+ try {
28
+ const next = await pending;
29
+ requireCheck(
30
+ next.done || next.value.environmentId === input.environmentId,
31
+ input.check,
32
+ "Environment watch returned a different identity."
33
+ );
34
+ } catch (error) {
35
+ if (!isAgentOperationAbortError(error)) throw error;
36
+ } finally {
37
+ await input.iterator.return?.();
38
+ }
39
+ }
22
40
  async function advanceToStartedTurn(session, input) {
23
41
  const iterator = session.runTurn(input)[Symbol.asyncIterator]();
24
42
  const outputs = [];
@@ -142,6 +160,46 @@ async function runAgentProviderConformance(scenario) {
142
160
  requireCheck(aborted2, "account_quota", "Pre-aborted quota work must fail with AbortError.");
143
161
  }
144
162
  checks.push("account_quota");
163
+ const environmentId = scenario.environmentId ?? parseAgentEnvironmentId("conformance-environment:instance");
164
+ if (instance.capabilities.environment.instance.kind === "unsupported") {
165
+ requireCheck(
166
+ instance.environment.kind === "unsupported",
167
+ "environment_instance",
168
+ "Unsupported instance environment exposed a discovery port."
169
+ );
170
+ } else {
171
+ requireCheck(
172
+ instance.environment.kind === instance.capabilities.environment.instance.kind,
173
+ "environment_instance",
174
+ "Instance environment capability has no matching discovery port."
175
+ );
176
+ const readSignal = new AbortController();
177
+ let executionStarted = 0;
178
+ const snapshot = await instance.environment.readEnvironment({
179
+ signal: readSignal.signal,
180
+ environmentId,
181
+ workingDirectory: scenario.workingDirectory,
182
+ configuration: scenario.configuration,
183
+ onProviderExecutionStarted: () => {
184
+ executionStarted += 1;
185
+ }
186
+ });
187
+ requireCheck(
188
+ snapshot.environmentId === environmentId && executionStarted === 1,
189
+ "environment_instance",
190
+ "Instance environment discovery did not echo its identity and start receipt."
191
+ );
192
+ if (instance.environment.kind === "read_and_watch") {
193
+ const iterator = instance.environment.watchEnvironment({
194
+ signal: readSignal.signal,
195
+ environmentId,
196
+ workingDirectory: scenario.workingDirectory,
197
+ configuration: scenario.configuration
198
+ })[Symbol.asyncIterator]();
199
+ await checkEnvironmentWatch({ iterator, controller: readSignal, environmentId, check: "environment_instance" });
200
+ }
201
+ }
202
+ checks.push("environment_instance");
145
203
  let createdBindingCount = 0;
146
204
  const created = await instance.adapter.createSession({
147
205
  sessionId: scenario.createSessionId,
@@ -158,6 +216,41 @@ async function runAgentProviderConformance(scenario) {
158
216
  "Create session did not report exactly one binding."
159
217
  );
160
218
  checks.push("create_session", "binding_callback");
219
+ if (instance.capabilities.environment.session.kind === "unsupported") {
220
+ requireCheck(
221
+ created.environment.kind === "unsupported",
222
+ "environment_session",
223
+ "Unsupported session environment exposed an observation port."
224
+ );
225
+ } else {
226
+ requireCheck(
227
+ created.environment.kind === instance.capabilities.environment.session.kind,
228
+ "environment_session",
229
+ "Session environment capability has no matching observation port."
230
+ );
231
+ const readSignal = new AbortController();
232
+ let executionStarted = 0;
233
+ const snapshot = await created.environment.readEnvironment({
234
+ signal: readSignal.signal,
235
+ environmentId,
236
+ onProviderExecutionStarted: () => {
237
+ executionStarted += 1;
238
+ }
239
+ });
240
+ requireCheck(
241
+ snapshot.environmentId === environmentId && executionStarted === 1,
242
+ "environment_session",
243
+ "Session environment observation did not echo its identity and start receipt."
244
+ );
245
+ if (created.environment.kind === "read_and_watch") {
246
+ const iterator = created.environment.watchEnvironment({
247
+ signal: readSignal.signal,
248
+ environmentId
249
+ })[Symbol.asyncIterator]();
250
+ await checkEnvironmentWatch({ iterator, controller: readSignal, environmentId, check: "environment_session" });
251
+ }
252
+ }
253
+ checks.push("environment_session");
161
254
  const aborted = new AbortController();
162
255
  aborted.abort(new DOMException("Conformance abort.", "AbortError"));
163
256
  try {
@@ -386,21 +479,6 @@ async function runAgentProviderConformance(scenario) {
386
479
  );
387
480
  }
388
481
  checks.push("managed_content");
389
- if (instance.capabilities.integrations.kind === "supported") {
390
- requireCheck(
391
- created.integrations.kind === "supported",
392
- "integrations",
393
- "Supported integrations have no observation port."
394
- );
395
- await created.integrations.observeIntegrations();
396
- } else {
397
- requireCheck(
398
- created.integrations.kind === "unsupported",
399
- "integrations",
400
- "Unsupported integrations exposed an observation port."
401
- );
402
- }
403
- checks.push("integrations");
404
482
  if (instance.capabilities.collaboration.kind === "supported") {
405
483
  requireCheck(
406
484
  created.collaboration.kind === "supported" && scenario.collaborationSpawn !== void 0,
@@ -22,6 +22,8 @@ export interface FakeAgentProviderSnapshot {
22
22
  readonly interruptedTurnIds: readonly AgentTurnId[];
23
23
  readonly steeringInputs: readonly FakeAgentSteeringInput[];
24
24
  readonly configurationRevisions: readonly string[];
25
+ readonly environmentReadIds: readonly string[];
26
+ readonly environmentInvalidationIds: readonly string[];
25
27
  readonly closeCounts: Readonly<Record<string, number>>;
26
28
  }
27
29
  export interface FakeAgentProvider {
@@ -1,4 +1,6 @@
1
1
  import {
2
+ AGENT_ENVIRONMENT_DOMAINS,
3
+ parseAgentEnvironmentSnapshot,
2
4
  parseAgentCapabilities,
3
5
  parseAgentInstanceId,
4
6
  parseAgentIsoDateTime,
@@ -33,10 +35,16 @@ function defaultCapabilities(providerKey) {
33
35
  maxHeightPixels: 6e3,
34
36
  maxPixelsPerImage: 36e6,
35
37
  supportsImageOnly: true
38
+ },
39
+ contentReferences: {
40
+ kind: "supported",
41
+ maxReferences: 16,
42
+ arguments: true,
43
+ textFormats: { prompt: "unrestricted", arguments: "unrestricted" }
36
44
  }
37
45
  };
38
46
  return parseAgentCapabilities({
39
- protocolVersion: 8,
47
+ protocolVersion: 9,
40
48
  providerKey,
41
49
  sessions: { create: true, resume: true, branch: { kind: "through_turn" } },
42
50
  turns: {
@@ -117,6 +125,16 @@ function defaultCapabilities(providerKey) {
117
125
  maxToolsPerServer: 100,
118
126
  maxResourcesPerServer: 100
119
127
  },
128
+ environment: {
129
+ instance: {
130
+ kind: "read_and_watch",
131
+ domains: [...AGENT_ENVIRONMENT_DOMAINS]
132
+ },
133
+ session: {
134
+ kind: "read_and_watch",
135
+ domains: [...AGENT_ENVIRONMENT_DOMAINS]
136
+ }
137
+ },
120
138
  collaboration: {
121
139
  kind: "supported",
122
140
  roles: ["delegate", "reviewer", "researcher", "specialist"],
@@ -147,7 +165,76 @@ function sessionBinding(state, sessionId) {
147
165
  };
148
166
  }
149
167
  function eventBase(sessionId, turnId, occurredAt) {
150
- return { protocolVersion: 8, sessionId, turnId, occurredAt };
168
+ return { protocolVersion: 9, sessionId, turnId, occurredAt };
169
+ }
170
+ function fakeEnvironmentSnapshot(environmentId) {
171
+ return parseAgentEnvironmentSnapshot({
172
+ schemaVersion: 1,
173
+ environmentId,
174
+ revision: 1,
175
+ content: { kind: "unavailable", reasons: ["transient_failure"] },
176
+ commands: { kind: "unavailable", reasons: ["transient_failure"] },
177
+ extensions: { kind: "unavailable", reasons: ["transient_failure"] },
178
+ integrations: { kind: "unavailable", reasons: ["transient_failure"] }
179
+ });
180
+ }
181
+ function fakeDiscoveryEnvironment(state, capability) {
182
+ if (capability.kind === "unsupported") return { kind: "unsupported" };
183
+ const domains = Object.freeze([
184
+ ...capability.domains
185
+ ]);
186
+ const readEnvironment = async ({ signal, environmentId }) => {
187
+ throwIfAgentOperationAborted(signal);
188
+ state.environmentReadIds.push(environmentId);
189
+ return fakeEnvironmentSnapshot(environmentId);
190
+ };
191
+ const watchEnvironment = async function* ({ signal, environmentId }) {
192
+ throwIfAgentOperationAborted(signal);
193
+ state.environmentInvalidationIds.push(environmentId);
194
+ yield { environmentId, domains: ["content"] };
195
+ };
196
+ if (capability.kind === "read") {
197
+ return {
198
+ kind: "read",
199
+ domains,
200
+ readEnvironment
201
+ };
202
+ }
203
+ return {
204
+ kind: "read_and_watch",
205
+ domains,
206
+ readEnvironment,
207
+ watchEnvironment
208
+ };
209
+ }
210
+ function fakeObservationEnvironment(state, capability) {
211
+ if (capability.kind === "unsupported") return { kind: "unsupported" };
212
+ const domains = Object.freeze([
213
+ ...capability.domains
214
+ ]);
215
+ const readEnvironment = async ({ signal, environmentId }) => {
216
+ throwIfAgentOperationAborted(signal);
217
+ state.environmentReadIds.push(environmentId);
218
+ return fakeEnvironmentSnapshot(environmentId);
219
+ };
220
+ const watchEnvironment = async function* ({ signal, environmentId }) {
221
+ throwIfAgentOperationAborted(signal);
222
+ state.environmentInvalidationIds.push(environmentId);
223
+ yield { environmentId, domains: ["content"] };
224
+ };
225
+ if (capability.kind === "read") {
226
+ return {
227
+ kind: "read",
228
+ domains,
229
+ readEnvironment
230
+ };
231
+ }
232
+ return {
233
+ kind: "read_and_watch",
234
+ domains,
235
+ readEnvironment,
236
+ watchEnvironment
237
+ };
151
238
  }
152
239
  function requestForTurn(turnId) {
153
240
  return {
@@ -383,17 +470,10 @@ function fakeSession(input) {
383
470
  return { revision: 1, entries: [] };
384
471
  }
385
472
  } : { kind: "unsupported" },
386
- integrations: input.capabilities.integrations.kind === "supported" ? {
387
- kind: "supported",
388
- observeIntegrations: async (listInput = {}) => {
389
- throwIfAgentOperationAborted(listInput.signal);
390
- return {
391
- revision: 1,
392
- observedAt: input.now(),
393
- integrations: []
394
- };
395
- }
396
- } : { kind: "unsupported" },
473
+ environment: fakeObservationEnvironment(
474
+ input.state,
475
+ input.capabilities.environment.session
476
+ ),
397
477
  collaboration: input.capabilities.collaboration.kind === "supported" ? {
398
478
  kind: "supported",
399
479
  spawnCollaboration: async (collaborationInput) => {
@@ -537,6 +617,8 @@ function createFakeAgentProvider(options = {}) {
537
617
  interruptedTurnIds: [],
538
618
  steeringInputs: [],
539
619
  configurationRevisions: [],
620
+ environmentReadIds: [],
621
+ environmentInvalidationIds: [],
540
622
  closeCounts: /* @__PURE__ */ new Map()
541
623
  };
542
624
  const driver = defineAgentProviderDriver({
@@ -555,6 +637,10 @@ function createFakeAgentProvider(options = {}) {
555
637
  instanceId: createdInstanceId,
556
638
  capabilities,
557
639
  adapter: fakeAdapter({ state, capabilities, now, steeringResult }),
640
+ environment: fakeDiscoveryEnvironment(
641
+ state,
642
+ capabilities.environment.instance
643
+ ),
558
644
  accountQuota: options.accountQuota ?? { kind: "unsupported" },
559
645
  checkReadiness: () => createAgentProviderReadiness({
560
646
  status: "ready",
@@ -591,6 +677,10 @@ function createFakeAgentProvider(options = {}) {
591
677
  configurationRevisions: Object.freeze([
592
678
  ...state.configurationRevisions
593
679
  ]),
680
+ environmentReadIds: Object.freeze([...state.environmentReadIds]),
681
+ environmentInvalidationIds: Object.freeze([
682
+ ...state.environmentInvalidationIds
683
+ ]),
594
684
  closeCounts: Object.freeze(Object.fromEntries(state.closeCounts))
595
685
  })
596
686
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agen-ai/agent-runtime",
3
- "version": "0.2.5",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Provider-neutral driver, instance, adapter, session, output, and conformance contracts for coding-agent runtimes.",
6
6
  "type": "module",
@@ -24,10 +24,14 @@
24
24
  "types": "./dist/testing/index.d.ts",
25
25
  "import": "./dist/testing/index.js"
26
26
  },
27
- "./package.json": "./package.json"
27
+ "./package.json": "./package.json",
28
+ "./environment": {
29
+ "types": "./dist/environment/index.d.ts",
30
+ "import": "./dist/environment/index.js"
31
+ }
28
32
  },
29
33
  "dependencies": {
30
- "@agen-ai/agent-protocol": "^0.2.5"
34
+ "@agen-ai/agent-protocol": "^0.3.1"
31
35
  },
32
36
  "devDependencies": {
33
37
  "tsup": "^8.5.0"