@tangle-network/agent-provider-tangle 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,20 @@
1
- import { AgentInteractiveSessionAttachSchema, AgentInteractiveSessionControlClaimAcknowledgementSchema, AgentInteractiveSessionControlClaimRequestSchema, AgentInteractiveSessionPromptAcknowledgementSchema, AgentInteractiveSessionPromptCommandSchema, AgentInteractiveSessionRefSchema, AgentInteractiveSessionStatusSchema, AgentInteractiveSessionStopAcknowledgementSchema, AgentInteractiveSessionStopCommandSchema, agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, agentInteractiveSessionControlClaimMatchesRef, agentInteractiveSessionPromptAcknowledgementMatchesCommand, agentInteractiveSessionRefMatchesStart, agentInteractiveSessionStopAcknowledgementMatchesCommand, canonicalCandidateDigest, exactAgentInteractiveSessionStart, } from "@tangle-network/agent-interface";
2
- import { parseBackendType } from "@tangle-network/sandbox";
1
+ import { AgentInteractiveSessionAttachSchema, AgentInteractiveSessionControlClaimSchema, AgentInteractiveSessionControlClaimAcknowledgementSchema, AgentInteractiveSessionControlClaimRequestSchema, AgentInteractiveSessionPromptAcknowledgementSchema, AgentInteractiveSessionPromptCommandSchema, AgentInteractiveSessionRefSchema, AgentInteractiveSessionStatusSchema, AgentInteractiveSessionStopAcknowledgementSchema, AgentInteractiveSessionStopCommandSchema, TerminalReplayWindowSchema, TerminalSessionRefSchema, agentInteractiveSessionControlClaimAcknowledgementMatchesRequest, agentInteractiveSessionControlClaimMatchesRef, agentInteractiveSessionPromptAcknowledgementMatchesCommand, agentInteractiveSessionRefMatchesStart, agentInteractiveSessionStatusMatchesRef, agentInteractiveSessionStopAcknowledgementMatchesCommand, canonicalCandidateDigest, exactAgentInteractiveSessionStart, } from "@tangle-network/agent-interface";
3
2
  import { assertOptionKeys } from "./tangle-environment-validation.js";
4
3
  import { transportFailureReason } from "./tangle-failure-reason.js";
5
- import { bindTangleInteractiveControl, closeTangleStreamQuietly, createTangleTerminalStreamCapture, prepareTangleTerminalAttachment, } from "./tangle-terminal.js";
6
4
  /**
7
5
  * Test the linked SDK surface without accepting any older interactive shape.
8
6
  *
9
- * The current public Sandbox release fails this test. That is intentional:
10
- * it has no receipt, control claim, or mutation acknowledgement protocol.
7
+ * Sandbox owns the exact reference, control claim, terminal binding, and
8
+ * mutation acknowledgements. An older handle shape fails this test closed.
11
9
  */
12
10
  export function sandboxBacksInteractiveAgent(box) {
13
11
  if (typeof box.session !== "function")
14
12
  return false;
15
13
  try {
16
- const candidate = box.session("__tangle-interactive-probe__").interactive?.();
17
- return (isSandboxInteractiveSession(candidate) &&
18
- typeof box.terminals?.get === "function");
14
+ const candidate = box
15
+ .session("__tangle-interactive-probe__")
16
+ .interactive?.();
17
+ return isSandboxInteractiveSession(candidate);
19
18
  }
20
19
  catch {
21
20
  return false;
@@ -29,9 +28,9 @@ export function sandboxBacksInteractiveAgent(box) {
29
28
  * never creates a second record for any of those facts.
30
29
  */
31
30
  export function createTangleInteractiveAgentRegistry(box, providerName, environmentId) {
32
- const exactHandle = (sessionId) => {
31
+ const exactHandle = (sessionId, binding) => {
33
32
  const session = box.session?.(sessionId);
34
- const candidate = session?.interactive?.();
33
+ const candidate = session?.interactive?.(binding);
35
34
  if (!isSandboxInteractiveSession(candidate)) {
36
35
  throw new Error("the linked Sandbox SDK does not expose the exact interactive control API");
37
36
  }
@@ -50,10 +49,13 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
50
49
  throw new Error("the Sandbox returned a different interactive session ref");
51
50
  }
52
51
  };
53
- const assertControl = (ref, control) => {
52
+ const assertControlMatchesRef = (ref, control) => {
54
53
  if (!agentInteractiveSessionControlClaimMatchesRef(ref, control)) {
55
54
  throw new Error("the interactive control claim belongs to another process");
56
55
  }
56
+ };
57
+ const assertActiveControl = (ref, control) => {
58
+ assertControlMatchesRef(ref, control);
57
59
  if (Date.parse(control.expiresAt) <= Date.now()) {
58
60
  throw new Error("the interactive control claim has expired");
59
61
  }
@@ -62,70 +64,68 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
62
64
  async start(request, options) {
63
65
  assertOptionKeys(options, ["signal"], "Tangle interactive agent start");
64
66
  const exactRequest = exactAgentInteractiveSessionStart(request);
65
- const requestedHarness = exactRequest.profile.harness;
66
- if (requestedHarness === undefined) {
67
+ if (exactRequest.profile.harness === undefined) {
67
68
  throw new Error("interactive agent sessions require AgentProfile.harness");
68
69
  }
69
- let harness;
70
- try {
71
- harness = parseBackendType(requestedHarness);
72
- }
73
- catch {
74
- throw new Error("the requested AgentProfile harness has no Tangle Sandbox runner");
75
- }
76
70
  options?.signal?.throwIfAborted();
77
71
  const handle = exactHandle(exactRequest.run.sessionId);
78
72
  let observed;
79
73
  try {
80
- observed = await handle.start({
81
- harness,
82
- ...(exactRequest.profile.model?.default === undefined
83
- ? {}
84
- : { model: exactRequest.profile.model.default }),
85
- ...(exactRequest.cwd === undefined ? {} : { cwd: exactRequest.cwd }),
86
- ...(exactRequest.cols === undefined ? {} : { cols: exactRequest.cols }),
87
- ...(exactRequest.rows === undefined ? {} : { rows: exactRequest.rows }),
88
- profile: exactRequest.profile,
89
- ...(exactRequest.initialPrompt === undefined
90
- ? {}
91
- : { initialPrompt: exactRequest.initialPrompt }),
92
- idempotencyKey: exactRequest.run.runId,
93
- requestDigest: exactRequest.run.requestDigest,
94
- });
74
+ observed = await handle.start(exactRequest, options);
95
75
  }
96
76
  catch (error) {
97
77
  options?.signal?.throwIfAborted();
98
78
  throw new Error(transportFailureReason("interactive start", error));
99
79
  }
100
80
  options?.signal?.throwIfAborted();
101
- const ref = refFromStart(exactRequest, observed);
81
+ const ref = AgentInteractiveSessionRefSchema.parse(observed.ref);
82
+ const control = AgentInteractiveSessionControlClaimSchema.parse(observed.control);
102
83
  if (!agentInteractiveSessionRefMatchesStart(exactRequest, ref)) {
103
84
  throw new Error("the Sandbox returned an interactive session with a different admission receipt");
104
85
  }
86
+ assertControlMatchesRef(ref, control);
87
+ if (observed.state === "running")
88
+ assertActiveControl(ref, control);
89
+ if (observed.state === "running" && observed.streamUrl.length === 0) {
90
+ throw new Error("the Sandbox returned a running session without a stream");
91
+ }
105
92
  return ref;
106
93
  },
107
94
  get(value) {
108
95
  const ref = exactRef(value);
109
- const handle = exactHandle(ref.run.sessionId);
96
+ const handle = exactHandle(ref.run.sessionId, { ref });
97
+ const mutationHandle = (control) => exactHandle(ref.run.sessionId, { ref, control });
110
98
  const readStatus = async (options) => {
111
99
  assertOptionKeys(options, ["signal"], "Tangle interactive agent status");
112
100
  options?.signal?.throwIfAborted();
113
101
  let observed;
114
102
  try {
115
- observed = await handle.status();
103
+ observed = await handle.status(options);
116
104
  }
117
105
  catch (error) {
118
106
  options?.signal?.throwIfAborted();
119
107
  throw new Error(transportFailureReason("interactive status", error));
120
108
  }
121
109
  options?.signal?.throwIfAborted();
122
- return statusFromSandbox(ref, observed);
110
+ if (observed === null) {
111
+ return AgentInteractiveSessionStatusSchema.parse({
112
+ state: "unknown",
113
+ ref,
114
+ message: "the Sandbox no longer knows this interactive agent session",
115
+ retryable: false,
116
+ });
117
+ }
118
+ const status = AgentInteractiveSessionStatusSchema.parse(observed);
119
+ if (!agentInteractiveSessionStatusMatchesRef(ref, status)) {
120
+ throw new Error("the Sandbox reported a different interactive agent session identity");
121
+ }
122
+ return status;
123
123
  };
124
124
  const validateControl = async (control, options) => {
125
- assertControl(ref, control);
125
+ assertActiveControl(ref, control);
126
126
  options?.signal?.throwIfAborted();
127
127
  try {
128
- await handle.validateControl(control);
128
+ await mutationHandle(control).validateControl(control, options);
129
129
  }
130
130
  catch (error) {
131
131
  options?.signal?.throwIfAborted();
@@ -142,7 +142,7 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
142
142
  options?.signal?.throwIfAborted();
143
143
  let raw;
144
144
  try {
145
- raw = await handle.claimControl(exactRequest);
145
+ raw = await handle.claimControl(exactRequest, options);
146
146
  }
147
147
  catch (error) {
148
148
  options?.signal?.throwIfAborted();
@@ -160,59 +160,33 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
160
160
  assertOptionKeys(options, ["signal"], "Tangle interactive agent attach");
161
161
  const exactRequest = AgentInteractiveSessionAttachSchema.parse(request ?? {});
162
162
  await validateControl(exactRequest.control, options);
163
- const before = await readStatus(options);
164
- if (before.state !== "running") {
165
- throw new Error("the exact interactive agent session is not running");
166
- }
167
- const terminals = box.terminals;
168
- if (terminals === undefined || typeof terminals.get !== "function") {
169
- throw new Error("the Sandbox client cannot read exact interactive terminal metadata");
170
- }
171
- const capture = createTangleTerminalStreamCapture(exactRequest);
172
- let stream;
163
+ let terminal;
173
164
  try {
174
- stream = await handle.attach({
175
- control: exactRequest.control,
176
- ...(exactRequest.cols === undefined ? {} : { cols: exactRequest.cols }),
177
- ...(exactRequest.rows === undefined ? {} : { rows: exactRequest.rows }),
178
- handlers: capture.handlers,
179
- });
165
+ terminal = await mutationHandle(exactRequest.control).attachAgentTerminal(exactRequest, options);
180
166
  }
181
167
  catch (error) {
182
168
  options?.signal?.throwIfAborted();
183
169
  throw new Error(transportFailureReason("interactive attach", error));
184
170
  }
185
- if (options?.signal?.aborted) {
186
- await closeTangleStreamQuietly(stream);
187
- options.signal.throwIfAborted();
188
- }
189
- const prepared = await prepareTangleTerminalAttachment({
190
- stream,
191
- capture,
192
- terminals,
193
- parentExecutionId: ref.run.executionId,
194
- expectedSessionId: ref.run.sessionId,
195
- signal: options?.signal,
196
- beforeMutation: (operation) => {
197
- if (operation === "detach")
198
- return Promise.resolve();
199
- return validateControl(exactRequest.control, options);
200
- },
201
- });
202
- if (prepared.status === "unknown") {
203
- throw new Error(prepared.message);
204
- }
205
- if (prepared.acknowledgement.restored !== true) {
206
- await prepared.session.detach();
207
- throw new Error("the exact interactive attach did not restore the coding-agent PTY");
171
+ try {
172
+ options?.signal?.throwIfAborted();
173
+ const returnedControl = AgentInteractiveSessionControlClaimSchema.parse(terminal.control);
174
+ const returnedRef = TerminalSessionRefSchema.parse(terminal.ref);
175
+ TerminalReplayWindowSchema.parse(terminal.cursors);
176
+ if (canonicalCandidateDigest(returnedControl) !==
177
+ canonicalCandidateDigest(exactRequest.control) ||
178
+ returnedRef.terminalSessionId !== ref.run.sessionId ||
179
+ returnedRef.parentExecutionId !== ref.run.executionId ||
180
+ returnedRef.expiresAt !== exactRequest.control.expiresAt ||
181
+ !returnedRef.isRunning) {
182
+ throw new Error("the Sandbox returned a terminal with a different interactive identity");
183
+ }
208
184
  }
209
- const after = await readStatus(options);
210
- if (after.state !== "running") {
211
- await prepared.session.detach();
212
- throw new Error("the exact interactive agent exited while its terminal attached");
185
+ catch (error) {
186
+ await terminal.detach().catch(() => undefined);
187
+ throw error;
213
188
  }
214
- const terminal = bindTangleInteractiveControl(prepared.session, exactRequest.control);
215
- return terminal;
189
+ return validatedTerminal(terminal, exactRequest.control, validateControl, ref);
216
190
  },
217
191
  async sendPrompt(command, options) {
218
192
  assertOptionKeys(options, ["signal"], "Tangle interactive agent prompt");
@@ -221,7 +195,7 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
221
195
  await validateControl(exactCommand.control, options);
222
196
  let raw;
223
197
  try {
224
- raw = await handle.sendPrompt(exactCommand);
198
+ raw = await mutationHandle(exactCommand.control).sendPrompt(exactCommand, options);
225
199
  }
226
200
  catch (error) {
227
201
  options?.signal?.throwIfAborted();
@@ -241,7 +215,7 @@ export function createTangleInteractiveAgentRegistry(box, providerName, environm
241
215
  await validateControl(exactCommand.control, options);
242
216
  let raw;
243
217
  try {
244
- raw = await handle.stop(exactCommand);
218
+ raw = await mutationHandle(exactCommand.control).stop(exactCommand, options);
245
219
  }
246
220
  catch (error) {
247
221
  options?.signal?.throwIfAborted();
@@ -266,62 +240,43 @@ function isSandboxInteractiveSession(value) {
266
240
  "start",
267
241
  "claimControl",
268
242
  "status",
269
- "attach",
243
+ "attachAgentTerminal",
270
244
  "validateControl",
271
245
  "sendPrompt",
272
246
  "stop",
273
247
  ].every((key) => typeof candidate[key] === "function");
274
248
  }
275
- function refFromStart(request, observed) {
276
- if (observed.sessionId !== request.run.sessionId ||
277
- observed.harness !== request.profile.harness) {
278
- throw new Error("the Sandbox returned a different interactive session identity");
279
- }
280
- return AgentInteractiveSessionRefSchema.parse({
281
- run: request.run,
282
- preparationReceipt: observed.preparationReceipt,
283
- incarnationId: observed.incarnationId,
284
- startedAt: observed.startedAt,
285
- });
286
- }
287
- function statusFromSandbox(ref, observed) {
288
- if (observed === null) {
289
- return AgentInteractiveSessionStatusSchema.parse({
290
- state: "unknown",
291
- ref,
292
- message: "the Sandbox no longer knows this interactive agent session",
293
- retryable: false,
294
- });
295
- }
296
- const observedRef = AgentInteractiveSessionRefSchema.parse({
297
- run: { ...ref.run, sessionId: observed.sessionId },
298
- preparationReceipt: observed.preparationReceipt,
299
- incarnationId: observed.incarnationId,
300
- startedAt: observed.startedAt,
301
- });
302
- if (observed.harness !== ref.preparationReceipt.harness ||
303
- !sameExactSessionRef(ref, observedRef)) {
304
- throw new Error("the Sandbox reported a different interactive agent session identity");
305
- }
306
- if (observed.state === "running") {
307
- if (observed.streamUrl.length === 0) {
308
- throw new Error("the Sandbox reported a running session without a stream");
309
- }
310
- return AgentInteractiveSessionStatusSchema.parse({ state: "running", ref });
311
- }
312
- return AgentInteractiveSessionStatusSchema.parse({
313
- state: "exited",
314
- ref,
315
- endedAt: observed.endedAt,
316
- reason: observed.reason,
317
- ...(Number.isSafeInteger(observed.exitCode)
318
- ? { exitCode: observed.exitCode }
319
- : {}),
320
- ...(typeof observed.exitSignal === "string" && observed.exitSignal.length > 0
321
- ? { exitSignal: observed.exitSignal }
322
- : {}),
323
- });
249
+ function validatedTerminal(terminal, control, validate, expectedRef) {
250
+ const boundControl = Object.freeze({ ...control });
251
+ return {
252
+ get ref() {
253
+ const ref = TerminalSessionRefSchema.parse(terminal.ref);
254
+ if (ref.terminalSessionId !== expectedRef.run.sessionId ||
255
+ ref.parentExecutionId !== expectedRef.run.executionId) {
256
+ throw new Error("the Sandbox terminal changed its interactive session identity");
257
+ }
258
+ return ref;
259
+ },
260
+ get cursors() {
261
+ return TerminalReplayWindowSchema.parse(terminal.cursors);
262
+ },
263
+ control: boundControl,
264
+ async input(input, options) {
265
+ await validate(boundControl, options);
266
+ await terminal.input(input, options);
267
+ },
268
+ async resize(resize, options) {
269
+ await validate(boundControl, options);
270
+ await terminal.resize(resize, options);
271
+ },
272
+ detach: (options) => terminal.detach(options),
273
+ async close(options) {
274
+ await validate(boundControl, options);
275
+ return terminal.close(options);
276
+ },
277
+ events: (options) => terminal.events(options),
278
+ };
324
279
  }
325
280
  function sameExactSessionRef(expected, candidate) {
326
- return canonicalCandidateDigest(expected) === canonicalCandidateDigest(candidate);
281
+ return (canonicalCandidateDigest(expected) === canonicalCandidateDigest(candidate));
327
282
  }
@@ -1,5 +1,5 @@
1
1
  import type { BackendRegistryEntry, BackendRegistryResponse, BackendType, CreateSandboxOptions, ExecResult as SandboxExecResult, PromptOptions, PromptResult, SandboxEvent } from "@tangle-network/sandbox";
2
- import type { AgentRunCancellationAcknowledgement, AgentRunCancellationRequest, AgentExecutionPreparationReceipt, AgentInteractiveSessionControlClaim, AgentInteractiveSessionControlClaimAcknowledgement, AgentInteractiveSessionControlClaimRequest, AgentInteractiveSessionPromptAcknowledgement, AgentInteractiveSessionPromptCommand, AgentInteractiveSessionStopAcknowledgement, AgentInteractiveSessionStopCommand, AgentProfile, ConfidentialAttestation, ConfidentialExecutionEnvironment, InputPart, InteractionRequest, InteractionResponseCommand } from "@tangle-network/agent-interface";
2
+ import type { AgentRunCancellationAcknowledgement, AgentRunCancellationRequest, AgentInteractiveSessionControlClaim, AgentInteractiveSessionControlClaimAcknowledgement, AgentInteractiveSessionControlClaimRequest, AgentInteractiveSessionAttach, AgentInteractiveSessionPromptAcknowledgement, AgentInteractiveSessionPromptCommand, AgentInteractiveSessionRef, AgentInteractiveSessionStart, AgentInteractiveSessionStatus, AgentInteractiveSessionStopAcknowledgement, AgentInteractiveSessionStopCommand, AgentInteractiveTerminalSession, ConfidentialAttestation, ConfidentialExecutionEnvironment, InputPart, InteractionRequest, InteractionResponseCommand } from "@tangle-network/agent-interface";
3
3
  import type { AgentEnvironmentCapabilities, AgentEnvironmentProvider, CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
4
4
  /**
5
5
  * The platform verdict for the create call that returned a sandbox.
@@ -335,57 +335,45 @@ export interface SandboxTerminalsLike {
335
335
  handlers?: SandboxTerminalHandlersLike;
336
336
  }): Promise<SandboxTerminalStreamLike>;
337
337
  }
338
- /** Identity returned by the Sandbox exact interactive-session route. */
339
- export interface SandboxInteractiveSessionIdentityLike {
340
- sessionId: string;
341
- harness: BackendType;
342
- startedAt: string;
343
- /** Provider-issued process incarnation. Replays return this exact value. */
344
- incarnationId: string;
345
- /** Canonical provider admission receipt for the effective route. */
346
- preparationReceipt: AgentExecutionPreparationReceipt;
347
- }
348
- /** Start acknowledgement from the canonical Sandbox interactive API. */
349
- export interface SandboxInteractiveSessionInfoLike extends SandboxInteractiveSessionIdentityLike {
350
- streamUrl: string;
351
- }
352
- /** Lifecycle returned by the Sandbox exact interactive-session route. */
353
- export type SandboxInteractiveSessionStatusLike = (SandboxInteractiveSessionInfoLike & {
338
+ /** Start result returned by the Sandbox exact interactive-session route. */
339
+ export type SandboxInteractiveSessionStartResultLike = {
354
340
  state: "running";
355
- }) | (SandboxInteractiveSessionIdentityLike & {
341
+ ref: AgentInteractiveSessionRef;
342
+ control: AgentInteractiveSessionControlClaim;
343
+ streamUrl: string;
344
+ } | {
356
345
  state: "exited";
346
+ ref: AgentInteractiveSessionRef;
347
+ control: AgentInteractiveSessionControlClaim;
357
348
  endedAt: string;
358
349
  exitCode?: number;
359
350
  exitSignal?: string;
360
351
  reason: "exited" | "stopped" | "lost";
361
- });
352
+ };
362
353
  /** Existing Sandbox SDK handle for one coding harness's native TUI. */
363
354
  export interface SandboxInteractiveSessionLike {
364
- start(options: {
365
- harness: BackendType;
366
- model?: string;
367
- cwd?: string;
368
- cols?: number;
369
- rows?: number;
370
- profile: AgentProfile;
371
- initialPrompt?: string;
372
- /** Derived from the exact run; owned by the Sandbox creation ledger. */
373
- idempotencyKey: string;
374
- /** Exact request digest paired with the idempotency key. */
375
- requestDigest: `sha256:${string}`;
376
- }): Promise<SandboxInteractiveSessionInfoLike>;
377
- claimControl(request: AgentInteractiveSessionControlClaimRequest): Promise<AgentInteractiveSessionControlClaimAcknowledgement>;
378
- status(): Promise<SandboxInteractiveSessionStatusLike | null>;
379
- attach(options: {
380
- control: AgentInteractiveSessionControlClaim;
381
- cols?: number;
382
- rows?: number;
383
- handlers?: SandboxTerminalHandlersLike;
384
- }): Promise<SandboxTerminalStreamLike>;
385
- /** Validate the current generation before each PTY mutation. */
386
- validateControl(control: AgentInteractiveSessionControlClaim): Promise<void>;
387
- sendPrompt(command: AgentInteractiveSessionPromptCommand): Promise<AgentInteractiveSessionPromptAcknowledgement>;
388
- stop(command: AgentInteractiveSessionStopCommand): Promise<AgentInteractiveSessionStopAcknowledgement>;
355
+ start(request: AgentInteractiveSessionStart, options?: {
356
+ signal?: AbortSignal;
357
+ }): Promise<SandboxInteractiveSessionStartResultLike>;
358
+ claimControl(request: AgentInteractiveSessionControlClaimRequest, options?: {
359
+ signal?: AbortSignal;
360
+ }): Promise<AgentInteractiveSessionControlClaimAcknowledgement>;
361
+ status(options?: {
362
+ signal?: AbortSignal;
363
+ }): Promise<AgentInteractiveSessionStatus | null>;
364
+ attachAgentTerminal(request: AgentInteractiveSessionAttach, options?: {
365
+ signal?: AbortSignal;
366
+ }): Promise<AgentInteractiveTerminalSession>;
367
+ /** Confirm that this claim is still the current writable generation. */
368
+ validateControl(control: AgentInteractiveSessionControlClaim, options?: {
369
+ signal?: AbortSignal;
370
+ }): Promise<void>;
371
+ sendPrompt(command: AgentInteractiveSessionPromptCommand, options?: {
372
+ signal?: AbortSignal;
373
+ }): Promise<AgentInteractiveSessionPromptAcknowledgement>;
374
+ stop(command: AgentInteractiveSessionStopCommand, options?: {
375
+ signal?: AbortSignal;
376
+ }): Promise<AgentInteractiveSessionStopAcknowledgement>;
389
377
  }
390
378
  export interface SandboxInstanceLike {
391
379
  id: string;
@@ -502,7 +490,10 @@ export interface SandboxInstanceLike {
502
490
  export interface SandboxSessionLike {
503
491
  readonly id: string;
504
492
  /** Exact native coding-agent TUI bound to this session id. */
505
- interactive?(): unknown;
493
+ interactive?(options?: {
494
+ ref?: AgentInteractiveSessionRef;
495
+ control?: AgentInteractiveSessionControlClaim;
496
+ }): unknown;
506
497
  status(options?: {
507
498
  signal?: AbortSignal;
508
499
  }): Promise<unknown | null>;