@tangle-network/agent-provider-cli-bridge 0.2.33 → 0.3.1

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 CHANGED
@@ -8,9 +8,23 @@ import { createCliBridgeProvider } from '@tangle-network/agent-provider-cli-brid
8
8
  const provider = createCliBridgeProvider({
9
9
  baseUrl: 'http://127.0.0.1:8787',
10
10
  bearerToken: process.env.CLI_BRIDGE_TOKEN,
11
- defaultModel: 'codex',
11
+ })
12
+
13
+ const environment = await provider.create({
14
+ profile: {
15
+ name: 'researcher',
16
+ harness: 'codex',
17
+ model: { default: 'gpt-5' },
18
+ },
12
19
  })
13
20
  ```
14
21
 
22
+ The bridge model is selected from run data in this order: the turn, the provider default, or the profile's `harness` plus `model.default`.
23
+ Execution fails before network use when none is present.
24
+
25
+ Passing the same `sessionId` on later turns continues the same CLI conversation.
26
+ `executionId` gives a turn stable bridge identity, and `lastEventId` reattaches after a reader failure.
27
+ Stopping a reader or destroying the environment cancels every active bridge run and waits for terminal confirmation.
28
+
15
29
  Response headers and streamed bodies have no transport timeout by default.
16
30
  For unattended runs, set `headersTimeoutMs`, `bodyTimeoutMs`, or an `AbortSignal` so an unresponsive bridge cannot wait forever.
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface CliBridgeProviderOptions {
17
17
  headersTimeoutMs?: number;
18
18
  /** Maximum idle time between response body chunks. Defaults to no timeout. */
19
19
  bodyTimeoutMs?: number;
20
+ /** Maximum wait for cli-bridge to confirm cancellation. Defaults to 30 seconds. */
21
+ cancelWaitMs?: number;
20
22
  fetch?: typeof fetch;
21
23
  name?: string;
22
24
  capabilities?: AgentEnvironmentCapabilities;
package/dist/index.js CHANGED
@@ -1,78 +1,250 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { Agent, fetch as undiciFetch } from "undici";
2
3
  export function createCliBridgeProvider(options) {
3
4
  assertTimeout(options.headersTimeoutMs, "headersTimeoutMs");
4
5
  assertTimeout(options.bodyTimeoutMs, "bodyTimeoutMs");
6
+ assertTimeout(options.cancelWaitMs, "cancelWaitMs");
5
7
  const name = options.name ?? "cli-bridge";
6
8
  return {
7
9
  name,
8
10
  capabilities: () => options.capabilities ?? defaultCliBridgeCapabilities(),
9
11
  async create(input) {
10
12
  const transport = createTransport(options);
13
+ const environmentId = input.idempotencyKey ?? crypto.randomUUID();
14
+ const runs = new Map();
15
+ const readers = new Set();
11
16
  let destroyed = false;
12
17
  let closePromise;
13
- return {
14
- id: input.idempotencyKey ?? input.name ?? crypto.randomUUID(),
18
+ const stream = async function* (turn) {
19
+ if (destroyed)
20
+ throw new Error("cli-bridge environment is destroyed");
21
+ yield* streamTrackedCliBridgeTurn(options, input, turn, transport, environmentId, runs, readers);
22
+ };
23
+ const environment = {
24
+ id: environmentId,
15
25
  provider: name,
16
26
  ...(input.name ? { name: input.name } : {}),
17
27
  status: async () => (destroyed ? "stopped" : "running"),
18
- stream: (turn) => {
19
- if (destroyed)
20
- throw new Error("cli-bridge environment is destroyed");
21
- return streamCliBridgeTurn(options, input, turn, transport);
22
- },
28
+ stream,
23
29
  placement: async () => ({
24
30
  kind: options.defaultExecution?.kind === "sandbox" ? "sandbox" : "local",
25
31
  providerMetadata: { baseUrl: options.baseUrl },
26
32
  }),
27
- destroy: () => {
33
+ async destroy() {
34
+ if (closePromise)
35
+ return closePromise;
28
36
  destroyed = true;
29
- closePromise ??= transport.close();
30
- return closePromise;
37
+ let cancellationsConfirmed = false;
38
+ const attempt = (async () => {
39
+ const cancellations = await Promise.allSettled(Array.from(runs.values()).map(async (run) => {
40
+ const snapshot = await cancelCliBridgeRun(options, transport, run);
41
+ if (runs.get(run.id) === run)
42
+ runs.delete(run.id);
43
+ return snapshot;
44
+ }));
45
+ const failures = cancellations.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
46
+ if (failures.length === 1)
47
+ throw failures[0];
48
+ if (failures.length > 1) {
49
+ throw new AggregateError(failures, "cli-bridge environment cancellation failed");
50
+ }
51
+ cancellationsConfirmed = true;
52
+ for (const reader of readers) {
53
+ reader.abort(new DOMException("cli-bridge environment was destroyed", "AbortError"));
54
+ }
55
+ await transport.close();
56
+ })();
57
+ closePromise = attempt;
58
+ try {
59
+ await attempt;
60
+ }
61
+ catch (error) {
62
+ closePromise = undefined;
63
+ if (!cancellationsConfirmed)
64
+ destroyed = false;
65
+ throw error;
66
+ }
31
67
  },
32
68
  };
69
+ return environment;
33
70
  },
34
71
  };
35
72
  }
36
- async function* streamCliBridgeTurn(options, environmentInput, turn, transport) {
73
+ async function* streamTrackedCliBridgeTurn(options, environmentInput, originalTurn, transport, environmentId, runs, readers) {
74
+ if (originalTurn.detach) {
75
+ throw new Error("cli-bridge provider does not support detached turns");
76
+ }
77
+ const sessionId = originalTurn.sessionId;
78
+ const turnId = originalTurn.turnId ?? crypto.randomUUID();
79
+ const runId = cliBridgeRunId(environmentId, originalTurn, turnId);
80
+ const controller = new AbortController();
81
+ const signals = [
82
+ originalTurn.signal,
83
+ environmentInput.signal,
84
+ controller.signal,
85
+ ].filter((signal) => signal !== undefined);
86
+ const turn = {
87
+ ...originalTurn,
88
+ turnId,
89
+ ...(signals.length > 0 ? { signal: AbortSignal.any(signals) } : {}),
90
+ };
91
+ const requestBody = JSON.stringify(toChatCompletionsBody(options, environmentInput, turn, runId));
92
+ const run = runs.get(runId) ?? {
93
+ id: runId,
94
+ readers: new Set(),
95
+ };
96
+ run.readers.add(controller);
97
+ readers.add(controller);
98
+ runs.set(runId, run);
99
+ let drained = false;
100
+ let threw = false;
101
+ try {
102
+ for await (const event of streamCliBridgeTurn(options, turn, requestBody, transport, runId, originalTurn.lastEventId, signals.length > 0 ? AbortSignal.any(signals) : undefined)) {
103
+ yield event;
104
+ }
105
+ drained = true;
106
+ if (runs.get(run.id) === run)
107
+ runs.delete(run.id);
108
+ }
109
+ catch (error) {
110
+ threw = true;
111
+ if (error instanceof CliBridgeRequestRejectedError) {
112
+ if (runs.get(run.id) === run)
113
+ runs.delete(run.id);
114
+ throw error;
115
+ }
116
+ let snapshot;
117
+ try {
118
+ snapshot = await getCliBridgeRun(options, transport, run.id);
119
+ }
120
+ catch {
121
+ snapshot = undefined;
122
+ }
123
+ if (snapshot?.terminal) {
124
+ if (runs.get(run.id) === run)
125
+ runs.delete(run.id);
126
+ }
127
+ else if ((originalTurn.signal?.aborted || environmentInput.signal?.aborted)) {
128
+ await cancelCliBridgeRun(options, transport, run);
129
+ if (runs.get(run.id) === run)
130
+ runs.delete(run.id);
131
+ }
132
+ throw error;
133
+ }
134
+ finally {
135
+ if (!drained && !threw && runs.get(run.id) === run) {
136
+ await cancelCliBridgeRun(options, transport, run);
137
+ runs.delete(run.id);
138
+ }
139
+ run.readers.delete(controller);
140
+ readers.delete(controller);
141
+ }
142
+ }
143
+ async function cancelCliBridgeRun(options, transport, run) {
144
+ if (run.cancellation)
145
+ return run.cancellation;
146
+ run.cancellation = (async () => {
147
+ const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/runs/${encodeURIComponent(run.id)}/cancel`, {
148
+ method: "POST",
149
+ headers: requestHeaders(options),
150
+ body: "{}",
151
+ });
152
+ if (!response.ok) {
153
+ throw new Error(`cli-bridge cancel ${response.status}: ${await response.text()}`);
154
+ }
155
+ let snapshot = cancelSnapshot(await response.text());
156
+ const waitBudgetMs = options.cancelWaitMs ?? 30_000;
157
+ const deadline = Date.now() + waitBudgetMs;
158
+ while (!snapshot.terminal) {
159
+ const remainingMs = deadline - Date.now();
160
+ if (remainingMs <= 0)
161
+ break;
162
+ snapshot = await getCliBridgeRun(options, transport, run.id, Math.min(remainingMs, 30_000));
163
+ if (snapshot === null) {
164
+ throw new Error(`cli-bridge lost run "${run.id}" before confirming cancellation`);
165
+ }
166
+ }
167
+ if (!snapshot?.terminal) {
168
+ throw new Error(`cli-bridge run "${run.id}" did not confirm terminal cancellation`);
169
+ }
170
+ for (const reader of run.readers) {
171
+ reader.abort(new DOMException(`cli-bridge run ended ${snapshot.status}`, "AbortError"));
172
+ }
173
+ return snapshot;
174
+ })();
175
+ try {
176
+ return await run.cancellation;
177
+ }
178
+ finally {
179
+ run.cancellation = undefined;
180
+ }
181
+ }
182
+ async function getCliBridgeRun(options, transport, runId, waitMs) {
183
+ const query = waitMs === undefined ? "" : `?wait_ms=${waitMs}`;
184
+ const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/runs/${encodeURIComponent(runId)}${query}`, {
185
+ method: "GET",
186
+ headers: requestHeaders(options),
187
+ });
188
+ if (response.status === 404)
189
+ return null;
190
+ if (!response.ok) {
191
+ throw new Error(`cli-bridge run status ${response.status}: ${await response.text()}`);
192
+ }
193
+ return runSnapshot(await response.text());
194
+ }
195
+ async function* streamCliBridgeTurn(options, turn, requestBody, transport, runId, lastEventId, signal) {
37
196
  const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/chat/completions`, {
38
197
  method: "POST",
39
198
  headers: {
40
- "content-type": "application/json",
199
+ ...requestHeaders(options),
41
200
  accept: "text/event-stream",
42
- ...(options.bearerToken ? { authorization: `Bearer ${options.bearerToken}` } : {}),
43
201
  ...(turn.sessionId ? { "x-session-id": turn.sessionId } : {}),
202
+ ...(lastEventId ? { "last-event-id": lastEventId } : {}),
44
203
  },
45
- body: JSON.stringify(toChatCompletionsBody(options, environmentInput, turn)),
46
- signal: turn.signal ?? environmentInput.signal,
204
+ body: requestBody,
205
+ signal,
47
206
  });
48
207
  if (!response.ok) {
49
- throw new Error(`cli-bridge ${response.status}: ${await response.text()}`);
208
+ let detail = "request rejected";
209
+ try {
210
+ detail = await response.text();
211
+ }
212
+ catch {
213
+ // The HTTP status already proves this request was rejected.
214
+ }
215
+ throw new CliBridgeRequestRejectedError(response.status, detail);
50
216
  }
51
217
  if (!response.body)
52
218
  throw new Error("cli-bridge response body is empty");
53
219
  let text = "";
54
- const sessionId = turn.sessionId ?? environmentInput.idempotencyKey ?? environmentInput.name ?? "cli-bridge";
220
+ const sessionId = turn.sessionId ?? runId;
55
221
  const messageId = turn.turnId ?? `${sessionId}:assistant`;
56
222
  const emittedToolCalls = new Set();
57
223
  let completed = false;
58
- for await (const event of parseSse(response.body)) {
59
- if (event === "[DONE]")
224
+ let terminalCursor;
225
+ for await (const frame of parseSse(response.body)) {
226
+ if (frame.data === "[DONE]")
60
227
  continue;
61
- const parsed = safeJson(event);
228
+ const parsed = safeJson(frame.data);
62
229
  if (!parsed)
63
230
  continue;
64
231
  if (parsed.error && typeof parsed.error === "object") {
65
232
  const error = parsed.error;
66
233
  const message = typeof error.message === "string" ? error.message : "cli-bridge error";
67
- yield { type: "status", data: { status: "failed", error: message } };
234
+ yield {
235
+ type: "status",
236
+ data: { status: "failed", error: message },
237
+ ...(frame.id ? { id: frame.id } : {}),
238
+ };
68
239
  throw new Error(`cli-bridge: ${message}`);
69
240
  }
70
241
  const choice = Array.isArray(parsed.choices) ? parsed.choices[0] : undefined;
71
242
  const delta = choice?.delta;
72
243
  const chunk = delta && typeof delta.content === "string" ? delta.content : "";
73
244
  const nextUsage = usageFromOpenAi(parsed.usage);
245
+ const frameEvents = [];
74
246
  if (nextUsage) {
75
- yield { type: "usage", data: {}, usage: nextUsage };
247
+ frameEvents.push({ type: "usage", data: {}, usage: nextUsage });
76
248
  }
77
249
  if (chunk) {
78
250
  text += chunk;
@@ -88,11 +260,11 @@ async function* streamCliBridgeTurn(options, environmentInput, turn, transport)
88
260
  part,
89
261
  delta: chunk,
90
262
  };
91
- yield {
263
+ frameEvents.push({
92
264
  type: "message.part.updated",
93
265
  data: { part, delta: chunk },
94
266
  normalized,
95
- };
267
+ });
96
268
  }
97
269
  for (const toolCall of toolCallsFromDelta(delta)) {
98
270
  const callId = toolCall.id ?? `${messageId}:tool:${toolCall.index}`;
@@ -112,33 +284,93 @@ async function* streamCliBridgeTurn(options, environmentInput, turn, transport)
112
284
  type: "message.part.updated",
113
285
  part,
114
286
  };
115
- yield {
287
+ frameEvents.push({
116
288
  type: "message.part.updated",
117
289
  data: { part },
118
290
  normalized,
119
- };
291
+ });
120
292
  }
121
293
  if (choice?.finish_reason) {
122
294
  if (choice.finish_reason === "error") {
123
- yield {
295
+ frameEvents.push({
124
296
  type: "status",
125
297
  data: { status: "failed", error: "cli-bridge returned finish_reason=error" },
126
- };
298
+ });
299
+ yield* eventsWithCursor(frameEvents, frame.id);
127
300
  throw new Error("cli-bridge returned finish_reason=error");
128
301
  }
129
302
  completed = true;
130
- yield {
131
- type: "result",
132
- data: {
133
- finalText: text,
134
- finishReason: choice.finish_reason,
135
- status: "completed",
136
- },
137
- };
303
+ if (lastEventId) {
304
+ terminalCursor = frame.id;
305
+ }
306
+ else {
307
+ frameEvents.push({
308
+ type: "result",
309
+ data: {
310
+ finalText: text,
311
+ finishReason: choice.finish_reason,
312
+ status: "completed",
313
+ },
314
+ });
315
+ }
138
316
  }
317
+ yield* eventsWithCursor(frameEvents, choice?.finish_reason && lastEventId ? undefined : frame.id);
139
318
  }
140
- if (!completed)
319
+ if (!completed && !lastEventId) {
141
320
  throw new Error("cli-bridge stream ended without a terminal result");
321
+ }
322
+ if (lastEventId) {
323
+ const result = await readFullCliBridgeResult(options, requestBody, transport, signal);
324
+ yield {
325
+ type: "result",
326
+ data: {
327
+ finalText: result.text,
328
+ finishReason: result.finishReason,
329
+ status: "completed",
330
+ },
331
+ id: terminalCursor ?? lastEventId,
332
+ };
333
+ }
334
+ }
335
+ class CliBridgeRequestRejectedError extends Error {
336
+ status;
337
+ constructor(status, detail) {
338
+ super(`cli-bridge ${status}: ${detail}`);
339
+ this.status = status;
340
+ this.name = "CliBridgeRequestRejectedError";
341
+ }
342
+ }
343
+ async function readFullCliBridgeResult(options, requestBody, transport, signal) {
344
+ const body = safeJson(requestBody);
345
+ if (!body)
346
+ throw new Error("cli-bridge replay request is not valid JSON");
347
+ const response = await transport.fetch(`${trimSlash(options.baseUrl)}/v1/chat/completions`, {
348
+ method: "POST",
349
+ headers: requestHeaders(options),
350
+ body: JSON.stringify({ ...body, stream: false }),
351
+ signal,
352
+ });
353
+ if (!response.ok) {
354
+ throw new Error(`cli-bridge replay result ${response.status}: ${await response.text()}`);
355
+ }
356
+ const parsed = safeJson(await response.text());
357
+ if (parsed?.error && typeof parsed.error === "object") {
358
+ const error = parsed.error;
359
+ const message = typeof error.message === "string" ? error.message : "cli-bridge replay failed";
360
+ throw new Error(`cli-bridge replay result failed: ${message}`);
361
+ }
362
+ const choice = Array.isArray(parsed?.choices) ? parsed.choices[0] : undefined;
363
+ const message = choice?.message && typeof choice.message === "object"
364
+ ? choice.message
365
+ : undefined;
366
+ if (typeof message?.content !== "string" ||
367
+ typeof choice?.finish_reason !== "string") {
368
+ throw new Error("cli-bridge replay result returned an invalid completion");
369
+ }
370
+ if (choice.finish_reason === "error" || choice.finish_reason === "timeout") {
371
+ throw new Error(`cli-bridge replay result ended ${choice.finish_reason}`);
372
+ }
373
+ return { text: message.content, finishReason: choice.finish_reason };
142
374
  }
143
375
  function createTransport(options) {
144
376
  if (options.fetch) {
@@ -167,13 +399,14 @@ function assertTimeout(value, name) {
167
399
  throw new Error(`createCliBridgeProvider ${name} must be a non-negative integer`);
168
400
  }
169
401
  }
170
- function toChatCompletionsBody(options, environmentInput, turn) {
402
+ function toChatCompletionsBody(options, environmentInput, turn, runId) {
171
403
  const profile = inlineProfile(environmentInput.profile);
172
404
  return {
173
- model: turn.model ?? environmentInput.backend ?? options.defaultModel ?? "opencode",
405
+ model: resolveBridgeModel(options, environmentInput, turn, profile),
174
406
  messages: messagesFromTurn(turn, profile),
175
407
  stream: true,
176
408
  ...(turn.sessionId ? { session_id: turn.sessionId } : {}),
409
+ run_id: runId,
177
410
  ...(options.defaultMode ? { mode: options.defaultMode } : {}),
178
411
  ...(profile ? { agent_profile: profile } : {}),
179
412
  ...(environmentInput.env ? { env: environmentInput.env } : {}),
@@ -186,6 +419,25 @@ function toChatCompletionsBody(options, environmentInput, turn) {
186
419
  },
187
420
  };
188
421
  }
422
+ function resolveBridgeModel(options, environmentInput, turn, profile) {
423
+ const harness = environmentInput.backend ?? profile?.harness;
424
+ const model = turn.model ?? options.defaultModel ?? profile?.model?.default;
425
+ const provider = profile?.model?.provider;
426
+ if (!harness) {
427
+ if (model)
428
+ return model;
429
+ throw new Error("createCliBridgeProvider requires an explicit bridge model or a profile/backend harness");
430
+ }
431
+ if (!model || model === harness)
432
+ return harness;
433
+ if (model.startsWith(`${harness}/`))
434
+ return model;
435
+ if (model.includes("/"))
436
+ return `${harness}/${model}`;
437
+ if (provider)
438
+ return `${harness}/${provider}/${model}`;
439
+ return `${harness}/${model}`;
440
+ }
189
441
  function messagesFromTurn(turn, profile) {
190
442
  const messages = [];
191
443
  const systemPrompt = profile?.prompt?.systemPrompt;
@@ -223,14 +475,14 @@ async function* parseSse(body) {
223
475
  const frame = buffer.slice(0, boundary.index);
224
476
  buffer = buffer.slice(boundary.index + boundary.length);
225
477
  const data = dataFromFrame(frame);
226
- if (data !== undefined)
478
+ if (data)
227
479
  yield data;
228
480
  boundary = findFrameBoundary(buffer);
229
481
  }
230
482
  }
231
483
  if (buffer) {
232
484
  const data = dataFromFrame(buffer);
233
- if (data !== undefined)
485
+ if (data)
234
486
  yield data;
235
487
  }
236
488
  }
@@ -249,7 +501,16 @@ function dataFromFrame(frame) {
249
501
  .filter((line) => line.startsWith("data:"))
250
502
  .map((line) => line.slice("data:".length).trimStart())
251
503
  .join("\n");
252
- return data || undefined;
504
+ if (!data)
505
+ return undefined;
506
+ const id = lines.find((line) => line.startsWith("id:"))?.slice("id:".length).trim();
507
+ return { data, ...(id ? { id } : {}) };
508
+ }
509
+ function* eventsWithCursor(events, cursor) {
510
+ for (let index = 0; index < events.length; index += 1) {
511
+ const event = events[index];
512
+ yield cursor && index === events.length - 1 ? { ...event, id: cursor } : event;
513
+ }
253
514
  }
254
515
  function toolCallsFromDelta(value) {
255
516
  if (!value || typeof value !== "object")
@@ -302,6 +563,52 @@ function usageFromOpenAi(value) {
302
563
  ...(cost !== undefined ? { cost } : {}),
303
564
  };
304
565
  }
566
+ function requestHeaders(options) {
567
+ return {
568
+ "content-type": "application/json",
569
+ ...(options.bearerToken ? { authorization: `Bearer ${options.bearerToken}` } : {}),
570
+ };
571
+ }
572
+ function cliBridgeRunId(environmentId, turn, turnId) {
573
+ if (turn.executionId &&
574
+ turn.executionId.length <= 128 &&
575
+ /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(turn.executionId)) {
576
+ return turn.executionId;
577
+ }
578
+ const digest = createHash("sha256")
579
+ .update(environmentId)
580
+ .update("\0")
581
+ .update(turn.sessionId ?? "")
582
+ .update("\0")
583
+ .update(turn.executionId ?? turnId)
584
+ .digest("hex");
585
+ return `agent-${digest}`;
586
+ }
587
+ function runSnapshot(value) {
588
+ const parsed = safeJson(value);
589
+ if (!parsed)
590
+ throw new Error("cli-bridge run status returned invalid JSON");
591
+ const id = parsed.id;
592
+ const status = parsed.status;
593
+ const terminal = parsed.terminal;
594
+ if (typeof id !== "string" ||
595
+ !["running", "done", "error", "cancelled"].includes(String(status)) ||
596
+ typeof terminal !== "boolean") {
597
+ throw new Error("cli-bridge run status returned an invalid snapshot");
598
+ }
599
+ return {
600
+ id,
601
+ status: status,
602
+ terminal,
603
+ };
604
+ }
605
+ function cancelSnapshot(value) {
606
+ const parsed = safeJson(value);
607
+ if (!parsed || !parsed.run || typeof parsed.run !== "object") {
608
+ throw new Error("cli-bridge cancel returned an invalid snapshot");
609
+ }
610
+ return runSnapshot(JSON.stringify(parsed.run));
611
+ }
305
612
  function number(value) {
306
613
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
307
614
  }
@@ -331,7 +638,7 @@ export function defaultCliBridgeCapabilities() {
331
638
  runtimeUpdate: false,
332
639
  validation: false,
333
640
  },
334
- streaming: { live: true, replay: false, detach: false, turnIdempotency: true },
641
+ streaming: { live: true, replay: true, detach: false, turnIdempotency: true },
335
642
  sessions: { continue: true, list: false, messages: false },
336
643
  workspace: { read: false, write: false, exec: false, git: false, upload: false, download: false },
337
644
  branching: { checkpoint: false, fork: false },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-provider-cli-bridge",
3
- "version": "0.2.33",
3
+ "version": "0.3.1",
4
4
  "description": "AgentEnvironmentProvider adapter for cli-bridge /v1/chat/completions",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,7 +29,7 @@
29
29
  "LICENSE"
30
30
  ],
31
31
  "dependencies": {
32
- "@tangle-network/agent-interface": "0.39.0",
32
+ "@tangle-network/agent-interface": "0.41.0",
33
33
  "undici": "7.28.0"
34
34
  },
35
35
  "devDependencies": {