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