@tangle-network/agent-provider-tangle 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/dist/tangle-capabilities.d.ts +32 -14
- package/dist/tangle-capabilities.js +130 -61
- package/dist/tangle-environment-control.d.ts +4 -1
- package/dist/tangle-environment-control.js +11 -10
- package/dist/tangle-environment-dispatch.js +49 -19
- package/dist/tangle-environment-session.d.ts +12 -3
- package/dist/tangle-environment-session.js +213 -60
- package/dist/tangle-environment-validation.d.ts +1 -8
- package/dist/tangle-environment-validation.js +0 -25
- package/dist/tangle-environment-values.d.ts +11 -1
- package/dist/tangle-environment-values.js +48 -13
- package/dist/tangle-environment.js +30 -57
- package/dist/tangle-events.d.ts +8 -0
- package/dist/tangle-events.js +164 -4
- package/dist/tangle-prompt.d.ts +2 -1
- package/dist/tangle-prompt.js +20 -3
- package/dist/tangle-provider.js +15 -8
- package/dist/tangle-session-control.d.ts +6 -5
- package/dist/tangle-session-control.js +31 -16
- package/dist/tangle-types.d.ts +15 -7
- package/package.json +5 -3
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,6 +1,6 @@
|
|
|
1
1
|
import type { PromptOptions, 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
4
|
export declare function promptFromTurnInput(input: AgentTurnInput): string | InputPart[];
|
|
5
5
|
export declare function executionIdFromTurnInput(input: AgentTurnInput): string | undefined;
|
|
6
6
|
export declare function promptOptionsFromTurnInput(input: AgentTurnInput, target: {
|
|
@@ -20,5 +20,6 @@ export declare function agentTurnResultFromPromptRecord(record: ValidatedSandbox
|
|
|
20
20
|
contextTransferRequested?: boolean;
|
|
21
21
|
contextTransferRequest?: import("@tangle-network/agent-interface").ContextTransferRequest;
|
|
22
22
|
sessionId?: string;
|
|
23
|
+
controlRef?: AgentExactRunControlRef;
|
|
23
24
|
}): AgentTurnResult;
|
|
24
25
|
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,
|
package/dist/tangle-provider.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createTangleProvider(options) {
|
|
|
15
15
|
providerName,
|
|
16
16
|
})
|
|
17
17
|
: undefined;
|
|
18
|
-
const
|
|
18
|
+
const resolveDeclaredCapabilities = async () => {
|
|
19
19
|
const configured = options.capabilities
|
|
20
20
|
? typeof options.capabilities === "function"
|
|
21
21
|
? await options.capabilities()
|
|
@@ -24,14 +24,17 @@ export function createTangleProvider(options) {
|
|
|
24
24
|
if (!exactProcess && configured.exactProcess) {
|
|
25
25
|
throw new Error("Tangle capabilities cannot advertise exactProcess without exactProcess configuration");
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
return exactProcess
|
|
28
28
|
? {
|
|
29
29
|
...configured,
|
|
30
30
|
exactProcess: { egress: ["blocked", "strict"] },
|
|
31
31
|
}
|
|
32
32
|
: configured;
|
|
33
|
-
return AgentEnvironmentCapabilitiesSchema.parse(capabilitiesForClient(withExactProcess, options.client));
|
|
34
33
|
};
|
|
34
|
+
// Provider-boundary document: client-stage facts only. It also validates
|
|
35
|
+
// the configured document, so create() and get() call it before any effect.
|
|
36
|
+
const narrowedProviderCapabilities = (declared) => AgentEnvironmentCapabilitiesSchema.parse(capabilitiesForClient(declared, options.client));
|
|
37
|
+
const resolveCapabilities = async () => narrowedProviderCapabilities(await resolveDeclaredCapabilities());
|
|
35
38
|
return {
|
|
36
39
|
name: providerName,
|
|
37
40
|
...(exactProcess ? { exactProcess } : {}),
|
|
@@ -44,7 +47,11 @@ export function createTangleProvider(options) {
|
|
|
44
47
|
if (input.providerOptions && Object.keys(input.providerOptions).length > 0) {
|
|
45
48
|
throw new Error("Tangle create providerOptions are not supported");
|
|
46
49
|
}
|
|
47
|
-
|
|
50
|
+
// The sandbox stage narrows from the declared document, not the
|
|
51
|
+
// provider-boundary one: the client stage cannot observe box-scoped
|
|
52
|
+
// facts, so measured instance facts must decide them per sandbox.
|
|
53
|
+
const declaredCapabilities = await resolveDeclaredCapabilities();
|
|
54
|
+
narrowedProviderCapabilities(declaredCapabilities);
|
|
48
55
|
const createOptions = options.mapCreateInput?.(input) ??
|
|
49
56
|
sandboxOptionsFromCreateInput(input, options.defaultBackend ?? "opencode");
|
|
50
57
|
assertMappedCreateOptions(createOptions);
|
|
@@ -76,7 +83,7 @@ export function createTangleProvider(options) {
|
|
|
76
83
|
}
|
|
77
84
|
try {
|
|
78
85
|
input.signal?.throwIfAborted();
|
|
79
|
-
const environment = sandboxInstanceAsEnvironment(box, providerName, options.client,
|
|
86
|
+
const environment = sandboxInstanceAsEnvironment(box, providerName, options.client, declaredCapabilities);
|
|
80
87
|
input.signal?.throwIfAborted();
|
|
81
88
|
return environment;
|
|
82
89
|
}
|
|
@@ -101,14 +108,14 @@ export function createTangleProvider(options) {
|
|
|
101
108
|
async get(id, operation) {
|
|
102
109
|
assertProviderOperationOptions(operation, "Tangle get");
|
|
103
110
|
boundedIdentifier(id, "Tangle environment id");
|
|
111
|
+
const declaredCapabilities = await resolveDeclaredCapabilities();
|
|
112
|
+
narrowedProviderCapabilities(declaredCapabilities);
|
|
104
113
|
operation?.signal?.throwIfAborted();
|
|
105
114
|
const box = await awaitWithSignal(options.client.get?.(id, operation), operation?.signal);
|
|
106
115
|
operation?.signal?.throwIfAborted();
|
|
107
116
|
if (!box || boundedIdentifier(box.id, "Tangle environment id") !== id)
|
|
108
117
|
return null;
|
|
109
|
-
return box
|
|
110
|
-
? sandboxInstanceAsEnvironment(box, providerName, options.client, await resolveCapabilities())
|
|
111
|
-
: null;
|
|
118
|
+
return sandboxInstanceAsEnvironment(box, providerName, options.client, declaredCapabilities);
|
|
112
119
|
},
|
|
113
120
|
}
|
|
114
121
|
: {}),
|
|
@@ -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,5 +1,5 @@
|
|
|
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 { AgentRunCancellationAcknowledgement, AgentRunCancellationRequest, InputPart } from "@tangle-network/agent-interface";
|
|
3
3
|
import type { AgentEnvironmentCapabilities, AgentEnvironmentProvider, CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
|
|
4
4
|
export interface TangleExactProcessOptions {
|
|
5
5
|
teamId?: string;
|
|
@@ -9,6 +9,17 @@ export interface SandboxClientLike {
|
|
|
9
9
|
signal?: AbortSignal;
|
|
10
10
|
timeoutMs?: number;
|
|
11
11
|
}): Promise<SandboxInstanceLike>;
|
|
12
|
+
/**
|
|
13
|
+
* SDK HttpClient transport, present on `Sandbox` and `TangleSandboxClient`.
|
|
14
|
+
* Retained control requires it: the provider mints a lazy probe instance
|
|
15
|
+
* over this surface to read the linked SDK's method surface before any
|
|
16
|
+
* sandbox exists. An object-spread wrapper (`{ ...client }`) drops class
|
|
17
|
+
* prototype methods including this one, so such a wrapper never claims
|
|
18
|
+
* retained control; pass the SDK client itself or delegate its methods.
|
|
19
|
+
*/
|
|
20
|
+
fetch?(path: string, options?: RequestInit, fetchOptions?: {
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}): Promise<Response>;
|
|
12
23
|
get?(id: string, requestOptions?: {
|
|
13
24
|
signal?: AbortSignal;
|
|
14
25
|
}): Promise<SandboxInstanceLike | null>;
|
|
@@ -95,12 +106,6 @@ export interface SandboxInstanceLike {
|
|
|
95
106
|
}): Promise<unknown>;
|
|
96
107
|
};
|
|
97
108
|
process?: SandboxProcessManagerLike;
|
|
98
|
-
checkpoint?(options?: {
|
|
99
|
-
signal?: AbortSignal;
|
|
100
|
-
} & Record<string, unknown>): Promise<unknown>;
|
|
101
|
-
fork?(checkpointId: string, options?: {
|
|
102
|
-
signal?: AbortSignal;
|
|
103
|
-
} & Record<string, unknown>): Promise<SandboxInstanceLike>;
|
|
104
109
|
refresh?(options?: {
|
|
105
110
|
signal?: AbortSignal;
|
|
106
111
|
}): Promise<void>;
|
|
@@ -129,6 +134,9 @@ export interface SandboxSessionLike {
|
|
|
129
134
|
}): Promise<{
|
|
130
135
|
cancelled: boolean;
|
|
131
136
|
}>;
|
|
137
|
+
cancelRun?(request: AgentRunCancellationRequest, options?: {
|
|
138
|
+
signal?: AbortSignal;
|
|
139
|
+
}): Promise<AgentRunCancellationAcknowledgement>;
|
|
132
140
|
}
|
|
133
141
|
export interface TangleProviderOptions {
|
|
134
142
|
client: SandboxClientLike;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-provider-tangle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "AgentEnvironmentProvider adapter for Tangle sandboxes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@tangle-network/agent-interface": "0.47.0"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@tangle-network/sandbox": ">=0.
|
|
73
|
+
"@tangle-network/sandbox": ">=0.19.6 <1.0.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependenciesMeta": {
|
|
76
76
|
"@tangle-network/sandbox": {
|
|
@@ -78,7 +78,9 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@tangle-network/
|
|
81
|
+
"@tangle-network/agent-eval": "0.145.3",
|
|
82
|
+
"@tangle-network/agent-runtime": "0.132.13",
|
|
83
|
+
"@tangle-network/sandbox": "0.21.1",
|
|
82
84
|
"@types/node": "25.6.0",
|
|
83
85
|
"typescript": "^6.0.3",
|
|
84
86
|
"vitest": "^4.1.5",
|