@tangle-network/agent-provider-cli-bridge 0.7.8 → 0.8.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 +36 -0
- package/dist/capability-discovery.d.ts +15 -0
- package/dist/capability-discovery.js +164 -0
- package/dist/environment-identity.d.ts +13 -0
- package/dist/environment-identity.js +87 -0
- package/dist/index.js +83 -12
- package/dist/interaction-response.d.ts +15 -0
- package/dist/interaction-response.js +100 -0
- package/dist/retained-canonical-event.d.ts +8 -1
- package/dist/retained-control.js +3 -1
- package/dist/retained-environment.d.ts +2 -0
- package/dist/retained-environment.js +49 -7
- package/dist/retained-execution.d.ts +4 -3
- package/dist/retained-execution.js +79 -34
- package/dist/retained-native.d.ts +27 -0
- package/dist/retained-native.js +204 -0
- package/dist/retained-run-state.d.ts +1 -1
- package/dist/retained-run-state.js +4 -0
- package/dist/retained-stream.d.ts +1 -1
- package/dist/retained-stream.js +33 -6
- package/dist/wire.d.ts +7 -2
- package/dist/wire.js +107 -4
- package/package.json +13 -4
package/dist/retained-stream.js
CHANGED
|
@@ -19,7 +19,7 @@ class CliBridgeRunCancelledError extends Error {
|
|
|
19
19
|
this.name = "CliBridgeRunCancelledError";
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
export async function* streamCliBridgeTurn(options, turn, requestBody, transport, runId, lastEventId, signal, onResponse, readAuthoritativeRun) {
|
|
22
|
+
export async function* streamCliBridgeTurn(options, turn, requestBody, transport, runId, lastEventId, signal, onResponse, readAuthoritativeRun, validateCanonicalEvent) {
|
|
23
23
|
const reconnect = requestBody === undefined;
|
|
24
24
|
const replay = retainedReplayRequest(lastEventId);
|
|
25
25
|
const response = await transport.fetch(reconnect
|
|
@@ -74,7 +74,8 @@ export async function* streamCliBridgeTurn(options, turn, requestBody, transport
|
|
|
74
74
|
let textBoundaryPending = false;
|
|
75
75
|
let sawProtocolEnd = false;
|
|
76
76
|
let protocol = "unknown";
|
|
77
|
-
let
|
|
77
|
+
let canonicalTerminalStatus;
|
|
78
|
+
let canonicalTerminalEvent;
|
|
78
79
|
let terminalFrameSeen = false;
|
|
79
80
|
let cancelledTerminalSeen = false;
|
|
80
81
|
let terminalFrame;
|
|
@@ -92,7 +93,7 @@ export async function* streamCliBridgeTurn(options, turn, requestBody, transport
|
|
|
92
93
|
throw new Error("cli-bridge mixed canonical and OpenAI stream formats");
|
|
93
94
|
}
|
|
94
95
|
protocol = "canonical";
|
|
95
|
-
if (
|
|
96
|
+
if (canonicalTerminalStatus !== undefined) {
|
|
96
97
|
throw new Error("cli-bridge emitted an event after canonical terminal status");
|
|
97
98
|
}
|
|
98
99
|
const event = retainedCanonicalEvent(frame, {
|
|
@@ -105,8 +106,14 @@ export async function* streamCliBridgeTurn(options, turn, requestBody, transport
|
|
|
105
106
|
if (!event) {
|
|
106
107
|
throw new Error("cli-bridge emitted an empty canonical event");
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
validateCanonicalEvent?.(event);
|
|
110
|
+
if (isTerminalCanonicalEvent(event)) {
|
|
111
|
+
canonicalTerminalStatus = event.normalized.status;
|
|
112
|
+
canonicalTerminalEvent = event;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
yield event;
|
|
116
|
+
}
|
|
110
117
|
continue;
|
|
111
118
|
}
|
|
112
119
|
if (protocol === "canonical") {
|
|
@@ -253,9 +260,29 @@ export async function* streamCliBridgeTurn(options, turn, requestBody, transport
|
|
|
253
260
|
yield* retainedEventsWithIdentity(frameEvents, frame.id, runId, sessionId, turn.executionId, replay.anchor);
|
|
254
261
|
}
|
|
255
262
|
if (protocol === "canonical") {
|
|
256
|
-
if (
|
|
263
|
+
if (canonicalTerminalStatus === undefined) {
|
|
257
264
|
throw new Error("cli-bridge canonical stream ended without terminal status");
|
|
258
265
|
}
|
|
266
|
+
const snapshot = await terminalRunSnapshot(runId, readAuthoritativeRun);
|
|
267
|
+
const expectedStatus = canonicalTerminalStatus === "completed"
|
|
268
|
+
? "done"
|
|
269
|
+
: canonicalTerminalStatus === "failed"
|
|
270
|
+
? "error"
|
|
271
|
+
: "cancelled";
|
|
272
|
+
if (snapshot.status !== expectedStatus) {
|
|
273
|
+
throw new Error(`cli-bridge canonical terminal status ${JSON.stringify(canonicalTerminalStatus)} contradicts retained run status ${JSON.stringify(snapshot.status)}`);
|
|
274
|
+
}
|
|
275
|
+
if (!canonicalTerminalEvent) {
|
|
276
|
+
throw new Error("cli-bridge canonical terminal event was not retained");
|
|
277
|
+
}
|
|
278
|
+
yield canonicalTerminalEvent;
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (!sawProtocolEnd &&
|
|
282
|
+
reconnect &&
|
|
283
|
+
lastEventId !== undefined &&
|
|
284
|
+
protocol === "unknown") {
|
|
285
|
+
await terminalRunSnapshot(runId, readAuthoritativeRun);
|
|
259
286
|
return;
|
|
260
287
|
}
|
|
261
288
|
if (!sawProtocolEnd) {
|
package/dist/wire.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import type { TokenUsage } from "@tangle-network/agent-interface";
|
|
1
|
+
import type { AgentProfile, AgentProfileRef, TokenUsage } from "@tangle-network/agent-interface";
|
|
2
2
|
import type { AgentTurnInput, CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
|
|
3
3
|
import type { CliBridgeProviderOptions } from "./provider-options.js";
|
|
4
|
-
import type { CliBridgeRunSnapshot } from "./retained-run-state.js";
|
|
4
|
+
import type { CliBridgeRun, CliBridgeRunSnapshot } from "./retained-run-state.js";
|
|
5
5
|
export declare function toChatCompletionsBody(options: CliBridgeProviderOptions, environmentInput: CreateAgentEnvironmentInput, turn: AgentTurnInput, runId: string): Record<string, unknown>;
|
|
6
|
+
export declare function resolveBridgeModel(options: CliBridgeProviderOptions, environmentInput: CreateAgentEnvironmentInput, turn: AgentTurnInput, profile: AgentProfile | undefined): string;
|
|
7
|
+
export declare function toRetainedSessionBody(options: CliBridgeProviderOptions, environmentInput: CreateAgentEnvironmentInput, profile: AgentProfile | undefined, sessionId: string, model: string): Record<string, unknown>;
|
|
8
|
+
/** Encode the exact retained turn contract without moving posture into metadata. */
|
|
9
|
+
export declare function toRetainedTurnBody(turn: AgentTurnInput, run: CliBridgeRun, providerName: string): Record<string, unknown>;
|
|
10
|
+
export declare function inlineProfile(profile: AgentProfileRef): AgentProfile | undefined;
|
|
6
11
|
export interface CliBridgeSseFrame {
|
|
7
12
|
readonly data: string;
|
|
8
13
|
readonly id?: string;
|
package/dist/wire.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { AgentExactRunControlRefSchema } from "@tangle-network/agent-interface";
|
|
2
|
+
import { AgentExactRunControlRefSchema, RequestedInteractionsSchema, } from "@tangle-network/agent-interface";
|
|
3
3
|
export function toChatCompletionsBody(options, environmentInput, turn, runId) {
|
|
4
4
|
const profile = inlineProfile(environmentInput.profile);
|
|
5
5
|
const execution = executionFromInput(options, environmentInput);
|
|
6
|
+
const interactions = turn.interactions === undefined
|
|
7
|
+
? undefined
|
|
8
|
+
: RequestedInteractionsSchema.parse(turn.interactions);
|
|
6
9
|
return {
|
|
7
10
|
model: resolveBridgeModel(options, environmentInput, turn, profile),
|
|
8
11
|
messages: messagesFromTurn(turn),
|
|
@@ -17,6 +20,7 @@ export function toChatCompletionsBody(options, environmentInput, turn, runId) {
|
|
|
17
20
|
...(environmentInput.env ? { env: environmentInput.env } : {}),
|
|
18
21
|
...(environmentInput.workspace?.cwd ? { cwd: environmentInput.workspace.cwd } : {}),
|
|
19
22
|
...(execution ? { execution } : {}),
|
|
23
|
+
...(interactions === undefined ? {} : { interactions }),
|
|
20
24
|
metadata: {
|
|
21
25
|
...(environmentInput.metadata ?? {}),
|
|
22
26
|
...(turn.context ?? {}),
|
|
@@ -24,7 +28,7 @@ export function toChatCompletionsBody(options, environmentInput, turn, runId) {
|
|
|
24
28
|
},
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
|
-
function resolveBridgeModel(options, environmentInput, turn, profile) {
|
|
31
|
+
export function resolveBridgeModel(options, environmentInput, turn, profile) {
|
|
28
32
|
const harness = environmentInput.backend ?? profile?.harness;
|
|
29
33
|
const model = turn.model ?? options.defaultModel ?? profile?.model?.default;
|
|
30
34
|
const provider = profile?.model?.provider;
|
|
@@ -43,6 +47,77 @@ function resolveBridgeModel(options, environmentInput, turn, profile) {
|
|
|
43
47
|
return `${harness}/${provider}/${model}`;
|
|
44
48
|
return `${harness}/${model}`;
|
|
45
49
|
}
|
|
50
|
+
export function toRetainedSessionBody(options, environmentInput, profile, sessionId, model) {
|
|
51
|
+
const execution = retainedExecutionFromInput(options, environmentInput);
|
|
52
|
+
return {
|
|
53
|
+
id: sessionId,
|
|
54
|
+
model,
|
|
55
|
+
interaction_policy: "interactive",
|
|
56
|
+
...(options.defaultMode ? { mode: options.defaultMode } : {}),
|
|
57
|
+
...(environmentInput.workspace?.cwd !== undefined
|
|
58
|
+
? { cwd: environmentInput.workspace.cwd }
|
|
59
|
+
: {}),
|
|
60
|
+
...(execution !== undefined ? { execution } : {}),
|
|
61
|
+
...(profile ? { agent_profile: profile } : {}),
|
|
62
|
+
...(environmentInput.env !== undefined ? { env: environmentInput.env } : {}),
|
|
63
|
+
...(environmentInput.metadata !== undefined
|
|
64
|
+
? { metadata: environmentInput.metadata }
|
|
65
|
+
: {}),
|
|
66
|
+
...(environmentInput.providerOptions !== undefined
|
|
67
|
+
? { provider_options: environmentInput.providerOptions }
|
|
68
|
+
: {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Encode the exact retained turn contract without moving posture into metadata. */
|
|
72
|
+
export function toRetainedTurnBody(turn, run, providerName) {
|
|
73
|
+
assertRetainedTurnInputSupported(turn);
|
|
74
|
+
const interactions = RequestedInteractionsSchema.parse(turn.interactions ?? {});
|
|
75
|
+
const content = {};
|
|
76
|
+
if (turn.prompt !== undefined) {
|
|
77
|
+
if (turn.prompt.length === 0) {
|
|
78
|
+
throw new Error("cli-bridge retained sessions require a non-empty message");
|
|
79
|
+
}
|
|
80
|
+
content.message = turn.prompt;
|
|
81
|
+
}
|
|
82
|
+
if (turn.parts !== undefined) {
|
|
83
|
+
if (turn.parts.length === 0) {
|
|
84
|
+
throw new Error("cli-bridge retained sessions require non-empty input parts");
|
|
85
|
+
}
|
|
86
|
+
content.parts = turn.parts;
|
|
87
|
+
}
|
|
88
|
+
if (turn.prompt === undefined && turn.parts === undefined) {
|
|
89
|
+
throw new Error("cli-bridge retained sessions require a non-empty message or input parts");
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
...content,
|
|
93
|
+
turn_id: run.turnId,
|
|
94
|
+
execution_id: run.executionId,
|
|
95
|
+
run_id: run.id,
|
|
96
|
+
provider: providerName,
|
|
97
|
+
environment_id: run.environmentId,
|
|
98
|
+
...(turn.interactions === undefined ? {} : { interactions }),
|
|
99
|
+
...(turn.context !== undefined ? { context: turn.context } : {}),
|
|
100
|
+
...(turn.providerOptions !== undefined
|
|
101
|
+
? { provider_options: turn.providerOptions }
|
|
102
|
+
: {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function assertRetainedTurnInputSupported(turn) {
|
|
106
|
+
const unsupported = [];
|
|
107
|
+
if (turn.timeoutMs !== undefined)
|
|
108
|
+
unsupported.push("timeoutMs");
|
|
109
|
+
if (turn.controlRef !== undefined)
|
|
110
|
+
unsupported.push("controlRef");
|
|
111
|
+
if (turn.contextTransfer !== undefined)
|
|
112
|
+
unsupported.push("contextTransfer");
|
|
113
|
+
if (turn.nativeContinuation !== undefined)
|
|
114
|
+
unsupported.push("nativeContinuation");
|
|
115
|
+
if (turn.detach === true)
|
|
116
|
+
unsupported.push("detach");
|
|
117
|
+
if (unsupported.length > 0) {
|
|
118
|
+
throw new Error(`cli-bridge retained turns cannot represent ${unsupported.join(", ")}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
46
121
|
/** Keep task messages separate from the profile's harness-owned prompt controls. */
|
|
47
122
|
function messagesFromTurn(turn) {
|
|
48
123
|
return [{ role: "user", content: contentFromTurn(turn) }];
|
|
@@ -52,7 +127,7 @@ function contentFromTurn(turn) {
|
|
|
52
127
|
return turn.parts;
|
|
53
128
|
return turn.prompt ?? "";
|
|
54
129
|
}
|
|
55
|
-
function inlineProfile(profile) {
|
|
130
|
+
export function inlineProfile(profile) {
|
|
56
131
|
return typeof profile === "string" ? undefined : profile;
|
|
57
132
|
}
|
|
58
133
|
function executionFromInput(options, input) {
|
|
@@ -66,6 +141,34 @@ function executionFromInput(options, input) {
|
|
|
66
141
|
...(input.workspace.gitRef ? { gitRef: input.workspace.gitRef } : {}),
|
|
67
142
|
};
|
|
68
143
|
}
|
|
144
|
+
function retainedExecutionFromInput(options, input) {
|
|
145
|
+
const unsupported = [];
|
|
146
|
+
if (typeof input.profile === "string")
|
|
147
|
+
unsupported.push("named profile");
|
|
148
|
+
if (input.workspace?.environment !== undefined)
|
|
149
|
+
unsupported.push("workspace.environment");
|
|
150
|
+
if (input.workspace?.image !== undefined)
|
|
151
|
+
unsupported.push("workspace.image");
|
|
152
|
+
if (input.workspace?.repoUrl !== undefined)
|
|
153
|
+
unsupported.push("workspace.repoUrl");
|
|
154
|
+
if (input.workspace?.gitRef !== undefined)
|
|
155
|
+
unsupported.push("workspace.gitRef");
|
|
156
|
+
if (input.workspace?.providerOptions !== undefined) {
|
|
157
|
+
unsupported.push("workspace.providerOptions");
|
|
158
|
+
}
|
|
159
|
+
if (input.resources !== undefined)
|
|
160
|
+
unsupported.push("resources");
|
|
161
|
+
if (input.secrets !== undefined)
|
|
162
|
+
unsupported.push("secrets");
|
|
163
|
+
if (unsupported.length > 0) {
|
|
164
|
+
throw new Error(`cli-bridge retained sessions cannot represent ${unsupported.join(", ")}`);
|
|
165
|
+
}
|
|
166
|
+
const execution = executionFromInput(options, input);
|
|
167
|
+
if (execution?.kind === "sandbox") {
|
|
168
|
+
throw new Error("cli-bridge retained sessions cannot execute in a sandbox; use one-shot execution");
|
|
169
|
+
}
|
|
170
|
+
return execution;
|
|
171
|
+
}
|
|
69
172
|
export async function* parseSse(body) {
|
|
70
173
|
const decoder = new TextDecoder();
|
|
71
174
|
let buffer = "";
|
|
@@ -227,7 +330,7 @@ export function runSnapshot(value) {
|
|
|
227
330
|
const terminal = parsed.terminal;
|
|
228
331
|
if (typeof id !== "string" ||
|
|
229
332
|
(requestDigest !== undefined && !requestDigest.success) ||
|
|
230
|
-
!["running", "done", "error", "cancelled"].includes(String(status)) ||
|
|
333
|
+
!["running", "done", "error", "cancelled", "unknown"].includes(String(status)) ||
|
|
231
334
|
typeof terminal !== "boolean") {
|
|
232
335
|
throw new Error("cli-bridge run status returned an invalid snapshot");
|
|
233
336
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-provider-cli-bridge",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "AgentEnvironmentProvider adapter for cli-bridge
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "AgentEnvironmentProvider adapter for cli-bridge retained sessions and one-shot turns",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -25,6 +25,12 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist/index.d.ts",
|
|
27
27
|
"dist/index.js",
|
|
28
|
+
"dist/environment-identity.d.ts",
|
|
29
|
+
"dist/environment-identity.js",
|
|
30
|
+
"dist/capability-discovery.d.ts",
|
|
31
|
+
"dist/capability-discovery.js",
|
|
32
|
+
"dist/interaction-response.d.ts",
|
|
33
|
+
"dist/interaction-response.js",
|
|
28
34
|
"dist/observation.d.ts",
|
|
29
35
|
"dist/observation.js",
|
|
30
36
|
"dist/provider-options.d.ts",
|
|
@@ -39,6 +45,8 @@
|
|
|
39
45
|
"dist/retained-event-cursor.js",
|
|
40
46
|
"dist/retained-execution.d.ts",
|
|
41
47
|
"dist/retained-execution.js",
|
|
48
|
+
"dist/retained-native.d.ts",
|
|
49
|
+
"dist/retained-native.js",
|
|
42
50
|
"dist/retained-run-state.d.ts",
|
|
43
51
|
"dist/retained-run-state.js",
|
|
44
52
|
"dist/retained-stream.d.ts",
|
|
@@ -60,9 +68,10 @@
|
|
|
60
68
|
"vitest": "^4.1.5"
|
|
61
69
|
},
|
|
62
70
|
"scripts": {
|
|
63
|
-
"build": "tsc -p tsconfig.json",
|
|
71
|
+
"build": "pnpm run clean && tsc -p tsconfig.build.json",
|
|
64
72
|
"check-types": "tsc --noEmit",
|
|
65
73
|
"clean": "rm -rf dist",
|
|
66
|
-
"test": "vitest run"
|
|
74
|
+
"test": "vitest run src",
|
|
75
|
+
"test:integration": "test -n \"$CLI_BRIDGE_INTEGRATION_ROOT\" || { echo 'CLI_BRIDGE_INTEGRATION_ROOT is required; see README' >&2; exit 1; }; vitest run tests/cli-bridge.integration.test.ts"
|
|
67
76
|
}
|
|
68
77
|
}
|