@perstack/runtime 0.0.61 → 0.0.62

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/src/index.js CHANGED
@@ -5,10 +5,10 @@ import { createDeepSeek } from '@ai-sdk/deepseek';
5
5
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
6
6
  import { createVertex } from '@ai-sdk/google-vertex';
7
7
  import { createOpenAI } from '@ai-sdk/openai';
8
- import { knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveThought, attemptCompletion, resolvePdfFile, resolveImageFile, resolveToolResult, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callDelegate, callInteractiveTool, callTool, startRun, startGeneration, finishToolCall, runParamsSchema, createRuntimeEvent, checkpointSchema, runSettingSchema } from '@perstack/core';
8
+ import { knownModels, stopRunByDelegate, stopRunByInteractiveTool, resolveToolResults, attemptCompletion, callDelegate, callInteractiveTool, stopRunByExceededMaxSteps, continueToNextStep, retry, completeRun, callTools, startRun, resumeToolCalls, finishAllToolCalls, startGeneration, finishToolCall, runParamsSchema, createRuntimeEvent, checkpointSchema, runSettingSchema } from '@perstack/core';
9
9
  import { createOllama } from 'ollama-ai-provider-v2';
10
10
  import { createId } from '@paralleldrive/cuid2';
11
- import { readFile, readdir, mkdir, writeFile } from 'fs/promises';
11
+ import { readdir, readFile, mkdir, writeFile } from 'fs/promises';
12
12
  import path from 'path';
13
13
  import { setup, assign, createActor } from 'xstate';
14
14
  import { generateText, tool, jsonSchema } from 'ai';
@@ -21,7 +21,7 @@ import { ApiV1Client } from '@perstack/api-client/v1';
21
21
 
22
22
  // package.json
23
23
  var package_default = {
24
- version: "0.0.61"};
24
+ version: "0.0.62"};
25
25
  function getModel(modelId, providerConfig) {
26
26
  switch (providerConfig.providerName) {
27
27
  case "anthropic": {
@@ -192,7 +192,9 @@ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentChec
192
192
  checkpoint: {
193
193
  ...parentCheckpoint,
194
194
  stepNumber: resultCheckpoint.stepNumber,
195
- usage: resultCheckpoint.usage
195
+ usage: resultCheckpoint.usage,
196
+ pendingToolCalls: parentCheckpoint.pendingToolCalls,
197
+ partialToolResults: parentCheckpoint.partialToolResults
196
198
  }
197
199
  };
198
200
  }
@@ -229,7 +231,9 @@ function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
229
231
  toolName,
230
232
  checkpointId: resultCheckpoint.id
231
233
  },
232
- usage: resultCheckpoint.usage
234
+ usage: resultCheckpoint.usage,
235
+ pendingToolCalls: void 0,
236
+ partialToolResults: void 0
233
237
  }
234
238
  };
235
239
  }
@@ -741,10 +745,14 @@ async function callingDelegateLogic({
741
745
  step,
742
746
  skillManagers
743
747
  }) {
744
- if (!step.toolCall) {
745
- throw new Error("No tool call found");
748
+ if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
749
+ throw new Error("No pending tool calls found");
746
750
  }
747
- const { id, toolName, args } = step.toolCall;
751
+ const toolCall = step.pendingToolCalls[0];
752
+ if (!toolCall) {
753
+ throw new Error("No pending tool call found");
754
+ }
755
+ const { id, toolName, args } = toolCall;
748
756
  const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
749
757
  if (!skillManager.expert) {
750
758
  throw new Error(`Delegation error: skill manager "${toolName}" not found`);
@@ -752,6 +760,8 @@ async function callingDelegateLogic({
752
760
  if (!args || !args.query || typeof args.query !== "string") {
753
761
  throw new Error("Delegation error: query is undefined");
754
762
  }
763
+ const currentToolCall = step.pendingToolCalls[0];
764
+ const remainingToolCalls = step.pendingToolCalls.slice(1);
755
765
  return stopRunByDelegate(setting, checkpoint, {
756
766
  checkpoint: {
757
767
  ...checkpoint,
@@ -765,7 +775,9 @@ async function callingDelegateLogic({
765
775
  toolCallId: id,
766
776
  toolName,
767
777
  query: args.query
768
- }
778
+ },
779
+ pendingToolCalls: [currentToolCall, ...remainingToolCalls],
780
+ partialToolResults: step.partialToolResults
769
781
  },
770
782
  step: {
771
783
  ...step,
@@ -778,10 +790,17 @@ async function callingInteractiveToolLogic({
778
790
  checkpoint,
779
791
  step
780
792
  }) {
793
+ if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
794
+ throw new Error("No pending tool calls found");
795
+ }
796
+ const currentToolCall = step.pendingToolCalls[0];
797
+ const remainingToolCalls = step.pendingToolCalls.slice(1);
781
798
  return stopRunByInteractiveTool(setting, checkpoint, {
782
799
  checkpoint: {
783
800
  ...checkpoint,
784
- status: "stoppedByInteractiveTool"
801
+ status: "stoppedByInteractiveTool",
802
+ pendingToolCalls: [currentToolCall, ...remainingToolCalls],
803
+ partialToolResults: step.partialToolResults
785
804
  },
786
805
  step: {
787
806
  ...step,
@@ -789,37 +808,155 @@ async function callingInteractiveToolLogic({
789
808
  }
790
809
  });
791
810
  }
811
+ function hasRemainingTodos(toolResult) {
812
+ const firstPart = toolResult.result[0];
813
+ if (!firstPart || firstPart.type !== "textPart") {
814
+ return false;
815
+ }
816
+ try {
817
+ const parsed = JSON.parse(firstPart.text);
818
+ return Array.isArray(parsed.remainingTodos) && parsed.remainingTodos.length > 0;
819
+ } catch {
820
+ return false;
821
+ }
822
+ }
823
+ function isFileInfo(value) {
824
+ return typeof value === "object" && value !== null && "path" in value && "mimeType" in value && "size" in value && typeof value.path === "string" && typeof value.mimeType === "string" && typeof value.size === "number";
825
+ }
826
+ async function processFileToolResult(toolResult, toolName) {
827
+ const processedContents = [];
828
+ for (const part of toolResult.result) {
829
+ if (part.type !== "textPart") {
830
+ processedContents.push(part);
831
+ continue;
832
+ }
833
+ let fileInfo;
834
+ try {
835
+ const parsed = JSON.parse(part.text);
836
+ if (isFileInfo(parsed)) {
837
+ fileInfo = parsed;
838
+ }
839
+ } catch {
840
+ processedContents.push(part);
841
+ continue;
842
+ }
843
+ if (!fileInfo) {
844
+ processedContents.push(part);
845
+ continue;
846
+ }
847
+ const { path: path2, mimeType } = fileInfo;
848
+ try {
849
+ const buffer = await readFile(path2);
850
+ if (toolName === "readImageFile") {
851
+ processedContents.push({
852
+ type: "imageInlinePart",
853
+ id: part.id,
854
+ encodedData: buffer.toString("base64"),
855
+ mimeType
856
+ });
857
+ } else {
858
+ processedContents.push({
859
+ type: "fileInlinePart",
860
+ id: part.id,
861
+ encodedData: buffer.toString("base64"),
862
+ mimeType
863
+ });
864
+ }
865
+ } catch (error) {
866
+ processedContents.push({
867
+ type: "textPart",
868
+ id: part.id,
869
+ text: `Failed to read file "${path2}": ${error instanceof Error ? error.message : String(error)}`
870
+ });
871
+ }
872
+ }
873
+ return { ...toolResult, result: processedContents };
874
+ }
875
+ async function executeMcpToolCall(toolCall, skillManagers) {
876
+ const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
877
+ if (skillManager.type !== "mcp") {
878
+ throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
879
+ }
880
+ const result = await skillManager.callTool(toolCall.toolName, toolCall.args);
881
+ const toolResult = {
882
+ id: toolCall.id,
883
+ skillName: toolCall.skillName,
884
+ toolName: toolCall.toolName,
885
+ result
886
+ };
887
+ if (toolCall.toolName === "readPdfFile" || toolCall.toolName === "readImageFile") {
888
+ return processFileToolResult(toolResult, toolCall.toolName);
889
+ }
890
+ return toolResult;
891
+ }
892
+ async function getToolType(toolCall, skillManagers) {
893
+ const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
894
+ return skillManager.type;
895
+ }
792
896
  async function callingToolLogic({
793
897
  setting,
794
898
  checkpoint,
795
899
  step,
796
900
  skillManagers
797
901
  }) {
798
- if (!step.toolCall) {
799
- throw new Error("No tool call found");
902
+ const pendingToolCalls = step.pendingToolCalls ?? step.toolCalls ?? [];
903
+ if (pendingToolCalls.length === 0) {
904
+ throw new Error("No tool calls found");
800
905
  }
801
- const { id, skillName, toolName, args } = step.toolCall;
802
- const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
803
- if (skillManager.type !== "mcp") {
804
- throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
906
+ const toolResults = step.toolResults ? [...step.toolResults] : [];
907
+ const attemptCompletionTool = pendingToolCalls.find(
908
+ (tc) => tc.skillName === "@perstack/base" && tc.toolName === "attemptCompletion"
909
+ );
910
+ if (attemptCompletionTool) {
911
+ const toolResult = await executeMcpToolCall(attemptCompletionTool, skillManagers);
912
+ if (hasRemainingTodos(toolResult)) {
913
+ return resolveToolResults(setting, checkpoint, { toolResults: [toolResult] });
914
+ }
915
+ return attemptCompletion(setting, checkpoint, { toolResult });
916
+ }
917
+ const toolCallTypes = await Promise.all(
918
+ pendingToolCalls.map(async (tc) => ({
919
+ toolCall: tc,
920
+ type: await getToolType(tc, skillManagers)
921
+ }))
922
+ );
923
+ const mcpToolCalls = toolCallTypes.filter((t) => t.type === "mcp").map((t) => t.toolCall);
924
+ const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
925
+ const interactiveToolCalls = toolCallTypes.filter((t) => t.type === "interactive").map((t) => t.toolCall);
926
+ if (mcpToolCalls.length > 0) {
927
+ const mcpResults = await Promise.all(
928
+ mcpToolCalls.map((tc) => executeMcpToolCall(tc, skillManagers))
929
+ );
930
+ toolResults.push(...mcpResults);
931
+ }
932
+ const remainingToolCalls = [...delegateToolCalls, ...interactiveToolCalls];
933
+ if (delegateToolCalls.length > 0) {
934
+ const delegateToolCall = delegateToolCalls[0];
935
+ if (!delegateToolCall) {
936
+ throw new Error("No delegate tool call found");
937
+ }
938
+ step.partialToolResults = toolResults;
939
+ step.pendingToolCalls = remainingToolCalls;
940
+ return callDelegate(setting, checkpoint, {
941
+ newMessage: checkpoint.messages[checkpoint.messages.length - 1],
942
+ toolCall: delegateToolCall,
943
+ usage: step.usage
944
+ });
805
945
  }
806
- const result = await skillManager.callTool(toolName, args);
807
- const toolResult = { id, skillName, toolName, result };
808
- if (skillName === "@perstack/base") {
809
- if (toolName === "think") {
810
- return resolveThought(setting, checkpoint, { toolResult });
811
- }
812
- if (toolName === "attemptCompletion") {
813
- return attemptCompletion(setting, checkpoint, { toolResult });
814
- }
815
- if (toolName === "readPdfFile") {
816
- return resolvePdfFile(setting, checkpoint, { toolResult });
817
- }
818
- if (toolName === "readImageFile") {
819
- return resolveImageFile(setting, checkpoint, { toolResult });
820
- }
946
+ if (interactiveToolCalls.length > 0) {
947
+ const interactiveToolCall = interactiveToolCalls[0];
948
+ if (!interactiveToolCall) {
949
+ throw new Error("No interactive tool call found");
950
+ }
951
+ step.partialToolResults = toolResults;
952
+ step.pendingToolCalls = remainingToolCalls;
953
+ return callInteractiveTool(setting, checkpoint, {
954
+ newMessage: checkpoint.messages[checkpoint.messages.length - 1],
955
+ toolCall: interactiveToolCall,
956
+ usage: step.usage
957
+ });
821
958
  }
822
- return resolveToolResult(setting, checkpoint, { toolResult });
959
+ return resolveToolResults(setting, checkpoint, { toolResults });
823
960
  }
824
961
  async function finishingStepLogic({
825
962
  setting,
@@ -1035,9 +1172,12 @@ function toolResultPartToCoreToolResultPart(part) {
1035
1172
  output: { type: "text", value: contents[0].text }
1036
1173
  };
1037
1174
  }
1038
- const contentValue = contents.map(
1039
- (content) => content.type === "textPart" ? { type: "text", text: content.text } : { type: "media", data: content.encodedData, mediaType: content.mimeType }
1040
- );
1175
+ const contentValue = contents.map((content) => {
1176
+ if (content.type === "textPart") {
1177
+ return { type: "text", text: content.text };
1178
+ }
1179
+ return { type: "media", data: content.encodedData, mediaType: content.mimeType };
1180
+ });
1041
1181
  return {
1042
1182
  type: "tool-result",
1043
1183
  toolCallId: part.toolCallId,
@@ -1052,21 +1192,21 @@ async function generatingRunResultLogic({
1052
1192
  checkpoint,
1053
1193
  step
1054
1194
  }) {
1055
- if (!step.toolCall || !step.toolResult) {
1056
- throw new Error("No tool call or tool result found");
1195
+ if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
1196
+ throw new Error("No tool calls or tool results found");
1057
1197
  }
1058
- const { id, toolName } = step.toolCall;
1059
- const { result } = step.toolResult;
1060
- const toolMessage = createToolMessage([
1061
- {
1198
+ const toolResultParts = step.toolResults.map((toolResult) => {
1199
+ const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
1200
+ return {
1062
1201
  type: "toolResultPart",
1063
- toolCallId: id,
1064
- toolName,
1065
- contents: result.filter(
1066
- (part) => part.type === "textPart" || part.type === "imageInlinePart"
1202
+ toolCallId: toolResult.id,
1203
+ toolName: toolCall?.toolName ?? toolResult.toolName,
1204
+ contents: toolResult.result.filter(
1205
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1067
1206
  )
1068
- }
1069
- ]);
1207
+ };
1208
+ });
1209
+ const toolMessage = createToolMessage(toolResultParts);
1070
1210
  const model = getModel(setting.model, setting.providerConfig);
1071
1211
  const { messages } = checkpoint;
1072
1212
  let generationResult;
@@ -1103,13 +1243,48 @@ async function generatingRunResultLogic({
1103
1243
  step: {
1104
1244
  ...step,
1105
1245
  newMessages: [...step.newMessages, ...newMessages],
1106
- finishedAt: (/* @__PURE__ */ new Date()).getTime(),
1246
+ finishedAt: Date.now(),
1107
1247
  usage: sumUsage(step.usage, usage)
1108
1248
  },
1109
1249
  text,
1110
1250
  usage
1111
1251
  });
1112
1252
  }
1253
+ async function classifyToolCalls(toolCalls, skillManagers) {
1254
+ return Promise.all(
1255
+ toolCalls.map(async (tc) => {
1256
+ const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
1257
+ return {
1258
+ toolCallId: tc.toolCallId,
1259
+ toolName: tc.toolName,
1260
+ input: tc.input,
1261
+ skillManager
1262
+ };
1263
+ })
1264
+ );
1265
+ }
1266
+ function sortToolCallsByPriority(toolCalls) {
1267
+ const priority = { mcp: 0, delegate: 1, interactive: 2 };
1268
+ return [...toolCalls].sort(
1269
+ (a, b) => (priority[a.skillManager.type] ?? 99) - (priority[b.skillManager.type] ?? 99)
1270
+ );
1271
+ }
1272
+ function buildToolCallParts(toolCalls) {
1273
+ return toolCalls.map((tc) => ({
1274
+ type: "toolCallPart",
1275
+ toolCallId: tc.toolCallId,
1276
+ toolName: tc.toolName,
1277
+ args: tc.input
1278
+ }));
1279
+ }
1280
+ function buildToolCalls(toolCalls) {
1281
+ return toolCalls.map((tc) => ({
1282
+ id: tc.toolCallId,
1283
+ skillName: tc.skillManager.name,
1284
+ toolName: tc.toolName,
1285
+ args: tc.input
1286
+ }));
1287
+ }
1113
1288
  async function generatingToolCallLogic({
1114
1289
  setting,
1115
1290
  checkpoint,
@@ -1141,8 +1316,7 @@ async function generatingToolCallLogic({
1141
1316
  }
1142
1317
  const usage = usageFromGenerateTextResult(result);
1143
1318
  const { text, toolCalls, finishReason } = result;
1144
- const toolCall = toolCalls[0];
1145
- if (!toolCall) {
1319
+ if (toolCalls.length === 0) {
1146
1320
  const reason = JSON.stringify({
1147
1321
  error: "Error: No tool call generated",
1148
1322
  message: "You must generate a tool call. Try again."
@@ -1153,42 +1327,26 @@ async function generatingToolCallLogic({
1153
1327
  usage
1154
1328
  });
1155
1329
  }
1156
- const contents = [
1157
- {
1158
- type: "toolCallPart",
1159
- toolCallId: toolCall.toolCallId,
1160
- toolName: toolCall.toolName,
1161
- args: toolCall.input
1162
- }
1163
- ];
1164
- if (text) {
1165
- contents.push({
1166
- type: "textPart",
1167
- text
1168
- });
1169
- }
1170
- const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
1171
- const eventPayload = {
1172
- newMessage: createExpertMessage(contents),
1173
- toolCall: {
1174
- id: toolCall.toolCallId,
1175
- skillName: skillManager.name,
1176
- toolName: toolCall.toolName,
1177
- args: toolCall.input
1178
- },
1179
- usage
1180
- };
1330
+ const classified = await classifyToolCalls(toolCalls, skillManagers);
1331
+ const sorted = sortToolCallsByPriority(classified);
1181
1332
  if (finishReason === "tool-calls" || finishReason === "stop") {
1182
- switch (skillManager.type) {
1183
- case "mcp":
1184
- return callTool(setting, checkpoint, eventPayload);
1185
- case "interactive":
1186
- return callInteractiveTool(setting, checkpoint, eventPayload);
1187
- case "delegate":
1188
- return callDelegate(setting, checkpoint, eventPayload);
1189
- }
1333
+ const toolCallParts = buildToolCallParts(sorted);
1334
+ const contents = [...toolCallParts];
1335
+ if (text) {
1336
+ contents.push({ type: "textPart", text });
1337
+ }
1338
+ const allToolCalls = buildToolCalls(sorted);
1339
+ return callTools(setting, checkpoint, {
1340
+ newMessage: createExpertMessage(contents),
1341
+ toolCalls: allToolCalls,
1342
+ usage
1343
+ });
1190
1344
  }
1191
1345
  if (finishReason === "length") {
1346
+ const firstToolCall = sorted[0];
1347
+ if (!firstToolCall) {
1348
+ throw new Error("No tool call found");
1349
+ }
1192
1350
  const reason = JSON.stringify({
1193
1351
  error: "Error: Tool call generation failed",
1194
1352
  message: "Generation length exceeded. Try again."
@@ -1199,27 +1357,36 @@ async function generatingToolCallLogic({
1199
1357
  createExpertMessage([
1200
1358
  {
1201
1359
  type: "toolCallPart",
1202
- toolCallId: toolCall.toolCallId,
1203
- toolName: toolCall.toolName,
1204
- args: toolCall.input
1360
+ toolCallId: firstToolCall.toolCallId,
1361
+ toolName: firstToolCall.toolName,
1362
+ args: firstToolCall.input
1205
1363
  }
1206
1364
  ]),
1207
1365
  createToolMessage([
1208
1366
  {
1209
1367
  type: "toolResultPart",
1210
- toolCallId: toolCall.toolCallId,
1211
- toolName: toolCall.toolName,
1368
+ toolCallId: firstToolCall.toolCallId,
1369
+ toolName: firstToolCall.toolName,
1212
1370
  contents: [{ type: "textPart", text: reason }]
1213
1371
  }
1214
1372
  ])
1215
1373
  ],
1216
- toolCall: eventPayload.toolCall,
1217
- toolResult: {
1218
- id: toolCall.toolCallId,
1219
- skillName: skillManager.name,
1220
- toolName: toolCall.toolName,
1221
- result: [{ type: "textPart", id: createId(), text: reason }]
1222
- },
1374
+ toolCalls: [
1375
+ {
1376
+ id: firstToolCall.toolCallId,
1377
+ skillName: firstToolCall.skillManager.name,
1378
+ toolName: firstToolCall.toolName,
1379
+ args: firstToolCall.input
1380
+ }
1381
+ ],
1382
+ toolResults: [
1383
+ {
1384
+ id: firstToolCall.toolCallId,
1385
+ skillName: firstToolCall.skillManager.name,
1386
+ toolName: firstToolCall.toolName,
1387
+ result: [{ type: "textPart", id: createId(), text: reason }]
1388
+ }
1389
+ ],
1223
1390
  usage
1224
1391
  });
1225
1392
  }
@@ -1348,18 +1515,26 @@ async function initLogic({
1348
1515
  if (!setting.input.interactiveToolCallResult) {
1349
1516
  throw new Error("Interactive tool call result is undefined");
1350
1517
  }
1351
- return startRun(setting, checkpoint, {
1352
- initialCheckpoint: checkpoint,
1353
- inputMessages: [
1354
- createToolMessage([
1355
- {
1356
- type: "toolResultPart",
1357
- toolCallId: setting.input.interactiveToolCallResult.toolCallId,
1358
- toolName: setting.input.interactiveToolCallResult.toolName,
1359
- contents: [{ type: "textPart", text: setting.input.interactiveToolCallResult.text }]
1360
- }
1361
- ])
1362
- ]
1518
+ const { toolCallId, toolName, text } = setting.input.interactiveToolCallResult;
1519
+ const pendingToolCalls = checkpoint.pendingToolCalls ?? [];
1520
+ const completedToolCall = pendingToolCalls.find((tc) => tc.id === toolCallId);
1521
+ const skillName = completedToolCall?.skillName ?? (checkpoint.status === "stoppedByDelegate" ? checkpoint.delegateTo?.expert.key : "") ?? "";
1522
+ const newToolResult = {
1523
+ id: toolCallId,
1524
+ skillName,
1525
+ toolName,
1526
+ result: [{ type: "textPart", id: createId(), text }]
1527
+ };
1528
+ const updatedPartialResults = [...checkpoint.partialToolResults ?? [], newToolResult];
1529
+ const updatedPendingToolCalls = pendingToolCalls.filter((tc) => tc.id !== toolCallId);
1530
+ const updatedCheckpoint = {
1531
+ ...checkpoint,
1532
+ partialToolResults: updatedPartialResults,
1533
+ pendingToolCalls: updatedPendingToolCalls.length > 0 ? updatedPendingToolCalls : void 0
1534
+ };
1535
+ return startRun(setting, updatedCheckpoint, {
1536
+ initialCheckpoint: updatedCheckpoint,
1537
+ inputMessages: []
1363
1538
  });
1364
1539
  }
1365
1540
  default:
@@ -1376,116 +1551,27 @@ async function preparingForStepLogic({
1376
1551
  setting,
1377
1552
  checkpoint
1378
1553
  }) {
1379
- return startGeneration(setting, checkpoint, {
1380
- messages: checkpoint.messages
1381
- });
1382
- }
1383
- async function resolvingImageFileLogic({
1384
- setting,
1385
- checkpoint,
1386
- step
1387
- }) {
1388
- if (!step.toolCall || !step.toolResult) {
1389
- throw new Error("No tool call or tool result found");
1390
- }
1391
- const { id, toolName } = step.toolCall;
1392
- const { result } = step.toolResult;
1393
- const textParts = result.filter((part) => part.type === "textPart");
1394
- const files = [];
1395
- for (const textPart of textParts) {
1396
- let imageInfo;
1397
- try {
1398
- imageInfo = JSON.parse(textPart.text);
1399
- } catch {
1400
- files.push({
1401
- type: "textPart",
1402
- text: textPart.text
1403
- });
1404
- continue;
1405
- }
1406
- const { path: path2, mimeType, size } = imageInfo;
1407
- try {
1408
- const buffer = await readFile(path2);
1409
- files.push({
1410
- type: "imageInlinePart",
1411
- encodedData: buffer.toString("base64"),
1412
- mimeType
1413
- });
1414
- } catch (error) {
1415
- files.push({
1416
- type: "textPart",
1417
- text: `Failed to read image file "${path2}": ${error instanceof Error ? error.message : String(error)}`
1418
- });
1419
- }
1554
+ if (checkpoint.pendingToolCalls && checkpoint.pendingToolCalls.length > 0) {
1555
+ return resumeToolCalls(setting, checkpoint, {
1556
+ pendingToolCalls: checkpoint.pendingToolCalls,
1557
+ partialToolResults: checkpoint.partialToolResults ?? []
1558
+ });
1420
1559
  }
1421
- return finishToolCall(setting, checkpoint, {
1422
- newMessages: [
1423
- createToolMessage([
1424
- {
1425
- type: "toolResultPart",
1426
- toolCallId: id,
1427
- toolName,
1428
- contents: files
1429
- }
1430
- ])
1431
- ]
1432
- });
1433
- }
1434
- async function resolvingPdfFileLogic({
1435
- setting,
1436
- checkpoint,
1437
- step
1438
- }) {
1439
- if (!step.toolCall || !step.toolResult) {
1440
- throw new Error("No tool call or tool result found");
1441
- }
1442
- const { id, toolName } = step.toolCall;
1443
- const { result } = step.toolResult;
1444
- const textParts = result.filter((part) => part.type === "textPart");
1445
- const files = [];
1446
- for (const textPart of textParts) {
1447
- let pdfInfo;
1448
- try {
1449
- pdfInfo = JSON.parse(textPart.text);
1450
- } catch {
1451
- files.push({
1452
- type: "textPart",
1453
- text: textPart.text
1454
- });
1455
- continue;
1456
- }
1457
- const { path: path2, mimeType, size } = pdfInfo;
1458
- try {
1459
- const buffer = await readFile(path2);
1460
- files.push({
1461
- type: "fileInlinePart",
1462
- encodedData: buffer.toString("base64"),
1463
- mimeType
1464
- });
1465
- } catch (error) {
1466
- files.push({
1467
- type: "textPart",
1468
- text: `Failed to read PDF file "${path2}": ${error instanceof Error ? error.message : String(error)}`
1469
- });
1470
- }
1560
+ if (checkpoint.partialToolResults && checkpoint.partialToolResults.length > 0) {
1561
+ const toolResultParts = checkpoint.partialToolResults.map((tr) => ({
1562
+ type: "toolResultPart",
1563
+ toolCallId: tr.id,
1564
+ toolName: tr.toolName,
1565
+ contents: tr.result.filter(
1566
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1567
+ )
1568
+ }));
1569
+ return finishAllToolCalls(setting, checkpoint, {
1570
+ newMessages: [createToolMessage(toolResultParts)]
1571
+ });
1471
1572
  }
1472
- return finishToolCall(setting, checkpoint, {
1473
- newMessages: [
1474
- createToolMessage([
1475
- {
1476
- type: "toolResultPart",
1477
- toolCallId: id,
1478
- toolName,
1479
- contents: [
1480
- {
1481
- type: "textPart",
1482
- text: "User uploads PDF file as follows."
1483
- }
1484
- ]
1485
- }
1486
- ]),
1487
- createUserMessage(files)
1488
- ]
1573
+ return startGeneration(setting, checkpoint, {
1574
+ messages: checkpoint.messages
1489
1575
  });
1490
1576
  }
1491
1577
  async function resolvingToolResultLogic({
@@ -1493,24 +1579,22 @@ async function resolvingToolResultLogic({
1493
1579
  checkpoint,
1494
1580
  step
1495
1581
  }) {
1496
- if (!step.toolCall || !step.toolResult) {
1497
- throw new Error("No tool call or tool result found");
1582
+ if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
1583
+ throw new Error("No tool calls or tool results found");
1498
1584
  }
1499
- const { id, toolName } = step.toolCall;
1500
- const { result } = step.toolResult;
1585
+ const toolResultParts = step.toolResults.map((toolResult) => {
1586
+ const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
1587
+ return {
1588
+ type: "toolResultPart",
1589
+ toolCallId: toolResult.id,
1590
+ toolName: toolCall?.toolName ?? toolResult.toolName,
1591
+ contents: toolResult.result.filter(
1592
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1593
+ )
1594
+ };
1595
+ });
1501
1596
  return finishToolCall(setting, checkpoint, {
1502
- newMessages: [
1503
- createToolMessage([
1504
- {
1505
- type: "toolResultPart",
1506
- toolCallId: id,
1507
- toolName,
1508
- contents: result.filter(
1509
- (part) => part.type === "textPart" || part.type === "imageInlinePart"
1510
- )
1511
- }
1512
- ])
1513
- ]
1597
+ newMessages: [createToolMessage(toolResultParts)]
1514
1598
  });
1515
1599
  }
1516
1600
 
@@ -1552,7 +1636,9 @@ var runtimeStateMachine = setup({
1552
1636
  checkpoint: ({ context, event }) => ({
1553
1637
  ...context.checkpoint,
1554
1638
  status: "proceeding",
1555
- messages: [...context.checkpoint.messages, ...event.inputMessages]
1639
+ messages: [...context.checkpoint.messages, ...event.inputMessages],
1640
+ pendingToolCalls: event.initialCheckpoint.pendingToolCalls,
1641
+ partialToolResults: event.initialCheckpoint.partialToolResults
1556
1642
  }),
1557
1643
  step: ({ context, event }) => ({
1558
1644
  ...context.step,
@@ -1575,6 +1661,38 @@ var runtimeStateMachine = setup({
1575
1661
  startedAt: event.timestamp
1576
1662
  })
1577
1663
  })
1664
+ },
1665
+ resumeToolCalls: {
1666
+ target: "CallingTool",
1667
+ actions: assign({
1668
+ step: ({ context, event }) => ({
1669
+ stepNumber: context.checkpoint.stepNumber,
1670
+ inputMessages: context.step.inputMessages ?? [],
1671
+ newMessages: context.step.newMessages,
1672
+ toolCalls: context.step.toolCalls,
1673
+ toolResults: event.partialToolResults,
1674
+ pendingToolCalls: event.pendingToolCalls,
1675
+ usage: context.step.usage,
1676
+ startedAt: context.step.startedAt
1677
+ })
1678
+ })
1679
+ },
1680
+ finishAllToolCalls: {
1681
+ target: "FinishingStep",
1682
+ actions: assign({
1683
+ checkpoint: ({ context, event }) => ({
1684
+ ...context.checkpoint,
1685
+ messages: [...context.checkpoint.messages, ...event.newMessages],
1686
+ pendingToolCalls: void 0,
1687
+ partialToolResults: void 0
1688
+ }),
1689
+ step: ({ context, event }) => ({
1690
+ ...context.step,
1691
+ newMessages: [...context.step.newMessages, ...event.newMessages],
1692
+ toolResults: context.checkpoint.partialToolResults,
1693
+ pendingToolCalls: void 0
1694
+ })
1695
+ })
1578
1696
  }
1579
1697
  }
1580
1698
  },
@@ -1591,13 +1709,13 @@ var runtimeStateMachine = setup({
1591
1709
  step: ({ context, event }) => ({
1592
1710
  ...context.step,
1593
1711
  newMessages: event.newMessages,
1594
- toolCall: event.toolCall,
1595
- toolResult: event.toolResult,
1712
+ toolCalls: event.toolCalls,
1713
+ toolResults: event.toolResults,
1596
1714
  usage: sumUsage(context.step.usage, event.usage)
1597
1715
  })
1598
1716
  })
1599
1717
  },
1600
- callTool: {
1718
+ callTools: {
1601
1719
  target: "CallingTool",
1602
1720
  actions: assign({
1603
1721
  checkpoint: ({ context, event }) => ({
@@ -1609,7 +1727,7 @@ var runtimeStateMachine = setup({
1609
1727
  step: ({ context, event }) => ({
1610
1728
  ...context.step,
1611
1729
  newMessages: [event.newMessage],
1612
- toolCall: event.toolCall,
1730
+ toolCalls: event.toolCalls,
1613
1731
  usage: sumUsage(context.step.usage, event.usage)
1614
1732
  })
1615
1733
  })
@@ -1626,7 +1744,7 @@ var runtimeStateMachine = setup({
1626
1744
  step: ({ context, event }) => ({
1627
1745
  ...context.step,
1628
1746
  newMessages: [event.newMessage],
1629
- toolCall: event.toolCall,
1747
+ toolCalls: [event.toolCall],
1630
1748
  usage: sumUsage(context.step.usage, event.usage)
1631
1749
  })
1632
1750
  })
@@ -1643,7 +1761,7 @@ var runtimeStateMachine = setup({
1643
1761
  step: ({ context, event }) => ({
1644
1762
  ...context.step,
1645
1763
  newMessages: [event.newMessage],
1646
- toolCall: event.toolCall,
1764
+ toolCalls: [event.toolCall],
1647
1765
  usage: sumUsage(context.step.usage, event.usage)
1648
1766
  })
1649
1767
  })
@@ -1652,12 +1770,13 @@ var runtimeStateMachine = setup({
1652
1770
  },
1653
1771
  CallingTool: {
1654
1772
  on: {
1655
- resolveToolResult: {
1773
+ resolveToolResults: {
1656
1774
  target: "ResolvingToolResult",
1657
1775
  actions: assign({
1658
1776
  step: ({ context, event }) => ({
1659
1777
  ...context.step,
1660
- toolResult: event.toolResult
1778
+ toolResults: event.toolResults,
1779
+ pendingToolCalls: void 0
1661
1780
  })
1662
1781
  })
1663
1782
  },
@@ -1666,34 +1785,40 @@ var runtimeStateMachine = setup({
1666
1785
  actions: assign({
1667
1786
  step: ({ context, event }) => ({
1668
1787
  ...context.step,
1669
- toolResult: event.toolResult
1788
+ toolResults: [event.toolResult]
1670
1789
  })
1671
1790
  })
1672
1791
  },
1673
- resolvePdfFile: {
1674
- target: "ResolvingPdfFile",
1792
+ attemptCompletion: {
1793
+ target: "GeneratingRunResult",
1675
1794
  actions: assign({
1676
1795
  step: ({ context, event }) => ({
1677
1796
  ...context.step,
1678
- toolResult: event.toolResult
1797
+ toolResults: [event.toolResult]
1679
1798
  })
1680
1799
  })
1681
1800
  },
1682
- resolveImageFile: {
1683
- target: "ResolvingImageFile",
1801
+ callDelegate: {
1802
+ target: "CallingDelegate",
1684
1803
  actions: assign({
1685
- step: ({ context, event }) => ({
1804
+ step: ({ context }) => ({
1686
1805
  ...context.step,
1687
- toolResult: event.toolResult
1806
+ toolCalls: context.step.toolCalls,
1807
+ toolResults: context.step.toolResults,
1808
+ pendingToolCalls: context.step.pendingToolCalls,
1809
+ partialToolResults: context.step.partialToolResults
1688
1810
  })
1689
1811
  })
1690
1812
  },
1691
- attemptCompletion: {
1692
- target: "GeneratingRunResult",
1813
+ callInteractiveTool: {
1814
+ target: "CallingInteractiveTool",
1693
1815
  actions: assign({
1694
- step: ({ context, event }) => ({
1816
+ step: ({ context }) => ({
1695
1817
  ...context.step,
1696
- toolResult: event.toolResult
1818
+ toolCalls: context.step.toolCalls,
1819
+ toolResults: context.step.toolResults,
1820
+ pendingToolCalls: context.step.pendingToolCalls,
1821
+ partialToolResults: context.step.partialToolResults
1697
1822
  })
1698
1823
  })
1699
1824
  }
@@ -1733,40 +1858,6 @@ var runtimeStateMachine = setup({
1733
1858
  }
1734
1859
  }
1735
1860
  },
1736
- ResolvingPdfFile: {
1737
- on: {
1738
- finishToolCall: {
1739
- target: "FinishingStep",
1740
- actions: assign({
1741
- checkpoint: ({ context, event }) => ({
1742
- ...context.checkpoint,
1743
- messages: [...context.checkpoint.messages, ...event.newMessages]
1744
- }),
1745
- step: ({ context, event }) => ({
1746
- ...context.step,
1747
- newMessages: [...context.step.newMessages, ...event.newMessages]
1748
- })
1749
- })
1750
- }
1751
- }
1752
- },
1753
- ResolvingImageFile: {
1754
- on: {
1755
- finishToolCall: {
1756
- target: "FinishingStep",
1757
- actions: assign({
1758
- checkpoint: ({ context, event }) => ({
1759
- ...context.checkpoint,
1760
- messages: [...context.checkpoint.messages, ...event.newMessages]
1761
- }),
1762
- step: ({ context, event }) => ({
1763
- ...context.step,
1764
- newMessages: [...context.step.newMessages, ...event.newMessages]
1765
- })
1766
- })
1767
- }
1768
- }
1769
- },
1770
1861
  GeneratingRunResult: {
1771
1862
  on: {
1772
1863
  retry: {
@@ -1780,8 +1871,8 @@ var runtimeStateMachine = setup({
1780
1871
  step: ({ context, event }) => ({
1781
1872
  ...context.step,
1782
1873
  newMessages: event.newMessages,
1783
- toolCall: event.toolCall,
1784
- toolResult: event.toolResult,
1874
+ toolCalls: event.toolCalls,
1875
+ toolResults: event.toolResults,
1785
1876
  usage: sumUsage(context.step.usage, event.usage)
1786
1877
  })
1787
1878
  })
@@ -1863,8 +1954,6 @@ var StateMachineLogics = {
1863
1954
  CallingTool: callingToolLogic,
1864
1955
  ResolvingToolResult: resolvingToolResultLogic,
1865
1956
  ResolvingThought: resolvingThoughtLogic,
1866
- ResolvingPdfFile: resolvingPdfFileLogic,
1867
- ResolvingImageFile: resolvingImageFileLogic,
1868
1957
  GeneratingRunResult: generatingRunResultLogic,
1869
1958
  CallingInteractiveTool: callingInteractiveToolLogic,
1870
1959
  CallingDelegate: callingDelegateLogic,