@perstack/runtime 0.0.20 → 0.0.22

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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { existsSync } from "fs";
3
3
  import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
4
4
  import path from "path";
5
- import { createId as createId7 } from "@paralleldrive/cuid2";
5
+ import { createId as createId8 } from "@paralleldrive/cuid2";
6
6
  import TOML from "smol-toml";
7
7
  import { createActor } from "xstate";
8
8
 
@@ -33,30 +33,29 @@ var AnthropicModels = [
33
33
  // "claude-3-haiku-20240307", - No pdf support
34
34
  ];
35
35
  var GoogleModels = [
36
- "gemini-2.5-pro-preview-05-06",
37
- "gemini-2.5-pro-exp-03-25",
38
- "gemini-2.5-flash-preview-04-17",
39
- "gemini-2.0-pro-exp-02-05",
40
- "gemini-2.0-flash-thinking-exp-01-21",
41
- "gemini-2.0-flash",
42
- "gemini-2.0-flash-001",
43
- "gemini-2.0-flash-live-001",
44
- "gemini-2.0-flash-lite",
45
- "gemini-2.0-flash-exp",
46
- "gemini-1.5-pro",
47
- "gemini-1.5-pro-latest",
48
- "gemini-1.5-pro-001",
49
- "gemini-1.5-pro-002",
50
36
  "gemini-1.5-flash",
51
37
  "gemini-1.5-flash-latest",
52
38
  "gemini-1.5-flash-001",
53
39
  "gemini-1.5-flash-002",
54
40
  "gemini-1.5-flash-8b",
55
41
  "gemini-1.5-flash-8b-latest",
56
- "gemini-1.5-flash-8b-001"
57
- // "gemini-exp-1206", - Unclear model spec
58
- // "gemma-3-27b-it", - Unclear model spec
59
- // "learnlm-1.5-pro-experimental", - Unclear model spec
42
+ "gemini-1.5-flash-8b-001",
43
+ "gemini-1.5-pro",
44
+ "gemini-1.5-pro-latest",
45
+ "gemini-1.5-pro-001",
46
+ "gemini-1.5-pro-002",
47
+ "gemini-2.0-flash",
48
+ "gemini-2.0-flash-001",
49
+ "gemini-2.0-flash-live-001",
50
+ "gemini-2.0-flash-lite",
51
+ "gemini-2.0-pro-exp-02-05",
52
+ "gemini-2.0-flash-thinking-exp-01-21",
53
+ "gemini-2.0-flash-exp",
54
+ "gemini-2.5-pro",
55
+ "gemini-2.5-flash",
56
+ "gemini-2.5-flash-lite",
57
+ "gemini-2.5-pro-exp-03-25",
58
+ "gemini-2.5-flash-preview-04-17"
60
59
  ];
61
60
  var OpenAIModels = [
62
61
  "o4-mini",
@@ -301,7 +300,10 @@ var ExpertSchema = z2.object({
301
300
  type: "mcpStdioSkill",
302
301
  description: "Base skill",
303
302
  command: "npx",
304
- args: ["-y", "@perstack/base"]
303
+ args: ["-y", "@perstack/base"],
304
+ pick: [],
305
+ omit: [],
306
+ requiredEnv: []
305
307
  }
306
308
  }).transform((skills) => {
307
309
  return Object.fromEntries(
@@ -393,7 +395,8 @@ var RunParamsSchema = z2.object({
393
395
  model: z2.string().optional().default(getDefaultModelName()),
394
396
  temperature: z2.number().min(0).max(1).optional().default(0.3),
395
397
  maxSteps: z2.number().min(1).optional(),
396
- maxRetries: z2.number().min(0).optional().default(10),
398
+ maxRetries: z2.number().min(0).optional().default(5),
399
+ timeout: z2.number().min(0).optional().default(1e3 * 60),
397
400
  workspace: z2.string().optional(),
398
401
  startedAt: z2.number().optional().default(Date.now()),
399
402
  updatedAt: z2.number().optional().default(Date.now())
@@ -463,7 +466,7 @@ async function defaultStoreEvent(event) {
463
466
  // package.json
464
467
  var package_default = {
465
468
  name: "@perstack/runtime",
466
- version: "0.0.20",
469
+ version: "0.0.22",
467
470
  description: "PerStack Runtime",
468
471
  author: "Wintermute Technologies, Inc.",
469
472
  license: "Apache-2.0",
@@ -487,23 +490,23 @@ var package_default = {
487
490
  prepublishOnly: "npm run clean && npm run build"
488
491
  },
489
492
  dependencies: {
490
- "@ai-sdk/anthropic": "^1.2.12",
491
- "@ai-sdk/google": "^1.2.19",
492
- "@ai-sdk/openai": "^1.3.22",
493
- "@modelcontextprotocol/sdk": "^1.12.3",
493
+ "@ai-sdk/anthropic": "^2.0.1",
494
+ "@ai-sdk/google": "^2.0.2",
495
+ "@ai-sdk/openai": "^2.0.3",
496
+ "@modelcontextprotocol/sdk": "^1.17.1",
494
497
  "@paralleldrive/cuid2": "^2.2.2",
495
- ai: "^4.3.16",
496
- "smol-toml": "^1.3.4",
498
+ ai: "^5.0.4",
499
+ "smol-toml": "^1.4.1",
497
500
  "ts-dedent": "^2.2.0",
498
- xstate: "^5.19.4",
499
- zod: "^3.25.67"
501
+ xstate: "^5.20.1",
502
+ zod: "^4.0.14"
500
503
  },
501
504
  devDependencies: {
502
505
  "@tsconfig/node22": "^22.0.2",
503
- "@types/node": "^24.0.3",
506
+ "@types/node": "^24.2.0",
504
507
  tsup: "^8.5.0",
505
- typescript: "^5.8.3",
506
- vitest: "^3.2.3"
508
+ typescript: "^5.9.2",
509
+ vitest: "^3.2.4"
507
510
  },
508
511
  engines: {
509
512
  node: ">=22.0.0"
@@ -523,7 +526,7 @@ async function defaultEventListener(e) {
523
526
  await defaultStoreEvent(e);
524
527
  switch (e.type) {
525
528
  case "startRun": {
526
- log(`${header(e)} \u{1F99C} Perstack@${package_default.version} started`);
529
+ log(`${header(e)} Perstack@${package_default.version} started`);
527
530
  const { inputMessages } = e;
528
531
  for (const message of inputMessages) {
529
532
  if (message.type === "userMessage") {
@@ -535,122 +538,122 @@ async function defaultEventListener(e) {
535
538
  break;
536
539
  }
537
540
  case "startGeneration": {
538
- log(`${header(e)} \u{1F99C} Generating tool call`);
541
+ log(`${header(e)} Generating tool call`);
539
542
  break;
540
543
  }
541
544
  case "retry": {
542
545
  logUsage(e);
543
- log(`${header(e)} \u{1F99C} Retrying tool call generation`);
546
+ log(`${header(e)} Retrying tool call generation`);
544
547
  break;
545
548
  }
546
549
  case "callTool": {
547
550
  logUsage(e);
548
- log(`${header(e)} \u{1F99C} Calling tool`);
551
+ log(`${header(e)} Calling tool`);
549
552
  if (e.toolCall.skillName === "@perstack/base") {
550
553
  switch (e.toolCall.toolName) {
551
554
  case "think": {
552
555
  const thought = e.toolCall.args.thought;
553
- log(`${header(e)} \u{1F4AD} Thought Updated:`);
556
+ log(`${header(e)} Thought Updated:`);
554
557
  debug(thought);
555
558
  break;
556
559
  }
557
560
  case "readPdfFile": {
558
561
  const path2 = e.toolCall.args.path;
559
- log(`${header(e)} \u{1F4C4} Reading PDF: ${path2}`);
562
+ log(`${header(e)} Reading PDF: ${path2}`);
560
563
  break;
561
564
  }
562
565
  case "readImageFile": {
563
566
  const path2 = e.toolCall.args.path;
564
- log(`${header(e)} \u{1F5BC}\uFE0F Reading Image: ${path2}`);
567
+ log(`${header(e)} Reading Image: ${path2}`);
565
568
  break;
566
569
  }
567
570
  default: {
568
- log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
569
- log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
571
+ log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
572
+ debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
570
573
  break;
571
574
  }
572
575
  }
573
576
  } else {
574
- log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
575
- log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
577
+ log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
578
+ debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
576
579
  }
577
580
  break;
578
581
  }
579
582
  case "callInteractiveTool": {
580
583
  logUsage(e);
581
- log(`${header(e)} \u{1F99C} Calling interactive tool`);
582
- log(`${header(e)} \u{1F527} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
583
- log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
584
+ log(`${header(e)} Calling interactive tool`);
585
+ log(`${header(e)} Tool: ${e.toolCall.skillName}/${e.toolCall.toolName}`);
586
+ debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
584
587
  break;
585
588
  }
586
589
  case "callDelegate": {
587
590
  logUsage(e);
588
- log(`${header(e)} \u{1F99C} Calling delegate`);
589
- log(`${header(e)} \u{1F527} Tool: ${e.toolCall.toolName}`);
590
- log(`${header(e)} \u{1F527} Args: ${e.toolCall.args}`);
591
+ log(`${header(e)} Calling delegate`);
592
+ log(`${header(e)} Tool: ${e.toolCall.toolName}`);
593
+ debug(`${header(e)} Args: ${JSON.stringify(e.toolCall.args, null, 2)}`);
591
594
  break;
592
595
  }
593
596
  case "resolveToolResult": {
594
- log(`${header(e)} \u{1F527} Resolved Tool Result`);
597
+ log(`${header(e)} Resolved Tool Result`);
595
598
  if (e.toolResult.skillName === "@perstack/base") {
596
599
  switch (e.toolResult.toolName) {
597
600
  case "todo": {
598
601
  const text = e.toolResult.result.find((r) => r.type === "textPart")?.text;
599
602
  const { todos } = JSON.parse(text ?? "{}");
600
- log(`${header(e)} \u{1F4C4} Todo:`);
603
+ log(`${header(e)} Todo:`);
601
604
  for (const todo of todos) {
602
605
  debug(`${todo.completed ? "[x]" : "[ ]"} ${todo.id}: ${todo.title}`);
603
606
  }
604
607
  break;
605
608
  }
606
609
  default: {
607
- log(`${header(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
608
- log(`${header(e)} \u{1F527} Result: ${e.toolResult.result}`);
610
+ log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
611
+ debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
609
612
  break;
610
613
  }
611
614
  }
612
615
  } else {
613
- log(`${header(e)} \u{1F527} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
614
- log(`${header(e)} \u{1F527} Result: ${e.toolResult.result}`);
616
+ log(`${header(e)} Tool: ${e.toolResult.skillName}/${e.toolResult.toolName}`);
617
+ debug(`${header(e)} Result: ${JSON.stringify(e.toolResult.result, null, 2)}`);
615
618
  }
616
619
  break;
617
620
  }
618
621
  case "resolveThought": {
619
- log(`${header(e)} \u{1F4AD} Resolved Thought:`, e.toolResult);
622
+ log(`${header(e)} Resolved Thought:`, e.toolResult);
620
623
  break;
621
624
  }
622
625
  case "resolvePdfFile": {
623
- log(`${header(e)} \u{1F4C4} Resolved PDF:`, e.toolResult);
626
+ log(`${header(e)} Resolved PDF:`, e.toolResult);
624
627
  break;
625
628
  }
626
629
  case "resolveImageFile": {
627
- log(`${header(e)} \u{1F5BC}\uFE0F Resolved Image:`, e.toolResult);
630
+ log(`${header(e)} Resolved Image:`, e.toolResult);
628
631
  break;
629
632
  }
630
633
  case "attemptCompletion": {
631
- log(`${header(e)} \u2705 Attempting completion`);
634
+ log(`${header(e)} Attempting completion`);
632
635
  break;
633
636
  }
634
637
  case "completeRun": {
635
638
  logUsage(e);
636
- log(`${header(e)} \u{1F99C} Completing run`);
637
- log(`${header(e)} \u{1F99C} Result:`, e.text);
639
+ log(`${header(e)} Completing run`);
640
+ debug(`${header(e)} Result:`, e.text);
638
641
  break;
639
642
  }
640
643
  case "stopRunByInteractiveTool": {
641
- log(`${header(e)} \u{1F99C} Stopping run by interactive tool`);
644
+ log(`${header(e)} Stopping run by interactive tool`);
642
645
  break;
643
646
  }
644
647
  case "stopRunByDelegate": {
645
- log(`${header(e)} \u{1F99C} Stopping run by delegate`);
648
+ log(`${header(e)} Stopping run by delegate`);
646
649
  break;
647
650
  }
648
651
  case "stopRunByExceededMaxSteps": {
649
- log(`${header(e)} \u{1F99C} Stopping run by exceeded max steps`);
652
+ log(`${header(e)} Stopping run by exceeded max steps`);
650
653
  break;
651
654
  }
652
655
  case "continueToNextStep": {
653
- log(`${header(e)} \u{1F99C} Continuing to next step`);
656
+ log(`${header(e)} Continuing to next step`);
654
657
  break;
655
658
  }
656
659
  }
@@ -660,19 +663,19 @@ function logUserMessage(message) {
660
663
  const contents = message.contents;
661
664
  for (const content of contents) {
662
665
  if (content.type === "textPart") {
663
- log(`${t} \u{1F4AC} User: ${content.text}`);
666
+ log(`${t} User: ${content.text}`);
664
667
  } else if (content.type === "imageUrlPart") {
665
- log(`${t} \u{1F5BC}\uFE0F User: ${content.url}`);
668
+ log(`${t} User: ${content.url}`);
666
669
  } else if (content.type === "imageInlinePart") {
667
- log(`${t} \u{1F5BC}\uFE0F User: Inline image`);
670
+ log(`${t} User: Inline image`);
668
671
  } else if (content.type === "imageBinaryPart") {
669
- log(`${t} \u{1F5BC}\uFE0F User: Binary image`);
672
+ log(`${t} User: Binary image`);
670
673
  } else if (content.type === "fileUrlPart") {
671
- log(`${t} \u{1F4C4} User: ${content.url}`);
674
+ log(`${t} User: ${content.url}`);
672
675
  } else if (content.type === "fileInlinePart") {
673
- log(`${t} \u{1F4C4} User: Inline file`);
676
+ log(`${t} User: Inline file`);
674
677
  } else if (content.type === "fileBinaryPart") {
675
- log(`${t} \u{1F4C4} User: Binary file`);
678
+ log(`${t} User: Binary file`);
676
679
  }
677
680
  }
678
681
  }
@@ -684,9 +687,9 @@ function logToolMessage(message) {
684
687
  const { contents: contents2 } = content;
685
688
  for (const content2 of contents2) {
686
689
  if (content2.type === "textPart") {
687
- log(`${t} \u{1F4AC} Tool Result: ${content2.text}`);
690
+ log(`${t} Tool Result: ${content2.text}`);
688
691
  } else if (content2.type === "imageInlinePart") {
689
- log(`${t} \u{1F5BC}\uFE0F Tool Result: Inline image`);
692
+ log(`${t} Tool Result: Inline image`);
690
693
  }
691
694
  }
692
695
  }
@@ -700,7 +703,7 @@ function logUsage(e) {
700
703
  `Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
701
704
  `Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
702
705
  ].join(", ");
703
- log(`${header(e)} \u{1F4CA} Tokens usage: ${usageByGeneration}`);
706
+ log(`${header(e)} Tokens usage: ${usageByGeneration}`);
704
707
  const usageByRun = [
705
708
  `In: ${e.usage.promptTokens.toLocaleString()}`,
706
709
  `Out: ${e.usage.completionTokens.toLocaleString()}`,
@@ -708,7 +711,7 @@ function logUsage(e) {
708
711
  `Cache-read: ${e.usage.cacheReadInputTokens.toLocaleString()}`,
709
712
  `Cache-write: ${e.usage.cacheCreationInputTokens.toLocaleString()}`
710
713
  ].join(", ");
711
- log(`${header(e)} \u{1F4CA} Total usage: ${usageByRun}`);
714
+ log(`${header(e)} Total usage: ${usageByRun}`);
712
715
  }
713
716
 
714
717
  // src/events/event-emitter.ts
@@ -749,8 +752,8 @@ var RegistryV1ExpertSchema = z3.object({
749
752
  tags: z3.array(z3.string()),
750
753
  status: z3.string(),
751
754
  owner: z3.object({ name: z3.string() }),
752
- createdAt: z3.string().datetime(),
753
- updatedAt: z3.string().datetime()
755
+ createdAt: z3.iso.datetime(),
756
+ updatedAt: z3.iso.datetime()
754
757
  });
755
758
  var RegistryV1ExpertsGetResponseSchema = z3.object({
756
759
  data: z3.object({
@@ -818,7 +821,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
818
821
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
819
822
  import { McpError } from "@modelcontextprotocol/sdk/types.js";
820
823
  import { createId as createId3 } from "@paralleldrive/cuid2";
821
- import { jsonSchema } from "ai";
824
+ import { jsonSchema, tool } from "ai";
822
825
  import { ZodError } from "zod";
823
826
  var SkillManager = class {
824
827
  _toolDefinitions = [];
@@ -895,11 +898,11 @@ var SkillManager = class {
895
898
  }
896
899
  }
897
900
  const { tools } = await this._mcpClient.listTools();
898
- this._toolDefinitions = tools.map((tool) => ({
901
+ this._toolDefinitions = tools.map((tool2) => ({
899
902
  skillName: params.skill.name,
900
- name: tool.name,
901
- description: tool.description,
902
- inputSchema: tool.inputSchema,
903
+ name: tool2.name,
904
+ description: tool2.description,
905
+ inputSchema: tool2.inputSchema,
903
906
  interactive: false
904
907
  }));
905
908
  this._initialized = true;
@@ -921,11 +924,11 @@ var SkillManager = class {
921
924
  return { command, args: newArgs };
922
925
  }
923
926
  async _initInteractiveSkill(params) {
924
- this._toolDefinitions = Object.values(params.interactiveSkill.tools).map((tool) => ({
927
+ this._toolDefinitions = Object.values(params.interactiveSkill.tools).map((tool2) => ({
925
928
  skillName: params.interactiveSkill.name,
926
- name: tool.name,
927
- description: tool.description,
928
- inputSchema: JSON.parse(tool.inputJsonSchema),
929
+ name: tool2.name,
930
+ description: tool2.description,
931
+ inputSchema: JSON.parse(tool2.inputJsonSchema),
929
932
  interactive: true
930
933
  }));
931
934
  this._initialized = true;
@@ -958,7 +961,7 @@ var SkillManager = class {
958
961
  if (this._params.type === "mcp") {
959
962
  const omit = this._params.skill.omit ?? [];
960
963
  const pick = this._params.skill.pick ?? [];
961
- return this._toolDefinitions.filter((tool) => omit.length > 0 ? !omit.includes(tool.name) : true).filter((tool) => pick.length > 0 ? pick.includes(tool.name) : true);
964
+ return this._toolDefinitions.filter((tool2) => omit.length > 0 ? !omit.includes(tool2.name) : true).filter((tool2) => pick.length > 0 ? pick.includes(tool2.name) : true);
962
965
  }
963
966
  return this._toolDefinitions;
964
967
  }
@@ -1133,10 +1136,10 @@ async function getToolSet(skillManagers) {
1133
1136
  for (const skillManager of Object.values(skillManagers)) {
1134
1137
  const toolDefinitions = await skillManager.getToolDefinitions();
1135
1138
  for (const toolDefinition of toolDefinitions) {
1136
- tools[toolDefinition.name] = {
1139
+ tools[toolDefinition.name] = tool({
1137
1140
  description: toolDefinition.description,
1138
- parameters: jsonSchema(toolDefinition.inputSchema)
1139
- };
1141
+ inputSchema: jsonSchema(toolDefinition.inputSchema)
1142
+ });
1140
1143
  }
1141
1144
  }
1142
1145
  return tools;
@@ -1401,42 +1404,42 @@ function imageUrlPartToCoreImagePart(part) {
1401
1404
  return {
1402
1405
  type: "image",
1403
1406
  image: part.url,
1404
- mimeType: part.mimeType
1407
+ mediaType: part.mimeType
1405
1408
  };
1406
1409
  }
1407
1410
  function imageInlinePartToCoreImagePart(part) {
1408
1411
  return {
1409
1412
  type: "image",
1410
1413
  image: part.encodedData,
1411
- mimeType: part.mimeType
1414
+ mediaType: part.mimeType
1412
1415
  };
1413
1416
  }
1414
1417
  function imageBinaryPartToCoreImagePart(part) {
1415
1418
  return {
1416
1419
  type: "image",
1417
1420
  image: part.data,
1418
- mimeType: part.mimeType
1421
+ mediaType: part.mimeType
1419
1422
  };
1420
1423
  }
1421
1424
  function fileUrlPartToCoreFilePart(part) {
1422
1425
  return {
1423
1426
  type: "file",
1424
1427
  data: part.url,
1425
- mimeType: part.mimeType
1428
+ mediaType: part.mimeType
1426
1429
  };
1427
1430
  }
1428
1431
  function fileInlinePartToCoreFilePart(part) {
1429
1432
  return {
1430
1433
  type: "file",
1431
1434
  data: part.encodedData,
1432
- mimeType: part.mimeType
1435
+ mediaType: part.mimeType
1433
1436
  };
1434
1437
  }
1435
1438
  function fileBinaryPartToCoreFilePart(part) {
1436
1439
  return {
1437
1440
  type: "file",
1438
1441
  data: part.data,
1439
- mimeType: part.mimeType
1442
+ mediaType: part.mimeType
1440
1443
  };
1441
1444
  }
1442
1445
  function toolCallPartToCoreToolCallPart(part) {
@@ -1444,31 +1447,29 @@ function toolCallPartToCoreToolCallPart(part) {
1444
1447
  type: "tool-call",
1445
1448
  toolCallId: part.toolCallId,
1446
1449
  toolName: part.toolName,
1447
- args: part.args
1450
+ input: part.args
1448
1451
  };
1449
1452
  }
1450
1453
  function toolResultPartToCoreToolResultPart(part) {
1454
+ const content = part.contents[0];
1455
+ const output = content.type === "textPart" ? {
1456
+ type: "text",
1457
+ value: content.text
1458
+ } : {
1459
+ type: "content",
1460
+ value: [
1461
+ {
1462
+ type: "media",
1463
+ data: content.encodedData,
1464
+ mediaType: content.mimeType
1465
+ }
1466
+ ]
1467
+ };
1451
1468
  return {
1452
1469
  type: "tool-result",
1453
1470
  toolCallId: part.toolCallId,
1454
1471
  toolName: part.toolName,
1455
- result: part.contents,
1456
- experimental_content: part.contents.map((part2) => {
1457
- switch (part2.type) {
1458
- case "textPart":
1459
- return {
1460
- type: "text",
1461
- text: part2.text
1462
- };
1463
- case "imageInlinePart":
1464
- return {
1465
- type: "image",
1466
- data: part2.encodedData,
1467
- mimeType: part2.mimeType
1468
- };
1469
- }
1470
- }),
1471
- isError: part.isError
1472
+ output
1472
1473
  };
1473
1474
  }
1474
1475
 
@@ -1491,9 +1492,9 @@ function usageFromGenerateTextResult(result) {
1491
1492
  cacheReadInputTokens = anthropicMetadata.cacheReadInputTokens || 0;
1492
1493
  }
1493
1494
  return {
1494
- promptTokens: result.usage.promptTokens,
1495
- completionTokens: result.usage.completionTokens,
1496
- totalTokens: result.usage.totalTokens,
1495
+ promptTokens: result.usage.inputTokens || 0,
1496
+ completionTokens: result.usage.outputTokens || 0,
1497
+ totalTokens: result.usage.totalTokens || 0,
1497
1498
  cacheCreationInputTokens,
1498
1499
  cacheReadInputTokens
1499
1500
  };
@@ -1531,12 +1532,32 @@ async function generatingRunResultLogic({
1531
1532
  ]);
1532
1533
  const model = getModel(setting.model);
1533
1534
  const { messages } = checkpoint;
1534
- const generationResult = await generateText({
1535
- model,
1536
- messages: [...messages, toolMessage].map(messageToCoreMessage),
1537
- temperature: setting.temperature,
1538
- maxRetries: setting.maxRetries
1539
- });
1535
+ let generationResult;
1536
+ try {
1537
+ generationResult = await generateText({
1538
+ model,
1539
+ messages: [...messages, toolMessage].map(messageToCoreMessage),
1540
+ temperature: setting.temperature,
1541
+ maxRetries: setting.maxRetries,
1542
+ abortSignal: AbortSignal.timeout(setting.timeout)
1543
+ });
1544
+ } catch (error) {
1545
+ if (error instanceof Error) {
1546
+ return retry(setting, checkpoint, {
1547
+ newMessages: [
1548
+ toolMessage,
1549
+ createUserMessage([
1550
+ {
1551
+ type: "textPart",
1552
+ text: JSON.stringify({ error: error.name, message: error.message })
1553
+ }
1554
+ ])
1555
+ ],
1556
+ usage: checkpoint.usage
1557
+ });
1558
+ }
1559
+ throw error;
1560
+ }
1540
1561
  const usage = usageFromGenerateTextResult(generationResult);
1541
1562
  const { text } = generationResult;
1542
1563
  const newMessages = [toolMessage, createExpertMessage(text ? [{ type: "textPart", text }] : [])];
@@ -1559,6 +1580,7 @@ async function generatingRunResultLogic({
1559
1580
  }
1560
1581
 
1561
1582
  // src/states/generating-tool-call.ts
1583
+ import { createId as createId6 } from "@paralleldrive/cuid2";
1562
1584
  import { generateText as generateText2 } from "ai";
1563
1585
  async function generatingToolCallLogic({
1564
1586
  setting,
@@ -1567,66 +1589,58 @@ async function generatingToolCallLogic({
1567
1589
  }) {
1568
1590
  const { messages } = checkpoint;
1569
1591
  const model = getModel(setting.model);
1570
- const result = await generateText2({
1571
- model,
1572
- messages: messages.map(messageToCoreMessage),
1573
- temperature: setting.temperature,
1574
- maxRetries: setting.maxRetries,
1575
- tools: await getToolSet(skillManagers),
1576
- toolChoice: "required"
1577
- });
1592
+ let result;
1593
+ try {
1594
+ result = await generateText2({
1595
+ model,
1596
+ messages: messages.map(messageToCoreMessage),
1597
+ temperature: setting.temperature,
1598
+ maxRetries: setting.maxRetries,
1599
+ tools: await getToolSet(skillManagers),
1600
+ toolChoice: "required",
1601
+ abortSignal: AbortSignal.timeout(setting.timeout)
1602
+ });
1603
+ } catch (error) {
1604
+ if (error instanceof Error) {
1605
+ return retry(setting, checkpoint, {
1606
+ newMessages: [
1607
+ createUserMessage([
1608
+ {
1609
+ type: "textPart",
1610
+ text: JSON.stringify({ error: error.name, message: error.message })
1611
+ }
1612
+ ])
1613
+ ],
1614
+ usage: checkpoint.usage
1615
+ });
1616
+ }
1617
+ throw error;
1618
+ }
1578
1619
  const usage = usageFromGenerateTextResult(result);
1579
1620
  const { text, toolCalls, finishReason } = result;
1580
1621
  const toolCall = toolCalls[0];
1581
1622
  if (!toolCall) {
1582
1623
  return retry(setting, checkpoint, {
1583
1624
  newMessages: [
1584
- createExpertMessage(text ? [{ type: "textPart", text }] : []),
1585
1625
  createUserMessage([
1586
1626
  {
1587
1627
  type: "textPart",
1588
- text: "You must generate a tool call. Try again."
1628
+ text: JSON.stringify({
1629
+ error: "No tool call generated",
1630
+ message: "You must generate a tool call. Try again."
1631
+ })
1589
1632
  }
1590
1633
  ])
1591
1634
  ],
1592
1635
  usage
1593
1636
  });
1594
1637
  }
1595
- if (finishReason !== "tool-calls") {
1596
- switch (finishReason) {
1597
- case "length": {
1598
- return retry(setting, checkpoint, {
1599
- newMessages: [
1600
- createExpertMessage([
1601
- {
1602
- type: "toolCallPart",
1603
- toolCallId: toolCall.toolCallId,
1604
- toolName: toolCall.toolName,
1605
- args: toolCall.args
1606
- }
1607
- ]),
1608
- createToolMessage([
1609
- {
1610
- type: "toolResultPart",
1611
- toolCallId: toolCall.toolCallId,
1612
- toolName: toolCall.toolName,
1613
- contents: [{ type: "textPart", text: "Error: Generation length exceeded" }]
1614
- }
1615
- ])
1616
- ],
1617
- usage
1618
- });
1619
- }
1620
- default:
1621
- throw new Error(`Unexpected finish reason: ${finishReason}`);
1622
- }
1623
- }
1624
1638
  const contents = [
1625
1639
  {
1626
1640
  type: "toolCallPart",
1627
1641
  toolCallId: toolCall.toolCallId,
1628
1642
  toolName: toolCall.toolName,
1629
- args: toolCall.args
1643
+ args: toolCall.input
1630
1644
  }
1631
1645
  ];
1632
1646
  if (text) {
@@ -1642,22 +1656,55 @@ async function generatingToolCallLogic({
1642
1656
  id: toolCall.toolCallId,
1643
1657
  skillName: skillManager.name,
1644
1658
  toolName: toolCall.toolName,
1645
- args: toolCall.args
1659
+ args: toolCall.input
1646
1660
  },
1647
1661
  usage
1648
1662
  };
1649
- switch (skillManager.type) {
1650
- case "mcp":
1651
- return callTool(setting, checkpoint, eventPayload);
1652
- case "interactive":
1653
- return callInteractiveTool(setting, checkpoint, eventPayload);
1654
- case "delegate":
1655
- return callDelegate(setting, checkpoint, eventPayload);
1663
+ if (finishReason === "tool-calls") {
1664
+ switch (skillManager.type) {
1665
+ case "mcp":
1666
+ return callTool(setting, checkpoint, eventPayload);
1667
+ case "interactive":
1668
+ return callInteractiveTool(setting, checkpoint, eventPayload);
1669
+ case "delegate":
1670
+ return callDelegate(setting, checkpoint, eventPayload);
1671
+ }
1656
1672
  }
1673
+ if (finishReason === "length") {
1674
+ return retry(setting, checkpoint, {
1675
+ newMessages: [
1676
+ createExpertMessage([
1677
+ {
1678
+ type: "toolCallPart",
1679
+ toolCallId: toolCall.toolCallId,
1680
+ toolName: toolCall.toolName,
1681
+ args: toolCall.input
1682
+ }
1683
+ ]),
1684
+ createToolMessage([
1685
+ {
1686
+ type: "toolResultPart",
1687
+ toolCallId: toolCall.toolCallId,
1688
+ toolName: toolCall.toolName,
1689
+ contents: [{ type: "textPart", text: "Error: Generation length exceeded" }]
1690
+ }
1691
+ ])
1692
+ ],
1693
+ toolCall: eventPayload.toolCall,
1694
+ toolResult: {
1695
+ id: toolCall.toolCallId,
1696
+ skillName: skillManager.name,
1697
+ toolName: toolCall.toolName,
1698
+ result: [{ type: "textPart", id: createId6(), text: "Error: Generation length exceeded" }]
1699
+ },
1700
+ usage
1701
+ });
1702
+ }
1703
+ throw new Error(`Unexpected finish reason: ${finishReason}`);
1657
1704
  }
1658
1705
 
1659
1706
  // src/messages/instruction-message.ts
1660
- import { createId as createId6 } from "@paralleldrive/cuid2";
1707
+ import { createId as createId7 } from "@paralleldrive/cuid2";
1661
1708
  import { dedent as dedent2 } from "ts-dedent";
1662
1709
  var metaInstruction = dedent2`
1663
1710
  IMPORTANT:
@@ -1731,12 +1778,12 @@ function createInstructionMessage(expert, experts) {
1731
1778
  type: "instructionMessage",
1732
1779
  contents: [
1733
1780
  {
1734
- id: createId6(),
1781
+ id: createId7(),
1735
1782
  type: "textPart",
1736
1783
  text: instruction
1737
1784
  }
1738
1785
  ],
1739
- id: createId6(),
1786
+ id: createId7(),
1740
1787
  cache: true
1741
1788
  };
1742
1789
  }
@@ -1797,6 +1844,19 @@ async function initLogic({
1797
1844
  inputMessages: [createUserMessage([{ type: "textPart", text: setting.input.text }])]
1798
1845
  });
1799
1846
  }
1847
+ case "stoppedByDelegate": {
1848
+ if (!setting.input.interactiveToolCallResult) {
1849
+ throw new Error("Interactive tool call result is undefined");
1850
+ }
1851
+ return startRun(setting, checkpoint, {
1852
+ initialCheckpoint: checkpoint,
1853
+ inputMessages: [
1854
+ createUserMessage([
1855
+ { type: "textPart", text: setting.input.interactiveToolCallResult.text }
1856
+ ])
1857
+ ]
1858
+ });
1859
+ }
1800
1860
  case "stoppedByInteractiveTool": {
1801
1861
  if (!setting.input.interactiveToolCallResult) {
1802
1862
  throw new Error("Interactive tool call result is undefined");
@@ -2045,6 +2105,8 @@ var runtimeStateMachine = setup({
2045
2105
  step: ({ context, event }) => ({
2046
2106
  ...context.step,
2047
2107
  newMessages: event.newMessages,
2108
+ toolCall: event.toolCall,
2109
+ toolResult: event.toolResult,
2048
2110
  usage: sumUsage(context.step.usage, event.usage)
2049
2111
  })
2050
2112
  })
@@ -2218,6 +2280,23 @@ var runtimeStateMachine = setup({
2218
2280
  },
2219
2281
  GeneratingRunResult: {
2220
2282
  on: {
2283
+ retry: {
2284
+ target: "FinishingStep",
2285
+ actions: assign({
2286
+ checkpoint: ({ context, event }) => ({
2287
+ ...context.checkpoint,
2288
+ messages: [...context.checkpoint.messages, ...event.newMessages],
2289
+ usage: sumUsage(context.checkpoint.usage, event.usage)
2290
+ }),
2291
+ step: ({ context, event }) => ({
2292
+ ...context.step,
2293
+ newMessages: event.newMessages,
2294
+ toolCall: event.toolCall,
2295
+ toolResult: event.toolResult,
2296
+ usage: sumUsage(context.step.usage, event.usage)
2297
+ })
2298
+ })
2299
+ },
2221
2300
  completeRun: {
2222
2301
  target: "Stopped",
2223
2302
  actions: assign({
@@ -2301,6 +2380,7 @@ var PerstackConfigSchema = z4.object({
2301
2380
  temperature: z4.number().optional(),
2302
2381
  maxSteps: z4.number().optional(),
2303
2382
  maxRetries: z4.number().optional(),
2383
+ timeout: z4.number().optional(),
2304
2384
  experts: z4.record(
2305
2385
  z4.string(),
2306
2386
  z4.object({
@@ -2358,6 +2438,9 @@ async function run(runInput, options) {
2358
2438
  const eventEmitter = new RunEventEmitter();
2359
2439
  eventEmitter.subscribe(eventListener);
2360
2440
  let { setting, checkpoint } = runParams;
2441
+ if (checkpoint) {
2442
+ checkpoint.status = "proceeding";
2443
+ }
2361
2444
  if (setting.workspace) {
2362
2445
  if (!path.isAbsolute(setting.workspace)) {
2363
2446
  throw new Error(`Workspace path must be absolute: ${setting.workspace}`);
@@ -2376,19 +2459,11 @@ async function run(runInput, options) {
2376
2459
  experts
2377
2460
  },
2378
2461
  initialCheckpoint: checkpoint ? {
2379
- id: createId7(),
2380
- runId: setting.runId,
2381
- expert: {
2382
- key: setting.expertKey,
2383
- name: expertToRun.name,
2384
- version: expertToRun.version
2385
- },
2386
- stepNumber: checkpoint.stepNumber + 1,
2387
- status: "proceeding",
2388
- messages: checkpoint.messages,
2389
- usage: checkpoint.usage
2462
+ ...checkpoint,
2463
+ id: createId8(),
2464
+ stepNumber: checkpoint.stepNumber + 1
2390
2465
  } : {
2391
- id: createId7(),
2466
+ id: createId8(),
2392
2467
  runId: setting.runId,
2393
2468
  expert: {
2394
2469
  key: setting.expertKey,
@@ -2468,8 +2543,7 @@ async function run(runInput, options) {
2468
2543
  };
2469
2544
  checkpoint = {
2470
2545
  ...await retrieveCheckpoint(setting.runId, checkpointId),
2471
- id: createId7(),
2472
- stepNumber: runResultCheckpoint.stepNumber + 1,
2546
+ stepNumber: runResultCheckpoint.stepNumber,
2473
2547
  usage: runResultCheckpoint.usage
2474
2548
  };
2475
2549
  break;
@@ -2492,10 +2566,8 @@ async function run(runInput, options) {
2492
2566
  }
2493
2567
  };
2494
2568
  checkpoint = {
2495
- id: createId7(),
2496
- runId: setting.runId,
2569
+ ...runResultCheckpoint,
2497
2570
  status: "init",
2498
- stepNumber: runResultCheckpoint.stepNumber + 1,
2499
2571
  messages: [],
2500
2572
  expert: {
2501
2573
  key: expert.key,
@@ -2540,13 +2612,14 @@ async function setupExperts(setting) {
2540
2612
  return { expertToRun, experts };
2541
2613
  }
2542
2614
  function printRunSetting(expertName, experts, setting) {
2543
- console.log("\u{1F99C} Starting Perstack \u{1F99C}");
2615
+ console.log("Starting Perstack");
2544
2616
  console.log(`Expert To Run: ${expertName}`);
2545
2617
  console.log(`Experts: ${Object.keys(experts).join(", ")}`);
2546
2618
  console.log(`Model: ${setting.model}`);
2547
2619
  console.log(`Temperature: ${setting.temperature}`);
2548
2620
  console.log(`Max Steps: ${setting.maxSteps}`);
2549
2621
  console.log(`Max Retries: ${setting.maxRetries}`);
2622
+ console.log(`Timeout: ${setting.timeout}`);
2550
2623
  if (setting.input.text) {
2551
2624
  console.log(`Query: ${setting.input.text}`);
2552
2625
  }
@@ -2618,6 +2691,16 @@ var RunInputSchema = z5.object({
2618
2691
  }
2619
2692
  return parsedValue;
2620
2693
  }),
2694
+ timeout: z5.string().optional().transform((value) => {
2695
+ if (value === void 0) {
2696
+ return void 0;
2697
+ }
2698
+ const parsedValue = Number.parseInt(value);
2699
+ if (Number.isNaN(parsedValue)) {
2700
+ return void 0;
2701
+ }
2702
+ return parsedValue;
2703
+ }),
2621
2704
  runId: z5.string().optional()
2622
2705
  })
2623
2706
  });