@tangle-network/agent-interface 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ export { AgentEnvironmentEgressPolicySchema } from "./environment-requests.js";
1
2
  export * from "./environment-requests.js";
2
3
  export * from "./environment-exact-process.js";
3
4
  export * from "./environment-interactive.js";
@@ -1,3 +1,4 @@
1
+ export { AgentEnvironmentEgressPolicySchema } from "./environment-requests.js";
1
2
  export * from "./environment-requests.js";
2
3
  export * from "./environment-exact-process.js";
3
4
  export * from "./environment-interactive.js";
@@ -5,6 +5,37 @@ import type { AgentProfile } from "./agent-profile.js";
5
5
  export type AgentProfileRef = AgentProfile | string;
6
6
  export type AgentEnvironmentStatus = "pending" | "provisioning" | "running" | "stopped" | "failed" | "expired" | "unknown";
7
7
  export type AgentSessionStatus = AgentEnvironmentStatus | "completed" | "cancelled";
8
+ /** Outbound network modes an ordinary agent environment can request. */
9
+ export type AgentEnvironmentEgressMode = "open" | "strict" | "blocked";
10
+ /**
11
+ * Outbound network policy for one agent environment.
12
+ *
13
+ * `open` permits every destination. `strict` permits only the named domains plus the model
14
+ * endpoints the provider itself provisioned into the environment. `blocked` denies every
15
+ * destination.
16
+ *
17
+ * A provider that cannot satisfy the requested mode must fail the create. It must never weaken
18
+ * the policy and never widen it: a policy that differs from the request is invisible from inside
19
+ * the environment, and the refusal it produces there reads as an authorization error that names
20
+ * nothing.
21
+ */
22
+ export type AgentEnvironmentEgressPolicy = {
23
+ mode: "open";
24
+ } | {
25
+ mode: "blocked";
26
+ } | {
27
+ mode: "strict";
28
+ allowDomains?: readonly string[];
29
+ };
30
+ /** Runtime contract for the portable egress policy carried by providers. */
31
+ export declare const AgentEnvironmentEgressPolicySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
32
+ mode: z.ZodLiteral<"open">;
33
+ }, z.core.$strict>, z.ZodObject<{
34
+ mode: z.ZodLiteral<"blocked">;
35
+ }, z.core.$strict>, z.ZodObject<{
36
+ mode: z.ZodLiteral<"strict">;
37
+ allowDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
38
+ }, z.core.$strict>], "mode">;
8
39
  export interface WorkspaceRequest {
9
40
  /** Provider-specific environment/template id, for example "universal". */
10
41
  environment?: string;
@@ -1,6 +1,33 @@
1
1
  import { z } from "zod";
2
- import { boundedIdentifierSchema, boundedJsonRecordSchema, boundedStringSchema, } from "./contract-limits.js";
2
+ import { boundedIdentifierSchema, boundedJsonRecordSchema, boundedStringSchema, CONTRACT_MAX_ARRAY_LENGTH, } from "./contract-limits.js";
3
3
  import { workspaceCwdSchema } from "./workspace-cwd.js";
4
+ /**
5
+ * One allowed destination host.
6
+ *
7
+ * A whitespace-only or outer-padded entry matches no host, so a policy carrying one is an
8
+ * allowlist the caller believes is in force and is not. That is the same silent-weakening this
9
+ * contract refuses everywhere else, so the entry is rejected rather than trimmed: trimming would
10
+ * accept a typo and change what the caller asked for.
11
+ */
12
+ const egressAllowDomainSchema = boundedIdentifierSchema;
13
+ /** Runtime contract for the portable egress policy carried by providers. */
14
+ export const AgentEnvironmentEgressPolicySchema = z.discriminatedUnion("mode", [
15
+ z.strictObject({ mode: z.literal("open") }),
16
+ z.strictObject({ mode: z.literal("blocked") }),
17
+ z.strictObject({
18
+ mode: z.literal("strict"),
19
+ /**
20
+ * Order does not change the policy, but it does change the create identity: this value is
21
+ * canonicalized into {@link agentEnvironmentCreateInputDigest}, so two same-key creates whose
22
+ * domains differ only in order are read as different creates and the second is refused. Build
23
+ * the list in a stable order.
24
+ */
25
+ allowDomains: z
26
+ .array(egressAllowDomainSchema)
27
+ .max(CONTRACT_MAX_ARRAY_LENGTH)
28
+ .optional(),
29
+ }),
30
+ ]);
4
31
  /** Runtime contract for the portable workspace request carried by providers. */
5
32
  export const WorkspaceRequestSchema = z
6
33
  .strictObject({
@@ -8,7 +8,7 @@ import { type InteractionAcknowledgement, type InteractionCapabilities, type Int
8
8
  import { type ContextTransferReceipt, type ContextTransferRequest, type ContextTransferResult, type NativeContextBoundaryProof, type NativeContextContinuationRequest, type NativeContextContinuationTurn } from "./portable-context.js";
9
9
  import { type AgentExactRunControlRef, type AgentRunCancellationAcknowledgement, type AgentRunCancellationRequest, type AgentRunControlRef } from "./runtime-control.js";
10
10
  import type { AgentWorkspaceBranching, AgentWorkspaceBranchingProvider } from "./workspace-branching.js";
11
- import type { AgentEnvironmentQuery, AgentEnvironmentStatus, AgentEnvironmentSummary, AgentProfileRef, AgentSessionStatus, CheckpointRef, CheckpointRequest, ExecRequest, ExecResult, ForkRequest, PlacementInfo, ResourceRequest, WorkspaceRequest } from "./environment-requests.js";
11
+ import type { AgentEnvironmentEgressMode, AgentEnvironmentEgressPolicy, AgentEnvironmentQuery, AgentEnvironmentStatus, AgentEnvironmentSummary, AgentProfileRef, AgentSessionStatus, CheckpointRef, CheckpointRequest, ExecRequest, ExecResult, ForkRequest, PlacementInfo, ResourceRequest, WorkspaceRequest } from "./environment-requests.js";
12
12
  import type { AgentExactProcessEgressMode, AgentExactProcessProvider } from "./environment-exact-process.js";
13
13
  import type { AgentEnvironmentObservation } from "./environment-observation.js";
14
14
  import type { AgentInteractiveSession, AgentInteractiveSessionRef, AgentInteractiveSessionStart } from "./environment-interactive.js";
@@ -827,6 +827,8 @@ export interface AgentEnvironmentCapabilities {
827
827
  branching: {
828
828
  checkpoint: boolean;
829
829
  fork: boolean;
830
+ /** True only when a fork can preserve and prove confidential requirements. */
831
+ confidential?: boolean;
830
832
  /** True only when key + canonical request digest semantics are implemented. */
831
833
  retrySafe?: boolean;
832
834
  /** True only when operations can be recovered by idempotency key. */
@@ -841,6 +843,28 @@ export interface AgentEnvironmentCapabilities {
841
843
  exactProcess?: {
842
844
  egress: readonly AgentExactProcessEgressMode[];
843
845
  };
846
+ /**
847
+ * Present only when create honors {@link CreateAgentEnvironmentInput.egress} or
848
+ * {@link CreateAgentEnvironmentInput.billingOwner}. Absent means neither field is read, so a
849
+ * caller that needs either must not send it and call the result a policy.
850
+ *
851
+ * Each member is present only when that field is honored, and the block carries at least one of
852
+ * them. A provider that takes a billing owner but no caller-controlled egress therefore states
853
+ * exactly that, instead of advertising an egress mode it does not accept.
854
+ */
855
+ create?: {
856
+ /**
857
+ * Egress modes create accepts, each named once. A mode absent here is refused, never weakened
858
+ * or widened. Absent altogether means create does not read
859
+ * {@link CreateAgentEnvironmentInput.egress} at all.
860
+ */
861
+ egress?: readonly AgentEnvironmentEgressMode[];
862
+ /**
863
+ * True when create carries the billing owner to the platform unchanged. The platform still
864
+ * authorizes the caller for that account; this flag states only that the field is not dropped.
865
+ */
866
+ billingOwner?: boolean;
867
+ };
844
868
  /** Per-surface flags for the normalized environment observation. */
845
869
  observation?: {
846
870
  identity: boolean;
@@ -962,6 +986,7 @@ export declare const AgentEnvironmentCapabilitiesSchema: z.ZodObject<{
962
986
  branching: z.ZodObject<{
963
987
  checkpoint: z.ZodBoolean;
964
988
  fork: z.ZodBoolean;
989
+ confidential: z.ZodOptional<z.ZodBoolean>;
965
990
  retrySafe: z.ZodOptional<z.ZodBoolean>;
966
991
  lookup: z.ZodOptional<z.ZodBoolean>;
967
992
  cleanup: z.ZodOptional<z.ZodBoolean>;
@@ -975,6 +1000,14 @@ export declare const AgentEnvironmentCapabilitiesSchema: z.ZodObject<{
975
1000
  strict: "strict";
976
1001
  }>>;
977
1002
  }, z.core.$strict>>;
1003
+ create: z.ZodOptional<z.ZodObject<{
1004
+ egress: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1005
+ blocked: "blocked";
1006
+ open: "open";
1007
+ strict: "strict";
1008
+ }>>>;
1009
+ billingOwner: z.ZodOptional<z.ZodBoolean>;
1010
+ }, z.core.$strict>>;
978
1011
  observation: z.ZodOptional<z.ZodObject<{
979
1012
  identity: z.ZodBoolean;
980
1013
  lifecycle: z.ZodBoolean;
@@ -1014,6 +1047,26 @@ export interface CreateAgentEnvironmentInput {
1014
1047
  resources?: ResourceRequest;
1015
1048
  env?: Record<string, string>;
1016
1049
  secrets?: string[] | Record<string, string>;
1050
+ /**
1051
+ * Outbound network policy for this environment.
1052
+ *
1053
+ * When absent the provider applies its own default. That default can be a strict allowlist,
1054
+ * and a model endpoint the environment must reach is then refused inside the environment as an
1055
+ * authorization error that names no policy. Declare the policy whenever the destinations the
1056
+ * environment needs are known.
1057
+ *
1058
+ * A provider that cannot satisfy the requested mode must fail the create rather than weaken or
1059
+ * widen the policy. {@link AgentEnvironmentCapabilities.create} names the modes it accepts.
1060
+ */
1061
+ egress?: AgentEnvironmentEgressPolicy;
1062
+ /**
1063
+ * Platform account whose balance funds this environment's managed model usage.
1064
+ *
1065
+ * A trusted first-party service acting for an authenticated user sets it. The provider honors
1066
+ * it only for a caller its platform has authorized to bill that account, and rejects the create
1067
+ * otherwise. It never grants a caller permission to charge an arbitrary account.
1068
+ */
1069
+ billingOwner?: string;
1017
1070
  metadata?: Record<string, unknown>;
1018
1071
  name?: string;
1019
1072
  /**
@@ -1085,7 +1138,15 @@ export interface AgentEnvironmentProvider {
1085
1138
  capabilities(): AgentEnvironmentCapabilities | Promise<AgentEnvironmentCapabilities>;
1086
1139
  validateProfile?(profile: AgentProfileRef): AgentProfileValidationResult | Promise<AgentProfileValidationResult>;
1087
1140
  /**
1088
- * Create or reconstruct one environment.
1141
+ * Create or reconstruct one environment that is ready to accept a turn.
1142
+ *
1143
+ * A returned environment answers {@link AgentEnvironment.stream} and every
1144
+ * other operation its capability document claims. A provider whose platform
1145
+ * starts an environment asynchronously holds this call until the environment
1146
+ * is running, and fails the call with the platform's reason when it never
1147
+ * gets there; it never returns a half-started environment for the caller to
1148
+ * poll. Every runtime seam relies on this: a caller streams the first turn
1149
+ * immediately after create, with nothing in between.
1089
1150
  *
1090
1151
  * With `input.idempotencyKey`, the provider must return the same environment
1091
1152
  * for the same canonical input and reject any changed input before creating.
@@ -215,6 +215,7 @@ export const AgentEnvironmentCapabilitiesSchema = z
215
215
  branching: z.strictObject({
216
216
  checkpoint: z.boolean(),
217
217
  fork: z.boolean(),
218
+ confidential: z.boolean().optional(),
218
219
  retrySafe: z.boolean().optional(),
219
220
  lookup: z.boolean().optional(),
220
221
  cleanup: z.boolean().optional(),
@@ -230,6 +231,16 @@ export const AgentEnvironmentCapabilitiesSchema = z
230
231
  .max(CONTRACT_MAX_ARRAY_LENGTH),
231
232
  })
232
233
  .optional(),
234
+ create: z
235
+ .strictObject({
236
+ egress: z
237
+ .array(z.enum(["open", "strict", "blocked"]))
238
+ .min(1)
239
+ .max(CONTRACT_MAX_ARRAY_LENGTH)
240
+ .optional(),
241
+ billingOwner: z.boolean().optional(),
242
+ })
243
+ .optional(),
233
244
  observation: z
234
245
  .strictObject({
235
246
  identity: z.boolean(),
@@ -332,6 +343,23 @@ export const AgentEnvironmentCapabilitiesSchema = z
332
343
  message: "exact process egress modes must be unique",
333
344
  });
334
345
  }
346
+ const create = capabilities.create;
347
+ if (create !== undefined) {
348
+ if (create.egress === undefined && create.billingOwner === undefined) {
349
+ refinement.addIssue({
350
+ code: "custom",
351
+ path: ["create"],
352
+ message: "a create capability block must state at least one honored field",
353
+ });
354
+ }
355
+ if (create.egress && new Set(create.egress).size !== create.egress.length) {
356
+ refinement.addIssue({
357
+ code: "custom",
358
+ path: ["create", "egress"],
359
+ message: "create egress modes must be unique",
360
+ });
361
+ }
362
+ }
335
363
  const terminal = capabilities.interactiveTerminal;
336
364
  if (terminal !== undefined &&
337
365
  (terminal.input || terminal.resize || terminal.reattach) &&
package/dist/index.d.ts CHANGED
@@ -41,3 +41,4 @@ export * from "./profile-schema.js";
41
41
  export * from "./profile-security.js";
42
42
  export * from "./sandbox-size.js";
43
43
  export { CONTRACT_MAX_CONFIDENTIAL_ATTESTATION_QUOTE_LENGTH, } from "./contract-limits.js";
44
+ export { AgentEnvironmentEgressPolicySchema } from "./environment-requests.js";
package/dist/index.js CHANGED
@@ -39,3 +39,4 @@ export * from "./profile-schema.js";
39
39
  export * from "./profile-security.js";
40
40
  export * from "./sandbox-size.js";
41
41
  export { CONTRACT_MAX_CONFIDENTIAL_ATTESTATION_QUOTE_LENGTH, } from "./contract-limits.js";
42
+ export { AgentEnvironmentEgressPolicySchema } from "./environment-requests.js";
@@ -9,6 +9,8 @@ import { type AgentExactRunControlRef } from "./runtime-control.js";
9
9
  export declare const ConfidentialAttestationSchema: z.ZodObject<{
10
10
  provider: z.ZodString;
11
11
  requested: z.ZodLiteral<true>;
12
+ tee: z.ZodOptional<z.ZodString>;
13
+ sealed: z.ZodOptional<z.ZodBoolean>;
12
14
  nonce: z.ZodString;
13
15
  measurement: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
14
16
  environmentId: z.ZodString;
@@ -32,11 +34,15 @@ export type ConfidentialAttestation = z.infer<typeof ConfidentialAttestationSche
32
34
  /** What the caller asked for, separate from what the provider proved. */
33
35
  export declare const ConfidentialExecutionRequestSchema: z.ZodObject<{
34
36
  requested: z.ZodBoolean;
37
+ tee: z.ZodOptional<z.ZodString>;
38
+ sealed: z.ZodOptional<z.ZodBoolean>;
35
39
  nonce: z.ZodOptional<z.ZodString>;
36
40
  policy: z.ZodOptional<z.ZodString>;
37
41
  profileDigest: z.ZodOptional<z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>>;
38
42
  }, z.core.$strict>;
39
43
  export type ConfidentialExecutionRequest = z.infer<typeof ConfidentialExecutionRequestSchema>;
44
+ /** Match one requested TEE against a provider-reported TEE identifier. */
45
+ export declare function confidentialTeeMatchesRequest(requested: string | undefined, attested: string | undefined): boolean;
40
46
  export interface ConfidentialExecutionEnvironment {
41
47
  provider: string;
42
48
  environmentId: string;
@@ -11,6 +11,10 @@ export const ConfidentialAttestationSchema = z
11
11
  .strictObject({
12
12
  provider: idSchema,
13
13
  requested: z.literal(true),
14
+ /** Provider-reported TEE type after external evidence verification. */
15
+ tee: idSchema.optional(),
16
+ /** Verified no-persistence posture, when the provider can prove it. */
17
+ sealed: z.boolean().optional(),
14
18
  nonce: idSchema,
15
19
  measurement: sha256DigestSchema,
16
20
  environmentId: idSchema,
@@ -43,11 +47,18 @@ export const ConfidentialAttestationSchema = z
43
47
  export const ConfidentialExecutionRequestSchema = z
44
48
  .strictObject({
45
49
  requested: z.boolean(),
50
+ /** Requested TEE. Omit it to accept any provider-supported TEE. */
51
+ tee: idSchema.optional(),
52
+ /** Require the provider to avoid persistence after the environment ends. */
53
+ sealed: z.boolean().optional(),
46
54
  nonce: idSchema.optional(),
47
55
  policy: idSchema.optional(),
48
56
  profileDigest: sha256DigestSchema.optional(),
49
57
  })
50
58
  .superRefine((request, refinement) => {
59
+ const carriesAdmissionField = request.nonce !== undefined ||
60
+ request.policy !== undefined ||
61
+ request.profileDigest !== undefined;
51
62
  const complete = request.nonce !== undefined &&
52
63
  request.policy !== undefined &&
53
64
  request.profileDigest !== undefined;
@@ -58,14 +69,48 @@ export const ConfidentialExecutionRequestSchema = z
58
69
  message: "a confidential execution request must bind nonce, policy, and profile digest",
59
70
  });
60
71
  }
61
- if (!request.requested && complete) {
72
+ if (!request.requested &&
73
+ (carriesAdmissionField ||
74
+ request.tee !== undefined ||
75
+ request.sealed !== undefined)) {
62
76
  refinement.addIssue({
63
77
  code: "custom",
64
78
  path: ["requested"],
65
- message: "a non-confidential execution request cannot carry confidential admission fields",
79
+ message: "a non-confidential execution request cannot carry confidential requirements or admission fields",
66
80
  });
67
81
  }
68
82
  });
83
+ function normalizedTeeId(value) {
84
+ const normalized = value.trim().toLowerCase().replaceAll("_", "-");
85
+ switch (normalized) {
86
+ case "aws-nitro":
87
+ case "aws-nitro-enclave":
88
+ case "aws-nitro-enclaves":
89
+ return "nitro";
90
+ case "intel-tdx":
91
+ return "tdx";
92
+ case "amd-sev":
93
+ case "amd-sev-snp":
94
+ case "sev":
95
+ return "sev-snp";
96
+ case "dstack":
97
+ case "phala":
98
+ return "phala-dstack";
99
+ default:
100
+ return normalized;
101
+ }
102
+ }
103
+ /** Match one requested TEE against a provider-reported TEE identifier. */
104
+ export function confidentialTeeMatchesRequest(requested, attested) {
105
+ if (attested === undefined)
106
+ return requested === undefined;
107
+ const normalizedAttested = normalizedTeeId(attested);
108
+ if (normalizedAttested === "none")
109
+ return false;
110
+ if (requested === undefined || normalizedTeeId(requested) === "any")
111
+ return true;
112
+ return (normalizedAttested === normalizedTeeId(requested));
113
+ }
69
114
  /**
70
115
  * Verify every request/result binding and require an external provider-key
71
116
  * check before exposing a positive confidentiality result.
@@ -104,6 +149,8 @@ export function confidentialExecutionVerified(input) {
104
149
  attestation.nonce !== request.nonce ||
105
150
  attestation.policy !== request.policy ||
106
151
  attestation.profileDigest !== request.profileDigest ||
152
+ !confidentialTeeMatchesRequest(request.tee, attestation.tee) ||
153
+ (request.sealed === true && attestation.sealed !== true) ||
107
154
  environment.confidentialRequested !== request.requested) {
108
155
  return false;
109
156
  }
@@ -114,6 +161,8 @@ export function confidentialExecutionRequestDigest(request) {
114
161
  const parsed = ConfidentialExecutionRequestSchema.parse(request);
115
162
  return wireDigest({
116
163
  requested: parsed.requested,
164
+ ...(parsed.tee === undefined ? {} : { tee: parsed.tee }),
165
+ ...(parsed.sealed === undefined ? {} : { sealed: parsed.sealed }),
117
166
  ...(parsed.nonce === undefined ? {} : { nonce: parsed.nonce }),
118
167
  ...(parsed.policy === undefined ? {} : { policy: parsed.policy }),
119
168
  ...(parsed.profileDigest === undefined
@@ -52,6 +52,8 @@ export declare const WorkspaceForkRequestSchema: z.ZodObject<{
52
52
  }, z.core.$strict>;
53
53
  confidential: z.ZodOptional<z.ZodObject<{
54
54
  requested: z.ZodBoolean;
55
+ tee: z.ZodOptional<z.ZodString>;
56
+ sealed: z.ZodOptional<z.ZodBoolean>;
55
57
  nonce: z.ZodOptional<z.ZodString>;
56
58
  policy: z.ZodOptional<z.ZodString>;
57
59
  profileDigest: z.ZodOptional<z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>>;
@@ -115,6 +117,8 @@ export declare const ForkedEnvironmentRefSchema: z.ZodObject<{
115
117
  confidentialAttestation: z.ZodOptional<z.ZodObject<{
116
118
  provider: z.ZodString;
117
119
  requested: z.ZodLiteral<true>;
120
+ tee: z.ZodOptional<z.ZodString>;
121
+ sealed: z.ZodOptional<z.ZodBoolean>;
118
122
  nonce: z.ZodString;
119
123
  measurement: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
120
124
  environmentId: z.ZodString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",