@perstack/runtime 0.0.66 → 0.0.68
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/README.md +15 -14
- package/dist/bin/cli.js +41 -3
- package/dist/bin/cli.js.map +1 -1
- package/dist/{chunk-LMD6DWPY.js → chunk-7QLRRSNX.js} +544 -361
- package/dist/chunk-7QLRRSNX.js.map +1 -0
- package/dist/src/index.d.ts +25 -2431
- package/dist/src/index.js +6 -6
- package/dist/src/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-LMD6DWPY.js.map +0 -1
|
@@ -5,7 +5,7 @@ import { createDeepSeek } from '@ai-sdk/deepseek';
|
|
|
5
5
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
6
6
|
import { createVertex } from '@ai-sdk/google-vertex';
|
|
7
7
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
8
|
-
import { runParamsSchema, createRuntimeEvent, knownModels, stopRunByExceededMaxSteps, continueToNextStep, stopRunByDelegate, stopRunByInteractiveTool, retry, completeRun, finishToolCall, resolveToolResults, attemptCompletion, callDelegate, callInteractiveTool, callTools, resumeToolCalls, finishAllToolCalls, startGeneration, startRun } from '@perstack/core';
|
|
8
|
+
import { runParamsSchema, createRuntimeEvent, knownModels, getFilteredEnv, stopRunByExceededMaxSteps, continueToNextStep, stopRunByDelegate, stopRunByInteractiveTool, retry, completeRun, finishToolCall, resolveToolResults, attemptCompletion, callDelegate, callInteractiveTool, callTools, resumeToolCalls, finishAllToolCalls, startGeneration, startRun } from '@perstack/core';
|
|
9
9
|
import { createOllama } from 'ollama-ai-provider-v2';
|
|
10
10
|
import { ProxyAgent, fetch } from 'undici';
|
|
11
11
|
import { setup, assign, createActor } from 'xstate';
|
|
@@ -22,7 +22,7 @@ import { ApiV1Client } from '@perstack/api-client/v1';
|
|
|
22
22
|
// package.json
|
|
23
23
|
var package_default = {
|
|
24
24
|
name: "@perstack/runtime",
|
|
25
|
-
version: "0.0.
|
|
25
|
+
version: "0.0.68",
|
|
26
26
|
description: "Perstack Runtime",
|
|
27
27
|
author: "Wintermute Technologies, Inc.",
|
|
28
28
|
license: "Apache-2.0",
|
|
@@ -64,7 +64,7 @@ var package_default = {
|
|
|
64
64
|
"@ai-sdk/google": "^2.0.44",
|
|
65
65
|
"@ai-sdk/google-vertex": "^3.0.24",
|
|
66
66
|
"@ai-sdk/openai": "^2.0.75",
|
|
67
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
68
68
|
"@paralleldrive/cuid2": "^3.0.4",
|
|
69
69
|
"@perstack/api-client": "workspace:*",
|
|
70
70
|
"@perstack/core": "workspace:*",
|
|
@@ -353,12 +353,15 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
353
353
|
name: `${this.skill.name}-mcp-client`,
|
|
354
354
|
version: "1.0.0"
|
|
355
355
|
});
|
|
356
|
+
let timingInfo;
|
|
356
357
|
if (this.skill.type === "mcpStdioSkill") {
|
|
357
|
-
await this._initStdio(this.skill);
|
|
358
|
+
timingInfo = await this._initStdio(this.skill);
|
|
358
359
|
} else {
|
|
359
360
|
await this._initSse(this.skill);
|
|
360
361
|
}
|
|
362
|
+
const toolDiscoveryStartTime = Date.now();
|
|
361
363
|
const { tools } = await this._mcpClient.listTools();
|
|
364
|
+
const toolDiscoveryDurationMs = Date.now() - toolDiscoveryStartTime;
|
|
362
365
|
this._toolDefinitions = tools.map((tool2) => ({
|
|
363
366
|
skillName: this.skill.name,
|
|
364
367
|
name: tool2.name,
|
|
@@ -366,18 +369,32 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
366
369
|
inputSchema: tool2.inputSchema,
|
|
367
370
|
interactive: false
|
|
368
371
|
}));
|
|
372
|
+
if (this._eventListener && timingInfo) {
|
|
373
|
+
const totalDurationMs = Date.now() - timingInfo.startTime;
|
|
374
|
+
const event = createRuntimeEvent("skillConnected", this._jobId, this._runId, {
|
|
375
|
+
skillName: this.skill.name,
|
|
376
|
+
serverInfo: timingInfo.serverInfo,
|
|
377
|
+
spawnDurationMs: timingInfo.spawnDurationMs,
|
|
378
|
+
handshakeDurationMs: timingInfo.handshakeDurationMs,
|
|
379
|
+
toolDiscoveryDurationMs,
|
|
380
|
+
connectDurationMs: timingInfo.spawnDurationMs + timingInfo.handshakeDurationMs,
|
|
381
|
+
totalDurationMs
|
|
382
|
+
});
|
|
383
|
+
this._eventListener(event);
|
|
384
|
+
}
|
|
369
385
|
}
|
|
370
386
|
async _initStdio(skill) {
|
|
371
387
|
if (!skill.command) {
|
|
372
388
|
throw new Error(`Skill ${skill.name} has no command`);
|
|
373
389
|
}
|
|
374
|
-
const
|
|
390
|
+
const requiredEnv = {};
|
|
375
391
|
for (const envName of skill.requiredEnv) {
|
|
376
392
|
if (!this._env[envName]) {
|
|
377
393
|
throw new Error(`Skill ${skill.name} requires environment variable ${envName}`);
|
|
378
394
|
}
|
|
379
|
-
|
|
395
|
+
requiredEnv[envName] = this._env[envName];
|
|
380
396
|
}
|
|
397
|
+
const env = getFilteredEnv(requiredEnv);
|
|
381
398
|
const startTime = Date.now();
|
|
382
399
|
const { command, args } = this._getCommandArgs(skill);
|
|
383
400
|
if (this._eventListener) {
|
|
@@ -389,6 +406,7 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
389
406
|
this._eventListener(event);
|
|
390
407
|
}
|
|
391
408
|
const transport = new StdioClientTransport({ command, args, env, stderr: "pipe" });
|
|
409
|
+
const spawnDurationMs = Date.now() - startTime;
|
|
392
410
|
if (transport.stderr) {
|
|
393
411
|
transport.stderr.on("data", (chunk) => {
|
|
394
412
|
if (this._eventListener) {
|
|
@@ -402,25 +420,57 @@ var McpSkillManager = class extends BaseSkillManager {
|
|
|
402
420
|
}
|
|
403
421
|
const connectStartTime = Date.now();
|
|
404
422
|
await this._mcpClient.connect(transport);
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
});
|
|
414
|
-
this._eventListener(event);
|
|
415
|
-
}
|
|
423
|
+
const handshakeDurationMs = Date.now() - connectStartTime;
|
|
424
|
+
const serverVersion = this._mcpClient.getServerVersion();
|
|
425
|
+
return {
|
|
426
|
+
startTime,
|
|
427
|
+
spawnDurationMs,
|
|
428
|
+
handshakeDurationMs,
|
|
429
|
+
serverInfo: serverVersion ? { name: serverVersion.name, version: serverVersion.version } : void 0
|
|
430
|
+
};
|
|
416
431
|
}
|
|
417
432
|
async _initSse(skill) {
|
|
418
433
|
if (!skill.endpoint) {
|
|
419
434
|
throw new Error(`Skill ${skill.name} has no endpoint`);
|
|
420
435
|
}
|
|
421
|
-
const
|
|
436
|
+
const url = new URL(skill.endpoint);
|
|
437
|
+
if (url.protocol !== "https:") {
|
|
438
|
+
throw new Error(`Skill ${skill.name} SSE endpoint must use HTTPS: ${skill.endpoint}`);
|
|
439
|
+
}
|
|
440
|
+
if (this._isPrivateOrLocalIP(url.hostname)) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
`Skill ${skill.name} SSE endpoint cannot use private/local IP: ${skill.endpoint}`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
const transport = new SSEClientTransport(url);
|
|
422
446
|
await this._mcpClient.connect(transport);
|
|
423
447
|
}
|
|
448
|
+
_isPrivateOrLocalIP(hostname) {
|
|
449
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "0.0.0.0") {
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
const ipv4Match = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
|
|
453
|
+
if (ipv4Match) {
|
|
454
|
+
const [, a, b] = ipv4Match.map(Number);
|
|
455
|
+
if (a === 10) return true;
|
|
456
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
457
|
+
if (a === 192 && b === 168) return true;
|
|
458
|
+
if (a === 169 && b === 254) return true;
|
|
459
|
+
if (a === 127) return true;
|
|
460
|
+
}
|
|
461
|
+
if (hostname.includes(":")) {
|
|
462
|
+
if (hostname.startsWith("fe80:") || hostname.startsWith("fc") || hostname.startsWith("fd")) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (hostname.startsWith("::ffff:")) {
|
|
467
|
+
const ipv4Part = hostname.slice(7);
|
|
468
|
+
if (this._isPrivateOrLocalIP(ipv4Part)) {
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
424
474
|
_getCommandArgs(skill) {
|
|
425
475
|
const { name, command, packageName, args } = skill;
|
|
426
476
|
if (!packageName && (!args || args.length === 0)) {
|
|
@@ -635,12 +685,140 @@ async function getToolSet(skillManagers) {
|
|
|
635
685
|
}
|
|
636
686
|
return tools;
|
|
637
687
|
}
|
|
688
|
+
function isFileInfo(value) {
|
|
689
|
+
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";
|
|
690
|
+
}
|
|
691
|
+
async function processFileToolResult(toolResult, toolName) {
|
|
692
|
+
const processedContents = [];
|
|
693
|
+
for (const part of toolResult.result) {
|
|
694
|
+
if (part.type !== "textPart") {
|
|
695
|
+
processedContents.push(part);
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
let fileInfo;
|
|
699
|
+
try {
|
|
700
|
+
const parsed = JSON.parse(part.text);
|
|
701
|
+
if (isFileInfo(parsed)) {
|
|
702
|
+
fileInfo = parsed;
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
processedContents.push(part);
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (!fileInfo) {
|
|
709
|
+
processedContents.push(part);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const { path, mimeType } = fileInfo;
|
|
713
|
+
try {
|
|
714
|
+
const buffer = await readFile(path);
|
|
715
|
+
if (toolName === "readImageFile") {
|
|
716
|
+
processedContents.push({
|
|
717
|
+
type: "imageInlinePart",
|
|
718
|
+
id: part.id,
|
|
719
|
+
encodedData: buffer.toString("base64"),
|
|
720
|
+
mimeType
|
|
721
|
+
});
|
|
722
|
+
} else {
|
|
723
|
+
processedContents.push({
|
|
724
|
+
type: "fileInlinePart",
|
|
725
|
+
id: part.id,
|
|
726
|
+
encodedData: buffer.toString("base64"),
|
|
727
|
+
mimeType
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
} catch (error) {
|
|
731
|
+
processedContents.push({
|
|
732
|
+
type: "textPart",
|
|
733
|
+
id: part.id,
|
|
734
|
+
text: `Failed to read file "${path}": ${error instanceof Error ? error.message : String(error)}`
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return { ...toolResult, result: processedContents };
|
|
739
|
+
}
|
|
740
|
+
var McpToolExecutor = class {
|
|
741
|
+
type = "mcp";
|
|
742
|
+
async execute(toolCall, skillManagers) {
|
|
743
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
744
|
+
if (skillManager.type !== "mcp") {
|
|
745
|
+
throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
|
|
746
|
+
}
|
|
747
|
+
const result = await skillManager.callTool(
|
|
748
|
+
toolCall.toolName,
|
|
749
|
+
toolCall.args
|
|
750
|
+
);
|
|
751
|
+
const toolResult = {
|
|
752
|
+
id: toolCall.id,
|
|
753
|
+
skillName: toolCall.skillName,
|
|
754
|
+
toolName: toolCall.toolName,
|
|
755
|
+
result
|
|
756
|
+
};
|
|
757
|
+
if (toolCall.toolName === "readPdfFile" || toolCall.toolName === "readImageFile") {
|
|
758
|
+
return processFileToolResult(toolResult, toolCall.toolName);
|
|
759
|
+
}
|
|
760
|
+
return toolResult;
|
|
761
|
+
}
|
|
762
|
+
};
|
|
638
763
|
|
|
639
|
-
// src/
|
|
640
|
-
|
|
764
|
+
// src/tool-execution/executor-factory.ts
|
|
765
|
+
var ToolExecutorFactory = class {
|
|
766
|
+
executors;
|
|
767
|
+
constructor() {
|
|
768
|
+
this.executors = /* @__PURE__ */ new Map([
|
|
769
|
+
["mcp", new McpToolExecutor()]
|
|
770
|
+
// delegate and interactive are handled specially (not executed here)
|
|
771
|
+
]);
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Get the executor for a given skill type
|
|
775
|
+
*/
|
|
776
|
+
getExecutor(type) {
|
|
777
|
+
return this.executors.get(type);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Execute a tool call using the appropriate executor
|
|
781
|
+
*/
|
|
782
|
+
async execute(toolCall, type, skillManagers) {
|
|
783
|
+
const executor = this.executors.get(type);
|
|
784
|
+
if (!executor) {
|
|
785
|
+
throw new Error(`No executor registered for skill type: ${type}`);
|
|
786
|
+
}
|
|
787
|
+
return executor.execute(toolCall, skillManagers);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Check if a skill type can be executed locally (vs requiring delegation)
|
|
791
|
+
*/
|
|
792
|
+
canExecuteLocally(type) {
|
|
793
|
+
return type === "mcp";
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
var toolExecutorFactory = new ToolExecutorFactory();
|
|
797
|
+
|
|
798
|
+
// src/tool-execution/tool-classifier.ts
|
|
799
|
+
async function getToolTypeByName(toolName, skillManagers) {
|
|
641
800
|
const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
|
|
642
801
|
return skillManager.type;
|
|
643
802
|
}
|
|
803
|
+
async function classifyToolCalls(toolCalls, skillManagers) {
|
|
804
|
+
const classified = {
|
|
805
|
+
mcp: [],
|
|
806
|
+
delegate: [],
|
|
807
|
+
interactive: []
|
|
808
|
+
};
|
|
809
|
+
const results = await Promise.all(
|
|
810
|
+
toolCalls.map(async (toolCall) => {
|
|
811
|
+
const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
|
|
812
|
+
return { toolCall, type: skillManager.type, skillManager };
|
|
813
|
+
})
|
|
814
|
+
);
|
|
815
|
+
for (const result of results) {
|
|
816
|
+
classified[result.type].push(result);
|
|
817
|
+
}
|
|
818
|
+
return classified;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// src/state-machine/states/calling-delegate.ts
|
|
644
822
|
async function callingDelegateLogic({
|
|
645
823
|
setting,
|
|
646
824
|
checkpoint,
|
|
@@ -653,7 +831,7 @@ async function callingDelegateLogic({
|
|
|
653
831
|
const toolCallTypes = await Promise.all(
|
|
654
832
|
step.pendingToolCalls.map(async (tc) => ({
|
|
655
833
|
toolCall: tc,
|
|
656
|
-
type: await
|
|
834
|
+
type: await getToolTypeByName(tc.toolName, skillManagers)
|
|
657
835
|
}))
|
|
658
836
|
);
|
|
659
837
|
const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
|
|
@@ -731,79 +909,6 @@ function hasRemainingTodos(toolResult) {
|
|
|
731
909
|
return false;
|
|
732
910
|
}
|
|
733
911
|
}
|
|
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
912
|
async function callingToolLogic({
|
|
808
913
|
setting,
|
|
809
914
|
checkpoint,
|
|
@@ -819,28 +924,26 @@ async function callingToolLogic({
|
|
|
819
924
|
(tc) => tc.skillName === "@perstack/base" && tc.toolName === "attemptCompletion"
|
|
820
925
|
);
|
|
821
926
|
if (attemptCompletionTool) {
|
|
822
|
-
const toolResult = await
|
|
927
|
+
const toolResult = await toolExecutorFactory.execute(
|
|
928
|
+
attemptCompletionTool,
|
|
929
|
+
"mcp",
|
|
930
|
+
skillManagers
|
|
931
|
+
);
|
|
823
932
|
if (hasRemainingTodos(toolResult)) {
|
|
824
933
|
return resolveToolResults(setting, checkpoint, { toolResults: [toolResult] });
|
|
825
934
|
}
|
|
826
935
|
return attemptCompletion(setting, checkpoint, { toolResult });
|
|
827
936
|
}
|
|
828
|
-
const
|
|
829
|
-
|
|
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) {
|
|
937
|
+
const classified = await classifyToolCalls(pendingToolCalls, skillManagers);
|
|
938
|
+
if (classified.mcp.length > 0) {
|
|
838
939
|
const mcpResults = await Promise.all(
|
|
839
|
-
|
|
940
|
+
classified.mcp.map((c) => toolExecutorFactory.execute(c.toolCall, "mcp", skillManagers))
|
|
840
941
|
);
|
|
841
942
|
toolResults.push(...mcpResults);
|
|
842
943
|
}
|
|
843
|
-
if (
|
|
944
|
+
if (classified.delegate.length > 0) {
|
|
945
|
+
const delegateToolCalls = classified.delegate.map((c) => c.toolCall);
|
|
946
|
+
const interactiveToolCalls = classified.interactive.map((c) => c.toolCall);
|
|
844
947
|
step.partialToolResults = toolResults;
|
|
845
948
|
step.pendingToolCalls = [...delegateToolCalls, ...interactiveToolCalls];
|
|
846
949
|
return callDelegate(setting, checkpoint, {
|
|
@@ -849,11 +952,12 @@ async function callingToolLogic({
|
|
|
849
952
|
usage: step.usage
|
|
850
953
|
});
|
|
851
954
|
}
|
|
852
|
-
if (
|
|
853
|
-
const interactiveToolCall =
|
|
955
|
+
if (classified.interactive.length > 0) {
|
|
956
|
+
const interactiveToolCall = classified.interactive[0]?.toolCall;
|
|
854
957
|
if (!interactiveToolCall) {
|
|
855
958
|
throw new Error("No interactive tool call found");
|
|
856
959
|
}
|
|
960
|
+
const interactiveToolCalls = classified.interactive.map((c) => c.toolCall);
|
|
857
961
|
step.partialToolResults = toolResults;
|
|
858
962
|
step.pendingToolCalls = interactiveToolCalls;
|
|
859
963
|
return callInteractiveTool(setting, checkpoint, {
|
|
@@ -1162,7 +1266,7 @@ async function generatingRunResultLogic({
|
|
|
1162
1266
|
usage
|
|
1163
1267
|
});
|
|
1164
1268
|
}
|
|
1165
|
-
async function
|
|
1269
|
+
async function classifyToolCalls2(toolCalls, skillManagers) {
|
|
1166
1270
|
return Promise.all(
|
|
1167
1271
|
toolCalls.map(async (tc) => {
|
|
1168
1272
|
const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
|
|
@@ -1239,7 +1343,7 @@ async function generatingToolCallLogic({
|
|
|
1239
1343
|
usage
|
|
1240
1344
|
});
|
|
1241
1345
|
}
|
|
1242
|
-
const classified = await
|
|
1346
|
+
const classified = await classifyToolCalls2(toolCalls, skillManagers);
|
|
1243
1347
|
const sorted = sortToolCallsByPriority(classified);
|
|
1244
1348
|
if (finishReason === "tool-calls" || finishReason === "stop") {
|
|
1245
1349
|
const toolCallParts = buildToolCallParts(sorted);
|
|
@@ -1927,29 +2031,6 @@ async function executeStateMachine(params) {
|
|
|
1927
2031
|
runActor.start();
|
|
1928
2032
|
});
|
|
1929
2033
|
}
|
|
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
2034
|
|
|
1954
2035
|
// src/helpers/checkpoint.ts
|
|
1955
2036
|
function createInitialCheckpoint(checkpointId, params) {
|
|
@@ -2013,46 +2094,6 @@ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentChec
|
|
|
2013
2094
|
}
|
|
2014
2095
|
};
|
|
2015
2096
|
}
|
|
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
2097
|
async function resolveExpertToRun(expertKey, experts, clientOptions) {
|
|
2057
2098
|
if (experts[expertKey]) {
|
|
2058
2099
|
return experts[expertKey];
|
|
@@ -2102,68 +2143,245 @@ async function setupExperts(setting, resolveExpertToRun2 = resolveExpertToRun) {
|
|
|
2102
2143
|
}
|
|
2103
2144
|
return { expertToRun, experts };
|
|
2104
2145
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2146
|
+
var SingleDelegationStrategy = class {
|
|
2147
|
+
async execute(delegations, setting, context, parentExpert, _runFn, _parentOptions) {
|
|
2148
|
+
if (delegations.length !== 1) {
|
|
2149
|
+
throw new Error("SingleDelegationStrategy requires exactly one delegation");
|
|
2150
|
+
}
|
|
2151
|
+
const delegation = delegations[0];
|
|
2152
|
+
const { expert, toolCallId, toolName, query } = delegation;
|
|
2153
|
+
const nextSetting = {
|
|
2154
|
+
...setting,
|
|
2155
|
+
expertKey: expert.key,
|
|
2156
|
+
input: { text: query }
|
|
2157
|
+
};
|
|
2158
|
+
const nextCheckpoint = {
|
|
2159
|
+
id: context.id,
|
|
2160
|
+
jobId: setting.jobId,
|
|
2161
|
+
runId: setting.runId,
|
|
2162
|
+
status: "init",
|
|
2163
|
+
stepNumber: context.stepNumber,
|
|
2164
|
+
messages: [],
|
|
2165
|
+
// Child starts fresh
|
|
2166
|
+
expert: {
|
|
2167
|
+
key: expert.key,
|
|
2168
|
+
name: expert.name,
|
|
2169
|
+
version: expert.version
|
|
2170
|
+
},
|
|
2171
|
+
delegatedBy: {
|
|
2172
|
+
expert: {
|
|
2173
|
+
key: parentExpert.key,
|
|
2174
|
+
name: parentExpert.name,
|
|
2175
|
+
version: parentExpert.version
|
|
2176
|
+
},
|
|
2177
|
+
toolCallId,
|
|
2178
|
+
toolName,
|
|
2179
|
+
checkpointId: context.id
|
|
2180
|
+
},
|
|
2181
|
+
usage: context.usage,
|
|
2182
|
+
contextWindow: context.contextWindow,
|
|
2183
|
+
pendingToolCalls: void 0,
|
|
2184
|
+
partialToolResults: void 0
|
|
2185
|
+
};
|
|
2186
|
+
return { nextSetting, nextCheckpoint };
|
|
2187
|
+
}
|
|
2112
2188
|
};
|
|
2113
|
-
var
|
|
2114
|
-
|
|
2115
|
-
|
|
2189
|
+
var ParallelDelegationStrategy = class {
|
|
2190
|
+
async execute(delegations, setting, context, parentExpert, runFn, parentOptions) {
|
|
2191
|
+
if (delegations.length < 2) {
|
|
2192
|
+
throw new Error("ParallelDelegationStrategy requires at least two delegations");
|
|
2193
|
+
}
|
|
2194
|
+
const [firstDelegation, ...remainingDelegations] = delegations;
|
|
2195
|
+
if (!firstDelegation) {
|
|
2196
|
+
throw new Error("No delegations found");
|
|
2197
|
+
}
|
|
2198
|
+
const allResults = await Promise.all(
|
|
2199
|
+
delegations.map(
|
|
2200
|
+
(delegation) => this.executeSingleDelegation(
|
|
2201
|
+
delegation,
|
|
2202
|
+
setting,
|
|
2203
|
+
context,
|
|
2204
|
+
parentExpert,
|
|
2205
|
+
runFn,
|
|
2206
|
+
parentOptions
|
|
2207
|
+
)
|
|
2208
|
+
)
|
|
2209
|
+
);
|
|
2210
|
+
const [firstResult, ...restResults] = allResults;
|
|
2211
|
+
if (!firstResult) {
|
|
2212
|
+
throw new Error("No delegation results");
|
|
2213
|
+
}
|
|
2214
|
+
const aggregatedUsage = allResults.reduce(
|
|
2215
|
+
(acc, result) => sumUsage(acc, result.deltaUsage),
|
|
2216
|
+
context.usage
|
|
2217
|
+
);
|
|
2218
|
+
const maxStepNumber = Math.max(...allResults.map((r) => r.stepNumber));
|
|
2219
|
+
const restToolResults = restResults.map((result) => ({
|
|
2220
|
+
id: result.toolCallId,
|
|
2221
|
+
skillName: `delegate/${result.expertKey}`,
|
|
2222
|
+
toolName: result.toolName,
|
|
2223
|
+
result: [{ type: "textPart", id: createId(), text: result.text }]
|
|
2224
|
+
}));
|
|
2225
|
+
const processedToolCallIds = new Set(remainingDelegations.map((d) => d.toolCallId));
|
|
2226
|
+
const remainingPendingToolCalls = context.pendingToolCalls?.filter(
|
|
2227
|
+
(tc) => !processedToolCallIds.has(tc.id) && tc.id !== firstDelegation.toolCallId
|
|
2228
|
+
);
|
|
2229
|
+
const nextSetting = {
|
|
2230
|
+
...setting,
|
|
2231
|
+
expertKey: parentExpert.key,
|
|
2232
|
+
input: {
|
|
2233
|
+
interactiveToolCallResult: {
|
|
2234
|
+
toolCallId: firstResult.toolCallId,
|
|
2235
|
+
toolName: firstResult.toolName,
|
|
2236
|
+
skillName: `delegate/${firstResult.expertKey}`,
|
|
2237
|
+
text: firstResult.text
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
const nextCheckpoint = {
|
|
2242
|
+
id: context.id,
|
|
2243
|
+
jobId: setting.jobId,
|
|
2244
|
+
runId: setting.runId,
|
|
2245
|
+
status: "stoppedByDelegate",
|
|
2246
|
+
stepNumber: maxStepNumber,
|
|
2247
|
+
messages: context.messages,
|
|
2248
|
+
// Restore parent's conversation history
|
|
2249
|
+
expert: {
|
|
2250
|
+
key: parentExpert.key,
|
|
2251
|
+
name: parentExpert.name,
|
|
2252
|
+
version: parentExpert.version
|
|
2253
|
+
},
|
|
2254
|
+
usage: aggregatedUsage,
|
|
2255
|
+
contextWindow: context.contextWindow,
|
|
2256
|
+
delegatedBy: context.delegatedBy,
|
|
2257
|
+
// Preserve parent reference for nested delegations
|
|
2258
|
+
delegateTo: void 0,
|
|
2259
|
+
pendingToolCalls: remainingPendingToolCalls?.length ? remainingPendingToolCalls : void 0,
|
|
2260
|
+
partialToolResults: [...context.partialToolResults ?? [], ...restToolResults]
|
|
2261
|
+
};
|
|
2262
|
+
return { nextSetting, nextCheckpoint };
|
|
2263
|
+
}
|
|
2264
|
+
async executeSingleDelegation(delegation, parentSetting, parentContext, parentExpert, runFn, parentOptions) {
|
|
2265
|
+
const { expert, toolCallId, toolName, query } = delegation;
|
|
2266
|
+
const delegateRunId = createId();
|
|
2267
|
+
const delegateSetting = {
|
|
2268
|
+
...parentSetting,
|
|
2269
|
+
runId: delegateRunId,
|
|
2270
|
+
expertKey: expert.key,
|
|
2271
|
+
input: { text: query }
|
|
2272
|
+
};
|
|
2273
|
+
const delegateCheckpoint = {
|
|
2274
|
+
id: createId(),
|
|
2275
|
+
jobId: parentSetting.jobId,
|
|
2276
|
+
runId: delegateRunId,
|
|
2277
|
+
status: "init",
|
|
2278
|
+
stepNumber: parentContext.stepNumber,
|
|
2279
|
+
messages: [],
|
|
2280
|
+
// Child starts fresh - no parent context inheritance
|
|
2281
|
+
expert: {
|
|
2282
|
+
key: expert.key,
|
|
2283
|
+
name: expert.name,
|
|
2284
|
+
version: expert.version
|
|
2285
|
+
},
|
|
2286
|
+
delegatedBy: {
|
|
2287
|
+
expert: {
|
|
2288
|
+
key: parentExpert.key,
|
|
2289
|
+
name: parentExpert.name,
|
|
2290
|
+
version: parentExpert.version
|
|
2291
|
+
},
|
|
2292
|
+
toolCallId,
|
|
2293
|
+
toolName,
|
|
2294
|
+
checkpointId: parentContext.id
|
|
2295
|
+
},
|
|
2296
|
+
usage: createEmptyUsage(),
|
|
2297
|
+
contextWindow: parentContext.contextWindow
|
|
2298
|
+
};
|
|
2299
|
+
const resultCheckpoint = await runFn(
|
|
2300
|
+
{ setting: delegateSetting, checkpoint: delegateCheckpoint },
|
|
2301
|
+
{ ...parentOptions, returnOnDelegationComplete: true }
|
|
2302
|
+
);
|
|
2303
|
+
return this.extractDelegationResult(resultCheckpoint, toolCallId, toolName, expert.key);
|
|
2304
|
+
}
|
|
2305
|
+
extractDelegationResult(checkpoint, toolCallId, toolName, expertKey) {
|
|
2306
|
+
const lastMessage = checkpoint.messages[checkpoint.messages.length - 1];
|
|
2307
|
+
if (!lastMessage || lastMessage.type !== "expertMessage") {
|
|
2308
|
+
throw new Error("Delegation error: delegation result message is incorrect");
|
|
2309
|
+
}
|
|
2310
|
+
const textPart = lastMessage.contents.find((c) => c.type === "textPart");
|
|
2311
|
+
if (!textPart || textPart.type !== "textPart") {
|
|
2312
|
+
throw new Error("Delegation error: delegation result message does not contain text");
|
|
2313
|
+
}
|
|
2314
|
+
return {
|
|
2315
|
+
toolCallId,
|
|
2316
|
+
toolName,
|
|
2317
|
+
expertKey,
|
|
2318
|
+
text: textPart.text,
|
|
2319
|
+
stepNumber: checkpoint.stepNumber,
|
|
2320
|
+
deltaUsage: checkpoint.usage
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2116
2323
|
};
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
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 };
|
|
2324
|
+
function selectDelegationStrategy(delegationCount) {
|
|
2325
|
+
if (delegationCount === 1) {
|
|
2326
|
+
return new SingleDelegationStrategy();
|
|
2142
2327
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2328
|
+
return new ParallelDelegationStrategy();
|
|
2329
|
+
}
|
|
2330
|
+
function buildReturnFromDelegation(currentSetting, resultCheckpoint, parentCheckpoint) {
|
|
2331
|
+
return buildDelegationReturnState(currentSetting, resultCheckpoint, parentCheckpoint);
|
|
2332
|
+
}
|
|
2333
|
+
function extractDelegationContext(checkpoint) {
|
|
2334
|
+
return {
|
|
2335
|
+
id: checkpoint.id,
|
|
2336
|
+
stepNumber: checkpoint.stepNumber,
|
|
2337
|
+
contextWindow: checkpoint.contextWindow,
|
|
2338
|
+
usage: checkpoint.usage,
|
|
2339
|
+
pendingToolCalls: checkpoint.pendingToolCalls,
|
|
2340
|
+
partialToolResults: checkpoint.partialToolResults,
|
|
2341
|
+
delegatedBy: checkpoint.delegatedBy,
|
|
2342
|
+
// Preserve for nested delegations
|
|
2343
|
+
messages: checkpoint.messages
|
|
2344
|
+
// Preserve for parent continuation after delegation
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2347
|
+
var RunEventEmitter = class {
|
|
2348
|
+
listeners = [];
|
|
2349
|
+
subscribe(listener) {
|
|
2350
|
+
this.listeners.push(listener);
|
|
2351
|
+
}
|
|
2352
|
+
async emit(event) {
|
|
2353
|
+
const errors = [];
|
|
2354
|
+
for (const listener of this.listeners) {
|
|
2355
|
+
try {
|
|
2356
|
+
await listener({
|
|
2357
|
+
...event,
|
|
2358
|
+
id: createId(),
|
|
2359
|
+
timestamp: Date.now()
|
|
2360
|
+
});
|
|
2361
|
+
} catch (error) {
|
|
2362
|
+
errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
2363
|
+
}
|
|
2161
2364
|
}
|
|
2365
|
+
if (errors.length > 0) {
|
|
2366
|
+
throw new AggregateError(errors, "One or more event listeners failed");
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
// src/orchestration/single-run-executor.ts
|
|
2372
|
+
var SingleRunExecutor = class {
|
|
2373
|
+
constructor(options = {}) {
|
|
2374
|
+
this.options = options;
|
|
2375
|
+
}
|
|
2376
|
+
async execute(setting, checkpoint) {
|
|
2377
|
+
const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
|
|
2378
|
+
const { expertToRun, experts } = await setupExperts(setting, this.options.resolveExpertToRun);
|
|
2379
|
+
this.emitInitEvent(setting, expertToRun, experts);
|
|
2162
2380
|
const skillManagers = await getSkillManagers(
|
|
2163
2381
|
expertToRun,
|
|
2164
2382
|
experts,
|
|
2165
2383
|
setting,
|
|
2166
|
-
options
|
|
2384
|
+
this.options.eventListener,
|
|
2167
2385
|
{ isDelegatedRun: !!checkpoint?.delegatedBy }
|
|
2168
2386
|
);
|
|
2169
2387
|
const initialCheckpoint = checkpoint ? createNextStepCheckpoint(createId(), checkpoint) : createInitialCheckpoint(createId(), {
|
|
@@ -2173,183 +2391,148 @@ async function run(runInput, options) {
|
|
|
2173
2391
|
expert: expertToRun,
|
|
2174
2392
|
contextWindow
|
|
2175
2393
|
});
|
|
2176
|
-
const
|
|
2394
|
+
const eventEmitter = new RunEventEmitter();
|
|
2395
|
+
const eventListener = this.createEventListener();
|
|
2396
|
+
eventEmitter.subscribe(eventListener);
|
|
2397
|
+
const resultCheckpoint = await executeStateMachine({
|
|
2177
2398
|
setting: { ...setting, experts },
|
|
2178
2399
|
initialCheckpoint,
|
|
2179
2400
|
eventListener,
|
|
2180
2401
|
skillManagers,
|
|
2181
2402
|
eventEmitter,
|
|
2182
|
-
storeCheckpoint
|
|
2183
|
-
|
|
2403
|
+
storeCheckpoint: this.options.storeCheckpoint ?? (async () => {
|
|
2404
|
+
}),
|
|
2405
|
+
shouldContinueRun: this.options.shouldContinueRun
|
|
2184
2406
|
});
|
|
2407
|
+
return { checkpoint: resultCheckpoint, expertToRun, experts };
|
|
2408
|
+
}
|
|
2409
|
+
createEventListener() {
|
|
2410
|
+
const userListener = this.options.eventListener;
|
|
2411
|
+
const storeEvent = this.options.storeEvent;
|
|
2412
|
+
return async (event) => {
|
|
2413
|
+
if ("stepNumber" in event && storeEvent) {
|
|
2414
|
+
await storeEvent(event);
|
|
2415
|
+
}
|
|
2416
|
+
userListener?.(event);
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
emitInitEvent(setting, expertToRun, experts) {
|
|
2420
|
+
if (!this.options.eventListener) return;
|
|
2421
|
+
const initEvent = createRuntimeEvent("initializeRuntime", setting.jobId, setting.runId, {
|
|
2422
|
+
runtimeVersion: package_default.version,
|
|
2423
|
+
runtime: "local",
|
|
2424
|
+
expertName: expertToRun.name,
|
|
2425
|
+
experts: Object.keys(experts),
|
|
2426
|
+
model: setting.model,
|
|
2427
|
+
temperature: setting.temperature,
|
|
2428
|
+
maxSteps: setting.maxSteps,
|
|
2429
|
+
maxRetries: setting.maxRetries,
|
|
2430
|
+
timeout: setting.timeout,
|
|
2431
|
+
query: setting.input.text,
|
|
2432
|
+
interactiveToolCall: setting.input.interactiveToolCallResult
|
|
2433
|
+
});
|
|
2434
|
+
this.options.eventListener(initEvent);
|
|
2435
|
+
}
|
|
2436
|
+
};
|
|
2437
|
+
|
|
2438
|
+
// src/run.ts
|
|
2439
|
+
var defaultCreateJob = (jobId, expertKey, maxSteps) => ({
|
|
2440
|
+
id: jobId,
|
|
2441
|
+
coordinatorExpertKey: expertKey,
|
|
2442
|
+
status: "running",
|
|
2443
|
+
totalSteps: 0,
|
|
2444
|
+
startedAt: Date.now(),
|
|
2445
|
+
maxSteps,
|
|
2446
|
+
usage: createEmptyUsage()
|
|
2447
|
+
});
|
|
2448
|
+
async function run(runInput, options) {
|
|
2449
|
+
const runParams = runParamsSchema.parse(runInput);
|
|
2450
|
+
let { setting, checkpoint } = runParams;
|
|
2451
|
+
const storeJob = options?.storeJob ?? (() => {
|
|
2452
|
+
});
|
|
2453
|
+
const retrieveJob = options?.retrieveJob ?? (() => void 0);
|
|
2454
|
+
const retrieveCheckpoint = options?.retrieveCheckpoint ?? (async () => {
|
|
2455
|
+
throw new Error("retrieveCheckpoint not provided");
|
|
2456
|
+
});
|
|
2457
|
+
const createJob = options?.createJob ?? defaultCreateJob;
|
|
2458
|
+
let job = retrieveJob(setting.jobId) ?? createJob(setting.jobId, setting.expertKey, setting.maxSteps);
|
|
2459
|
+
if (job.status !== "running") {
|
|
2460
|
+
job = { ...job, status: "running", finishedAt: void 0 };
|
|
2461
|
+
}
|
|
2462
|
+
storeJob(job);
|
|
2463
|
+
const runExecutor = new SingleRunExecutor({
|
|
2464
|
+
shouldContinueRun: options?.shouldContinueRun,
|
|
2465
|
+
storeCheckpoint: options?.storeCheckpoint,
|
|
2466
|
+
storeEvent: options?.storeEvent,
|
|
2467
|
+
eventListener: options?.eventListener,
|
|
2468
|
+
resolveExpertToRun: options?.resolveExpertToRun
|
|
2469
|
+
});
|
|
2470
|
+
while (true) {
|
|
2471
|
+
const runResult = await runExecutor.execute(setting, checkpoint);
|
|
2472
|
+
const resultCheckpoint = runResult.checkpoint;
|
|
2185
2473
|
job = {
|
|
2186
2474
|
...job,
|
|
2187
|
-
totalSteps:
|
|
2188
|
-
usage:
|
|
2475
|
+
totalSteps: resultCheckpoint.stepNumber,
|
|
2476
|
+
usage: resultCheckpoint.usage
|
|
2189
2477
|
};
|
|
2190
|
-
switch (
|
|
2478
|
+
switch (resultCheckpoint.status) {
|
|
2191
2479
|
case "completed": {
|
|
2192
2480
|
if (options?.returnOnDelegationComplete) {
|
|
2193
2481
|
storeJob(job);
|
|
2194
|
-
return
|
|
2482
|
+
return resultCheckpoint;
|
|
2195
2483
|
}
|
|
2196
|
-
if (
|
|
2484
|
+
if (resultCheckpoint.delegatedBy) {
|
|
2197
2485
|
storeJob(job);
|
|
2198
2486
|
const parentCheckpoint = await retrieveCheckpoint(
|
|
2199
2487
|
setting.jobId,
|
|
2200
|
-
|
|
2488
|
+
resultCheckpoint.delegatedBy.checkpointId
|
|
2201
2489
|
);
|
|
2202
|
-
const result =
|
|
2490
|
+
const result = buildReturnFromDelegation(setting, resultCheckpoint, parentCheckpoint);
|
|
2203
2491
|
setting = result.setting;
|
|
2204
2492
|
checkpoint = result.checkpoint;
|
|
2205
2493
|
break;
|
|
2206
2494
|
}
|
|
2207
2495
|
storeJob({ ...job, status: "completed", finishedAt: Date.now() });
|
|
2208
|
-
return
|
|
2496
|
+
return resultCheckpoint;
|
|
2209
2497
|
}
|
|
2210
2498
|
case "stoppedByInteractiveTool": {
|
|
2211
2499
|
storeJob({ ...job, status: "stoppedByInteractiveTool" });
|
|
2212
|
-
return
|
|
2500
|
+
return resultCheckpoint;
|
|
2213
2501
|
}
|
|
2214
2502
|
case "stoppedByDelegate": {
|
|
2215
2503
|
storeJob(job);
|
|
2216
|
-
const { delegateTo } =
|
|
2504
|
+
const { delegateTo } = resultCheckpoint;
|
|
2217
2505
|
if (!delegateTo || delegateTo.length === 0) {
|
|
2218
2506
|
throw new Error("No delegations found in checkpoint");
|
|
2219
2507
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
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
|
|
2508
|
+
const strategy = selectDelegationStrategy(delegateTo.length);
|
|
2509
|
+
const context = extractDelegationContext(resultCheckpoint);
|
|
2510
|
+
const delegationResult = await strategy.execute(
|
|
2511
|
+
delegateTo,
|
|
2512
|
+
setting,
|
|
2513
|
+
context,
|
|
2514
|
+
runResult.expertToRun,
|
|
2515
|
+
run,
|
|
2516
|
+
options
|
|
2248
2517
|
);
|
|
2249
|
-
setting =
|
|
2250
|
-
|
|
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
|
-
};
|
|
2518
|
+
setting = delegationResult.nextSetting;
|
|
2519
|
+
checkpoint = delegationResult.nextCheckpoint;
|
|
2273
2520
|
break;
|
|
2274
2521
|
}
|
|
2275
2522
|
case "stoppedByExceededMaxSteps": {
|
|
2276
2523
|
storeJob({ ...job, status: "stoppedByMaxSteps", finishedAt: Date.now() });
|
|
2277
|
-
return
|
|
2524
|
+
return resultCheckpoint;
|
|
2278
2525
|
}
|
|
2279
2526
|
case "stoppedByError": {
|
|
2280
2527
|
storeJob({ ...job, status: "stoppedByError", finishedAt: Date.now() });
|
|
2281
|
-
return
|
|
2528
|
+
return resultCheckpoint;
|
|
2282
2529
|
}
|
|
2283
2530
|
default:
|
|
2284
2531
|
throw new Error("Run stopped by unknown reason");
|
|
2285
2532
|
}
|
|
2286
2533
|
}
|
|
2287
2534
|
}
|
|
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
2535
|
|
|
2353
2536
|
export { getModel, package_default, run, runtimeStateMachine };
|
|
2354
|
-
//# sourceMappingURL=chunk-
|
|
2355
|
-
//# sourceMappingURL=chunk-
|
|
2537
|
+
//# sourceMappingURL=chunk-7QLRRSNX.js.map
|
|
2538
|
+
//# sourceMappingURL=chunk-7QLRRSNX.js.map
|