@opencode-ai/ai 0.0.0-next-15840 → 0.0.0-next-15845

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.
@@ -165,8 +165,14 @@ export declare const OpenAIChatEvent: Schema.Struct<{
165
165
  }>>>;
166
166
  }>;
167
167
  export type OpenAIChatEvent = Schema.Schema.Type<typeof OpenAIChatEvent>;
168
+ interface PendingToolDelta {
169
+ readonly id?: string;
170
+ readonly name?: string;
171
+ readonly input: string;
172
+ }
168
173
  export interface ParserState {
169
174
  readonly tools: ToolStream.State<number>;
175
+ readonly pendingTools: Partial<Record<number, PendingToolDelta>>;
170
176
  readonly toolCallEvents: ReadonlyArray<LLMEvent>;
171
177
  readonly usage?: Usage;
172
178
  readonly finishReason?: FinishReason;
@@ -368,6 +368,7 @@ const step = (state, event) => Effect.gen(function* () {
368
368
  const delta = choice?.delta;
369
369
  const toolDeltas = delta?.tool_calls ?? [];
370
370
  let tools = state.tools;
371
+ let pendingTools = state.pendingTools;
371
372
  let lifecycle = state.lifecycle;
372
373
  const reasoning = reasoningDelta(delta);
373
374
  const reasoningField = state.reasoningField ?? reasoning?.field;
@@ -382,7 +383,20 @@ const step = (state, event) => Effect.gen(function* () {
382
383
  if (toolDeltas.length)
383
384
  lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0");
384
385
  for (const tool of toolDeltas) {
385
- const result = ToolStream.appendOrStart(ADAPTER, tools, tool.index, { id: tool.id ?? undefined, name: tool.function?.name ?? undefined, text: tool.function?.arguments ?? "" }, "OpenAI Chat tool call delta is missing id or name");
386
+ const current = tools[tool.index];
387
+ const pending = pendingTools[tool.index];
388
+ const id = current?.id ?? pending?.id ?? (tool.id || undefined);
389
+ const name = current?.name ?? pending?.name ?? (tool.function?.name || undefined);
390
+ const text = `${pending?.input ?? ""}${tool.function?.arguments ?? ""}`;
391
+ if (!current && (!id || !name)) {
392
+ pendingTools = { ...pendingTools, [tool.index]: { id: id || undefined, name: name || undefined, input: text } };
393
+ continue;
394
+ }
395
+ if (pending) {
396
+ pendingTools = { ...pendingTools };
397
+ delete pendingTools[tool.index];
398
+ }
399
+ const result = ToolStream.appendOrStart(ADAPTER, tools, tool.index, { id: id || undefined, name: name || undefined, text }, "OpenAI Chat tool call delta is missing id or name");
386
400
  if (ToolStream.isError(result))
387
401
  return yield* result;
388
402
  tools = result.tools;
@@ -390,6 +404,8 @@ const step = (state, event) => Effect.gen(function* () {
390
404
  lifecycle = Lifecycle.stepStart(lifecycle, events);
391
405
  events.push(...result.events);
392
406
  }
407
+ if (finishReason !== undefined && state.finishReason === undefined && Object.keys(pendingTools).length > 0)
408
+ return yield* ProviderShared.eventError(ADAPTER, "OpenAI Chat tool call delta is missing id or name");
393
409
  // Finalize accumulated tool inputs eagerly when finish_reason arrives so
394
410
  // valid calls and malformed local calls settle independently.
395
411
  const finished = finishReason !== undefined && state.finishReason === undefined && Object.keys(tools).length > 0
@@ -398,6 +414,7 @@ const step = (state, event) => Effect.gen(function* () {
398
414
  return [
399
415
  {
400
416
  tools: finished?.tools ?? tools,
417
+ pendingTools,
401
418
  toolCallEvents: finished?.events ?? state.toolCallEvents,
402
419
  usage,
403
420
  finishReason,
@@ -436,6 +453,7 @@ export const protocol = Protocol.make({
436
453
  event: Protocol.jsonEvent(OpenAIChatEvent),
437
454
  initial: () => ({
438
455
  tools: ToolStream.empty(),
456
+ pendingTools: {},
439
457
  toolCallEvents: [],
440
458
  lifecycle: Lifecycle.initial(),
441
459
  reasoningField: undefined,
@@ -69,8 +69,8 @@ export const start = (tools, key, tool) => withTool(tools, key, { ...tool, input
69
69
  */
70
70
  export const appendOrStart = (route, tools, key, delta, missingToolMessage) => {
71
71
  const current = tools[key];
72
- const id = delta.id ?? current?.id;
73
- const name = delta.name ?? current?.name;
72
+ const id = current?.id ?? delta.id;
73
+ const name = current?.name ?? delta.name;
74
74
  if (!id || !name)
75
75
  return eventError(route, missingToolMessage);
76
76
  const tool = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-15840",
3
+ "version": "0.0.0-next-15845",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@clack/prompts": "1.0.0-alpha.1",
28
28
  "@effect/platform-node": "4.0.0-beta.98",
29
- "@opencode-ai/http-recorder": "0.0.0-next-15840",
29
+ "@opencode-ai/http-recorder": "0.0.0-next-15845",
30
30
  "@tsconfig/bun": "1.0.9",
31
31
  "@types/bun": "1.3.13",
32
32
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@smithy/eventstream-codec": "4.2.14",
37
37
  "@smithy/util-utf8": "4.2.2",
38
- "@opencode-ai/schema": "0.0.0-next-15840",
38
+ "@opencode-ai/schema": "0.0.0-next-15845",
39
39
  "aws4fetch": "1.0.20",
40
40
  "effect": "4.0.0-beta.98",
41
41
  "google-auth-library": "10.5.0"