veryfront 0.1.209 → 0.1.211
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/esm/cli/mcp/advanced-tools.d.ts.map +1 -1
- package/esm/cli/mcp/advanced-tools.js +6 -0
- package/esm/cli/mcp/standalone.d.ts.map +1 -1
- package/esm/cli/mcp/standalone.js +53 -0
- package/esm/cli/mcp/tools/build-tool.d.ts +28 -0
- package/esm/cli/mcp/tools/build-tool.d.ts.map +1 -0
- package/esm/cli/mcp/tools/build-tool.js +88 -0
- package/esm/cli/mcp/tools/run-lint-tool.d.ts +14 -0
- package/esm/cli/mcp/tools/run-lint-tool.d.ts.map +1 -0
- package/esm/cli/mcp/tools/run-lint-tool.js +55 -0
- package/esm/cli/mcp/tools/run-tests-tool.d.ts +25 -0
- package/esm/cli/mcp/tools/run-tests-tool.d.ts.map +1 -0
- package/esm/cli/mcp/tools/run-tests-tool.js +80 -0
- package/esm/deno.js +1 -1
- package/esm/src/agent/data-stream.d.ts.map +1 -1
- package/esm/src/agent/data-stream.js +27 -5
- package/esm/src/agent/runtime/index.d.ts +52 -1
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +95 -10
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/cli/mcp/advanced-tools.ts +6 -0
- package/src/cli/mcp/standalone.ts +56 -0
- package/src/cli/mcp/tools/build-tool.ts +115 -0
- package/src/cli/mcp/tools/run-lint-tool.ts +69 -0
- package/src/cli/mcp/tools/run-tests-tool.ts +101 -0
- package/src/deno.js +1 -1
- package/src/src/agent/data-stream.ts +32 -5
- package/src/src/agent/runtime/index.ts +140 -10
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -198,6 +198,97 @@ export function captureStreamedToolCallInput(
|
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
/**
|
|
202
|
+
* A streamed tool call is "incomplete" when the provider stream terminated
|
|
203
|
+
* (abort, stall, timeout, transport error) before the SDK emitted the
|
|
204
|
+
* finalizing `tool-call` event that sets `inputAvailable: true`. In that state
|
|
205
|
+
* `arguments` only holds partial JSON fragments from `tool-input-delta` events,
|
|
206
|
+
* so the tool call is NOT a committed model choice and must not be parsed or
|
|
207
|
+
* executed. This is semantically distinct from a parse failure on a finalized
|
|
208
|
+
* tool call (`inputAvailable: true` but malformed JSON — which only happens on
|
|
209
|
+
* genuine provider bugs) and needs to be reported as a stream-termination
|
|
210
|
+
* error rather than a tool-argument error.
|
|
211
|
+
*/
|
|
212
|
+
export function isStreamedToolCallIncomplete(
|
|
213
|
+
toolCall: Pick<StreamingToolCall, "inputAvailable">,
|
|
214
|
+
): boolean {
|
|
215
|
+
return toolCall.inputAvailable !== true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Classification of a streamed tool call when we reach end-of-stream and need
|
|
220
|
+
* to persist it into the assistant message. Three distinct cases, each with
|
|
221
|
+
* different semantics downstream:
|
|
222
|
+
*
|
|
223
|
+
* - `complete`: provider emitted the finalizing `tool-call` event and the
|
|
224
|
+
* arguments parsed cleanly. Execute the tool normally.
|
|
225
|
+
* - `parse-error`: provider emitted the finalizing `tool-call` event but the
|
|
226
|
+
* arguments are not valid JSON. This is a provider/SDK bug; record it as a
|
|
227
|
+
* tool-argument error so the step can recover.
|
|
228
|
+
* - `incomplete`: stream terminated before the finalizing event fired. The
|
|
229
|
+
* model never committed this tool use; record it as a stream-termination
|
|
230
|
+
* error so the parent (e.g. child-fork watchdog) can decide whether to
|
|
231
|
+
* retry the step cleanly instead of seeing a malformed tool call.
|
|
232
|
+
*/
|
|
233
|
+
export type StreamedToolCallMaterialization =
|
|
234
|
+
| { readonly kind: "complete"; readonly part: MessagePart }
|
|
235
|
+
| {
|
|
236
|
+
readonly kind: "parse-error";
|
|
237
|
+
readonly part: MessagePart;
|
|
238
|
+
readonly parseError: string;
|
|
239
|
+
}
|
|
240
|
+
| {
|
|
241
|
+
readonly kind: "incomplete";
|
|
242
|
+
readonly part: MessagePart;
|
|
243
|
+
readonly partialArgumentsLength: number;
|
|
244
|
+
readonly partialArgumentsPreview: string;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Classify and build the persisted `MessagePart` for a single streamed tool
|
|
249
|
+
* call. Pure function — no logging, no SSE, no memory. Callers decide what to
|
|
250
|
+
* do with the result so this stays unit-testable.
|
|
251
|
+
*
|
|
252
|
+
* The resulting `part` is always pushed into the assistant message so the
|
|
253
|
+
* conversation history is transparent: even incomplete tool calls leave a
|
|
254
|
+
* visible trace with their partial `inputText`. What differs is the caller's
|
|
255
|
+
* error-surfacing behavior (log warning, SSE event, tool-result error).
|
|
256
|
+
*/
|
|
257
|
+
export function materializeStreamedToolCall(
|
|
258
|
+
tc: StreamingToolCall,
|
|
259
|
+
): StreamedToolCallMaterialization {
|
|
260
|
+
const basePart: MessagePart = {
|
|
261
|
+
type: `tool-${tc.name}`,
|
|
262
|
+
toolCallId: tc.id,
|
|
263
|
+
toolName: tc.name,
|
|
264
|
+
args: {},
|
|
265
|
+
...(tc.arguments.length > 0 ? { inputText: tc.arguments } : {}),
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
if (isStreamedToolCallIncomplete(tc)) {
|
|
269
|
+
return {
|
|
270
|
+
kind: "incomplete",
|
|
271
|
+
part: basePart,
|
|
272
|
+
partialArgumentsLength: tc.arguments.length,
|
|
273
|
+
partialArgumentsPreview: tc.arguments.slice(0, 200),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const capturedInput = captureStreamedToolCallInput(tc);
|
|
278
|
+
const part: MessagePart = {
|
|
279
|
+
type: `tool-${tc.name}`,
|
|
280
|
+
toolCallId: tc.id,
|
|
281
|
+
toolName: tc.name,
|
|
282
|
+
args: capturedInput.args,
|
|
283
|
+
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
if (capturedInput.parseError) {
|
|
287
|
+
return { kind: "parse-error", part, parseError: capturedInput.parseError };
|
|
288
|
+
}
|
|
289
|
+
return { kind: "complete", part };
|
|
290
|
+
}
|
|
291
|
+
|
|
201
292
|
function isToolResultPart(part: MessagePart): part is ToolResultPart {
|
|
202
293
|
return part.type === "tool-result" && "result" in part;
|
|
203
294
|
}
|
|
@@ -961,20 +1052,35 @@ export class AgentRuntime {
|
|
|
961
1052
|
if (state.accumulatedText) streamParts.push({ type: "text", text: state.accumulatedText });
|
|
962
1053
|
|
|
963
1054
|
for (const tc of state.toolCalls.values()) {
|
|
964
|
-
const
|
|
965
|
-
|
|
1055
|
+
const materialized = materializeStreamedToolCall(tc);
|
|
1056
|
+
streamParts.push(materialized.part);
|
|
1057
|
+
|
|
1058
|
+
if (materialized.kind === "incomplete") {
|
|
1059
|
+
// Stream terminated before the provider emitted the finalizing
|
|
1060
|
+
// `tool-call` event for this block. The model never committed this
|
|
1061
|
+
// tool use. Surface the failure via SSE so the live client can
|
|
1062
|
+
// react, and leave the partial fragment under `inputText` in the
|
|
1063
|
+
// persisted part above so the history is replayable and transparent.
|
|
1064
|
+
logger.warn("Streamed tool call terminated before tool-call event", {
|
|
1065
|
+
toolCallId: tc.id,
|
|
1066
|
+
toolName: tc.name,
|
|
1067
|
+
partialArgumentsLength: materialized.partialArgumentsLength,
|
|
1068
|
+
partialArgumentsPreview: materialized.partialArgumentsPreview,
|
|
1069
|
+
});
|
|
1070
|
+
const dynamicIncomplete = isDynamicTool(tc.name);
|
|
1071
|
+
sendSSE(controller, encoder, {
|
|
1072
|
+
type: "tool-input-error",
|
|
1073
|
+
toolCallId: tc.id,
|
|
1074
|
+
errorText: `Stream terminated before tool-call event fired for "${tc.name}". ` +
|
|
1075
|
+
`Received ${materialized.partialArgumentsLength} chars of partial tool-input deltas.`,
|
|
1076
|
+
...(dynamicIncomplete ? { dynamic: true } : {}),
|
|
1077
|
+
});
|
|
1078
|
+
} else if (materialized.kind === "parse-error") {
|
|
966
1079
|
logger.warn("Failed to parse streamed tool arguments", {
|
|
967
1080
|
toolCallId: tc.id,
|
|
968
|
-
error:
|
|
1081
|
+
error: materialized.parseError,
|
|
969
1082
|
});
|
|
970
1083
|
}
|
|
971
|
-
streamParts.push({
|
|
972
|
-
type: `tool-${tc.name}`,
|
|
973
|
-
toolCallId: tc.id,
|
|
974
|
-
toolName: tc.name,
|
|
975
|
-
args: capturedInput.args,
|
|
976
|
-
...(capturedInput.inputText ? { inputText: capturedInput.inputText } : {}),
|
|
977
|
-
});
|
|
978
1084
|
}
|
|
979
1085
|
|
|
980
1086
|
const assistantMessage: Message = {
|
|
@@ -1033,6 +1139,30 @@ export class AgentRuntime {
|
|
|
1033
1139
|
|
|
1034
1140
|
for (const tc of streamedToolCalls) {
|
|
1035
1141
|
throwIfAborted(abortSignal);
|
|
1142
|
+
if (isStreamedToolCallIncomplete(tc)) {
|
|
1143
|
+
// Stream ended before the provider finalized this tool call. We
|
|
1144
|
+
// cannot execute it — record a distinct stream-termination error
|
|
1145
|
+
// (not a tool-argument parse error) so the parent step and any
|
|
1146
|
+
// upstream orchestrator (e.g. the child-fork watchdog) see a
|
|
1147
|
+
// completed step with a clearly-labelled failure and can recover.
|
|
1148
|
+
const incompleteToolCall: ToolCall = {
|
|
1149
|
+
id: tc.id,
|
|
1150
|
+
name: tc.name,
|
|
1151
|
+
args: {},
|
|
1152
|
+
...(tc.arguments.length > 0 ? { inputText: tc.arguments } : {}),
|
|
1153
|
+
status: "pending",
|
|
1154
|
+
};
|
|
1155
|
+
await this.recordToolError(
|
|
1156
|
+
incompleteToolCall,
|
|
1157
|
+
`Stream terminated before tool-call event fired for "${tc.name}". ` +
|
|
1158
|
+
`Received ${tc.arguments.length} chars of partial tool-input deltas.`,
|
|
1159
|
+
controller,
|
|
1160
|
+
encoder,
|
|
1161
|
+
currentMessages,
|
|
1162
|
+
toolCalls,
|
|
1163
|
+
);
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1036
1166
|
const capturedInput = captureStreamedToolCallInput(tc);
|
|
1037
1167
|
const toolCall: ToolCall = {
|
|
1038
1168
|
id: tc.id,
|