@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.
@@ -0,0 +1,533 @@
1
+ import path from "node:path";
2
+ import {
3
+ AGENT_ENVIRONMENT_DOMAINS,
4
+ parseAgentEnvironmentId,
5
+ parseAgentEnvironmentInvalidation,
6
+ parseAgentEnvironmentSnapshot,
7
+ parseAgentSessionConfiguration
8
+ } from "@agen-ai/agent-protocol";
9
+ import { throwAgentProviderContractError } from "../contractErrors.js";
10
+ import {
11
+ throwIfAgentOperationAborted
12
+ } from "../foundation.js";
13
+ import { containsAgentControlCharacter } from "../internal/controlCharacters.js";
14
+ const AGENT_ENVIRONMENT_ITERATOR_CLEANUP_TIMEOUT_MS = 1e3;
15
+ function isObject(value) {
16
+ return value !== null && typeof value === "object" && !Array.isArray(value);
17
+ }
18
+ function exactKeys(value, expected) {
19
+ const actual = Reflect.ownKeys(value);
20
+ return actual.length === expected.length && actual.every((key) => typeof key === "string" && expected.includes(key)) && expected.every((key) => Object.prototype.hasOwnProperty.call(value, key));
21
+ }
22
+ function exactKeysWithOptional(value, required, optional) {
23
+ const actual = Reflect.ownKeys(value);
24
+ const allowed = [...required, ...optional];
25
+ return actual.length >= required.length && actual.length <= allowed.length && actual.every((key) => typeof key === "string" && allowed.includes(key)) && required.every((key) => Object.prototype.hasOwnProperty.call(value, key));
26
+ }
27
+ function assertSignal(value, field) {
28
+ if (!(value instanceof AbortSignal)) {
29
+ throw new TypeError(`Environment ${field} must be an AbortSignal.`);
30
+ }
31
+ }
32
+ function abortReason(signal) {
33
+ return signal.reason instanceof Error ? signal.reason : new DOMException("The agent operation was aborted.", "AbortError");
34
+ }
35
+ function environmentContractError(providerKey, detail) {
36
+ return throwAgentProviderContractError(
37
+ providerKey,
38
+ "capability_port_mismatch",
39
+ `Provider ${providerKey} returned an invalid environment ${detail}.`
40
+ );
41
+ }
42
+ function invalidEnvironmentMetadata(providerKey, detail) {
43
+ return throwAgentProviderContractError(
44
+ providerKey,
45
+ "invalid_inventory",
46
+ `Provider ${providerKey} returned invalid environment metadata: ${detail}.`
47
+ );
48
+ }
49
+ function sameDomains(actual, expected) {
50
+ return actual.length === expected.length && actual.every((domain, index) => domain === expected[index]);
51
+ }
52
+ function parsePortDomains(providerKey, value) {
53
+ if (!Array.isArray(value) || value.length < 1) {
54
+ return environmentContractError(providerKey, "port domains");
55
+ }
56
+ const domains = [];
57
+ for (const candidate of value) {
58
+ if (typeof candidate !== "string" || !AGENT_ENVIRONMENT_DOMAINS.includes(candidate) || domains.includes(candidate)) {
59
+ return environmentContractError(providerKey, "port domains");
60
+ }
61
+ domains.push(candidate);
62
+ }
63
+ return Object.freeze(domains);
64
+ }
65
+ function validateDeclaredPort(providerKey, capability, candidate) {
66
+ if (!isObject(candidate)) return environmentContractError(providerKey, "port");
67
+ const kind = candidate.kind;
68
+ if (kind === "unsupported") {
69
+ if (!exactKeys(candidate, ["kind"]) || capability.kind !== "unsupported") {
70
+ return environmentContractError(providerKey, "port");
71
+ }
72
+ return "unsupported";
73
+ }
74
+ if (kind !== "read" && kind !== "read_and_watch") {
75
+ return environmentContractError(providerKey, "port");
76
+ }
77
+ if (capability.kind === "unsupported" || capability.kind !== kind) {
78
+ return environmentContractError(providerKey, "port capability parity");
79
+ }
80
+ const expectedKeys = kind === "read" ? ["kind", "domains", "readEnvironment"] : ["kind", "domains", "readEnvironment", "watchEnvironment"];
81
+ if (!exactKeys(candidate, expectedKeys)) {
82
+ return environmentContractError(providerKey, "port shape");
83
+ }
84
+ const domains = parsePortDomains(
85
+ providerKey,
86
+ candidate.domains
87
+ );
88
+ if (!sameDomains(domains, capability.domains)) {
89
+ return environmentContractError(providerKey, "port capability domains");
90
+ }
91
+ if (typeof candidate.readEnvironment !== "function" || kind === "read_and_watch" && typeof candidate.watchEnvironment !== "function") {
92
+ return environmentContractError(providerKey, "port methods");
93
+ }
94
+ return kind;
95
+ }
96
+ function parseCanonicalWorkingDirectory(value) {
97
+ if (typeof value !== "string" || value.length < 1 || value.length > 4096 || !path.isAbsolute(value) || path.normalize(value) !== value || containsAgentControlCharacter(value)) {
98
+ throw new TypeError(
99
+ "Environment workingDirectory must be a canonical absolute path."
100
+ );
101
+ }
102
+ return value;
103
+ }
104
+ function parseDiscoveryInput(input) {
105
+ if (!isObject(input) || !exactKeysWithOptional(input, [
106
+ "signal",
107
+ "environmentId",
108
+ "workingDirectory",
109
+ "configuration"
110
+ ], ["onProviderExecutionStarted"])) {
111
+ throw new TypeError(
112
+ "Environment discovery input must contain signal, environmentId, workingDirectory, and configuration."
113
+ );
114
+ }
115
+ const candidate = input;
116
+ assertSignal(candidate.signal, "discovery signal");
117
+ if (candidate.onProviderExecutionStarted !== void 0 && typeof candidate.onProviderExecutionStarted !== "function") {
118
+ throw new TypeError(
119
+ "Environment discovery onProviderExecutionStarted must be callable."
120
+ );
121
+ }
122
+ return {
123
+ signal: candidate.signal,
124
+ environmentId: parseAgentEnvironmentId(candidate.environmentId),
125
+ workingDirectory: parseCanonicalWorkingDirectory(candidate.workingDirectory),
126
+ configuration: parseAgentSessionConfiguration(candidate.configuration),
127
+ ...candidate.onProviderExecutionStarted === void 0 ? {} : {
128
+ onProviderExecutionStarted: candidate.onProviderExecutionStarted
129
+ }
130
+ };
131
+ }
132
+ function parseDiscoveryWatchInput(input) {
133
+ if (!isObject(input) || !exactKeys(input, [
134
+ "signal",
135
+ "environmentId",
136
+ "workingDirectory",
137
+ "configuration"
138
+ ])) {
139
+ throw new TypeError(
140
+ "Environment discovery watch input must contain signal, environmentId, workingDirectory, and configuration."
141
+ );
142
+ }
143
+ const candidate = input;
144
+ assertSignal(candidate.signal, "discovery watch signal");
145
+ return {
146
+ signal: candidate.signal,
147
+ environmentId: parseAgentEnvironmentId(candidate.environmentId),
148
+ workingDirectory: parseCanonicalWorkingDirectory(candidate.workingDirectory),
149
+ configuration: parseAgentSessionConfiguration(candidate.configuration)
150
+ };
151
+ }
152
+ function parseObservationInput(input) {
153
+ if (!isObject(input) || !exactKeysWithOptional(
154
+ input,
155
+ ["signal", "environmentId"],
156
+ ["onProviderExecutionStarted"]
157
+ )) {
158
+ throw new TypeError(
159
+ "Environment observation input must contain signal and environmentId."
160
+ );
161
+ }
162
+ const candidate = input;
163
+ assertSignal(candidate.signal, "observation signal");
164
+ if (candidate.onProviderExecutionStarted !== void 0 && typeof candidate.onProviderExecutionStarted !== "function") {
165
+ throw new TypeError(
166
+ "Environment observation onProviderExecutionStarted must be callable."
167
+ );
168
+ }
169
+ return {
170
+ signal: candidate.signal,
171
+ environmentId: parseAgentEnvironmentId(candidate.environmentId),
172
+ ...candidate.onProviderExecutionStarted === void 0 ? {} : {
173
+ onProviderExecutionStarted: candidate.onProviderExecutionStarted
174
+ }
175
+ };
176
+ }
177
+ function parseObservationWatchInput(input) {
178
+ if (!isObject(input) || !exactKeys(input, ["signal", "environmentId"])) {
179
+ throw new TypeError(
180
+ "Environment observation watch input must contain signal and environmentId."
181
+ );
182
+ }
183
+ const candidate = input;
184
+ assertSignal(candidate.signal, "observation watch signal");
185
+ return {
186
+ signal: candidate.signal,
187
+ environmentId: parseAgentEnvironmentId(candidate.environmentId)
188
+ };
189
+ }
190
+ function validateSnapshotDomains(providerKey, snapshot, declaredDomains, maximumExtent) {
191
+ for (const domain of AGENT_ENVIRONMENT_DOMAINS) {
192
+ const result = snapshot[domain];
193
+ if (maximumExtent === "workspace_discovery" && result.kind === "available" && result.extent === "session") {
194
+ return invalidEnvironmentMetadata(
195
+ providerKey,
196
+ `instance discovery cannot claim session evidence for domain ${domain}`
197
+ );
198
+ }
199
+ if (!declaredDomains.includes(domain) && result.kind === "available") {
200
+ return invalidEnvironmentMetadata(
201
+ providerKey,
202
+ `domain ${domain} was marked available by an undeclared port domain`
203
+ );
204
+ }
205
+ }
206
+ return snapshot;
207
+ }
208
+ function validateSnapshot(providerKey, candidate, expectedEnvironmentId, declaredDomains, maximumExtent) {
209
+ let snapshot;
210
+ try {
211
+ snapshot = parseAgentEnvironmentSnapshot(candidate);
212
+ } catch {
213
+ return invalidEnvironmentMetadata(providerKey, "snapshot");
214
+ }
215
+ if (snapshot.environmentId !== expectedEnvironmentId) {
216
+ return invalidEnvironmentMetadata(providerKey, "snapshot environmentId does not echo the request");
217
+ }
218
+ return validateSnapshotDomains(providerKey, snapshot, declaredDomains, maximumExtent);
219
+ }
220
+ function validateInvalidation(providerKey, candidate, expectedEnvironmentId, declaredDomains) {
221
+ let invalidation;
222
+ try {
223
+ invalidation = parseAgentEnvironmentInvalidation(candidate);
224
+ } catch {
225
+ return invalidEnvironmentMetadata(providerKey, "invalidation");
226
+ }
227
+ if (invalidation.environmentId !== expectedEnvironmentId) {
228
+ return invalidEnvironmentMetadata(providerKey, "invalidation environmentId does not echo the request");
229
+ }
230
+ if (invalidation.domains.some((domain) => !declaredDomains.includes(domain))) {
231
+ return invalidEnvironmentMetadata(providerKey, "invalidation names an undeclared domain");
232
+ }
233
+ return invalidation;
234
+ }
235
+ function awaitCancellable(candidate, signal) {
236
+ const providerResult = Promise.resolve(candidate);
237
+ return new Promise((resolve, reject) => {
238
+ let settled = false;
239
+ const remove = () => signal.removeEventListener("abort", onAbort);
240
+ const onAbort = () => {
241
+ if (settled) return;
242
+ settled = true;
243
+ remove();
244
+ reject(abortReason(signal));
245
+ };
246
+ signal.addEventListener("abort", onAbort, { once: true });
247
+ providerResult.then(
248
+ (value) => {
249
+ if (settled) return;
250
+ settled = true;
251
+ remove();
252
+ resolve(value);
253
+ },
254
+ (error) => {
255
+ if (settled) return;
256
+ settled = true;
257
+ remove();
258
+ reject(error);
259
+ }
260
+ );
261
+ if (signal.aborted) onAbort();
262
+ });
263
+ }
264
+ function providerIterator(providerKey, iterable) {
265
+ if (!isObject(iterable)) return environmentContractError(providerKey, "watch result");
266
+ const factory = iterable[Symbol.asyncIterator];
267
+ if (typeof factory !== "function") {
268
+ return environmentContractError(providerKey, "watch iterator");
269
+ }
270
+ const iterator = factory.call(iterable);
271
+ if (!isObject(iterator)) return environmentContractError(providerKey, "watch iterator");
272
+ const next = iterator.next;
273
+ if (typeof next !== "function") {
274
+ return environmentContractError(providerKey, "watch iterator");
275
+ }
276
+ const returnMethod = iterator.return;
277
+ if (returnMethod !== void 0 && typeof returnMethod !== "function") {
278
+ return environmentContractError(providerKey, "watch iterator");
279
+ }
280
+ return {
281
+ iterator,
282
+ next,
283
+ returnMethod
284
+ };
285
+ }
286
+ function iteratorResult(value) {
287
+ if (!isObject(value)) return false;
288
+ const done = value.done;
289
+ return done === void 0 || typeof done === "boolean";
290
+ }
291
+ function boundedCleanup(result, providerKey) {
292
+ let timer;
293
+ const timeout = new Promise((_, reject) => {
294
+ timer = setTimeout(() => reject(new Error(
295
+ `Provider ${providerKey} environment watch cleanup exceeded ${AGENT_ENVIRONMENT_ITERATOR_CLEANUP_TIMEOUT_MS}ms.`
296
+ )), AGENT_ENVIRONMENT_ITERATOR_CLEANUP_TIMEOUT_MS);
297
+ });
298
+ return Promise.race([Promise.resolve(result), timeout]).then(() => void 0).finally(() => {
299
+ if (timer !== void 0) clearTimeout(timer);
300
+ });
301
+ }
302
+ function beginCleanup(state) {
303
+ if (state.cleanupPromise !== void 0) return state.cleanupPromise;
304
+ const source = state.source;
305
+ if (source === void 0) return Promise.resolve();
306
+ if (source.returnMethod === void 0) {
307
+ state.cleanupPromise = Promise.resolve();
308
+ } else {
309
+ try {
310
+ state.cleanupPromise = boundedCleanup(
311
+ source.returnMethod.call(source.iterator),
312
+ state.providerKey
313
+ );
314
+ } catch (error) {
315
+ state.cleanupPromise = Promise.reject(error);
316
+ }
317
+ }
318
+ state.cleanupPromise.catch(() => void 0);
319
+ return state.cleanupPromise;
320
+ }
321
+ function closeState(state) {
322
+ if (state.closed) {
323
+ void beginCleanup(state);
324
+ return;
325
+ }
326
+ state.closed = true;
327
+ state.signal.removeEventListener("abort", state.onAbort);
328
+ if (!state.controller.signal.aborted) state.controller.abort();
329
+ void beginCleanup(state);
330
+ }
331
+ function createWatchIterator(providerKey, declaredWatch, receiver, lifetimeSignal, watchInput, declaredDomains) {
332
+ const controller = new AbortController();
333
+ const signal = AbortSignal.any([
334
+ lifetimeSignal,
335
+ watchInput.signal,
336
+ controller.signal
337
+ ]);
338
+ const state = {
339
+ providerKey,
340
+ controller,
341
+ signal,
342
+ onAbort: () => closeState(state),
343
+ declaredWatch,
344
+ receiver,
345
+ watchInput,
346
+ declaredDomains,
347
+ closed: false,
348
+ closedNormally: false,
349
+ pendingNext: false
350
+ };
351
+ if (signal.aborted) closeState(state);
352
+ else signal.addEventListener("abort", state.onAbort, { once: true });
353
+ const source = () => {
354
+ if (state.source !== void 0) return state.source;
355
+ throwIfAgentOperationAborted(signal);
356
+ const iterable = state.declaredWatch.call(state.receiver, {
357
+ ...state.watchInput,
358
+ signal
359
+ });
360
+ state.source = providerIterator(providerKey, iterable);
361
+ if (signal.aborted) closeState(state);
362
+ return state.source;
363
+ };
364
+ const next = async () => {
365
+ if (state.pendingNext) {
366
+ throw new TypeError("Concurrent environment watch next calls are not supported.");
367
+ }
368
+ if (state.closed) {
369
+ if (state.closedNormally || !signal.aborted) {
370
+ return { done: true, value: void 0 };
371
+ }
372
+ throwIfAgentOperationAborted(signal);
373
+ }
374
+ throwIfAgentOperationAborted(signal);
375
+ state.pendingNext = true;
376
+ try {
377
+ const iterator = source();
378
+ throwIfAgentOperationAborted(signal);
379
+ const result = await awaitCancellable(
380
+ iterator.next.call(iterator.iterator),
381
+ signal
382
+ );
383
+ throwIfAgentOperationAborted(signal);
384
+ if (!iteratorResult(result)) {
385
+ return environmentContractError(providerKey, "watch iterator result");
386
+ }
387
+ if (result.done === true) {
388
+ state.closedNormally = true;
389
+ closeState(state);
390
+ await beginCleanup(state);
391
+ return { done: true, value: void 0 };
392
+ }
393
+ return {
394
+ done: false,
395
+ value: validateInvalidation(
396
+ providerKey,
397
+ result.value,
398
+ state.watchInput.environmentId,
399
+ state.declaredDomains
400
+ )
401
+ };
402
+ } catch (error) {
403
+ closeState(state);
404
+ throw error;
405
+ } finally {
406
+ state.pendingNext = false;
407
+ }
408
+ };
409
+ const returnIterator = async (value) => {
410
+ state.closedNormally = true;
411
+ state.controller.abort();
412
+ closeState(state);
413
+ await beginCleanup(state);
414
+ return { done: true, value };
415
+ };
416
+ return { next, return: returnIterator };
417
+ }
418
+ function wrapRead(input) {
419
+ return async (rawInput) => {
420
+ const parsedInput = input.parseInput(rawInput);
421
+ const signal = AbortSignal.any([
422
+ input.lifetimeSignal,
423
+ parsedInput.signal
424
+ ]);
425
+ throwIfAgentOperationAborted(signal);
426
+ const {
427
+ onProviderExecutionStarted,
428
+ ...providerInput
429
+ } = parsedInput;
430
+ onProviderExecutionStarted?.();
431
+ const candidate = input.declaredRead.call(input.receiver, {
432
+ ...providerInput,
433
+ signal
434
+ });
435
+ const result = await awaitCancellable(candidate, signal);
436
+ throwIfAgentOperationAborted(signal);
437
+ return validateSnapshot(
438
+ input.providerKey,
439
+ result,
440
+ parsedInput.environmentId,
441
+ input.declaredDomains,
442
+ input.maximumExtent
443
+ );
444
+ };
445
+ }
446
+ function wrapWatch(input) {
447
+ return (rawInput) => {
448
+ const parsedInput = input.parseInput(rawInput);
449
+ return {
450
+ [Symbol.asyncIterator]() {
451
+ return createWatchIterator(
452
+ input.providerKey,
453
+ input.declaredWatch,
454
+ input.receiver,
455
+ input.lifetimeSignal,
456
+ parsedInput,
457
+ input.declaredDomains
458
+ );
459
+ }
460
+ };
461
+ };
462
+ }
463
+ function validateAgentEnvironmentDiscoveryPort(input) {
464
+ assertSignal(input.signal, "instance signal");
465
+ const kind = validateDeclaredPort(input.providerKey, input.capability, input.port);
466
+ if (kind === "unsupported") return Object.freeze({ kind: "unsupported" });
467
+ const declared = input.port;
468
+ const domains = Object.freeze([...declared.domains]);
469
+ const readEnvironment = wrapRead({
470
+ providerKey: input.providerKey,
471
+ declaredRead: declared.readEnvironment,
472
+ receiver: declared,
473
+ lifetimeSignal: input.signal,
474
+ parseInput: parseDiscoveryInput,
475
+ declaredDomains: domains,
476
+ maximumExtent: "workspace_discovery"
477
+ });
478
+ if (kind === "read") {
479
+ return Object.freeze({ kind: "read", domains, readEnvironment });
480
+ }
481
+ const watchEnvironment = wrapWatch({
482
+ providerKey: input.providerKey,
483
+ declaredWatch: declared.watchEnvironment,
484
+ receiver: declared,
485
+ lifetimeSignal: input.signal,
486
+ declaredDomains: domains,
487
+ parseInput: parseDiscoveryWatchInput
488
+ });
489
+ return Object.freeze({
490
+ kind: "read_and_watch",
491
+ domains,
492
+ readEnvironment,
493
+ watchEnvironment
494
+ });
495
+ }
496
+ function validateAgentEnvironmentObservationPort(input) {
497
+ assertSignal(input.signal, "session signal");
498
+ const kind = validateDeclaredPort(input.providerKey, input.capability, input.port);
499
+ if (kind === "unsupported") return Object.freeze({ kind: "unsupported" });
500
+ const declared = input.port;
501
+ const domains = Object.freeze([...declared.domains]);
502
+ const readEnvironment = wrapRead({
503
+ providerKey: input.providerKey,
504
+ declaredRead: declared.readEnvironment,
505
+ receiver: declared,
506
+ lifetimeSignal: input.signal,
507
+ parseInput: parseObservationInput,
508
+ declaredDomains: domains,
509
+ maximumExtent: "session"
510
+ });
511
+ if (kind === "read") {
512
+ return Object.freeze({ kind: "read", domains, readEnvironment });
513
+ }
514
+ const watchEnvironment = wrapWatch({
515
+ providerKey: input.providerKey,
516
+ declaredWatch: declared.watchEnvironment,
517
+ receiver: declared,
518
+ lifetimeSignal: input.signal,
519
+ declaredDomains: domains,
520
+ parseInput: parseObservationWatchInput
521
+ });
522
+ return Object.freeze({
523
+ kind: "read_and_watch",
524
+ domains,
525
+ readEnvironment,
526
+ watchEnvironment
527
+ });
528
+ }
529
+ export {
530
+ AGENT_ENVIRONMENT_ITERATOR_CLEANUP_TIMEOUT_MS,
531
+ validateAgentEnvironmentDiscoveryPort,
532
+ validateAgentEnvironmentObservationPort
533
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./adapterValidation.js";
2
2
  export * from "./accountQuota/index.js";
3
+ export * from "./environment/index.js";
3
4
  export * from "./artifacts.js";
4
5
  export * from "./contractErrors.js";
5
6
  export * from "./evidence.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./adapterValidation.js";
2
2
  export * from "./accountQuota/index.js";
3
+ export * from "./environment/index.js";
3
4
  export * from "./artifacts.js";
4
5
  export * from "./contractErrors.js";
5
6
  export * from "./evidence.js";
@@ -77,9 +77,7 @@ function validateAgentIntegrationCatalogForCapabilities(capabilities, candidate)
77
77
  }
78
78
  const capability = capabilities.integrations;
79
79
  if (capability.kind !== "supported" || catalog.integrations.length > capability.maxIntegrations || catalog.integrations.some(
80
- (integration) => !capability.integrationKinds.includes(integration.kind) || integration.servers.length > capability.maxServersPerIntegration || integration.servers.some(
81
- (server) => server.tools.length > capability.maxToolsPerServer || server.resources.length > capability.maxResourcesPerServer
82
- )
80
+ (integration) => !capability.integrationKinds.includes(integration.kind) || (integration.kind === "mcp" ? integration.servers.length > capability.maxServersPerIntegration || integration.servers.some((server) => server.tools.length > capability.maxToolsPerServer || server.resources.length > capability.maxResourcesPerServer) : (integration.tools?.length ?? 0) > capability.maxToolsPerServer)
83
81
  )) {
84
82
  return invalidInventory(
85
83
  capabilities.providerKey,
@@ -1,6 +1,7 @@
1
1
  import { type AgentCapabilities, type AgentInstanceId, type AgentProviderKey } from "@agen-ai/agent-protocol";
2
2
  import type { MaybePromise } from "./foundation.js";
3
3
  import type { AgentAccountQuotaPort } from "./accountQuota/types.js";
4
+ import type { AgentEnvironmentDiscoveryPort } from "./environment/types.js";
4
5
  import type { AgentProviderReadiness } from "./readiness.js";
5
6
  import type { AgentProviderAdapter } from "./sessions.js";
6
7
  export interface AgentProviderInstanceDefinition {
@@ -16,6 +17,7 @@ export interface MaterializedAgentProviderInstance {
16
17
  readonly capabilities: AgentCapabilities;
17
18
  readonly adapter: AgentProviderAdapter;
18
19
  readonly accountQuota: AgentAccountQuotaPort;
20
+ readonly environment: AgentEnvironmentDiscoveryPort;
19
21
  readonly checkReadiness: (input?: AgentProviderReadinessCheckInput) => MaybePromise<AgentProviderReadiness>;
20
22
  readonly dispose: () => MaybePromise<void>;
21
23
  }
@@ -5,6 +5,7 @@ import {
5
5
  } from "@agen-ai/agent-protocol";
6
6
  import { validateAgentProviderAdapter } from "./adapterValidation.js";
7
7
  import { validateAgentAccountQuotaPort } from "./accountQuota/validation.js";
8
+ import { validateAgentEnvironmentDiscoveryPort } from "./environment/validation.js";
8
9
  import {
9
10
  createAgentProviderCatalogEntries,
10
11
  createAgentProviderInstanceCatalogEntries
@@ -140,6 +141,7 @@ function managedInstance(rawInstance, providerKey) {
140
141
  disposePromise: null
141
142
  };
142
143
  const quotaLifetime = new AbortController();
144
+ const environmentLifetime = new AbortController();
143
145
  const instance = Object.freeze({
144
146
  instanceId: parseAgentInstanceId(rawInstance.instanceId),
145
147
  capabilities,
@@ -149,6 +151,12 @@ function managedInstance(rawInstance, providerKey) {
149
151
  port: rawInstance.accountQuota,
150
152
  signal: quotaLifetime.signal
151
153
  }),
154
+ environment: validateAgentEnvironmentDiscoveryPort({
155
+ providerKey,
156
+ capability: capabilities.environment.instance,
157
+ port: rawInstance.environment,
158
+ signal: environmentLifetime.signal
159
+ }),
152
160
  checkReadiness: async (input) => {
153
161
  const readiness = await checkReadiness(input);
154
162
  return validateAgentProviderReadiness(readiness);
@@ -158,6 +166,7 @@ function managedInstance(rawInstance, providerKey) {
158
166
  if (lifecycle.disposePromise) return lifecycle.disposePromise;
159
167
  lifecycle.status = "disposing";
160
168
  quotaLifetime.abort(new DOMException("The provider instance was disposed.", "AbortError"));
169
+ environmentLifetime.abort(new DOMException("The provider instance was disposed.", "AbortError"));
161
170
  lifecycle.disposePromise = Promise.resolve().then(() => dispose()).then(() => {
162
171
  lifecycle.status = "disposed";
163
172
  }).catch((error) => {