@opencode/ai 0.0.0-dev-19545 → 0.0.0-dev-19551

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.
@@ -10,6 +10,7 @@ import { BedrockAuth } from "./utils/bedrock-auth.js";
10
10
  import { BedrockCache } from "./utils/bedrock-cache.js";
11
11
  import { BedrockMedia } from "./utils/bedrock-media.js";
12
12
  import { Lifecycle } from "./utils/lifecycle.js";
13
+ import { MistralToolID } from "./utils/mistral-tool-id.js";
13
14
  import { ToolSchemaProjection } from "./utils/tool-schema.js";
14
15
  import { ToolStream } from "./utils/tool-stream.js";
15
16
  const ADAPTER = "bedrock-converse";
@@ -201,9 +202,9 @@ const removeEmptyToolInputKeys = (input) => {
201
202
  return input;
202
203
  return Object.fromEntries(Object.entries(input).flatMap(([key, value]) => (key === "" ? [] : [[key, removeEmptyToolInputKeys(value)]])));
203
204
  };
204
- const lowerToolCall = (part) => ({
205
+ const lowerToolCall = (part, normalizeID) => ({
205
206
  toolUse: {
206
- toolUseId: part.id,
207
+ toolUseId: normalizeID(part.id),
207
208
  // Models can emit names that Converse rejects when replayed in history.
208
209
  name: part.name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64) || "_",
209
210
  input: removeEmptyToolInputKeys(part.input),
@@ -230,10 +231,10 @@ const lowerToolResultContent = Effect.fn("BedrockConverse.lowerToolResultContent
230
231
  }
231
232
  return content;
232
233
  });
233
- const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (part, documentNames) {
234
+ const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (part, documentNames, normalizeID) {
234
235
  return {
235
236
  toolResult: {
236
- toolUseId: part.id,
237
+ toolUseId: normalizeID(part.id),
237
238
  content: yield* lowerToolResultContent(part, documentNames),
238
239
  status: part.result.type === "error" ? "error" : "success",
239
240
  },
@@ -242,6 +243,8 @@ const lowerToolResult = Effect.fn("BedrockConverse.lowerToolResult")(function* (
242
243
  const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (request, breakpoints) {
243
244
  const messages = [];
244
245
  const documentNames = new Set();
246
+ // Mistral can reject replay IDs even when they satisfy Converse's broader ID syntax.
247
+ const normalizeID = request.model.id.includes("mistral.") ? MistralToolID.normalizer(request) : (id) => id;
245
248
  const providerMetadataKey = request.model.route.providerMetadataKey ?? String(request.model.provider);
246
249
  for (const message of request.messages) {
247
250
  if (message.role === "system") {
@@ -306,7 +309,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (requ
306
309
  continue;
307
310
  }
308
311
  if (part.type === "tool-call") {
309
- content.push(lowerToolCall(part));
312
+ content.push(lowerToolCall(part, normalizeID));
310
313
  continue;
311
314
  }
312
315
  }
@@ -318,7 +321,7 @@ const lowerMessages = Effect.fn("BedrockConverse.lowerMessages")(function* (requ
318
321
  for (const part of message.content) {
319
322
  if (!ProviderShared.supportsContent(part, ["tool-result"]))
320
323
  return yield* ProviderShared.unsupportedContent("Bedrock Converse", "tool", ["tool-result"]);
321
- content.push(yield* lowerToolResult(part, documentNames));
324
+ content.push(yield* lowerToolResult(part, documentNames, normalizeID));
322
325
  const cachePoint = BedrockCache.block(breakpoints, part.cache);
323
326
  if (cachePoint)
324
327
  content.push(cachePoint);
@@ -9,10 +9,10 @@ import { AIError, InvalidProviderOutputError, LLMEvent, ProviderInternalError, U
9
9
  import { classifyProviderFailure } from "../provider-error.js";
10
10
  import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
11
11
  import { Lifecycle } from "./utils/lifecycle.js";
12
+ import { MistralToolID } from "./utils/mistral-tool-id.js";
12
13
  import { ToolStream } from "./utils/tool-stream.js";
13
14
  const ADAPTER = "mistral-chat";
14
15
  const DONE = "[DONE]";
15
- const TOOL_ID = /^[A-Za-z0-9]{9}$/;
16
16
  export const DEFAULT_BASE_URL = "https://api.mistral.ai/v1";
17
17
  export const PATH = "/chat/completions";
18
18
  const MistralTextContent = Schema.Struct({
@@ -140,34 +140,6 @@ const MistralEvent = Schema.StructWithRest(Schema.Struct({
140
140
  error: optionalNull(MistralError),
141
141
  }), [Schema.Record(Schema.String, Schema.Unknown)]);
142
142
  const MistralStreamEvent = Schema.Union([Schema.Literal(DONE), Protocol.jsonEvent(MistralEvent)]);
143
- const hashID = (value) => {
144
- const hash = (seed) => {
145
- let result = seed;
146
- for (const char of value)
147
- result = Math.imul(result ^ char.charCodeAt(0), 16777619);
148
- return (result >>> 0).toString(36);
149
- };
150
- return `${hash(2166136261).padStart(7, "0")}${hash(2246822519).padStart(7, "0")}`.slice(-9);
151
- };
152
- const toolIDNormalizer = (request) => {
153
- const ids = request.messages.flatMap((message) => message.content.flatMap((part) => (part.type === "tool-call" || part.type === "tool-result" ? [part.id] : [])));
154
- const used = new Set(ids.filter((id) => TOOL_ID.test(id)));
155
- const normalized = new Map();
156
- return (id) => {
157
- if (TOOL_ID.test(id))
158
- return id;
159
- const previous = normalized.get(id);
160
- if (previous)
161
- return previous;
162
- let attempt = 0;
163
- let candidate = hashID(id);
164
- while (used.has(candidate))
165
- candidate = hashID(`${id}:${++attempt}`);
166
- used.add(candidate);
167
- normalized.set(id, candidate);
168
- return candidate;
169
- };
170
- };
171
143
  const lowerMedia = Effect.fn("MistralChat.lowerMedia")(function* (part) {
172
144
  const media = ProviderShared.normalizeMedia(part);
173
145
  const url = typeof part.data === "string" && /^(?:https?:|data:)/.test(part.data) ? part.data : media.dataUrl;
@@ -269,7 +241,7 @@ const lowerToolResults = Effect.fn("MistralChat.lowerToolResults")(function* (me
269
241
  return output;
270
242
  });
271
243
  const lowerMessages = Effect.fn("MistralChat.lowerMessages")(function* (request) {
272
- const normalizeID = toolIDNormalizer(request);
244
+ const normalizeID = MistralToolID.normalizer(request);
273
245
  const messages = request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }];
274
246
  for (const message of request.messages) {
275
247
  if (message.role === "system") {
@@ -439,15 +411,15 @@ const appendContent = (state, events, content) => {
439
411
  }, state);
440
412
  };
441
413
  const normalizeStreamToolID = (state, source) => {
442
- if (TOOL_ID.test(source))
414
+ if (MistralToolID.valid.test(source))
443
415
  return { id: source, state: { ...state, usedToolIDs: new Set([...state.usedToolIDs, source]) } };
444
416
  const previous = state.toolIDs.get(source);
445
417
  if (previous)
446
418
  return { id: previous, state };
447
419
  let attempt = 0;
448
- let id = hashID(source);
420
+ let id = MistralToolID.hash(source);
449
421
  while (state.usedToolIDs.has(id))
450
- id = hashID(`${source}:${++attempt}`);
422
+ id = MistralToolID.hash(`${source}:${++attempt}`);
451
423
  return {
452
424
  id,
453
425
  state: {
@@ -0,0 +1,5 @@
1
+ import type { LLMRequest } from "../../schema/index.js";
2
+ export declare const valid: RegExp;
3
+ export declare const hash: (value: string) => string;
4
+ export declare const normalizer: (request: LLMRequest) => (id: string) => string;
5
+ export * as MistralToolID from "./mistral-tool-id.js";
@@ -0,0 +1,31 @@
1
+ export const valid = /^[A-Za-z0-9]{9}$/;
2
+ export const hash = (value) => {
3
+ const hash = (seed) => {
4
+ let result = seed;
5
+ for (const char of value)
6
+ result = Math.imul(result ^ char.charCodeAt(0), 16777619);
7
+ return (result >>> 0).toString(36);
8
+ };
9
+ return `${hash(2166136261).padStart(7, "0")}${hash(2246822519).padStart(7, "0")}`.slice(-9);
10
+ };
11
+ export const normalizer = (request) => {
12
+ const ids = request.messages.flatMap((message) => message.content.flatMap((part) => (part.type === "tool-call" || part.type === "tool-result" ? [part.id] : [])));
13
+ // Reserve valid IDs before projecting any history, including IDs encountered later.
14
+ const used = new Set(ids.filter((id) => valid.test(id)));
15
+ const normalized = new Map();
16
+ return (id) => {
17
+ if (valid.test(id))
18
+ return id;
19
+ const previous = normalized.get(id);
20
+ if (previous)
21
+ return previous;
22
+ let attempt = 0;
23
+ let candidate = hash(id);
24
+ while (used.has(candidate))
25
+ candidate = hash(`${id}:${++attempt}`);
26
+ used.add(candidate);
27
+ normalized.set(id, candidate);
28
+ return candidate;
29
+ };
30
+ };
31
+ export * as MistralToolID from "./mistral-tool-id.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-dev-19545",
3
+ "version": "0.0.0-dev-19551",
4
4
  "name": "@opencode/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-rc.112",
33
- "@opencode/http-recorder": "0.0.0-dev-19545",
33
+ "@opencode/http-recorder": "0.0.0-dev-19551",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.4.0",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -40,7 +40,7 @@
40
40
  "@aws-sdk/credential-providers": "3.1057.0",
41
41
  "@smithy/eventstream-codec": "4.2.14",
42
42
  "@smithy/util-utf8": "4.2.2",
43
- "@opencode/schema": "0.0.0-dev-19545",
43
+ "@opencode/schema": "0.0.0-dev-19551",
44
44
  "aws4fetch": "1.0.20",
45
45
  "effect": "4.0.0-rc.112",
46
46
  "google-auth-library": "10.5.0"