@tangle-network/agent-provider-cli-bridge 0.10.0 → 0.11.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 +3 -1
- package/dist/capability-discovery.js +13 -0
- package/dist/context-transfer.d.ts +8 -0
- package/dist/context-transfer.js +117 -0
- package/dist/environment-identity.d.ts +2 -0
- package/dist/environment-identity.js +6 -0
- package/dist/index.js +34 -9
- package/dist/retained-execution.js +19 -0
- package/dist/retained-run-state.d.ts +2 -0
- package/dist/retained-run-state.js +11 -3
- package/dist/wire.js +6 -2
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -102,7 +102,9 @@ Pi retained sessions also expose typed permission interactions and native contin
|
|
|
102
102
|
The provider stores the selected harness, exact model route, and canonical create digest inside its opaque environment identifier.
|
|
103
103
|
This identifier lets `provider.get()` reconstruct profile-selected routes after process death without a cache or repeated configuration.
|
|
104
104
|
The create digest prevents an altered profile or workspace from reusing the previous retained identity.
|
|
105
|
-
|
|
105
|
+
An accepted portable-context destination can supply its exact environment identifier through `requestedId`.
|
|
106
|
+
The provider recovers that caller-owned route from cli-bridge's durable transfer receipt.
|
|
107
|
+
An unknown caller-owned identifier returns `null`.
|
|
106
108
|
The provider queries `/v1/capabilities` for each retained route restored through `provider.get()`.
|
|
107
109
|
`provider.capabilities()` queries the configured default route before the Runtime selects retained execution.
|
|
108
110
|
The provider shares concurrent discovery requests and refreshes every later query.
|
|
@@ -72,6 +72,18 @@ export function narrowCliBridgeCapabilities(adapter, reported, selectedBackend,
|
|
|
72
72
|
: {}),
|
|
73
73
|
}
|
|
74
74
|
: undefined;
|
|
75
|
+
const contextTransfer = adapter.contextTransfer?.freshSession === true &&
|
|
76
|
+
adapter.contextTransfer.requestIdempotency === true &&
|
|
77
|
+
adapter.contextTransfer.lookup === true &&
|
|
78
|
+
reported.contextTransfer?.freshSession === true &&
|
|
79
|
+
reported.contextTransfer.requestIdempotency === true &&
|
|
80
|
+
reported.contextTransfer.lookup === true
|
|
81
|
+
? {
|
|
82
|
+
freshSession: true,
|
|
83
|
+
requestIdempotency: true,
|
|
84
|
+
lookup: true,
|
|
85
|
+
}
|
|
86
|
+
: undefined;
|
|
75
87
|
const sessions = {
|
|
76
88
|
continue: adapter.sessions.continue && reported.sessions.continue,
|
|
77
89
|
// The Bridge can list and inspect sessions, but this adapter exposes neither method.
|
|
@@ -125,6 +137,7 @@ export function narrowCliBridgeCapabilities(adapter, reported, selectedBackend,
|
|
|
125
137
|
sessions,
|
|
126
138
|
...(retainedControl === undefined ? {} : { retainedControl }),
|
|
127
139
|
...(nativeContinuation === undefined ? {} : { nativeContinuation }),
|
|
140
|
+
...(contextTransfer === undefined ? {} : { contextTransfer }),
|
|
128
141
|
...(interactions === undefined ? {} : { interactions }),
|
|
129
142
|
// Remote workspace support cannot create absent adapter methods.
|
|
130
143
|
workspace: {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type AgentContextTransferProvider, type ContextTransferRequest, type ContextTransferResult, type ContextTransferReceipt } from "@tangle-network/agent-interface";
|
|
2
|
+
import type { CliBridgeProviderOptions } from "./provider-options.js";
|
|
3
|
+
/** Bind the shared portable-context contract to CLI Bridge's durable endpoint. */
|
|
4
|
+
export declare function createCliBridgeContextTransfer(options: CliBridgeProviderOptions): AgentContextTransferProvider;
|
|
5
|
+
export declare function transferCliBridgeContext(options: CliBridgeProviderOptions, request: ContextTransferRequest, signal?: AbortSignal): Promise<ContextTransferResult>;
|
|
6
|
+
export declare function lookupCliBridgeContext(options: CliBridgeProviderOptions, request: ContextTransferRequest, signal?: AbortSignal): Promise<ContextTransferResult | undefined>;
|
|
7
|
+
/** Recover the accepted destination route for one caller-owned environment id. */
|
|
8
|
+
export declare function lookupCliBridgeContextByEnvironment(options: CliBridgeProviderOptions, environmentId: string, signal?: AbortSignal): Promise<ContextTransferReceipt | undefined>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ContextTransferRequestSchema, ContextTransferResultSchema, contextTransferResultMatchesRequest, } from "@tangle-network/agent-interface";
|
|
2
|
+
import { createCliBridgeTransport, MAX_CLI_BRIDGE_CONTROL_RESPONSE_BYTES, readBoundedCliBridgeResponse, requestHeaders, trimSlash, } from "./transport.js";
|
|
3
|
+
/** Bind the shared portable-context contract to CLI Bridge's durable endpoint. */
|
|
4
|
+
export function createCliBridgeContextTransfer(options) {
|
|
5
|
+
return {
|
|
6
|
+
transfer: (request, transferOptions) => transferCliBridgeContext(options, request, transferOptions?.signal),
|
|
7
|
+
lookup: (request, lookupOptions) => lookupCliBridgeContext(options, request, lookupOptions?.signal),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export async function transferCliBridgeContext(options, request, signal) {
|
|
11
|
+
const exact = ContextTransferRequestSchema.parse(request);
|
|
12
|
+
const transport = createCliBridgeTransport(options);
|
|
13
|
+
try {
|
|
14
|
+
const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/context-transfers`, {
|
|
15
|
+
method: "POST",
|
|
16
|
+
headers: requestHeaders(options),
|
|
17
|
+
body: JSON.stringify(exact),
|
|
18
|
+
...(signal === undefined ? {} : { signal }),
|
|
19
|
+
});
|
|
20
|
+
const body = await readBoundedCliBridgeResponse(response, MAX_CLI_BRIDGE_CONTROL_RESPONSE_BYTES, signal);
|
|
21
|
+
return parseContextTransferResponse(exact, response.status, body);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
signal?.throwIfAborted();
|
|
25
|
+
return transportFailure(exact, error);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
await transport.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function lookupCliBridgeContext(options, request, signal) {
|
|
32
|
+
const exact = ContextTransferRequestSchema.parse(request);
|
|
33
|
+
const transport = createCliBridgeTransport(options);
|
|
34
|
+
try {
|
|
35
|
+
const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/context-transfers/${encodeURIComponent(exact.operationId)}?request_digest=${encodeURIComponent(exact.requestDigest)}`, {
|
|
36
|
+
method: "GET",
|
|
37
|
+
headers: requestHeaders(options),
|
|
38
|
+
...(signal === undefined ? {} : { signal }),
|
|
39
|
+
});
|
|
40
|
+
const body = await readBoundedCliBridgeResponse(response, MAX_CLI_BRIDGE_CONTROL_RESPONSE_BYTES, signal);
|
|
41
|
+
if (response.status === 404)
|
|
42
|
+
return undefined;
|
|
43
|
+
return parseContextTransferResponse(exact, response.status, body);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
signal?.throwIfAborted();
|
|
47
|
+
return transportFailure(exact, error);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await transport.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Recover the accepted destination route for one caller-owned environment id. */
|
|
54
|
+
export async function lookupCliBridgeContextByEnvironment(options, environmentId, signal) {
|
|
55
|
+
const transport = createCliBridgeTransport(options);
|
|
56
|
+
try {
|
|
57
|
+
const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/context-transfer-environments/${encodeURIComponent(environmentId)}`, {
|
|
58
|
+
method: "GET",
|
|
59
|
+
headers: requestHeaders(options),
|
|
60
|
+
...(signal === undefined ? {} : { signal }),
|
|
61
|
+
});
|
|
62
|
+
const body = await readBoundedCliBridgeResponse(response, MAX_CLI_BRIDGE_CONTROL_RESPONSE_BYTES, signal);
|
|
63
|
+
if (response.status === 404)
|
|
64
|
+
return undefined;
|
|
65
|
+
const parsed = parseContextTransferResult(body, response.status);
|
|
66
|
+
if ((parsed.status !== "accepted" && parsed.status !== "replayed") ||
|
|
67
|
+
parsed.environmentId !== environmentId) {
|
|
68
|
+
throw new Error("CLI Bridge returned a context transfer for another environment");
|
|
69
|
+
}
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await transport.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function parseContextTransferResponse(request, status, body) {
|
|
77
|
+
let value;
|
|
78
|
+
try {
|
|
79
|
+
value = JSON.parse(body);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return transportFailure(request, new Error(`CLI Bridge context transfer returned HTTP ${status} with invalid JSON`, {
|
|
83
|
+
cause: error,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
const parsed = ContextTransferResultSchema.safeParse(value);
|
|
87
|
+
if (!parsed.success || !contextTransferResultMatchesRequest(request, parsed.data)) {
|
|
88
|
+
return transportFailure(request, new Error(`CLI Bridge context transfer returned HTTP ${status} with an invalid result`));
|
|
89
|
+
}
|
|
90
|
+
return parsed.data;
|
|
91
|
+
}
|
|
92
|
+
function parseContextTransferResult(body, status) {
|
|
93
|
+
let value;
|
|
94
|
+
try {
|
|
95
|
+
value = JSON.parse(body);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
throw new Error(`CLI Bridge context transfer returned HTTP ${status} with invalid JSON`, {
|
|
99
|
+
cause: error,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const parsed = ContextTransferResultSchema.safeParse(value);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
throw new Error(`CLI Bridge context transfer returned HTTP ${status} with an invalid result`);
|
|
105
|
+
}
|
|
106
|
+
return parsed.data;
|
|
107
|
+
}
|
|
108
|
+
function transportFailure(request, error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return ContextTransferResultSchema.parse({
|
|
111
|
+
status: "transport_failure",
|
|
112
|
+
operationId: request.operationId,
|
|
113
|
+
requestDigest: request.requestDigest,
|
|
114
|
+
message: message.slice(0, 4_096) || "CLI Bridge context transfer failed",
|
|
115
|
+
retryable: true,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -11,3 +11,5 @@ export interface CliBridgeEnvironmentRoute {
|
|
|
11
11
|
export declare function cliBridgeEnvironmentId(route: Omit<CliBridgeEnvironmentRoute, "createDigest">, createDigest: Sha256Digest, idempotencyKey?: string): string;
|
|
12
12
|
/** Recover the exact route from a provider-owned retained identifier. */
|
|
13
13
|
export declare function cliBridgeEnvironmentRoute(id: string): CliBridgeEnvironmentRoute;
|
|
14
|
+
/** Parse a provider-owned route, or identify a caller-owned destination id. */
|
|
15
|
+
export declare function optionalCliBridgeEnvironmentRoute(id: string): CliBridgeEnvironmentRoute | undefined;
|
|
@@ -60,6 +60,12 @@ export function cliBridgeEnvironmentRoute(id) {
|
|
|
60
60
|
createDigest,
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
/** Parse a provider-owned route, or identify a caller-owned destination id. */
|
|
64
|
+
export function optionalCliBridgeEnvironmentRoute(id) {
|
|
65
|
+
if (!id.startsWith(CLI_BRIDGE_ENVIRONMENT_ID_PREFIX))
|
|
66
|
+
return undefined;
|
|
67
|
+
return cliBridgeEnvironmentRoute(id);
|
|
68
|
+
}
|
|
63
69
|
function optionalRouteValue(value) {
|
|
64
70
|
return value === null || routeValue(value);
|
|
65
71
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,9 +4,10 @@ import { createCliBridgeEnvironment, } from "./retained-environment.js";
|
|
|
4
4
|
import { lookupExactCliBridgeRun, } from "./retained-control.js";
|
|
5
5
|
import { cachedCliBridgeCapabilityDiscovery, narrowCliBridgeCapabilities, } from "./capability-discovery.js";
|
|
6
6
|
import { supportsCliBridgeNativeInteractions, } from "./retained-native.js";
|
|
7
|
+
import { createCliBridgeContextTransfer, lookupCliBridgeContextByEnvironment, } from "./context-transfer.js";
|
|
7
8
|
import { assertCliBridgeProviderOptions, } from "./provider-options.js";
|
|
8
9
|
import { narrowedCliBridgeObservation } from "./observation.js";
|
|
9
|
-
import { cliBridgeEnvironmentId,
|
|
10
|
+
import { cliBridgeEnvironmentId, optionalCliBridgeEnvironmentRoute, } from "./environment-identity.js";
|
|
10
11
|
import { resolveBridgeModel } from "./wire.js";
|
|
11
12
|
export { safeEndpointFromBaseUrl } from "./observation.js";
|
|
12
13
|
export function createCliBridgeProvider(options) {
|
|
@@ -68,7 +69,8 @@ export function createCliBridgeProvider(options) {
|
|
|
68
69
|
const model = hasEnvironmentModel
|
|
69
70
|
? resolveBridgeModel(options, environmentInput, {}, profile)
|
|
70
71
|
: undefined;
|
|
71
|
-
const environmentId = cliBridgeEnvironmentId({ backend: selectedBackend, model }, agentEnvironmentCreateInputDigest(environmentInput), input.idempotencyKey);
|
|
72
|
+
const environmentId = input.requestedId ?? cliBridgeEnvironmentId({ backend: selectedBackend, model }, agentEnvironmentCreateInputDigest(environmentInput), input.idempotencyKey);
|
|
73
|
+
assertRequestedEnvironmentId(environmentId);
|
|
72
74
|
return createCliBridgeEnvironment({
|
|
73
75
|
options,
|
|
74
76
|
providerName: name,
|
|
@@ -85,6 +87,7 @@ export function createCliBridgeProvider(options) {
|
|
|
85
87
|
};
|
|
86
88
|
return {
|
|
87
89
|
name,
|
|
90
|
+
contextTransfer: createCliBridgeContextTransfer(options),
|
|
88
91
|
lookupRun: (input) => lookupExactCliBridgeRun(options, name, input),
|
|
89
92
|
capabilities: () => {
|
|
90
93
|
if (options.defaultModel !== undefined) {
|
|
@@ -95,31 +98,41 @@ export function createCliBridgeProvider(options) {
|
|
|
95
98
|
create(input) {
|
|
96
99
|
return createAgentEnvironmentWithIdempotency(createRecords, input, () => createEnvironment(input));
|
|
97
100
|
},
|
|
98
|
-
async get(id) {
|
|
101
|
+
async get(id, getOptions) {
|
|
99
102
|
if (id.length === 0 || id.trim() !== id) {
|
|
100
103
|
throw new Error("cli-bridge environment id must be non-empty and have no outer whitespace");
|
|
101
104
|
}
|
|
102
|
-
const
|
|
105
|
+
const ownedRoute = optionalCliBridgeEnvironmentRoute(id);
|
|
106
|
+
const transfer = ownedRoute === undefined
|
|
107
|
+
? await lookupCliBridgeContextByEnvironment(options, id, getOptions?.signal)
|
|
108
|
+
: undefined;
|
|
109
|
+
const backend = ownedRoute?.backend ?? transfer?.destination.runner;
|
|
110
|
+
const destinationModel = transfer?.destination.model;
|
|
111
|
+
const model = ownedRoute?.model ?? (destinationModel === undefined || destinationModel === backend || destinationModel.startsWith(`${backend}/`)
|
|
112
|
+
? destinationModel ?? backend
|
|
113
|
+
: `${backend}/${destinationModel}`);
|
|
114
|
+
if (ownedRoute === undefined && transfer === undefined)
|
|
115
|
+
return null;
|
|
103
116
|
return createCliBridgeEnvironment({
|
|
104
117
|
options,
|
|
105
118
|
providerName: name,
|
|
106
119
|
environmentInput: {
|
|
107
120
|
profile: { name: "reconnected" },
|
|
108
|
-
...(
|
|
121
|
+
...(backend === undefined ? {} : { backend }),
|
|
109
122
|
idempotencyKey: id,
|
|
110
123
|
},
|
|
111
124
|
environmentId: id,
|
|
112
125
|
allowDispatch: false,
|
|
113
126
|
cancelRunsOnDestroy: false,
|
|
114
|
-
capabilities: await resolveCapabilities(
|
|
115
|
-
selectedBackend:
|
|
116
|
-
selectedModel:
|
|
127
|
+
capabilities: await resolveCapabilities(backend, model, true, true),
|
|
128
|
+
selectedBackend: backend,
|
|
129
|
+
selectedModel: model,
|
|
117
130
|
});
|
|
118
131
|
},
|
|
119
132
|
};
|
|
120
133
|
}
|
|
121
134
|
function withoutRemoteRetainedCapabilities(capabilities) {
|
|
122
|
-
const { retainedControl: _retainedControl, nativeContinuation: _nativeContinuation, interactions: _interactions, ...local } = capabilities;
|
|
135
|
+
const { retainedControl: _retainedControl, nativeContinuation: _nativeContinuation, contextTransfer: _contextTransfer, interactions: _interactions, ...local } = capabilities;
|
|
123
136
|
return local;
|
|
124
137
|
}
|
|
125
138
|
function selectedBackendFromInput(input, configuredBackend) {
|
|
@@ -135,6 +148,13 @@ function selectedBackendFromRoute(route) {
|
|
|
135
148
|
const parsed = harnessTypeSchema.safeParse(candidate);
|
|
136
149
|
return parsed.success ? parsed.data : undefined;
|
|
137
150
|
}
|
|
151
|
+
function assertRequestedEnvironmentId(value) {
|
|
152
|
+
if (value.length === 0 ||
|
|
153
|
+
value.length > 256 ||
|
|
154
|
+
!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(value)) {
|
|
155
|
+
throw new Error("cli-bridge requested environment id must be 1-256 URL-safe characters");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
138
158
|
/**
|
|
139
159
|
* Describe the bridge backend, not this adapter.
|
|
140
160
|
*
|
|
@@ -171,6 +191,11 @@ export function defaultCliBridgeCapabilities(harness) {
|
|
|
171
191
|
eventIdentity: true,
|
|
172
192
|
cancellationIdempotency: true,
|
|
173
193
|
},
|
|
194
|
+
contextTransfer: {
|
|
195
|
+
freshSession: true,
|
|
196
|
+
requestIdempotency: true,
|
|
197
|
+
lookup: true,
|
|
198
|
+
},
|
|
174
199
|
...(harness === "pi"
|
|
175
200
|
? {
|
|
176
201
|
nativeContinuation: {
|
|
@@ -4,6 +4,7 @@ import { CliBridgeRequestRejectedError, streamCliBridgeTurn, } from "./retained-
|
|
|
4
4
|
import { beginCliBridgeNativeTurn, } from "./retained-native.js";
|
|
5
5
|
import { MAX_CLI_BRIDGE_CONTROL_RESPONSE_BYTES, readBoundedCliBridgeResponse, requestHeaders, trimSlash, } from "./transport.js";
|
|
6
6
|
import { modelRequestCount } from "./wire.js";
|
|
7
|
+
import { lookupCliBridgeContext } from "./context-transfer.js";
|
|
7
8
|
export async function* streamTrackedCliBridgeTurn(options, environmentInput, providerName, prepared, transport, runs, sessions, readers, nativeSessions, useNativeInteractions) {
|
|
8
9
|
const originalTurn = prepared.turn;
|
|
9
10
|
if (originalTurn.detach) {
|
|
@@ -48,6 +49,7 @@ export async function* streamTrackedCliBridgeTurn(options, environmentInput, pro
|
|
|
48
49
|
yield event;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
await confirmCliBridgeContextTransfer(options, originalTurn, run, signal);
|
|
51
53
|
drained = true;
|
|
52
54
|
if (runs.get(run.id) === run)
|
|
53
55
|
runs.delete(run.id);
|
|
@@ -142,6 +144,7 @@ export async function dispatchCliBridgeTurn(options, environmentInput, prepared,
|
|
|
142
144
|
}
|
|
143
145
|
const controlRef = exactControlRefForRun(run, providerName);
|
|
144
146
|
run.controlRef = controlRef;
|
|
147
|
+
await confirmCliBridgeContextTransfer(options, prepared.turn, run, signal);
|
|
145
148
|
if (responseBody) {
|
|
146
149
|
await detachCliBridgeReader(responseBody);
|
|
147
150
|
responseBody = undefined;
|
|
@@ -150,6 +153,9 @@ export async function dispatchCliBridgeTurn(options, environmentInput, prepared,
|
|
|
150
153
|
id: run.sessionId,
|
|
151
154
|
provider: providerName,
|
|
152
155
|
controlRef,
|
|
156
|
+
...(run.contextTransferReceipt === undefined
|
|
157
|
+
? {}
|
|
158
|
+
: { contextTransferReceipt: run.contextTransferReceipt }),
|
|
153
159
|
metadata: {
|
|
154
160
|
runId: run.id,
|
|
155
161
|
requestDigest: run.requestDigest,
|
|
@@ -188,6 +194,16 @@ export async function dispatchCliBridgeTurn(options, environmentInput, prepared,
|
|
|
188
194
|
throw failure;
|
|
189
195
|
}
|
|
190
196
|
}
|
|
197
|
+
async function confirmCliBridgeContextTransfer(options, turn, run, signal) {
|
|
198
|
+
if (turn.contextTransfer === undefined)
|
|
199
|
+
return;
|
|
200
|
+
const transfer = await lookupCliBridgeContext(options, turn.contextTransfer, signal);
|
|
201
|
+
if (transfer === undefined ||
|
|
202
|
+
(transfer.status !== "accepted" && transfer.status !== "replayed")) {
|
|
203
|
+
throw new Error("cli-bridge did not confirm admitted portable context");
|
|
204
|
+
}
|
|
205
|
+
run.contextTransferReceipt = transfer;
|
|
206
|
+
}
|
|
191
207
|
export async function* streamCliBridgeSessionEvents(options, environmentInput, providerName, run, transport, runs, readers, eventOptions) {
|
|
192
208
|
if (eventOptions?.executionId !== undefined &&
|
|
193
209
|
eventOptions.executionId !== run.executionId) {
|
|
@@ -302,6 +318,9 @@ export async function collectCliBridgeTurnResult(source, run, options, transport
|
|
|
302
318
|
}
|
|
303
319
|
: {}),
|
|
304
320
|
...(run.sessionId ? { sessionId: run.sessionId } : {}),
|
|
321
|
+
...(run.contextTransferReceipt === undefined
|
|
322
|
+
? {}
|
|
323
|
+
: { contextTransferReceipt: run.contextTransferReceipt }),
|
|
305
324
|
...(usage ? { usage } : {}),
|
|
306
325
|
metadata: {
|
|
307
326
|
runId: run.id,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AgentExactRunControlRef, type AgentRunControlRef, type Sha256Digest } from "@tangle-network/agent-interface";
|
|
2
2
|
import type { AgentTurnInput, CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
|
|
3
|
+
import type { ContextTransferReceipt } from "@tangle-network/agent-interface";
|
|
3
4
|
import type { CliBridgeProviderOptions } from "./provider-options.js";
|
|
4
5
|
export interface CliBridgeRun {
|
|
5
6
|
id: string;
|
|
@@ -13,6 +14,7 @@ export interface CliBridgeRun {
|
|
|
13
14
|
requestDigest?: Sha256Digest;
|
|
14
15
|
controlRef?: AgentExactRunControlRef;
|
|
15
16
|
cancellation?: Promise<CliBridgeRunSnapshot>;
|
|
17
|
+
contextTransferReceipt?: ContextTransferReceipt;
|
|
16
18
|
}
|
|
17
19
|
export interface CliBridgeSessionState {
|
|
18
20
|
readonly id: string;
|
|
@@ -77,9 +77,17 @@ export function prepareCliBridgeRun(options, environmentInput, originalTurn, pro
|
|
|
77
77
|
throw new Error("native cli-bridge turns require stable turnId and executionId");
|
|
78
78
|
}
|
|
79
79
|
const turnId = validatedTurn.turnId ?? crypto.randomUUID();
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
80
|
+
const destination = validatedTurn.contextTransfer?.plan.destination;
|
|
81
|
+
const runId = destination?.runId ?? cliBridgeRunId(environmentId, validatedTurn, turnId);
|
|
82
|
+
const sessionId = validatedTurn.sessionId ?? destination?.sessionId ?? runId;
|
|
83
|
+
const executionId = validatedTurn.executionId ?? destination?.executionId ?? runId;
|
|
84
|
+
if (destination !== undefined &&
|
|
85
|
+
(destination.provider !== providerName ||
|
|
86
|
+
destination.environmentId !== environmentId ||
|
|
87
|
+
destination.sessionId !== sessionId ||
|
|
88
|
+
destination.executionId !== executionId)) {
|
|
89
|
+
throw new Error("cli-bridge context transfer targets another destination");
|
|
90
|
+
}
|
|
83
91
|
const turn = {
|
|
84
92
|
...validatedTurn,
|
|
85
93
|
turnId,
|
package/dist/wire.js
CHANGED
|
@@ -24,6 +24,9 @@ export function toChatCompletionsBody(options, environmentInput, turn, coordinat
|
|
|
24
24
|
...(environmentInput.workspace?.cwd ? { cwd: environmentInput.workspace.cwd } : {}),
|
|
25
25
|
...(execution ? { execution } : {}),
|
|
26
26
|
...(interactions === undefined ? {} : { interactions }),
|
|
27
|
+
...(turn.contextTransfer === undefined
|
|
28
|
+
? {}
|
|
29
|
+
: { context_transfer: turn.contextTransfer }),
|
|
27
30
|
metadata: {
|
|
28
31
|
...(environmentInput.metadata ?? {}),
|
|
29
32
|
...(turn.context ?? {}),
|
|
@@ -99,6 +102,9 @@ export function toRetainedTurnBody(turn, run, providerName) {
|
|
|
99
102
|
provider: providerName,
|
|
100
103
|
environment_id: run.environmentId,
|
|
101
104
|
...(turn.interactions === undefined ? {} : { interactions }),
|
|
105
|
+
...(turn.contextTransfer === undefined
|
|
106
|
+
? {}
|
|
107
|
+
: { context_transfer: turn.contextTransfer }),
|
|
102
108
|
...(turn.context !== undefined ? { context: turn.context } : {}),
|
|
103
109
|
...(turn.providerOptions !== undefined
|
|
104
110
|
? { provider_options: turn.providerOptions }
|
|
@@ -111,8 +117,6 @@ function assertRetainedTurnInputSupported(turn) {
|
|
|
111
117
|
unsupported.push("timeoutMs");
|
|
112
118
|
if (turn.controlRef !== undefined)
|
|
113
119
|
unsupported.push("controlRef");
|
|
114
|
-
if (turn.contextTransfer !== undefined)
|
|
115
|
-
unsupported.push("contextTransfer");
|
|
116
120
|
if (turn.nativeContinuation !== undefined)
|
|
117
121
|
unsupported.push("nativeContinuation");
|
|
118
122
|
// `detach` controls the provider's response reader. The bridge request stays unchanged.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-provider-cli-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "AgentEnvironmentProvider adapter for cli-bridge retained sessions and one-shot turns",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"dist/environment-identity.js",
|
|
30
30
|
"dist/capability-discovery.d.ts",
|
|
31
31
|
"dist/capability-discovery.js",
|
|
32
|
+
"dist/context-transfer.d.ts",
|
|
33
|
+
"dist/context-transfer.js",
|
|
32
34
|
"dist/interaction-response.d.ts",
|
|
33
35
|
"dist/interaction-response.js",
|
|
34
36
|
"dist/observation.d.ts",
|
|
@@ -59,13 +61,13 @@
|
|
|
59
61
|
"LICENSE"
|
|
60
62
|
],
|
|
61
63
|
"dependencies": {
|
|
62
|
-
"@tangle-network/agent-interface": "^1.
|
|
64
|
+
"@tangle-network/agent-interface": "^1.9.0",
|
|
63
65
|
"undici": "7.29.0"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
|
-
"@types/node": "
|
|
67
|
-
"typescript": "
|
|
68
|
-
"vitest": "
|
|
68
|
+
"@types/node": "26.4.0",
|
|
69
|
+
"typescript": "7.0.2",
|
|
70
|
+
"vitest": "4.1.11"
|
|
69
71
|
},
|
|
70
72
|
"scripts": {
|
|
71
73
|
"build": "pnpm run clean && tsc -p tsconfig.build.json",
|