@perstack/runtime 0.0.67 → 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.
@@ -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.67",
25
+ version: "0.0.68",
26
26
  description: "Perstack Runtime",
27
27
  author: "Wintermute Technologies, Inc.",
28
28
  license: "Apache-2.0",
@@ -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,6 +369,19 @@ 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) {
@@ -390,6 +406,7 @@ var McpSkillManager = class extends BaseSkillManager {
390
406
  this._eventListener(event);
391
407
  }
392
408
  const transport = new StdioClientTransport({ command, args, env, stderr: "pipe" });
409
+ const spawnDurationMs = Date.now() - startTime;
393
410
  if (transport.stderr) {
394
411
  transport.stderr.on("data", (chunk) => {
395
412
  if (this._eventListener) {
@@ -403,17 +420,14 @@ var McpSkillManager = class extends BaseSkillManager {
403
420
  }
404
421
  const connectStartTime = Date.now();
405
422
  await this._mcpClient.connect(transport);
406
- const connectTime = Date.now();
407
- if (this._eventListener) {
408
- const serverInfo = this._mcpClient.getServerVersion();
409
- const event = createRuntimeEvent("skillConnected", this._jobId, this._runId, {
410
- skillName: skill.name,
411
- serverInfo: serverInfo ? { name: serverInfo.name, version: serverInfo.version } : void 0,
412
- connectDurationMs: connectTime - connectStartTime,
413
- totalDurationMs: connectTime - startTime
414
- });
415
- this._eventListener(event);
416
- }
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
+ };
417
431
  }
418
432
  async _initSse(skill) {
419
433
  if (!skill.endpoint) {
@@ -671,12 +685,140 @@ async function getToolSet(skillManagers) {
671
685
  }
672
686
  return tools;
673
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
+ };
674
763
 
675
- // src/state-machine/states/calling-delegate.ts
676
- async function getToolType(toolName, skillManagers) {
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) {
677
800
  const skillManager = await getSkillManagerByToolName(skillManagers, toolName);
678
801
  return skillManager.type;
679
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
680
822
  async function callingDelegateLogic({
681
823
  setting,
682
824
  checkpoint,
@@ -689,7 +831,7 @@ async function callingDelegateLogic({
689
831
  const toolCallTypes = await Promise.all(
690
832
  step.pendingToolCalls.map(async (tc) => ({
691
833
  toolCall: tc,
692
- type: await getToolType(tc.toolName, skillManagers)
834
+ type: await getToolTypeByName(tc.toolName, skillManagers)
693
835
  }))
694
836
  );
695
837
  const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
@@ -767,79 +909,6 @@ function hasRemainingTodos(toolResult) {
767
909
  return false;
768
910
  }
769
911
  }
770
- function isFileInfo(value) {
771
- 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";
772
- }
773
- async function processFileToolResult(toolResult, toolName) {
774
- const processedContents = [];
775
- for (const part of toolResult.result) {
776
- if (part.type !== "textPart") {
777
- processedContents.push(part);
778
- continue;
779
- }
780
- let fileInfo;
781
- try {
782
- const parsed = JSON.parse(part.text);
783
- if (isFileInfo(parsed)) {
784
- fileInfo = parsed;
785
- }
786
- } catch {
787
- processedContents.push(part);
788
- continue;
789
- }
790
- if (!fileInfo) {
791
- processedContents.push(part);
792
- continue;
793
- }
794
- const { path, mimeType } = fileInfo;
795
- try {
796
- const buffer = await readFile(path);
797
- if (toolName === "readImageFile") {
798
- processedContents.push({
799
- type: "imageInlinePart",
800
- id: part.id,
801
- encodedData: buffer.toString("base64"),
802
- mimeType
803
- });
804
- } else {
805
- processedContents.push({
806
- type: "fileInlinePart",
807
- id: part.id,
808
- encodedData: buffer.toString("base64"),
809
- mimeType
810
- });
811
- }
812
- } catch (error) {
813
- processedContents.push({
814
- type: "textPart",
815
- id: part.id,
816
- text: `Failed to read file "${path}": ${error instanceof Error ? error.message : String(error)}`
817
- });
818
- }
819
- }
820
- return { ...toolResult, result: processedContents };
821
- }
822
- async function executeMcpToolCall(toolCall, skillManagers) {
823
- const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
824
- if (skillManager.type !== "mcp") {
825
- throw new Error(`Incorrect SkillType, required MCP, got ${skillManager.type}`);
826
- }
827
- const result = await skillManager.callTool(toolCall.toolName, toolCall.args);
828
- const toolResult = {
829
- id: toolCall.id,
830
- skillName: toolCall.skillName,
831
- toolName: toolCall.toolName,
832
- result
833
- };
834
- if (toolCall.toolName === "readPdfFile" || toolCall.toolName === "readImageFile") {
835
- return processFileToolResult(toolResult, toolCall.toolName);
836
- }
837
- return toolResult;
838
- }
839
- async function getToolType2(toolCall, skillManagers) {
840
- const skillManager = await getSkillManagerByToolName(skillManagers, toolCall.toolName);
841
- return skillManager.type;
842
- }
843
912
  async function callingToolLogic({
844
913
  setting,
845
914
  checkpoint,
@@ -855,28 +924,26 @@ async function callingToolLogic({
855
924
  (tc) => tc.skillName === "@perstack/base" && tc.toolName === "attemptCompletion"
856
925
  );
857
926
  if (attemptCompletionTool) {
858
- const toolResult = await executeMcpToolCall(attemptCompletionTool, skillManagers);
927
+ const toolResult = await toolExecutorFactory.execute(
928
+ attemptCompletionTool,
929
+ "mcp",
930
+ skillManagers
931
+ );
859
932
  if (hasRemainingTodos(toolResult)) {
860
933
  return resolveToolResults(setting, checkpoint, { toolResults: [toolResult] });
861
934
  }
862
935
  return attemptCompletion(setting, checkpoint, { toolResult });
863
936
  }
864
- const toolCallTypes = await Promise.all(
865
- pendingToolCalls.map(async (tc) => ({
866
- toolCall: tc,
867
- type: await getToolType2(tc, skillManagers)
868
- }))
869
- );
870
- const mcpToolCalls = toolCallTypes.filter((t) => t.type === "mcp").map((t) => t.toolCall);
871
- const delegateToolCalls = toolCallTypes.filter((t) => t.type === "delegate").map((t) => t.toolCall);
872
- const interactiveToolCalls = toolCallTypes.filter((t) => t.type === "interactive").map((t) => t.toolCall);
873
- if (mcpToolCalls.length > 0) {
937
+ const classified = await classifyToolCalls(pendingToolCalls, skillManagers);
938
+ if (classified.mcp.length > 0) {
874
939
  const mcpResults = await Promise.all(
875
- mcpToolCalls.map((tc) => executeMcpToolCall(tc, skillManagers))
940
+ classified.mcp.map((c) => toolExecutorFactory.execute(c.toolCall, "mcp", skillManagers))
876
941
  );
877
942
  toolResults.push(...mcpResults);
878
943
  }
879
- if (delegateToolCalls.length > 0) {
944
+ if (classified.delegate.length > 0) {
945
+ const delegateToolCalls = classified.delegate.map((c) => c.toolCall);
946
+ const interactiveToolCalls = classified.interactive.map((c) => c.toolCall);
880
947
  step.partialToolResults = toolResults;
881
948
  step.pendingToolCalls = [...delegateToolCalls, ...interactiveToolCalls];
882
949
  return callDelegate(setting, checkpoint, {
@@ -885,11 +952,12 @@ async function callingToolLogic({
885
952
  usage: step.usage
886
953
  });
887
954
  }
888
- if (interactiveToolCalls.length > 0) {
889
- const interactiveToolCall = interactiveToolCalls[0];
955
+ if (classified.interactive.length > 0) {
956
+ const interactiveToolCall = classified.interactive[0]?.toolCall;
890
957
  if (!interactiveToolCall) {
891
958
  throw new Error("No interactive tool call found");
892
959
  }
960
+ const interactiveToolCalls = classified.interactive.map((c) => c.toolCall);
893
961
  step.partialToolResults = toolResults;
894
962
  step.pendingToolCalls = interactiveToolCalls;
895
963
  return callInteractiveTool(setting, checkpoint, {
@@ -1198,7 +1266,7 @@ async function generatingRunResultLogic({
1198
1266
  usage
1199
1267
  });
1200
1268
  }
1201
- async function classifyToolCalls(toolCalls, skillManagers) {
1269
+ async function classifyToolCalls2(toolCalls, skillManagers) {
1202
1270
  return Promise.all(
1203
1271
  toolCalls.map(async (tc) => {
1204
1272
  const skillManager = await getSkillManagerByToolName(skillManagers, tc.toolName);
@@ -1275,7 +1343,7 @@ async function generatingToolCallLogic({
1275
1343
  usage
1276
1344
  });
1277
1345
  }
1278
- const classified = await classifyToolCalls(toolCalls, skillManagers);
1346
+ const classified = await classifyToolCalls2(toolCalls, skillManagers);
1279
1347
  const sorted = sortToolCallsByPriority(classified);
1280
1348
  if (finishReason === "tool-calls" || finishReason === "stop") {
1281
1349
  const toolCallParts = buildToolCallParts(sorted);
@@ -1963,29 +2031,6 @@ async function executeStateMachine(params) {
1963
2031
  runActor.start();
1964
2032
  });
1965
2033
  }
1966
- var RunEventEmitter = class {
1967
- listeners = [];
1968
- subscribe(listener) {
1969
- this.listeners.push(listener);
1970
- }
1971
- async emit(event) {
1972
- const errors = [];
1973
- for (const listener of this.listeners) {
1974
- try {
1975
- await listener({
1976
- ...event,
1977
- id: createId(),
1978
- timestamp: Date.now()
1979
- });
1980
- } catch (error) {
1981
- errors.push(error instanceof Error ? error : new Error(String(error)));
1982
- }
1983
- }
1984
- if (errors.length > 0) {
1985
- throw new AggregateError(errors, "One or more event listeners failed");
1986
- }
1987
- }
1988
- };
1989
2034
 
1990
2035
  // src/helpers/checkpoint.ts
1991
2036
  function createInitialCheckpoint(checkpointId, params) {
@@ -2049,46 +2094,6 @@ function buildDelegationReturnState(currentSetting, resultCheckpoint, parentChec
2049
2094
  }
2050
2095
  };
2051
2096
  }
2052
- function buildDelegateToState(currentSetting, resultCheckpoint, currentExpert) {
2053
- const { delegateTo } = resultCheckpoint;
2054
- if (!delegateTo || delegateTo.length === 0) {
2055
- throw new Error("delegateTo is required for buildDelegateToState");
2056
- }
2057
- const firstDelegation = delegateTo[0];
2058
- const { expert, toolCallId, toolName, query } = firstDelegation;
2059
- return {
2060
- setting: {
2061
- ...currentSetting,
2062
- expertKey: expert.key,
2063
- input: {
2064
- text: query
2065
- }
2066
- },
2067
- checkpoint: {
2068
- ...resultCheckpoint,
2069
- status: "init",
2070
- messages: [],
2071
- expert: {
2072
- key: expert.key,
2073
- name: expert.name,
2074
- version: expert.version
2075
- },
2076
- delegatedBy: {
2077
- expert: {
2078
- key: currentExpert.key,
2079
- name: currentExpert.name,
2080
- version: currentExpert.version
2081
- },
2082
- toolCallId,
2083
- toolName,
2084
- checkpointId: resultCheckpoint.id
2085
- },
2086
- usage: resultCheckpoint.usage,
2087
- pendingToolCalls: void 0,
2088
- partialToolResults: void 0
2089
- }
2090
- };
2091
- }
2092
2097
  async function resolveExpertToRun(expertKey, experts, clientOptions) {
2093
2098
  if (experts[expertKey]) {
2094
2099
  return experts[expertKey];
@@ -2138,68 +2143,245 @@ async function setupExperts(setting, resolveExpertToRun2 = resolveExpertToRun) {
2138
2143
  }
2139
2144
  return { expertToRun, experts };
2140
2145
  }
2141
-
2142
- // src/run.ts
2143
- var noopStoreCheckpoint = async () => {
2144
- };
2145
- var noopStoreEvent = async () => {
2146
- };
2147
- var noopStoreJob = () => {
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
+ }
2148
2188
  };
2149
- var noopRetrieveJob = () => void 0;
2150
- var noopRetrieveCheckpoint = async (_jobId, _checkpointId) => {
2151
- throw new Error("retrieveCheckpoint not provided");
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
+ }
2152
2323
  };
2153
- var defaultCreateJob = (jobId, expertKey, maxSteps) => ({
2154
- id: jobId,
2155
- coordinatorExpertKey: expertKey,
2156
- status: "running",
2157
- totalSteps: 0,
2158
- startedAt: Date.now(),
2159
- maxSteps,
2160
- usage: createEmptyUsage()
2161
- });
2162
- async function run(runInput, options) {
2163
- const runParams = runParamsSchema.parse(runInput);
2164
- const storeCheckpoint = options?.storeCheckpoint ?? noopStoreCheckpoint;
2165
- const storeEvent = options?.storeEvent ?? noopStoreEvent;
2166
- const storeJob = options?.storeJob ?? noopStoreJob;
2167
- const retrieveJob = options?.retrieveJob ?? noopRetrieveJob;
2168
- const retrieveCheckpoint = options?.retrieveCheckpoint ?? noopRetrieveCheckpoint;
2169
- const createJob = options?.createJob ?? defaultCreateJob;
2170
- const eventListener = createEventListener(options?.eventListener, storeEvent);
2171
- const eventEmitter = new RunEventEmitter();
2172
- eventEmitter.subscribe(eventListener);
2173
- let { setting, checkpoint } = runParams;
2174
- const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
2175
- let job = retrieveJob(setting.jobId) ?? createJob(setting.jobId, setting.expertKey, setting.maxSteps);
2176
- if (job.status !== "running") {
2177
- job = { ...job, status: "running", finishedAt: void 0 };
2324
+ function selectDelegationStrategy(delegationCount) {
2325
+ if (delegationCount === 1) {
2326
+ return new SingleDelegationStrategy();
2178
2327
  }
2179
- storeJob(job);
2180
- while (true) {
2181
- const { expertToRun, experts } = await setupExperts(setting, options?.resolveExpertToRun);
2182
- if (options?.eventListener) {
2183
- const initEvent = createRuntimeEvent("initializeRuntime", setting.jobId, setting.runId, {
2184
- runtimeVersion: package_default.version,
2185
- runtime: "local",
2186
- expertName: expertToRun.name,
2187
- experts: Object.keys(experts),
2188
- model: setting.model,
2189
- temperature: setting.temperature,
2190
- maxSteps: setting.maxSteps,
2191
- maxRetries: setting.maxRetries,
2192
- timeout: setting.timeout,
2193
- query: setting.input.text,
2194
- interactiveToolCall: setting.input.interactiveToolCallResult
2195
- });
2196
- options.eventListener(initEvent);
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
+ }
2364
+ }
2365
+ if (errors.length > 0) {
2366
+ throw new AggregateError(errors, "One or more event listeners failed");
2197
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);
2198
2380
  const skillManagers = await getSkillManagers(
2199
2381
  expertToRun,
2200
2382
  experts,
2201
2383
  setting,
2202
- options?.eventListener,
2384
+ this.options.eventListener,
2203
2385
  { isDelegatedRun: !!checkpoint?.delegatedBy }
2204
2386
  );
2205
2387
  const initialCheckpoint = checkpoint ? createNextStepCheckpoint(createId(), checkpoint) : createInitialCheckpoint(createId(), {
@@ -2209,183 +2391,148 @@ async function run(runInput, options) {
2209
2391
  expert: expertToRun,
2210
2392
  contextWindow
2211
2393
  });
2212
- const runResultCheckpoint = await executeStateMachine({
2394
+ const eventEmitter = new RunEventEmitter();
2395
+ const eventListener = this.createEventListener();
2396
+ eventEmitter.subscribe(eventListener);
2397
+ const resultCheckpoint = await executeStateMachine({
2213
2398
  setting: { ...setting, experts },
2214
2399
  initialCheckpoint,
2215
2400
  eventListener,
2216
2401
  skillManagers,
2217
2402
  eventEmitter,
2218
- storeCheckpoint,
2219
- shouldContinueRun: options?.shouldContinueRun
2403
+ storeCheckpoint: this.options.storeCheckpoint ?? (async () => {
2404
+ }),
2405
+ shouldContinueRun: this.options.shouldContinueRun
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
2220
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;
2221
2473
  job = {
2222
2474
  ...job,
2223
- totalSteps: runResultCheckpoint.stepNumber,
2224
- usage: runResultCheckpoint.usage
2475
+ totalSteps: resultCheckpoint.stepNumber,
2476
+ usage: resultCheckpoint.usage
2225
2477
  };
2226
- switch (runResultCheckpoint.status) {
2478
+ switch (resultCheckpoint.status) {
2227
2479
  case "completed": {
2228
2480
  if (options?.returnOnDelegationComplete) {
2229
2481
  storeJob(job);
2230
- return runResultCheckpoint;
2482
+ return resultCheckpoint;
2231
2483
  }
2232
- if (runResultCheckpoint.delegatedBy) {
2484
+ if (resultCheckpoint.delegatedBy) {
2233
2485
  storeJob(job);
2234
2486
  const parentCheckpoint = await retrieveCheckpoint(
2235
2487
  setting.jobId,
2236
- runResultCheckpoint.delegatedBy.checkpointId
2488
+ resultCheckpoint.delegatedBy.checkpointId
2237
2489
  );
2238
- const result = buildDelegationReturnState(setting, runResultCheckpoint, parentCheckpoint);
2490
+ const result = buildReturnFromDelegation(setting, resultCheckpoint, parentCheckpoint);
2239
2491
  setting = result.setting;
2240
2492
  checkpoint = result.checkpoint;
2241
2493
  break;
2242
2494
  }
2243
2495
  storeJob({ ...job, status: "completed", finishedAt: Date.now() });
2244
- return runResultCheckpoint;
2496
+ return resultCheckpoint;
2245
2497
  }
2246
2498
  case "stoppedByInteractiveTool": {
2247
2499
  storeJob({ ...job, status: "stoppedByInteractiveTool" });
2248
- return runResultCheckpoint;
2500
+ return resultCheckpoint;
2249
2501
  }
2250
2502
  case "stoppedByDelegate": {
2251
2503
  storeJob(job);
2252
- const { delegateTo } = runResultCheckpoint;
2504
+ const { delegateTo } = resultCheckpoint;
2253
2505
  if (!delegateTo || delegateTo.length === 0) {
2254
2506
  throw new Error("No delegations found in checkpoint");
2255
2507
  }
2256
- if (delegateTo.length === 1) {
2257
- const result = buildDelegateToState(setting, runResultCheckpoint, expertToRun);
2258
- setting = result.setting;
2259
- checkpoint = result.checkpoint;
2260
- break;
2261
- }
2262
- const firstDelegation = delegateTo[0];
2263
- const remainingDelegations = delegateTo.slice(1);
2264
- const [firstResult, ...restResults] = await Promise.all(
2265
- delegateTo.map(
2266
- (delegation) => runDelegate(delegation, setting, runResultCheckpoint, expertToRun, options)
2267
- )
2268
- );
2269
- const allResults = [firstResult, ...restResults];
2270
- const aggregatedUsage = allResults.reduce(
2271
- (acc, result) => sumUsage(acc, result.deltaUsage),
2272
- runResultCheckpoint.usage
2273
- );
2274
- const maxStepNumber = Math.max(...allResults.map((r) => r.stepNumber));
2275
- const restToolResults = restResults.map((result) => ({
2276
- id: result.toolCallId,
2277
- skillName: `delegate/${result.expertKey}`,
2278
- toolName: result.toolName,
2279
- result: [{ type: "textPart", id: createId(), text: result.text }]
2280
- }));
2281
- const processedToolCallIds = new Set(remainingDelegations.map((d) => d.toolCallId));
2282
- const remainingToolCalls = runResultCheckpoint.pendingToolCalls?.filter(
2283
- (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
2284
2517
  );
2285
- setting = {
2286
- ...setting,
2287
- expertKey: expertToRun.key,
2288
- input: {
2289
- interactiveToolCallResult: {
2290
- toolCallId: firstResult.toolCallId,
2291
- toolName: firstResult.toolName,
2292
- skillName: `delegate/${firstResult.expertKey}`,
2293
- text: firstResult.text
2294
- }
2295
- }
2296
- };
2297
- checkpoint = {
2298
- ...runResultCheckpoint,
2299
- status: "stoppedByDelegate",
2300
- delegateTo: void 0,
2301
- stepNumber: maxStepNumber,
2302
- usage: aggregatedUsage,
2303
- pendingToolCalls: remainingToolCalls?.length ? remainingToolCalls : void 0,
2304
- partialToolResults: [
2305
- ...runResultCheckpoint.partialToolResults ?? [],
2306
- ...restToolResults
2307
- ]
2308
- };
2518
+ setting = delegationResult.nextSetting;
2519
+ checkpoint = delegationResult.nextCheckpoint;
2309
2520
  break;
2310
2521
  }
2311
2522
  case "stoppedByExceededMaxSteps": {
2312
2523
  storeJob({ ...job, status: "stoppedByMaxSteps", finishedAt: Date.now() });
2313
- return runResultCheckpoint;
2524
+ return resultCheckpoint;
2314
2525
  }
2315
2526
  case "stoppedByError": {
2316
2527
  storeJob({ ...job, status: "stoppedByError", finishedAt: Date.now() });
2317
- return runResultCheckpoint;
2528
+ return resultCheckpoint;
2318
2529
  }
2319
2530
  default:
2320
2531
  throw new Error("Run stopped by unknown reason");
2321
2532
  }
2322
2533
  }
2323
2534
  }
2324
- function createEventListener(userListener, storeEvent) {
2325
- const listener = userListener ?? ((e) => console.log(JSON.stringify(e)));
2326
- return async (event) => {
2327
- if ("stepNumber" in event && storeEvent) {
2328
- await storeEvent(event);
2329
- }
2330
- listener(event);
2331
- };
2332
- }
2333
- async function runDelegate(delegation, parentSetting, parentCheckpoint, parentExpert, options) {
2334
- const { expert, toolCallId, toolName, query } = delegation;
2335
- const delegateRunId = createId();
2336
- const delegateSetting = {
2337
- ...parentSetting,
2338
- runId: delegateRunId,
2339
- expertKey: expert.key,
2340
- input: { text: query }
2341
- };
2342
- const delegateCheckpoint = {
2343
- id: createId(),
2344
- jobId: parentSetting.jobId,
2345
- runId: delegateRunId,
2346
- status: "init",
2347
- stepNumber: parentCheckpoint.stepNumber,
2348
- messages: [],
2349
- expert: {
2350
- key: expert.key,
2351
- name: expert.name,
2352
- version: expert.version
2353
- },
2354
- delegatedBy: {
2355
- expert: {
2356
- key: parentExpert.key,
2357
- name: parentExpert.name,
2358
- version: parentExpert.version
2359
- },
2360
- toolCallId,
2361
- toolName,
2362
- checkpointId: parentCheckpoint.id
2363
- },
2364
- usage: createEmptyUsage(),
2365
- contextWindow: parentCheckpoint.contextWindow
2366
- };
2367
- const resultCheckpoint = await run(
2368
- { setting: delegateSetting, checkpoint: delegateCheckpoint },
2369
- { ...options, returnOnDelegationComplete: true }
2370
- );
2371
- const lastMessage = resultCheckpoint.messages[resultCheckpoint.messages.length - 1];
2372
- if (!lastMessage || lastMessage.type !== "expertMessage") {
2373
- throw new Error("Delegation error: delegation result message is incorrect");
2374
- }
2375
- const textPart = lastMessage.contents.find((c) => c.type === "textPart");
2376
- if (!textPart || textPart.type !== "textPart") {
2377
- throw new Error("Delegation error: delegation result message does not contain text");
2378
- }
2379
- return {
2380
- toolCallId,
2381
- toolName,
2382
- expertKey: expert.key,
2383
- text: textPart.text,
2384
- stepNumber: resultCheckpoint.stepNumber,
2385
- deltaUsage: resultCheckpoint.usage
2386
- };
2387
- }
2388
2535
 
2389
2536
  export { getModel, package_default, run, runtimeStateMachine };
2390
- //# sourceMappingURL=chunk-AQSRQW5R.js.map
2391
- //# sourceMappingURL=chunk-AQSRQW5R.js.map
2537
+ //# sourceMappingURL=chunk-7QLRRSNX.js.map
2538
+ //# sourceMappingURL=chunk-7QLRRSNX.js.map