@tangle-network/agent-provider-tangle 0.6.2 → 0.6.3
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 +2 -1
- package/dist/tangle-capabilities.d.ts +3 -0
- package/dist/tangle-capabilities.js +35 -4
- package/dist/tangle-environment-control.d.ts +4 -1
- package/dist/tangle-environment-control.js +10 -2
- package/dist/tangle-environment-dispatch.js +49 -19
- package/dist/tangle-environment-session.d.ts +12 -3
- package/dist/tangle-environment-session.js +206 -59
- package/dist/tangle-environment.js +23 -4
- package/dist/tangle-events.d.ts +8 -0
- package/dist/tangle-events.js +164 -4
- package/dist/tangle-prompt.d.ts +5 -3
- package/dist/tangle-prompt.js +20 -3
- package/dist/tangle-session-control.d.ts +6 -5
- package/dist/tangle-session-control.js +31 -16
- package/dist/tangle-types.d.ts +11 -5
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -13,8 +13,9 @@ const provider = createTangleProvider({
|
|
|
13
13
|
|
|
14
14
|
Detached dispatch returns the immutable Sandbox execution receipt in `controlRef`.
|
|
15
15
|
The adapter validates its complete capability document and omits optional environment methods whose capabilities are disabled.
|
|
16
|
-
Reconstruct an exact session with `environment.session(reference.id, { controlRef: reference.controlRef })`; replay cursors are exclusive at the agent interface
|
|
16
|
+
Reconstruct an exact session with `environment.session(reference.id, { controlRef: reference.controlRef })`; replay cursors are exclusive at both the agent interface and Sandbox session stream.
|
|
17
17
|
Result, replay, and cancel operations select that exact execution instead of whichever execution most recently changed the shared session.
|
|
18
|
+
Retained control is advertised only when the Sandbox exposes dispatch, replay, exact results and events, turn idempotency, and `cancelRun`; native continuation remains absent.
|
|
18
19
|
After `session.prompt()` admits another turn, that session object's `controlRef` advances only when Sandbox returns the requested execution ID; a mismatched receipt fails without advancing local state.
|
|
19
20
|
Sandbox keeps execution identifiers optional for older or unproven service paths, so this adapter fails closed when a dispatch or prompt does not return one and never falls back to latest-session state.
|
|
20
21
|
Sessions reconstructed without a control reference may start a new prompt, but result lookup, cancellation, and cursor replay fail before calling Sandbox because those operations could otherwise select the newest unrelated execution.
|
|
@@ -20,6 +20,7 @@ export interface SandboxCapabilitySupport {
|
|
|
20
20
|
fork: boolean;
|
|
21
21
|
placement: boolean;
|
|
22
22
|
destroy: boolean;
|
|
23
|
+
cancelRun: boolean;
|
|
23
24
|
}
|
|
24
25
|
export declare function sandboxCapabilitySupport(box: SandboxInstanceLike, client: SandboxClientLike): SandboxCapabilitySupport;
|
|
25
26
|
/**
|
|
@@ -28,6 +29,8 @@ export declare function sandboxCapabilitySupport(box: SandboxInstanceLike, clien
|
|
|
28
29
|
* A client without placement metadata cannot satisfy placement(), and this
|
|
29
30
|
* adapter has no durable branching, interaction, or native-continuation
|
|
30
31
|
* implementation regardless of an overly broad configured document.
|
|
32
|
+
* Per-sandbox retained-run claims are narrowed later, after a concrete
|
|
33
|
+
* session handle proves that cancelRun is available.
|
|
31
34
|
*/
|
|
32
35
|
export declare function capabilitiesForClient(declared: AgentEnvironmentCapabilities, client: SandboxClientLike): AgentEnvironmentCapabilities;
|
|
33
36
|
/**
|
|
@@ -33,6 +33,7 @@ export function defaultTangleSandboxCapabilities(harness) {
|
|
|
33
33
|
streaming: { live: true, replay: true, detach: true, turnIdempotency: true },
|
|
34
34
|
// A Sandbox session id is not a Braid context-boundary proof. Native
|
|
35
35
|
// continuation stays unavailable until this adapter can verify one.
|
|
36
|
+
// Retained control stays opt-in until a concrete session exposes cancelRun.
|
|
36
37
|
sessions: { continue: false, list: false, messages: false },
|
|
37
38
|
workspace: { read: true, write: true, exec: true, git: false, upload: true, download: true },
|
|
38
39
|
branching: { checkpoint: false, fork: false },
|
|
@@ -43,7 +44,19 @@ export function defaultTangleSandboxCapabilities(harness) {
|
|
|
43
44
|
confidential: false,
|
|
44
45
|
};
|
|
45
46
|
}
|
|
47
|
+
const CAPABILITY_PROBE_SESSION_ID = "__tangle-capability-probe__";
|
|
46
48
|
export function sandboxCapabilitySupport(box, client) {
|
|
49
|
+
let session;
|
|
50
|
+
if (typeof box.session === "function") {
|
|
51
|
+
try {
|
|
52
|
+
// Sandbox session handles are lazy. Inspecting one does not call the
|
|
53
|
+
// service, and keeps retained-control claims tied to the actual handle.
|
|
54
|
+
session = box.session(CAPABILITY_PROBE_SESSION_ID);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// A client that cannot produce a session handle cannot prove retained control.
|
|
58
|
+
}
|
|
59
|
+
}
|
|
47
60
|
return {
|
|
48
61
|
dispatchPrompt: typeof box.dispatchPrompt === "function",
|
|
49
62
|
session: typeof box.session === "function",
|
|
@@ -54,6 +67,7 @@ export function sandboxCapabilitySupport(box, client) {
|
|
|
54
67
|
fork: typeof box.fork === "function",
|
|
55
68
|
placement: typeof client.describePlacement === "function",
|
|
56
69
|
destroy: typeof box.delete === "function",
|
|
70
|
+
cancelRun: typeof session?.cancelRun === "function",
|
|
57
71
|
};
|
|
58
72
|
}
|
|
59
73
|
/**
|
|
@@ -62,8 +76,11 @@ export function sandboxCapabilitySupport(box, client) {
|
|
|
62
76
|
* A client without placement metadata cannot satisfy placement(), and this
|
|
63
77
|
* adapter has no durable branching, interaction, or native-continuation
|
|
64
78
|
* implementation regardless of an overly broad configured document.
|
|
79
|
+
* Per-sandbox retained-run claims are narrowed later, after a concrete
|
|
80
|
+
* session handle proves that cancelRun is available.
|
|
65
81
|
*/
|
|
66
82
|
export function capabilitiesForClient(declared, client) {
|
|
83
|
+
const canReconstructRetainedEnvironment = typeof client.get === "function";
|
|
67
84
|
const narrowed = {
|
|
68
85
|
...declared,
|
|
69
86
|
streaming: {
|
|
@@ -72,7 +89,12 @@ export function capabilitiesForClient(declared, client) {
|
|
|
72
89
|
detach: declared.streaming.detach,
|
|
73
90
|
turnIdempotency: declared.streaming.turnIdempotency,
|
|
74
91
|
},
|
|
75
|
-
sessions: {
|
|
92
|
+
sessions: {
|
|
93
|
+
...declared.sessions,
|
|
94
|
+
continue: declared.sessions.continue && canReconstructRetainedEnvironment,
|
|
95
|
+
list: false,
|
|
96
|
+
messages: false,
|
|
97
|
+
},
|
|
76
98
|
workspace: { ...declared.workspace, git: false },
|
|
77
99
|
usage: false,
|
|
78
100
|
branching: {
|
|
@@ -86,7 +108,8 @@ export function capabilitiesForClient(declared, client) {
|
|
|
86
108
|
placement: declared.placement && typeof client.describePlacement === "function",
|
|
87
109
|
};
|
|
88
110
|
delete narrowed.interactions;
|
|
89
|
-
|
|
111
|
+
if (!canReconstructRetainedEnvironment)
|
|
112
|
+
delete narrowed.retainedControl;
|
|
90
113
|
delete narrowed.nativeContinuation;
|
|
91
114
|
return narrowed;
|
|
92
115
|
}
|
|
@@ -98,9 +121,17 @@ export function capabilitiesForClient(declared, client) {
|
|
|
98
121
|
*/
|
|
99
122
|
export function capabilitiesForSandbox(declared, support) {
|
|
100
123
|
const narrowed = { ...declared };
|
|
124
|
+
const supportsRetainedControl = declared.sessions.continue &&
|
|
125
|
+
declared.streaming.detach &&
|
|
126
|
+
declared.streaming.replay &&
|
|
127
|
+
declared.streaming.turnIdempotency &&
|
|
128
|
+
support.dispatchPrompt &&
|
|
129
|
+
support.session &&
|
|
130
|
+
support.cancelRun;
|
|
101
131
|
delete narrowed.interactions;
|
|
102
|
-
delete narrowed.retainedControl;
|
|
103
132
|
delete narrowed.nativeContinuation;
|
|
133
|
+
if (!supportsRetainedControl)
|
|
134
|
+
delete narrowed.retainedControl;
|
|
104
135
|
return {
|
|
105
136
|
...narrowed,
|
|
106
137
|
streaming: {
|
|
@@ -111,7 +142,7 @@ export function capabilitiesForSandbox(declared, support) {
|
|
|
111
142
|
},
|
|
112
143
|
sessions: {
|
|
113
144
|
...declared.sessions,
|
|
114
|
-
continue:
|
|
145
|
+
continue: declared.sessions.continue && supportsRetainedControl,
|
|
115
146
|
list: false,
|
|
116
147
|
messages: false,
|
|
117
148
|
},
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { AgentSessionRef, AgentTurnInput } from "@tangle-network/agent-interface/environment-provider";
|
|
2
2
|
import type { SandboxInstanceLike, SandboxSessionLike } from "./tangle-types.js";
|
|
3
|
-
export declare function sessionPromptRequestDigest(input: AgentTurnInput, provider: string, environmentId: string, sessionId: string,
|
|
3
|
+
export declare function sessionPromptRequestDigest(input: AgentTurnInput, provider: string, environmentId: string, sessionId: string, options?: {
|
|
4
|
+
executionId?: string;
|
|
5
|
+
nonce?: string;
|
|
6
|
+
}): `sha256:${string}`;
|
|
4
7
|
export declare function hasReplayPayload(input: AgentTurnInput): boolean;
|
|
5
8
|
export declare function interruptAfterAbort(box: SandboxInstanceLike, reference: AgentSessionRef): Promise<void>;
|
|
6
9
|
export declare function interruptExecutionAfterAbort(source: SandboxInstanceLike | SandboxSessionLike, sessionId: string, executionId: string): Promise<void>;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { canonicalCandidateDigest } from "@tangle-network/agent-interface";
|
|
2
|
-
export function sessionPromptRequestDigest(input, provider, environmentId, sessionId,
|
|
2
|
+
export function sessionPromptRequestDigest(input, provider, environmentId, sessionId, options = {}) {
|
|
3
3
|
return canonicalCandidateDigest({
|
|
4
4
|
provider,
|
|
5
5
|
environmentId,
|
|
6
6
|
sessionId,
|
|
7
|
-
executionId
|
|
7
|
+
...(options.executionId === undefined
|
|
8
|
+
? {}
|
|
9
|
+
: { executionId: options.executionId }),
|
|
10
|
+
...(options.nonce === undefined ? {} : { nonce: options.nonce }),
|
|
8
11
|
...(input.turnId === undefined ? {} : { turnId: input.turnId }),
|
|
9
12
|
...(input.prompt === undefined ? {} : { prompt: input.prompt }),
|
|
10
13
|
...(input.parts === undefined ? {} : { parts: input.parts }),
|
|
@@ -25,6 +28,11 @@ export function hasReplayPayload(input) {
|
|
|
25
28
|
input.providerOptions !== undefined);
|
|
26
29
|
}
|
|
27
30
|
export async function interruptAfterAbort(box, reference) {
|
|
31
|
+
const metadata = reference.metadata;
|
|
32
|
+
// A duplicate dispatch receipt identifies work owned by another caller.
|
|
33
|
+
// Only a receipt that proves this call admitted new work may be interrupted.
|
|
34
|
+
if (metadata?.dispatched !== true || metadata.alreadyExisted === true)
|
|
35
|
+
return;
|
|
28
36
|
const sessionId = reference.id;
|
|
29
37
|
const executionId = reference.controlRef?.executionId;
|
|
30
38
|
if (!box.session || executionId === undefined)
|
|
@@ -1,23 +1,61 @@
|
|
|
1
|
-
import { AgentTurnInputSchema } from "@tangle-network/agent-interface";
|
|
2
|
-
import {
|
|
3
|
-
import { retainedSessionControlRef, sessionRefFromSandboxDispatch } from "./tangle-session-control.js";
|
|
1
|
+
import { AgentExactRunControlRefSchema, AgentTurnInputSchema, } from "@tangle-network/agent-interface";
|
|
2
|
+
import { promptFromTurnInput, promptOptionsFromTurnInput, } from "./tangle-prompt.js";
|
|
3
|
+
import { retainedSessionControlRef, sessionPromptExecutionId, sessionPromptSessionId, sessionRefFromSandboxDispatch, } from "./tangle-session-control.js";
|
|
4
4
|
import { awaitWithSignal } from "./tangle-contract-safety.js";
|
|
5
5
|
import { interruptAfterAbort, sessionPromptRequestDigest, } from "./tangle-environment-control.js";
|
|
6
6
|
export function dispatchEnvironmentRun(box, provider, environmentId) {
|
|
7
7
|
return async (input) => {
|
|
8
8
|
AgentTurnInputSchema.parse(input);
|
|
9
9
|
input.signal?.throwIfAborted();
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const callerControlRef = input.controlRef === undefined
|
|
11
|
+
? undefined
|
|
12
|
+
: AgentExactRunControlRefSchema.parse(input.controlRef);
|
|
13
|
+
if (callerControlRef !== undefined) {
|
|
14
|
+
if (callerControlRef.provider !== provider ||
|
|
15
|
+
callerControlRef.environmentId !== environmentId) {
|
|
16
|
+
throw new Error("Tangle control reference does not match this environment");
|
|
17
|
+
}
|
|
18
|
+
if (input.sessionId !== undefined &&
|
|
19
|
+
input.sessionId !== callerControlRef.sessionId) {
|
|
20
|
+
throw new Error("Tangle sessionId conflicts with the control reference");
|
|
21
|
+
}
|
|
22
|
+
if (input.executionId !== undefined &&
|
|
23
|
+
input.executionId !== callerControlRef.executionId) {
|
|
24
|
+
throw new Error("Tangle executionId conflicts with the control reference");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const expectedSessionId = input.sessionId ??
|
|
28
|
+
callerControlRef?.sessionId ??
|
|
29
|
+
sessionPromptSessionId(provider, environmentId, input.turnId);
|
|
30
|
+
if (expectedSessionId === undefined) {
|
|
31
|
+
throw new Error("Tangle detached dispatch requires an exact sessionId or turnId");
|
|
32
|
+
}
|
|
33
|
+
const exactInput = { ...input, sessionId: expectedSessionId };
|
|
34
|
+
const requestedExecutionId = input.executionId ?? callerControlRef?.executionId;
|
|
35
|
+
const baseRequestDigest = sessionPromptRequestDigest(exactInput, provider, environmentId, expectedSessionId);
|
|
36
|
+
const expectedExecutionId = requestedExecutionId ?? sessionPromptExecutionId(baseRequestDigest);
|
|
37
|
+
const requestDigest = sessionPromptRequestDigest(exactInput, provider, environmentId, expectedSessionId, { executionId: expectedExecutionId });
|
|
38
|
+
if (callerControlRef !== undefined) {
|
|
39
|
+
if (callerControlRef.requestDigest !== requestDigest) {
|
|
40
|
+
throw new Error("Tangle prompt request digest conflicts with the control reference");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const runControlRef = retainedSessionControlRef(expectedSessionId, expectedExecutionId, provider, environmentId, requestDigest, callerControlRef?.runId);
|
|
44
|
+
const dispatchInput = {
|
|
45
|
+
...exactInput,
|
|
46
|
+
executionId: expectedExecutionId,
|
|
47
|
+
controlRef: runControlRef,
|
|
48
|
+
};
|
|
49
|
+
const promise = box.dispatchPrompt?.(promptFromTurnInput(dispatchInput), promptOptionsFromTurnInput(dispatchInput, { provider, environmentId }));
|
|
12
50
|
let dispatched;
|
|
13
51
|
try {
|
|
14
52
|
dispatched = await awaitWithSignal(promise, input.signal);
|
|
15
53
|
}
|
|
16
54
|
catch (error) {
|
|
17
|
-
if (input.signal?.aborted && promise) {
|
|
55
|
+
if (input.signal?.aborted && input.detach !== true && promise) {
|
|
18
56
|
void promise
|
|
19
57
|
.then((late) => {
|
|
20
|
-
const lateRef = sessionRefFromSandboxDispatch(late, provider, environmentId,
|
|
58
|
+
const lateRef = sessionRefFromSandboxDispatch(late, provider, environmentId, expectedExecutionId, requestDigest, expectedSessionId, callerControlRef);
|
|
21
59
|
return interruptAfterAbort(box, lateRef);
|
|
22
60
|
})
|
|
23
61
|
.catch(() => undefined);
|
|
@@ -26,7 +64,7 @@ export function dispatchEnvironmentRun(box, provider, environmentId) {
|
|
|
26
64
|
}
|
|
27
65
|
let reference;
|
|
28
66
|
try {
|
|
29
|
-
reference = sessionRefFromSandboxDispatch(dispatched, provider, environmentId,
|
|
67
|
+
reference = sessionRefFromSandboxDispatch(dispatched, provider, environmentId, expectedExecutionId, requestDigest, expectedSessionId, callerControlRef);
|
|
30
68
|
}
|
|
31
69
|
catch (error) {
|
|
32
70
|
try {
|
|
@@ -41,20 +79,12 @@ export function dispatchEnvironmentRun(box, provider, environmentId) {
|
|
|
41
79
|
}
|
|
42
80
|
throw error;
|
|
43
81
|
}
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
? undefined
|
|
47
|
-
: sessionPromptRequestDigest(input, provider, environmentId, reference.id, executionId);
|
|
48
|
-
const boundReference = executionId === undefined || requestDigest === undefined
|
|
49
|
-
? reference
|
|
50
|
-
: {
|
|
51
|
-
...reference,
|
|
52
|
-
controlRef: retainedSessionControlRef(reference.id, executionId, provider, environmentId, requestDigest),
|
|
53
|
-
};
|
|
54
|
-
if (input.signal?.aborted) {
|
|
82
|
+
const boundReference = reference;
|
|
83
|
+
if (input.signal?.aborted && input.detach !== true) {
|
|
55
84
|
await interruptAfterAbort(box, boundReference);
|
|
56
85
|
input.signal.throwIfAborted();
|
|
57
86
|
}
|
|
87
|
+
input.signal?.throwIfAborted();
|
|
58
88
|
return boundReference;
|
|
59
89
|
};
|
|
60
90
|
}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import type { AgentSession } from "@tangle-network/agent-interface/environment-provider";
|
|
2
|
-
import type { AgentRunControlRef } from "@tangle-network/agent-interface";
|
|
1
|
+
import type { AgentSession, AgentSessionRef, AgentTurnInput } from "@tangle-network/agent-interface/environment-provider";
|
|
2
|
+
import type { AgentExactRunControlRef, AgentRunControlRef } from "@tangle-network/agent-interface";
|
|
3
|
+
import type { SandboxEvent } from "@tangle-network/sandbox";
|
|
3
4
|
import type { SandboxSessionLike } from "./tangle-types.js";
|
|
4
|
-
|
|
5
|
+
type ExactExecutionEventStream = (options: {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
executionId: string;
|
|
8
|
+
since?: string;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
controlRef?: AgentExactRunControlRef;
|
|
11
|
+
}) => AsyncIterable<SandboxEvent>;
|
|
12
|
+
export declare function sandboxSessionAsAgentSession(session: SandboxSessionLike, controlRef: AgentRunControlRef | undefined, provider: string, environmentId: string, dispatch?: (input: AgentTurnInput) => Promise<AgentSessionRef>, exactExecutionEvents?: ExactExecutionEventStream): AgentSession;
|
|
13
|
+
export {};
|
|
@@ -1,17 +1,45 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { AgentTurnInputSchema } from "@tangle-network/agent-interface";
|
|
2
|
-
import {
|
|
3
|
+
import { AgentRunCancellationAcknowledgementSchema, AgentRunCancellationRequestSchema, agentRunCancellationAcknowledgementMatchesRequest, } from "@tangle-network/agent-interface";
|
|
4
|
+
import { environmentEventFromSandboxEvent, isSandboxConnectionMarker, sandboxEventIdentity, } from "./tangle-events.js";
|
|
3
5
|
import { agentTurnResultFromPromptRecord, promptFromTurnInput, promptOptionsFromTurnInput, validatedSandboxPromptResult, } from "./tangle-prompt.js";
|
|
4
6
|
import { retainedSessionControlRef, resolveRetainedSessionControlRef, sameRunControlRef, sessionPromptExecutionId, } from "./tangle-session-control.js";
|
|
5
7
|
import { sessionStatusFromUnknown } from "./tangle-environment-values.js";
|
|
6
8
|
import { awaitWithSignal, boundedIdentifier, } from "./tangle-contract-safety.js";
|
|
7
9
|
import { assertOptionKeys } from "./tangle-environment-validation.js";
|
|
8
10
|
import { hasReplayPayload, interruptExecutionAfterAbort, sessionPromptRequestDigest, } from "./tangle-environment-control.js";
|
|
9
|
-
export function sandboxSessionAsAgentSession(session, controlRef, provider, environmentId) {
|
|
10
|
-
let activeControlRef = controlRef
|
|
11
|
+
export function sandboxSessionAsAgentSession(session, controlRef, provider, environmentId, dispatch, exactExecutionEvents) {
|
|
12
|
+
let activeControlRef = controlRef
|
|
13
|
+
? resolveRetainedSessionControlRef(controlRef, session.id, provider, environmentId)
|
|
14
|
+
: undefined;
|
|
15
|
+
let promptInFlight = false;
|
|
16
|
+
const cancelRunMethod = session.cancelRun;
|
|
17
|
+
const cancelRun = typeof cancelRunMethod === "function"
|
|
18
|
+
? async (request, options) => {
|
|
19
|
+
assertOptionKeys(options, ["signal"], "Tangle exact session cancellation");
|
|
20
|
+
const exactRequest = AgentRunCancellationRequestSchema.parse(request);
|
|
21
|
+
const expectedRun = activeControlRef;
|
|
22
|
+
if (expectedRun === undefined) {
|
|
23
|
+
throw new Error("Tangle exact session cancellation requires an exact run control reference");
|
|
24
|
+
}
|
|
25
|
+
if (!sameRunControlRef(exactRequest.run, expectedRun)) {
|
|
26
|
+
throw new Error("Tangle exact session cancellation targets another run, session, execution, or request");
|
|
27
|
+
}
|
|
28
|
+
options?.signal?.throwIfAborted();
|
|
29
|
+
const acknowledgement = await awaitWithSignal(cancelRunMethod.call(session, exactRequest, options?.signal ? { signal: options.signal } : undefined), options?.signal);
|
|
30
|
+
options?.signal?.throwIfAborted();
|
|
31
|
+
const exactAcknowledgement = AgentRunCancellationAcknowledgementSchema.parse(acknowledgement);
|
|
32
|
+
if (!agentRunCancellationAcknowledgementMatchesRequest(exactRequest, exactAcknowledgement) ||
|
|
33
|
+
!sameRunControlRef(exactAcknowledgement.run, expectedRun)) {
|
|
34
|
+
throw new Error("Tangle exact session cancellation returned an acknowledgement for another run, session, execution, or request");
|
|
35
|
+
}
|
|
36
|
+
return exactAcknowledgement;
|
|
37
|
+
}
|
|
38
|
+
: undefined;
|
|
11
39
|
return {
|
|
12
40
|
id: session.id,
|
|
13
41
|
get controlRef() {
|
|
14
|
-
return activeControlRef;
|
|
42
|
+
return activeControlRef === undefined ? undefined : { ...activeControlRef };
|
|
15
43
|
},
|
|
16
44
|
async status(options) {
|
|
17
45
|
assertOptionKeys(options, ["signal"], "Tangle session status");
|
|
@@ -36,11 +64,20 @@ export function sandboxSessionAsAgentSession(session, controlRef, provider, envi
|
|
|
36
64
|
}
|
|
37
65
|
options?.signal?.throwIfAborted();
|
|
38
66
|
const seenEventIds = new Set();
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
const useExactExecutionStream = exactExecutionEvents !== undefined && executionId !== undefined;
|
|
68
|
+
const iterator = (useExactExecutionStream
|
|
69
|
+
? exactExecutionEvents({
|
|
70
|
+
sessionId: session.id,
|
|
71
|
+
executionId,
|
|
72
|
+
...(options?.since !== undefined ? { since: options.since } : {}),
|
|
73
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
74
|
+
...(activeControlRef ? { controlRef: activeControlRef } : {}),
|
|
75
|
+
})
|
|
76
|
+
: session.events({
|
|
77
|
+
...(options?.since !== undefined ? { since: options.since } : {}),
|
|
78
|
+
...(executionId !== undefined ? { executionId } : {}),
|
|
79
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
80
|
+
}))[Symbol.asyncIterator]();
|
|
44
81
|
let completed = false;
|
|
45
82
|
try {
|
|
46
83
|
while (true) {
|
|
@@ -50,16 +87,28 @@ export function sandboxSessionAsAgentSession(session, controlRef, provider, envi
|
|
|
50
87
|
break;
|
|
51
88
|
}
|
|
52
89
|
options?.signal?.throwIfAborted();
|
|
53
|
-
if (
|
|
90
|
+
if (isSandboxConnectionMarker(next.value)) {
|
|
91
|
+
const markerIdentity = sandboxEventIdentity(next.value);
|
|
92
|
+
if (executionId !== undefined &&
|
|
93
|
+
markerIdentity.executionId !== undefined &&
|
|
94
|
+
markerIdentity.executionId !== executionId) {
|
|
95
|
+
throw new Error("Tangle exact session connection identified a different executionId");
|
|
96
|
+
}
|
|
97
|
+
if (markerIdentity.sessionId !== undefined &&
|
|
98
|
+
markerIdentity.sessionId !== session.id) {
|
|
99
|
+
throw new Error("Tangle exact session connection identified a different sessionId");
|
|
100
|
+
}
|
|
54
101
|
continue;
|
|
102
|
+
}
|
|
55
103
|
const converted = environmentEventFromSandboxEvent(next.value, {
|
|
56
104
|
executionId,
|
|
57
105
|
sessionId: session.id,
|
|
106
|
+
...(useExactExecutionStream ? { streamBound: true } : {}),
|
|
58
107
|
});
|
|
59
108
|
if (converted.id === undefined)
|
|
60
109
|
throw new Error("Tangle session event arrived without a stable id");
|
|
61
110
|
if (seenEventIds.has(converted.id))
|
|
62
|
-
|
|
111
|
+
continue;
|
|
63
112
|
seenEventIds.add(converted.id);
|
|
64
113
|
options?.signal?.throwIfAborted();
|
|
65
114
|
yield converted;
|
|
@@ -82,63 +131,160 @@ export function sandboxSessionAsAgentSession(session, controlRef, provider, envi
|
|
|
82
131
|
const resultRecord = validatedSandboxPromptResult(result);
|
|
83
132
|
if (resultRecord.executionId !== expectedExecutionId)
|
|
84
133
|
throw new Error("Tangle session result did not confirm its exact executionId");
|
|
85
|
-
return agentTurnResultFromPromptRecord(resultRecord, {
|
|
134
|
+
return agentTurnResultFromPromptRecord(resultRecord, {
|
|
135
|
+
sessionId: session.id,
|
|
136
|
+
controlRef: activeControlRef,
|
|
137
|
+
});
|
|
86
138
|
},
|
|
87
139
|
async prompt(input) {
|
|
88
140
|
AgentTurnInputSchema.parse(input);
|
|
89
141
|
input.signal?.throwIfAborted();
|
|
90
|
-
if (
|
|
91
|
-
throw new Error("Tangle
|
|
92
|
-
const requestedControlRef = resolveRetainedSessionControlRef(input.controlRef, session.id, provider, environmentId);
|
|
93
|
-
if (activeControlRef && requestedControlRef && !sameRunControlRef(activeControlRef, requestedControlRef))
|
|
94
|
-
throw new Error("Tangle prompt control reference conflicts with this session");
|
|
95
|
-
const sourceControlRef = requestedControlRef ?? activeControlRef;
|
|
96
|
-
const replay = input.lastEventId !== undefined;
|
|
97
|
-
if (replay && sourceControlRef?.executionId !== undefined && input.executionId !== undefined && input.executionId !== sourceControlRef.executionId)
|
|
98
|
-
throw new Error("Tangle replay executionId conflicts with the control reference");
|
|
99
|
-
if (replay && sourceControlRef?.requestDigest === undefined) {
|
|
100
|
-
throw new Error("Tangle replay requires an exact request digest from its control reference");
|
|
101
|
-
}
|
|
102
|
-
const executionId = replay ? input.executionId ?? sourceControlRef?.executionId : input.executionId ?? sessionPromptExecutionId(provider, environmentId, session.id, input.turnId);
|
|
103
|
-
if (executionId === undefined)
|
|
104
|
-
throw new Error("Tangle session replay requires the exact executionId from its control reference");
|
|
105
|
-
const computedRequestDigest = sessionPromptRequestDigest(input, provider, environmentId, session.id, executionId);
|
|
106
|
-
const sameExecution = sourceControlRef?.executionId === executionId;
|
|
107
|
-
if (sourceControlRef?.requestDigest !== undefined &&
|
|
108
|
-
sameExecution &&
|
|
109
|
-
(!replay || hasReplayPayload(input)) &&
|
|
110
|
-
sourceControlRef.requestDigest !== computedRequestDigest) {
|
|
111
|
-
throw new Error("Tangle prompt request digest conflicts with the control reference");
|
|
142
|
+
if (promptInFlight) {
|
|
143
|
+
throw new Error("Tangle session already has a prompt in flight");
|
|
112
144
|
}
|
|
113
|
-
|
|
114
|
-
? sourceControlRef?.requestDigest
|
|
115
|
-
: computedRequestDigest;
|
|
116
|
-
if (requestDigest === undefined) {
|
|
117
|
-
throw new Error("Tangle prompt could not establish an exact request digest");
|
|
118
|
-
}
|
|
119
|
-
const promptInput = replay
|
|
120
|
-
? { ...input, sessionId: session.id, executionId, controlRef: sourceControlRef }
|
|
121
|
-
: { ...input, sessionId: session.id, executionId, controlRef: undefined };
|
|
145
|
+
promptInFlight = true;
|
|
122
146
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const resultRecord = validatedSandboxPromptResult(result);
|
|
126
|
-
if (resultRecord.executionId !== executionId) {
|
|
127
|
-
void interruptExecutionAfterAbort(session, session.id, executionId);
|
|
128
|
-
throw new Error("Tangle session prompt did not confirm its exact executionId");
|
|
147
|
+
if (input.sessionId !== undefined && input.sessionId !== session.id) {
|
|
148
|
+
throw new Error("Tangle sessionId conflicts with this session");
|
|
129
149
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
150
|
+
const requestedControlRef = resolveRetainedSessionControlRef(input.controlRef, session.id, provider, environmentId);
|
|
151
|
+
if (activeControlRef &&
|
|
152
|
+
requestedControlRef &&
|
|
153
|
+
!sameRunControlRef(activeControlRef, requestedControlRef)) {
|
|
154
|
+
throw new Error("Tangle prompt control reference conflicts with this session");
|
|
155
|
+
}
|
|
156
|
+
if (requestedControlRef?.executionId !== undefined &&
|
|
157
|
+
input.executionId !== undefined &&
|
|
158
|
+
requestedControlRef.executionId !== input.executionId) {
|
|
159
|
+
throw new Error("Tangle executionId conflicts with the control reference");
|
|
160
|
+
}
|
|
161
|
+
if (input.detach === true && input.lastEventId === undefined) {
|
|
162
|
+
if (dispatch === undefined) {
|
|
163
|
+
throw new Error("Tangle detached session prompt requires the sandbox dispatch primitive");
|
|
164
|
+
}
|
|
165
|
+
const detachedInput = {
|
|
166
|
+
...input,
|
|
167
|
+
signal: undefined,
|
|
168
|
+
controlRef: undefined,
|
|
169
|
+
sessionId: session.id,
|
|
170
|
+
detach: true,
|
|
171
|
+
...(input.turnId === undefined ? { turnId: randomUUID() } : {}),
|
|
172
|
+
};
|
|
173
|
+
const reference = await dispatch(detachedInput);
|
|
174
|
+
const nextControlRef = resolveRetainedSessionControlRef(reference.controlRef, session.id, provider, environmentId);
|
|
175
|
+
if (nextControlRef === undefined) {
|
|
176
|
+
throw new Error("Tangle detached session dispatch returned no exact control reference");
|
|
177
|
+
}
|
|
178
|
+
// The admission receipt is the durability boundary. Store it before
|
|
179
|
+
// waiting for the result so an aborted caller can reconnect later.
|
|
180
|
+
activeControlRef = nextControlRef;
|
|
181
|
+
const result = await awaitWithSignal(session.result({
|
|
182
|
+
executionId: nextControlRef.executionId,
|
|
183
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
184
|
+
}), input.signal);
|
|
185
|
+
input.signal?.throwIfAborted();
|
|
186
|
+
const resultRecord = validatedSandboxPromptResult(result);
|
|
187
|
+
if (resultRecord.executionId !== nextControlRef.executionId) {
|
|
188
|
+
throw new Error("Tangle detached session prompt did not confirm its exact executionId");
|
|
189
|
+
}
|
|
190
|
+
return agentTurnResultFromPromptRecord(resultRecord, {
|
|
191
|
+
sessionId: session.id,
|
|
192
|
+
controlRef: nextControlRef,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const sourceControlRef = requestedControlRef ?? activeControlRef;
|
|
196
|
+
const replay = input.lastEventId !== undefined;
|
|
197
|
+
if (replay &&
|
|
198
|
+
sourceControlRef?.executionId !== undefined &&
|
|
199
|
+
input.executionId !== undefined &&
|
|
200
|
+
input.executionId !== sourceControlRef.executionId) {
|
|
201
|
+
throw new Error("Tangle replay executionId conflicts with the control reference");
|
|
202
|
+
}
|
|
203
|
+
if (replay && sourceControlRef?.requestDigest === undefined) {
|
|
204
|
+
throw new Error("Tangle replay requires an exact request digest from its control reference");
|
|
205
|
+
}
|
|
206
|
+
const requestedExecutionId = input.executionId ?? requestedControlRef?.executionId;
|
|
207
|
+
const nonce = !replay &&
|
|
208
|
+
input.turnId === undefined &&
|
|
209
|
+
requestedExecutionId === undefined &&
|
|
210
|
+
requestedControlRef === undefined
|
|
211
|
+
? randomUUID()
|
|
212
|
+
: undefined;
|
|
213
|
+
const baseRequestDigest = sessionPromptRequestDigest(input, provider, environmentId, session.id, nonce === undefined ? {} : { nonce });
|
|
214
|
+
const executionId = replay
|
|
215
|
+
? input.executionId ?? sourceControlRef?.executionId
|
|
216
|
+
: requestedExecutionId ?? sessionPromptExecutionId(baseRequestDigest);
|
|
217
|
+
if (executionId === undefined) {
|
|
218
|
+
throw new Error("Tangle session replay requires the exact executionId from its control reference");
|
|
219
|
+
}
|
|
220
|
+
const explicitRequestDigest = sessionPromptRequestDigest(input, provider, environmentId, session.id, {
|
|
221
|
+
executionId,
|
|
222
|
+
...(nonce === undefined ? {} : { nonce }),
|
|
135
223
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
224
|
+
if (!replay &&
|
|
225
|
+
requestedControlRef?.requestDigest !== undefined &&
|
|
226
|
+
requestedControlRef.requestDigest !== explicitRequestDigest) {
|
|
227
|
+
throw new Error("Tangle prompt request digest conflicts with the control reference");
|
|
228
|
+
}
|
|
229
|
+
if (replay &&
|
|
230
|
+
hasReplayPayload(input) &&
|
|
231
|
+
sourceControlRef?.requestDigest !== explicitRequestDigest) {
|
|
232
|
+
throw new Error("Tangle prompt request digest conflicts with the control reference");
|
|
140
233
|
}
|
|
141
|
-
|
|
234
|
+
const requestDigest = replay
|
|
235
|
+
? sourceControlRef?.requestDigest
|
|
236
|
+
: requestedControlRef?.requestDigest ?? explicitRequestDigest;
|
|
237
|
+
if (requestDigest === undefined) {
|
|
238
|
+
throw new Error("Tangle prompt could not establish an exact request digest");
|
|
239
|
+
}
|
|
240
|
+
const targetControlRef = retainedSessionControlRef(session.id, executionId, provider, environmentId, requestDigest, requestedControlRef?.runId);
|
|
241
|
+
const promptInput = replay
|
|
242
|
+
? {
|
|
243
|
+
...input,
|
|
244
|
+
sessionId: session.id,
|
|
245
|
+
executionId,
|
|
246
|
+
controlRef: sourceControlRef,
|
|
247
|
+
}
|
|
248
|
+
: {
|
|
249
|
+
...input,
|
|
250
|
+
sessionId: session.id,
|
|
251
|
+
executionId,
|
|
252
|
+
controlRef: targetControlRef,
|
|
253
|
+
};
|
|
254
|
+
try {
|
|
255
|
+
const result = await awaitWithSignal(session.prompt(promptFromTurnInput(input), promptOptionsFromTurnInput(promptInput, {
|
|
256
|
+
provider,
|
|
257
|
+
environmentId,
|
|
258
|
+
sessionId: session.id,
|
|
259
|
+
})), input.signal);
|
|
260
|
+
input.signal?.throwIfAborted();
|
|
261
|
+
const resultRecord = validatedSandboxPromptResult(result);
|
|
262
|
+
if (resultRecord.executionId !== executionId) {
|
|
263
|
+
void interruptExecutionAfterAbort(session, session.id, executionId);
|
|
264
|
+
throw new Error("Tangle session prompt did not confirm its exact executionId");
|
|
265
|
+
}
|
|
266
|
+
const nextControlRef = targetControlRef;
|
|
267
|
+
activeControlRef = nextControlRef;
|
|
268
|
+
return agentTurnResultFromPromptRecord(resultRecord, {
|
|
269
|
+
sessionId: session.id,
|
|
270
|
+
controlRef: nextControlRef,
|
|
271
|
+
...(input.contextTransfer
|
|
272
|
+
? { contextTransferRequest: input.contextTransfer }
|
|
273
|
+
: {}),
|
|
274
|
+
...(input.contextTransfer
|
|
275
|
+
? { contextTransferRequested: true }
|
|
276
|
+
: {}),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
if (input.signal?.aborted && input.detach !== true) {
|
|
281
|
+
void interruptExecutionAfterAbort(session, session.id, executionId);
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
promptInFlight = false;
|
|
142
288
|
}
|
|
143
289
|
},
|
|
144
290
|
async cancel(options) {
|
|
@@ -152,5 +298,6 @@ export function sandboxSessionAsAgentSession(session, controlRef, provider, envi
|
|
|
152
298
|
if (result.cancelled !== true)
|
|
153
299
|
throw new Error("Tangle sandbox did not confirm cancellation");
|
|
154
300
|
},
|
|
301
|
+
...(cancelRun ? { cancelRun } : {}),
|
|
155
302
|
};
|
|
156
303
|
}
|
|
@@ -21,6 +21,18 @@ export function sandboxInstanceAsEnvironment(box, providerName, client, declared
|
|
|
21
21
|
}
|
|
22
22
|
const support = sandboxCapabilitySupport(box, client);
|
|
23
23
|
const capabilities = capabilitiesForSandbox(declaredCapabilities, support);
|
|
24
|
+
const dispatch = capabilities.streaming.detach && box.dispatchPrompt
|
|
25
|
+
? dispatchEnvironmentRun(box, providerName, environmentId)
|
|
26
|
+
: undefined;
|
|
27
|
+
const exactExecutionEvents = (options) => box.streamPrompt("", {
|
|
28
|
+
sessionId: options.sessionId,
|
|
29
|
+
executionId: options.executionId,
|
|
30
|
+
// The execution replay endpoint owns stable event IDs. A missing cursor
|
|
31
|
+
// starts at the beginning so the caller can journal a complete run.
|
|
32
|
+
lastEventId: options.since ?? "0",
|
|
33
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
34
|
+
...(options.controlRef ? { runControlRef: options.controlRef } : {}),
|
|
35
|
+
});
|
|
24
36
|
return {
|
|
25
37
|
id: environmentId,
|
|
26
38
|
provider: providerName,
|
|
@@ -51,6 +63,9 @@ export function sandboxInstanceAsEnvironment(box, providerName, client, declared
|
|
|
51
63
|
const converted = environmentEventFromSandboxEvent(next.value, {
|
|
52
64
|
executionId: expectedExecutionId,
|
|
53
65
|
sessionId: expectedSessionId,
|
|
66
|
+
...(expectedExecutionId !== undefined || expectedSessionId !== undefined
|
|
67
|
+
? { streamBound: true }
|
|
68
|
+
: {}),
|
|
54
69
|
});
|
|
55
70
|
input.signal?.throwIfAborted();
|
|
56
71
|
yield converted;
|
|
@@ -58,6 +73,7 @@ export function sandboxInstanceAsEnvironment(box, providerName, client, declared
|
|
|
58
73
|
}
|
|
59
74
|
catch (error) {
|
|
60
75
|
if (input.signal?.aborted &&
|
|
76
|
+
input.detach !== true &&
|
|
61
77
|
expectedSessionId !== undefined &&
|
|
62
78
|
expectedExecutionId !== undefined) {
|
|
63
79
|
void interruptExecutionAfterAbort(box, expectedSessionId, expectedExecutionId);
|
|
@@ -71,9 +87,7 @@ export function sandboxInstanceAsEnvironment(box, providerName, client, declared
|
|
|
71
87
|
}
|
|
72
88
|
input.signal?.throwIfAborted();
|
|
73
89
|
},
|
|
74
|
-
...(
|
|
75
|
-
? { dispatch: dispatchEnvironmentRun(box, providerName, environmentId) }
|
|
76
|
-
: {}),
|
|
90
|
+
...(dispatch ? { dispatch } : {}),
|
|
77
91
|
...((capabilities.sessions.continue || capabilities.streaming.replay || capabilities.streaming.detach) &&
|
|
78
92
|
box.session
|
|
79
93
|
? {
|
|
@@ -89,7 +103,12 @@ export function sandboxInstanceAsEnvironment(box, providerName, client, declared
|
|
|
89
103
|
if (session.id !== id) {
|
|
90
104
|
throw new Error("sandbox session(id) returned an unrelated session");
|
|
91
105
|
}
|
|
92
|
-
|
|
106
|
+
const agentSession = sandboxSessionAsAgentSession(session, resolveRetainedSessionControlRef(options?.controlRef, id, providerName, environmentId), providerName, environmentId, dispatch, exactExecutionEvents);
|
|
107
|
+
if (capabilities.sessions.continue &&
|
|
108
|
+
typeof agentSession.cancelRun !== "function") {
|
|
109
|
+
throw new Error("Tangle retained session support requires SandboxSession.cancelRun");
|
|
110
|
+
}
|
|
111
|
+
return agentSession;
|
|
93
112
|
},
|
|
94
113
|
}
|
|
95
114
|
: {}),
|
package/dist/tangle-events.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { SandboxEvent } from "@tangle-network/sandbox";
|
|
2
2
|
import type { AgentEnvironmentEvent } from "@tangle-network/agent-interface/environment-provider";
|
|
3
|
+
/** The sidecar sends this envelope when an SSE connection is ready. */
|
|
4
|
+
export declare function isSandboxConnectionMarker(event: SandboxEvent): boolean;
|
|
5
|
+
/** Read identity from both run frames and /agents/events session envelopes. */
|
|
6
|
+
export declare function sandboxEventIdentity(event: SandboxEvent): {
|
|
7
|
+
executionId?: string;
|
|
8
|
+
sessionId?: string;
|
|
9
|
+
};
|
|
3
10
|
export declare function environmentEventFromSandboxEvent(event: SandboxEvent, expected?: {
|
|
4
11
|
executionId?: string;
|
|
5
12
|
sessionId?: string;
|
|
13
|
+
streamBound?: boolean;
|
|
6
14
|
}): AgentEnvironmentEvent;
|
package/dist/tangle-events.js
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
|
+
import { CanonicalStreamEventSchema, } from "@tangle-network/agent-interface";
|
|
1
2
|
import { assertBoundedJson } from "./tangle-contract-safety.js";
|
|
2
3
|
import { optionalNonEmptyString } from "./tangle-environment-values.js";
|
|
3
4
|
import { tokenUsageFromData } from "./tangle-result-values.js";
|
|
5
|
+
/** The sidecar sends this envelope when an SSE connection is ready. */
|
|
6
|
+
export function isSandboxConnectionMarker(event) {
|
|
7
|
+
if (!event || typeof event !== "object")
|
|
8
|
+
return false;
|
|
9
|
+
const record = event;
|
|
10
|
+
if (record.type === "connection.established")
|
|
11
|
+
return true;
|
|
12
|
+
const data = record.data;
|
|
13
|
+
return (data !== null &&
|
|
14
|
+
typeof data === "object" &&
|
|
15
|
+
!Array.isArray(data) &&
|
|
16
|
+
data.type === "connection.established");
|
|
17
|
+
}
|
|
18
|
+
/** Read identity from both run frames and /agents/events session envelopes. */
|
|
19
|
+
export function sandboxEventIdentity(event) {
|
|
20
|
+
const record = event;
|
|
21
|
+
const data = record.data;
|
|
22
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
const dataRecord = data;
|
|
26
|
+
const properties = dataRecord.properties &&
|
|
27
|
+
typeof dataRecord.properties === "object" &&
|
|
28
|
+
!Array.isArray(dataRecord.properties)
|
|
29
|
+
? dataRecord.properties
|
|
30
|
+
: undefined;
|
|
31
|
+
const info = properties?.info &&
|
|
32
|
+
typeof properties.info === "object" &&
|
|
33
|
+
!Array.isArray(properties.info)
|
|
34
|
+
? properties.info
|
|
35
|
+
: undefined;
|
|
36
|
+
return {
|
|
37
|
+
executionId: optionalNonEmptyString(dataRecord.executionId ?? properties?.executionId ?? info?.executionId, "Tangle Sandbox event executionId"),
|
|
38
|
+
sessionId: optionalNonEmptyString(dataRecord.sessionId ??
|
|
39
|
+
dataRecord.sessionID ??
|
|
40
|
+
properties?.sessionId ??
|
|
41
|
+
properties?.sessionID ??
|
|
42
|
+
info?.sessionId ??
|
|
43
|
+
info?.sessionID, "Tangle Sandbox event sessionId"),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
4
46
|
export function environmentEventFromSandboxEvent(event, expected = {}) {
|
|
5
47
|
if (!event || typeof event !== "object") {
|
|
6
48
|
throw new Error("Tangle Sandbox emitted a non-object event");
|
|
@@ -30,27 +72,145 @@ export function environmentEventFromSandboxEvent(event, expected = {}) {
|
|
|
30
72
|
if (Object.prototype.hasOwnProperty.call(data, "contextTransferReceipt")) {
|
|
31
73
|
throw new Error("Tangle Sandbox emitted an unsolicited context transfer receipt");
|
|
32
74
|
}
|
|
33
|
-
const
|
|
34
|
-
const eventSessionId = optionalNonEmptyString(data.sessionId, "Tangle Sandbox event sessionId");
|
|
75
|
+
const { executionId: eventExecutionId, sessionId: eventSessionId } = sandboxEventIdentity(event);
|
|
35
76
|
if (expected.executionId !== undefined &&
|
|
36
|
-
(eventExecutionId === undefined
|
|
77
|
+
((eventExecutionId === undefined && expected.streamBound !== true) ||
|
|
78
|
+
(eventExecutionId !== undefined && eventExecutionId !== expected.executionId))) {
|
|
37
79
|
throw new Error("Tangle exact session event identified a different executionId");
|
|
38
80
|
}
|
|
39
81
|
if (expected.sessionId !== undefined &&
|
|
40
|
-
(eventSessionId === undefined
|
|
82
|
+
((eventSessionId === undefined && expected.streamBound !== true) ||
|
|
83
|
+
(eventSessionId !== undefined && eventSessionId !== expected.sessionId))) {
|
|
41
84
|
throw new Error("Tangle exact session event identified a different sessionId");
|
|
42
85
|
}
|
|
43
86
|
const usage = tokenUsageFromData(data);
|
|
87
|
+
const normalized = normalizeSandboxEvent(record.type, data);
|
|
44
88
|
return {
|
|
45
89
|
type: record.type,
|
|
46
90
|
data,
|
|
47
91
|
...(typeof record.id === "string" ? { id: record.id } : {}),
|
|
92
|
+
...(normalized ? { normalized } : {}),
|
|
48
93
|
// Absent rather than zeroed: an event that reported no usage must not
|
|
49
94
|
// contribute a total to whatever sums these events.
|
|
50
95
|
...(usage ? { usage } : {}),
|
|
51
96
|
providerEvent: event,
|
|
52
97
|
};
|
|
53
98
|
}
|
|
99
|
+
function normalizeSandboxEvent(type, data) {
|
|
100
|
+
const supplied = data.normalized;
|
|
101
|
+
if (supplied !== undefined) {
|
|
102
|
+
const parsed = CanonicalStreamEventSchema.safeParse(supplied);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
throw new Error("Tangle Sandbox emitted an invalid normalized event");
|
|
105
|
+
}
|
|
106
|
+
if (parsed.data.type !== type) {
|
|
107
|
+
throw new Error(`Tangle Sandbox normalized event type "${parsed.data.type}" does not match transport type "${type}"`);
|
|
108
|
+
}
|
|
109
|
+
return parsed.data;
|
|
110
|
+
}
|
|
111
|
+
switch (type) {
|
|
112
|
+
case "status": {
|
|
113
|
+
const status = statusFromSandboxValue(data.status);
|
|
114
|
+
if (!status)
|
|
115
|
+
return undefined;
|
|
116
|
+
return {
|
|
117
|
+
type: "status",
|
|
118
|
+
status,
|
|
119
|
+
...detailFromSandboxData(data),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
case "message.part.updated": {
|
|
123
|
+
const candidate = {
|
|
124
|
+
type,
|
|
125
|
+
...(data.part !== undefined ? { part: data.part } : {}),
|
|
126
|
+
...(typeof data.delta === "string" ? { delta: data.delta } : {}),
|
|
127
|
+
};
|
|
128
|
+
const parsed = CanonicalStreamEventSchema.safeParse(candidate);
|
|
129
|
+
return parsed.success ? parsed.data : undefined;
|
|
130
|
+
}
|
|
131
|
+
case "tool-heartbeat":
|
|
132
|
+
return parseCanonical({
|
|
133
|
+
type,
|
|
134
|
+
toolName: data.toolName,
|
|
135
|
+
partId: data.partId,
|
|
136
|
+
elapsedMs: data.elapsedMs,
|
|
137
|
+
});
|
|
138
|
+
case "tool-slow":
|
|
139
|
+
return parseCanonical({
|
|
140
|
+
type,
|
|
141
|
+
toolName: data.toolName,
|
|
142
|
+
partId: data.partId,
|
|
143
|
+
elapsedMs: data.elapsedMs,
|
|
144
|
+
thresholdMs: data.thresholdMs,
|
|
145
|
+
});
|
|
146
|
+
case "model-processing":
|
|
147
|
+
return parseCanonical({
|
|
148
|
+
type,
|
|
149
|
+
phase: data.phase,
|
|
150
|
+
...(typeof data.toolName === "string" ? { toolName: data.toolName } : {}),
|
|
151
|
+
...(typeof data.elapsedMs === "number" ? { elapsedMs: data.elapsedMs } : {}),
|
|
152
|
+
});
|
|
153
|
+
case "warning":
|
|
154
|
+
return parseCanonical({
|
|
155
|
+
type,
|
|
156
|
+
code: data.code,
|
|
157
|
+
message: data.message,
|
|
158
|
+
});
|
|
159
|
+
case "session.updated":
|
|
160
|
+
return parseCanonical({
|
|
161
|
+
type,
|
|
162
|
+
sessionId: data.sessionId ?? data.sessionID,
|
|
163
|
+
...(typeof data.title === "string" ? { title: data.title } : {}),
|
|
164
|
+
...(data.time !== undefined ? { time: data.time } : {}),
|
|
165
|
+
});
|
|
166
|
+
case "interaction":
|
|
167
|
+
return parseCanonical({ type, request: data.request });
|
|
168
|
+
case "interaction.cancel":
|
|
169
|
+
return parseCanonical({
|
|
170
|
+
type,
|
|
171
|
+
id: data.id,
|
|
172
|
+
...(typeof data.reason === "string" ? { reason: data.reason } : {}),
|
|
173
|
+
});
|
|
174
|
+
case "plan.submitted":
|
|
175
|
+
return parseCanonical({ type, plan: data.plan });
|
|
176
|
+
case "result":
|
|
177
|
+
case "done":
|
|
178
|
+
default:
|
|
179
|
+
// These transport frames carry terminal/result data, but they are not
|
|
180
|
+
// members of the provider-neutral canonical event union. The exact
|
|
181
|
+
// result endpoint remains the authoritative result surface.
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function parseCanonical(value) {
|
|
186
|
+
const parsed = CanonicalStreamEventSchema.safeParse(value);
|
|
187
|
+
return parsed.success ? parsed.data : undefined;
|
|
188
|
+
}
|
|
189
|
+
function statusFromSandboxValue(value) {
|
|
190
|
+
if (typeof value !== "string")
|
|
191
|
+
return undefined;
|
|
192
|
+
switch (value) {
|
|
193
|
+
case "started":
|
|
194
|
+
case "queued":
|
|
195
|
+
return "started";
|
|
196
|
+
case "processing":
|
|
197
|
+
case "running":
|
|
198
|
+
return "processing";
|
|
199
|
+
case "completed":
|
|
200
|
+
case "success":
|
|
201
|
+
return "completed";
|
|
202
|
+
case "failed":
|
|
203
|
+
case "error":
|
|
204
|
+
case "cancelled":
|
|
205
|
+
return "failed";
|
|
206
|
+
default:
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function detailFromSandboxData(data) {
|
|
211
|
+
const detail = [data.detail, data.error, data.message].find((value) => typeof value === "string" && value.length > 0);
|
|
212
|
+
return detail === undefined ? {} : { detail };
|
|
213
|
+
}
|
|
54
214
|
function assertBoundedRecord(value) {
|
|
55
215
|
if (Object.keys(value).length > 256) {
|
|
56
216
|
throw new Error("Tangle Sandbox event data has too many fields");
|
package/dist/tangle-prompt.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PromptResult } from "@tangle-network/sandbox";
|
|
2
2
|
import type { AgentTurnInput, AgentTurnResult } from "@tangle-network/agent-interface/environment-provider";
|
|
3
|
-
import type { InputPart } from "@tangle-network/agent-interface";
|
|
3
|
+
import type { AgentExactRunControlRef, InputPart } from "@tangle-network/agent-interface";
|
|
4
|
+
import type { TanglePromptOptions } from "./tangle-types.js";
|
|
4
5
|
export declare function promptFromTurnInput(input: AgentTurnInput): string | InputPart[];
|
|
5
6
|
export declare function executionIdFromTurnInput(input: AgentTurnInput): string | undefined;
|
|
6
7
|
export declare function promptOptionsFromTurnInput(input: AgentTurnInput, target: {
|
|
7
8
|
provider: string;
|
|
8
9
|
environmentId: string;
|
|
9
10
|
sessionId?: string;
|
|
10
|
-
}):
|
|
11
|
+
}): TanglePromptOptions;
|
|
11
12
|
type SandboxRunStatus = "success" | "failed" | "blocked_on_approval" | "awaiting_question" | "awaiting_plan_decision";
|
|
12
13
|
type ValidatedSandboxPromptResult = Record<string, unknown> & {
|
|
13
14
|
success: boolean;
|
|
@@ -20,5 +21,6 @@ export declare function agentTurnResultFromPromptRecord(record: ValidatedSandbox
|
|
|
20
21
|
contextTransferRequested?: boolean;
|
|
21
22
|
contextTransferRequest?: import("@tangle-network/agent-interface").ContextTransferRequest;
|
|
22
23
|
sessionId?: string;
|
|
24
|
+
controlRef?: AgentExactRunControlRef;
|
|
23
25
|
}): AgentTurnResult;
|
|
24
26
|
export {};
|
package/dist/tangle-prompt.js
CHANGED
|
@@ -31,9 +31,6 @@ export function promptOptionsFromTurnInput(input, target) {
|
|
|
31
31
|
if (controlRef.sessionId === undefined || controlRef.executionId === undefined) {
|
|
32
32
|
throw new Error("Tangle control reference requires exact sessionId and executionId");
|
|
33
33
|
}
|
|
34
|
-
if (controlRef.runId !== controlRef.executionId) {
|
|
35
|
-
throw new Error("Tangle control reference requires runId to equal executionId");
|
|
36
|
-
}
|
|
37
34
|
if (input.sessionId !== undefined &&
|
|
38
35
|
input.sessionId !== controlRef.sessionId) {
|
|
39
36
|
throw new Error("Tangle sessionId conflicts with the control reference");
|
|
@@ -55,6 +52,7 @@ export function promptOptionsFromTurnInput(input, target) {
|
|
|
55
52
|
...(input.context ? { context: input.context } : {}),
|
|
56
53
|
...(input.signal ? { signal: input.signal } : {}),
|
|
57
54
|
...(executionId ? { executionId } : {}),
|
|
55
|
+
...(controlRef ? { runControlRef: controlRef } : {}),
|
|
58
56
|
...(input.lastEventId ? { lastEventId: input.lastEventId } : {}),
|
|
59
57
|
...(input.turnId ? { turnId: input.turnId } : {}),
|
|
60
58
|
...(input.detach !== undefined ? { detach: input.detach } : {}),
|
|
@@ -118,6 +116,18 @@ const AWAITING_STATUSES = new Set([
|
|
|
118
116
|
"awaiting_plan_decision",
|
|
119
117
|
]);
|
|
120
118
|
export function agentTurnResultFromPromptRecord(record, options = {}) {
|
|
119
|
+
const controlRef = options.controlRef
|
|
120
|
+
? AgentExactRunControlRefSchema.parse(options.controlRef)
|
|
121
|
+
: undefined;
|
|
122
|
+
if (controlRef !== undefined &&
|
|
123
|
+
options.sessionId !== undefined &&
|
|
124
|
+
controlRef.sessionId !== options.sessionId) {
|
|
125
|
+
throw new Error("Tangle prompt result sessionId conflicts with its control reference");
|
|
126
|
+
}
|
|
127
|
+
if (controlRef !== undefined &&
|
|
128
|
+
record.executionId !== controlRef.executionId) {
|
|
129
|
+
throw new Error("Tangle prompt result did not confirm its exact executionId");
|
|
130
|
+
}
|
|
121
131
|
const text = typeof record.response === "string"
|
|
122
132
|
? record.response
|
|
123
133
|
: typeof record.text === "string"
|
|
@@ -155,6 +165,13 @@ export function agentTurnResultFromPromptRecord(record, options = {}) {
|
|
|
155
165
|
// "the agent is asking you something" indistinguishable from "the turn
|
|
156
166
|
// failed", while the sandbox stayed alive waiting for an answer.
|
|
157
167
|
metadata: {
|
|
168
|
+
...(controlRef
|
|
169
|
+
? {
|
|
170
|
+
runId: controlRef.runId,
|
|
171
|
+
executionId: controlRef.executionId,
|
|
172
|
+
requestDigest: controlRef.requestDigest,
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
158
175
|
status: record.status,
|
|
159
176
|
awaitingInteraction: awaiting,
|
|
160
177
|
terminal: !awaiting,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { AgentRunControlRef } from "@tangle-network/agent-interface";
|
|
1
|
+
import type { AgentExactRunControlRef, AgentRunControlRef } from "@tangle-network/agent-interface";
|
|
2
2
|
import type { AgentSessionRef } from "@tangle-network/agent-interface/environment-provider";
|
|
3
|
-
export declare function sessionRefFromSandboxDispatch(dispatched: unknown, providerName: string, environmentId: string, expectedExecutionId: string | undefined, requestDigest?: `sha256:${string}` | undefined, expectedSessionId?: string | undefined): AgentSessionRef;
|
|
4
|
-
export declare function retainedSessionControlRef(sessionId: string, executionId: string, provider: string, environmentId: string, requestDigest?: `sha256:${string}
|
|
5
|
-
export declare function sessionPromptExecutionId(
|
|
3
|
+
export declare function sessionRefFromSandboxDispatch(dispatched: unknown, providerName: string, environmentId: string, expectedExecutionId: string | undefined, requestDigest?: `sha256:${string}` | undefined, expectedSessionId?: string | undefined, callerControlRef?: AgentExactRunControlRef | undefined): AgentSessionRef;
|
|
4
|
+
export declare function retainedSessionControlRef(sessionId: string, executionId: string, provider: string, environmentId: string, requestDigest?: `sha256:${string}`, runId?: string): AgentExactRunControlRef;
|
|
5
|
+
export declare function sessionPromptExecutionId(requestDigest: `sha256:${string}`): string;
|
|
6
|
+
export declare function sessionPromptSessionId(provider: string, environmentId: string, turnId: string | undefined): string | undefined;
|
|
6
7
|
export declare function sameRunControlRef(left: AgentRunControlRef, right: AgentRunControlRef): boolean;
|
|
7
|
-
export declare function resolveRetainedSessionControlRef(candidate: AgentRunControlRef | undefined, sessionId: string, provider: string, environmentId: string):
|
|
8
|
+
export declare function resolveRetainedSessionControlRef(candidate: AgentRunControlRef | undefined, sessionId: string, provider: string, environmentId: string): AgentExactRunControlRef | undefined;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createHash
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
2
|
import { AgentExactRunControlRefSchema, canonicalCandidateDigest, } from "@tangle-network/agent-interface";
|
|
3
3
|
import { nonEmptyString } from "./tangle-environment-values.js";
|
|
4
4
|
import { assertBoundedJson, boundedIdentifier, } from "./tangle-contract-safety.js";
|
|
5
|
-
export function sessionRefFromSandboxDispatch(dispatched, providerName, environmentId, expectedExecutionId, requestDigest = undefined, expectedSessionId = undefined) {
|
|
5
|
+
export function sessionRefFromSandboxDispatch(dispatched, providerName, environmentId, expectedExecutionId, requestDigest = undefined, expectedSessionId = undefined, callerControlRef = undefined) {
|
|
6
6
|
const record = dispatched && typeof dispatched === "object" && !Array.isArray(dispatched)
|
|
7
7
|
? dispatched
|
|
8
8
|
: undefined;
|
|
@@ -34,11 +34,26 @@ export function sessionRefFromSandboxDispatch(dispatched, providerName, environm
|
|
|
34
34
|
if (record.dispatched !== undefined && typeof record.dispatched !== "boolean") {
|
|
35
35
|
throw new Error("sandbox dispatch returned an invalid dispatched flag");
|
|
36
36
|
}
|
|
37
|
+
const accepted = record.runControlRef === undefined
|
|
38
|
+
? callerControlRef === undefined
|
|
39
|
+
? undefined
|
|
40
|
+
: AgentExactRunControlRefSchema.safeParse(callerControlRef)
|
|
41
|
+
: AgentExactRunControlRefSchema.safeParse(record.runControlRef);
|
|
42
|
+
if (accepted === undefined || !accepted.success) {
|
|
43
|
+
throw new Error("sandbox dispatch did not return the exact run control reference");
|
|
44
|
+
}
|
|
45
|
+
if (accepted.data.provider !== providerName ||
|
|
46
|
+
accepted.data.environmentId !== environmentId ||
|
|
47
|
+
accepted.data.sessionId !== id ||
|
|
48
|
+
accepted.data.executionId !== executionId ||
|
|
49
|
+
(requestDigest !== undefined && accepted.data.requestDigest !== requestDigest)) {
|
|
50
|
+
throw new Error("sandbox dispatch returned an exact control reference for another run");
|
|
51
|
+
}
|
|
52
|
+
const controlRef = Object.freeze(accepted.data);
|
|
37
53
|
return {
|
|
38
54
|
id,
|
|
39
55
|
provider: providerName,
|
|
40
|
-
controlRef
|
|
41
|
-
canonicalCandidateDigest({ provider: providerName, environmentId, id, executionId })),
|
|
56
|
+
controlRef,
|
|
42
57
|
metadata: {
|
|
43
58
|
...(record.status ? { status: record.status } : {}),
|
|
44
59
|
...(record.alreadyExisted !== undefined ? { alreadyExisted: record.alreadyExisted } : {}),
|
|
@@ -46,24 +61,27 @@ export function sessionRefFromSandboxDispatch(dispatched, providerName, environm
|
|
|
46
61
|
},
|
|
47
62
|
};
|
|
48
63
|
}
|
|
49
|
-
export function retainedSessionControlRef(sessionId, executionId, provider, environmentId, requestDigest) {
|
|
50
|
-
return AgentExactRunControlRefSchema.parse({
|
|
51
|
-
runId
|
|
64
|
+
export function retainedSessionControlRef(sessionId, executionId, provider, environmentId, requestDigest, runId = executionId) {
|
|
65
|
+
return Object.freeze(AgentExactRunControlRefSchema.parse({
|
|
66
|
+
runId,
|
|
52
67
|
provider,
|
|
53
68
|
environmentId,
|
|
54
69
|
sessionId,
|
|
55
70
|
executionId,
|
|
56
71
|
requestDigest: requestDigest ??
|
|
57
72
|
canonicalCandidateDigest({ provider, environmentId, sessionId, executionId }),
|
|
58
|
-
});
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
export function sessionPromptExecutionId(requestDigest) {
|
|
76
|
+
return `session-turn-${requestDigest.slice("sha256:".length)}`;
|
|
59
77
|
}
|
|
60
|
-
export function
|
|
78
|
+
export function sessionPromptSessionId(provider, environmentId, turnId) {
|
|
61
79
|
if (turnId === undefined)
|
|
62
|
-
return
|
|
80
|
+
return undefined;
|
|
63
81
|
const digest = createHash("sha256")
|
|
64
|
-
.update(`${provider}\0${environmentId}\0${
|
|
82
|
+
.update(`${provider}\0${environmentId}\0${turnId}`)
|
|
65
83
|
.digest("hex");
|
|
66
|
-
return `session
|
|
84
|
+
return `session-${digest}`;
|
|
67
85
|
}
|
|
68
86
|
export function sameRunControlRef(left, right) {
|
|
69
87
|
return (left.runId === right.runId &&
|
|
@@ -82,8 +100,5 @@ export function resolveRetainedSessionControlRef(candidate, sessionId, provider,
|
|
|
82
100
|
controlRef.sessionId !== sessionId) {
|
|
83
101
|
throw new Error("Tangle control reference does not match this session");
|
|
84
102
|
}
|
|
85
|
-
|
|
86
|
-
throw new Error("Tangle session control reference requires runId to equal executionId");
|
|
87
|
-
}
|
|
88
|
-
return controlRef;
|
|
103
|
+
return retainedSessionControlRef(sessionId, controlRef.executionId, provider, environmentId, controlRef.requestDigest, controlRef.runId);
|
|
89
104
|
}
|
package/dist/tangle-types.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { BackendType, CreateSandboxOptions, ExecResult as SandboxExecResult, PromptOptions, PromptResult, SandboxEvent } from "@tangle-network/sandbox";
|
|
2
|
-
import type { InputPart } from "@tangle-network/agent-interface";
|
|
2
|
+
import type { AgentExactRunControlRef, AgentRunCancellationAcknowledgement, AgentRunCancellationRequest, InputPart } from "@tangle-network/agent-interface";
|
|
3
3
|
import type { AgentEnvironmentCapabilities, AgentEnvironmentProvider, CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
|
|
4
|
+
export type TanglePromptOptions = PromptOptions & {
|
|
5
|
+
runControlRef?: AgentExactRunControlRef;
|
|
6
|
+
};
|
|
4
7
|
export interface TangleExactProcessOptions {
|
|
5
8
|
teamId?: string;
|
|
6
9
|
}
|
|
@@ -53,9 +56,9 @@ export interface SandboxInstanceLike {
|
|
|
53
56
|
name?: string;
|
|
54
57
|
status?: unknown;
|
|
55
58
|
metadata?: Record<string, unknown>;
|
|
56
|
-
streamPrompt(message: string | InputPart[], options?:
|
|
57
|
-
prompt?(message: string | InputPart[], options?:
|
|
58
|
-
dispatchPrompt?(message: string | InputPart[], options?:
|
|
59
|
+
streamPrompt(message: string | InputPart[], options?: TanglePromptOptions): AsyncIterable<SandboxEvent>;
|
|
60
|
+
prompt?(message: string | InputPart[], options?: TanglePromptOptions): Promise<PromptResult>;
|
|
61
|
+
dispatchPrompt?(message: string | InputPart[], options?: TanglePromptOptions): Promise<unknown>;
|
|
59
62
|
session?(id: string, options?: {
|
|
60
63
|
signal?: AbortSignal;
|
|
61
64
|
}): SandboxSessionLike;
|
|
@@ -122,13 +125,16 @@ export interface SandboxSessionLike {
|
|
|
122
125
|
executionId?: string;
|
|
123
126
|
signal?: AbortSignal;
|
|
124
127
|
}): Promise<PromptResult>;
|
|
125
|
-
prompt(message: string | InputPart[], options?:
|
|
128
|
+
prompt(message: string | InputPart[], options?: TanglePromptOptions): Promise<PromptResult>;
|
|
126
129
|
interrupt(options?: {
|
|
127
130
|
executionId?: string;
|
|
128
131
|
signal?: AbortSignal;
|
|
129
132
|
}): Promise<{
|
|
130
133
|
cancelled: boolean;
|
|
131
134
|
}>;
|
|
135
|
+
cancelRun?(request: AgentRunCancellationRequest, options?: {
|
|
136
|
+
signal?: AbortSignal;
|
|
137
|
+
}): Promise<AgentRunCancellationAcknowledgement>;
|
|
132
138
|
}
|
|
133
139
|
export interface TangleProviderOptions {
|
|
134
140
|
client: SandboxClientLike;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-provider-tangle",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "AgentEnvironmentProvider adapter for Tangle sandboxes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@tangle-network/
|
|
81
|
+
"@tangle-network/agent-runtime": "0.132.6",
|
|
82
|
+
"@tangle-network/sandbox": "0.19.4",
|
|
82
83
|
"@types/node": "25.6.0",
|
|
83
84
|
"typescript": "^6.0.3",
|
|
84
85
|
"vitest": "^4.1.5",
|