@frockbot/plugin-shell 0.3.14 → 0.3.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-shell",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -51,42 +51,42 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@cordisjs/plugin-webui": "0.8.2",
54
- "@frockbot/agent-runtime": "0.3.14",
55
- "@frockbot/application-foundation": "0.3.14",
56
- "@frockbot/catalog-core": "0.3.14",
57
- "@frockbot/client-core": "0.3.14",
58
- "@frockbot/client-ui": "0.3.14",
59
- "@frockbot/computer-core": "0.3.14",
60
- "@frockbot/computer-host-protocol": "0.3.14",
61
- "@frockbot/configuration-core": "0.3.14",
62
- "@frockbot/connection-core": "0.3.14",
63
- "@frockbot/kernel-agent-loop": "0.3.14",
64
- "@frockbot/kernel-composition": "0.3.14",
65
- "@frockbot/kernel-contracts": "0.3.14",
66
- "@frockbot/kernel-do": "0.3.14",
67
- "@frockbot/machine-protocol": "0.3.14",
68
- "@frockbot/plugin-applets": "0.3.14",
69
- "@frockbot/plugin-authoring": "0.3.14",
70
- "@frockbot/plugin-bot-template": "0.3.14",
71
- "@frockbot/plugin-computer": "0.3.14",
72
- "@frockbot/plugin-flock": "0.3.14",
73
- "@frockbot/plugin-image": "0.3.14",
74
- "@frockbot/plugin-machine-messages": "0.3.14",
75
- "@frockbot/plugin-mcp": "0.3.14",
76
- "@frockbot/plugin-memory": "0.3.14",
77
- "@frockbot/plugin-package-catalog": "0.3.14",
78
- "@frockbot/plugin-routines": "0.3.14",
79
- "@frockbot/plugin-skills": "0.3.14",
80
- "@frockbot/plugin-subagents": "0.3.14",
81
- "@frockbot/plugin-user-machine": "0.3.14",
82
- "@frockbot/protocol": "0.3.14",
54
+ "@frockbot/agent-runtime": "0.3.16",
55
+ "@frockbot/application-foundation": "0.3.16",
56
+ "@frockbot/catalog-core": "0.3.16",
57
+ "@frockbot/client-core": "0.3.16",
58
+ "@frockbot/client-ui": "0.3.16",
59
+ "@frockbot/computer-core": "0.3.16",
60
+ "@frockbot/computer-host-protocol": "0.3.16",
61
+ "@frockbot/configuration-core": "0.3.16",
62
+ "@frockbot/connection-core": "0.3.16",
63
+ "@frockbot/kernel-agent-loop": "0.3.16",
64
+ "@frockbot/kernel-composition": "0.3.16",
65
+ "@frockbot/kernel-contracts": "0.3.16",
66
+ "@frockbot/kernel-do": "0.3.16",
67
+ "@frockbot/machine-protocol": "0.3.16",
68
+ "@frockbot/plugin-applets": "0.3.16",
69
+ "@frockbot/plugin-authoring": "0.3.16",
70
+ "@frockbot/plugin-bot-template": "0.3.16",
71
+ "@frockbot/plugin-computer": "0.3.16",
72
+ "@frockbot/plugin-flock": "0.3.16",
73
+ "@frockbot/plugin-image": "0.3.16",
74
+ "@frockbot/plugin-machine-messages": "0.3.16",
75
+ "@frockbot/plugin-mcp": "0.3.16",
76
+ "@frockbot/plugin-memory": "0.3.16",
77
+ "@frockbot/plugin-package-catalog": "0.3.16",
78
+ "@frockbot/plugin-routines": "0.3.16",
79
+ "@frockbot/plugin-skills": "0.3.16",
80
+ "@frockbot/plugin-subagents": "0.3.16",
81
+ "@frockbot/plugin-user-machine": "0.3.16",
82
+ "@frockbot/protocol": "0.3.16",
83
83
  "cordis": "4.0.0-rc.8",
84
84
  "vue": "3.5.41"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@cloudflare/workers-types": "latest",
88
- "@frockbot/plugin-prompt": "0.3.14",
89
- "@frockbot/plugin-tools": "0.3.14",
88
+ "@frockbot/plugin-prompt": "0.3.16",
89
+ "@frockbot/plugin-tools": "0.3.16",
90
90
  "@types/bun": "1.3.6",
91
91
  "@types/node": "26.2.0",
92
92
  "@vitejs/plugin-vue": "6.0.8",
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import { type SessionEvent } from "@frockbot/kernel-contracts";
3
3
  import { initializeBotSettingsV1 } from "@frockbot/configuration-core";
4
+ import { SessionEventLog } from "@frockbot/kernel-do";
4
5
  import {
5
6
  botTurnCommandFingerprintV1,
6
7
  type BotTurnCompletion,
@@ -81,11 +82,15 @@ class MemoryRunStorage implements RunTerminalStorage {
81
82
  return Promise.resolve(this.values.get(key) as T | undefined);
82
83
  }
83
84
 
84
- put(entries: Record<string, unknown>): Promise<void> {
85
- this.putBatches.push(structuredClone(entries));
86
- if (this.putFailure) return Promise.reject(this.putFailure);
87
- for (const [key, value] of Object.entries(entries)) {
85
+ put(key: string | Record<string, unknown>, value?: unknown): Promise<void> {
86
+ if (typeof key === "string") {
88
87
  this.values.set(key, structuredClone(value));
88
+ return Promise.resolve();
89
+ }
90
+ this.putBatches.push(structuredClone(key));
91
+ if (this.putFailure) return Promise.reject(this.putFailure);
92
+ for (const [entry, item] of Object.entries(key)) {
93
+ this.values.set(entry, structuredClone(item));
89
94
  }
90
95
  return Promise.resolve();
91
96
  }
@@ -93,6 +98,16 @@ class MemoryRunStorage implements RunTerminalStorage {
93
98
  delete(key: string): Promise<boolean> {
94
99
  return Promise.resolve(this.values.delete(key));
95
100
  }
101
+
102
+ list<T>(options: { prefix: string }): Promise<Map<string, T>> {
103
+ return Promise.resolve(
104
+ new Map(
105
+ [...this.values.entries()].filter(([key]) =>
106
+ key.startsWith(options.prefix),
107
+ ) as Array<[string, T]>,
108
+ ),
109
+ );
110
+ }
96
111
  }
97
112
 
98
113
  describe("Bot run terminal persistence", () => {
@@ -284,6 +299,8 @@ describe("Bot run terminal persistence", () => {
284
299
  failure: "provider-bound retrieval unavailable",
285
300
  });
286
301
  expect(storage.values.get(keys.activeRun)).toBe("run-1");
287
- expect(storage.values.get(keys.latestEvents)).toEqual([request]);
302
+ expect(await new SessionEventLog(storage).read("user:primary")).toEqual([
303
+ request,
304
+ ]);
288
305
  });
289
306
  });
@@ -6,6 +6,7 @@ import type {
6
6
  UserSettingsViewV1,
7
7
  } from "@frockbot/configuration-core";
8
8
  import type { PackageSettingDefinition } from "@frockbot/kernel-composition";
9
+ import { SessionEventLog } from "@frockbot/kernel-do";
9
10
  import { createShellBotBackendContribution } from "./backend.js";
10
11
  import { createIsolateCapabilityHost } from "./backend-isolate.js";
11
12
  import { notificationIdV1 } from "./notification-id.js";
@@ -549,11 +550,12 @@ describe("generic per-Turn model resolution", () => {
549
550
  });
550
551
 
551
552
  expect(result.text).toBe("Cordis runtime: hello");
552
- const durableEvents = storage.values.get("latest-events") as
553
- Array<{ seq: number }> | undefined;
554
- expect(durableEvents?.length).toBeGreaterThan(0);
555
- expect(durableEvents?.map((event) => event.seq)).toEqual(
556
- durableEvents?.map((_, index) => index),
553
+ const durableEvents = await new SessionEventLog(storage).read(
554
+ "user-1:primary",
555
+ );
556
+ expect(durableEvents.length).toBeGreaterThan(0);
557
+ expect(durableEvents.map((event) => event.seq)).toEqual(
558
+ durableEvents.map((_, index) => index),
557
559
  );
558
560
  const settings = await contribution.getSettings(identity);
559
561
  expect(settings).toMatchObject({ revision: 0, packageValues: {} });
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import { initializeBotSettingsV1 } from "@frockbot/configuration-core";
3
+ import { SessionEventLog, storedRunRecordV2 } from "@frockbot/kernel-do";
3
4
  import { createShellBotBackendContribution } from "./backend.js";
4
5
  import type { StoredRun } from "./backend-contracts.js";
5
6
 
@@ -154,6 +155,54 @@ describe("Bot debug snapshot", () => {
154
155
  expect(snapshot.activeRunId).toBeUndefined();
155
156
  });
156
157
 
158
+ test("reads bounded projections instead of hydrating full model prompts", async () => {
159
+ const storage = new MemoryStorage();
160
+ const event = {
161
+ type: "model/request",
162
+ turn: 1,
163
+ step: 1,
164
+ request: {
165
+ requestId: "request-large",
166
+ provider: "fake",
167
+ model: "large-context",
168
+ system: "s".repeat(80_000),
169
+ messages: [{ role: "user", content: "last question" }],
170
+ tools: [],
171
+ },
172
+ seq: 0,
173
+ timestamp: "2026-08-28T00:00:01.000Z",
174
+ } as StoredRun["events"][number];
175
+ const run = storedRun({ events: [event] });
176
+ await new SessionEventLog(storage).rewrite(run.sessionId, [event]);
177
+ await storage.put({
178
+ identity: IDENTITY,
179
+ [`run:${run.runId}`]: storedRunRecordV2(run),
180
+ [`run-index:${run.acceptedAt}:${run.runId}`]: run.runId,
181
+ });
182
+
183
+ const snapshot = await contributionOver(storage).debugSnapshot(IDENTITY, {
184
+ schemaVersion: 1,
185
+ runId: run.runId,
186
+ });
187
+
188
+ expect(snapshot.runs[0]?.eventCount).toBe(1);
189
+ expect(snapshot.runs[0]?.events?.[0]).toMatchObject({
190
+ type: "model/request",
191
+ cut: { marker: "content-cut", originalBytes: expect.any(Number) },
192
+ request: {
193
+ requestId: "request-large",
194
+ messageCount: 1,
195
+ toolCount: 0,
196
+ sha256: expect.stringMatching(/^[0-9a-f]{64}$/),
197
+ truncated: true,
198
+ },
199
+ });
200
+ expect(JSON.stringify(snapshot)).not.toContain("s".repeat(10_000));
201
+ expect(
202
+ new TextEncoder().encode(JSON.stringify(snapshot)).byteLength,
203
+ ).toBeLessThan(20_000);
204
+ });
205
+
157
206
  test("includes an active run older than the page it would otherwise fall off", async () => {
158
207
  const storage = new MemoryStorage();
159
208
  const stale = storedRun({
@@ -25,6 +25,7 @@ import type {
25
25
  MemoryRuntimeHostV1,
26
26
  } from "@frockbot/plugin-memory/agent";
27
27
  import { MemoryStore } from "@frockbot/plugin-memory/store";
28
+ import type { MemoryChunkIndexWriterV1 } from "@frockbot/plugin-memory/chunk-index";
28
29
 
29
30
  /** The Bot and User whose Memory a Turn may read and write. */
30
31
  export interface BotMemoryIdentity {
@@ -50,6 +51,8 @@ export interface BotMemoryEnv {
50
51
  MEMORY_PROJECTS?: MemoryProjectsV1;
51
52
  /** Display names per Bot id, for the `[via …]` tag on a shared fact. */
52
53
  MEMORY_BOT_NAMES?: Readonly<Record<string, string>>;
54
+ /** Bot-scoped vector-id ledger supplied by the Durable Object host. */
55
+ MEMORY_CHUNK_INDEX?: MemoryChunkIndexWriterV1;
53
56
  }
54
57
 
55
58
  /**
@@ -85,5 +88,8 @@ export function createBotMemoryHost(
85
88
  runId: turn.runId,
86
89
  },
87
90
  ...(bindings.MEMORY_PROJECTS ? { projects: bindings.MEMORY_PROJECTS } : {}),
91
+ ...(bindings.MEMORY_CHUNK_INDEX
92
+ ? { chunkIndex: bindings.MEMORY_CHUNK_INDEX }
93
+ : {}),
88
94
  };
89
95
  }
@@ -9,6 +9,7 @@ import {
9
9
  type UserSettingsViewV1,
10
10
  } from "@frockbot/configuration-core";
11
11
  import { compileFoundationApplication } from "@frockbot/application-foundation/runtime";
12
+ import { SessionEventLog } from "@frockbot/kernel-do";
12
13
  import { createShellBotBackendContribution } from "./backend.js";
13
14
  import {
14
15
  botTurnCommandFingerprintV1,
@@ -291,10 +292,23 @@ describe("Bot recovery", () => {
291
292
  },
292
293
  ]);
293
294
  expect(settledEffects).toHaveLength(2);
295
+ const eventLog = new SessionEventLog(storage);
294
296
  for (const runId of ["ollama-run-1", "ollama-run-2"]) {
295
- const run = await storage.get<StoredRun>(`run:${runId}`);
297
+ const run = await storage.get<
298
+ Omit<StoredRun, "events"> & {
299
+ eventRange: { startSeq: number; endSeq: number };
300
+ }
301
+ >(`run:${runId}`);
302
+ expect(run).not.toHaveProperty("events");
303
+ const events = run
304
+ ? await eventLog.readRange(
305
+ "user-1:primary",
306
+ run.eventRange.startSeq,
307
+ run.eventRange.endSeq,
308
+ )
309
+ : [];
296
310
  expect(
297
- run?.events.find((event) => event.type === "model/request"),
311
+ events.find((event) => event.type === "model/request"),
298
312
  ).toMatchObject({
299
313
  request: {
300
314
  provider: "ollama-cloud",
package/src/backend.ts CHANGED
@@ -46,12 +46,13 @@ import {
46
46
  ACTIVE_RUN_KEY,
47
47
  BotDurableAuthority,
48
48
  IDENTITY_KEY,
49
- LATEST_EVENTS_KEY,
50
49
  NOTIFICATION_PREFIX,
51
50
  RECOVERY_ALARM_DELAY_MS,
52
51
  RUN_ADMISSION_FENCE_PREFIX,
53
52
  RUN_INDEX_PREFIX,
54
53
  RUN_PREFIX,
54
+ SessionEventLog,
55
+ storedRunRecordV2,
55
56
  CONVERSATION_BUSY_MESSAGE_V1,
56
57
  isConversationBusyV1,
57
58
  type BotIdentity,
@@ -589,7 +590,8 @@ export interface BotStateEnv {
589
590
  * Bot authoring still compiles; `package_author` then refuses visibly.
590
591
  */
591
592
  PACKAGE_BUNDLER?: PackageBundlerBinding;
592
- MEMORY_INDEX: VectorizeIndex;
593
+ /** Optional in local and workerd hosts, which have no Vectorize simulator. */
594
+ MEMORY_INDEX?: VectorizeIndex;
593
595
  /** The native AI binding consumed through the image Package adapter. */
594
596
  AI?: NativeAiBindingV1;
595
597
  /** The Frock AI Gateway adapter constructed by the Cloudflare host. */
@@ -1155,8 +1157,10 @@ export class ShellBotBackendContribution {
1155
1157
  const announcements = [...stored.entries()]
1156
1158
  .sort(([left], [right]) => left.localeCompare(right))
1157
1159
  .map(([, value]) => decodeSessionEvent(value));
1158
- const session =
1159
- (await this.ctx.storage.get<SessionEvent[]>(LATEST_EVENTS_KEY)) ?? [];
1160
+ const sessionId = await this.authority.readConversationSessionId();
1161
+ const session = sessionId
1162
+ ? await this.authority.readSessionEvents(sessionId)
1163
+ : [];
1160
1164
  for (const event of session) {
1161
1165
  if (event.type === "conversation/compacted") announcements.push(event);
1162
1166
  }
@@ -1170,8 +1174,10 @@ export class ShellBotBackendContribution {
1170
1174
  * timestamp of the place it belongs rather than the moment it was written.
1171
1175
  */
1172
1176
  private async projectAnnouncementPage() {
1173
- const session =
1174
- (await this.ctx.storage.get<SessionEvent[]>(LATEST_EVENTS_KEY)) ?? [];
1177
+ const sessionId = await this.authority.readConversationSessionId();
1178
+ const session = sessionId
1179
+ ? await this.authority.readSessionEvents(sessionId)
1180
+ : [];
1175
1181
  return projectClientAnnouncementsV1(
1176
1182
  await this.listAnnouncements(),
1177
1183
  session,
@@ -5950,10 +5956,13 @@ export class ShellBotBackendContribution {
5950
5956
  let budget = BOT_DEBUG_EVENT_BYTES_V1;
5951
5957
  const runs: BotDebugRunV1[] = [];
5952
5958
  for (const candidate of candidates) {
5953
- const stored = await this.authority.readStoredRun(candidate.runId);
5954
- if (!stored) continue;
5959
+ const projected = await this.authority.readRunEventProjections(
5960
+ candidate.runId,
5961
+ );
5962
+ if (!projected) continue;
5963
+ const { run: stored } = projected;
5955
5964
  const bounded = includeEvents
5956
- ? boundDebugEventsV1(stored.events, budget)
5965
+ ? boundDebugEventsV1(projected.events, budget)
5957
5966
  : undefined;
5958
5967
  if (bounded) budget = Math.max(0, budget - bounded.spent);
5959
5968
  runs.push({
@@ -5966,7 +5975,7 @@ export class ShellBotBackendContribution {
5966
5975
  commandFingerprint: stored.commandFingerprint,
5967
5976
  compositionGenerationId: stored.compositionGenerationId,
5968
5977
  previousEventCount: stored.previousEventCount,
5969
- eventCount: stored.events.length,
5978
+ eventCount: projected.eventCount,
5970
5979
  ...(stored.responseText === undefined
5971
5980
  ? {}
5972
5981
  : { responseText: stored.responseText }),
@@ -6327,7 +6336,24 @@ export class ShellBotBackendContribution {
6327
6336
  transaction.get<BotIdentity>(IDENTITY_KEY),
6328
6337
  transaction.get<unknown>(`${RUN_PREFIX}${runId}`),
6329
6338
  ]);
6330
- const run = optionalStoredRun(candidate);
6339
+ const storedRun = optionalStoredRun(candidate);
6340
+ let run = storedRun;
6341
+ if (storedRun?.eventRange) {
6342
+ const events = await new SessionEventLog(transaction).readRange(
6343
+ storedRun.sessionId,
6344
+ storedRun.eventRange.startSeq,
6345
+ storedRun.eventRange.endSeq,
6346
+ );
6347
+ if (
6348
+ events.length !==
6349
+ storedRun.eventRange.endSeq - storedRun.eventRange.startSeq
6350
+ ) {
6351
+ throw new Error(
6352
+ `run "${storedRun.runId}" has an incomplete event range`,
6353
+ );
6354
+ }
6355
+ run = requireStoredRunV1({ ...storedRun, events });
6356
+ }
6331
6357
  if (
6332
6358
  activeRunId !== runId ||
6333
6359
  !run ||
@@ -6387,7 +6413,10 @@ export class ShellBotBackendContribution {
6387
6413
  { kind: effect.kind, effectId: effect.effectId, outcome },
6388
6414
  ],
6389
6415
  } satisfies StoredRun);
6390
- await transaction.put(`${RUN_PREFIX}${runId}`, structuredClone(next));
6416
+ await transaction.put(
6417
+ `${RUN_PREFIX}${runId}`,
6418
+ structuredClone(storedRunRecordV2(next)),
6419
+ );
6391
6420
  return outcome === "admitted";
6392
6421
  });
6393
6422
  }
@@ -11,17 +11,17 @@ import {
11
11
  } from "./voice-worklet.js";
12
12
 
13
13
  /** What the upstream is told to expect, and therefore what leaves here. */
14
- export const VOICE_CAPTURE_SAMPLE_RATE_V1 = 16_000;
14
+ export const VOICE_CAPTURE_SAMPLE_RATE_V1 = 24_000;
15
15
 
16
- /** Samples per frame at 16 kHz: 32 ms, small enough to feel live. */
17
- const FRAME_SAMPLES = 512;
16
+ /** Samples per frame at 24 kHz: 32 ms, small enough to feel live. */
17
+ const FRAME_SAMPLES = 768;
18
18
 
19
19
  export interface VoiceMicrophoneV1 {
20
20
  stop(): Promise<void>;
21
21
  }
22
22
 
23
23
  export interface VoiceMicrophoneOptionsV1 {
24
- /** One frame of PCM16, little-endian, mono, 16 kHz. */
24
+ /** One frame of PCM16, little-endian, mono, 24 kHz. */
25
25
  audio(pcm16: ArrayBuffer): void;
26
26
  /** Peak amplitude of the frame, 0…1, for the capture animation. */
27
27
  level(value: number): void;
@@ -20,9 +20,10 @@ export const BOT_DEBUG_RUN_LIMIT_V1 = 20;
20
20
  export const BOT_DEBUG_DEFAULT_RUN_LIMIT_V1 = 5;
21
21
  export const BOT_DEBUG_GENERATION_LIMIT_V1 = 5;
22
22
  /**
23
- * The event budget one snapshot spends. Session events carry whole prompts, so
24
- * a handful of runs can be megabytes; past this the *oldest* events of a run
25
- * are dropped, because a failure is described by the tail of its log.
23
+ * The event budget one snapshot spends. Large durable events are already
24
+ * bounded diagnostic projections, but a handful of runs can still accumulate
25
+ * many of them; past this the *oldest* events of a run are dropped, because a
26
+ * failure is described by the tail of its log.
26
27
  */
27
28
  export const BOT_DEBUG_EVENT_BYTES_V1 = 512_000;
28
29
 
@@ -3,6 +3,7 @@ import type { SessionEvent } from "@frockbot/kernel-contracts";
3
3
  import { MODEL_FIRST_BYTE_DEADLINE_REASON_V1 } from "@frockbot/kernel-contracts";
4
4
  import {
5
5
  knownFailureCopyV1,
6
+ MODEL_PROVIDER_FAILURE_COPY_V1,
6
7
  RUN_FAILURE_COPY_V1,
7
8
  RUN_FAILURE_FALLBACK_COPY_V1,
8
9
  runFailureCopyV1,
@@ -75,6 +76,14 @@ function failedRun(failure: string, events: SessionEvent[]): StoredRun {
75
76
  describe("runFailureCopyV1", () => {
76
77
  test("every mapped sentence is written for a person", () => {
77
78
  for (const copy of Object.values(RUN_FAILURE_COPY_V1)) assertPlainV1(copy);
79
+ expect(Object.keys(MODEL_PROVIDER_FAILURE_COPY_V1).sort()).toEqual([
80
+ "permanent",
81
+ "transient",
82
+ "unknown",
83
+ ]);
84
+ for (const copy of Object.values(MODEL_PROVIDER_FAILURE_COPY_V1)) {
85
+ assertPlainV1(copy);
86
+ }
78
87
  assertPlainV1(RUN_FAILURE_FALLBACK_COPY_V1);
79
88
  for (const reason of USER_FACING_FAILURE_REASONS_V1) assertPlainV1(reason);
80
89
  });
@@ -1,4 +1,8 @@
1
- import type { SessionEvent, TurnOutcome } from "@frockbot/kernel-contracts";
1
+ import type {
2
+ ModelProviderFailureClassV1,
3
+ SessionEvent,
4
+ TurnOutcome,
5
+ } from "@frockbot/kernel-contracts";
2
6
  import {
3
7
  MODEL_FIRST_BYTE_DEADLINE_REASON_V1,
4
8
  MODEL_IDLE_DEADLINE_REASON_V1,
@@ -59,6 +63,16 @@ export const RUN_FAILURE_COPY_V1: Record<TurnOutcome, string> = {
59
63
  "tool-error": "Something the Bot was using didn't work. Try again.",
60
64
  };
61
65
 
66
+ /** Every provider class reaches the same intentionally plain model-error copy. */
67
+ export const MODEL_PROVIDER_FAILURE_COPY_V1: Record<
68
+ ModelProviderFailureClassV1,
69
+ string
70
+ > = {
71
+ transient: RUN_FAILURE_COPY_V1["model-error"],
72
+ permanent: RUN_FAILURE_COPY_V1["model-error"],
73
+ unknown: RUN_FAILURE_COPY_V1["model-error"],
74
+ };
75
+
62
76
  /** What a Turn says when nothing more specific is known about how it ended. */
63
77
  export const RUN_FAILURE_FALLBACK_COPY_V1 =
64
78
  "This Bot couldn't finish its reply. Try again.";