@tangle-network/agent-interface 2.2.0 → 2.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.
@@ -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. */
@@ -984,6 +986,7 @@ export declare const AgentEnvironmentCapabilitiesSchema: z.ZodObject<{
984
986
  branching: z.ZodObject<{
985
987
  checkpoint: z.ZodBoolean;
986
988
  fork: z.ZodBoolean;
989
+ confidential: z.ZodOptional<z.ZodBoolean>;
987
990
  retrySafe: z.ZodOptional<z.ZodBoolean>;
988
991
  lookup: z.ZodOptional<z.ZodBoolean>;
989
992
  cleanup: z.ZodOptional<z.ZodBoolean>;
@@ -1084,6 +1087,8 @@ export interface CreateAgentEnvironmentInput {
1084
1087
  * The operation key names the request and the abort signal controls one
1085
1088
  * attempt, so neither belongs in the input identity. Every other field is
1086
1089
  * canonicalized with the shared RFC 8785 JSON representation.
1090
+ * A keyed create therefore accepts only canonical JSON material.
1091
+ * Unkeyed creates can retain opaque provider-native mapper values.
1087
1092
  * @internal
1088
1093
  */
1089
1094
  export declare function agentEnvironmentCreateInputDigest(input: CreateAgentEnvironmentInput): Sha256Digest;
@@ -1115,9 +1120,15 @@ export declare function replayedAgentEnvironmentView<T extends object>(environme
1115
1120
  * with the creation verdict the provider could prove. Every same-key call
1116
1121
  * after it, including one that awaited the same pending create, receives
1117
1122
  * {@link replayedAgentEnvironmentView} of that environment.
1123
+ *
1124
+ * A keyed create callback receives one immutable canonical JSON snapshot.
1125
+ * Its digest and provider therefore read identical request bytes even when
1126
+ * the caller mutates its original object after this function returns.
1127
+ * An unkeyed create retains its original input so opaque provider-native
1128
+ * mapper values retain their public identity.
1118
1129
  * @internal
1119
1130
  */
1120
- export declare function createAgentEnvironmentWithIdempotency<T extends object>(records: Map<string, AgentEnvironmentCreateIdempotencyRecord<T>>, input: CreateAgentEnvironmentInput, create: () => Promise<T>): Promise<T>;
1131
+ export declare function createAgentEnvironmentWithIdempotency<T extends object>(records: Map<string, AgentEnvironmentCreateIdempotencyRecord<T>>, input: CreateAgentEnvironmentInput, create: (input: CreateAgentEnvironmentInput) => Promise<T>): Promise<T>;
1121
1132
  export interface AgentEnvironmentProvider {
1122
1133
  readonly name: string;
1123
1134
  readonly exactProcess?: AgentExactProcessProvider;
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
- import { canonicalCandidateDigest } from "./agent-candidate-schema-common.js";
2
+ import { canonicalCandidateDigest, canonicalCandidateJson, } from "./agent-candidate-schema-common.js";
3
3
  import { InteractionCapabilitiesSchema, RequestedInteractionsSchema } from "./interaction.js";
4
4
  import { ContextTransferReceiptSchema, ContextTransferRequestSchema, NativeContextBoundaryProofSchema, NativeContextContinuationAcknowledgementSchema, NativeContextContinuationRequestSchema, nativeContextContinuationAcknowledgementMatches } from "./portable-context.js";
5
5
  import { AgentExactRunControlRefSchema, AgentRunControlRefSchema, CanonicalStreamEventSchema } from "./runtime-control.js";
6
6
  import { AgentProfileCapabilitiesSchema } from "./environment-profile-capabilities.js";
7
7
  import { boundedIdentifierSchema, boundedJsonRecordSchema, boundedJsonSchema, boundedStringSchema, CONTRACT_MAX_ARRAY_LENGTH } from "./contract-limits.js";
8
8
  import { InputPartSchema } from "./portable-context-shared.js";
9
+ import { deepFreeze } from "./deep-freeze.js";
9
10
  export const AgentTurnInputSchema = z.strictObject({
10
11
  prompt: boundedStringSchema.optional(),
11
12
  parts: z.array(InputPartSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
@@ -215,6 +216,7 @@ export const AgentEnvironmentCapabilitiesSchema = z
215
216
  branching: z.strictObject({
216
217
  checkpoint: z.boolean(),
217
218
  fork: z.boolean(),
219
+ confidential: z.boolean().optional(),
218
220
  retrySafe: z.boolean().optional(),
219
221
  lookup: z.boolean().optional(),
220
222
  cleanup: z.boolean().optional(),
@@ -422,6 +424,8 @@ export const AgentEnvironmentCapabilitiesSchema = z
422
424
  * The operation key names the request and the abort signal controls one
423
425
  * attempt, so neither belongs in the input identity. Every other field is
424
426
  * canonicalized with the shared RFC 8785 JSON representation.
427
+ * A keyed create therefore accepts only canonical JSON material.
428
+ * Unkeyed creates can retain opaque provider-native mapper values.
425
429
  * @internal
426
430
  */
427
431
  export function agentEnvironmentCreateInputDigest(input) {
@@ -459,14 +463,26 @@ export function replayedAgentEnvironmentView(environment) {
459
463
  * with the creation verdict the provider could prove. Every same-key call
460
464
  * after it, including one that awaited the same pending create, receives
461
465
  * {@link replayedAgentEnvironmentView} of that environment.
466
+ *
467
+ * A keyed create callback receives one immutable canonical JSON snapshot.
468
+ * Its digest and provider therefore read identical request bytes even when
469
+ * the caller mutates its original object after this function returns.
470
+ * An unkeyed create retains its original input so opaque provider-native
471
+ * mapper values retain their public identity.
462
472
  * @internal
463
473
  */
464
474
  export async function createAgentEnvironmentWithIdempotency(records, input, create) {
465
- input.signal?.throwIfAborted();
466
- const key = input.idempotencyKey;
475
+ const { idempotencyKey: key, signal, ...material } = input;
476
+ signal?.throwIfAborted();
467
477
  if (key === undefined)
468
- return create();
469
- const digest = agentEnvironmentCreateInputDigest(input);
478
+ return create(input);
479
+ const snapshot = deepFreeze(JSON.parse(canonicalCandidateJson(material)));
480
+ const acceptedInput = Object.freeze({
481
+ ...snapshot,
482
+ idempotencyKey: key,
483
+ ...(signal === undefined ? {} : { signal }),
484
+ });
485
+ const digest = agentEnvironmentCreateInputDigest(acceptedInput);
470
486
  const existing = records.get(key);
471
487
  if (existing !== undefined) {
472
488
  if (existing.digest !== digest) {
@@ -474,7 +490,7 @@ export async function createAgentEnvironmentWithIdempotency(records, input, crea
474
490
  }
475
491
  return replayedAgentEnvironmentView(existing.environment ?? (await existing.pending));
476
492
  }
477
- const pending = Promise.resolve().then(create);
493
+ const pending = Promise.resolve().then(() => create(acceptedInput));
478
494
  const record = {
479
495
  digest,
480
496
  pending,
@@ -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.2.0",
3
+ "version": "2.3.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -88,7 +88,7 @@
88
88
  "zod": "4.5.4"
89
89
  },
90
90
  "devDependencies": {
91
- "@types/node": "26.4.0",
91
+ "@types/node": "26.4.1",
92
92
  "@types/spdx-expression-parse": "4.0.0",
93
93
  "typescript": "7.0.2",
94
94
  "vitest": "4.1.11"