@tangle-network/agent-interface 0.44.0 → 0.46.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.
Files changed (91) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-candidate-code-schema.d.ts +1 -0
  3. package/dist/agent-candidate-execution-plan-schema.d.ts +8 -6
  4. package/dist/agent-candidate-profile-schema.d.ts +3 -2
  5. package/dist/agent-candidate-promotion-schema.d.ts +59 -42
  6. package/dist/agent-candidate-receipt-schema.d.ts +5 -4
  7. package/dist/agent-candidate-schema-common.js +1 -1
  8. package/dist/agent-candidate-schema.d.ts +4 -2
  9. package/dist/agent-execution-preparation.d.ts +27 -26
  10. package/dist/agent-profile.d.ts +10 -3
  11. package/dist/agent-profile.js +45 -22
  12. package/dist/backend-message.d.ts +14 -0
  13. package/dist/backend-message.js +1 -0
  14. package/dist/contract-limits.d.ts +26 -0
  15. package/dist/contract-limits.js +175 -0
  16. package/dist/environment-exact-process.d.ts +161 -0
  17. package/dist/environment-exact-process.js +1 -0
  18. package/dist/environment-profile-capabilities.d.ts +26 -0
  19. package/dist/environment-profile-capabilities.js +34 -0
  20. package/dist/environment-provider.d.ts +3 -730
  21. package/dist/environment-provider.js +3 -245
  22. package/dist/environment-requests.d.ts +70 -0
  23. package/dist/environment-requests.js +1 -0
  24. package/dist/environment-runtime.d.ts +834 -0
  25. package/dist/environment-runtime.js +228 -0
  26. package/dist/execution-types.d.ts +47 -0
  27. package/dist/execution-types.js +1 -0
  28. package/dist/harness-capabilities.js +5 -0
  29. package/dist/harness.d.ts +6 -1
  30. package/dist/harness.js +1 -0
  31. package/dist/host-services.d.ts +91 -0
  32. package/dist/host-services.js +1 -0
  33. package/dist/index.d.ts +10 -617
  34. package/dist/index.js +10 -125
  35. package/dist/interaction-answer-validation.d.ts +13 -0
  36. package/dist/interaction-answer-validation.js +149 -0
  37. package/dist/interaction-data.d.ts +22 -0
  38. package/dist/interaction-data.js +46 -0
  39. package/dist/interaction-envelope.d.ts +258 -0
  40. package/dist/interaction-envelope.js +245 -0
  41. package/dist/interaction-fields.d.ts +130 -0
  42. package/dist/interaction-fields.js +227 -0
  43. package/dist/interaction-permissions.d.ts +28 -0
  44. package/dist/interaction-permissions.js +57 -0
  45. package/dist/interaction-resolution-validation.d.ts +9 -0
  46. package/dist/interaction-resolution-validation.js +50 -0
  47. package/dist/interaction-response-validation.d.ts +17 -0
  48. package/dist/interaction-response-validation.js +102 -0
  49. package/dist/interaction.d.ts +6 -397
  50. package/dist/interaction.js +6 -603
  51. package/dist/mcp.d.ts +38 -0
  52. package/dist/mcp.js +1 -0
  53. package/dist/parts.d.ts +104 -0
  54. package/dist/parts.js +55 -0
  55. package/dist/portable-context-base.d.ts +82 -0
  56. package/dist/portable-context-base.js +63 -0
  57. package/dist/portable-context-continuation.d.ts +168 -0
  58. package/dist/portable-context-continuation.js +194 -0
  59. package/dist/portable-context-plan-request.d.ts +86 -0
  60. package/dist/portable-context-plan-request.js +102 -0
  61. package/dist/portable-context-plan.d.ts +229 -0
  62. package/dist/portable-context-plan.js +241 -0
  63. package/dist/portable-context-shared.d.ts +36 -0
  64. package/dist/portable-context-shared.js +46 -0
  65. package/dist/portable-context-transfer.d.ts +319 -0
  66. package/dist/portable-context-transfer.js +206 -0
  67. package/dist/portable-context.d.ts +5 -753
  68. package/dist/portable-context.js +5 -754
  69. package/dist/profile-diff.js +56 -47
  70. package/dist/profile-schema.d.ts +1 -0
  71. package/dist/provider-adapter.d.ts +45 -0
  72. package/dist/provider-adapter.js +1 -0
  73. package/dist/provider-config.d.ts +58 -0
  74. package/dist/provider-config.js +42 -0
  75. package/dist/runtime-control.d.ts +87 -10
  76. package/dist/runtime-control.js +159 -38
  77. package/dist/stream-events.d.ts +55 -0
  78. package/dist/stream-events.js +1 -0
  79. package/dist/workspace-branching-shared.d.ts +7 -0
  80. package/dist/workspace-branching-shared.js +20 -0
  81. package/dist/workspace-branching.d.ts +4 -254
  82. package/dist/workspace-branching.js +4 -400
  83. package/dist/workspace-checkpoint.d.ts +99 -0
  84. package/dist/workspace-checkpoint.js +169 -0
  85. package/dist/workspace-cleanup.d.ts +85 -0
  86. package/dist/workspace-cleanup.js +156 -0
  87. package/dist/workspace-confidentiality.d.ts +59 -0
  88. package/dist/workspace-confidentiality.js +123 -0
  89. package/dist/workspace-fork.d.ts +182 -0
  90. package/dist/workspace-fork.js +267 -0
  91. package/package.json +1 -1
@@ -221,7 +221,7 @@ function configRecordSchema(keySchema) {
221
221
  return z
222
222
  .record(keySchema, agentCandidateConfigValueSchema)
223
223
  .superRefine((config, ctx) => {
224
- for (const [name, value] of Object.entries(config)) {
224
+ for (const name of Object.keys(config)) {
225
225
  if (secretNamePattern.test(name)) {
226
226
  ctx.addIssue({
227
227
  code: "custom",
@@ -41,6 +41,7 @@ export declare const agentCandidateBundleSchema: z.ZodObject<{
41
41
  opencode: "opencode";
42
42
  "kimi-code": "kimi-code";
43
43
  pi: "pi";
44
+ prime: "prime";
44
45
  gemini: "gemini";
45
46
  hermes: "hermes";
46
47
  openclaw: "openclaw";
@@ -60,9 +61,9 @@ export declare const agentCandidateBundleSchema: z.ZodObject<{
60
61
  mcp: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./agent-candidate.js").AgentCandidateMcpServer, unknown, z.core.$ZodTypeInternals<import("./agent-candidate.js").AgentCandidateMcpServer, unknown>>>>;
61
62
  subagents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
62
63
  prompt: z.ZodOptional<z.ZodString>;
63
- permissions: z.ZodOptional<z.ZodType<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown, z.core.$ZodTypeInternals<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown>>>;
64
64
  tools: z.ZodOptional<z.ZodType<Record<string, boolean>, unknown, z.core.$ZodTypeInternals<Record<string, boolean>, unknown>>>;
65
65
  description: z.ZodOptional<z.ZodString>;
66
+ permissions: z.ZodOptional<z.ZodType<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown, z.core.$ZodTypeInternals<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown>>>;
66
67
  model: z.ZodOptional<z.ZodString>;
67
68
  maxSteps: z.ZodOptional<z.ZodNumber>;
68
69
  }, z.core.$strict>>>;
@@ -539,9 +540,9 @@ export declare const agentCandidateBundleSchema: z.ZodObject<{
539
540
  }, z.core.$strict>>>>;
540
541
  modes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
541
542
  prompt: z.ZodOptional<z.ZodString>;
542
- permissions: z.ZodOptional<z.ZodType<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown, z.core.$ZodTypeInternals<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown>>>;
543
543
  tools: z.ZodOptional<z.ZodType<Record<string, boolean>, unknown, z.core.$ZodTypeInternals<Record<string, boolean>, unknown>>>;
544
544
  description: z.ZodOptional<z.ZodString>;
545
+ permissions: z.ZodOptional<z.ZodType<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown, z.core.$ZodTypeInternals<Record<string, "allow" | "ask" | "deny" | Record<string, "allow" | "ask" | "deny">>, unknown>>>;
545
546
  model: z.ZodOptional<z.ZodString>;
546
547
  }, z.core.$strict>>>;
547
548
  confidential: z.ZodOptional<z.ZodObject<{
@@ -591,6 +592,7 @@ export declare const agentCandidateBundleSchema: z.ZodObject<{
591
592
  opencode: "opencode";
592
593
  "kimi-code": "kimi-code";
593
594
  pi: "pi";
595
+ prime: "prime";
594
596
  gemini: "gemini";
595
597
  hermes: "hermes";
596
598
  openclaw: "openclaw";
@@ -148,27 +148,27 @@ export type AgentExecutionPreparationValidationResult = {
148
148
  };
149
149
  export declare const agentExecutionPreparationAxisResultSchema: z.ZodObject<{
150
150
  axis: z.ZodEnum<{
151
- permissions: "permissions";
151
+ name: "name";
152
+ systemPrompt: "systemPrompt";
153
+ appendSystemPrompt: "appendSystemPrompt";
154
+ instructions: "instructions";
155
+ files: "files";
152
156
  tools: "tools";
157
+ skills: "skills";
158
+ commands: "commands";
159
+ description: "description";
160
+ version: "version";
161
+ tags: "tags";
162
+ harness: "harness";
163
+ permissions: "permissions";
153
164
  mcp: "mcp";
154
165
  connections: "connections";
155
166
  subagents: "subagents";
156
167
  hooks: "hooks";
157
168
  modes: "modes";
158
- metadata: "metadata";
159
- description: "description";
160
- name: "name";
161
- files: "files";
162
- version: "version";
163
- tags: "tags";
164
- harness: "harness";
165
169
  confidential: "confidential";
170
+ metadata: "metadata";
166
171
  extensions: "extensions";
167
- systemPrompt: "systemPrompt";
168
- appendSystemPrompt: "appendSystemPrompt";
169
- instructions: "instructions";
170
- skills: "skills";
171
- commands: "commands";
172
172
  modelDefault: "modelDefault";
173
173
  modelSmall: "modelSmall";
174
174
  modelProvider: "modelProvider";
@@ -234,6 +234,7 @@ export declare const agentExecutionPreparationReceiptSchema: z.ZodObject<{
234
234
  opencode: "opencode";
235
235
  "kimi-code": "kimi-code";
236
236
  pi: "pi";
237
+ prime: "prime";
237
238
  gemini: "gemini";
238
239
  hermes: "hermes";
239
240
  openclaw: "openclaw";
@@ -295,27 +296,27 @@ export declare const agentExecutionPreparationReceiptSchema: z.ZodObject<{
295
296
  }, z.core.$strict>;
296
297
  axisResults: z.ZodArray<z.ZodObject<{
297
298
  axis: z.ZodEnum<{
298
- permissions: "permissions";
299
+ name: "name";
300
+ systemPrompt: "systemPrompt";
301
+ appendSystemPrompt: "appendSystemPrompt";
302
+ instructions: "instructions";
303
+ files: "files";
299
304
  tools: "tools";
305
+ skills: "skills";
306
+ commands: "commands";
307
+ description: "description";
308
+ version: "version";
309
+ tags: "tags";
310
+ harness: "harness";
311
+ permissions: "permissions";
300
312
  mcp: "mcp";
301
313
  connections: "connections";
302
314
  subagents: "subagents";
303
315
  hooks: "hooks";
304
316
  modes: "modes";
305
- metadata: "metadata";
306
- description: "description";
307
- name: "name";
308
- files: "files";
309
- version: "version";
310
- tags: "tags";
311
- harness: "harness";
312
317
  confidential: "confidential";
318
+ metadata: "metadata";
313
319
  extensions: "extensions";
314
- systemPrompt: "systemPrompt";
315
- appendSystemPrompt: "appendSystemPrompt";
316
- instructions: "instructions";
317
- skills: "skills";
318
- commands: "commands";
319
320
  modelDefault: "modelDefault";
320
321
  modelSmall: "modelSmall";
321
322
  modelProvider: "modelProvider";
@@ -442,9 +442,16 @@ export interface AgentProfileValidationResult {
442
442
  /**
443
443
  * Merge two public AgentProfile values.
444
444
  *
445
- * Overlay fields win on conflicts. Additive fields compose instead: array-like
446
- * instruction sets are concatenated, and `prompt.appendSystemPrompt` values are
447
- * joined base-first with a blank line between them.
445
+ * Overlay fields win on conflicts, but only where they carry a value: an
446
+ * `undefined` entry reads as "not specified" and keeps the base value, because
447
+ * removal is the `remove` channel's job on {@link AgentProfileDiff}. Keys that
448
+ * resolve to nothing are omitted rather than written as `undefined` — RFC 8785
449
+ * canonicalization enumerates own keys, so a present-but-undefined key is a
450
+ * different document from an absent one.
451
+ *
452
+ * Additive fields compose instead of overwriting: array-like instruction sets
453
+ * are concatenated, and `prompt.appendSystemPrompt` values are joined
454
+ * base-first with a blank line between them.
448
455
  */
449
456
  export declare function mergeAgentProfiles(base: AgentProfile | undefined, overlay: AgentProfile | undefined): AgentProfile | undefined;
450
457
  export {};
@@ -23,10 +23,12 @@ export function defineInlineResource(name, content) {
23
23
  export function defineGitHubResource(path, options = {}) {
24
24
  return {
25
25
  kind: "github",
26
- repository: options.repository,
27
26
  path,
28
- ref: options.ref,
29
- name: options.name,
27
+ ...(options.repository === undefined
28
+ ? {}
29
+ : { repository: options.repository }),
30
+ ...(options.ref === undefined ? {} : { ref: options.ref }),
31
+ ...(options.name === undefined ? {} : { name: options.name }),
30
32
  };
31
33
  }
32
34
  /**
@@ -67,6 +69,29 @@ export function defineAgentProfileSecretRef(key, format) {
67
69
  export function defineAgentProfile(profile) {
68
70
  return profile;
69
71
  }
72
+ /**
73
+ * Layer objects the way a spread would, except that an `undefined` entry means
74
+ * "not specified" and is never written to the result.
75
+ *
76
+ * Profile identity is an RFC 8785 digest over the profile, and canonicalization
77
+ * enumerates own keys: `{ tools: undefined }` is a different document from `{}`,
78
+ * so writing a key with no value moves the digest of a profile whose content did
79
+ * not change, or is refused outright by the canonical JSON domain. Later sources
80
+ * therefore win only where they carry a value; removal is expressed through
81
+ * {@link AgentProfileDiff}'s `remove` channel, never by overlaying `undefined`.
82
+ */
83
+ function assignDefined(...sources) {
84
+ const merged = {};
85
+ for (const source of sources) {
86
+ if (source === undefined)
87
+ continue;
88
+ for (const [key, value] of Object.entries(source)) {
89
+ if (value !== undefined)
90
+ merged[key] = value;
91
+ }
92
+ }
93
+ return merged;
94
+ }
70
95
  function mergeStringArrays(base, overlay) {
71
96
  if (!base && !overlay)
72
97
  return undefined;
@@ -88,10 +113,7 @@ function mergeAppendedSystemPrompts(base, overlay) {
88
113
  function mergeRecord(base, overlay) {
89
114
  if (!base && !overlay)
90
115
  return undefined;
91
- return {
92
- ...(base ?? {}),
93
- ...(overlay ?? {}),
94
- };
116
+ return assignDefined(base, overlay);
95
117
  }
96
118
  function mergeOptionalArrays(base, overlay) {
97
119
  if (!base && !overlay)
@@ -101,36 +123,37 @@ function mergeOptionalArrays(base, overlay) {
101
123
  /**
102
124
  * Merge two public AgentProfile values.
103
125
  *
104
- * Overlay fields win on conflicts. Additive fields compose instead: array-like
105
- * instruction sets are concatenated, and `prompt.appendSystemPrompt` values are
106
- * joined base-first with a blank line between them.
126
+ * Overlay fields win on conflicts, but only where they carry a value: an
127
+ * `undefined` entry reads as "not specified" and keeps the base value, because
128
+ * removal is the `remove` channel's job on {@link AgentProfileDiff}. Keys that
129
+ * resolve to nothing are omitted rather than written as `undefined` — RFC 8785
130
+ * canonicalization enumerates own keys, so a present-but-undefined key is a
131
+ * different document from an absent one.
132
+ *
133
+ * Additive fields compose instead of overwriting: array-like instruction sets
134
+ * are concatenated, and `prompt.appendSystemPrompt` values are joined
135
+ * base-first with a blank line between them.
107
136
  */
108
137
  export function mergeAgentProfiles(base, overlay) {
109
138
  if (!base && !overlay)
110
139
  return undefined;
111
140
  const mergedPrompt = base?.prompt || overlay?.prompt
112
- ? {
113
- ...(base?.prompt ?? {}),
114
- ...(overlay?.prompt ?? {}),
141
+ ? assignDefined(base?.prompt, overlay?.prompt, {
115
142
  appendSystemPrompt: mergeAppendedSystemPrompts(base?.prompt?.appendSystemPrompt, overlay?.prompt?.appendSystemPrompt),
116
143
  instructions: mergeStringArrays(base?.prompt?.instructions, overlay?.prompt?.instructions),
117
- }
144
+ })
118
145
  : undefined;
119
146
  const mergedResources = base?.resources || overlay?.resources
120
- ? {
121
- ...(base?.resources ?? {}),
122
- ...(overlay?.resources ?? {}),
147
+ ? assignDefined(base?.resources, overlay?.resources, {
123
148
  files: mergeOptionalArrays(base?.resources?.files, overlay?.resources?.files),
124
149
  tools: mergeOptionalArrays(base?.resources?.tools, overlay?.resources?.tools),
125
150
  skills: mergeOptionalArrays(base?.resources?.skills, overlay?.resources?.skills),
126
151
  agents: mergeOptionalArrays(base?.resources?.agents, overlay?.resources?.agents),
127
152
  commands: mergeOptionalArrays(base?.resources?.commands, overlay?.resources?.commands),
128
153
  instructions: overlay?.resources?.instructions ?? base?.resources?.instructions,
129
- }
154
+ })
130
155
  : undefined;
131
- return {
132
- ...(base ?? {}),
133
- ...(overlay ?? {}),
156
+ return assignDefined(base, overlay, {
134
157
  prompt: mergedPrompt,
135
158
  permissions: mergeRecord(base?.permissions, overlay?.permissions),
136
159
  tools: mergeRecord(base?.tools, overlay?.tools),
@@ -142,5 +165,5 @@ export function mergeAgentProfiles(base, overlay) {
142
165
  modes: mergeRecord(base?.modes, overlay?.modes),
143
166
  metadata: mergeRecord(base?.metadata, overlay?.metadata),
144
167
  extensions: mergeRecord(base?.extensions, overlay?.extensions),
145
- };
168
+ });
146
169
  }
@@ -0,0 +1,14 @@
1
+ import type { InputPart, Part } from "./parts.js";
2
+ export type BackendMessage = {
3
+ id: string;
4
+ role: "user" | "assistant" | "system";
5
+ parts: Array<Part | InputPart | unknown>;
6
+ timestamp: string;
7
+ metadata?: Record<string, unknown>;
8
+ };
9
+ export type GetMessagesOptions = {
10
+ sessionId: string;
11
+ limit?: number;
12
+ offset?: number;
13
+ since?: number;
14
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ /** Limits applied before a value is hashed, copied, or sent to a provider. */
3
+ export declare const CONTRACT_MAX_STRING_LENGTH = 16384;
4
+ export declare const CONTRACT_MAX_IDENTIFIER_LENGTH = 512;
5
+ export declare const CONTRACT_MAX_ARRAY_LENGTH = 1024;
6
+ export declare const CONTRACT_MAX_MAP_ENTRIES = 256;
7
+ export declare const CONTRACT_MAX_JSON_DEPTH = 16;
8
+ export declare const CONTRACT_MAX_JSON_BYTES = 1048576;
9
+ export declare const CONTRACT_MAX_JSON_NODES = 8192;
10
+ export declare const boundedStringSchema: z.ZodString;
11
+ export declare const boundedIdentifierSchema: z.ZodString;
12
+ /**
13
+ * Validate JSON without asking Zod to recursively copy an attacker-sized
14
+ * value. This is intentionally iterative so depth and collection limits are
15
+ * checked before canonical serialization.
16
+ */
17
+ export declare function isBoundedJsonValue(value: unknown): boolean;
18
+ /** Validate digest input while matching JSON's omission of undefined object fields. */
19
+ export declare function isBoundedJsonMaterial(value: unknown): boolean;
20
+ export declare const boundedJsonSchema: z.ZodCustom<unknown, unknown>;
21
+ export declare const boundedJsonRecordSchema: z.ZodCustom<Record<string, unknown>, Record<string, unknown>>;
22
+ export declare function assertBoundedJson(value: unknown): void;
23
+ export declare function assertBoundedSerializedJson(value: string): void;
24
+ /** Copy arbitrary metadata into a map that cannot inherit prototype keys. */
25
+ export declare function nullPrototypeRecord<T>(value: Record<string, T>): Record<string, T>;
26
+ export declare function hasOwn(value: object, key: PropertyKey): boolean;
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ /** Limits applied before a value is hashed, copied, or sent to a provider. */
3
+ export const CONTRACT_MAX_STRING_LENGTH = 16_384;
4
+ export const CONTRACT_MAX_IDENTIFIER_LENGTH = 512;
5
+ export const CONTRACT_MAX_ARRAY_LENGTH = 1_024;
6
+ export const CONTRACT_MAX_MAP_ENTRIES = 256;
7
+ export const CONTRACT_MAX_JSON_DEPTH = 16;
8
+ export const CONTRACT_MAX_JSON_BYTES = 1_048_576;
9
+ export const CONTRACT_MAX_JSON_NODES = 8_192;
10
+ export const boundedStringSchema = z.string().max(CONTRACT_MAX_STRING_LENGTH);
11
+ export const boundedIdentifierSchema = boundedStringSchema
12
+ .min(1)
13
+ .max(CONTRACT_MAX_IDENTIFIER_LENGTH)
14
+ .refine((value) => value.trim() === value, "identifier cannot have outer whitespace");
15
+ /**
16
+ * Validate JSON without asking Zod to recursively copy an attacker-sized
17
+ * value. This is intentionally iterative so depth and collection limits are
18
+ * checked before canonical serialization.
19
+ */
20
+ export function isBoundedJsonValue(value) {
21
+ const pending = [{ value, depth: 0 }];
22
+ const ancestors = new Set();
23
+ let nodes = 0;
24
+ while (pending.length > 0) {
25
+ const item = pending.pop();
26
+ if (!item)
27
+ continue;
28
+ nodes += 1;
29
+ if (nodes > CONTRACT_MAX_JSON_NODES)
30
+ return false;
31
+ const current = item.value;
32
+ if (item.leave) {
33
+ ancestors.delete(current);
34
+ continue;
35
+ }
36
+ if (current === undefined)
37
+ return false;
38
+ if (current === null || typeof current === "boolean")
39
+ continue;
40
+ if (typeof current === "string") {
41
+ if (current.length > CONTRACT_MAX_STRING_LENGTH)
42
+ return false;
43
+ continue;
44
+ }
45
+ if (typeof current === "number") {
46
+ if (!Number.isFinite(current))
47
+ return false;
48
+ continue;
49
+ }
50
+ if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH) {
51
+ return false;
52
+ }
53
+ if (ancestors.has(current))
54
+ return false;
55
+ ancestors.add(current);
56
+ pending.push({ value: current, depth: item.depth, leave: true });
57
+ if (Array.isArray(current)) {
58
+ if (current.length > CONTRACT_MAX_ARRAY_LENGTH)
59
+ return false;
60
+ for (const entry of current) {
61
+ pending.push({ value: entry, depth: item.depth + 1 });
62
+ }
63
+ continue;
64
+ }
65
+ const prototype = Object.getPrototypeOf(current);
66
+ if (prototype !== Object.prototype && prototype !== null)
67
+ return false;
68
+ const keys = Object.keys(current);
69
+ if (keys.length > CONTRACT_MAX_MAP_ENTRIES)
70
+ return false;
71
+ for (const key of keys) {
72
+ if (key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
73
+ return false;
74
+ pending.push({
75
+ value: current[key],
76
+ depth: item.depth + 1,
77
+ });
78
+ }
79
+ }
80
+ return true;
81
+ }
82
+ /** Validate digest input while matching JSON's omission of undefined object fields. */
83
+ export function isBoundedJsonMaterial(value) {
84
+ const pending = [{ value, depth: 0 }];
85
+ const ancestors = new Set();
86
+ let nodes = 0;
87
+ while (pending.length > 0) {
88
+ const item = pending.pop();
89
+ if (!item)
90
+ continue;
91
+ if (item.leave) {
92
+ ancestors.delete(item.value);
93
+ continue;
94
+ }
95
+ if (item.value === undefined) {
96
+ if (item.omitUndefined === true)
97
+ continue;
98
+ return false;
99
+ }
100
+ nodes += 1;
101
+ if (nodes > CONTRACT_MAX_JSON_NODES)
102
+ return false;
103
+ const current = item.value;
104
+ if (current === null || typeof current === "boolean")
105
+ continue;
106
+ if (typeof current === "string") {
107
+ if (current.length > CONTRACT_MAX_STRING_LENGTH)
108
+ return false;
109
+ continue;
110
+ }
111
+ if (typeof current === "number") {
112
+ if (!Number.isFinite(current))
113
+ return false;
114
+ continue;
115
+ }
116
+ if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH) {
117
+ return false;
118
+ }
119
+ if (ancestors.has(current))
120
+ return false;
121
+ ancestors.add(current);
122
+ pending.push({ value: current, depth: item.depth, leave: true });
123
+ if (Array.isArray(current)) {
124
+ if (current.length > CONTRACT_MAX_ARRAY_LENGTH)
125
+ return false;
126
+ for (const entry of current) {
127
+ pending.push({ value: entry, depth: item.depth + 1 });
128
+ }
129
+ continue;
130
+ }
131
+ const prototype = Object.getPrototypeOf(current);
132
+ if (prototype !== Object.prototype && prototype !== null)
133
+ return false;
134
+ const keys = Object.keys(current);
135
+ if (keys.length > CONTRACT_MAX_MAP_ENTRIES)
136
+ return false;
137
+ for (const key of keys) {
138
+ if (key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
139
+ return false;
140
+ pending.push({
141
+ value: current[key],
142
+ depth: item.depth + 1,
143
+ omitUndefined: true,
144
+ });
145
+ }
146
+ }
147
+ return true;
148
+ }
149
+ export const boundedJsonSchema = z.custom(isBoundedJsonValue, {
150
+ message: "value exceeds the contract bounds or is not finite JSON",
151
+ });
152
+ export const boundedJsonRecordSchema = z.custom((value) => typeof value === "object" &&
153
+ value !== null &&
154
+ !Array.isArray(value) &&
155
+ isBoundedJsonValue(value), { message: "metadata exceeds the contract bounds or is not a JSON object" });
156
+ export function assertBoundedJson(value) {
157
+ if (!isBoundedJsonValue(value)) {
158
+ throw new Error("value exceeds the contract bounds or is not finite JSON");
159
+ }
160
+ }
161
+ export function assertBoundedSerializedJson(value) {
162
+ if (new TextEncoder().encode(value).byteLength > CONTRACT_MAX_JSON_BYTES) {
163
+ throw new Error("serialized contract material exceeds its byte bound");
164
+ }
165
+ }
166
+ /** Copy arbitrary metadata into a map that cannot inherit prototype keys. */
167
+ export function nullPrototypeRecord(value) {
168
+ const result = Object.create(null);
169
+ for (const key of Object.keys(value))
170
+ result[key] = value[key];
171
+ return result;
172
+ }
173
+ export function hasOwn(value, key) {
174
+ return Object.prototype.hasOwnProperty.call(value, key);
175
+ }
@@ -0,0 +1,161 @@
1
+ import type { AgentCandidateTermination } from "./agent-candidate.js";
2
+ export type AgentExactProcessEgressMode = "blocked" | "strict";
3
+ /**
4
+ * Outbound network policy for an exact process environment. `blocked` denies
5
+ * every protocol. `strict` permits only the named domains; direct-address,
6
+ * alternate-protocol, and cross-environment bypasses must fail.
7
+ */
8
+ export type AgentExactProcessEgressPolicy = {
9
+ mode: "blocked";
10
+ } | {
11
+ mode: "strict";
12
+ allowDomains: readonly string[];
13
+ };
14
+ /** Explicit portable limits for an exact process environment. */
15
+ export interface AgentExactProcessResources {
16
+ /** Positive CPU core count. */
17
+ cpu: number;
18
+ /** Positive integer mebibytes of memory. */
19
+ memoryMb: number;
20
+ /** Positive integer mebibytes of disk. */
21
+ diskMb: number;
22
+ }
23
+ /** Terminal or running state reported by an exact process host. */
24
+ export interface AgentExactProcessStatus {
25
+ pid: number;
26
+ running: boolean;
27
+ /** -1 while running; the exact process exit code after termination. */
28
+ exitCode: number;
29
+ exitSignal?: string;
30
+ /** Required after termination; absent only while running. */
31
+ termination?: AgentCandidateTermination;
32
+ }
33
+ /** Recoverable handle for one shell-free process. */
34
+ export interface AgentExactProcess {
35
+ readonly pid: number;
36
+ status(options?: {
37
+ signal?: AbortSignal;
38
+ }): Promise<AgentExactProcessStatus>;
39
+ wait(options?: {
40
+ signal?: AbortSignal;
41
+ }): Promise<AgentCandidateTermination>;
42
+ /** Force-stop the full process tree. Idempotent after the process exits. */
43
+ kill(options?: {
44
+ signal?: AbortSignal;
45
+ }): Promise<void>;
46
+ /** Each iteration replays buffered UTF-8 stdout, then continues until exit. */
47
+ stdout(options?: {
48
+ signal?: AbortSignal;
49
+ }): AsyncIterable<string>;
50
+ /** Each iteration replays buffered UTF-8 stderr, then continues until exit. */
51
+ stderr(options?: {
52
+ signal?: AbortSignal;
53
+ }): AsyncIterable<string>;
54
+ }
55
+ /** Shell-free launch whose environment replaces, rather than extends, ambient variables. */
56
+ export interface AgentExactProcessLaunch {
57
+ /** Absolute path unless {@link env} supplies an explicit `PATH`. */
58
+ executable: string;
59
+ args: readonly string[];
60
+ cwd: string;
61
+ env: Readonly<Record<string, string>>;
62
+ stdin?: string;
63
+ /** Positive integer milliseconds, or zero to disable the process timeout. */
64
+ timeoutMs: number;
65
+ }
66
+ export interface AgentExactProcessManager {
67
+ list(options?: {
68
+ signal?: AbortSignal;
69
+ }): Promise<AgentExactProcessStatus[]>;
70
+ get(pid: number, options?: {
71
+ signal?: AbortSignal;
72
+ }): Promise<AgentExactProcess | null>;
73
+ /**
74
+ * The abort signal is checked before and after spawn dispatch.
75
+ * If abort is observed after a process starts, the provider kills its tree
76
+ * before rejecting the spawn operation.
77
+ */
78
+ spawn(input: AgentExactProcessLaunch, options?: {
79
+ signal?: AbortSignal;
80
+ }): Promise<AgentExactProcess>;
81
+ }
82
+ /**
83
+ * Fresh environment with no provider-managed user workload.
84
+ *
85
+ * Authenticated provider control services may exist, but no customer workload
86
+ * ingress or provider-managed user process may exist. The launched process
87
+ * sees only its supplied environment variables, with no ambient or injected
88
+ * secrets.
89
+ */
90
+ export interface AgentExactProcessEnvironment {
91
+ readonly id: string;
92
+ readonly provider: string;
93
+ readonly metadata?: Record<string, unknown>;
94
+ readonly process: AgentExactProcessManager;
95
+ /**
96
+ * Write exact bytes to an absolute path with a POSIX mode from 0 through
97
+ * 07777.
98
+ *
99
+ * The abort signal is checked before and after the write.
100
+ * Once dispatched the write is not cancellable, so an abort may reject after
101
+ * the bytes have landed.
102
+ */
103
+ writeFile(path: string, bytes: Uint8Array, options: {
104
+ mode: number;
105
+ signal?: AbortSignal;
106
+ }): Promise<void>;
107
+ /**
108
+ * Read exact bytes, or fail before content is loaded when the file exceeds
109
+ * maxBytes. A file that grows after the size check is still rejected, but
110
+ * only after its content was transferred. Reads have no side effect, so the
111
+ * abort signal may reject at any point.
112
+ */
113
+ readFile(path: string, options: {
114
+ maxBytes: number;
115
+ signal?: AbortSignal;
116
+ }): Promise<Uint8Array>;
117
+ destroy(options?: {
118
+ signal?: AbortSignal;
119
+ }): Promise<void>;
120
+ }
121
+ export interface AgentExactProcessEnvironmentQuery {
122
+ /** Every supplied key/value must match persisted environment metadata exactly. */
123
+ metadata?: Record<string, unknown>;
124
+ providerOptions?: Record<string, unknown>;
125
+ /** Abort a paginated lookup before the next remote page or conversion. */
126
+ signal?: AbortSignal;
127
+ }
128
+ /** Input for a fresh environment with no provider-managed agent process. */
129
+ export interface CreateAgentExactProcessEnvironmentInput {
130
+ /** Provider-specific immutable image reference. */
131
+ image: string;
132
+ egress: AgentExactProcessEgressPolicy;
133
+ /** Positive integer milliseconds. */
134
+ maxLifetimeMs: number;
135
+ /** Positive integer milliseconds when supplied. */
136
+ provisionTimeoutMs?: number;
137
+ /** Required limits; exact execution never inherits provider defaults. */
138
+ resources: AgentExactProcessResources;
139
+ metadata: Record<string, unknown>;
140
+ idempotencyKey: string;
141
+ signal?: AbortSignal;
142
+ /** Provider-native fields may narrow, but never weaken, the isolation contract. */
143
+ providerOptions?: Record<string, unknown>;
144
+ }
145
+ /** Optional all-or-nothing exact process capability of an environment provider. */
146
+ export interface AgentExactProcessProvider {
147
+ /**
148
+ * Repeating the same idempotency key and input returns the same environment.
149
+ * Reusing the key with any different create input must fail.
150
+ * Unsupported egress modes must fail instead of weakening the policy.
151
+ */
152
+ create(input: CreateAgentExactProcessEnvironmentInput): Promise<AgentExactProcessEnvironment>;
153
+ /** Ordinary environments must return null. */
154
+ get(id: string, options?: {
155
+ signal?: AbortSignal;
156
+ }): Promise<AgentExactProcessEnvironment | null>;
157
+ /** Return every matching exact environment; providers own any native pagination. */
158
+ list(query?: AgentExactProcessEnvironmentQuery, options?: {
159
+ signal?: AbortSignal;
160
+ }): Promise<AgentExactProcessEnvironment[]>;
161
+ }
@@ -0,0 +1 @@
1
+ export {};