@perstack/runtime 0.0.63 → 0.0.64

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.
@@ -0,0 +1,2355 @@
1
+ import { createAmazonBedrock } from '@ai-sdk/amazon-bedrock';
2
+ import { createAnthropic } from '@ai-sdk/anthropic';
3
+ import { createAzure } from '@ai-sdk/azure';
4
+ import { createDeepSeek } from '@ai-sdk/deepseek';
5
+ import { createGoogleGenerativeAI } from '@ai-sdk/google';
6
+ import { createVertex } from '@ai-sdk/google-vertex';
7
+ import { createOpenAI } from '@ai-sdk/openai';
8
+ import { runParamsSchema, createRuntimeEvent, knownModels, stopRunByExceededMaxSteps, continueToNextStep, stopRunByDelegate, stopRunByInteractiveTool, retry, completeRun, finishToolCall, resolveToolResults, attemptCompletion, callDelegate, callInteractiveTool, callTools, resumeToolCalls, finishAllToolCalls, startGeneration, startRun } from '@perstack/core';
9
+ import { createOllama } from 'ollama-ai-provider-v2';
10
+ import { ProxyAgent, fetch } from 'undici';
11
+ import { setup, assign, createActor } from 'xstate';
12
+ import { generateText, tool, jsonSchema } from 'ai';
13
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
14
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
15
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
16
+ import { McpError } from '@modelcontextprotocol/sdk/types.js';
17
+ import { createId } from '@paralleldrive/cuid2';
18
+ import { readFile } from 'fs/promises';
19
+ import { dedent } from 'ts-dedent';
20
+ import { ApiV1Client } from '@perstack/api-client/v1';
21
+
22
+ // package.json
23
+ var package_default = {
24
+ name: "@perstack/runtime",
25
+ version: "0.0.63",
26
+ description: "Perstack Runtime",
27
+ author: "Wintermute Technologies, Inc.",
28
+ license: "Apache-2.0",
29
+ type: "module",
30
+ bin: {
31
+ "perstack-runtime": "./bin/cli.ts"
32
+ },
33
+ exports: {
34
+ ".": "./src/index.ts"
35
+ },
36
+ publishConfig: {
37
+ access: "public",
38
+ bin: {
39
+ "perstack-runtime": "dist/bin/cli.js"
40
+ },
41
+ exports: {
42
+ ".": "./dist/src/index.js"
43
+ },
44
+ types: {
45
+ ".": "./dist/src/index.d.ts"
46
+ }
47
+ },
48
+ files: [
49
+ "dist"
50
+ ],
51
+ scripts: {
52
+ clean: "rm -rf dist",
53
+ build: "pnpm run clean && tsup --config ./tsup.config.ts",
54
+ typecheck: "tsc --noEmit"
55
+ },
56
+ dependencies: {
57
+ commander: "^14.0.2",
58
+ dotenv: "^17.2.3",
59
+ "smol-toml": "^1.5.2",
60
+ "@ai-sdk/amazon-bedrock": "^3.0.62",
61
+ "@ai-sdk/anthropic": "^2.0.50",
62
+ "@ai-sdk/azure": "^2.0.77",
63
+ "@ai-sdk/deepseek": "^1.0.16",
64
+ "@ai-sdk/google": "^2.0.44",
65
+ "@ai-sdk/google-vertex": "^3.0.24",
66
+ "@ai-sdk/openai": "^2.0.75",
67
+ "@modelcontextprotocol/sdk": "^1.23.0",
68
+ "@paralleldrive/cuid2": "^3.0.4",
69
+ "@perstack/api-client": "workspace:*",
70
+ "@perstack/core": "workspace:*",
71
+ ai: "^5.0.104",
72
+ "ollama-ai-provider-v2": "^1.5.5",
73
+ "ts-dedent": "^2.2.0",
74
+ xstate: "^5.24.0",
75
+ undici: "^7.9.0"
76
+ },
77
+ devDependencies: {
78
+ "@tsconfig/node22": "^22.0.5",
79
+ "@types/node": "^24.10.1",
80
+ tsup: "^8.5.1",
81
+ typescript: "^5.9.3",
82
+ vitest: "^4.0.14"
83
+ },
84
+ engines: {
85
+ node: ">=22.0.0"
86
+ }
87
+ };
88
+ function createProxyFetch(proxyUrl) {
89
+ const agent = new ProxyAgent(proxyUrl);
90
+ return (input, init) => {
91
+ return fetch(input, { ...init, dispatcher: agent });
92
+ };
93
+ }
94
+ function getModel(modelId, providerConfig, options) {
95
+ const customFetch = options?.proxyUrl ? createProxyFetch(options.proxyUrl) : void 0;
96
+ switch (providerConfig.providerName) {
97
+ case "anthropic": {
98
+ const anthropic = createAnthropic({
99
+ apiKey: providerConfig.apiKey,
100
+ baseURL: providerConfig.baseUrl,
101
+ headers: providerConfig.headers,
102
+ fetch: customFetch
103
+ });
104
+ return anthropic(modelId);
105
+ }
106
+ case "google": {
107
+ const google = createGoogleGenerativeAI({
108
+ apiKey: providerConfig.apiKey,
109
+ baseURL: providerConfig.baseUrl,
110
+ headers: providerConfig.headers,
111
+ fetch: customFetch
112
+ });
113
+ return google(modelId);
114
+ }
115
+ case "openai": {
116
+ const openai = createOpenAI({
117
+ apiKey: providerConfig.apiKey,
118
+ baseURL: providerConfig.baseUrl,
119
+ organization: providerConfig.organization,
120
+ project: providerConfig.project,
121
+ name: providerConfig.name,
122
+ headers: providerConfig.headers,
123
+ fetch: customFetch
124
+ });
125
+ return openai(modelId);
126
+ }
127
+ case "ollama": {
128
+ const ollama = createOllama({
129
+ baseURL: providerConfig.baseUrl,
130
+ headers: providerConfig.headers,
131
+ fetch: customFetch
132
+ });
133
+ return ollama(modelId);
134
+ }
135
+ case "azure-openai": {
136
+ const azure = createAzure({
137
+ apiKey: providerConfig.apiKey,
138
+ resourceName: providerConfig.resourceName,
139
+ apiVersion: providerConfig.apiVersion,
140
+ baseURL: providerConfig.baseUrl,
141
+ headers: providerConfig.headers,
142
+ useDeploymentBasedUrls: providerConfig.useDeploymentBasedUrls,
143
+ fetch: customFetch
144
+ });
145
+ return azure(modelId);
146
+ }
147
+ case "amazon-bedrock": {
148
+ const amazonBedrock = createAmazonBedrock({
149
+ accessKeyId: providerConfig.accessKeyId,
150
+ secretAccessKey: providerConfig.secretAccessKey,
151
+ region: providerConfig.region,
152
+ sessionToken: providerConfig.sessionToken,
153
+ fetch: customFetch
154
+ });
155
+ return amazonBedrock(modelId);
156
+ }
157
+ case "google-vertex": {
158
+ const vertex = createVertex({
159
+ project: providerConfig.project,
160
+ location: providerConfig.location,
161
+ baseURL: providerConfig.baseUrl,
162
+ headers: providerConfig.headers,
163
+ fetch: customFetch
164
+ });
165
+ return vertex(modelId);
166
+ }
167
+ case "deepseek": {
168
+ const deepseek = createDeepSeek({
169
+ apiKey: providerConfig.apiKey,
170
+ baseURL: providerConfig.baseUrl,
171
+ headers: providerConfig.headers,
172
+ fetch: customFetch
173
+ });
174
+ return deepseek(modelId);
175
+ }
176
+ default: {
177
+ const _exhaustive = providerConfig;
178
+ throw new Error(`Unknown provider: ${_exhaustive.providerName}`);
179
+ }
180
+ }
181
+ }
182
+ function getContextWindow(providerName, modelId) {
183
+ const modelConfig = knownModels.find((model) => model.provider === providerName)?.models.find((model) => model.name === modelId);
184
+ return modelConfig?.contextWindow;
185
+ }
186
+ function calculateContextWindowUsage(usage, contextWindow) {
187
+ return (usage.inputTokens + usage.cachedInputTokens + usage.outputTokens) / contextWindow;
188
+ }
189
+
190
+ // src/helpers/usage.ts
191
+ function createEmptyUsage() {
192
+ return {
193
+ inputTokens: 0,
194
+ outputTokens: 0,
195
+ reasoningTokens: 0,
196
+ totalTokens: 0,
197
+ cachedInputTokens: 0
198
+ };
199
+ }
200
+ function usageFromGenerateTextResult(result) {
201
+ return {
202
+ inputTokens: result.usage.inputTokens || 0,
203
+ outputTokens: result.usage.outputTokens || 0,
204
+ reasoningTokens: result.usage.reasoningTokens || 0,
205
+ totalTokens: result.usage.totalTokens || 0,
206
+ cachedInputTokens: result.usage.cachedInputTokens || 0
207
+ };
208
+ }
209
+ function sumUsage(a, b) {
210
+ return {
211
+ inputTokens: a.inputTokens + b.inputTokens,
212
+ outputTokens: a.outputTokens + b.outputTokens,
213
+ reasoningTokens: a.reasoningTokens + b.reasoningTokens,
214
+ totalTokens: a.totalTokens + b.totalTokens,
215
+ cachedInputTokens: a.cachedInputTokens + b.cachedInputTokens
216
+ };
217
+ }
218
+
219
+ // src/skill-manager/base.ts
220
+ var BaseSkillManager = class {
221
+ _toolDefinitions = [];
222
+ _initialized = false;
223
+ _initializing;
224
+ skill;
225
+ interactiveSkill;
226
+ expert;
227
+ _jobId;
228
+ _runId;
229
+ _eventListener;
230
+ constructor(jobId, runId, eventListener) {
231
+ this._jobId = jobId;
232
+ this._runId = runId;
233
+ this._eventListener = eventListener;
234
+ }
235
+ async init() {
236
+ if (this._initialized) {
237
+ throw new Error(`Skill ${this.name} is already initialized`);
238
+ }
239
+ if (this._initializing) {
240
+ throw new Error(`Skill ${this.name} is already initializing`);
241
+ }
242
+ const initPromise = this._performInit();
243
+ this._initializing = initPromise;
244
+ if (!this.lazyInit) {
245
+ try {
246
+ await initPromise;
247
+ } catch (error) {
248
+ this._initialized = false;
249
+ this._initializing = void 0;
250
+ throw error;
251
+ }
252
+ }
253
+ }
254
+ isInitialized() {
255
+ return this._initialized;
256
+ }
257
+ async _performInit() {
258
+ await this._doInit();
259
+ this._initialized = true;
260
+ this._initializing = void 0;
261
+ }
262
+ async getToolDefinitions() {
263
+ if (!this.isInitialized() && !this.lazyInit) {
264
+ throw new Error(`Skill ${this.name} is not initialized`);
265
+ }
266
+ if (!this.isInitialized() && this.lazyInit) {
267
+ return [];
268
+ }
269
+ return this._filterTools(this._toolDefinitions);
270
+ }
271
+ _filterTools(tools) {
272
+ return tools;
273
+ }
274
+ };
275
+
276
+ // src/skill-manager/delegate.ts
277
+ var DelegateSkillManager = class extends BaseSkillManager {
278
+ name;
279
+ type = "delegate";
280
+ lazyInit = false;
281
+ expert;
282
+ constructor(expert, jobId, runId, eventListener) {
283
+ super(jobId, runId, eventListener);
284
+ this.name = expert.name;
285
+ this.expert = expert;
286
+ }
287
+ async _doInit() {
288
+ this._toolDefinitions = [
289
+ {
290
+ skillName: this.expert.name,
291
+ name: this.expert.name.split("/").pop() ?? this.expert.name,
292
+ description: this.expert.description,
293
+ inputSchema: {
294
+ type: "object",
295
+ properties: {
296
+ query: { type: "string" }
297
+ },
298
+ required: ["query"]
299
+ },
300
+ interactive: false
301
+ }
302
+ ];
303
+ }
304
+ async close() {
305
+ }
306
+ async callTool(_toolName, _input) {
307
+ return [];
308
+ }
309
+ };
310
+
311
+ // src/skill-manager/interactive.ts
312
+ var InteractiveSkillManager = class extends BaseSkillManager {
313
+ name;
314
+ type = "interactive";
315
+ lazyInit = false;
316
+ interactiveSkill;
317
+ constructor(interactiveSkill, jobId, runId, eventListener) {
318
+ super(jobId, runId, eventListener);
319
+ this.name = interactiveSkill.name;
320
+ this.interactiveSkill = interactiveSkill;
321
+ }
322
+ async _doInit() {
323
+ this._toolDefinitions = Object.values(this.interactiveSkill.tools).map((tool2) => ({
324
+ skillName: this.interactiveSkill.name,
325
+ name: tool2.name,
326
+ description: tool2.description,
327
+ inputSchema: JSON.parse(tool2.inputJsonSchema),
328
+ interactive: true
329
+ }));
330
+ }
331
+ async close() {
332
+ }
333
+ async callTool(_toolName, _input) {
334
+ return [];
335
+ }
336
+ };
337
+ var McpSkillManager = class extends BaseSkillManager {
338
+ name;
339
+ type = "mcp";
340
+ lazyInit;
341
+ skill;
342
+ _mcpClient;
343
+ _env;
344
+ constructor(skill, env, jobId, runId, eventListener) {
345
+ super(jobId, runId, eventListener);
346
+ this.name = skill.name;
347
+ this.skill = skill;
348
+ this._env = env;
349
+ this.lazyInit = skill.type === "mcpStdioSkill" && skill.lazyInit && skill.name !== "@perstack/base";
350
+ }
351
+ async _doInit() {
352
+ this._mcpClient = new Client({
353
+ name: `${this.skill.name}-mcp-client`,
354
+ version: "1.0.0"
355
+ });
356
+ if (this.skill.type === "mcpStdioSkill") {
357
+ await this._initStdio(this.skill);
358
+ } else {
359
+ await this._initSse(this.skill);
360
+ }
361
+ const { tools } = await this._mcpClient.listTools();
362
+ this._toolDefinitions = tools.map((tool2) => ({
363
+ skillName: this.skill.name,
364
+ name: tool2.name,
365
+ description: tool2.description,
366
+ inputSchema: tool2.inputSchema,
367
+ interactive: false
368
+ }));
369
+ }
370
+ async _initStdio(skill) {
371
+ if (!skill.command) {
372
+ throw new Error(`Skill ${skill.name} has no command`);
373
+ }
374
+ const env = {};
375
+ for (const envName of skill.requiredEnv) {
376
+ if (!this._env[envName]) {
377
+ throw new Error(`Skill ${skill.name} requires environment variable ${envName}`);
378
+ }
379
+ env[envName] = this._env[envName];
380
+ }
381
+ const startTime = Date.now();
382
+ const { command, args } = this._getCommandArgs(skill);
383
+ if (this._eventListener) {
384
+ const event = createRuntimeEvent("skillStarting", this._jobId, this._runId, {
385
+ skillName: skill.name,
386
+ command,
387
+ args
388
+ });
389
+ this._eventListener(event);
390
+ }
391
+ const transport = new StdioClientTransport({ command, args, env, stderr: "pipe" });
392
+ if (transport.stderr) {
393
+ transport.stderr.on("data", (chunk) => {
394
+ if (this._eventListener) {
395
+ const event = createRuntimeEvent("skillStderr", this._jobId, this._runId, {
396
+ skillName: skill.name,
397
+ message: chunk.toString().trim()
398
+ });
399
+ this._eventListener(event);
400
+ }
401
+ });
402
+ }
403
+ const connectStartTime = Date.now();
404
+ await this._mcpClient.connect(transport);
405
+ const connectTime = Date.now();
406
+ if (this._eventListener) {
407
+ const serverInfo = this._mcpClient.getServerVersion();
408
+ const event = createRuntimeEvent("skillConnected", this._jobId, this._runId, {
409
+ skillName: skill.name,
410
+ serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0,
411
+ connectDurationMs: connectTime - connectStartTime,
412
+ totalDurationMs: connectTime - startTime
413
+ });
414
+ this._eventListener(event);
415
+ }
416
+ }
417
+ async _initSse(skill) {
418
+ if (!skill.endpoint) {
419
+ throw new Error(`Skill ${skill.name} has no endpoint`);
420
+ }
421
+ const transport = new SSEClientTransport(new URL(skill.endpoint));
422
+ await this._mcpClient.connect(transport);
423
+ }
424
+ _getCommandArgs(skill) {
425
+ const { name, command, packageName, args } = skill;
426
+ if (!packageName && (!args || args.length === 0)) {
427
+ throw new Error(`Skill ${name} has no packageName or args. Please provide one of them.`);
428
+ }
429
+ if (packageName && args && args.length > 0) {
430
+ throw new Error(
431
+ `Skill ${name} has both packageName and args. Please provide only one of them.`
432
+ );
433
+ }
434
+ let newArgs = args && args.length > 0 ? args : [packageName];
435
+ if (command === "npx" && !newArgs.includes("-y")) {
436
+ newArgs = ["-y", ...newArgs];
437
+ }
438
+ return { command, args: newArgs };
439
+ }
440
+ async close() {
441
+ if (this._mcpClient) {
442
+ await this._mcpClient.close();
443
+ if (this._eventListener && this.skill) {
444
+ const event = createRuntimeEvent("skillDisconnected", this._jobId, this._runId, {
445
+ skillName: this.skill.name
446
+ });
447
+ this._eventListener(event);
448
+ }
449
+ }
450
+ }
451
+ _filterTools(tools) {
452
+ const omit = this.skill.omit ?? [];
453
+ const pick = this.skill.pick ?? [];
454
+ return tools.filter((tool2) => omit.length > 0 ? !omit.includes(tool2.name) : true).filter((tool2) => pick.length > 0 ? pick.includes(tool2.name) : true);
455
+ }
456
+ async callTool(toolName, input) {
457
+ if (!this.isInitialized() || !this._mcpClient) {
458
+ throw new Error(`${this.name} is not initialized`);
459
+ }
460
+ try {
461
+ const result = await this._mcpClient.callTool({
462
+ name: toolName,
463
+ arguments: input
464
+ });
465
+ return this._convertToolResult(result, toolName, input);
466
+ } catch (error) {
467
+ return this._handleToolError(error, toolName);
468
+ }
469
+ }
470
+ _handleToolError(error, toolName) {
471
+ if (error instanceof McpError) {
472
+ return [
473
+ {
474
+ type: "textPart",
475
+ text: `Error calling tool ${toolName}: ${error.message}`,
476
+ id: createId()
477
+ }
478
+ ];
479
+ }
480
+ throw error;
481
+ }
482
+ _convertToolResult(result, toolName, input) {
483
+ if (!result.content || result.content.length === 0) {
484
+ return [
485
+ {
486
+ type: "textPart",
487
+ text: `Tool ${toolName} returned nothing with arguments: ${JSON.stringify(input)}`,
488
+ id: createId()
489
+ }
490
+ ];
491
+ }
492
+ return result.content.filter((part) => part.type !== "audio" && part.type !== "resource_link").map((part) => this._convertPart(part));
493
+ }
494
+ _convertPart(part) {
495
+ switch (part.type) {
496
+ case "text":
497
+ if (!part.text || part.text === "") {
498
+ return { type: "textPart", text: "Error: No content", id: createId() };
499
+ }
500
+ return { type: "textPart", text: part.text, id: createId() };
501
+ case "image":
502
+ if (!part.data || !part.mimeType) {
503
+ throw new Error("Image part must have both data and mimeType");
504
+ }
505
+ return {
506
+ type: "imageInlinePart",
507
+ encodedData: part.data,
508
+ mimeType: part.mimeType,
509
+ id: createId()
510
+ };
511
+ case "resource":
512
+ if (!part.resource) {
513
+ throw new Error("Resource part must have resource content");
514
+ }
515
+ return this._convertResource(part.resource);
516
+ }
517
+ }
518
+ _convertResource(resource) {
519
+ if (!resource.mimeType) {
520
+ throw new Error(`Resource ${JSON.stringify(resource)} has no mimeType`);
521
+ }
522
+ if (resource.text && typeof resource.text === "string") {
523
+ return { type: "textPart", text: resource.text, id: createId() };
524
+ }
525
+ if (resource.blob && typeof resource.blob === "string") {
526
+ return {
527
+ type: "fileInlinePart",
528
+ encodedData: resource.blob,
529
+ mimeType: resource.mimeType,
530
+ id: createId()
531
+ };
532
+ }
533
+ throw new Error(`Unsupported resource type: ${JSON.stringify(resource)}`);
534
+ }
535
+ };
536
+
537
+ // src/skill-manager/helpers.ts
538
+ async function initSkillManagersWithCleanup(managers, allManagers) {
539
+ const results = await Promise.allSettled(managers.map((m) => m.init()));
540
+ const firstRejected = results.find((r) => r.status === "rejected");
541
+ if (firstRejected) {
542
+ await Promise.all(allManagers.map((m) => m.close().catch(() => {
543
+ })));
544
+ throw firstRejected.reason;
545
+ }
546
+ }
547
+ async function getSkillManagers(expert, experts, setting, eventListener, options) {
548
+ const { perstackBaseSkillCommand, env, jobId, runId } = setting;
549
+ const { skills } = expert;
550
+ if (!skills["@perstack/base"]) {
551
+ throw new Error("Base skill is not defined");
552
+ }
553
+ const allManagers = [];
554
+ const mcpSkills = Object.values(skills).filter((skill) => skill.type === "mcpStdioSkill" || skill.type === "mcpSseSkill").map((skill) => {
555
+ if (perstackBaseSkillCommand && skill.type === "mcpStdioSkill") {
556
+ const matchesBaseByPackage = skill.command === "npx" && skill.packageName === "@perstack/base";
557
+ const matchesBaseByArgs = skill.command === "npx" && Array.isArray(skill.args) && skill.args.includes("@perstack/base");
558
+ if (matchesBaseByPackage || matchesBaseByArgs) {
559
+ const [overrideCommand, ...overrideArgs] = perstackBaseSkillCommand;
560
+ if (!overrideCommand) {
561
+ throw new Error("perstackBaseSkillCommand must have at least one element");
562
+ }
563
+ return {
564
+ ...skill,
565
+ command: overrideCommand,
566
+ packageName: void 0,
567
+ args: overrideArgs,
568
+ lazyInit: false
569
+ };
570
+ }
571
+ }
572
+ return skill;
573
+ });
574
+ const mcpSkillManagers = mcpSkills.map((skill) => {
575
+ const manager = new McpSkillManager(skill, env, jobId, runId, eventListener);
576
+ allManagers.push(manager);
577
+ return manager;
578
+ });
579
+ await initSkillManagersWithCleanup(mcpSkillManagers, allManagers);
580
+ if (!options?.isDelegatedRun) {
581
+ const interactiveSkills = Object.values(skills).filter(
582
+ (skill) => skill.type === "interactiveSkill"
583
+ );
584
+ const interactiveSkillManagers = interactiveSkills.map((interactiveSkill) => {
585
+ const manager = new InteractiveSkillManager(interactiveSkill, jobId, runId, eventListener);
586
+ allManagers.push(manager);
587
+ return manager;
588
+ });
589
+ await initSkillManagersWithCleanup(interactiveSkillManagers, allManagers);
590
+ }
591
+ const delegateSkillManagers = [];
592
+ for (const delegateExpertName of expert.delegates) {
593
+ const delegate = experts[delegateExpertName];
594
+ if (!delegate) {
595
+ await Promise.all(allManagers.map((m) => m.close().catch(() => {
596
+ })));
597
+ throw new Error(`Delegate expert "${delegateExpertName}" not found in experts`);
598
+ }
599
+ const manager = new DelegateSkillManager(delegate, jobId, runId, eventListener);
600
+ allManagers.push(manager);
601
+ delegateSkillManagers.push(manager);
602
+ }
603
+ await initSkillManagersWithCleanup(delegateSkillManagers, allManagers);
604
+ const skillManagers = {};
605
+ for (const manager of allManagers) {
606
+ skillManagers[manager.name] = manager;
607
+ }
608
+ return skillManagers;
609
+ }
610
+ async function closeSkillManagers(skillManagers) {
611
+ await Promise.all(Object.values(skillManagers).map((m) => m.close().catch(() => {
612
+ })));
613
+ }
614
+ async function getSkillManagerByToolName(skillManagers, toolName) {
615
+ for (const skillManager of Object.values(skillManagers)) {
616
+ const toolDefinitions = await skillManager.getToolDefinitions();
617
+ for (const toolDefinition of toolDefinitions) {
618
+ if (toolDefinition.name === toolName) {
619
+ return skillManager;
620
+ }
621
+ }
622
+ }
623
+ throw new Error(`Tool ${toolName} not found`);
624
+ }
625
+ async function getToolSet(skillManagers) {
626
+ const tools = {};
627
+ for (const skillManager of Object.values(skillManagers)) {
628
+ const toolDefinitions = await skillManager.getToolDefinitions();
629
+ for (const toolDefinition of toolDefinitions) {
630
+ tools[toolDefinition.name] = tool({
631
+ description: toolDefinition.description,
632
+ inputSchema: jsonSchema(toolDefinition.inputSchema)
633
+ });
634
+ }
635
+ }
636
+ return tools;
637
+ }
638
+
639
+ // src/state-machine/states/calling-delegate.ts
640
+ async function getToolType(toolName, skillManagers) {
641
+ const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
642
+ return skillManager.type;
643
+ }
644
+ async function callingDelegateLogic({
645
+ setting,
646
+ checkpoint,
647
+ step,
648
+ skillManagers
649
+ }) {
650
+ if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
651
+ throw new Error("No pending tool calls found");
652
+ }
653
+ const toolCallTypes = await Promise.all(
654
+ step.pendingToolCalls.map(async (tc) => ({
655
+ toolCall: tc,
656
+ type: await getToolType(tc.toolName, skillManagers)
657
+ }))
658
+ );
659
+ const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
660
+ const nonDelegateToolCalls = toolCallTypes.filter((t) => t.type !== "delegate").map((t) => t.toolCall);
661
+ if (delegateToolCalls.length === 0) {
662
+ throw new Error("No delegate tool calls found");
663
+ }
664
+ const delegations = await Promise.all(
665
+ delegateToolCalls.map(async (tc) => {
666
+ const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
667
+ if (!skillManager.expert) {
668
+ throw new Error(`Delegation error: skill manager "${tc.toolName}" not found`);
669
+ }
670
+ if (!tc.args || !tc.args.query || typeof tc.args.query !== "string") {
671
+ throw new Error(`Delegation error: query is undefined for ${tc.toolName}`);
672
+ }
673
+ return {
674
+ expert: {
675
+ key: skillManager.expert.key,
676
+ name: skillManager.expert.name,
677
+ version: skillManager.expert.version
678
+ },
679
+ toolCallId: tc.id,
680
+ toolName: tc.toolName,
681
+ query: tc.args.query
682
+ };
683
+ })
684
+ );
685
+ return stopRunByDelegate(setting, checkpoint, {
686
+ checkpoint: {
687
+ ...checkpoint,
688
+ status: "stoppedByDelegate",
689
+ delegateTo: delegations,
690
+ pendingToolCalls: nonDelegateToolCalls.length > 0 ? nonDelegateToolCalls : void 0,
691
+ partialToolResults: step.partialToolResults
692
+ },
693
+ step: {
694
+ ...step,
695
+ finishedAt: Date.now()
696
+ }
697
+ });
698
+ }
699
+ async function callingInteractiveToolLogic({
700
+ setting,
701
+ checkpoint,
702
+ step
703
+ }) {
704
+ if (!step.pendingToolCalls || step.pendingToolCalls.length === 0) {
705
+ throw new Error("No pending tool calls found");
706
+ }
707
+ const currentToolCall = step.pendingToolCalls[0];
708
+ const remainingToolCalls = step.pendingToolCalls.slice(1);
709
+ return stopRunByInteractiveTool(setting, checkpoint, {
710
+ checkpoint: {
711
+ ...checkpoint,
712
+ status: "stoppedByInteractiveTool",
713
+ pendingToolCalls: [currentToolCall, ...remainingToolCalls],
714
+ partialToolResults: step.partialToolResults
715
+ },
716
+ step: {
717
+ ...step,
718
+ finishedAt: Date.now()
719
+ }
720
+ });
721
+ }
722
+ function hasRemainingTodos(toolResult) {
723
+ const firstPart = toolResult.result[0];
724
+ if (!firstPart || firstPart.type !== "textPart") {
725
+ return false;
726
+ }
727
+ try {
728
+ const parsed = JSON.parse(firstPart.text);
729
+ return Array.isArray(parsed.remainingTodos) && parsed.remainingTodos.length > 0;
730
+ } catch {
731
+ return false;
732
+ }
733
+ }
734
+ function isFileInfo(value) {
735
+ 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";
736
+ }
737
+ async function processFileToolResult(toolResult, toolName) {
738
+ const processedContents = [];
739
+ for (const part of toolResult.result) {
740
+ if (part.type !== "textPart") {
741
+ processedContents.push(part);
742
+ continue;
743
+ }
744
+ let fileInfo;
745
+ try {
746
+ const parsed = JSON.parse(part.text);
747
+ if (isFileInfo(parsed)) {
748
+ fileInfo = parsed;
749
+ }
750
+ } catch {
751
+ processedContents.push(part);
752
+ continue;
753
+ }
754
+ if (!fileInfo) {
755
+ processedContents.push(part);
756
+ continue;
757
+ }
758
+ const { path, mimeType } = fileInfo;
759
+ try {
760
+ const buffer = await readFile(path);
761
+ if (toolName === "readImageFile") {
762
+ processedContents.push({
763
+ type: "imageInlinePart",
764
+ id: part.id,
765
+ encodedData: buffer.toString("base64"),
766
+ mimeType
767
+ });
768
+ } else {
769
+ processedContents.push({
770
+ type: "fileInlinePart",
771
+ id: part.id,
772
+ encodedData: buffer.toString("base64"),
773
+ mimeType
774
+ });
775
+ }
776
+ } catch (error) {
777
+ processedContents.push({
778
+ type: "textPart",
779
+ id: part.id,
780
+ text: `Failed to read file "${path}": ${error instanceof Error ? error.message : String(error)}`
781
+ });
782
+ }
783
+ }
784
+ return { ...toolResult, result: processedContents };
785
+ }
786
+ async function executeMcpToolCall(toolCall, skillManagers) {
787
+ const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
788
+ if (skillManager.type !== "mcp") {
789
+ throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
790
+ }
791
+ const result = await skillManager.callTool(toolCall.toolName, toolCall.args);
792
+ const toolResult = {
793
+ id: toolCall.id,
794
+ skillName: toolCall.skillName,
795
+ toolName: toolCall.toolName,
796
+ result
797
+ };
798
+ if (toolCall.toolName === "readPdfFile" || toolCall.toolName === "readImageFile") {
799
+ return processFileToolResult(toolResult, toolCall.toolName);
800
+ }
801
+ return toolResult;
802
+ }
803
+ async function getToolType2(toolCall, skillManagers) {
804
+ const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
805
+ return skillManager.type;
806
+ }
807
+ async function callingToolLogic({
808
+ setting,
809
+ checkpoint,
810
+ step,
811
+ skillManagers
812
+ }) {
813
+ const pendingToolCalls = step.pendingToolCalls ?? step.toolCalls ?? [];
814
+ if (pendingToolCalls.length === 0) {
815
+ throw new Error("No tool calls found");
816
+ }
817
+ const toolResults = step.toolResults ? [...step.toolResults] : [];
818
+ const attemptCompletionTool = pendingToolCalls.find(
819
+ (tc) => tc.skillName === "@perstack/base" && tc.toolName === "attemptCompletion"
820
+ );
821
+ if (attemptCompletionTool) {
822
+ const toolResult = await executeMcpToolCall(attemptCompletionTool, skillManagers);
823
+ if (hasRemainingTodos(toolResult)) {
824
+ return resolveToolResults(setting, checkpoint, { toolResults: [toolResult] });
825
+ }
826
+ return attemptCompletion(setting, checkpoint, { toolResult });
827
+ }
828
+ const toolCallTypes = await Promise.all(
829
+ pendingToolCalls.map(async (tc) => ({
830
+ toolCall: tc,
831
+ type: await getToolType2(tc, skillManagers)
832
+ }))
833
+ );
834
+ const mcpToolCalls = toolCallTypes.filter((t) => t.type === "mcp").map((t) => t.toolCall);
835
+ const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
836
+ const interactiveToolCalls = toolCallTypes.filter((t) => t.type === "interactive").map((t) => t.toolCall);
837
+ if (mcpToolCalls.length > 0) {
838
+ const mcpResults = await Promise.all(
839
+ mcpToolCalls.map((tc) => executeMcpToolCall(tc, skillManagers))
840
+ );
841
+ toolResults.push(...mcpResults);
842
+ }
843
+ if (delegateToolCalls.length > 0) {
844
+ step.partialToolResults = toolResults;
845
+ step.pendingToolCalls = [...delegateToolCalls, ...interactiveToolCalls];
846
+ return callDelegate(setting, checkpoint, {
847
+ newMessage: checkpoint.messages[checkpoint.messages.length - 1],
848
+ toolCalls: delegateToolCalls,
849
+ usage: step.usage
850
+ });
851
+ }
852
+ if (interactiveToolCalls.length > 0) {
853
+ const interactiveToolCall = interactiveToolCalls[0];
854
+ if (!interactiveToolCall) {
855
+ throw new Error("No interactive tool call found");
856
+ }
857
+ step.partialToolResults = toolResults;
858
+ step.pendingToolCalls = interactiveToolCalls;
859
+ return callInteractiveTool(setting, checkpoint, {
860
+ newMessage: checkpoint.messages[checkpoint.messages.length - 1],
861
+ toolCall: interactiveToolCall,
862
+ usage: step.usage
863
+ });
864
+ }
865
+ return resolveToolResults(setting, checkpoint, { toolResults });
866
+ }
867
+ async function finishingStepLogic({
868
+ setting,
869
+ checkpoint,
870
+ step
871
+ }) {
872
+ if (setting.maxSteps !== void 0 && checkpoint.stepNumber >= setting.maxSteps) {
873
+ return stopRunByExceededMaxSteps(setting, checkpoint, {
874
+ checkpoint: {
875
+ ...checkpoint,
876
+ status: "stoppedByExceededMaxSteps"
877
+ },
878
+ step: {
879
+ ...step,
880
+ finishedAt: Date.now()
881
+ }
882
+ });
883
+ }
884
+ return continueToNextStep(setting, checkpoint, {
885
+ checkpoint: {
886
+ ...checkpoint
887
+ },
888
+ step: {
889
+ ...step,
890
+ finishedAt: Date.now()
891
+ },
892
+ nextCheckpoint: {
893
+ ...checkpoint,
894
+ id: createId(),
895
+ stepNumber: checkpoint.stepNumber + 1
896
+ }
897
+ });
898
+ }
899
+ function createUserMessage(contents) {
900
+ return {
901
+ type: "userMessage",
902
+ contents: contents.map((part) => ({
903
+ ...part,
904
+ id: createId()
905
+ })),
906
+ id: createId()
907
+ };
908
+ }
909
+ function createExpertMessage(contents) {
910
+ return {
911
+ type: "expertMessage",
912
+ contents: contents.map((part) => ({
913
+ ...part,
914
+ id: createId()
915
+ })),
916
+ id: createId()
917
+ };
918
+ }
919
+ function createToolMessage(contents) {
920
+ return {
921
+ type: "toolMessage",
922
+ contents: contents.map((part) => ({
923
+ ...part,
924
+ contents: part.contents.map((part2) => ({
925
+ ...part2,
926
+ id: createId()
927
+ })),
928
+ id: createId()
929
+ })),
930
+ id: createId()
931
+ };
932
+ }
933
+ function messageToCoreMessage(message) {
934
+ switch (message.type) {
935
+ case "instructionMessage":
936
+ return {
937
+ role: "system",
938
+ content: instructionContentsToCoreContent(message.contents),
939
+ providerOptions: message.cache ? {
940
+ anthropic: { cacheControl: { type: "ephemeral" } }
941
+ } : void 0
942
+ };
943
+ case "userMessage":
944
+ return {
945
+ role: "user",
946
+ content: userContentsToCoreContent(message.contents),
947
+ providerOptions: message.cache ? {
948
+ anthropic: { cacheControl: { type: "ephemeral" } }
949
+ } : void 0
950
+ };
951
+ case "expertMessage":
952
+ return {
953
+ role: "assistant",
954
+ content: expertContentsToCoreContent(message.contents),
955
+ providerOptions: message.cache ? {
956
+ anthropic: { cacheControl: { type: "ephemeral" } }
957
+ } : void 0
958
+ };
959
+ case "toolMessage":
960
+ return {
961
+ role: "tool",
962
+ content: toolContentsToCoreContent(message.contents),
963
+ providerOptions: message.cache ? {
964
+ anthropic: { cacheControl: { type: "ephemeral" } }
965
+ } : void 0
966
+ };
967
+ }
968
+ }
969
+ function instructionContentsToCoreContent(contents) {
970
+ return contents.reduce((acc, part) => {
971
+ return dedent`
972
+ ${acc}
973
+ ${part.text}
974
+ `.trim();
975
+ }, "");
976
+ }
977
+ function userContentsToCoreContent(contents) {
978
+ return contents.map((part) => {
979
+ switch (part.type) {
980
+ case "textPart":
981
+ return textPartToCoreTextPart(part);
982
+ case "imageUrlPart":
983
+ return imageUrlPartToCoreImagePart(part);
984
+ case "imageInlinePart":
985
+ return imageInlinePartToCoreImagePart(part);
986
+ case "imageBinaryPart":
987
+ return imageBinaryPartToCoreImagePart(part);
988
+ case "fileUrlPart":
989
+ return fileUrlPartToCoreFilePart(part);
990
+ case "fileInlinePart":
991
+ return fileInlinePartToCoreFilePart(part);
992
+ case "fileBinaryPart":
993
+ return fileBinaryPartToCoreFilePart(part);
994
+ default:
995
+ throw new Error(`Unknown user content type: ${part.type}`);
996
+ }
997
+ });
998
+ }
999
+ function expertContentsToCoreContent(contents) {
1000
+ return contents.map((part) => {
1001
+ switch (part.type) {
1002
+ case "textPart":
1003
+ return textPartToCoreTextPart(part);
1004
+ case "toolCallPart":
1005
+ return toolCallPartToCoreToolCallPart(part);
1006
+ default:
1007
+ throw new Error(`Unknown expert content type: ${part.type}`);
1008
+ }
1009
+ });
1010
+ }
1011
+ function toolContentsToCoreContent(contents) {
1012
+ return contents.map((part) => {
1013
+ switch (part.type) {
1014
+ case "toolResultPart":
1015
+ return toolResultPartToCoreToolResultPart(part);
1016
+ default:
1017
+ throw new Error(`Unknown tool content type: ${part.type}`);
1018
+ }
1019
+ });
1020
+ }
1021
+ function textPartToCoreTextPart(part) {
1022
+ return {
1023
+ type: "text",
1024
+ text: part.text
1025
+ };
1026
+ }
1027
+ function imageUrlPartToCoreImagePart(part) {
1028
+ return {
1029
+ type: "image",
1030
+ image: part.url,
1031
+ mediaType: part.mimeType
1032
+ };
1033
+ }
1034
+ function imageInlinePartToCoreImagePart(part) {
1035
+ return {
1036
+ type: "image",
1037
+ image: part.encodedData,
1038
+ mediaType: part.mimeType
1039
+ };
1040
+ }
1041
+ function imageBinaryPartToCoreImagePart(part) {
1042
+ return {
1043
+ type: "image",
1044
+ image: part.data,
1045
+ mediaType: part.mimeType
1046
+ };
1047
+ }
1048
+ function fileUrlPartToCoreFilePart(part) {
1049
+ return {
1050
+ type: "file",
1051
+ data: part.url,
1052
+ mediaType: part.mimeType
1053
+ };
1054
+ }
1055
+ function fileInlinePartToCoreFilePart(part) {
1056
+ return {
1057
+ type: "file",
1058
+ data: part.encodedData,
1059
+ mediaType: part.mimeType
1060
+ };
1061
+ }
1062
+ function fileBinaryPartToCoreFilePart(part) {
1063
+ return {
1064
+ type: "file",
1065
+ data: part.data,
1066
+ mediaType: part.mimeType
1067
+ };
1068
+ }
1069
+ function toolCallPartToCoreToolCallPart(part) {
1070
+ return {
1071
+ type: "tool-call",
1072
+ toolCallId: part.toolCallId,
1073
+ toolName: part.toolName,
1074
+ input: part.args
1075
+ };
1076
+ }
1077
+ function toolResultPartToCoreToolResultPart(part) {
1078
+ const { contents } = part;
1079
+ if (contents.length === 1 && contents[0].type === "textPart") {
1080
+ return {
1081
+ type: "tool-result",
1082
+ toolCallId: part.toolCallId,
1083
+ toolName: part.toolName,
1084
+ output: { type: "text", value: contents[0].text }
1085
+ };
1086
+ }
1087
+ const contentValue = contents.map((content) => {
1088
+ if (content.type === "textPart") {
1089
+ return { type: "text", text: content.text };
1090
+ }
1091
+ return { type: "media", data: content.encodedData, mediaType: content.mimeType };
1092
+ });
1093
+ return {
1094
+ type: "tool-result",
1095
+ toolCallId: part.toolCallId,
1096
+ toolName: part.toolName,
1097
+ output: { type: "content", value: contentValue }
1098
+ };
1099
+ }
1100
+
1101
+ // src/state-machine/states/generating-run-result.ts
1102
+ async function generatingRunResultLogic({
1103
+ setting,
1104
+ checkpoint,
1105
+ step
1106
+ }) {
1107
+ if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
1108
+ throw new Error("No tool calls or tool results found");
1109
+ }
1110
+ const toolResultParts = step.toolResults.map((toolResult) => {
1111
+ const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
1112
+ return {
1113
+ type: "toolResultPart",
1114
+ toolCallId: toolResult.id,
1115
+ toolName: toolCall?.toolName ?? toolResult.toolName,
1116
+ contents: toolResult.result.filter(
1117
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1118
+ )
1119
+ };
1120
+ });
1121
+ const toolMessage = createToolMessage(toolResultParts);
1122
+ const model = getModel(setting.model, setting.providerConfig, { proxyUrl: setting.proxyUrl });
1123
+ const { messages } = checkpoint;
1124
+ let generationResult;
1125
+ try {
1126
+ generationResult = await generateText({
1127
+ model,
1128
+ messages: [...messages, toolMessage].map(messageToCoreMessage),
1129
+ temperature: setting.temperature,
1130
+ maxRetries: setting.maxRetries,
1131
+ abortSignal: AbortSignal.timeout(setting.timeout)
1132
+ });
1133
+ } catch (error) {
1134
+ if (error instanceof Error) {
1135
+ const reason = JSON.stringify({ error: error.name, message: error.message });
1136
+ return retry(setting, checkpoint, {
1137
+ reason,
1138
+ newMessages: [toolMessage, createUserMessage([{ type: "textPart", text: reason }])],
1139
+ usage: createEmptyUsage()
1140
+ });
1141
+ }
1142
+ throw error;
1143
+ }
1144
+ const usage = usageFromGenerateTextResult(generationResult);
1145
+ const { text } = generationResult;
1146
+ const newMessages = [toolMessage, createExpertMessage(text ? [{ type: "textPart", text }] : [])];
1147
+ return completeRun(setting, checkpoint, {
1148
+ checkpoint: {
1149
+ ...checkpoint,
1150
+ messages: [...messages, ...newMessages],
1151
+ usage: sumUsage(checkpoint.usage, usage),
1152
+ contextWindowUsage: checkpoint.contextWindow ? calculateContextWindowUsage(usage, checkpoint.contextWindow) : void 0,
1153
+ status: "completed"
1154
+ },
1155
+ step: {
1156
+ ...step,
1157
+ newMessages: [...step.newMessages, ...newMessages],
1158
+ finishedAt: Date.now(),
1159
+ usage: sumUsage(step.usage, usage)
1160
+ },
1161
+ text,
1162
+ usage
1163
+ });
1164
+ }
1165
+ async function classifyToolCalls(toolCalls, skillManagers) {
1166
+ return Promise.all(
1167
+ toolCalls.map(async (tc) => {
1168
+ const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
1169
+ return {
1170
+ toolCallId: tc.toolCallId,
1171
+ toolName: tc.toolName,
1172
+ input: tc.input,
1173
+ skillManager
1174
+ };
1175
+ })
1176
+ );
1177
+ }
1178
+ function sortToolCallsByPriority(toolCalls) {
1179
+ const priority = { mcp: 0, delegate: 1, interactive: 2 };
1180
+ return [...toolCalls].sort(
1181
+ (a, b) => (priority[a.skillManager.type] ?? 99) - (priority[b.skillManager.type] ?? 99)
1182
+ );
1183
+ }
1184
+ function buildToolCallParts(toolCalls) {
1185
+ return toolCalls.map((tc) => ({
1186
+ type: "toolCallPart",
1187
+ toolCallId: tc.toolCallId,
1188
+ toolName: tc.toolName,
1189
+ args: tc.input
1190
+ }));
1191
+ }
1192
+ function buildToolCalls(toolCalls) {
1193
+ return toolCalls.map((tc) => ({
1194
+ id: tc.toolCallId,
1195
+ skillName: tc.skillManager.name,
1196
+ toolName: tc.toolName,
1197
+ args: tc.input
1198
+ }));
1199
+ }
1200
+ async function generatingToolCallLogic({
1201
+ setting,
1202
+ checkpoint,
1203
+ skillManagers
1204
+ }) {
1205
+ const { messages } = checkpoint;
1206
+ const model = getModel(setting.model, setting.providerConfig, { proxyUrl: setting.proxyUrl });
1207
+ let result;
1208
+ try {
1209
+ result = await generateText({
1210
+ model,
1211
+ messages: messages.map(messageToCoreMessage),
1212
+ temperature: setting.temperature,
1213
+ maxRetries: setting.maxRetries,
1214
+ tools: await getToolSet(skillManagers),
1215
+ toolChoice: "required",
1216
+ abortSignal: AbortSignal.timeout(setting.timeout)
1217
+ });
1218
+ } catch (error) {
1219
+ if (error instanceof Error) {
1220
+ const reason = JSON.stringify({ error: error.name, message: error.message });
1221
+ return retry(setting, checkpoint, {
1222
+ reason,
1223
+ newMessages: [createUserMessage([{ type: "textPart", text: reason }])],
1224
+ usage: createEmptyUsage()
1225
+ });
1226
+ }
1227
+ throw error;
1228
+ }
1229
+ const usage = usageFromGenerateTextResult(result);
1230
+ const { text, toolCalls, finishReason } = result;
1231
+ if (toolCalls.length === 0) {
1232
+ const reason = JSON.stringify({
1233
+ error: "Error: No tool call generated",
1234
+ message: "You must generate a tool call. Try again."
1235
+ });
1236
+ return retry(setting, checkpoint, {
1237
+ reason,
1238
+ newMessages: [createUserMessage([{ type: "textPart", text: reason }])],
1239
+ usage
1240
+ });
1241
+ }
1242
+ const classified = await classifyToolCalls(toolCalls, skillManagers);
1243
+ const sorted = sortToolCallsByPriority(classified);
1244
+ if (finishReason === "tool-calls" || finishReason === "stop") {
1245
+ const toolCallParts = buildToolCallParts(sorted);
1246
+ const contents = [...toolCallParts];
1247
+ if (text) {
1248
+ contents.push({ type: "textPart", text });
1249
+ }
1250
+ const allToolCalls = buildToolCalls(sorted);
1251
+ return callTools(setting, checkpoint, {
1252
+ newMessage: createExpertMessage(contents),
1253
+ toolCalls: allToolCalls,
1254
+ usage
1255
+ });
1256
+ }
1257
+ if (finishReason === "length") {
1258
+ const firstToolCall = sorted[0];
1259
+ if (!firstToolCall) {
1260
+ throw new Error("No tool call found");
1261
+ }
1262
+ const reason = JSON.stringify({
1263
+ error: "Error: Tool call generation failed",
1264
+ message: "Generation length exceeded. Try again."
1265
+ });
1266
+ return retry(setting, checkpoint, {
1267
+ reason,
1268
+ newMessages: [
1269
+ createExpertMessage([
1270
+ {
1271
+ type: "toolCallPart",
1272
+ toolCallId: firstToolCall.toolCallId,
1273
+ toolName: firstToolCall.toolName,
1274
+ args: firstToolCall.input
1275
+ }
1276
+ ]),
1277
+ createToolMessage([
1278
+ {
1279
+ type: "toolResultPart",
1280
+ toolCallId: firstToolCall.toolCallId,
1281
+ toolName: firstToolCall.toolName,
1282
+ contents: [{ type: "textPart", text: reason }]
1283
+ }
1284
+ ])
1285
+ ],
1286
+ toolCalls: [
1287
+ {
1288
+ id: firstToolCall.toolCallId,
1289
+ skillName: firstToolCall.skillManager.name,
1290
+ toolName: firstToolCall.toolName,
1291
+ args: firstToolCall.input
1292
+ }
1293
+ ],
1294
+ toolResults: [
1295
+ {
1296
+ id: firstToolCall.toolCallId,
1297
+ skillName: firstToolCall.skillManager.name,
1298
+ toolName: firstToolCall.toolName,
1299
+ result: [{ type: "textPart", id: createId(), text: reason }]
1300
+ }
1301
+ ],
1302
+ usage
1303
+ });
1304
+ }
1305
+ throw new Error(`Unexpected finish reason: ${finishReason}`);
1306
+ }
1307
+ function getMetaInstruction(startedAt) {
1308
+ return dedent`
1309
+ IMPORTANT:
1310
+ Based on the user's initial message, you must determine what needs to be done.
1311
+ You must iterate through hypothesis and verification to fulfill the task.
1312
+ YOU MUST CONTINUE TO CALL TOOLS UNTIL THE TASK IS COMPLETE.
1313
+ If you do not call tools, the task will be considered complete, and the agent loop will end.
1314
+
1315
+ You operate in an agent loop, iteratively completing tasks through these steps:
1316
+ 1. Analyze Events: Understand user needs and current state through the event stream, focusing on the latest user messages and execution results
1317
+ 2. Select Tools: Choose the next tool call based on current state, task planning, relevant knowledge, and available data APIs
1318
+ 3. Wait for Execution: The selected tool action will be executed by the sandbox environment with new observations added to the event stream
1319
+ 4. Iterate: Choose only one tool call per iteration, patiently repeat the above steps until task completion
1320
+ 5. Notify Task Completion: Call the attemptCompletion tool to inform the user when the task is complete
1321
+ 6. Generate Final Results: Produce a final result that clearly describes each task you performed, step by step
1322
+
1323
+ Conditions for ending the agent loop:
1324
+ If any of the following apply, **immediately call the attemptCompletion tool**.
1325
+ When the agent loop must end, calling any tool other than attemptCompletion is highly dangerous.
1326
+ Under all circumstances, strictly follow this rule.
1327
+ - When the task is complete
1328
+ - When the user's request is outside your expertise
1329
+ - When the user's request is unintelligible
1330
+
1331
+ Rules for requests outside your area of expertise:
1332
+ - Tell your area of expertise to the user in final results
1333
+
1334
+ Environment information:
1335
+ - Current time is ${new Date(startedAt).toISOString()}
1336
+ - Current working directory is ${process.cwd()}
1337
+ `;
1338
+ }
1339
+ function createInstructionMessage(expert, experts, startedAt) {
1340
+ const instruction = dedent`
1341
+ You are Perstack, an AI expert that tackles tasks requested by users by utilizing all available tools.
1342
+
1343
+ (The following information describes your nature and role as an AI, the mechanisms of the AI system, and other meta-cognitive aspects.)
1344
+
1345
+ ${getMetaInstruction(startedAt)}
1346
+
1347
+ ---
1348
+ (The following describes the objective, steps, rules, etc. regarding your expert task.)
1349
+
1350
+ ${expert.instruction}
1351
+
1352
+ ---
1353
+ (The following is an overview of each skill and the rules for calling tools.)
1354
+
1355
+ ${getSkillRules(expert)}
1356
+
1357
+ ---
1358
+ (The following is an overview of each delegate expert and the rules for calling tools.)
1359
+
1360
+ You can delegate tasks to the following experts by calling delegate expert name as a tool:
1361
+
1362
+ ${getDelegateRules(expert, experts)}
1363
+ `;
1364
+ return {
1365
+ type: "instructionMessage",
1366
+ contents: [
1367
+ {
1368
+ id: createId(),
1369
+ type: "textPart",
1370
+ text: instruction
1371
+ }
1372
+ ],
1373
+ id: createId(),
1374
+ cache: true
1375
+ };
1376
+ }
1377
+ function getSkillRules(expert) {
1378
+ return Object.values(expert.skills).reduce((acc, skill) => {
1379
+ if (!skill.rule) {
1380
+ return acc;
1381
+ }
1382
+ return dedent`
1383
+ ${acc}
1384
+
1385
+ "${skill.name}" skill rules:
1386
+ ${skill.rule}
1387
+ `.trim();
1388
+ }, "");
1389
+ }
1390
+ function getDelegateRules(expert, experts) {
1391
+ return expert.delegates.reduce((acc, delegateExpertName) => {
1392
+ const delegate = experts[delegateExpertName];
1393
+ if (!delegate) {
1394
+ return acc;
1395
+ }
1396
+ return dedent`
1397
+ ${acc}
1398
+
1399
+ About "${delegate.name}":
1400
+ ${delegate.description}
1401
+ `.trim();
1402
+ }, "");
1403
+ }
1404
+
1405
+ // src/state-machine/states/init.ts
1406
+ async function initLogic({
1407
+ setting,
1408
+ checkpoint
1409
+ }) {
1410
+ const { expertKey, experts } = setting;
1411
+ const expert = experts[expertKey];
1412
+ switch (checkpoint.status) {
1413
+ case "init": {
1414
+ if (!setting.input.text) {
1415
+ throw new Error("Input message is undefined");
1416
+ }
1417
+ return startRun(setting, checkpoint, {
1418
+ initialCheckpoint: checkpoint,
1419
+ inputMessages: [
1420
+ createInstructionMessage(expert, experts, setting.startedAt),
1421
+ createUserMessage([{ type: "textPart", text: setting.input.text }])
1422
+ ]
1423
+ });
1424
+ }
1425
+ case "stoppedByDelegate":
1426
+ case "stoppedByInteractiveTool": {
1427
+ if (!setting.input.interactiveToolCallResult) {
1428
+ throw new Error("Interactive tool call result is undefined");
1429
+ }
1430
+ const { toolCallId, toolName, skillName, text } = setting.input.interactiveToolCallResult;
1431
+ const pendingToolCalls = checkpoint.pendingToolCalls ?? [];
1432
+ const newToolResult = {
1433
+ id: toolCallId,
1434
+ skillName,
1435
+ toolName,
1436
+ result: [{ type: "textPart", id: createId(), text }]
1437
+ };
1438
+ const updatedPartialResults = [...checkpoint.partialToolResults ?? [], newToolResult];
1439
+ const updatedPendingToolCalls = pendingToolCalls.filter((tc) => tc.id !== toolCallId);
1440
+ const updatedCheckpoint = {
1441
+ ...checkpoint,
1442
+ partialToolResults: updatedPartialResults,
1443
+ pendingToolCalls: updatedPendingToolCalls.length > 0 ? updatedPendingToolCalls : void 0
1444
+ };
1445
+ return startRun(setting, updatedCheckpoint, {
1446
+ initialCheckpoint: updatedCheckpoint,
1447
+ inputMessages: []
1448
+ });
1449
+ }
1450
+ default:
1451
+ if (!setting.input.text) {
1452
+ throw new Error("Input message is undefined");
1453
+ }
1454
+ return startRun(setting, checkpoint, {
1455
+ initialCheckpoint: checkpoint,
1456
+ inputMessages: [createUserMessage([{ type: "textPart", text: setting.input.text }])]
1457
+ });
1458
+ }
1459
+ }
1460
+ async function preparingForStepLogic({
1461
+ setting,
1462
+ checkpoint
1463
+ }) {
1464
+ if (checkpoint.pendingToolCalls && checkpoint.pendingToolCalls.length > 0) {
1465
+ return resumeToolCalls(setting, checkpoint, {
1466
+ pendingToolCalls: checkpoint.pendingToolCalls,
1467
+ partialToolResults: checkpoint.partialToolResults ?? []
1468
+ });
1469
+ }
1470
+ if (checkpoint.partialToolResults && checkpoint.partialToolResults.length > 0) {
1471
+ const toolResultParts = checkpoint.partialToolResults.map((tr) => ({
1472
+ type: "toolResultPart",
1473
+ toolCallId: tr.id,
1474
+ toolName: tr.toolName,
1475
+ contents: tr.result.filter(
1476
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1477
+ )
1478
+ }));
1479
+ return finishAllToolCalls(setting, checkpoint, {
1480
+ newMessages: [createToolMessage(toolResultParts)]
1481
+ });
1482
+ }
1483
+ return startGeneration(setting, checkpoint, {
1484
+ messages: checkpoint.messages
1485
+ });
1486
+ }
1487
+ async function resolvingToolResultLogic({
1488
+ setting,
1489
+ checkpoint,
1490
+ step
1491
+ }) {
1492
+ if (!step.toolCalls || !step.toolResults || step.toolResults.length === 0) {
1493
+ throw new Error("No tool calls or tool results found");
1494
+ }
1495
+ const toolResultParts = step.toolResults.map((toolResult) => {
1496
+ const toolCall = step.toolCalls?.find((tc) => tc.id === toolResult.id);
1497
+ return {
1498
+ type: "toolResultPart",
1499
+ toolCallId: toolResult.id,
1500
+ toolName: toolCall?.toolName ?? toolResult.toolName,
1501
+ contents: toolResult.result.filter(
1502
+ (part) => part.type === "textPart" || part.type === "imageInlinePart" || part.type === "fileInlinePart"
1503
+ )
1504
+ };
1505
+ });
1506
+ return finishToolCall(setting, checkpoint, {
1507
+ newMessages: [createToolMessage(toolResultParts)]
1508
+ });
1509
+ }
1510
+
1511
+ // src/state-machine/states/resolving-thought.ts
1512
+ async function resolvingThoughtLogic(context) {
1513
+ return resolvingToolResultLogic(context);
1514
+ }
1515
+
1516
+ // src/state-machine/machine.ts
1517
+ var runtimeStateMachine = setup({
1518
+ types: {
1519
+ input: {},
1520
+ context: {},
1521
+ events: {}
1522
+ }
1523
+ }).createMachine({
1524
+ /** @xstate-layout N4IgpgJg5mDOIC5QCUCuA7AdASXQSwBcBiWAgQwCcC10BtABgF1FQAHAe1kL3fRZAAeiAKz0AzJgDsAJgAsksZNlixs+gDZZAGhABPRIszCAjGIAcY4crPHhJyQF8HOmpgAKFMK0p50UAGLsFADKBF4k5FQA4mDoYBRkBDx0TPwcXEm8-EIIZmaymPRK8saSopL00tI6+gjq9JjSltLG9HnqZkqdTi4YmDFxCUl+ACrs7AA2AMJkExNEngQUugzMSCDp3FnrOcam0pgAnHbS9GeSZsIq6jWIh2KHUsYd9MbSwl2S6j0grgPxiV8UDGkxmcyIAGNZhMQRNVmlOFs+DtEPt1Jg3odpGVbFcrLcENIzOjbGYipIZMpnmYfn9YgDhsDxtNoZDobgwgkIUkAG5gWHw9abTLI0C7ImSTDqYTY2S45TCCwE54HUr1WRXSqHcRVWl9f5DIGwsHzKFzAAiYAmYCgiTAgrYiJF2VRYnoj1ehwskkOZTEz2EBMMJnMFnezxUF2+zl+fRNRuZCzgkz5sOQcFQEwIDo2TuSLoQpWJUvowl9Flk0sqBLlZik2Mkpjyan90d6WHjo0TnlgKf5AAt2KgoP3s6khXntmLUT6JL6PkoFJX1MZtHoRKIpdrK3sqorpG3Yx3oQnJknexM+W4IAAzfx4a054X5lGFn2PMTYlSyWQH32WAl1DKKV1Q6L1V1EWQ9WPOZT3mHs+2wABbMgYHvR9x0dDIX2nN8zEeOVjGORsmgsMQCTJYRMGJORwP9MwDxpGNXE7Jkz0SMIkNYAgpnYLjrRFJ9J1FQQZzJTBPxUfDDjlN1OgJIkSUVUtDlXCpsUPVx0wvHk4O0zNiBvXw8FgftjWhITsKnUTCU-SU92JMlfSaGtjDrGQKSbfJxGeaDMG0lMjUHYdRyIIz8FM8y5kspECyabFGneRz3RkN011qOwGnUbcVzeJKDz8gLLyBa87wfMAwuMyLmRNGLnVfeL7KSl5nPI9c6mA9RQPwmwNVLQrk2KvxkNQsB0Iq8KTLMmqLMw3MrJEnJGsSxUWtSgkfSU-JDmIqNKj8g1AT8Gh9KzSE+NYASwBoOqcJs+K61sDo5SKBifwU4tSRUtTKi+KDmLjE9hvQTkyG5PBU0TUh2FYGgACFdA5AFwchyZbus3YyklYRst-DUlDEaU2tqFUMS+NyPmlPZDiAvzWMta1bTCCIYfh3QGZtO10cWmcLiOQnG3qHHiSDbGvM-Ex1EjYk-PvCL+yBUJwghXhhlQfl2AAOTAAQCCV1hubiqxjCMRUZXMGSvVUZV6A1LcuorDp8I0WWqoVvx9ZZ2GMARgBRAQITASBIAAWTIAR9dgQ2Gs0Ki8nyK4bCuDVRZNzRwxse4GK6pwY3QdgIDgfgaARBaCxUBosU0cw5TLUiCQAWlkR4nPdbVCeyld-vbHB8AIUvYtfRQ6yr6xa6xcwCTLaiadj1dKxk1phD8jwvB8PxAhCMJWAH+rcKAyuyVaa5Wi+KeW9njV59xpeDvpQ0u1BaFd7u3Y2keDpO7LW2FDLc+Z6AnsPEO4ZYAxghMOCL8MaojsJKYi8cHZ5C9EGNoRg1CdH3GcZeYD-KDV0o-CYp1+4TjLg1cQkp-SfjDE9Ew1R2o-hNnsUs1MfR-UuANHSQUhwjmIVhQeuFTg-ilF8GUblTgKDoRlBQhQJEaiJMnfCHDAp+FKuNKBPNCSlgaOIeQttWxS0DO1bKdY9HvH9D+FotMcFFXwVAEaaFyrqLirbBo1M7KvFUDJCiRJGgalUM3LEMk5R30GEdKAJ0MxZicWQlQEklAyillWVoZgUF1isG0MoDEsF0yBnYkGyNeQa0mNE3CtgiIYnEEBToFwvS+mVPkU20l8JWG1OIHJsE-AcyZmAEpNlbBqEwLo7yVRbY3HatPfCXUOgfCsGSTQmk+hyymorbevSloykeFYCkrxfqrmJiIaRRQ7JbP8Q8PyoQYasEgGsxA2JWiNB-jjdQTRqb1IkP6OwRF8KMSAbnBwQA */
1525
+ id: "Run",
1526
+ initial: "Init",
1527
+ context: ({ input }) => ({
1528
+ setting: input.setting,
1529
+ checkpoint: input.initialCheckpoint,
1530
+ step: {
1531
+ stepNumber: input.initialCheckpoint.stepNumber,
1532
+ inputMessages: [],
1533
+ newMessages: [],
1534
+ usage: createEmptyUsage(),
1535
+ startedAt: Date.now()
1536
+ },
1537
+ eventListener: input.eventListener,
1538
+ skillManagers: input.skillManagers
1539
+ }),
1540
+ states: {
1541
+ Init: {
1542
+ on: {
1543
+ startRun: {
1544
+ target: "PreparingForStep",
1545
+ actions: assign({
1546
+ checkpoint: ({ context, event }) => ({
1547
+ ...context.checkpoint,
1548
+ status: "proceeding",
1549
+ messages: [...context.checkpoint.messages, ...event.inputMessages],
1550
+ pendingToolCalls: event.initialCheckpoint.pendingToolCalls,
1551
+ partialToolResults: event.initialCheckpoint.partialToolResults
1552
+ }),
1553
+ step: ({ context, event }) => ({
1554
+ ...context.step,
1555
+ inputMessages: event.inputMessages
1556
+ })
1557
+ })
1558
+ }
1559
+ }
1560
+ },
1561
+ PreparingForStep: {
1562
+ on: {
1563
+ startGeneration: {
1564
+ target: "GeneratingToolCall",
1565
+ actions: assign({
1566
+ step: ({ context, event }) => ({
1567
+ stepNumber: context.checkpoint.stepNumber,
1568
+ inputMessages: context.step.inputMessages ?? [],
1569
+ newMessages: [],
1570
+ usage: createEmptyUsage(),
1571
+ startedAt: event.timestamp
1572
+ })
1573
+ })
1574
+ },
1575
+ resumeToolCalls: {
1576
+ target: "CallingTool",
1577
+ actions: assign({
1578
+ step: ({ context, event }) => ({
1579
+ stepNumber: context.checkpoint.stepNumber,
1580
+ inputMessages: context.step.inputMessages ?? [],
1581
+ newMessages: context.step.newMessages,
1582
+ toolCalls: context.step.toolCalls,
1583
+ toolResults: event.partialToolResults,
1584
+ pendingToolCalls: event.pendingToolCalls,
1585
+ usage: context.step.usage,
1586
+ startedAt: context.step.startedAt
1587
+ })
1588
+ })
1589
+ },
1590
+ finishAllToolCalls: {
1591
+ target: "FinishingStep",
1592
+ actions: assign({
1593
+ checkpoint: ({ context, event }) => ({
1594
+ ...context.checkpoint,
1595
+ messages: [...context.checkpoint.messages, ...event.newMessages],
1596
+ pendingToolCalls: void 0,
1597
+ partialToolResults: void 0
1598
+ }),
1599
+ step: ({ context, event }) => ({
1600
+ ...context.step,
1601
+ newMessages: [...context.step.newMessages, ...event.newMessages],
1602
+ toolResults: context.checkpoint.partialToolResults,
1603
+ pendingToolCalls: void 0
1604
+ })
1605
+ })
1606
+ }
1607
+ }
1608
+ },
1609
+ GeneratingToolCall: {
1610
+ on: {
1611
+ retry: {
1612
+ target: "FinishingStep",
1613
+ actions: assign({
1614
+ checkpoint: ({ context, event }) => ({
1615
+ ...context.checkpoint,
1616
+ messages: [...context.checkpoint.messages, ...event.newMessages],
1617
+ usage: sumUsage(context.checkpoint.usage, event.usage)
1618
+ }),
1619
+ step: ({ context, event }) => ({
1620
+ ...context.step,
1621
+ newMessages: event.newMessages,
1622
+ toolCalls: event.toolCalls,
1623
+ toolResults: event.toolResults,
1624
+ usage: sumUsage(context.step.usage, event.usage)
1625
+ })
1626
+ })
1627
+ },
1628
+ callTools: {
1629
+ target: "CallingTool",
1630
+ actions: assign({
1631
+ checkpoint: ({ context, event }) => ({
1632
+ ...context.checkpoint,
1633
+ messages: [...context.checkpoint.messages, event.newMessage],
1634
+ usage: sumUsage(context.checkpoint.usage, event.usage),
1635
+ contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
1636
+ }),
1637
+ step: ({ context, event }) => ({
1638
+ ...context.step,
1639
+ newMessages: [event.newMessage],
1640
+ toolCalls: event.toolCalls,
1641
+ usage: sumUsage(context.step.usage, event.usage)
1642
+ })
1643
+ })
1644
+ },
1645
+ callInteractiveTool: {
1646
+ target: "CallingInteractiveTool",
1647
+ actions: assign({
1648
+ checkpoint: ({ context, event }) => ({
1649
+ ...context.checkpoint,
1650
+ messages: [...context.checkpoint.messages, event.newMessage],
1651
+ usage: sumUsage(context.checkpoint.usage, event.usage),
1652
+ contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
1653
+ }),
1654
+ step: ({ context, event }) => ({
1655
+ ...context.step,
1656
+ newMessages: [event.newMessage],
1657
+ toolCalls: [event.toolCall],
1658
+ usage: sumUsage(context.step.usage, event.usage)
1659
+ })
1660
+ })
1661
+ },
1662
+ callDelegate: {
1663
+ target: "CallingDelegate",
1664
+ actions: assign({
1665
+ checkpoint: ({ context, event }) => ({
1666
+ ...context.checkpoint,
1667
+ messages: [...context.checkpoint.messages, event.newMessage],
1668
+ usage: sumUsage(context.checkpoint.usage, event.usage),
1669
+ contextWindowUsage: context.checkpoint.contextWindow ? calculateContextWindowUsage(event.usage, context.checkpoint.contextWindow) : void 0
1670
+ }),
1671
+ step: ({ context, event }) => ({
1672
+ ...context.step,
1673
+ newMessages: [event.newMessage],
1674
+ toolCalls: event.toolCalls,
1675
+ usage: sumUsage(context.step.usage, event.usage)
1676
+ })
1677
+ })
1678
+ }
1679
+ }
1680
+ },
1681
+ CallingTool: {
1682
+ on: {
1683
+ resolveToolResults: {
1684
+ target: "ResolvingToolResult",
1685
+ actions: assign({
1686
+ step: ({ context, event }) => ({
1687
+ ...context.step,
1688
+ toolResults: event.toolResults,
1689
+ pendingToolCalls: void 0
1690
+ })
1691
+ })
1692
+ },
1693
+ resolveThought: {
1694
+ target: "ResolvingThought",
1695
+ actions: assign({
1696
+ step: ({ context, event }) => ({
1697
+ ...context.step,
1698
+ toolResults: [event.toolResult]
1699
+ })
1700
+ })
1701
+ },
1702
+ attemptCompletion: {
1703
+ target: "GeneratingRunResult",
1704
+ actions: assign({
1705
+ step: ({ context, event }) => ({
1706
+ ...context.step,
1707
+ toolResults: [event.toolResult]
1708
+ })
1709
+ })
1710
+ },
1711
+ callDelegate: {
1712
+ target: "CallingDelegate",
1713
+ actions: assign({
1714
+ step: ({ context }) => ({
1715
+ ...context.step,
1716
+ toolCalls: context.step.toolCalls,
1717
+ toolResults: context.step.toolResults,
1718
+ pendingToolCalls: context.step.pendingToolCalls,
1719
+ partialToolResults: context.step.partialToolResults
1720
+ })
1721
+ })
1722
+ },
1723
+ callInteractiveTool: {
1724
+ target: "CallingInteractiveTool",
1725
+ actions: assign({
1726
+ step: ({ context }) => ({
1727
+ ...context.step,
1728
+ toolCalls: context.step.toolCalls,
1729
+ toolResults: context.step.toolResults,
1730
+ pendingToolCalls: context.step.pendingToolCalls,
1731
+ partialToolResults: context.step.partialToolResults
1732
+ })
1733
+ })
1734
+ }
1735
+ }
1736
+ },
1737
+ ResolvingToolResult: {
1738
+ on: {
1739
+ finishToolCall: {
1740
+ target: "FinishingStep",
1741
+ actions: assign({
1742
+ checkpoint: ({ context, event }) => ({
1743
+ ...context.checkpoint,
1744
+ messages: [...context.checkpoint.messages, ...event.newMessages]
1745
+ }),
1746
+ step: ({ context, event }) => ({
1747
+ ...context.step,
1748
+ newMessages: [...context.step.newMessages, ...event.newMessages]
1749
+ })
1750
+ })
1751
+ }
1752
+ }
1753
+ },
1754
+ ResolvingThought: {
1755
+ on: {
1756
+ finishToolCall: {
1757
+ target: "FinishingStep",
1758
+ actions: assign({
1759
+ checkpoint: ({ context, event }) => ({
1760
+ ...context.checkpoint,
1761
+ messages: [...context.checkpoint.messages, ...event.newMessages]
1762
+ }),
1763
+ step: ({ context, event }) => ({
1764
+ ...context.step,
1765
+ newMessages: [...context.step.newMessages, ...event.newMessages]
1766
+ })
1767
+ })
1768
+ }
1769
+ }
1770
+ },
1771
+ GeneratingRunResult: {
1772
+ on: {
1773
+ retry: {
1774
+ target: "FinishingStep",
1775
+ actions: assign({
1776
+ checkpoint: ({ context, event }) => ({
1777
+ ...context.checkpoint,
1778
+ messages: [...context.checkpoint.messages, ...event.newMessages],
1779
+ usage: sumUsage(context.checkpoint.usage, event.usage)
1780
+ }),
1781
+ step: ({ context, event }) => ({
1782
+ ...context.step,
1783
+ newMessages: event.newMessages,
1784
+ toolCalls: event.toolCalls,
1785
+ toolResults: event.toolResults,
1786
+ usage: sumUsage(context.step.usage, event.usage)
1787
+ })
1788
+ })
1789
+ },
1790
+ completeRun: {
1791
+ target: "Stopped",
1792
+ actions: assign({
1793
+ checkpoint: ({ event }) => event.checkpoint,
1794
+ step: ({ event }) => ({
1795
+ ...event.step,
1796
+ inputMessages: void 0
1797
+ })
1798
+ })
1799
+ }
1800
+ }
1801
+ },
1802
+ CallingInteractiveTool: {
1803
+ on: {
1804
+ stopRunByInteractiveTool: {
1805
+ target: "Stopped",
1806
+ actions: assign({
1807
+ checkpoint: ({ event }) => event.checkpoint,
1808
+ step: ({ event }) => ({
1809
+ ...event.step,
1810
+ inputMessages: void 0
1811
+ })
1812
+ })
1813
+ }
1814
+ }
1815
+ },
1816
+ CallingDelegate: {
1817
+ on: {
1818
+ stopRunByDelegate: {
1819
+ target: "Stopped",
1820
+ actions: assign({
1821
+ checkpoint: ({ event }) => event.checkpoint,
1822
+ step: ({ event }) => ({
1823
+ ...event.step,
1824
+ inputMessages: void 0
1825
+ })
1826
+ })
1827
+ }
1828
+ }
1829
+ },
1830
+ FinishingStep: {
1831
+ on: {
1832
+ continueToNextStep: {
1833
+ target: "PreparingForStep",
1834
+ actions: assign({
1835
+ checkpoint: ({ event }) => event.nextCheckpoint,
1836
+ step: ({ event }) => ({
1837
+ ...event.step,
1838
+ inputMessages: void 0
1839
+ })
1840
+ }),
1841
+ reenter: true
1842
+ },
1843
+ stopRunByExceededMaxSteps: {
1844
+ target: "Stopped",
1845
+ actions: assign({
1846
+ checkpoint: ({ event }) => event.checkpoint,
1847
+ step: ({ event }) => ({
1848
+ ...event.step,
1849
+ inputMessages: void 0
1850
+ })
1851
+ })
1852
+ }
1853
+ }
1854
+ },
1855
+ Stopped: {
1856
+ type: "final"
1857
+ }
1858
+ }
1859
+ });
1860
+ var StateMachineLogics = {
1861
+ Init: initLogic,
1862
+ PreparingForStep: preparingForStepLogic,
1863
+ GeneratingToolCall: generatingToolCallLogic,
1864
+ CallingTool: callingToolLogic,
1865
+ ResolvingToolResult: resolvingToolResultLogic,
1866
+ ResolvingThought: resolvingThoughtLogic,
1867
+ GeneratingRunResult: generatingRunResultLogic,
1868
+ CallingInteractiveTool: callingInteractiveToolLogic,
1869
+ CallingDelegate: callingDelegateLogic,
1870
+ FinishingStep: finishingStepLogic
1871
+ };
1872
+ async function executeStateMachine(params) {
1873
+ const {
1874
+ setting,
1875
+ initialCheckpoint,
1876
+ eventListener,
1877
+ skillManagers,
1878
+ eventEmitter,
1879
+ storeCheckpoint,
1880
+ shouldContinueRun
1881
+ } = params;
1882
+ const runActor = createActor(runtimeStateMachine, {
1883
+ input: {
1884
+ setting,
1885
+ initialCheckpoint,
1886
+ eventListener,
1887
+ skillManagers
1888
+ }
1889
+ });
1890
+ return new Promise((resolve, reject) => {
1891
+ runActor.subscribe(async (runState) => {
1892
+ try {
1893
+ if (runState.value === "Stopped") {
1894
+ const { checkpoint, skillManagers: skillManagers2 } = runState.context;
1895
+ if (!checkpoint) {
1896
+ throw new Error("Checkpoint is undefined");
1897
+ }
1898
+ await closeSkillManagers(skillManagers2);
1899
+ resolve(checkpoint);
1900
+ } else {
1901
+ const event = await StateMachineLogics[runState.value](runState.context);
1902
+ if ("checkpoint" in event) {
1903
+ await storeCheckpoint(event.checkpoint);
1904
+ }
1905
+ await eventEmitter.emit(event);
1906
+ if (shouldContinueRun) {
1907
+ const shouldContinue = await shouldContinueRun(
1908
+ runState.context.setting,
1909
+ runState.context.checkpoint,
1910
+ runState.context.step
1911
+ );
1912
+ if (!shouldContinue) {
1913
+ runActor.stop();
1914
+ await closeSkillManagers(runState.context.skillManagers);
1915
+ resolve(runState.context.checkpoint);
1916
+ return;
1917
+ }
1918
+ }
1919
+ runActor.send(event);
1920
+ }
1921
+ } catch (error) {
1922
+ await closeSkillManagers(skillManagers).catch(() => {
1923
+ });
1924
+ reject(error);
1925
+ }
1926
+ });
1927
+ runActor.start();
1928
+ });
1929
+ }
1930
+ var RunEventEmitter = class {
1931
+ listeners = [];
1932
+ subscribe(listener) {
1933
+ this.listeners.push(listener);
1934
+ }
1935
+ async emit(event) {
1936
+ const errors = [];
1937
+ for (const listener of this.listeners) {
1938
+ try {
1939
+ await listener({
1940
+ ...event,
1941
+ id: createId(),
1942
+ timestamp: Date.now()
1943
+ });
1944
+ } catch (error) {
1945
+ errors.push(error instanceof Error ? error : new Error(String(error)));
1946
+ }
1947
+ }
1948
+ if (errors.length > 0) {
1949
+ throw new AggregateError(errors, "One or more event listeners failed");
1950
+ }
1951
+ }
1952
+ };
1953
+
1954
+ // src/helpers/checkpoint.ts
1955
+ function createInitialCheckpoint(checkpointId, params) {
1956
+ return {
1957
+ id: checkpointId,
1958
+ jobId: params.jobId,
1959
+ runId: params.runId,
1960
+ expert: {
1961
+ key: params.expertKey,
1962
+ name: params.expert.name,
1963
+ version: params.expert.version
1964
+ },
1965
+ stepNumber: 1,
1966
+ status: "init",
1967
+ messages: [],
1968
+ usage: createEmptyUsage(),
1969
+ contextWindow: params.contextWindow,
1970
+ contextWindowUsage: params.contextWindow ? 0 : void 0
1971
+ };
1972
+ }
1973
+ function createNextStepCheckpoint(checkpointId, checkpoint) {
1974
+ return {
1975
+ ...checkpoint,
1976
+ id: checkpointId,
1977
+ stepNumber: checkpoint.stepNumber + 1
1978
+ };
1979
+ }
1980
+ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentCheckpoint) {
1981
+ const { messages, delegatedBy } = resultCheckpoint;
1982
+ if (!delegatedBy) {
1983
+ throw new Error("delegatedBy is required for buildDelegationReturnState");
1984
+ }
1985
+ const delegateResultMessage = messages[messages.length - 1];
1986
+ if (!delegateResultMessage || delegateResultMessage.type !== "expertMessage") {
1987
+ throw new Error("Delegation error: delegation result message is incorrect");
1988
+ }
1989
+ const delegateText = delegateResultMessage.contents.find((content) => content.type === "textPart");
1990
+ if (!delegateText) {
1991
+ throw new Error("Delegation error: delegation result message does not contain a text");
1992
+ }
1993
+ const { expert, toolCallId, toolName } = delegatedBy;
1994
+ return {
1995
+ setting: {
1996
+ ...currentSetting,
1997
+ expertKey: expert.key,
1998
+ input: {
1999
+ interactiveToolCallResult: {
2000
+ toolCallId,
2001
+ toolName,
2002
+ skillName: `delegate/${resultCheckpoint.expert.key}`,
2003
+ text: delegateText.text
2004
+ }
2005
+ }
2006
+ },
2007
+ checkpoint: {
2008
+ ...parentCheckpoint,
2009
+ stepNumber: resultCheckpoint.stepNumber,
2010
+ usage: resultCheckpoint.usage,
2011
+ pendingToolCalls: parentCheckpoint.pendingToolCalls,
2012
+ partialToolResults: parentCheckpoint.partialToolResults
2013
+ }
2014
+ };
2015
+ }
2016
+ function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
2017
+ const { delegateTo } = resultCheckpoint;
2018
+ if (!delegateTo || delegateTo.length === 0) {
2019
+ throw new Error("delegateTo is required for buildDelegateToState");
2020
+ }
2021
+ const firstDelegation = delegateTo[0];
2022
+ const { expert, toolCallId, toolName, query } = firstDelegation;
2023
+ return {
2024
+ setting: {
2025
+ ...currentSetting,
2026
+ expertKey: expert.key,
2027
+ input: {
2028
+ text: query
2029
+ }
2030
+ },
2031
+ checkpoint: {
2032
+ ...resultCheckpoint,
2033
+ status: "init",
2034
+ messages: [],
2035
+ expert: {
2036
+ key: expert.key,
2037
+ name: expert.name,
2038
+ version: expert.version
2039
+ },
2040
+ delegatedBy: {
2041
+ expert: {
2042
+ key: currentExpert.key,
2043
+ name: currentExpert.name,
2044
+ version: currentExpert.version
2045
+ },
2046
+ toolCallId,
2047
+ toolName,
2048
+ checkpointId: resultCheckpoint.id
2049
+ },
2050
+ usage: resultCheckpoint.usage,
2051
+ pendingToolCalls: void 0,
2052
+ partialToolResults: void 0
2053
+ }
2054
+ };
2055
+ }
2056
+ async function resolveExpertToRun(expertKey, experts, clientOptions) {
2057
+ if (experts[expertKey]) {
2058
+ return experts[expertKey];
2059
+ }
2060
+ const client = new ApiV1Client({
2061
+ baseUrl: clientOptions.perstackApiBaseUrl,
2062
+ apiKey: clientOptions.perstackApiKey
2063
+ });
2064
+ const { expert } = await client.registry.experts.get({ expertKey });
2065
+ return toRuntimeExpert(expert);
2066
+ }
2067
+ function toRuntimeExpert(expert) {
2068
+ const skills = Object.fromEntries(
2069
+ Object.entries(expert.skills).map(([name, skill]) => {
2070
+ switch (skill.type) {
2071
+ case "mcpStdioSkill":
2072
+ return [name, { ...skill, name }];
2073
+ case "mcpSseSkill":
2074
+ return [name, { ...skill, name }];
2075
+ case "interactiveSkill":
2076
+ return [name, { ...skill, name }];
2077
+ default: {
2078
+ throw new Error(`Unknown skill type: ${skill.type}`);
2079
+ }
2080
+ }
2081
+ })
2082
+ );
2083
+ return { ...expert, skills };
2084
+ }
2085
+
2086
+ // src/helpers/setup-experts.ts
2087
+ async function setupExperts(setting, resolveExpertToRun2 = resolveExpertToRun) {
2088
+ const { expertKey } = setting;
2089
+ const experts = { ...setting.experts };
2090
+ const clientOptions = {
2091
+ perstackApiBaseUrl: setting.perstackApiBaseUrl,
2092
+ perstackApiKey: setting.perstackApiKey
2093
+ };
2094
+ const expertToRun = await resolveExpertToRun2(expertKey, experts, clientOptions);
2095
+ experts[expertKey] = expertToRun;
2096
+ for (const delegateName of expertToRun.delegates) {
2097
+ const delegate = await resolveExpertToRun2(delegateName, experts, clientOptions);
2098
+ if (!delegate) {
2099
+ throw new Error(`Delegate ${delegateName} not found`);
2100
+ }
2101
+ experts[delegateName] = delegate;
2102
+ }
2103
+ return { expertToRun, experts };
2104
+ }
2105
+
2106
+ // src/run.ts
2107
+ var noopStoreCheckpoint = async () => {
2108
+ };
2109
+ var noopStoreEvent = async () => {
2110
+ };
2111
+ var noopStoreJob = () => {
2112
+ };
2113
+ var noopRetrieveJob = () => void 0;
2114
+ var noopRetrieveCheckpoint = async (_jobId, _checkpointId) => {
2115
+ throw new Error("retrieveCheckpoint not provided");
2116
+ };
2117
+ var defaultCreateJob = (jobId, expertKey, maxSteps) => ({
2118
+ id: jobId,
2119
+ coordinatorExpertKey: expertKey,
2120
+ status: "running",
2121
+ totalSteps: 0,
2122
+ startedAt: Date.now(),
2123
+ maxSteps,
2124
+ usage: createEmptyUsage()
2125
+ });
2126
+ async function run(runInput, options) {
2127
+ const runParams = runParamsSchema.parse(runInput);
2128
+ const storeCheckpoint = options?.storeCheckpoint ?? noopStoreCheckpoint;
2129
+ const storeEvent = options?.storeEvent ?? noopStoreEvent;
2130
+ const storeJob = options?.storeJob ?? noopStoreJob;
2131
+ const retrieveJob = options?.retrieveJob ?? noopRetrieveJob;
2132
+ const retrieveCheckpoint = options?.retrieveCheckpoint ?? noopRetrieveCheckpoint;
2133
+ const createJob = options?.createJob ?? defaultCreateJob;
2134
+ const eventListener = createEventListener(options?.eventListener, storeEvent);
2135
+ const eventEmitter = new RunEventEmitter();
2136
+ eventEmitter.subscribe(eventListener);
2137
+ let { setting, checkpoint } = runParams;
2138
+ const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
2139
+ let job = retrieveJob(setting.jobId) ?? createJob(setting.jobId, setting.expertKey, setting.maxSteps);
2140
+ if (job.status !== "running") {
2141
+ job = { ...job, status: "running", finishedAt: void 0 };
2142
+ }
2143
+ storeJob(job);
2144
+ while (true) {
2145
+ const { expertToRun, experts } = await setupExperts(setting, options?.resolveExpertToRun);
2146
+ if (options?.eventListener) {
2147
+ const initEvent = createRuntimeEvent("initializeRuntime", setting.jobId, setting.runId, {
2148
+ runtimeVersion: package_default.version,
2149
+ runtime: "perstack",
2150
+ expertName: expertToRun.name,
2151
+ experts: Object.keys(experts),
2152
+ model: setting.model,
2153
+ temperature: setting.temperature,
2154
+ maxSteps: setting.maxSteps,
2155
+ maxRetries: setting.maxRetries,
2156
+ timeout: setting.timeout,
2157
+ query: setting.input.text,
2158
+ interactiveToolCall: setting.input.interactiveToolCallResult
2159
+ });
2160
+ options.eventListener(initEvent);
2161
+ }
2162
+ const skillManagers = await getSkillManagers(
2163
+ expertToRun,
2164
+ experts,
2165
+ setting,
2166
+ options?.eventListener,
2167
+ { isDelegatedRun: !!checkpoint?.delegatedBy }
2168
+ );
2169
+ const initialCheckpoint = checkpoint ? createNextStepCheckpoint(createId(), checkpoint) : createInitialCheckpoint(createId(), {
2170
+ jobId: setting.jobId,
2171
+ runId: setting.runId,
2172
+ expertKey: setting.expertKey,
2173
+ expert: expertToRun,
2174
+ contextWindow
2175
+ });
2176
+ const runResultCheckpoint = await executeStateMachine({
2177
+ setting: { ...setting, experts },
2178
+ initialCheckpoint,
2179
+ eventListener,
2180
+ skillManagers,
2181
+ eventEmitter,
2182
+ storeCheckpoint,
2183
+ shouldContinueRun: options?.shouldContinueRun
2184
+ });
2185
+ job = {
2186
+ ...job,
2187
+ totalSteps: runResultCheckpoint.stepNumber,
2188
+ usage: runResultCheckpoint.usage
2189
+ };
2190
+ switch (runResultCheckpoint.status) {
2191
+ case "completed": {
2192
+ if (options?.returnOnDelegationComplete) {
2193
+ storeJob(job);
2194
+ return runResultCheckpoint;
2195
+ }
2196
+ if (runResultCheckpoint.delegatedBy) {
2197
+ storeJob(job);
2198
+ const parentCheckpoint = await retrieveCheckpoint(
2199
+ setting.jobId,
2200
+ runResultCheckpoint.delegatedBy.checkpointId
2201
+ );
2202
+ const result = buildDelegationReturnState(setting, runResultCheckpoint, parentCheckpoint);
2203
+ setting = result.setting;
2204
+ checkpoint = result.checkpoint;
2205
+ break;
2206
+ }
2207
+ storeJob({ ...job, status: "completed", finishedAt: Date.now() });
2208
+ return runResultCheckpoint;
2209
+ }
2210
+ case "stoppedByInteractiveTool": {
2211
+ storeJob({ ...job, status: "stoppedByInteractiveTool" });
2212
+ return runResultCheckpoint;
2213
+ }
2214
+ case "stoppedByDelegate": {
2215
+ storeJob(job);
2216
+ const { delegateTo } = runResultCheckpoint;
2217
+ if (!delegateTo || delegateTo.length === 0) {
2218
+ throw new Error("No delegations found in checkpoint");
2219
+ }
2220
+ if (delegateTo.length === 1) {
2221
+ const result = buildDelegateToState(setting, runResultCheckpoint, expertToRun);
2222
+ setting = result.setting;
2223
+ checkpoint = result.checkpoint;
2224
+ break;
2225
+ }
2226
+ const firstDelegation = delegateTo[0];
2227
+ const remainingDelegations = delegateTo.slice(1);
2228
+ const [firstResult, ...restResults] = await Promise.all(
2229
+ delegateTo.map(
2230
+ (delegation) => runDelegate(delegation, setting, runResultCheckpoint, expertToRun, options)
2231
+ )
2232
+ );
2233
+ const allResults = [firstResult, ...restResults];
2234
+ const aggregatedUsage = allResults.reduce(
2235
+ (acc, result) => sumUsage(acc, result.deltaUsage),
2236
+ runResultCheckpoint.usage
2237
+ );
2238
+ const maxStepNumber = Math.max(...allResults.map((r) => r.stepNumber));
2239
+ const restToolResults = restResults.map((result) => ({
2240
+ id: result.toolCallId,
2241
+ skillName: `delegate/${result.expertKey}`,
2242
+ toolName: result.toolName,
2243
+ result: [{ type: "textPart", id: createId(), text: result.text }]
2244
+ }));
2245
+ const processedToolCallIds = new Set(remainingDelegations.map((d) => d.toolCallId));
2246
+ const remainingToolCalls = runResultCheckpoint.pendingToolCalls?.filter(
2247
+ (tc) => !processedToolCallIds.has(tc.id) && tc.id !== firstDelegation.toolCallId
2248
+ );
2249
+ setting = {
2250
+ ...setting,
2251
+ expertKey: expertToRun.key,
2252
+ input: {
2253
+ interactiveToolCallResult: {
2254
+ toolCallId: firstResult.toolCallId,
2255
+ toolName: firstResult.toolName,
2256
+ skillName: `delegate/${firstResult.expertKey}`,
2257
+ text: firstResult.text
2258
+ }
2259
+ }
2260
+ };
2261
+ checkpoint = {
2262
+ ...runResultCheckpoint,
2263
+ status: "stoppedByDelegate",
2264
+ delegateTo: void 0,
2265
+ stepNumber: maxStepNumber,
2266
+ usage: aggregatedUsage,
2267
+ pendingToolCalls: remainingToolCalls?.length ? remainingToolCalls : void 0,
2268
+ partialToolResults: [
2269
+ ...runResultCheckpoint.partialToolResults ?? [],
2270
+ ...restToolResults
2271
+ ]
2272
+ };
2273
+ break;
2274
+ }
2275
+ case "stoppedByExceededMaxSteps": {
2276
+ storeJob({ ...job, status: "stoppedByMaxSteps", finishedAt: Date.now() });
2277
+ return runResultCheckpoint;
2278
+ }
2279
+ case "stoppedByError": {
2280
+ storeJob({ ...job, status: "stoppedByError", finishedAt: Date.now() });
2281
+ return runResultCheckpoint;
2282
+ }
2283
+ default:
2284
+ throw new Error("Run stopped by unknown reason");
2285
+ }
2286
+ }
2287
+ }
2288
+ function createEventListener(userListener, storeEvent) {
2289
+ const listener = userListener ?? ((e) => console.log(JSON.stringify(e)));
2290
+ return async (event) => {
2291
+ if ("stepNumber" in event && storeEvent) {
2292
+ await storeEvent(event);
2293
+ }
2294
+ listener(event);
2295
+ };
2296
+ }
2297
+ async function runDelegate(delegation, parentSetting, parentCheckpoint, parentExpert, options) {
2298
+ const { expert, toolCallId, toolName, query } = delegation;
2299
+ const delegateRunId = createId();
2300
+ const delegateSetting = {
2301
+ ...parentSetting,
2302
+ runId: delegateRunId,
2303
+ expertKey: expert.key,
2304
+ input: { text: query }
2305
+ };
2306
+ const delegateCheckpoint = {
2307
+ id: createId(),
2308
+ jobId: parentSetting.jobId,
2309
+ runId: delegateRunId,
2310
+ status: "init",
2311
+ stepNumber: parentCheckpoint.stepNumber,
2312
+ messages: [],
2313
+ expert: {
2314
+ key: expert.key,
2315
+ name: expert.name,
2316
+ version: expert.version
2317
+ },
2318
+ delegatedBy: {
2319
+ expert: {
2320
+ key: parentExpert.key,
2321
+ name: parentExpert.name,
2322
+ version: parentExpert.version
2323
+ },
2324
+ toolCallId,
2325
+ toolName,
2326
+ checkpointId: parentCheckpoint.id
2327
+ },
2328
+ usage: createEmptyUsage(),
2329
+ contextWindow: parentCheckpoint.contextWindow
2330
+ };
2331
+ const resultCheckpoint = await run(
2332
+ { setting: delegateSetting, checkpoint: delegateCheckpoint },
2333
+ { ...options, returnOnDelegationComplete: true }
2334
+ );
2335
+ const lastMessage = resultCheckpoint.messages[resultCheckpoint.messages.length - 1];
2336
+ if (!lastMessage || lastMessage.type !== "expertMessage") {
2337
+ throw new Error("Delegation error: delegation result message is incorrect");
2338
+ }
2339
+ const textPart = lastMessage.contents.find((c) => c.type === "textPart");
2340
+ if (!textPart || textPart.type !== "textPart") {
2341
+ throw new Error("Delegation error: delegation result message does not contain text");
2342
+ }
2343
+ return {
2344
+ toolCallId,
2345
+ toolName,
2346
+ expertKey: expert.key,
2347
+ text: textPart.text,
2348
+ stepNumber: resultCheckpoint.stepNumber,
2349
+ deltaUsage: resultCheckpoint.usage
2350
+ };
2351
+ }
2352
+
2353
+ export { getModel, package_default, run, runtimeStateMachine };
2354
+ //# sourceMappingURL=chunk-C7AFEVYF.js.map
2355
+ //# sourceMappingURL=chunk-C7AFEVYF.js.map