@code-yeongyu/senpi-ai 2026.10.25-4 → 2026.10.26-2
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/dist/api/anthropic-messages.js +19 -2
- package/dist/api/azure-openai-responses.js +1 -1
- package/dist/api/bedrock-converse-stream.js +13 -1
- package/dist/api/cursor-agent.js +2 -2
- package/dist/api/devin-agent/stream-state.js +8 -0
- package/dist/api/devin-agent.js +1 -1
- package/dist/api/google-generative-ai.js +22 -10
- package/dist/api/google-vertex.js +22 -10
- package/dist/api/lazy.js +8 -2
- package/dist/api/mistral-conversations.js +1 -1
- package/dist/api/openai-codex-responses.js +1 -1
- package/dist/api/openai-completions.js +9 -4
- package/dist/api/openai-responses-shared.js +37 -3
- package/dist/api/openai-responses.js +1 -1
- package/dist/api/pi-messages.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/faux.js +5 -1
- package/dist/tool-call-middleware/protocols/kimi-xtml/thinking-recovery-stream.js +37 -2
- package/dist/tool-call-middleware/recovery-event-stream.d.ts +12 -1
- package/dist/tool-call-middleware/recovery-event-stream.js +30 -4
- package/dist/tool-call-middleware/recovery-native-projection.d.ts +10 -0
- package/dist/tool-call-middleware/recovery-native-projection.js +36 -7
- package/dist/tool-call-middleware/recovery-stream-failure.js +3 -2
- package/dist/tool-call-middleware/recovery-stream-terminal.d.ts +6 -0
- package/dist/tool-call-middleware/recovery-stream-terminal.js +35 -17
- package/dist/tool-call-middleware/recovery-stream-wrapper.js +26 -12
- package/dist/tool-call-middleware/stream-wrapper.js +3 -1
- package/dist/utils/assistant-capture.d.ts +101 -0
- package/dist/utils/assistant-capture.js +327 -0
- package/dist/utils/event-stream.d.ts +15 -2
- package/dist/utils/event-stream.js +57 -8
- package/dist/utils/tool-argument-coercion.d.ts +2 -0
- package/dist/utils/tool-argument-coercion.js +74 -0
- package/dist/utils/tool-argument-validator.d.ts +20 -0
- package/dist/utils/tool-argument-validator.js +9 -0
- package/dist/utils/tool-schema-compat.d.ts +5 -0
- package/dist/utils/tool-schema-compat.js +5 -0
- package/dist/utils/validation.d.ts +1 -0
- package/dist/utils/validation.js +14 -75
- package/package.json +2 -2
|
@@ -5,6 +5,7 @@ import { readProviderDiagnostic } from "../provider-diagnostic.js";
|
|
|
5
5
|
import { registerSessionResourceCleanup } from "../session-resources.js";
|
|
6
6
|
import { isVideoMimeType } from "../types.js";
|
|
7
7
|
import { combineAbortSignals } from "../utils/abort-signals.js";
|
|
8
|
+
import { pushAssistantProgress } from "../utils/assistant-capture.js";
|
|
8
9
|
import { claudeCodeVersionTooOldHint, getClaudeCodeVersion, isClaudeCodeVersionTooOldError, recoverClaudeCodeVersion, } from "../utils/claude-code-version.js";
|
|
9
10
|
import { splitDeferredTools } from "../utils/deferred-tools.js";
|
|
10
11
|
import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
|
|
@@ -920,7 +921,7 @@ async function* iterateAnthropicEvents(response, signal) {
|
|
|
920
921
|
}
|
|
921
922
|
}
|
|
922
923
|
export const stream = (model, context, options) => {
|
|
923
|
-
const stream = new AssistantMessageEventStream();
|
|
924
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
924
925
|
const normalizedContext = resolveTranscript(context, supportsMidConvoSystemMessages(model));
|
|
925
926
|
const currentTools = getCurrentTools(normalizedContext.messages);
|
|
926
927
|
(async () => {
|
|
@@ -1248,7 +1249,12 @@ export const stream = (model, context, options) => {
|
|
|
1248
1249
|
index: event.index,
|
|
1249
1250
|
};
|
|
1250
1251
|
output.content.push(block);
|
|
1251
|
-
|
|
1252
|
+
pushAssistantProgress(stream, {
|
|
1253
|
+
type: "message_block_state",
|
|
1254
|
+
contentIndex: output.content.length - 1,
|
|
1255
|
+
phase: "insert",
|
|
1256
|
+
block,
|
|
1257
|
+
});
|
|
1252
1258
|
}
|
|
1253
1259
|
}
|
|
1254
1260
|
else if (event.type === "content_block_delta") {
|
|
@@ -1304,6 +1310,11 @@ export const stream = (model, context, options) => {
|
|
|
1304
1310
|
if (block && block.type === "thinking") {
|
|
1305
1311
|
block.thinkingSignature = block.thinkingSignature || "";
|
|
1306
1312
|
block.thinkingSignature += event.delta.signature;
|
|
1313
|
+
pushAssistantProgress(stream, {
|
|
1314
|
+
type: "message_block_metadata",
|
|
1315
|
+
contentIndex: index,
|
|
1316
|
+
thinkingSignatureDelta: event.delta.signature,
|
|
1317
|
+
});
|
|
1307
1318
|
}
|
|
1308
1319
|
}
|
|
1309
1320
|
}
|
|
@@ -1346,6 +1357,12 @@ export const stream = (model, context, options) => {
|
|
|
1346
1357
|
if (partialJson !== undefined && isRecord(block.raw)) {
|
|
1347
1358
|
block.raw = { ...block.raw, input: parseStreamingJson(partialJson) };
|
|
1348
1359
|
}
|
|
1360
|
+
pushAssistantProgress(stream, {
|
|
1361
|
+
type: "message_block_state",
|
|
1362
|
+
contentIndex: index,
|
|
1363
|
+
phase: "end",
|
|
1364
|
+
block,
|
|
1365
|
+
});
|
|
1349
1366
|
}
|
|
1350
1367
|
}
|
|
1351
1368
|
}
|
|
@@ -44,7 +44,7 @@ function formatAzureOpenAIError(error) {
|
|
|
44
44
|
* Generate function for Azure OpenAI Responses API
|
|
45
45
|
*/
|
|
46
46
|
export const stream = (model, context, options) => {
|
|
47
|
-
const stream = new AssistantMessageEventStream();
|
|
47
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
48
48
|
const normalizedContext = resolveTranscript(context, model.compat?.supportsMidConvoSystemMessages);
|
|
49
49
|
// Start async processing
|
|
50
50
|
(async () => {
|
|
@@ -3,6 +3,7 @@ import { NodeHttpHandler } from "@smithy/node-http-handler";
|
|
|
3
3
|
import { HttpProxyAgent } from "http-proxy-agent";
|
|
4
4
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
5
5
|
import { calculateCost } from "../models.js";
|
|
6
|
+
import { pushAssistantProgress } from "../utils/assistant-capture.js";
|
|
6
7
|
import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
|
|
7
8
|
import { normalizeProviderError } from "../utils/error-body.js";
|
|
8
9
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
@@ -24,7 +25,7 @@ const EMPTY_TEXT_PLACEHOLDER = "<empty>";
|
|
|
24
25
|
/** Matches the placeholder the Anthropic API path uses for redacted thinking. */
|
|
25
26
|
const REDACTED_THINKING_PLACEHOLDER = "[Reasoning redacted]";
|
|
26
27
|
export const stream = (model, context, options = {}) => {
|
|
27
|
-
const stream = new AssistantMessageEventStream();
|
|
28
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
28
29
|
// Bedrock has no mid-conversation system messages; fold them into the leading prompt.
|
|
29
30
|
const normalizedContext = collapseSystemMessages(context);
|
|
30
31
|
(async () => {
|
|
@@ -500,12 +501,23 @@ function handleContentBlockDelta(event, blocks, output, stream) {
|
|
|
500
501
|
if (delta.reasoningContent.signature && !thinkingBlock.redacted) {
|
|
501
502
|
thinkingBlock.thinkingSignature =
|
|
502
503
|
(thinkingBlock.thinkingSignature || "") + delta.reasoningContent.signature;
|
|
504
|
+
pushAssistantProgress(stream, {
|
|
505
|
+
type: "message_block_metadata",
|
|
506
|
+
contentIndex: thinkingIndex,
|
|
507
|
+
thinkingSignatureDelta: delta.reasoningContent.signature,
|
|
508
|
+
});
|
|
503
509
|
}
|
|
504
510
|
if (delta.reasoningContent.redactedContent?.length) {
|
|
505
511
|
// Encrypted reasoning from non-Anthropic models on Bedrock (e.g. OpenAI GPT-5.6).
|
|
506
512
|
// The payload is opaque, so keep it verbatim in `thinkingSignature` the way the
|
|
507
513
|
// Anthropic path stores redacted thinking, and replay it on the next turn.
|
|
508
514
|
if (!thinkingBlock.redacted) {
|
|
515
|
+
pushAssistantProgress(stream, {
|
|
516
|
+
type: "message_block_metadata",
|
|
517
|
+
contentIndex: thinkingIndex,
|
|
518
|
+
redacted: true,
|
|
519
|
+
...(thinkingBlock.thinkingSignature ? { thinkingSignature: "" } : {}),
|
|
520
|
+
});
|
|
509
521
|
thinkingBlock.redacted = true;
|
|
510
522
|
thinkingBlock.thinkingSignature = "";
|
|
511
523
|
thinkingBlock.thinking += REDACTED_THINKING_PLACEHOLDER;
|
package/dist/api/cursor-agent.js
CHANGED
|
@@ -475,7 +475,7 @@ export function mapH2TransportError(error, baseUrl) {
|
|
|
475
475
|
return error;
|
|
476
476
|
}
|
|
477
477
|
export const stream = (model, context, options) => {
|
|
478
|
-
const stream = new AssistantMessageEventStream();
|
|
478
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
479
479
|
const requestView = toCursorRequestView(context);
|
|
480
480
|
(async () => {
|
|
481
481
|
const output = {
|
|
@@ -3061,7 +3061,7 @@ export function processInteractionUpdate(update, output, stream, state, usageSta
|
|
|
3061
3061
|
target[kStreamingLastParseLen] = nextBuffer.length;
|
|
3062
3062
|
}
|
|
3063
3063
|
const idx = output.content.indexOf(target);
|
|
3064
|
-
stream.push({ type: "toolcall_delta", contentIndex: idx, delta: chunk, partial: output });
|
|
3064
|
+
stream.push({ type: "toolcall_delta", contentIndex: idx, delta: chunk, partial: output }, { parsedThrough: target[kStreamingLastParseLen] ?? 0 });
|
|
3065
3065
|
}
|
|
3066
3066
|
}
|
|
3067
3067
|
else if (updateCase === "toolCallCompleted") {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* or closes them. This module owns the open-block state so the adapter emits
|
|
7
7
|
* senpi's start/delta/end triples in the right order.
|
|
8
8
|
*/
|
|
9
|
+
import { pushAssistantProgress, pushAssistantSignature } from "../../utils/assistant-capture.js";
|
|
9
10
|
import { parseStreamingJson } from "../../utils/json-parse.js";
|
|
10
11
|
import { StopReason } from "./gen/cascade_pb.js";
|
|
11
12
|
export function createDevinStreamState() {
|
|
@@ -41,6 +42,7 @@ function appendThinking(message, output, events, state) {
|
|
|
41
42
|
if (block?.type !== "thinking")
|
|
42
43
|
return;
|
|
43
44
|
block.thinking += message.deltaThinking;
|
|
45
|
+
pushAssistantSignature(events, state.thinkingIndex, block, message.deltaSignature);
|
|
44
46
|
if (message.deltaSignature)
|
|
45
47
|
block.thinkingSignature = message.deltaSignature;
|
|
46
48
|
events.push({
|
|
@@ -84,6 +86,12 @@ function appendToolCall(call, output, events, state) {
|
|
|
84
86
|
const block = output.content[entry.contentIndex];
|
|
85
87
|
if (block?.type !== "toolCall")
|
|
86
88
|
return;
|
|
89
|
+
if (call.name && call.name !== block.name)
|
|
90
|
+
pushAssistantProgress(events, {
|
|
91
|
+
type: "message_block_metadata",
|
|
92
|
+
contentIndex: entry.contentIndex,
|
|
93
|
+
name: call.name,
|
|
94
|
+
});
|
|
87
95
|
if (call.name)
|
|
88
96
|
block.name = call.name;
|
|
89
97
|
if (!call.argumentsJson)
|
package/dist/api/devin-agent.js
CHANGED
|
@@ -20,7 +20,7 @@ import { AssignModelRequestSchema, AssignModelResponseSchema, GetChatMessageRequ
|
|
|
20
20
|
import { applyDevinResponse, createDevinStreamState } from "./devin-agent/stream-state.js";
|
|
21
21
|
import { buildDevinChatRequest, buildDevinRouterPrompt, DEVIN_ASSIGN_MODEL_PATH, DEVIN_CHAT_HEADERS, DEVIN_CHAT_MESSAGE_PATH, DEVIN_DEFAULT_BASE_URL, DEVIN_USER_JWT_PATH, decodeDevinFrames, devinCliMetadata, encodeDevinRequestFrame, postDevinUnary, readDevinTrailerError, } from "./devin-agent/wire.js";
|
|
22
22
|
export const stream = (model, context, options) => {
|
|
23
|
-
const events = new AssistantMessageEventStream();
|
|
23
|
+
const events = new AssistantMessageEventStream({ captureBlocks: true });
|
|
24
24
|
void run(model, context, events, options);
|
|
25
25
|
return events;
|
|
26
26
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GoogleGenAI, } from "@google/genai";
|
|
2
2
|
import { calculateCost, clampThinkingLevel } from "../models.js";
|
|
3
|
+
import { pushAssistantCompleteBlock, pushAssistantSignature } from "../utils/assistant-capture.js";
|
|
3
4
|
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
4
5
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
6
|
import { providerHeadersToRecord } from "../utils/headers.js";
|
|
@@ -13,7 +14,7 @@ import { applyExtraBody, buildBaseOptions, GOOGLE_RESERVED_BODY_KEYS } from "./s
|
|
|
13
14
|
// Counter for generating unique tool call IDs
|
|
14
15
|
let toolCallCounter = 0;
|
|
15
16
|
export const stream = (model, context, options) => {
|
|
16
|
-
const stream = new AssistantMessageEventStream();
|
|
17
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
17
18
|
const normalizedContext = collapseSystemMessages(context);
|
|
18
19
|
(async () => {
|
|
19
20
|
const output = {
|
|
@@ -50,10 +51,13 @@ export const stream = (model, context, options) => {
|
|
|
50
51
|
const googleStream = await retryGoogleRequest(() => client.models.generateContentStream(params), options);
|
|
51
52
|
stream.push({ type: "start", partial: output });
|
|
52
53
|
let currentBlock = null;
|
|
54
|
+
let currentIndex = 0;
|
|
53
55
|
let groundingMetadataEmitted = false;
|
|
54
56
|
let urlContextMetadataEmitted = false;
|
|
55
57
|
const blocks = output.content;
|
|
56
58
|
const blockIndex = () => blocks.length - 1;
|
|
59
|
+
// blockIndex() drifts past the open block once a native part is appended; capture keeps the block's own index.
|
|
60
|
+
const pushOpen = (event) => stream.push(event, { contentIndex: currentIndex });
|
|
57
61
|
for await (const chunk of googleStream) {
|
|
58
62
|
await options?.onProviderStreamEvent?.(chunk, model);
|
|
59
63
|
// @google/genai documents GenerateContentResponse.responseId as an output-only field
|
|
@@ -71,7 +75,7 @@ export const stream = (model, context, options) => {
|
|
|
71
75
|
(!isThinking && currentBlock.type !== "text")) {
|
|
72
76
|
if (currentBlock) {
|
|
73
77
|
if (currentBlock.type === "text") {
|
|
74
|
-
|
|
78
|
+
pushOpen({
|
|
75
79
|
type: "text_end",
|
|
76
80
|
contentIndex: blocks.length - 1,
|
|
77
81
|
content: currentBlock.text,
|
|
@@ -79,7 +83,7 @@ export const stream = (model, context, options) => {
|
|
|
79
83
|
});
|
|
80
84
|
}
|
|
81
85
|
else {
|
|
82
|
-
|
|
86
|
+
pushOpen({
|
|
83
87
|
type: "thinking_end",
|
|
84
88
|
contentIndex: blockIndex(),
|
|
85
89
|
content: currentBlock.thinking,
|
|
@@ -90,18 +94,21 @@ export const stream = (model, context, options) => {
|
|
|
90
94
|
if (isThinking) {
|
|
91
95
|
currentBlock = { type: "thinking", thinking: "", thinkingSignature: undefined };
|
|
92
96
|
output.content.push(currentBlock);
|
|
97
|
+
currentIndex = blockIndex();
|
|
93
98
|
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
94
99
|
}
|
|
95
100
|
else {
|
|
96
101
|
currentBlock = { type: "text", text: "" };
|
|
97
102
|
output.content.push(currentBlock);
|
|
103
|
+
currentIndex = blockIndex();
|
|
98
104
|
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
if (currentBlock.type === "thinking") {
|
|
102
108
|
currentBlock.thinking += part.text;
|
|
109
|
+
pushAssistantSignature(stream, currentIndex, currentBlock, part.thoughtSignature);
|
|
103
110
|
currentBlock.thinkingSignature = retainThoughtSignature(currentBlock.thinkingSignature, part.thoughtSignature);
|
|
104
|
-
|
|
111
|
+
pushOpen({
|
|
105
112
|
type: "thinking_delta",
|
|
106
113
|
contentIndex: blockIndex(),
|
|
107
114
|
delta: part.text,
|
|
@@ -110,8 +117,9 @@ export const stream = (model, context, options) => {
|
|
|
110
117
|
}
|
|
111
118
|
else {
|
|
112
119
|
currentBlock.text += part.text;
|
|
120
|
+
pushAssistantSignature(stream, currentIndex, currentBlock, part.thoughtSignature);
|
|
113
121
|
currentBlock.textSignature = retainThoughtSignature(currentBlock.textSignature, part.thoughtSignature);
|
|
114
|
-
|
|
122
|
+
pushOpen({
|
|
115
123
|
type: "text_delta",
|
|
116
124
|
contentIndex: blockIndex(),
|
|
117
125
|
delta: part.text,
|
|
@@ -123,7 +131,7 @@ export const stream = (model, context, options) => {
|
|
|
123
131
|
handledAsKnownPart = true;
|
|
124
132
|
if (currentBlock) {
|
|
125
133
|
if (currentBlock.type === "text") {
|
|
126
|
-
|
|
134
|
+
pushOpen({
|
|
127
135
|
type: "text_end",
|
|
128
136
|
contentIndex: blockIndex(),
|
|
129
137
|
content: currentBlock.text,
|
|
@@ -131,7 +139,7 @@ export const stream = (model, context, options) => {
|
|
|
131
139
|
});
|
|
132
140
|
}
|
|
133
141
|
else {
|
|
134
|
-
|
|
142
|
+
pushOpen({
|
|
135
143
|
type: "thinking_end",
|
|
136
144
|
contentIndex: blockIndex(),
|
|
137
145
|
content: currentBlock.thinking,
|
|
@@ -155,16 +163,18 @@ export const stream = (model, context, options) => {
|
|
|
155
163
|
};
|
|
156
164
|
output.content.push(toolCall);
|
|
157
165
|
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
166
|
+
// The adapter never parses its own spelling: the complete parsed seed stays authoritative.
|
|
158
167
|
stream.push({
|
|
159
168
|
type: "toolcall_delta",
|
|
160
169
|
contentIndex: blockIndex(),
|
|
161
170
|
delta: JSON.stringify(toolCall.arguments),
|
|
162
171
|
partial: output,
|
|
163
|
-
});
|
|
172
|
+
}, { parsedThrough: 0 });
|
|
164
173
|
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
|
165
174
|
}
|
|
166
175
|
if (!handledAsKnownPart) {
|
|
167
176
|
output.content.push(toProviderNativeContent(part));
|
|
177
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
168
178
|
}
|
|
169
179
|
}
|
|
170
180
|
}
|
|
@@ -174,6 +184,7 @@ export const stream = (model, context, options) => {
|
|
|
174
184
|
subtype: "groundingMetadata",
|
|
175
185
|
raw: candidate.groundingMetadata,
|
|
176
186
|
});
|
|
187
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
177
188
|
groundingMetadataEmitted = true;
|
|
178
189
|
}
|
|
179
190
|
if (candidate?.urlContextMetadata && !urlContextMetadataEmitted) {
|
|
@@ -182,6 +193,7 @@ export const stream = (model, context, options) => {
|
|
|
182
193
|
subtype: "urlContextMetadata",
|
|
183
194
|
raw: candidate.urlContextMetadata,
|
|
184
195
|
});
|
|
196
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
185
197
|
urlContextMetadataEmitted = true;
|
|
186
198
|
}
|
|
187
199
|
if (candidate?.finishReason) {
|
|
@@ -213,7 +225,7 @@ export const stream = (model, context, options) => {
|
|
|
213
225
|
}
|
|
214
226
|
if (currentBlock) {
|
|
215
227
|
if (currentBlock.type === "text") {
|
|
216
|
-
|
|
228
|
+
pushOpen({
|
|
217
229
|
type: "text_end",
|
|
218
230
|
contentIndex: blockIndex(),
|
|
219
231
|
content: currentBlock.text,
|
|
@@ -221,7 +233,7 @@ export const stream = (model, context, options) => {
|
|
|
221
233
|
});
|
|
222
234
|
}
|
|
223
235
|
else {
|
|
224
|
-
|
|
236
|
+
pushOpen({
|
|
225
237
|
type: "thinking_end",
|
|
226
238
|
contentIndex: blockIndex(),
|
|
227
239
|
content: currentBlock.thinking,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GoogleGenAI, ResourceScope, } from "@google/genai";
|
|
2
2
|
import { calculateCost, clampThinkingLevel } from "../models.js";
|
|
3
|
+
import { pushAssistantCompleteBlock, pushAssistantSignature } from "../utils/assistant-capture.js";
|
|
3
4
|
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
4
5
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
5
6
|
import { providerHeadersToRecord } from "../utils/headers.js";
|
|
@@ -17,7 +18,7 @@ const GCP_VERTEX_CREDENTIALS_MARKER = "gcp-vertex-credentials";
|
|
|
17
18
|
// Counter for generating unique tool call IDs
|
|
18
19
|
let toolCallCounter = 0;
|
|
19
20
|
export const stream = (model, context, options) => {
|
|
20
|
-
const stream = new AssistantMessageEventStream();
|
|
21
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
21
22
|
const normalizedContext = collapseSystemMessages(context);
|
|
22
23
|
(async () => {
|
|
23
24
|
const output = {
|
|
@@ -55,10 +56,13 @@ export const stream = (model, context, options) => {
|
|
|
55
56
|
const googleStream = await retryGoogleRequest(() => client.models.generateContentStream(params), options);
|
|
56
57
|
stream.push({ type: "start", partial: output });
|
|
57
58
|
let currentBlock = null;
|
|
59
|
+
let currentIndex = 0;
|
|
58
60
|
let groundingMetadataEmitted = false;
|
|
59
61
|
let urlContextMetadataEmitted = false;
|
|
60
62
|
const blocks = output.content;
|
|
61
63
|
const blockIndex = () => blocks.length - 1;
|
|
64
|
+
// blockIndex() drifts past the open block once a native part is appended; capture keeps the block's own index.
|
|
65
|
+
const pushOpen = (event) => stream.push(event, { contentIndex: currentIndex });
|
|
62
66
|
for await (const chunk of googleStream) {
|
|
63
67
|
await options?.onProviderStreamEvent?.(chunk, model);
|
|
64
68
|
// Vertex uses the same @google/genai GenerateContentResponse type as Gemini.
|
|
@@ -76,7 +80,7 @@ export const stream = (model, context, options) => {
|
|
|
76
80
|
(!isThinking && currentBlock.type !== "text")) {
|
|
77
81
|
if (currentBlock) {
|
|
78
82
|
if (currentBlock.type === "text") {
|
|
79
|
-
|
|
83
|
+
pushOpen({
|
|
80
84
|
type: "text_end",
|
|
81
85
|
contentIndex: blocks.length - 1,
|
|
82
86
|
content: currentBlock.text,
|
|
@@ -84,7 +88,7 @@ export const stream = (model, context, options) => {
|
|
|
84
88
|
});
|
|
85
89
|
}
|
|
86
90
|
else {
|
|
87
|
-
|
|
91
|
+
pushOpen({
|
|
88
92
|
type: "thinking_end",
|
|
89
93
|
contentIndex: blockIndex(),
|
|
90
94
|
content: currentBlock.thinking,
|
|
@@ -95,18 +99,21 @@ export const stream = (model, context, options) => {
|
|
|
95
99
|
if (isThinking) {
|
|
96
100
|
currentBlock = { type: "thinking", thinking: "", thinkingSignature: undefined };
|
|
97
101
|
output.content.push(currentBlock);
|
|
102
|
+
currentIndex = blockIndex();
|
|
98
103
|
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
99
104
|
}
|
|
100
105
|
else {
|
|
101
106
|
currentBlock = { type: "text", text: "" };
|
|
102
107
|
output.content.push(currentBlock);
|
|
108
|
+
currentIndex = blockIndex();
|
|
103
109
|
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
if (currentBlock.type === "thinking") {
|
|
107
113
|
currentBlock.thinking += part.text;
|
|
114
|
+
pushAssistantSignature(stream, currentIndex, currentBlock, part.thoughtSignature);
|
|
108
115
|
currentBlock.thinkingSignature = retainThoughtSignature(currentBlock.thinkingSignature, part.thoughtSignature);
|
|
109
|
-
|
|
116
|
+
pushOpen({
|
|
110
117
|
type: "thinking_delta",
|
|
111
118
|
contentIndex: blockIndex(),
|
|
112
119
|
delta: part.text,
|
|
@@ -115,8 +122,9 @@ export const stream = (model, context, options) => {
|
|
|
115
122
|
}
|
|
116
123
|
else {
|
|
117
124
|
currentBlock.text += part.text;
|
|
125
|
+
pushAssistantSignature(stream, currentIndex, currentBlock, part.thoughtSignature);
|
|
118
126
|
currentBlock.textSignature = retainThoughtSignature(currentBlock.textSignature, part.thoughtSignature);
|
|
119
|
-
|
|
127
|
+
pushOpen({
|
|
120
128
|
type: "text_delta",
|
|
121
129
|
contentIndex: blockIndex(),
|
|
122
130
|
delta: part.text,
|
|
@@ -128,7 +136,7 @@ export const stream = (model, context, options) => {
|
|
|
128
136
|
handledAsKnownPart = true;
|
|
129
137
|
if (currentBlock) {
|
|
130
138
|
if (currentBlock.type === "text") {
|
|
131
|
-
|
|
139
|
+
pushOpen({
|
|
132
140
|
type: "text_end",
|
|
133
141
|
contentIndex: blockIndex(),
|
|
134
142
|
content: currentBlock.text,
|
|
@@ -136,7 +144,7 @@ export const stream = (model, context, options) => {
|
|
|
136
144
|
});
|
|
137
145
|
}
|
|
138
146
|
else {
|
|
139
|
-
|
|
147
|
+
pushOpen({
|
|
140
148
|
type: "thinking_end",
|
|
141
149
|
contentIndex: blockIndex(),
|
|
142
150
|
content: currentBlock.thinking,
|
|
@@ -159,16 +167,18 @@ export const stream = (model, context, options) => {
|
|
|
159
167
|
};
|
|
160
168
|
output.content.push(toolCall);
|
|
161
169
|
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
170
|
+
// The adapter never parses its own spelling: the complete parsed seed stays authoritative.
|
|
162
171
|
stream.push({
|
|
163
172
|
type: "toolcall_delta",
|
|
164
173
|
contentIndex: blockIndex(),
|
|
165
174
|
delta: JSON.stringify(toolCall.arguments),
|
|
166
175
|
partial: output,
|
|
167
|
-
});
|
|
176
|
+
}, { parsedThrough: 0 });
|
|
168
177
|
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
|
169
178
|
}
|
|
170
179
|
if (!handledAsKnownPart) {
|
|
171
180
|
output.content.push(toProviderNativeContent(part));
|
|
181
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
}
|
|
@@ -178,6 +188,7 @@ export const stream = (model, context, options) => {
|
|
|
178
188
|
subtype: "groundingMetadata",
|
|
179
189
|
raw: candidate.groundingMetadata,
|
|
180
190
|
});
|
|
191
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
181
192
|
groundingMetadataEmitted = true;
|
|
182
193
|
}
|
|
183
194
|
if (candidate?.urlContextMetadata && !urlContextMetadataEmitted) {
|
|
@@ -186,6 +197,7 @@ export const stream = (model, context, options) => {
|
|
|
186
197
|
subtype: "urlContextMetadata",
|
|
187
198
|
raw: candidate.urlContextMetadata,
|
|
188
199
|
});
|
|
200
|
+
pushAssistantCompleteBlock(stream, blockIndex(), blocks[blockIndex()]);
|
|
189
201
|
urlContextMetadataEmitted = true;
|
|
190
202
|
}
|
|
191
203
|
if (candidate?.finishReason) {
|
|
@@ -217,7 +229,7 @@ export const stream = (model, context, options) => {
|
|
|
217
229
|
}
|
|
218
230
|
if (currentBlock) {
|
|
219
231
|
if (currentBlock.type === "text") {
|
|
220
|
-
|
|
232
|
+
pushOpen({
|
|
221
233
|
type: "text_end",
|
|
222
234
|
contentIndex: blockIndex(),
|
|
223
235
|
content: currentBlock.text,
|
|
@@ -225,7 +237,7 @@ export const stream = (model, context, options) => {
|
|
|
225
237
|
});
|
|
226
238
|
}
|
|
227
239
|
else {
|
|
228
|
-
|
|
240
|
+
pushOpen({
|
|
229
241
|
type: "thinking_end",
|
|
230
242
|
contentIndex: blockIndex(),
|
|
231
243
|
content: currentBlock.thinking,
|
package/dist/api/lazy.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assistantEvents, pushAssistantProgress, setAssistantReader } from "../utils/assistant-capture.js";
|
|
1
2
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
2
3
|
import { isOAuthRefreshUnavailableError } from "../utils/oauth-refresh-error.js";
|
|
3
4
|
import { describeReplacedInstall } from "../utils/provider-failure-description.js";
|
|
@@ -39,9 +40,13 @@ function hasResult(source) {
|
|
|
39
40
|
class LazyAssistantMessageEventStream extends AssistantMessageEventStream {
|
|
40
41
|
setCancellationHandler(handler) {
|
|
41
42
|
this.cancellationHandler = handler;
|
|
43
|
+
// The forwarder carries records, so this wrapper is carrier-aware and keeps its cancellation for them.
|
|
44
|
+
setAssistantReader(this, (project) => this.withCancellation(this.mapIterator(project)));
|
|
42
45
|
}
|
|
43
46
|
[Symbol.asyncIterator]() {
|
|
44
|
-
|
|
47
|
+
return this.withCancellation(super[Symbol.asyncIterator]());
|
|
48
|
+
}
|
|
49
|
+
withCancellation(iterator) {
|
|
45
50
|
return {
|
|
46
51
|
next: () => iterator.next(),
|
|
47
52
|
return: async () => {
|
|
@@ -68,7 +73,8 @@ async function forwardStream(target, source, iterator) {
|
|
|
68
73
|
export function lazyStream(model, setup) {
|
|
69
74
|
const outer = new LazyAssistantMessageEventStream();
|
|
70
75
|
const inner = setup();
|
|
71
|
-
|
|
76
|
+
// Progress crosses as it is read, so the forwarder loop runs exactly the turns it runs for events alone.
|
|
77
|
+
const iterator = inner.then((source) => assistantEvents(source, (progress) => pushAssistantProgress(outer, progress))[Symbol.asyncIterator]());
|
|
72
78
|
let cancellation;
|
|
73
79
|
outer.setCancellationHandler(() => {
|
|
74
80
|
cancellation ??= iterator.then(async (sourceIterator) => {
|
|
@@ -16,7 +16,7 @@ const MAX_MISTRAL_ERROR_BODY_CHARS = 4000;
|
|
|
16
16
|
* Stream responses from the native Mistral Chat Completions endpoint.
|
|
17
17
|
*/
|
|
18
18
|
export const stream = (model, context, options) => {
|
|
19
|
-
const stream = new AssistantMessageEventStream();
|
|
19
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
20
20
|
const normalizedContext = resolveTranscript(context, model.compat?.supportsMidConvoSystemMessages);
|
|
21
21
|
(async () => {
|
|
22
22
|
const output = createOutput(model);
|
|
@@ -142,7 +142,7 @@ function copyBytesToArrayBuffer(bytes) {
|
|
|
142
142
|
// Main Stream Function
|
|
143
143
|
// ============================================================================
|
|
144
144
|
export const stream = (model, context, options) => {
|
|
145
|
-
const stream = new AssistantMessageEventStream();
|
|
145
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
146
146
|
const normalizedContext = resolveTranscript(context, model.compat?.supportsMidConvoSystemMessages);
|
|
147
147
|
(async () => {
|
|
148
148
|
const output = {
|
|
@@ -318,7 +318,7 @@ function resolveCacheRetention(cacheRetention, env) {
|
|
|
318
318
|
return "short";
|
|
319
319
|
}
|
|
320
320
|
export const stream = (model, context, options) => {
|
|
321
|
-
const stream = new AssistantMessageEventStream();
|
|
321
|
+
const stream = new AssistantMessageEventStream({ captureBlocks: true });
|
|
322
322
|
const normalizedContext = resolveTranscript(context, getCompat(model).supportsMidConvoSystemMessages);
|
|
323
323
|
(async () => {
|
|
324
324
|
const output = {
|
|
@@ -589,19 +589,24 @@ export const stream = (model, context, options) => {
|
|
|
589
589
|
for (const block of blocks) {
|
|
590
590
|
const contentIndex = getContentIndex(block);
|
|
591
591
|
if (block.type === "text") {
|
|
592
|
-
stream.push({ type: "text_start", contentIndex, partial: output });
|
|
592
|
+
stream.push({ type: "text_start", contentIndex, partial: output }, { offset: 0 });
|
|
593
593
|
for (const delta of deferredTextDeltas) {
|
|
594
594
|
stream.push({ type: "text_delta", contentIndex, delta, partial: output });
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
else if (block.type === "thinking") {
|
|
598
|
-
stream.push({ type: "thinking_start", contentIndex, partial: output });
|
|
598
|
+
stream.push({ type: "thinking_start", contentIndex, partial: output }, { offset: 0 });
|
|
599
599
|
for (const delta of deferredThinkingDeltas) {
|
|
600
600
|
stream.push({ type: "thinking_delta", contentIndex, delta, partial: output });
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
603
|
else {
|
|
604
|
-
|
|
604
|
+
const rawArgumentPrefix = (deferredToolCallDeltas.get(block) ?? []).join("");
|
|
605
|
+
stream.push({ type: "toolcall_start", contentIndex, partial: output }, {
|
|
606
|
+
rawArgumentPrefix,
|
|
607
|
+
parsedThrough: rawArgumentPrefix.length,
|
|
608
|
+
offset: 0,
|
|
609
|
+
});
|
|
605
610
|
for (const delta of deferredToolCallDeltas.get(block) ?? []) {
|
|
606
611
|
stream.push({ type: "toolcall_delta", contentIndex, delta, partial: output });
|
|
607
612
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CONTEXT_PROVENANCE_FIELD, contextProvenanceFingerprint, getContextProvenance, } from "../context-provenance.js";
|
|
2
2
|
import { calculateCost, supportsConfigurationUpdate } from "../models.js";
|
|
3
|
+
import { enableAssistantCapture, pushAssistantProgress } from "../utils/assistant-capture.js";
|
|
3
4
|
import { splitDeferredTools } from "../utils/deferred-tools.js";
|
|
4
5
|
import { shortHash } from "../utils/hash.js";
|
|
5
6
|
import { parseStreamingJson } from "../utils/json-parse.js";
|
|
@@ -581,6 +582,7 @@ function reconcileNativeImageGenerationCall(item) {
|
|
|
581
582
|
};
|
|
582
583
|
}
|
|
583
584
|
export async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
585
|
+
enableAssistantCapture(stream);
|
|
584
586
|
let sawTerminalResponseEvent = false;
|
|
585
587
|
let nativeImageBase64Chars = 0;
|
|
586
588
|
const outputSlots = new Map();
|
|
@@ -644,7 +646,7 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
644
646
|
contentIndex: output.content.length - 1,
|
|
645
647
|
};
|
|
646
648
|
outputSlots.set(outputIndex, slot);
|
|
647
|
-
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
649
|
+
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output }, { rawArgumentPrefix: block.partialJson });
|
|
648
650
|
return slot;
|
|
649
651
|
}
|
|
650
652
|
if (isResponseCustomToolCallItem(item)) {
|
|
@@ -686,6 +688,12 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
686
688
|
reconcileNativeImageSlot(outputIndex, slot, imageItem);
|
|
687
689
|
output.content.push(block);
|
|
688
690
|
outputSlots.set(outputIndex, slot);
|
|
691
|
+
pushAssistantProgress(stream, {
|
|
692
|
+
type: "message_block_state",
|
|
693
|
+
contentIndex: slot.contentIndex,
|
|
694
|
+
phase: "insert",
|
|
695
|
+
block,
|
|
696
|
+
});
|
|
689
697
|
return slot;
|
|
690
698
|
};
|
|
691
699
|
const getOrCreateSlot = (outputIndex, item) => {
|
|
@@ -732,10 +740,16 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
732
740
|
if (!imageItem)
|
|
733
741
|
continue;
|
|
734
742
|
const existingSlot = getSlot(outputIndex, "providerNative");
|
|
743
|
+
const slot = existingSlot ?? createSlot(outputIndex, outputItem);
|
|
735
744
|
if (existingSlot)
|
|
736
745
|
reconcileNativeImageSlot(outputIndex, existingSlot, imageItem);
|
|
737
|
-
|
|
738
|
-
|
|
746
|
+
if (slot)
|
|
747
|
+
pushAssistantProgress(stream, {
|
|
748
|
+
type: "message_block_state",
|
|
749
|
+
contentIndex: slot.contentIndex,
|
|
750
|
+
phase: "end",
|
|
751
|
+
block: slot.block,
|
|
752
|
+
});
|
|
739
753
|
outputSlots.delete(outputIndex);
|
|
740
754
|
}
|
|
741
755
|
};
|
|
@@ -897,6 +911,14 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
897
911
|
if (delta.length > 0)
|
|
898
912
|
pushToolCallDelta(slot, delta);
|
|
899
913
|
}
|
|
914
|
+
else {
|
|
915
|
+
pushAssistantProgress(stream, {
|
|
916
|
+
type: "message_tool_arguments_reset",
|
|
917
|
+
contentIndex: slot.contentIndex,
|
|
918
|
+
rawArgumentPrefix: event.arguments,
|
|
919
|
+
parsedThrough: event.arguments.length,
|
|
920
|
+
});
|
|
921
|
+
}
|
|
900
922
|
}
|
|
901
923
|
else if (event.type === "response.custom_tool_call_input.delta") {
|
|
902
924
|
const slot = getSlot(event.output_index, "toolCall");
|
|
@@ -917,6 +939,12 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
917
939
|
const imageItem = readNativeImageGenerationCall(item);
|
|
918
940
|
if (imageItem && slot?.type === "providerNative") {
|
|
919
941
|
reconcileNativeImageSlot(event.output_index, slot, imageItem);
|
|
942
|
+
pushAssistantProgress(stream, {
|
|
943
|
+
type: "message_block_state",
|
|
944
|
+
contentIndex: slot.contentIndex,
|
|
945
|
+
phase: "end",
|
|
946
|
+
block: slot.block,
|
|
947
|
+
});
|
|
920
948
|
finalizedNativeImageOutputIndexes.add(event.output_index);
|
|
921
949
|
outputSlots.delete(event.output_index);
|
|
922
950
|
}
|
|
@@ -990,6 +1018,12 @@ export async function processResponsesStream(openaiStream, output, stream, model
|
|
|
990
1018
|
else if (slot?.type === "providerNative") {
|
|
991
1019
|
slot.block.subtype = item.type;
|
|
992
1020
|
slot.block.raw = item;
|
|
1021
|
+
pushAssistantProgress(stream, {
|
|
1022
|
+
type: "message_block_state",
|
|
1023
|
+
contentIndex: slot.contentIndex,
|
|
1024
|
+
phase: "end",
|
|
1025
|
+
block: slot.block,
|
|
1026
|
+
});
|
|
993
1027
|
outputSlots.delete(event.output_index);
|
|
994
1028
|
}
|
|
995
1029
|
}
|