@tangle-network/agent-interface 0.31.0 → 0.33.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.
package/README.md CHANGED
@@ -62,6 +62,13 @@ const provider: AgentEnvironmentProvider = {
62
62
  };
63
63
  ```
64
64
 
65
+ ## Exact process environments
66
+
67
+ Providers may expose the optional `exactProcess` capability for isolated, reproducible process execution.
68
+ It is separate from agent-backed `create()` because it guarantees a fresh environment, immutable image identity, explicit resources, bounded exact-byte file reads, shell-free argv, replacement process environment, recoverable output and terminal reason, bounded network access, and collision-safe idempotent recovery without starting a provider-managed agent.
69
+ Higher-level runtimes can use this primitive for measured candidates without making candidate lifecycle part of the provider contract.
70
+ Providers must omit the capability unless every property is enforced on their real execution path.
71
+
65
72
  ## Frozen improvement candidates
66
73
 
67
74
  `AgentCandidateBundle` is the portable output of an improvement run: a recursively strict profile, an explicit disabled/no-op/changed code result, a shell-free launch, optional knowledge, isolated memory, ancestry, and spend.
@@ -174,6 +174,16 @@ export const agentCandidateExecutionSchema = z
174
174
  })
175
175
  .strict()
176
176
  .superRefine((execution, ctx) => {
177
+ const requiresPath = execution.launch.kind === "container-command"
178
+ ? !execution.launch.executable.startsWith("/")
179
+ : execution.launch.interpreter !== undefined;
180
+ if (requiresPath && !execution.env?.PATH?.value.trim()) {
181
+ ctx.addIssue({
182
+ code: "custom",
183
+ path: ["env", "PATH"],
184
+ message: "relative candidate executables require an explicit public PATH",
185
+ });
186
+ }
177
187
  if (execution.env?.TANGLE_CANDIDATE_TASK_PATH !== undefined) {
178
188
  ctx.addIssue({
179
189
  code: "custom",
@@ -320,6 +320,13 @@ export const agentCandidateExecutionPlanMaterialSchema = z
320
320
  })
321
321
  .strict()
322
322
  .superRefine((material, ctx) => {
323
+ if (!material.launch.executable.startsWith("/") && !material.launch.env.PATH?.value.trim()) {
324
+ ctx.addIssue({
325
+ code: "custom",
326
+ path: ["launch", "env", "PATH"],
327
+ message: "relative execution-plan executables require an explicit public PATH",
328
+ });
329
+ }
323
330
  const routeIds = material.model.routes.map((route) => route.kind === "mode" || route.kind === "subagent"
324
331
  ? `${route.kind}:${route.name}`
325
332
  : route.kind);
@@ -71,6 +71,10 @@ export declare function candidateFixture(): {
71
71
  kind: "public";
72
72
  value: string;
73
73
  };
74
+ PATH: {
75
+ kind: "public";
76
+ value: string;
77
+ };
74
78
  };
75
79
  environment: {
76
80
  kind: "pinned-container";
@@ -62,6 +62,7 @@ export function candidateFixture() {
62
62
  cwd: { workspace: "candidate", path: "." },
63
63
  env: {
64
64
  NODE_ENV: { kind: "public", value: "production" },
65
+ PATH: { kind: "public", value: "/usr/local/bin:/usr/bin:/bin" },
65
66
  },
66
67
  environment: {
67
68
  kind: "pinned-container",
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+ import type { Sha256Digest } from "./agent-candidate.js";
3
+ export type CertifiedContextKind = "prompt" | "skill" | "instructions";
4
+ export type CertifiedContextDelivery = {
5
+ readonly kind: "inline";
6
+ readonly content: string;
7
+ } | {
8
+ readonly kind: "file";
9
+ readonly path: string;
10
+ readonly content: string;
11
+ };
12
+ export interface CertifiedContextProvenance {
13
+ /** SHA-256 of the entry id, kind, name, and delivery. */
14
+ readonly contentHash: Sha256Digest;
15
+ /** Positive release number, or null when the source has no released version. */
16
+ readonly version: number | null;
17
+ readonly promotedAt: string;
18
+ }
19
+ export interface CertifiedContextEntry {
20
+ readonly id: string;
21
+ readonly kind: CertifiedContextKind;
22
+ readonly name: string;
23
+ readonly delivery: CertifiedContextDelivery;
24
+ readonly provenance: CertifiedContextProvenance;
25
+ }
26
+ /**
27
+ * Tenant-bound context delivered by Intelligence.
28
+ *
29
+ * This contract intentionally excludes tools, credentials, executable files,
30
+ * profile patches, MCP servers, and arbitrary network requests.
31
+ */
32
+ export interface CertifiedContext {
33
+ readonly tenantId: string;
34
+ readonly target: string;
35
+ readonly state: "active" | "revoked";
36
+ /** Monotonic decimal revision for this tenant and target. */
37
+ readonly revision: string;
38
+ readonly generatedAt: string;
39
+ readonly expiresAt: string;
40
+ readonly entries: readonly CertifiedContextEntry[];
41
+ /** SHA-256 of tenantId, target, state, revision, and entries. */
42
+ readonly contentHash: Sha256Digest;
43
+ }
44
+ export declare const certifiedContextDeliverySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
45
+ kind: z.ZodLiteral<"inline">;
46
+ content: z.ZodString;
47
+ }, z.core.$strict>, z.ZodObject<{
48
+ kind: z.ZodLiteral<"file">;
49
+ path: z.ZodString;
50
+ content: z.ZodString;
51
+ }, z.core.$strict>], "kind">;
52
+ export declare const certifiedContextProvenanceSchema: z.ZodObject<{
53
+ contentHash: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
54
+ version: z.ZodNullable<z.ZodNumber>;
55
+ promotedAt: z.ZodISODateTime;
56
+ }, z.core.$strict>;
57
+ /** Compute the exact digest stored in an entry's provenance. */
58
+ export declare function certifiedContextEntryContentHash(entry: Pick<CertifiedContextEntry, "id" | "kind" | "name" | "delivery">): Sha256Digest;
59
+ export declare const certifiedContextEntrySchema: z.ZodObject<{
60
+ id: z.ZodString;
61
+ kind: z.ZodEnum<{
62
+ prompt: "prompt";
63
+ instructions: "instructions";
64
+ skill: "skill";
65
+ }>;
66
+ name: z.ZodString;
67
+ delivery: z.ZodDiscriminatedUnion<[z.ZodObject<{
68
+ kind: z.ZodLiteral<"inline">;
69
+ content: z.ZodString;
70
+ }, z.core.$strict>, z.ZodObject<{
71
+ kind: z.ZodLiteral<"file">;
72
+ path: z.ZodString;
73
+ content: z.ZodString;
74
+ }, z.core.$strict>], "kind">;
75
+ provenance: z.ZodObject<{
76
+ contentHash: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
77
+ version: z.ZodNullable<z.ZodNumber>;
78
+ promotedAt: z.ZodISODateTime;
79
+ }, z.core.$strict>;
80
+ }, z.core.$strict>;
81
+ /** Compute the stable hash for one context revision. */
82
+ export declare function certifiedContextContentHash(context: Pick<CertifiedContext, "tenantId" | "target" | "state" | "revision" | "entries">): Sha256Digest;
83
+ export declare const certifiedContextSchema: z.ZodObject<{
84
+ tenantId: z.ZodString;
85
+ target: z.ZodString;
86
+ state: z.ZodEnum<{
87
+ active: "active";
88
+ revoked: "revoked";
89
+ }>;
90
+ revision: z.ZodString;
91
+ generatedAt: z.ZodISODateTime;
92
+ expiresAt: z.ZodISODateTime;
93
+ entries: z.ZodArray<z.ZodObject<{
94
+ id: z.ZodString;
95
+ kind: z.ZodEnum<{
96
+ prompt: "prompt";
97
+ instructions: "instructions";
98
+ skill: "skill";
99
+ }>;
100
+ name: z.ZodString;
101
+ delivery: z.ZodDiscriminatedUnion<[z.ZodObject<{
102
+ kind: z.ZodLiteral<"inline">;
103
+ content: z.ZodString;
104
+ }, z.core.$strict>, z.ZodObject<{
105
+ kind: z.ZodLiteral<"file">;
106
+ path: z.ZodString;
107
+ content: z.ZodString;
108
+ }, z.core.$strict>], "kind">;
109
+ provenance: z.ZodObject<{
110
+ contentHash: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
111
+ version: z.ZodNullable<z.ZodNumber>;
112
+ promotedAt: z.ZodISODateTime;
113
+ }, z.core.$strict>;
114
+ }, z.core.$strict>>;
115
+ contentHash: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
116
+ }, z.core.$strict>;
117
+ /** Parse, clone, and recursively freeze one untrusted context response. */
118
+ export declare function parseCertifiedContext(value: unknown): CertifiedContext;
@@ -0,0 +1,228 @@
1
+ import { z } from "zod";
2
+ import { canonicalCandidateDigest, isSafeRelativePath, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
3
+ const MAX_INLINE_CONTEXT_BYTES = 65_536;
4
+ const MAX_TOTAL_INLINE_CONTEXT_BYTES = 131_072;
5
+ const MAX_FILE_CONTEXT_BYTES = 1_048_576;
6
+ const MAX_CERTIFIED_CONTEXT_BYTES = 16_777_216;
7
+ const MAX_CERTIFIED_CONTEXT_LIFETIME_MS = 900_000;
8
+ const revisionPattern = /^(0|[1-9]\d{0,18})$/;
9
+ const nonBlankStringSchema = z
10
+ .string()
11
+ .refine((value) => value.trim().length > 0, "value cannot be blank");
12
+ const identifierSchema = nonBlankStringSchema.max(256);
13
+ const relativePathSchema = nonBlankStringSchema
14
+ .max(1_024)
15
+ .refine((value) => isSafeRelativePath(value, false), "value must be a canonical relative path");
16
+ const inlineContentSchema = z
17
+ .string()
18
+ .max(MAX_INLINE_CONTEXT_BYTES)
19
+ .refine((value) => new TextEncoder().encode(value).byteLength <= MAX_INLINE_CONTEXT_BYTES, `inline content exceeds ${MAX_INLINE_CONTEXT_BYTES} UTF-8 bytes`);
20
+ const fileContentSchema = z
21
+ .string()
22
+ .max(MAX_FILE_CONTEXT_BYTES)
23
+ .refine((value) => new TextEncoder().encode(value).byteLength <= MAX_FILE_CONTEXT_BYTES, `file content exceeds ${MAX_FILE_CONTEXT_BYTES} UTF-8 bytes`);
24
+ export const certifiedContextDeliverySchema = z.discriminatedUnion("kind", [
25
+ z.strictObject({
26
+ kind: z.literal("inline"),
27
+ content: inlineContentSchema,
28
+ }),
29
+ z.strictObject({
30
+ kind: z.literal("file"),
31
+ path: relativePathSchema,
32
+ content: fileContentSchema,
33
+ }),
34
+ ]);
35
+ export const certifiedContextProvenanceSchema = z.strictObject({
36
+ contentHash: sha256DigestSchema,
37
+ version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable(),
38
+ promotedAt: z.iso.datetime(),
39
+ });
40
+ function jsonMaterial(value) {
41
+ const serialized = JSON.stringify(value);
42
+ if (serialized === undefined) {
43
+ throw new Error("certified context material must be JSON serializable");
44
+ }
45
+ return JSON.parse(serialized);
46
+ }
47
+ /** Compute the exact digest stored in an entry's provenance. */
48
+ export function certifiedContextEntryContentHash(entry) {
49
+ return canonicalCandidateDigest(jsonMaterial({
50
+ id: entry.id,
51
+ kind: entry.kind,
52
+ name: entry.name,
53
+ delivery: entry.delivery,
54
+ }));
55
+ }
56
+ export const certifiedContextEntrySchema = z
57
+ .strictObject({
58
+ id: identifierSchema,
59
+ kind: z.enum(["prompt", "skill", "instructions"]),
60
+ name: identifierSchema,
61
+ delivery: certifiedContextDeliverySchema,
62
+ provenance: certifiedContextProvenanceSchema,
63
+ })
64
+ .superRefine((entry, context) => {
65
+ if (entry.kind === "skill" && entry.delivery.kind !== "file") {
66
+ context.addIssue({
67
+ code: "custom",
68
+ path: ["delivery", "kind"],
69
+ message: "skills must be delivered as files",
70
+ });
71
+ }
72
+ if (entry.kind !== "skill" && entry.delivery.kind !== "inline") {
73
+ context.addIssue({
74
+ code: "custom",
75
+ path: ["delivery", "kind"],
76
+ message: "prompts and instructions must be delivered inline",
77
+ });
78
+ }
79
+ if (entry.delivery.kind === "inline" &&
80
+ entry.delivery.content.trim().length === 0) {
81
+ context.addIssue({
82
+ code: "custom",
83
+ path: ["delivery", "content"],
84
+ message: "inline context cannot be blank",
85
+ });
86
+ }
87
+ if (entry.provenance.contentHash !== certifiedContextEntryContentHash(entry)) {
88
+ context.addIssue({
89
+ code: "custom",
90
+ path: ["provenance", "contentHash"],
91
+ message: "entry content hash does not match the delivered context",
92
+ });
93
+ }
94
+ });
95
+ /** Compute the stable hash for one context revision. */
96
+ export function certifiedContextContentHash(context) {
97
+ return canonicalCandidateDigest(jsonMaterial(context));
98
+ }
99
+ export const certifiedContextSchema = z
100
+ .strictObject({
101
+ tenantId: identifierSchema,
102
+ target: identifierSchema,
103
+ state: z.enum(["active", "revoked"]),
104
+ revision: z
105
+ .string()
106
+ .regex(revisionPattern)
107
+ .refine((value) => !revisionPattern.test(value) ||
108
+ BigInt(value) <= 9223372036854775807n, "revision exceeds signed 64-bit range"),
109
+ generatedAt: z.iso.datetime(),
110
+ expiresAt: z.iso.datetime(),
111
+ entries: z.array(certifiedContextEntrySchema).max(128),
112
+ contentHash: sha256DigestSchema,
113
+ })
114
+ .superRefine((context, refinement) => {
115
+ const generatedAt = Date.parse(context.generatedAt);
116
+ const expiresAt = Date.parse(context.expiresAt);
117
+ if (expiresAt <= generatedAt) {
118
+ refinement.addIssue({
119
+ code: "custom",
120
+ path: ["expiresAt"],
121
+ message: "expiresAt must be after generatedAt",
122
+ });
123
+ }
124
+ else if (expiresAt - generatedAt >
125
+ MAX_CERTIFIED_CONTEXT_LIFETIME_MS) {
126
+ refinement.addIssue({
127
+ code: "custom",
128
+ path: ["expiresAt"],
129
+ message: "certified context cannot live longer than 15 minutes",
130
+ });
131
+ }
132
+ const serialized = JSON.stringify(context);
133
+ if (new TextEncoder().encode(serialized).byteLength >
134
+ MAX_CERTIFIED_CONTEXT_BYTES) {
135
+ refinement.addIssue({
136
+ code: "too_big",
137
+ maximum: MAX_CERTIFIED_CONTEXT_BYTES,
138
+ origin: "string",
139
+ inclusive: true,
140
+ message: `serialized context exceeds ${MAX_CERTIFIED_CONTEXT_BYTES} UTF-8 bytes`,
141
+ });
142
+ }
143
+ const ids = new Set();
144
+ const filePaths = new Set();
145
+ let inlineBytes = 0;
146
+ for (const [index, entry] of context.entries.entries()) {
147
+ if (ids.has(entry.id)) {
148
+ refinement.addIssue({
149
+ code: "custom",
150
+ path: ["entries", index, "id"],
151
+ message: `duplicate context id: ${entry.id}`,
152
+ });
153
+ }
154
+ ids.add(entry.id);
155
+ if (entry.delivery.kind === "file") {
156
+ if (filePaths.has(entry.delivery.path)) {
157
+ refinement.addIssue({
158
+ code: "custom",
159
+ path: ["entries", index, "delivery", "path"],
160
+ message: `duplicate file path: ${entry.delivery.path}`,
161
+ });
162
+ }
163
+ filePaths.add(entry.delivery.path);
164
+ }
165
+ else {
166
+ inlineBytes += new TextEncoder().encode(entry.delivery.content).byteLength;
167
+ }
168
+ if (Date.parse(entry.provenance.promotedAt) > generatedAt) {
169
+ refinement.addIssue({
170
+ code: "custom",
171
+ path: ["entries", index, "provenance", "promotedAt"],
172
+ message: "context cannot be promoted after bundle generation",
173
+ });
174
+ }
175
+ }
176
+ if (inlineBytes > MAX_TOTAL_INLINE_CONTEXT_BYTES) {
177
+ refinement.addIssue({
178
+ code: "custom",
179
+ path: ["entries"],
180
+ message: `inline context exceeds ${MAX_TOTAL_INLINE_CONTEXT_BYTES} UTF-8 bytes`,
181
+ });
182
+ }
183
+ if ((context.state === "active" && context.entries.length === 0) ||
184
+ (context.state === "revoked" && context.entries.length !== 0)) {
185
+ refinement.addIssue({
186
+ code: "custom",
187
+ path: ["entries"],
188
+ message: "active context requires entries and revoked context requires none",
189
+ });
190
+ }
191
+ const material = {
192
+ tenantId: context.tenantId,
193
+ target: context.target,
194
+ state: context.state,
195
+ revision: context.revision,
196
+ entries: context.entries,
197
+ };
198
+ if (context.contentHash !== certifiedContextContentHash(material)) {
199
+ refinement.addIssue({
200
+ code: "custom",
201
+ path: ["contentHash"],
202
+ message: "context content hash does not match the delivered context",
203
+ });
204
+ }
205
+ });
206
+ function deepFreeze(value) {
207
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
208
+ for (const child of Object.values(value)) {
209
+ deepFreeze(child);
210
+ }
211
+ Object.freeze(value);
212
+ }
213
+ return value;
214
+ }
215
+ const _certifiedContextDeliverySchemaMatches = true;
216
+ const _certifiedContextProvenanceSchemaMatches = true;
217
+ const _certifiedContextEntrySchemaMatches = true;
218
+ const _certifiedContextSchemaMatches = true;
219
+ void [
220
+ _certifiedContextDeliverySchemaMatches,
221
+ _certifiedContextProvenanceSchemaMatches,
222
+ _certifiedContextEntrySchemaMatches,
223
+ _certifiedContextSchemaMatches,
224
+ ];
225
+ /** Parse, clone, and recursively freeze one untrusted context response. */
226
+ export function parseCertifiedContext(value) {
227
+ return deepFreeze(certifiedContextSchema.parse(value));
228
+ }
@@ -1,4 +1,5 @@
1
1
  import type { AgentProfile, AgentProfileCapabilities, AgentProfileValidationResult } from "./agent-profile.js";
2
+ import type { AgentCandidateTermination } from "./agent-candidate.js";
2
3
  import type { InputPart, StreamEvent, TokenUsage } from "./index.js";
3
4
  /** Portable profile reference: inline profile or provider catalog id. */
4
5
  export type AgentProfileRef = AgentProfile | string;
@@ -48,6 +49,128 @@ export interface ExecResult {
48
49
  stdout: string;
49
50
  stderr: string;
50
51
  }
52
+ export type AgentExactProcessEgressMode = "blocked" | "strict";
53
+ /**
54
+ * Outbound network policy for an exact process environment. `blocked` denies
55
+ * every protocol. `strict` permits only the named domains; direct-address,
56
+ * alternate-protocol, and cross-environment bypasses must fail.
57
+ */
58
+ export type AgentExactProcessEgressPolicy = {
59
+ mode: "blocked";
60
+ } | {
61
+ mode: "strict";
62
+ allowDomains: readonly string[];
63
+ };
64
+ /** Explicit portable limits for an exact process environment. */
65
+ export interface AgentExactProcessResources {
66
+ /** Positive CPU core count. */
67
+ cpu: number;
68
+ /** Positive integer mebibytes of memory. */
69
+ memoryMb: number;
70
+ /** Positive integer mebibytes of disk. */
71
+ diskMb: number;
72
+ }
73
+ /** Terminal or running state reported by an exact process host. */
74
+ export interface AgentExactProcessStatus {
75
+ pid: number;
76
+ running: boolean;
77
+ /** -1 while running; the exact process exit code after termination. */
78
+ exitCode: number;
79
+ exitSignal?: string;
80
+ /** Required after termination; absent only while running. */
81
+ termination?: AgentCandidateTermination;
82
+ }
83
+ /** Recoverable handle for one shell-free process. */
84
+ export interface AgentExactProcess {
85
+ readonly pid: number;
86
+ status(): Promise<AgentExactProcessStatus>;
87
+ wait(): Promise<AgentCandidateTermination>;
88
+ /** Force-stop the full process tree. Idempotent after the process exits. */
89
+ kill(): Promise<void>;
90
+ /** Each iteration replays buffered UTF-8 stdout, then continues until exit. */
91
+ stdout(): AsyncIterable<string>;
92
+ /** Each iteration replays buffered UTF-8 stderr, then continues until exit. */
93
+ stderr(): AsyncIterable<string>;
94
+ }
95
+ /** Shell-free launch whose environment replaces, rather than extends, ambient variables. */
96
+ export interface AgentExactProcessLaunch {
97
+ /** Absolute path unless {@link env} supplies an explicit `PATH`. */
98
+ executable: string;
99
+ args: readonly string[];
100
+ cwd: string;
101
+ env: Readonly<Record<string, string>>;
102
+ stdin?: string;
103
+ /** Positive integer milliseconds, or zero to disable the process timeout. */
104
+ timeoutMs: number;
105
+ }
106
+ export interface AgentExactProcessManager {
107
+ list(): Promise<AgentExactProcessStatus[]>;
108
+ get(pid: number): Promise<AgentExactProcess | null>;
109
+ /** Providers must honor the abort signal when supplied. */
110
+ spawn(input: AgentExactProcessLaunch, options?: {
111
+ signal?: AbortSignal;
112
+ }): Promise<AgentExactProcess>;
113
+ }
114
+ /**
115
+ * Fresh environment with no provider-managed user workload.
116
+ *
117
+ * Authenticated provider control services may exist, but no customer workload
118
+ * ingress or provider-managed user process may exist. The launched process
119
+ * sees only its supplied environment variables, with no ambient or injected
120
+ * secrets.
121
+ */
122
+ export interface AgentExactProcessEnvironment {
123
+ readonly id: string;
124
+ readonly provider: string;
125
+ readonly metadata?: Record<string, unknown>;
126
+ readonly process: AgentExactProcessManager;
127
+ /** Write exact bytes to an absolute path with a POSIX mode from 0 through 07777. Providers must honor the abort signal when supplied. */
128
+ writeFile(path: string, bytes: Uint8Array, options: {
129
+ mode: number;
130
+ signal?: AbortSignal;
131
+ }): Promise<void>;
132
+ /** Read exact bytes or fail before content is loaded when the file exceeds maxBytes. */
133
+ readFile(path: string, options: {
134
+ maxBytes: number;
135
+ signal?: AbortSignal;
136
+ }): Promise<Uint8Array>;
137
+ destroy(): Promise<void>;
138
+ }
139
+ export interface AgentExactProcessEnvironmentQuery {
140
+ /** Every supplied key/value must match persisted environment metadata exactly. */
141
+ metadata?: Record<string, unknown>;
142
+ providerOptions?: Record<string, unknown>;
143
+ }
144
+ /** Input for a fresh environment with no provider-managed agent process. */
145
+ export interface CreateAgentExactProcessEnvironmentInput {
146
+ /** Provider-specific immutable image reference. */
147
+ image: string;
148
+ egress: AgentExactProcessEgressPolicy;
149
+ /** Positive integer milliseconds. */
150
+ maxLifetimeMs: number;
151
+ /** Positive integer milliseconds when supplied. */
152
+ provisionTimeoutMs?: number;
153
+ /** Required limits; exact execution never inherits provider defaults. */
154
+ resources: AgentExactProcessResources;
155
+ metadata: Record<string, unknown>;
156
+ idempotencyKey: string;
157
+ signal?: AbortSignal;
158
+ /** Provider-native fields may narrow, but never weaken, the isolation contract. */
159
+ providerOptions?: Record<string, unknown>;
160
+ }
161
+ /** Optional all-or-nothing exact process capability of an environment provider. */
162
+ export interface AgentExactProcessProvider {
163
+ /**
164
+ * Repeating the same idempotency key and input returns the same environment.
165
+ * Reusing the key with any different create input must fail.
166
+ * Unsupported egress modes must fail instead of weakening the policy.
167
+ */
168
+ create(input: CreateAgentExactProcessEnvironmentInput): Promise<AgentExactProcessEnvironment>;
169
+ /** Ordinary environments must return null. */
170
+ get(id: string): Promise<AgentExactProcessEnvironment | null>;
171
+ /** Return every matching exact environment; providers own any native pagination. */
172
+ list(query?: AgentExactProcessEnvironmentQuery): Promise<AgentExactProcessEnvironment[]>;
173
+ }
51
174
  export interface CheckpointRequest {
52
175
  name?: string;
53
176
  metadata?: Record<string, unknown>;
@@ -165,6 +288,10 @@ export interface AgentEnvironmentCapabilities {
165
288
  placement: boolean;
166
289
  usage: boolean;
167
290
  confidential: boolean;
291
+ /** Present only when {@link AgentEnvironmentProvider.exactProcess} is implemented. */
292
+ exactProcess?: {
293
+ egress: readonly AgentExactProcessEgressMode[];
294
+ };
168
295
  }
169
296
  export interface CreateAgentEnvironmentInput {
170
297
  profile: AgentProfileRef;
@@ -182,6 +309,7 @@ export interface CreateAgentEnvironmentInput {
182
309
  }
183
310
  export interface AgentEnvironmentProvider {
184
311
  readonly name: string;
312
+ readonly exactProcess?: AgentExactProcessProvider;
185
313
  capabilities(): AgentEnvironmentCapabilities | Promise<AgentEnvironmentCapabilities>;
186
314
  validateProfile?(profile: AgentProfileRef): AgentProfileValidationResult | Promise<AgentProfileValidationResult>;
187
315
  create(input: CreateAgentEnvironmentInput): Promise<AgentEnvironment>;
package/dist/index.d.ts CHANGED
@@ -615,6 +615,7 @@ export * from "./agent-candidate.js";
615
615
  export * from "./agent-candidate-schema.js";
616
616
  export * from "./agent-candidate-promotion-schema.js";
617
617
  export * from "./agent-profile.js";
618
+ export * from "./certified-context.js";
618
619
  export * from "./profile-diff.js";
619
620
  export * from "./harness.js";
620
621
  export * from "./harness-capabilities.js";
package/dist/index.js CHANGED
@@ -128,6 +128,7 @@ export * from "./agent-candidate.js";
128
128
  export * from "./agent-candidate-schema.js";
129
129
  export * from "./agent-candidate-promotion-schema.js";
130
130
  export * from "./agent-profile.js";
131
+ export * from "./certified-context.js";
131
132
  export * from "./profile-diff.js";
132
133
  export * from "./harness.js";
133
134
  export * from "./harness-capabilities.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",