@opencode-ai/ai 0.0.0-dev-18049 → 0.0.0-dev-18051
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.
|
@@ -434,6 +434,7 @@ export declare const Event: Schema.StructWithRest<Schema.Struct<{
|
|
|
434
434
|
readonly headers: Schema.optional<Schema.Unknown>;
|
|
435
435
|
}>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>;
|
|
436
436
|
export type Event = Schema.Schema.Type<typeof Event>;
|
|
437
|
+
export type ItemKind = "message" | "reasoning" | "function-call" | "reference";
|
|
437
438
|
export interface Extension {
|
|
438
439
|
readonly id: string;
|
|
439
440
|
readonly name: string;
|
|
@@ -442,6 +443,7 @@ export interface Extension {
|
|
|
442
443
|
readonly media: ProviderShared.NormalizedMedia;
|
|
443
444
|
readonly request: LLMRequest;
|
|
444
445
|
}) => MediaInput | undefined;
|
|
446
|
+
readonly acceptsItemID?: (kind: ItemKind, id: string) => boolean;
|
|
445
447
|
}
|
|
446
448
|
export interface ParserState {
|
|
447
449
|
readonly id: string;
|
|
@@ -245,26 +245,34 @@ export const lowerToolChoice = (protocolName, toolChoice) => ProviderShared.matc
|
|
|
245
245
|
required: () => "required",
|
|
246
246
|
tool: (toolName) => ({ type: "function", name: toolName }),
|
|
247
247
|
});
|
|
248
|
+
// Servers validate item ids on replayed history, and a malformed or oversized
|
|
249
|
+
// id can fail an otherwise valid request. Only server-issued tokens are worth
|
|
250
|
+
// resending; anything else is treated as absent so the item is resent without
|
|
251
|
+
// an id (or skipped, for items that cannot be expressed without one).
|
|
252
|
+
const ITEM_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
248
253
|
const itemID = (providerMetadata, providerMetadataKey) => {
|
|
249
254
|
const metadata = providerMetadata?.[providerMetadataKey];
|
|
250
|
-
return ProviderShared.isRecord(metadata) &&
|
|
255
|
+
return ProviderShared.isRecord(metadata) &&
|
|
256
|
+
typeof metadata.itemId === "string" &&
|
|
257
|
+
ITEM_ID_PATTERN.test(metadata.itemId)
|
|
251
258
|
? metadata.itemId
|
|
252
259
|
: undefined;
|
|
253
260
|
};
|
|
254
|
-
const
|
|
261
|
+
const acceptsItemID = (extension, kind, id) => id !== undefined && (extension.acceptsItemID?.(kind, id) ?? true);
|
|
262
|
+
const lowerToolCall = (part, providerMetadataKey, extension) => {
|
|
255
263
|
const id = itemID(part.providerMetadata, providerMetadataKey);
|
|
256
264
|
return {
|
|
257
265
|
type: "function_call",
|
|
258
|
-
...(id ? { id } : {}),
|
|
266
|
+
...(acceptsItemID(extension, "function-call", id) ? { id } : {}),
|
|
259
267
|
call_id: part.id,
|
|
260
268
|
name: part.name,
|
|
261
269
|
arguments: ProviderShared.encodeJson(part.input),
|
|
262
270
|
};
|
|
263
271
|
};
|
|
264
|
-
const lowerReasoning = (part, providerMetadataKey) => {
|
|
272
|
+
const lowerReasoning = (part, providerMetadataKey, extension) => {
|
|
265
273
|
const metadata = part.providerMetadata?.[providerMetadataKey];
|
|
266
274
|
const id = itemID(part.providerMetadata, providerMetadataKey);
|
|
267
|
-
if (!ProviderShared.isRecord(metadata) || !id)
|
|
275
|
+
if (!ProviderShared.isRecord(metadata) || !acceptsItemID(extension, "reasoning", id))
|
|
268
276
|
return undefined;
|
|
269
277
|
const encryptedContent = typeof metadata.reasoningEncryptedContent === "string" || metadata.reasoningEncryptedContent === null
|
|
270
278
|
? metadata.reasoningEncryptedContent
|
|
@@ -359,7 +367,8 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
359
367
|
return;
|
|
360
368
|
const groups = content.reduce((groups, part) => {
|
|
361
369
|
const metadata = part.providerMetadata?.[providerMetadataKey];
|
|
362
|
-
const
|
|
370
|
+
const rawID = itemID(part.providerMetadata, providerMetadataKey);
|
|
371
|
+
const id = acceptsItemID(extension, "message", rawID) ? rawID : undefined;
|
|
363
372
|
const phase = ProviderShared.isRecord(metadata) ? messagePhase(metadata.phase) : undefined;
|
|
364
373
|
const group = groups.at(-1);
|
|
365
374
|
if (group && group.id === id && group.phase === phase)
|
|
@@ -384,7 +393,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
384
393
|
}
|
|
385
394
|
if (part.type === "reasoning") {
|
|
386
395
|
flushText();
|
|
387
|
-
const reasoning = lowerReasoning(part, providerMetadataKey);
|
|
396
|
+
const reasoning = lowerReasoning(part, providerMetadataKey, extension);
|
|
388
397
|
if (!reasoning)
|
|
389
398
|
continue;
|
|
390
399
|
if (store !== false) {
|
|
@@ -408,14 +417,15 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
408
417
|
flushText();
|
|
409
418
|
if (part.providerExecuted === true)
|
|
410
419
|
continue;
|
|
411
|
-
input.push(lowerToolCall(part, providerMetadataKey));
|
|
420
|
+
input.push(lowerToolCall(part, providerMetadataKey, extension));
|
|
412
421
|
continue;
|
|
413
422
|
}
|
|
414
423
|
if (part.type === "tool-result" && part.providerExecuted === true) {
|
|
415
424
|
flushText();
|
|
416
425
|
const id = itemID(part.providerMetadata, providerMetadataKey);
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
const reference = acceptsItemID(extension, "reference", id) ? id : undefined;
|
|
427
|
+
if (store !== false && reference && !hostedToolReferences.has(reference))
|
|
428
|
+
input.push({ type: "item_reference", id: reference });
|
|
419
429
|
if (store === false) {
|
|
420
430
|
// The server is not storing this exchange, so the tool outcome has to
|
|
421
431
|
// travel in the input. Non-content results degrade to their text form.
|
|
@@ -427,8 +437,8 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
427
437
|
content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, extension)),
|
|
428
438
|
});
|
|
429
439
|
}
|
|
430
|
-
if (
|
|
431
|
-
hostedToolReferences.add(
|
|
440
|
+
if (reference)
|
|
441
|
+
hostedToolReferences.add(reference);
|
|
432
442
|
continue;
|
|
433
443
|
}
|
|
434
444
|
return yield* ProviderShared.unsupportedContent(extension.name, "assistant", [
|
|
@@ -43,9 +43,27 @@ const OpenAIResponsesBody = Schema.Struct({
|
|
|
43
43
|
...OpenAIResponsesCoreFields,
|
|
44
44
|
stream: Schema.Literal(true),
|
|
45
45
|
});
|
|
46
|
+
// Replayed items are paired with stored server state by id, so a foreign or
|
|
47
|
+
// synthetic token can fail request validation even when `call_id` pairing is
|
|
48
|
+
// intact. Only resend ids in each item kind's own grammar; hosted tool
|
|
49
|
+
// references keep generic validation because every hosted tool mints its own
|
|
50
|
+
// prefix. The same allowlist approach codex uses before resending history
|
|
51
|
+
// (codex-rs core/src/client.rs, `prepare_response_items_for_request`).
|
|
52
|
+
const ITEM_ID_PREFIXES = {
|
|
53
|
+
message: ["msg_"],
|
|
54
|
+
reasoning: ["rs_"],
|
|
55
|
+
"function-call": ["fc_"],
|
|
56
|
+
// Every hosted tool mints its own id prefix, so references keep generic
|
|
57
|
+
// validation only.
|
|
58
|
+
reference: [],
|
|
59
|
+
};
|
|
46
60
|
const extension = {
|
|
47
61
|
id: ADAPTER,
|
|
48
62
|
name: NAME,
|
|
63
|
+
acceptsItemID: (kind, id) => {
|
|
64
|
+
const prefixes = ITEM_ID_PREFIXES[kind];
|
|
65
|
+
return prefixes.length === 0 || prefixes.some((prefix) => id.startsWith(prefix));
|
|
66
|
+
},
|
|
49
67
|
};
|
|
50
68
|
const nativeImageToolInput = (tool) => {
|
|
51
69
|
const native = tool.native?.openai;
|
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-
|
|
3
|
+
"version": "0.0.0-dev-18051",
|
|
4
4
|
"name": "@opencode-ai/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.111",
|
|
33
|
-
"@opencode-ai/http-recorder": "0.0.0-dev-
|
|
33
|
+
"@opencode-ai/http-recorder": "0.0.0-dev-18051",
|
|
34
34
|
"@tsconfig/bun": "1.0.9",
|
|
35
35
|
"@types/bun": "1.3.13",
|
|
36
36
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@smithy/eventstream-codec": "4.2.14",
|
|
41
41
|
"@smithy/util-utf8": "4.2.2",
|
|
42
|
-
"@opencode-ai/schema": "0.0.0-dev-
|
|
42
|
+
"@opencode-ai/schema": "0.0.0-dev-18051",
|
|
43
43
|
"aws4fetch": "1.0.20",
|
|
44
44
|
"effect": "4.0.0-rc.111",
|
|
45
45
|
"google-auth-library": "10.5.0"
|