@tangle-network/agent-provider-tangle 0.5.0 → 0.6.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 (41) hide show
  1. package/dist/exact-process.d.ts +1 -4
  2. package/dist/exact-process.js +123 -206
  3. package/dist/index.d.ts +4 -126
  4. package/dist/index.js +3 -687
  5. package/dist/tangle-capabilities.d.ts +39 -0
  6. package/dist/tangle-capabilities.js +140 -0
  7. package/dist/tangle-contract-safety.d.ts +19 -0
  8. package/dist/tangle-contract-safety.js +240 -0
  9. package/dist/tangle-create-options.d.ts +9 -0
  10. package/dist/tangle-create-options.js +243 -0
  11. package/dist/tangle-environment-control.d.ts +6 -0
  12. package/dist/tangle-environment-control.js +50 -0
  13. package/dist/tangle-environment-dispatch.d.ts +3 -0
  14. package/dist/tangle-environment-dispatch.js +60 -0
  15. package/dist/tangle-environment-session.d.ts +4 -0
  16. package/dist/tangle-environment-session.js +156 -0
  17. package/dist/tangle-environment-validation.d.ts +11 -0
  18. package/dist/tangle-environment-validation.js +63 -0
  19. package/dist/tangle-environment-values.d.ts +8 -0
  20. package/dist/tangle-environment-values.js +84 -0
  21. package/dist/tangle-environment.d.ts +3 -0
  22. package/dist/tangle-environment.js +216 -0
  23. package/dist/tangle-events.d.ts +6 -0
  24. package/dist/tangle-events.js +111 -0
  25. package/dist/tangle-exact-process-environment.d.ts +3 -0
  26. package/dist/tangle-exact-process-environment.js +184 -0
  27. package/dist/tangle-exact-process-runtime.d.ts +5 -0
  28. package/dist/tangle-exact-process-runtime.js +150 -0
  29. package/dist/tangle-exact-process-validation.d.ts +17 -0
  30. package/dist/tangle-exact-process-validation.js +123 -0
  31. package/dist/tangle-prompt.d.ts +24 -0
  32. package/dist/tangle-prompt.js +166 -0
  33. package/dist/tangle-provider.d.ts +3 -0
  34. package/dist/tangle-provider.js +192 -0
  35. package/dist/tangle-result-values.d.ts +5 -0
  36. package/dist/tangle-result-values.js +94 -0
  37. package/dist/tangle-session-control.d.ts +7 -0
  38. package/dist/tangle-session-control.js +89 -0
  39. package/dist/tangle-types.d.ts +141 -0
  40. package/dist/tangle-types.js +1 -0
  41. package/package.json +39 -3
@@ -1,8 +1,5 @@
1
1
  import type { AgentExactProcessProvider } from "@tangle-network/agent-interface/environment-provider";
2
- import type { SandboxClientLike } from "./index.js";
3
- export interface TangleExactProcessOptions {
4
- teamId?: string;
5
- }
2
+ import type { SandboxClientLike, TangleExactProcessOptions } from "./tangle-types.js";
6
3
  export declare function createTangleExactProcessProvider(input: {
7
4
  client: SandboxClientLike;
8
5
  options: TangleExactProcessOptions;
@@ -1,11 +1,14 @@
1
- import { isAbsolute } from "node:path";
2
- import { isDeepStrictEqual } from "node:util";
1
+ import { assertBoundedJson, attachCleanupHandle, awaitWithSignal, boundedIdentifier, boundedString, exactProcessRequestDigest, isBoundedJson, MAX_LIST_RESULTS, } from "./tangle-contract-safety.js";
2
+ import { sandboxInstanceAsExactProcessEnvironment } from "./tangle-exact-process-environment.js";
3
+ import { assertExactProcessSandbox, assertSupportedProviderOptions, assertUnreservedMetadata, EXACT_PROCESS_METADATA_KEY, isExactProcessRequestConflict, isExactProcessSandbox, metadataMatches, assertSignalOptions, } from "./tangle-exact-process-validation.js";
3
4
  const IMMUTABLE_TANGLE_IMAGE = /^(?:sha256:[a-f0-9]{64}|\S+@sha256:[a-f0-9]{64})$/i;
4
- const EXACT_PROCESS_METADATA_KEY = "tangle.exactProcess";
5
5
  const LIST_PAGE_SIZE = 1_000;
6
- const MAX_LIST_OFFSET = 1_000;
7
6
  export function createTangleExactProcessProvider(input) {
8
7
  const { client, options, providerName } = input;
8
+ boundedIdentifier(providerName, "Tangle exact process provider");
9
+ if (options.teamId !== undefined) {
10
+ boundedIdentifier(options.teamId, "Tangle exact process team id");
11
+ }
9
12
  const get = client.get;
10
13
  const list = client.list;
11
14
  if (!get || !list) {
@@ -13,69 +16,137 @@ export function createTangleExactProcessProvider(input) {
13
16
  }
14
17
  return {
15
18
  async create(createInput) {
19
+ createInput.signal?.throwIfAborted();
16
20
  assertSupportedProviderOptions(createInput.providerOptions);
17
21
  assertUnreservedMetadata(createInput.metadata);
18
- const box = await client.create(exactSandboxOptions(createInput, options), {
22
+ const identityDigest = exactProcessRequestDigest(createInput, providerName, options);
23
+ createInput.signal?.throwIfAborted();
24
+ const createPromise = client.create(exactSandboxOptions(createInput, options, providerName, identityDigest), {
19
25
  ...(createInput.signal ? { signal: createInput.signal } : {}),
20
26
  ...(createInput.provisionTimeoutMs === undefined
21
27
  ? {}
22
28
  : { timeoutMs: createInput.provisionTimeoutMs }),
23
29
  });
30
+ let box;
31
+ try {
32
+ box = await awaitWithSignal(createPromise, createInput.signal);
33
+ }
34
+ catch (error) {
35
+ if (createInput.signal?.aborted) {
36
+ void createPromise
37
+ .then(async (lateBox) => {
38
+ if (!lateBox.delete) {
39
+ attachCleanupHandle(error, lateBox);
40
+ return;
41
+ }
42
+ try {
43
+ await lateBox.delete();
44
+ }
45
+ catch (cleanupError) {
46
+ attachCleanupHandle(error, lateBox, cleanupError);
47
+ }
48
+ })
49
+ .catch((lateError) => attachCleanupHandle(error, undefined, lateError));
50
+ }
51
+ throw error;
52
+ }
24
53
  try {
25
- assertExactProcessSandbox(box);
54
+ createInput.signal?.throwIfAborted();
55
+ assertExactProcessSandbox(box, providerName, options.teamId, identityDigest);
26
56
  return sandboxInstanceAsExactProcessEnvironment(box, providerName);
27
57
  }
28
58
  catch (error) {
29
- if (!box.delete)
30
- throw error;
59
+ if (isExactProcessRequestConflict(box, providerName, options.teamId, createInput.idempotencyKey, identityDigest)) {
60
+ throw new Error("Tangle exact process idempotency key conflicts with an existing request", { cause: error });
61
+ }
62
+ if (!box.delete) {
63
+ const baseError = error instanceof Error ? error : new Error(String(error));
64
+ throw Object.assign(baseError, {
65
+ cleanupHandle: box,
66
+ message: `${error instanceof Error ? error.message : String(error)}; provider returned no cleanup handle`,
67
+ });
68
+ }
31
69
  try {
32
70
  await box.delete();
33
71
  }
34
72
  catch (cleanupError) {
35
- throw new AggregateError([error, cleanupError], "Tangle exact process validation and cleanup both failed");
73
+ const combined = new AggregateError([error, cleanupError], "Tangle exact process validation and cleanup both failed");
74
+ attachCleanupHandle(combined, box, cleanupError);
75
+ throw combined;
36
76
  }
37
77
  throw error;
38
78
  }
39
79
  },
40
- async get(id) {
41
- const box = await get.call(client, id);
42
- if (!box || !isExactProcessSandbox(box))
80
+ async get(id, operation = {}) {
81
+ assertSignalOptions(operation, "Tangle exact process get");
82
+ boundedIdentifier(id, "exact process environment id");
83
+ operation.signal?.throwIfAborted();
84
+ const box = await awaitWithSignal(get.call(client, id, operation.signal ? { signal: operation.signal } : undefined), operation.signal);
85
+ operation.signal?.throwIfAborted();
86
+ if (!box ||
87
+ boundedIdentifier(box.id, "exact process environment id") !== id ||
88
+ (box.metadata !== undefined && !isBoundedJson(box.metadata)) ||
89
+ !isExactProcessSandbox(box, providerName, options.teamId))
43
90
  return null;
44
91
  return sandboxInstanceAsExactProcessEnvironment(box, providerName);
45
92
  },
46
- async list(query) {
93
+ async list(query, operation = {}) {
94
+ assertSignalOptions(operation, "Tangle exact process list");
95
+ query?.signal?.throwIfAborted();
96
+ operation.signal?.throwIfAborted();
97
+ const signal = query?.signal ?? operation.signal;
47
98
  assertSupportedProviderOptions(query?.providerOptions);
99
+ assertExactProcessListQuery(query);
48
100
  const matches = [];
49
- for (let offset = 0; offset <= MAX_LIST_OFFSET; offset += LIST_PAGE_SIZE) {
50
- const page = await list.call(client, {
101
+ for (let offset = 0;; offset += LIST_PAGE_SIZE) {
102
+ if (offset > MAX_LIST_RESULTS) {
103
+ throw new Error("Tangle exact process list exceeded its page bound");
104
+ }
105
+ signal?.throwIfAborted();
106
+ const page = await awaitWithSignal(list.call(client, {
51
107
  ...(options.teamId
52
108
  ? { scope: `team:${options.teamId}` }
53
109
  : { scope: "personal" }),
54
110
  limit: LIST_PAGE_SIZE,
55
111
  offset,
56
- });
112
+ ...(signal ? { signal } : {}),
113
+ }), signal);
114
+ if (!Array.isArray(page) || page.length > LIST_PAGE_SIZE) {
115
+ throw new Error("Tangle exact process list returned an invalid page size");
116
+ }
117
+ if (offset + page.length > MAX_LIST_RESULTS) {
118
+ throw new Error("Tangle exact process list exceeded its result bound");
119
+ }
120
+ signal?.throwIfAborted();
57
121
  for (const box of page) {
58
- if (isExactProcessSandbox(box) &&
122
+ signal?.throwIfAborted();
123
+ boundedIdentifier(box.id, "exact process environment id");
124
+ if (box.metadata !== undefined && !isBoundedJson(box.metadata)) {
125
+ throw new Error("Tangle exact process metadata exceeds its bound");
126
+ }
127
+ if (isExactProcessSandbox(box, providerName, options.teamId) &&
59
128
  metadataMatches(box.metadata, query?.metadata)) {
60
129
  matches.push(sandboxInstanceAsExactProcessEnvironment(box, providerName));
130
+ if (matches.length > MAX_LIST_RESULTS) {
131
+ throw new Error("Tangle exact process list exceeded its result bound");
132
+ }
61
133
  }
62
134
  }
63
135
  if (page.length < LIST_PAGE_SIZE)
64
136
  return matches;
65
137
  }
66
- throw new Error("Tangle exact process lookup exceeds the Sandbox list pagination limit");
67
138
  },
68
139
  };
69
140
  }
70
- function exactSandboxOptions(input, defaults) {
141
+ function exactSandboxOptions(input, defaults, providerName, identityDigest) {
142
+ boundedString(input.image, "exact process image");
143
+ boundedIdentifier(input.idempotencyKey, "exact process idempotencyKey");
144
+ assertBoundedJson(input.metadata);
71
145
  if (!input.image.trim())
72
146
  throw new Error("exact process image is required");
73
147
  if (!IMMUTABLE_TANGLE_IMAGE.test(input.image)) {
74
148
  throw new Error("Tangle exact process image must include a sha256 manifest digest");
75
149
  }
76
- if (!input.idempotencyKey.trim()) {
77
- throw new Error("exact process idempotencyKey is required");
78
- }
79
150
  if (!Number.isSafeInteger(input.maxLifetimeMs) ||
80
151
  input.maxLifetimeMs < 1 ||
81
152
  input.maxLifetimeMs % 1_000 !== 0) {
@@ -111,7 +182,13 @@ function exactSandboxOptions(input, defaults) {
111
182
  idempotencyKey: input.idempotencyKey,
112
183
  metadata: {
113
184
  ...input.metadata,
114
- [EXACT_PROCESS_METADATA_KEY]: true,
185
+ [EXACT_PROCESS_METADATA_KEY]: {
186
+ version: 1,
187
+ provider: providerName,
188
+ ...(defaults.teamId ? { teamId: defaults.teamId } : {}),
189
+ idempotencyKey: input.idempotencyKey,
190
+ requestDigest: identityDigest,
191
+ },
115
192
  },
116
193
  ...(defaults.teamId ? { teamId: defaults.teamId } : {}),
117
194
  resources,
@@ -135,188 +212,28 @@ function sandboxResourcesFromRequest(requested) {
135
212
  diskGB: requested.diskMb / 1_024,
136
213
  };
137
214
  }
138
- function sandboxInstanceAsExactProcessEnvironment(box, providerName) {
139
- if (!box.fs ||
140
- box.fs.supportsWriteMode !== true ||
141
- !box.process ||
142
- !box.delete) {
143
- throw new Error("Tangle sandbox does not expose exact files, processes, and deletion");
144
- }
145
- const process = box.process;
146
- const fs = box.fs;
147
- const destroy = box.delete.bind(box);
148
- return {
149
- id: String(box.id),
150
- provider: providerName,
151
- ...(box.metadata ? { metadata: box.metadata } : {}),
152
- process: {
153
- async list() {
154
- return (await process.list()).map(exactProcessStatusFromSandbox);
155
- },
156
- async get(pid) {
157
- const handle = await process.get(pid);
158
- return handle ? sandboxProcessAsExactProcess(handle) : null;
159
- },
160
- async spawn(launch, operation = {}) {
161
- operation.signal?.throwIfAborted();
162
- validateExactProcessLaunch(launch);
163
- const handle = await process.spawnExact(launch.executable, launch.args, {
164
- cwd: launch.cwd,
165
- env: { ...launch.env },
166
- inheritEnv: false,
167
- ...(launch.stdin === undefined ? {} : { stdin: launch.stdin }),
168
- timeoutMs: launch.timeoutMs,
169
- ...(operation.signal ? { signal: operation.signal } : {}),
170
- });
171
- operation.signal?.throwIfAborted();
172
- return sandboxProcessAsExactProcess(handle);
173
- },
174
- },
175
- async writeFile(path, bytes, options) {
176
- options.signal?.throwIfAborted();
177
- assertAbsoluteFilePath(path);
178
- if (!Number.isSafeInteger(options.mode) ||
179
- options.mode < 0 ||
180
- options.mode > 0o7777) {
181
- throw new Error("Tangle exact process file mode must be between 0 and 07777");
182
- }
183
- await fs.write(path, Buffer.from(bytes).toString("base64"), {
184
- encoding: "base64",
185
- mode: options.mode,
186
- });
187
- options.signal?.throwIfAborted();
188
- },
189
- async readFile(path, options) {
190
- options.signal?.throwIfAborted();
191
- assertAbsoluteFilePath(path);
192
- if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1) {
193
- throw new Error("Tangle exact process maxBytes must be a positive integer");
194
- }
195
- const stat = await fs.stat(path);
196
- options.signal?.throwIfAborted();
197
- if (!stat.isFile) {
198
- throw new Error("Tangle exact process path is not a regular file");
199
- }
200
- if (stat.size > options.maxBytes) {
201
- throw new Error("Tangle exact process file exceeds maxBytes");
202
- }
203
- const result = await fs.readBatch([path], { encoding: "base64" });
204
- options.signal?.throwIfAborted();
205
- const file = result.files[0];
206
- if (result.errors.length !== 0 ||
207
- result.files.length !== 1 ||
208
- !file ||
209
- file.path !== path ||
210
- file.encoding !== "base64") {
211
- throw new Error(result.errors[0]?.error ??
212
- "Tangle exact process file read returned an invalid result");
213
- }
214
- const bytes = Uint8Array.from(Buffer.from(file.content, "base64"));
215
- if (bytes.byteLength !== file.size ||
216
- bytes.byteLength !== stat.size ||
217
- bytes.byteLength > options.maxBytes) {
218
- throw new Error("Tangle exact process file read violated its byte bound");
219
- }
220
- return bytes;
221
- },
222
- async destroy() {
223
- await destroy();
224
- },
225
- };
226
- }
227
- function validateExactProcessLaunch(input) {
228
- if (!input.executable ||
229
- (!isAbsolute(input.executable) && !input.env.PATH?.trim())) {
230
- throw new Error("Tangle exact process executable must be absolute unless env.PATH is supplied");
215
+ function assertExactProcessListQuery(query) {
216
+ if (query !== undefined &&
217
+ (!query ||
218
+ typeof query !== "object" ||
219
+ Array.isArray(query) ||
220
+ (Object.getPrototypeOf(query) !== Object.prototype &&
221
+ Object.getPrototypeOf(query) !== null))) {
222
+ throw new Error("Tangle exact process list query must be a plain object");
223
+ }
224
+ if (query?.metadata !== undefined) {
225
+ if (!query.metadata ||
226
+ typeof query.metadata !== "object" ||
227
+ Array.isArray(query.metadata) ||
228
+ !isBoundedJson(query.metadata)) {
229
+ throw new Error("Tangle exact process metadata query exceeds its bound");
230
+ }
231
+ }
232
+ if (query !== undefined) {
233
+ const keys = new Set(Object.keys(query));
234
+ for (const key of ["metadata", "providerOptions", "signal"])
235
+ keys.delete(key);
236
+ if (keys.size > 0)
237
+ throw new Error("Tangle exact process list query contains unsupported fields");
231
238
  }
232
- if (!input.cwd)
233
- throw new Error("Tangle exact process cwd is required");
234
- if (!Number.isSafeInteger(input.timeoutMs) || input.timeoutMs < 0) {
235
- throw new Error("Tangle exact process timeoutMs must be a non-negative integer");
236
- }
237
- }
238
- function sandboxProcessAsExactProcess(process) {
239
- return {
240
- pid: process.pid,
241
- async status() {
242
- return exactProcessStatusFromSandbox(await process.status());
243
- },
244
- async wait() {
245
- await process.wait();
246
- const status = exactProcessStatusFromSandbox(await process.status());
247
- if (!status.termination) {
248
- throw new Error("Tangle exact process remained running after wait()");
249
- }
250
- return status.termination;
251
- },
252
- async kill() {
253
- await process.kill("SIGKILL", { tree: true });
254
- },
255
- async *stdout() {
256
- yield* process.stdout();
257
- },
258
- async *stderr() {
259
- yield* process.stderr();
260
- },
261
- };
262
- }
263
- function exactProcessStatusFromSandbox(status) {
264
- if (status.running && status.exitSignal) {
265
- throw new Error("Tangle exact process reported an exit signal while running");
266
- }
267
- const termination = processTermination(status);
268
- return {
269
- pid: status.pid,
270
- running: status.running,
271
- exitCode: status.exitCode,
272
- ...(status.exitSignal ? { exitSignal: status.exitSignal } : {}),
273
- ...(termination ? { termination } : {}),
274
- };
275
- }
276
- function processTermination(status) {
277
- if (status.running)
278
- return undefined;
279
- return status.exitSignal
280
- ? { kind: "signal", signal: status.exitSignal }
281
- : { kind: "exit", exitCode: status.exitCode };
282
- }
283
- function assertExactProcessSandbox(box) {
284
- if (!isExactProcessSandbox(box)) {
285
- throw new Error("Tangle Sandbox did not create the requested process-only runtime");
286
- }
287
- }
288
- function isExactProcessSandbox(box) {
289
- return (box.metadata?.runtimeMode === "control" &&
290
- box.metadata[EXACT_PROCESS_METADATA_KEY] === true);
291
- }
292
- function assertUnreservedMetadata(metadata) {
293
- const reserved = [
294
- "capabilities",
295
- "customer_id",
296
- "exactProcess",
297
- "integrationLaunch",
298
- "runtimeMode",
299
- "teamId",
300
- EXACT_PROCESS_METADATA_KEY,
301
- ];
302
- if (reserved.some((name) => Object.hasOwn(metadata, name))) {
303
- throw new Error("exact process ownership metadata is reserved by Tangle");
304
- }
305
- }
306
- function assertSupportedProviderOptions(providerOptions) {
307
- if (providerOptions && Object.keys(providerOptions).length > 0) {
308
- throw new Error("Tangle exact process providerOptions are not supported");
309
- }
310
- }
311
- function assertAbsoluteFilePath(path) {
312
- if (!isAbsolute(path)) {
313
- throw new Error("Tangle exact process file path must be absolute");
314
- }
315
- }
316
- function metadataMatches(actual, expected) {
317
- if (!expected)
318
- return true;
319
- if (!actual)
320
- return false;
321
- return Object.entries(expected).every(([key, value]) => isDeepStrictEqual(actual[key], value));
322
239
  }
package/dist/index.d.ts CHANGED
@@ -1,126 +1,4 @@
1
- import type { BackendType, CreateSandboxOptions, ExecResult as SandboxExecResult, PromptOptions, PromptResult, SandboxEvent } from "@tangle-network/sandbox";
2
- import { type AgentEnvironmentCapabilities, type AgentEnvironmentProvider, type CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
3
- import type { HarnessType, InputPart } from "@tangle-network/agent-interface";
4
- import { type TangleExactProcessOptions } from "./exact-process.js";
5
- export type { TangleExactProcessOptions } from "./exact-process.js";
6
- export interface SandboxClientLike {
7
- create(options?: CreateSandboxOptions, requestOptions?: {
8
- signal?: AbortSignal;
9
- timeoutMs?: number;
10
- }): Promise<SandboxInstanceLike>;
11
- get?(id: string): Promise<SandboxInstanceLike | null>;
12
- list?(options?: unknown): Promise<SandboxInstanceLike[]>;
13
- describePlacement?(box: SandboxInstanceLike): unknown;
14
- }
15
- export interface SandboxProcessStatusLike {
16
- pid: number;
17
- running: boolean;
18
- exitCode: number;
19
- exitSignal?: string;
20
- }
21
- export interface SandboxProcessLike {
22
- readonly pid: number;
23
- status(): Promise<SandboxProcessStatusLike>;
24
- wait(): Promise<number>;
25
- kill(signal?: "SIGKILL", options?: {
26
- tree?: boolean;
27
- }): Promise<void>;
28
- stdout(): AsyncIterable<string>;
29
- stderr(): AsyncIterable<string>;
30
- }
31
- export interface SandboxProcessManagerLike {
32
- list(): Promise<SandboxProcessStatusLike[]>;
33
- get(pid: number): Promise<SandboxProcessLike | null>;
34
- spawnExact(executable: string, args: readonly string[], options?: {
35
- cwd?: string;
36
- env?: Record<string, string>;
37
- inheritEnv?: boolean;
38
- stdin?: string;
39
- timeoutMs?: number;
40
- signal?: AbortSignal;
41
- }): Promise<SandboxProcessLike>;
42
- }
43
- export interface SandboxInstanceLike {
44
- id: string;
45
- name?: string;
46
- status?: unknown;
47
- metadata?: Record<string, unknown>;
48
- streamPrompt(message: string | InputPart[], options?: PromptOptions): AsyncIterable<SandboxEvent>;
49
- prompt?(message: string | InputPart[], options?: PromptOptions): Promise<PromptResult>;
50
- dispatchPrompt?(message: string | InputPart[], options?: PromptOptions): Promise<unknown>;
51
- session?(id: string): SandboxSessionLike;
52
- read?(path: string, options?: {
53
- sessionId?: string;
54
- }): Promise<string>;
55
- write?(path: string, content: string, options?: {
56
- sessionId?: string;
57
- }): Promise<unknown>;
58
- exec?(command: string, options?: unknown): Promise<SandboxExecResult>;
59
- fs?: {
60
- supportsWriteMode?: true;
61
- stat(path: string): Promise<{
62
- size: number;
63
- isFile: boolean;
64
- }>;
65
- readBatch(paths: string[], options?: {
66
- encoding?: "utf8" | "base64";
67
- }): Promise<{
68
- files: Array<{
69
- path: string;
70
- content: string;
71
- encoding: "utf8" | "base64";
72
- size: number;
73
- }>;
74
- errors: Array<{
75
- path: string;
76
- error: string;
77
- code?: string;
78
- }>;
79
- }>;
80
- write(path: string, content: string, options: {
81
- encoding: "base64";
82
- mode: number;
83
- }): Promise<unknown>;
84
- };
85
- process?: SandboxProcessManagerLike;
86
- checkpoint?(options?: unknown): Promise<unknown>;
87
- fork?(checkpointId: string, options?: unknown): Promise<SandboxInstanceLike>;
88
- refresh?(): Promise<void>;
89
- delete?(): Promise<void>;
90
- }
91
- export interface SandboxSessionLike {
92
- readonly id: string;
93
- status(): Promise<unknown | null>;
94
- events(options?: {
95
- since?: string;
96
- executionId?: string;
97
- signal?: AbortSignal;
98
- }): AsyncIterable<SandboxEvent>;
99
- result(options?: {
100
- executionId?: string;
101
- }): Promise<PromptResult>;
102
- prompt(message: string | InputPart[], options?: PromptOptions): Promise<PromptResult>;
103
- interrupt(options?: {
104
- executionId?: string;
105
- }): Promise<{
106
- cancelled: boolean;
107
- }>;
108
- }
109
- export interface TangleProviderOptions {
110
- client: SandboxClientLike;
111
- name?: string;
112
- defaultBackend?: BackendType;
113
- capabilities?: AgentEnvironmentCapabilities | (() => AgentEnvironmentCapabilities | Promise<AgentEnvironmentCapabilities>);
114
- validateProfile?: AgentEnvironmentProvider["validateProfile"];
115
- mapCreateInput?: (input: CreateAgentEnvironmentInput) => CreateSandboxOptions;
116
- exactProcess?: TangleExactProcessOptions;
117
- }
118
- export declare function createTangleProvider(options: TangleProviderOptions): AgentEnvironmentProvider;
119
- /**
120
- * @param harness The harness the sandbox will materialize the profile with. The prompt intents are
121
- * that harness's, not this adapter's: forwarding the whole profile on the wire makes both fields
122
- * *expressible*, but the sandbox's materializer refuses the intent its harness has no control for
123
- * (opencode has no replacement, codex and gemini no addition). Omit it and both intents declare
124
- * `false` — an adapter that cannot name its harness cannot promise either one.
125
- */
126
- export declare function defaultTangleSandboxCapabilities(harness?: HarnessType): AgentEnvironmentCapabilities;
1
+ export type { TangleExactProcessOptions } from "./tangle-types.js";
2
+ export * from "./tangle-types.js";
3
+ export { createTangleProvider } from "./tangle-provider.js";
4
+ export { defaultTangleSandboxCapabilities } from "./tangle-capabilities.js";