@perstack/core 0.0.54 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +6363 -28
- package/dist/src/index.js +1982 -28
- package/dist/src/index.js.map +1 -1
- package/package.json +13 -16
- package/dist/src/adapters/event-creators.d.ts +0 -26
- package/dist/src/adapters/event-creators.d.ts.map +0 -1
- package/dist/src/adapters/event-creators.js +0 -126
- package/dist/src/adapters/event-creators.js.map +0 -1
- package/dist/src/adapters/index.d.ts +0 -2
- package/dist/src/adapters/index.d.ts.map +0 -1
- package/dist/src/adapters/index.js +0 -2
- package/dist/src/adapters/index.js.map +0 -1
- package/dist/src/constants/constants.d.ts +0 -11
- package/dist/src/constants/constants.d.ts.map +0 -1
- package/dist/src/constants/constants.js +0 -13
- package/dist/src/constants/constants.js.map +0 -1
- package/dist/src/errors.d.ts +0 -4
- package/dist/src/errors.d.ts.map +0 -1
- package/dist/src/errors.js +0 -7
- package/dist/src/errors.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/known-models/index.d.ts +0 -9
- package/dist/src/known-models/index.d.ts.map +0 -1
- package/dist/src/known-models/index.js +0 -216
- package/dist/src/known-models/index.js.map +0 -1
- package/dist/src/schemas/activity.d.ts +0 -2159
- package/dist/src/schemas/activity.d.ts.map +0 -1
- package/dist/src/schemas/activity.js +0 -209
- package/dist/src/schemas/activity.js.map +0 -1
- package/dist/src/schemas/checkpoint.d.ts +0 -338
- package/dist/src/schemas/checkpoint.d.ts.map +0 -1
- package/dist/src/schemas/checkpoint.js +0 -69
- package/dist/src/schemas/checkpoint.js.map +0 -1
- package/dist/src/schemas/expert.d.ts +0 -309
- package/dist/src/schemas/expert.d.ts.map +0 -1
- package/dist/src/schemas/expert.js +0 -70
- package/dist/src/schemas/expert.js.map +0 -1
- package/dist/src/schemas/job.d.ts +0 -44
- package/dist/src/schemas/job.d.ts.map +0 -1
- package/dist/src/schemas/job.js +0 -22
- package/dist/src/schemas/job.js.map +0 -1
- package/dist/src/schemas/lockfile.d.ts +0 -143
- package/dist/src/schemas/lockfile.d.ts.map +0 -1
- package/dist/src/schemas/lockfile.js +0 -26
- package/dist/src/schemas/lockfile.js.map +0 -1
- package/dist/src/schemas/message-part.d.ts +0 -239
- package/dist/src/schemas/message-part.d.ts.map +0 -1
- package/dist/src/schemas/message-part.js +0 -80
- package/dist/src/schemas/message-part.js.map +0 -1
- package/dist/src/schemas/message.d.ts +0 -236
- package/dist/src/schemas/message.d.ts.map +0 -1
- package/dist/src/schemas/message.js +0 -44
- package/dist/src/schemas/message.js.map +0 -1
- package/dist/src/schemas/perstack-toml.d.ts +0 -366
- package/dist/src/schemas/perstack-toml.d.ts.map +0 -1
- package/dist/src/schemas/perstack-toml.js +0 -175
- package/dist/src/schemas/perstack-toml.js.map +0 -1
- package/dist/src/schemas/provider-config.d.ts +0 -216
- package/dist/src/schemas/provider-config.d.ts.map +0 -1
- package/dist/src/schemas/provider-config.js +0 -86
- package/dist/src/schemas/provider-config.js.map +0 -1
- package/dist/src/schemas/provider-tools.d.ts +0 -93
- package/dist/src/schemas/provider-tools.d.ts.map +0 -1
- package/dist/src/schemas/provider-tools.js +0 -60
- package/dist/src/schemas/provider-tools.js.map +0 -1
- package/dist/src/schemas/run-command.d.ts +0 -127
- package/dist/src/schemas/run-command.d.ts.map +0 -1
- package/dist/src/schemas/run-command.js +0 -82
- package/dist/src/schemas/run-command.js.map +0 -1
- package/dist/src/schemas/runtime-version.d.ts +0 -4
- package/dist/src/schemas/runtime-version.d.ts.map +0 -1
- package/dist/src/schemas/runtime-version.js +0 -6
- package/dist/src/schemas/runtime-version.js.map +0 -1
- package/dist/src/schemas/runtime.d.ts +0 -1317
- package/dist/src/schemas/runtime.d.ts.map +0 -1
- package/dist/src/schemas/runtime.js +0 -205
- package/dist/src/schemas/runtime.js.map +0 -1
- package/dist/src/schemas/skill-manager.d.ts +0 -64
- package/dist/src/schemas/skill-manager.d.ts.map +0 -1
- package/dist/src/schemas/skill-manager.js +0 -2
- package/dist/src/schemas/skill-manager.js.map +0 -1
- package/dist/src/schemas/skill.d.ts +0 -147
- package/dist/src/schemas/skill.d.ts.map +0 -1
- package/dist/src/schemas/skill.js +0 -99
- package/dist/src/schemas/skill.js.map +0 -1
- package/dist/src/schemas/step.d.ts +0 -370
- package/dist/src/schemas/step.d.ts.map +0 -1
- package/dist/src/schemas/step.js +0 -21
- package/dist/src/schemas/step.js.map +0 -1
- package/dist/src/schemas/tool-call.d.ts +0 -19
- package/dist/src/schemas/tool-call.d.ts.map +0 -1
- package/dist/src/schemas/tool-call.js +0 -10
- package/dist/src/schemas/tool-call.js.map +0 -1
- package/dist/src/schemas/tool-result.d.ts +0 -86
- package/dist/src/schemas/tool-result.d.ts.map +0 -1
- package/dist/src/schemas/tool-result.js +0 -11
- package/dist/src/schemas/tool-result.js.map +0 -1
- package/dist/src/schemas/usage.d.ts +0 -22
- package/dist/src/schemas/usage.d.ts.map +0 -1
- package/dist/src/schemas/usage.js +0 -10
- package/dist/src/schemas/usage.js.map +0 -1
- package/dist/src/utils/activity.d.ts +0 -20
- package/dist/src/utils/activity.d.ts.map +0 -1
- package/dist/src/utils/activity.js +0 -449
- package/dist/src/utils/activity.js.map +0 -1
- package/dist/src/utils/env-filter.d.ts +0 -4
- package/dist/src/utils/env-filter.d.ts.map +0 -1
- package/dist/src/utils/env-filter.js +0 -50
- package/dist/src/utils/env-filter.js.map +0 -1
- package/dist/src/utils/event-filter.d.ts +0 -16
- package/dist/src/utils/event-filter.d.ts.map +0 -1
- package/dist/src/utils/event-filter.js +0 -31
- package/dist/src/utils/event-filter.js.map +0 -1
- package/dist/src/utils/expert-type.d.ts +0 -38
- package/dist/src/utils/expert-type.d.ts.map +0 -1
- package/dist/src/utils/expert-type.js +0 -88
- package/dist/src/utils/expert-type.js.map +0 -1
- package/dist/src/utils/zod-error.d.ts +0 -4
- package/dist/src/utils/zod-error.d.ts.map +0 -1
- package/dist/src/utils/zod-error.js +0 -17
- package/dist/src/utils/zod-error.js.map +0 -1
package/dist/src/index.js
CHANGED
|
@@ -1,29 +1,1983 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/event-creators.ts
|
|
5
|
+
function createEmptyUsage() {
|
|
6
|
+
return {
|
|
7
|
+
inputTokens: 0,
|
|
8
|
+
outputTokens: 0,
|
|
9
|
+
reasoningTokens: 0,
|
|
10
|
+
totalTokens: 0,
|
|
11
|
+
cachedInputTokens: 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function createNormalizedCheckpoint(params) {
|
|
15
|
+
const { jobId, runId, expert, output } = params;
|
|
16
|
+
return {
|
|
17
|
+
id: createId(),
|
|
18
|
+
jobId,
|
|
19
|
+
runId,
|
|
20
|
+
status: "completed",
|
|
21
|
+
stepNumber: 1,
|
|
22
|
+
messages: [{
|
|
23
|
+
id: createId(),
|
|
24
|
+
type: "expertMessage",
|
|
25
|
+
contents: [{
|
|
26
|
+
type: "textPart",
|
|
27
|
+
id: createId(),
|
|
28
|
+
text: output
|
|
29
|
+
}]
|
|
30
|
+
}],
|
|
31
|
+
expert: {
|
|
32
|
+
key: expert.key,
|
|
33
|
+
name: expert.name,
|
|
34
|
+
version: expert.version
|
|
35
|
+
},
|
|
36
|
+
usage: createEmptyUsage()
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createStartRunEvent(jobId, runId, expertKey, checkpoint) {
|
|
40
|
+
return {
|
|
41
|
+
type: "startRun",
|
|
42
|
+
id: createId(),
|
|
43
|
+
expertKey,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
jobId,
|
|
46
|
+
runId,
|
|
47
|
+
stepNumber: checkpoint.stepNumber,
|
|
48
|
+
initialCheckpoint: checkpoint,
|
|
49
|
+
inputMessages: []
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function createRuntimeInitEvent(jobId, runId, expertName, version, query) {
|
|
53
|
+
return {
|
|
54
|
+
type: "initializeRuntime",
|
|
55
|
+
id: createId(),
|
|
56
|
+
timestamp: Date.now(),
|
|
57
|
+
jobId,
|
|
58
|
+
runId,
|
|
59
|
+
runtimeVersion: version,
|
|
60
|
+
expertName,
|
|
61
|
+
experts: [],
|
|
62
|
+
model: "local:default",
|
|
63
|
+
maxRetries: 0,
|
|
64
|
+
timeout: 0,
|
|
65
|
+
query
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function createCompleteRunEvent(jobId, runId, expertKey, checkpoint, output, startedAt) {
|
|
69
|
+
const lastMessage = checkpoint.messages[checkpoint.messages.length - 1];
|
|
70
|
+
return {
|
|
71
|
+
type: "completeRun",
|
|
72
|
+
id: createId(),
|
|
73
|
+
expertKey,
|
|
74
|
+
timestamp: Date.now(),
|
|
75
|
+
jobId,
|
|
76
|
+
runId,
|
|
77
|
+
stepNumber: checkpoint.stepNumber,
|
|
78
|
+
checkpoint,
|
|
79
|
+
step: {
|
|
80
|
+
stepNumber: checkpoint.stepNumber,
|
|
81
|
+
newMessages: lastMessage ? [lastMessage] : [],
|
|
82
|
+
usage: createEmptyUsage(),
|
|
83
|
+
startedAt: startedAt ?? Date.now()
|
|
84
|
+
},
|
|
85
|
+
text: output,
|
|
86
|
+
usage: createEmptyUsage()
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function createCallToolsEvent(jobId, runId, expertKey, stepNumber, toolCalls, _checkpoint) {
|
|
90
|
+
const expertMessage = {
|
|
91
|
+
id: createId(),
|
|
92
|
+
type: "expertMessage",
|
|
93
|
+
contents: []
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
type: "callTools",
|
|
97
|
+
id: createId(),
|
|
98
|
+
expertKey,
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
jobId,
|
|
101
|
+
runId,
|
|
102
|
+
stepNumber,
|
|
103
|
+
newMessage: expertMessage,
|
|
104
|
+
toolCalls,
|
|
105
|
+
usage: createEmptyUsage()
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function createResolveToolResultsEvent(jobId, runId, expertKey, stepNumber, toolResults) {
|
|
109
|
+
return {
|
|
110
|
+
type: "resolveToolResults",
|
|
111
|
+
id: createId(),
|
|
112
|
+
expertKey,
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
jobId,
|
|
115
|
+
runId,
|
|
116
|
+
stepNumber,
|
|
117
|
+
toolResults
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function createToolMessage(toolCallId, toolName, resultText) {
|
|
121
|
+
return {
|
|
122
|
+
id: createId(),
|
|
123
|
+
type: "toolMessage",
|
|
124
|
+
contents: [{
|
|
125
|
+
type: "toolResultPart",
|
|
126
|
+
id: createId(),
|
|
127
|
+
toolCallId,
|
|
128
|
+
toolName,
|
|
129
|
+
contents: [{
|
|
130
|
+
type: "textPart",
|
|
131
|
+
id: createId(),
|
|
132
|
+
text: resultText
|
|
133
|
+
}]
|
|
134
|
+
}]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/constants/constants.ts
|
|
140
|
+
const defaultPerstackApiBaseUrl = "https://api.perstack.ai";
|
|
141
|
+
const expertKeyRegex = /^((?:@[a-z0-9][a-z0-9_.-]*\/)?[a-z0-9][a-z0-9_.-]*)(?:@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?)|@([a-z0-9][a-z0-9_.-]*))?$/;
|
|
142
|
+
const expertNameRegex = /^(@[a-z0-9][a-z0-9_-]*\/)?[a-z0-9][a-z0-9_-]*$/;
|
|
143
|
+
const expertVersionRegex = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
|
|
144
|
+
const tagNameRegex = /^[a-z0-9][a-z0-9_-]*$/;
|
|
145
|
+
const maxExpertNameLength = 255;
|
|
146
|
+
const defaultMaxRetries = 5;
|
|
147
|
+
const defaultTimeout = 5 * 1e3 * 60;
|
|
148
|
+
const maxSkillNameLength = 255;
|
|
149
|
+
const maxSkillToolNameLength = 255;
|
|
150
|
+
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/errors.ts
|
|
153
|
+
var PerstackError = class extends Error {
|
|
154
|
+
constructor(message) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "PerstackError";
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/known-models/index.ts
|
|
162
|
+
const knownModels = [
|
|
163
|
+
{
|
|
164
|
+
provider: "anthropic",
|
|
165
|
+
models: [
|
|
166
|
+
{
|
|
167
|
+
name: "claude-opus-4-6",
|
|
168
|
+
contextWindow: 2e5,
|
|
169
|
+
maxOutputTokens: 128e3
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "claude-opus-4-5",
|
|
173
|
+
contextWindow: 2e5,
|
|
174
|
+
maxOutputTokens: 32e3
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "claude-opus-4-1",
|
|
178
|
+
contextWindow: 2e5,
|
|
179
|
+
maxOutputTokens: 32e3
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "claude-opus-4-20250514",
|
|
183
|
+
contextWindow: 2e5,
|
|
184
|
+
maxOutputTokens: 32e3
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "claude-sonnet-4-5",
|
|
188
|
+
contextWindow: 2e5,
|
|
189
|
+
maxOutputTokens: 64e3
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "claude-sonnet-4-20250514",
|
|
193
|
+
contextWindow: 2e5,
|
|
194
|
+
maxOutputTokens: 64e3
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "claude-3-7-sonnet-20250219",
|
|
198
|
+
contextWindow: 2e5,
|
|
199
|
+
maxOutputTokens: 64e3
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "claude-haiku-4-5",
|
|
203
|
+
contextWindow: 2e5,
|
|
204
|
+
maxOutputTokens: 8192
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "claude-3-5-haiku-latest",
|
|
208
|
+
contextWindow: 2e5,
|
|
209
|
+
maxOutputTokens: 8192
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
provider: "google",
|
|
215
|
+
models: [
|
|
216
|
+
{
|
|
217
|
+
name: "gemini-3-flash-preview",
|
|
218
|
+
contextWindow: 1048576,
|
|
219
|
+
maxOutputTokens: 65536
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "gemini-3-pro-preview",
|
|
223
|
+
contextWindow: 1048576,
|
|
224
|
+
maxOutputTokens: 65536
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: "gemini-2.5-pro",
|
|
228
|
+
contextWindow: 1048576,
|
|
229
|
+
maxOutputTokens: 65536
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: "gemini-2.5-flash",
|
|
233
|
+
contextWindow: 1048576,
|
|
234
|
+
maxOutputTokens: 65536
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "gemini-2.5-flash-lite",
|
|
238
|
+
contextWindow: 1048576,
|
|
239
|
+
maxOutputTokens: 65536
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
provider: "openai",
|
|
245
|
+
models: [
|
|
246
|
+
{
|
|
247
|
+
name: "gpt-5",
|
|
248
|
+
contextWindow: 4e5,
|
|
249
|
+
maxOutputTokens: 128e3
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
name: "gpt-5-mini",
|
|
253
|
+
contextWindow: 4e5,
|
|
254
|
+
maxOutputTokens: 128e3
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "gpt-5-nano",
|
|
258
|
+
contextWindow: 4e5,
|
|
259
|
+
maxOutputTokens: 128e3
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: "gpt-5.2",
|
|
263
|
+
contextWindow: 4e5,
|
|
264
|
+
maxOutputTokens: 128e3
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: "gpt-5.2-pro",
|
|
268
|
+
contextWindow: 4e5,
|
|
269
|
+
maxOutputTokens: 128e3
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: "gpt-5.1",
|
|
273
|
+
contextWindow: 4e5,
|
|
274
|
+
maxOutputTokens: 128e3
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "gpt-5-chat-latest",
|
|
278
|
+
contextWindow: 128e3,
|
|
279
|
+
maxOutputTokens: 16384
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "o4-mini",
|
|
283
|
+
contextWindow: 2e5,
|
|
284
|
+
maxOutputTokens: 1e5
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "o3",
|
|
288
|
+
contextWindow: 2e5,
|
|
289
|
+
maxOutputTokens: 1e4
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "o3-mini",
|
|
293
|
+
contextWindow: 2e5,
|
|
294
|
+
maxOutputTokens: 1e4
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: "gpt-4.1",
|
|
298
|
+
contextWindow: 1047576,
|
|
299
|
+
maxOutputTokens: 32768
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
provider: "deepseek",
|
|
305
|
+
models: [{
|
|
306
|
+
name: "deepseek-chat",
|
|
307
|
+
contextWindow: 128e3,
|
|
308
|
+
maxOutputTokens: 8192
|
|
309
|
+
}, {
|
|
310
|
+
name: "deepseek-reasoner",
|
|
311
|
+
contextWindow: 128e3,
|
|
312
|
+
maxOutputTokens: 64e3
|
|
313
|
+
}]
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
provider: "ollama",
|
|
317
|
+
models: [
|
|
318
|
+
{
|
|
319
|
+
name: "gpt-oss:20b",
|
|
320
|
+
contextWindow: 131072,
|
|
321
|
+
maxOutputTokens: 131072
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "gpt-oss:120b",
|
|
325
|
+
contextWindow: 131072,
|
|
326
|
+
maxOutputTokens: 131072
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "gemma3:1b",
|
|
330
|
+
contextWindow: 32e3,
|
|
331
|
+
maxOutputTokens: 32e3
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "gemma3:4b",
|
|
335
|
+
contextWindow: 128e3,
|
|
336
|
+
maxOutputTokens: 128e3
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: "gemma3:12b",
|
|
340
|
+
contextWindow: 128e3,
|
|
341
|
+
maxOutputTokens: 128e3
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "gemma3:27b",
|
|
345
|
+
contextWindow: 128e3,
|
|
346
|
+
maxOutputTokens: 128e3
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
];
|
|
351
|
+
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/schemas/message-part.ts
|
|
354
|
+
const basePartSchema = z.object({ id: z.string() });
|
|
355
|
+
const textPartSchema = basePartSchema.extend({
|
|
356
|
+
type: z.literal("textPart"),
|
|
357
|
+
text: z.string()
|
|
358
|
+
});
|
|
359
|
+
const imageUrlPartSchema = basePartSchema.extend({
|
|
360
|
+
type: z.literal("imageUrlPart"),
|
|
361
|
+
url: z.url(),
|
|
362
|
+
mimeType: z.string()
|
|
363
|
+
});
|
|
364
|
+
const imageInlinePartSchema = basePartSchema.extend({
|
|
365
|
+
type: z.literal("imageInlinePart"),
|
|
366
|
+
encodedData: z.string(),
|
|
367
|
+
mimeType: z.string()
|
|
368
|
+
});
|
|
369
|
+
const imageBinaryPartSchema = basePartSchema.extend({
|
|
370
|
+
type: z.literal("imageBinaryPart"),
|
|
371
|
+
data: z.string(),
|
|
372
|
+
mimeType: z.string()
|
|
373
|
+
});
|
|
374
|
+
const fileUrlPartSchema = basePartSchema.extend({
|
|
375
|
+
type: z.literal("fileUrlPart"),
|
|
376
|
+
url: z.string().url(),
|
|
377
|
+
mimeType: z.string()
|
|
378
|
+
});
|
|
379
|
+
const fileInlinePartSchema = basePartSchema.extend({
|
|
380
|
+
type: z.literal("fileInlinePart"),
|
|
381
|
+
encodedData: z.string(),
|
|
382
|
+
mimeType: z.string()
|
|
383
|
+
});
|
|
384
|
+
const fileBinaryPartSchema = basePartSchema.extend({
|
|
385
|
+
type: z.literal("fileBinaryPart"),
|
|
386
|
+
data: z.string(),
|
|
387
|
+
mimeType: z.string()
|
|
388
|
+
});
|
|
389
|
+
const toolCallPartSchema = basePartSchema.extend({
|
|
390
|
+
type: z.literal("toolCallPart"),
|
|
391
|
+
toolCallId: z.string(),
|
|
392
|
+
toolName: z.string(),
|
|
393
|
+
args: z.record(z.string(), z.unknown())
|
|
394
|
+
});
|
|
395
|
+
const thinkingPartSchema = basePartSchema.extend({
|
|
396
|
+
type: z.literal("thinkingPart"),
|
|
397
|
+
thinking: z.string(),
|
|
398
|
+
signature: z.string().optional()
|
|
399
|
+
});
|
|
400
|
+
const toolResultPartSchema = basePartSchema.extend({
|
|
401
|
+
type: z.literal("toolResultPart"),
|
|
402
|
+
toolCallId: z.string(),
|
|
403
|
+
toolName: z.string(),
|
|
404
|
+
contents: z.array(z.union([
|
|
405
|
+
textPartSchema,
|
|
406
|
+
imageInlinePartSchema,
|
|
407
|
+
fileInlinePartSchema
|
|
408
|
+
])),
|
|
409
|
+
isError: z.boolean().optional()
|
|
410
|
+
});
|
|
411
|
+
const messagePartSchema = z.discriminatedUnion("type", [
|
|
412
|
+
textPartSchema,
|
|
413
|
+
imageUrlPartSchema,
|
|
414
|
+
imageInlinePartSchema,
|
|
415
|
+
imageBinaryPartSchema,
|
|
416
|
+
fileUrlPartSchema,
|
|
417
|
+
fileInlinePartSchema,
|
|
418
|
+
fileBinaryPartSchema,
|
|
419
|
+
toolCallPartSchema,
|
|
420
|
+
toolResultPartSchema,
|
|
421
|
+
thinkingPartSchema
|
|
422
|
+
]);
|
|
423
|
+
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region src/schemas/activity.ts
|
|
426
|
+
const baseActivitySchema = z.object({
|
|
427
|
+
id: z.string(),
|
|
428
|
+
expertKey: z.string(),
|
|
429
|
+
runId: z.string(),
|
|
430
|
+
previousActivityId: z.string().optional(),
|
|
431
|
+
delegatedBy: z.object({
|
|
432
|
+
expertKey: z.string(),
|
|
433
|
+
runId: z.string()
|
|
434
|
+
}).optional(),
|
|
435
|
+
reasoning: z.string().optional()
|
|
436
|
+
});
|
|
437
|
+
const queryActivitySchema = baseActivitySchema.extend({
|
|
438
|
+
type: z.literal("query"),
|
|
439
|
+
text: z.string()
|
|
440
|
+
});
|
|
441
|
+
const retryActivitySchema = baseActivitySchema.extend({
|
|
442
|
+
type: z.literal("retry"),
|
|
443
|
+
error: z.string(),
|
|
444
|
+
message: z.string()
|
|
445
|
+
});
|
|
446
|
+
const completeActivitySchema = baseActivitySchema.extend({
|
|
447
|
+
type: z.literal("complete"),
|
|
448
|
+
text: z.string()
|
|
449
|
+
});
|
|
450
|
+
const errorActivitySchema = baseActivitySchema.extend({
|
|
451
|
+
type: z.literal("error"),
|
|
452
|
+
error: z.string().optional(),
|
|
453
|
+
errorName: z.string().optional(),
|
|
454
|
+
isRetryable: z.boolean().optional()
|
|
455
|
+
});
|
|
456
|
+
const attemptCompletionActivitySchema = baseActivitySchema.extend({
|
|
457
|
+
type: z.literal("attemptCompletion"),
|
|
458
|
+
remainingTodos: z.array(z.object({
|
|
459
|
+
id: z.number(),
|
|
460
|
+
title: z.string(),
|
|
461
|
+
completed: z.boolean()
|
|
462
|
+
})).optional(),
|
|
463
|
+
error: z.string().optional()
|
|
464
|
+
});
|
|
465
|
+
const todoActivitySchema = baseActivitySchema.extend({
|
|
466
|
+
type: z.literal("todo"),
|
|
467
|
+
newTodos: z.array(z.string()).optional(),
|
|
468
|
+
completedTodos: z.array(z.number()).optional(),
|
|
469
|
+
todos: z.array(z.object({
|
|
470
|
+
id: z.number(),
|
|
471
|
+
title: z.string(),
|
|
472
|
+
completed: z.boolean()
|
|
473
|
+
})),
|
|
474
|
+
error: z.string().optional()
|
|
475
|
+
});
|
|
476
|
+
const clearTodoActivitySchema = baseActivitySchema.extend({
|
|
477
|
+
type: z.literal("clearTodo"),
|
|
478
|
+
error: z.string().optional()
|
|
479
|
+
});
|
|
480
|
+
const readImageFileActivitySchema = baseActivitySchema.extend({
|
|
481
|
+
type: z.literal("readImageFile"),
|
|
482
|
+
path: z.string(),
|
|
483
|
+
mimeType: z.string().optional(),
|
|
484
|
+
size: z.number().optional(),
|
|
485
|
+
error: z.string().optional()
|
|
486
|
+
});
|
|
487
|
+
const readPdfFileActivitySchema = baseActivitySchema.extend({
|
|
488
|
+
type: z.literal("readPdfFile"),
|
|
489
|
+
path: z.string(),
|
|
490
|
+
mimeType: z.string().optional(),
|
|
491
|
+
size: z.number().optional(),
|
|
492
|
+
error: z.string().optional()
|
|
493
|
+
});
|
|
494
|
+
const readTextFileActivitySchema = baseActivitySchema.extend({
|
|
495
|
+
type: z.literal("readTextFile"),
|
|
496
|
+
path: z.string(),
|
|
497
|
+
content: z.string().optional(),
|
|
498
|
+
from: z.number().optional(),
|
|
499
|
+
to: z.number().optional(),
|
|
500
|
+
error: z.string().optional()
|
|
501
|
+
});
|
|
502
|
+
const editTextFileActivitySchema = baseActivitySchema.extend({
|
|
503
|
+
type: z.literal("editTextFile"),
|
|
504
|
+
path: z.string(),
|
|
505
|
+
newText: z.string(),
|
|
506
|
+
oldText: z.string(),
|
|
507
|
+
error: z.string().optional()
|
|
508
|
+
});
|
|
509
|
+
const writeTextFileActivitySchema = baseActivitySchema.extend({
|
|
510
|
+
type: z.literal("writeTextFile"),
|
|
511
|
+
path: z.string(),
|
|
512
|
+
text: z.string(),
|
|
513
|
+
error: z.string().optional()
|
|
514
|
+
});
|
|
515
|
+
const execActivitySchema = baseActivitySchema.extend({
|
|
516
|
+
type: z.literal("exec"),
|
|
517
|
+
command: z.string(),
|
|
518
|
+
args: z.array(z.string()),
|
|
519
|
+
cwd: z.string(),
|
|
520
|
+
output: z.string().optional(),
|
|
521
|
+
error: z.string().optional(),
|
|
522
|
+
stdout: z.string().optional(),
|
|
523
|
+
stderr: z.string().optional()
|
|
524
|
+
});
|
|
525
|
+
const delegateActivitySchema = baseActivitySchema.extend({
|
|
526
|
+
type: z.literal("delegate"),
|
|
527
|
+
delegateExpertKey: z.string(),
|
|
528
|
+
query: z.string()
|
|
529
|
+
});
|
|
530
|
+
const delegationCompleteActivitySchema = baseActivitySchema.extend({
|
|
531
|
+
type: z.literal("delegationComplete"),
|
|
532
|
+
count: z.number()
|
|
533
|
+
});
|
|
534
|
+
const interactiveToolActivitySchema = baseActivitySchema.extend({
|
|
535
|
+
type: z.literal("interactiveTool"),
|
|
536
|
+
skillName: z.string(),
|
|
537
|
+
toolName: z.string(),
|
|
538
|
+
args: z.record(z.string(), z.unknown())
|
|
539
|
+
});
|
|
540
|
+
const generalToolActivitySchema = baseActivitySchema.extend({
|
|
541
|
+
type: z.literal("generalTool"),
|
|
542
|
+
skillName: z.string(),
|
|
543
|
+
toolName: z.string(),
|
|
544
|
+
args: z.record(z.string(), z.unknown()),
|
|
545
|
+
result: z.array(messagePartSchema).optional(),
|
|
546
|
+
error: z.string().optional()
|
|
547
|
+
});
|
|
548
|
+
const addSkillActivitySchema = baseActivitySchema.extend({
|
|
549
|
+
type: z.literal("addSkill"),
|
|
550
|
+
name: z.string(),
|
|
551
|
+
skillType: z.string(),
|
|
552
|
+
tools: z.array(z.string()).optional(),
|
|
553
|
+
error: z.string().optional()
|
|
554
|
+
});
|
|
555
|
+
const removeSkillActivitySchema = baseActivitySchema.extend({
|
|
556
|
+
type: z.literal("removeSkill"),
|
|
557
|
+
skillName: z.string(),
|
|
558
|
+
error: z.string().optional()
|
|
559
|
+
});
|
|
560
|
+
const addDelegateActivitySchema = baseActivitySchema.extend({
|
|
561
|
+
type: z.literal("addDelegate"),
|
|
562
|
+
targetExpertKey: z.string(),
|
|
563
|
+
delegateToolName: z.string().optional(),
|
|
564
|
+
error: z.string().optional()
|
|
565
|
+
});
|
|
566
|
+
const removeDelegateActivitySchema = baseActivitySchema.extend({
|
|
567
|
+
type: z.literal("removeDelegate"),
|
|
568
|
+
expertName: z.string(),
|
|
569
|
+
error: z.string().optional()
|
|
570
|
+
});
|
|
571
|
+
const createExpertActivitySchema = baseActivitySchema.extend({
|
|
572
|
+
type: z.literal("createExpert"),
|
|
573
|
+
targetKey: z.string(),
|
|
574
|
+
description: z.string().optional(),
|
|
575
|
+
resultExpertKey: z.string().optional(),
|
|
576
|
+
error: z.string().optional()
|
|
577
|
+
});
|
|
578
|
+
const activitySchema = z.discriminatedUnion("type", [
|
|
579
|
+
queryActivitySchema,
|
|
580
|
+
retryActivitySchema,
|
|
581
|
+
completeActivitySchema,
|
|
582
|
+
errorActivitySchema,
|
|
583
|
+
attemptCompletionActivitySchema,
|
|
584
|
+
todoActivitySchema,
|
|
585
|
+
clearTodoActivitySchema,
|
|
586
|
+
readImageFileActivitySchema,
|
|
587
|
+
readPdfFileActivitySchema,
|
|
588
|
+
readTextFileActivitySchema,
|
|
589
|
+
editTextFileActivitySchema,
|
|
590
|
+
writeTextFileActivitySchema,
|
|
591
|
+
execActivitySchema,
|
|
592
|
+
delegateActivitySchema,
|
|
593
|
+
delegationCompleteActivitySchema,
|
|
594
|
+
interactiveToolActivitySchema,
|
|
595
|
+
generalToolActivitySchema,
|
|
596
|
+
addSkillActivitySchema,
|
|
597
|
+
removeSkillActivitySchema,
|
|
598
|
+
addDelegateActivitySchema,
|
|
599
|
+
removeDelegateActivitySchema,
|
|
600
|
+
createExpertActivitySchema
|
|
601
|
+
]);
|
|
602
|
+
const parallelActivitiesGroupSchema = z.object({
|
|
603
|
+
type: z.literal("parallelGroup"),
|
|
604
|
+
id: z.string(),
|
|
605
|
+
expertKey: z.string(),
|
|
606
|
+
runId: z.string(),
|
|
607
|
+
reasoning: z.string().optional(),
|
|
608
|
+
activities: z.array(activitySchema)
|
|
609
|
+
});
|
|
610
|
+
const activityOrGroupSchema = z.union([activitySchema, parallelActivitiesGroupSchema]);
|
|
611
|
+
|
|
612
|
+
//#endregion
|
|
613
|
+
//#region src/schemas/message.ts
|
|
614
|
+
const baseMessageSchema = z.object({ id: z.string() });
|
|
615
|
+
const instructionMessageSchema = baseMessageSchema.extend({
|
|
616
|
+
type: z.literal("instructionMessage"),
|
|
617
|
+
contents: z.array(textPartSchema),
|
|
618
|
+
cache: z.boolean().optional()
|
|
619
|
+
});
|
|
620
|
+
const userMessageSchema = baseMessageSchema.extend({
|
|
621
|
+
type: z.literal("userMessage"),
|
|
622
|
+
contents: z.array(z.union([
|
|
623
|
+
textPartSchema,
|
|
624
|
+
imageUrlPartSchema,
|
|
625
|
+
imageInlinePartSchema,
|
|
626
|
+
imageBinaryPartSchema,
|
|
627
|
+
fileUrlPartSchema,
|
|
628
|
+
fileInlinePartSchema,
|
|
629
|
+
fileBinaryPartSchema
|
|
630
|
+
])),
|
|
631
|
+
cache: z.boolean().optional()
|
|
632
|
+
});
|
|
633
|
+
const expertMessageSchema = baseMessageSchema.extend({
|
|
634
|
+
type: z.literal("expertMessage"),
|
|
635
|
+
contents: z.array(z.union([
|
|
636
|
+
textPartSchema,
|
|
637
|
+
toolCallPartSchema,
|
|
638
|
+
thinkingPartSchema
|
|
639
|
+
])),
|
|
640
|
+
cache: z.boolean().optional()
|
|
641
|
+
});
|
|
642
|
+
const toolMessageSchema = baseMessageSchema.extend({
|
|
643
|
+
type: z.literal("toolMessage"),
|
|
644
|
+
contents: z.array(toolResultPartSchema),
|
|
645
|
+
cache: z.boolean().optional()
|
|
646
|
+
});
|
|
647
|
+
const messageSchema = z.union([
|
|
648
|
+
instructionMessageSchema,
|
|
649
|
+
userMessageSchema,
|
|
650
|
+
expertMessageSchema,
|
|
651
|
+
toolMessageSchema
|
|
652
|
+
]);
|
|
653
|
+
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/schemas/tool-call.ts
|
|
656
|
+
const toolCallSchema = z.object({
|
|
657
|
+
id: z.string().min(1).max(255),
|
|
658
|
+
skillName: z.string().min(1).max(maxSkillNameLength),
|
|
659
|
+
toolName: z.string().min(1).max(maxSkillToolNameLength),
|
|
660
|
+
args: z.record(z.string().min(1), z.unknown())
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
//#endregion
|
|
664
|
+
//#region src/schemas/tool-result.ts
|
|
665
|
+
const toolResultSchema = z.object({
|
|
666
|
+
id: z.string().min(1).max(255),
|
|
667
|
+
skillName: z.string().min(1).max(maxSkillNameLength),
|
|
668
|
+
toolName: z.string().min(1).max(maxSkillToolNameLength),
|
|
669
|
+
result: z.array(messagePartSchema)
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
//#endregion
|
|
673
|
+
//#region src/schemas/usage.ts
|
|
674
|
+
const usageSchema = z.object({
|
|
675
|
+
inputTokens: z.number(),
|
|
676
|
+
outputTokens: z.number(),
|
|
677
|
+
reasoningTokens: z.number(),
|
|
678
|
+
totalTokens: z.number(),
|
|
679
|
+
cachedInputTokens: z.number()
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/schemas/checkpoint.ts
|
|
684
|
+
const checkpointStatusSchema = z.enum([
|
|
685
|
+
"init",
|
|
686
|
+
"proceeding",
|
|
687
|
+
"completed",
|
|
688
|
+
"stoppedByInteractiveTool",
|
|
689
|
+
"stoppedByDelegate",
|
|
690
|
+
"stoppedByError",
|
|
691
|
+
"stoppedByCancellation"
|
|
692
|
+
]);
|
|
693
|
+
const delegationTargetSchema = z.object({
|
|
694
|
+
expert: z.object({
|
|
695
|
+
key: z.string(),
|
|
696
|
+
name: z.string(),
|
|
697
|
+
version: z.string()
|
|
698
|
+
}),
|
|
699
|
+
toolCallId: z.string(),
|
|
700
|
+
toolName: z.string(),
|
|
701
|
+
query: z.string()
|
|
702
|
+
});
|
|
703
|
+
const checkpointSchema = z.object({
|
|
704
|
+
id: z.string(),
|
|
705
|
+
jobId: z.string(),
|
|
706
|
+
runId: z.string(),
|
|
707
|
+
status: checkpointStatusSchema,
|
|
708
|
+
stepNumber: z.number(),
|
|
709
|
+
messages: z.array(messageSchema),
|
|
710
|
+
expert: z.object({
|
|
711
|
+
key: z.string(),
|
|
712
|
+
name: z.string(),
|
|
713
|
+
version: z.string()
|
|
714
|
+
}),
|
|
715
|
+
delegateTo: z.array(delegationTargetSchema).optional(),
|
|
716
|
+
delegatedBy: z.object({
|
|
717
|
+
expert: z.object({
|
|
718
|
+
key: z.string(),
|
|
719
|
+
name: z.string(),
|
|
720
|
+
version: z.string()
|
|
721
|
+
}),
|
|
722
|
+
toolCallId: z.string(),
|
|
723
|
+
toolName: z.string(),
|
|
724
|
+
checkpointId: z.string(),
|
|
725
|
+
runId: z.string()
|
|
726
|
+
}).optional(),
|
|
727
|
+
usage: usageSchema,
|
|
728
|
+
contextWindow: z.number().optional(),
|
|
729
|
+
contextWindowUsage: z.number().optional(),
|
|
730
|
+
pendingToolCalls: z.array(toolCallSchema).optional(),
|
|
731
|
+
partialToolResults: z.array(toolResultSchema).optional(),
|
|
732
|
+
metadata: z.object({}).passthrough().optional(),
|
|
733
|
+
error: z.object({
|
|
734
|
+
name: z.string(),
|
|
735
|
+
message: z.string(),
|
|
736
|
+
statusCode: z.number().optional(),
|
|
737
|
+
isRetryable: z.boolean()
|
|
738
|
+
}).optional(),
|
|
739
|
+
retryCount: z.number().optional()
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
//#endregion
|
|
743
|
+
//#region src/utils/expert-type.ts
|
|
744
|
+
function getExpertType(expertName) {
|
|
745
|
+
return expertName.startsWith("@") ? "delegate" : "coordinator";
|
|
746
|
+
}
|
|
747
|
+
function isCoordinatorExpert(expertName) {
|
|
748
|
+
return getExpertType(expertName) === "coordinator";
|
|
749
|
+
}
|
|
750
|
+
function isDelegateExpert(expertName) {
|
|
751
|
+
return getExpertType(expertName) === "delegate";
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Returns the scope of an expert.
|
|
755
|
+
* Handles both bare names and versioned keys (with @version/@tag suffix).
|
|
756
|
+
* - Coordinator "game-producer" -> "game-producer"
|
|
757
|
+
* - Coordinator "game-producer@1.0.0" -> "game-producer"
|
|
758
|
+
* - Delegate "@game-producer/designer" -> "game-producer"
|
|
759
|
+
* - Delegate "@game-producer/designer@1.0.0" -> "game-producer"
|
|
760
|
+
*/
|
|
761
|
+
function getExpertScope(expertName) {
|
|
762
|
+
if (isDelegateExpert(expertName)) {
|
|
763
|
+
const withoutAt = expertName.slice(1);
|
|
764
|
+
const slashIndex = withoutAt.indexOf("/");
|
|
765
|
+
return slashIndex === -1 ? withoutAt : withoutAt.slice(0, slashIndex);
|
|
766
|
+
}
|
|
767
|
+
const atIndex = expertName.indexOf("@");
|
|
768
|
+
return atIndex === -1 ? expertName : expertName.slice(0, atIndex);
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Returns the short name of an expert.
|
|
772
|
+
* Handles both bare names and versioned keys (with @version/@tag suffix).
|
|
773
|
+
* - Coordinator "game-producer" -> "game-producer"
|
|
774
|
+
* - Coordinator "game-producer@1.0.0" -> "game-producer"
|
|
775
|
+
* - Delegate "@game-producer/designer" -> "designer"
|
|
776
|
+
* - Delegate "@game-producer/designer@1.0.0" -> "designer"
|
|
777
|
+
*/
|
|
778
|
+
function getExpertShortName(expertName) {
|
|
779
|
+
if (isDelegateExpert(expertName)) {
|
|
780
|
+
const slashIndex = expertName.indexOf("/");
|
|
781
|
+
const shortName = slashIndex === -1 ? expertName : expertName.slice(slashIndex + 1);
|
|
782
|
+
const atIndex = shortName.indexOf("@");
|
|
783
|
+
return atIndex === -1 ? shortName : shortName.slice(0, atIndex);
|
|
784
|
+
}
|
|
785
|
+
const atIndex = expertName.indexOf("@");
|
|
786
|
+
return atIndex === -1 ? expertName : expertName.slice(0, atIndex);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Validates whether a delegation from source to target is allowed.
|
|
790
|
+
* Returns null if valid, an error message string if invalid.
|
|
791
|
+
*
|
|
792
|
+
* Rules:
|
|
793
|
+
* - No self-delegation
|
|
794
|
+
* - If target is a delegate (@scope/name), source must be in the same scope
|
|
795
|
+
* - A delegate cannot delegate to its own coordinator
|
|
796
|
+
*/
|
|
797
|
+
function validateDelegation(source, target) {
|
|
798
|
+
if (source === target) return `Expert "${source}" cannot delegate to itself`;
|
|
799
|
+
const sourceScope = getExpertScope(source);
|
|
800
|
+
if (isDelegateExpert(target)) {
|
|
801
|
+
if (sourceScope !== getExpertScope(target)) return `Expert "${source}" cannot delegate to out-of-scope delegate "${target}"`;
|
|
802
|
+
}
|
|
803
|
+
if (isDelegateExpert(source) && isCoordinatorExpert(target) && getExpertScope(target) === sourceScope) return `Delegate "${source}" cannot delegate to its own coordinator "${target}"`;
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Validates all delegations for an expert.
|
|
808
|
+
* Returns an array of error messages (empty if all valid).
|
|
809
|
+
*/
|
|
810
|
+
function validateAllDelegations(expertName, delegates) {
|
|
811
|
+
const errors = [];
|
|
812
|
+
for (const delegate of delegates) {
|
|
813
|
+
const error = validateDelegation(expertName, delegate);
|
|
814
|
+
if (error) errors.push(error);
|
|
815
|
+
}
|
|
816
|
+
return errors;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region src/schemas/provider-tools.ts
|
|
821
|
+
const anthropicProviderToolNameSchema = z.enum([
|
|
822
|
+
"webSearch",
|
|
823
|
+
"webFetch",
|
|
824
|
+
"codeExecution"
|
|
825
|
+
]);
|
|
826
|
+
const builtinAnthropicSkillSchema = z.object({
|
|
827
|
+
type: z.literal("builtin"),
|
|
828
|
+
skillId: z.enum([
|
|
829
|
+
"pdf",
|
|
830
|
+
"docx",
|
|
831
|
+
"pptx",
|
|
832
|
+
"xlsx"
|
|
833
|
+
])
|
|
834
|
+
});
|
|
835
|
+
const customAnthropicSkillSchema = z.object({
|
|
836
|
+
type: z.literal("custom"),
|
|
837
|
+
name: z.string().min(1),
|
|
838
|
+
definition: z.string().min(1)
|
|
839
|
+
});
|
|
840
|
+
const anthropicProviderSkillSchema = z.discriminatedUnion("type", [builtinAnthropicSkillSchema, customAnthropicSkillSchema]);
|
|
841
|
+
const openaiProviderToolNameSchema = z.enum([
|
|
842
|
+
"webSearch",
|
|
843
|
+
"fileSearch",
|
|
844
|
+
"codeInterpreter",
|
|
845
|
+
"imageGeneration"
|
|
846
|
+
]);
|
|
847
|
+
const googleProviderToolNameSchema = z.enum([
|
|
848
|
+
"googleSearch",
|
|
849
|
+
"codeExecution",
|
|
850
|
+
"urlContext",
|
|
851
|
+
"fileSearch",
|
|
852
|
+
"googleMaps"
|
|
853
|
+
]);
|
|
854
|
+
const azureOpenAIProviderToolNameSchema = z.enum([
|
|
855
|
+
"webSearchPreview",
|
|
856
|
+
"fileSearch",
|
|
857
|
+
"codeInterpreter",
|
|
858
|
+
"imageGeneration"
|
|
859
|
+
]);
|
|
860
|
+
const vertexProviderToolNameSchema = z.enum([
|
|
861
|
+
"codeExecution",
|
|
862
|
+
"urlContext",
|
|
863
|
+
"googleSearch",
|
|
864
|
+
"enterpriseWebSearch",
|
|
865
|
+
"googleMaps"
|
|
866
|
+
]);
|
|
867
|
+
const webSearchOptionsSchema = z.object({
|
|
868
|
+
maxUses: z.number().int().positive().optional(),
|
|
869
|
+
allowedDomains: z.array(z.string()).optional()
|
|
870
|
+
});
|
|
871
|
+
const webFetchOptionsSchema = z.object({ maxUses: z.number().int().positive().optional() });
|
|
872
|
+
const fileSearchOptionsSchema = z.object({
|
|
873
|
+
vectorStoreIds: z.array(z.string()).optional(),
|
|
874
|
+
maxNumResults: z.number().int().positive().optional()
|
|
875
|
+
});
|
|
876
|
+
const providerToolOptionsSchema = z.object({
|
|
877
|
+
webSearch: webSearchOptionsSchema.optional(),
|
|
878
|
+
webFetch: webFetchOptionsSchema.optional(),
|
|
879
|
+
fileSearch: fileSearchOptionsSchema.optional()
|
|
880
|
+
}).optional();
|
|
881
|
+
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region src/schemas/runtime-version.ts
|
|
884
|
+
const runtimeVersionSchema = z.string().regex(/^v\d+\.\d+$/, "Runtime version must be in format \"vX.Y\" (e.g., \"v1.0\")").transform((v) => v);
|
|
885
|
+
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region src/schemas/skill.ts
|
|
888
|
+
function isPrivateOrLocalIP(hostname) {
|
|
889
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "0.0.0.0") return true;
|
|
890
|
+
const ipv4Match = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
|
|
891
|
+
if (ipv4Match) {
|
|
892
|
+
const a = Number(ipv4Match[1]);
|
|
893
|
+
const b = Number(ipv4Match[2]);
|
|
894
|
+
if (a === 10) return true;
|
|
895
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
896
|
+
if (a === 192 && b === 168) return true;
|
|
897
|
+
if (a === 169 && b === 254) return true;
|
|
898
|
+
if (a === 127) return true;
|
|
899
|
+
}
|
|
900
|
+
if (hostname.includes(":")) {
|
|
901
|
+
if (hostname.startsWith("fe80:") || hostname.startsWith("fc") || hostname.startsWith("fd")) return true;
|
|
902
|
+
}
|
|
903
|
+
if (hostname.startsWith("::ffff:")) {
|
|
904
|
+
if (isPrivateOrLocalIP(hostname.slice(7))) return true;
|
|
905
|
+
}
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
const sseEndpointSchema$1 = z.string().url().refine((url) => {
|
|
909
|
+
try {
|
|
910
|
+
const parsed = new URL(url);
|
|
911
|
+
if (parsed.protocol !== "https:") return false;
|
|
912
|
+
if (isPrivateOrLocalIP(parsed.hostname)) return false;
|
|
913
|
+
return true;
|
|
914
|
+
} catch {
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
}, { message: "Endpoint must be a public HTTPS URL" });
|
|
918
|
+
const mcpStdioSkillSchema = z.object({
|
|
919
|
+
type: z.literal("mcpStdioSkill"),
|
|
920
|
+
name: z.string(),
|
|
921
|
+
description: z.string().optional(),
|
|
922
|
+
rule: z.string().optional(),
|
|
923
|
+
pick: z.array(z.string()).optional().default([]),
|
|
924
|
+
omit: z.array(z.string()).optional().default([]),
|
|
925
|
+
command: z.string(),
|
|
926
|
+
packageName: z.string().optional(),
|
|
927
|
+
args: z.array(z.string()).optional().default([]),
|
|
928
|
+
requiredEnv: z.array(z.string()).optional().default([])
|
|
929
|
+
});
|
|
930
|
+
const mcpSseSkillSchema = z.object({
|
|
931
|
+
type: z.literal("mcpSseSkill"),
|
|
932
|
+
name: z.string(),
|
|
933
|
+
description: z.string().optional(),
|
|
934
|
+
rule: z.string().optional(),
|
|
935
|
+
pick: z.array(z.string()).optional().default([]),
|
|
936
|
+
omit: z.array(z.string()).optional().default([]),
|
|
937
|
+
endpoint: sseEndpointSchema$1
|
|
938
|
+
});
|
|
939
|
+
const interactiveToolSchema = z.object({
|
|
940
|
+
name: z.string(),
|
|
941
|
+
description: z.string().optional(),
|
|
942
|
+
inputJsonSchema: z.string()
|
|
943
|
+
});
|
|
944
|
+
const interactiveSkillSchema = z.object({
|
|
945
|
+
type: z.literal("interactiveSkill"),
|
|
946
|
+
name: z.string(),
|
|
947
|
+
description: z.string().optional(),
|
|
948
|
+
rule: z.string().optional(),
|
|
949
|
+
tools: z.record(z.string(), interactiveToolSchema.omit({ name: true })).transform((tools) => {
|
|
950
|
+
return Object.fromEntries(Object.entries(tools).map(([key, toolWithoutName]) => [key, interactiveToolSchema.parse({
|
|
951
|
+
...toolWithoutName,
|
|
952
|
+
name: key
|
|
953
|
+
})]));
|
|
954
|
+
})
|
|
955
|
+
});
|
|
956
|
+
const skillSchema = z.discriminatedUnion("type", [
|
|
957
|
+
mcpStdioSkillSchema,
|
|
958
|
+
mcpSseSkillSchema,
|
|
959
|
+
interactiveSkillSchema
|
|
960
|
+
]);
|
|
961
|
+
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region src/schemas/expert.ts
|
|
964
|
+
/**
|
|
965
|
+
* Base object schema for Expert. Use this for `.omit()` / `.pick()` operations.
|
|
966
|
+
* For parsing with delegation validation, use `expertSchema` instead.
|
|
967
|
+
*/
|
|
968
|
+
const expertBaseSchema = z.object({
|
|
969
|
+
key: z.string().regex(expertKeyRegex).min(1),
|
|
970
|
+
name: z.string().regex(expertNameRegex).min(1).max(maxExpertNameLength),
|
|
971
|
+
version: z.string().regex(expertVersionRegex),
|
|
972
|
+
description: z.string().max(1024 * 2).optional(),
|
|
973
|
+
instruction: z.string().min(1).max(1024 * 20),
|
|
974
|
+
skills: z.record(z.string(), z.discriminatedUnion("type", [
|
|
975
|
+
mcpStdioSkillSchema.omit({ name: true }),
|
|
976
|
+
mcpSseSkillSchema.omit({ name: true }),
|
|
977
|
+
interactiveSkillSchema.omit({ name: true })
|
|
978
|
+
])).optional().default({ "@perstack/base": {
|
|
979
|
+
type: "mcpStdioSkill",
|
|
980
|
+
description: "Base skill",
|
|
981
|
+
command: "npx",
|
|
982
|
+
args: ["-y", "@perstack/base"],
|
|
983
|
+
pick: [],
|
|
984
|
+
omit: [],
|
|
985
|
+
requiredEnv: []
|
|
986
|
+
} }).transform((skills) => {
|
|
987
|
+
return Object.fromEntries(Object.entries(skills).map(([key, skillWithoutName]) => [key, z.discriminatedUnion("type", [
|
|
988
|
+
mcpStdioSkillSchema,
|
|
989
|
+
mcpSseSkillSchema,
|
|
990
|
+
interactiveSkillSchema
|
|
991
|
+
]).parse({
|
|
992
|
+
...skillWithoutName,
|
|
993
|
+
name: key
|
|
994
|
+
})]));
|
|
995
|
+
}),
|
|
996
|
+
delegates: z.array(z.string().regex(expertKeyRegex).min(1)).optional().default([]),
|
|
997
|
+
tags: z.array(z.string().regex(tagNameRegex).min(1)).optional().default([]),
|
|
998
|
+
minRuntimeVersion: runtimeVersionSchema.default("v1.0"),
|
|
999
|
+
providerTools: z.array(z.string()).optional(),
|
|
1000
|
+
providerSkills: z.array(anthropicProviderSkillSchema).optional(),
|
|
1001
|
+
providerToolOptions: providerToolOptionsSchema
|
|
1002
|
+
});
|
|
1003
|
+
/**
|
|
1004
|
+
* Expert schema with delegation rule validation.
|
|
1005
|
+
* Rejects self-delegation, out-of-scope delegates, and delegate-to-own-coordinator.
|
|
1006
|
+
*/
|
|
1007
|
+
const expertSchema = expertBaseSchema.superRefine((data, ctx) => {
|
|
1008
|
+
const errors = validateAllDelegations(data.key, data.delegates);
|
|
1009
|
+
for (const error of errors) ctx.addIssue({
|
|
1010
|
+
code: z.ZodIssueCode.custom,
|
|
1011
|
+
message: error,
|
|
1012
|
+
path: ["delegates"]
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
//#endregion
|
|
1017
|
+
//#region src/schemas/job.ts
|
|
1018
|
+
const jobStatusSchema = z.enum([
|
|
1019
|
+
"running",
|
|
1020
|
+
"completed",
|
|
1021
|
+
"stoppedByInteractiveTool",
|
|
1022
|
+
"stoppedByError",
|
|
1023
|
+
"stoppedByCancellation"
|
|
1024
|
+
]);
|
|
1025
|
+
const jobSchema = z.object({
|
|
1026
|
+
id: z.string(),
|
|
1027
|
+
status: jobStatusSchema,
|
|
1028
|
+
coordinatorExpertKey: z.string(),
|
|
1029
|
+
runtimeVersion: runtimeVersionSchema,
|
|
1030
|
+
totalSteps: z.number(),
|
|
1031
|
+
usage: usageSchema,
|
|
1032
|
+
startedAt: z.number(),
|
|
1033
|
+
finishedAt: z.number().optional()
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region src/schemas/lockfile.ts
|
|
1038
|
+
const lockfileToolDefinitionSchema = z.object({
|
|
1039
|
+
skillName: z.string(),
|
|
1040
|
+
name: z.string(),
|
|
1041
|
+
description: z.string().optional(),
|
|
1042
|
+
inputSchema: z.record(z.string(), z.unknown())
|
|
1043
|
+
});
|
|
1044
|
+
const lockfileExpertSchema = z.object({
|
|
1045
|
+
key: z.string(),
|
|
1046
|
+
name: z.string(),
|
|
1047
|
+
version: z.string(),
|
|
1048
|
+
description: z.string().optional(),
|
|
1049
|
+
instruction: z.string(),
|
|
1050
|
+
skills: z.record(z.string(), skillSchema),
|
|
1051
|
+
delegates: z.array(z.string()),
|
|
1052
|
+
tags: z.array(z.string()),
|
|
1053
|
+
toolDefinitions: z.array(lockfileToolDefinitionSchema)
|
|
1054
|
+
});
|
|
1055
|
+
const lockfileSchema = z.object({
|
|
1056
|
+
version: z.literal("1"),
|
|
1057
|
+
generatedAt: z.number(),
|
|
1058
|
+
configPath: z.string(),
|
|
1059
|
+
experts: z.record(z.string(), lockfileExpertSchema)
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
//#endregion
|
|
1063
|
+
//#region src/schemas/provider-config.ts
|
|
1064
|
+
const headersSchema = z.record(z.string(), z.string()).optional();
|
|
1065
|
+
const providerNameSchema = z.enum([
|
|
1066
|
+
"anthropic",
|
|
1067
|
+
"google",
|
|
1068
|
+
"openai",
|
|
1069
|
+
"ollama",
|
|
1070
|
+
"azure-openai",
|
|
1071
|
+
"amazon-bedrock",
|
|
1072
|
+
"google-vertex",
|
|
1073
|
+
"deepseek"
|
|
1074
|
+
]);
|
|
1075
|
+
const anthropicProviderConfigSchema = z.object({
|
|
1076
|
+
providerName: z.literal(providerNameSchema.enum.anthropic),
|
|
1077
|
+
apiKey: z.string(),
|
|
1078
|
+
baseUrl: z.string().optional(),
|
|
1079
|
+
headers: headersSchema
|
|
1080
|
+
});
|
|
1081
|
+
const googleGenerativeAiProviderConfigSchema = z.object({
|
|
1082
|
+
providerName: z.literal(providerNameSchema.enum.google),
|
|
1083
|
+
apiKey: z.string(),
|
|
1084
|
+
baseUrl: z.string().optional(),
|
|
1085
|
+
headers: headersSchema
|
|
1086
|
+
});
|
|
1087
|
+
const openAiProviderConfigSchema = z.object({
|
|
1088
|
+
providerName: z.literal(providerNameSchema.enum.openai),
|
|
1089
|
+
apiKey: z.string(),
|
|
1090
|
+
baseUrl: z.string().optional(),
|
|
1091
|
+
organization: z.string().optional(),
|
|
1092
|
+
project: z.string().optional(),
|
|
1093
|
+
name: z.string().optional(),
|
|
1094
|
+
headers: headersSchema
|
|
1095
|
+
});
|
|
1096
|
+
const ollamaProviderConfigSchema = z.object({
|
|
1097
|
+
providerName: z.literal(providerNameSchema.enum.ollama),
|
|
1098
|
+
baseUrl: z.string().optional(),
|
|
1099
|
+
headers: headersSchema
|
|
1100
|
+
});
|
|
1101
|
+
const azureOpenAiProviderConfigSchema = z.object({
|
|
1102
|
+
providerName: z.literal(providerNameSchema.enum["azure-openai"]),
|
|
1103
|
+
apiKey: z.string(),
|
|
1104
|
+
resourceName: z.string().optional(),
|
|
1105
|
+
apiVersion: z.string().optional(),
|
|
1106
|
+
baseUrl: z.string().optional(),
|
|
1107
|
+
headers: headersSchema,
|
|
1108
|
+
useDeploymentBasedUrls: z.boolean().optional()
|
|
1109
|
+
});
|
|
1110
|
+
const amazonBedrockProviderConfigSchema = z.object({
|
|
1111
|
+
providerName: z.literal(providerNameSchema.enum["amazon-bedrock"]),
|
|
1112
|
+
accessKeyId: z.string(),
|
|
1113
|
+
secretAccessKey: z.string(),
|
|
1114
|
+
region: z.string(),
|
|
1115
|
+
sessionToken: z.string().optional()
|
|
1116
|
+
});
|
|
1117
|
+
const googleVertexProviderConfigSchema = z.object({
|
|
1118
|
+
providerName: z.literal(providerNameSchema.enum["google-vertex"]),
|
|
1119
|
+
project: z.string().optional(),
|
|
1120
|
+
location: z.string().optional(),
|
|
1121
|
+
baseUrl: z.string().optional(),
|
|
1122
|
+
headers: headersSchema
|
|
1123
|
+
});
|
|
1124
|
+
const deepseekProviderConfigSchema = z.object({
|
|
1125
|
+
providerName: z.literal(providerNameSchema.enum.deepseek),
|
|
1126
|
+
apiKey: z.string(),
|
|
1127
|
+
baseUrl: z.string().optional(),
|
|
1128
|
+
headers: headersSchema
|
|
1129
|
+
});
|
|
1130
|
+
const providerConfigSchema = z.discriminatedUnion("providerName", [
|
|
1131
|
+
anthropicProviderConfigSchema,
|
|
1132
|
+
googleGenerativeAiProviderConfigSchema,
|
|
1133
|
+
openAiProviderConfigSchema,
|
|
1134
|
+
ollamaProviderConfigSchema,
|
|
1135
|
+
azureOpenAiProviderConfigSchema,
|
|
1136
|
+
amazonBedrockProviderConfigSchema,
|
|
1137
|
+
googleVertexProviderConfigSchema,
|
|
1138
|
+
deepseekProviderConfigSchema
|
|
1139
|
+
]);
|
|
1140
|
+
|
|
1141
|
+
//#endregion
|
|
1142
|
+
//#region src/schemas/perstack-toml.ts
|
|
1143
|
+
/** Default reasoning budget - enables extended thinking by default */
|
|
1144
|
+
const defaultReasoningBudget = "low";
|
|
1145
|
+
const reasoningBudgetSchema = z.union([z.enum([
|
|
1146
|
+
"none",
|
|
1147
|
+
"minimal",
|
|
1148
|
+
"low",
|
|
1149
|
+
"medium",
|
|
1150
|
+
"high"
|
|
1151
|
+
]), z.number().int().nonnegative()]);
|
|
1152
|
+
const domainPatternRegex = /^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
|
1153
|
+
const punycodeRegex = /(?:^|\.)(xn--)/i;
|
|
1154
|
+
const domainPatternSchema = z.string().regex(domainPatternRegex, { message: "Invalid domain pattern. Use exact domain (example.com) or wildcard prefix (*.example.com)" }).refine((domain) => !punycodeRegex.test(domain), { message: "Punycode domains (xn--) are not allowed to prevent homograph attacks. Use ASCII domains only." });
|
|
1155
|
+
const sseEndpointSchema = z.string().url().refine((url) => {
|
|
1156
|
+
try {
|
|
1157
|
+
const parsed = new URL(url);
|
|
1158
|
+
if (parsed.protocol !== "https:") return false;
|
|
1159
|
+
if (isPrivateOrLocalIP(parsed.hostname)) return false;
|
|
1160
|
+
return true;
|
|
1161
|
+
} catch {
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
}, { message: "SSE endpoint must be a public HTTPS URL" });
|
|
1165
|
+
const httpsUrlSchema = z.string().url().refine((url) => url.startsWith("https://"), { message: "URL must use HTTPS" });
|
|
1166
|
+
const anthropicSettingSchema = z.object({
|
|
1167
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1168
|
+
headers: headersSchema
|
|
1169
|
+
});
|
|
1170
|
+
const googleSettingSchema = z.object({
|
|
1171
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1172
|
+
headers: headersSchema
|
|
1173
|
+
});
|
|
1174
|
+
const openAiSettingSchema = z.object({
|
|
1175
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1176
|
+
organization: z.string().optional(),
|
|
1177
|
+
project: z.string().optional(),
|
|
1178
|
+
name: z.string().optional(),
|
|
1179
|
+
headers: headersSchema
|
|
1180
|
+
});
|
|
1181
|
+
const ollamaSettingSchema = z.object({
|
|
1182
|
+
baseUrl: z.string().optional(),
|
|
1183
|
+
headers: headersSchema
|
|
1184
|
+
});
|
|
1185
|
+
const azureOpenAiSettingSchema = z.object({
|
|
1186
|
+
resourceName: z.string().optional(),
|
|
1187
|
+
apiVersion: z.string().optional(),
|
|
1188
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1189
|
+
headers: headersSchema,
|
|
1190
|
+
useDeploymentBasedUrls: z.boolean().optional()
|
|
1191
|
+
});
|
|
1192
|
+
const amazonBedrockSettingSchema = z.object({ region: z.string().optional() });
|
|
1193
|
+
const googleVertexSettingSchema = z.object({
|
|
1194
|
+
project: z.string().optional(),
|
|
1195
|
+
location: z.string().optional(),
|
|
1196
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1197
|
+
headers: headersSchema
|
|
1198
|
+
});
|
|
1199
|
+
const deepseekSettingSchema = z.object({
|
|
1200
|
+
baseUrl: httpsUrlSchema.optional(),
|
|
1201
|
+
headers: headersSchema
|
|
1202
|
+
});
|
|
1203
|
+
const providerTableSchema = z.discriminatedUnion("providerName", [
|
|
1204
|
+
z.object({
|
|
1205
|
+
providerName: z.literal("anthropic"),
|
|
1206
|
+
setting: anthropicSettingSchema.optional()
|
|
1207
|
+
}),
|
|
1208
|
+
z.object({
|
|
1209
|
+
providerName: z.literal("google"),
|
|
1210
|
+
setting: googleSettingSchema.optional()
|
|
1211
|
+
}),
|
|
1212
|
+
z.object({
|
|
1213
|
+
providerName: z.literal("openai"),
|
|
1214
|
+
setting: openAiSettingSchema.optional()
|
|
1215
|
+
}),
|
|
1216
|
+
z.object({
|
|
1217
|
+
providerName: z.literal("ollama"),
|
|
1218
|
+
setting: ollamaSettingSchema.optional()
|
|
1219
|
+
}),
|
|
1220
|
+
z.object({
|
|
1221
|
+
providerName: z.literal("azure-openai"),
|
|
1222
|
+
setting: azureOpenAiSettingSchema.optional()
|
|
1223
|
+
}),
|
|
1224
|
+
z.object({
|
|
1225
|
+
providerName: z.literal("amazon-bedrock"),
|
|
1226
|
+
setting: amazonBedrockSettingSchema.optional()
|
|
1227
|
+
}),
|
|
1228
|
+
z.object({
|
|
1229
|
+
providerName: z.literal("google-vertex"),
|
|
1230
|
+
setting: googleVertexSettingSchema.optional()
|
|
1231
|
+
}),
|
|
1232
|
+
z.object({
|
|
1233
|
+
providerName: z.literal("deepseek"),
|
|
1234
|
+
setting: deepseekSettingSchema.optional()
|
|
1235
|
+
})
|
|
1236
|
+
]);
|
|
1237
|
+
const perstackConfigSchema = z.object({
|
|
1238
|
+
provider: providerTableSchema.optional(),
|
|
1239
|
+
model: z.string().optional(),
|
|
1240
|
+
reasoningBudget: reasoningBudgetSchema.optional(),
|
|
1241
|
+
maxRetries: z.number().optional(),
|
|
1242
|
+
timeout: z.number().optional(),
|
|
1243
|
+
experts: z.record(z.string(), z.object({
|
|
1244
|
+
version: z.string().optional(),
|
|
1245
|
+
minRuntimeVersion: runtimeVersionSchema.optional(),
|
|
1246
|
+
description: z.string().optional(),
|
|
1247
|
+
instruction: z.string(),
|
|
1248
|
+
skills: z.record(z.string(), z.discriminatedUnion("type", [
|
|
1249
|
+
z.object({
|
|
1250
|
+
type: z.literal("mcpStdioSkill"),
|
|
1251
|
+
description: z.string().optional(),
|
|
1252
|
+
rule: z.string().optional(),
|
|
1253
|
+
pick: z.array(z.string()).optional(),
|
|
1254
|
+
omit: z.array(z.string()).optional(),
|
|
1255
|
+
command: z.string(),
|
|
1256
|
+
packageName: z.string().optional(),
|
|
1257
|
+
args: z.array(z.string()).optional(),
|
|
1258
|
+
requiredEnv: z.array(z.string()).optional(),
|
|
1259
|
+
allowedDomains: z.array(domainPatternSchema).optional()
|
|
1260
|
+
}),
|
|
1261
|
+
z.object({
|
|
1262
|
+
type: z.literal("mcpSseSkill"),
|
|
1263
|
+
description: z.string().optional(),
|
|
1264
|
+
rule: z.string().optional(),
|
|
1265
|
+
pick: z.array(z.string()).optional(),
|
|
1266
|
+
omit: z.array(z.string()).optional(),
|
|
1267
|
+
endpoint: sseEndpointSchema,
|
|
1268
|
+
allowedDomains: z.array(domainPatternSchema).optional()
|
|
1269
|
+
}),
|
|
1270
|
+
z.object({
|
|
1271
|
+
type: z.literal("interactiveSkill"),
|
|
1272
|
+
description: z.string().optional(),
|
|
1273
|
+
rule: z.string().optional(),
|
|
1274
|
+
tools: z.record(z.string(), z.object({
|
|
1275
|
+
description: z.string().optional(),
|
|
1276
|
+
inputJsonSchema: z.string()
|
|
1277
|
+
}))
|
|
1278
|
+
})
|
|
1279
|
+
])).optional(),
|
|
1280
|
+
delegates: z.array(z.string()).optional(),
|
|
1281
|
+
tags: z.array(z.string()).optional(),
|
|
1282
|
+
providerTools: z.array(z.string()).optional(),
|
|
1283
|
+
providerSkills: z.array(anthropicProviderSkillSchema).optional(),
|
|
1284
|
+
providerToolOptions: providerToolOptionsSchema
|
|
1285
|
+
})).optional(),
|
|
1286
|
+
perstackApiBaseUrl: z.url().refine((url) => url.startsWith("https://"), { message: "perstackApiBaseUrl must use HTTPS" }).optional(),
|
|
1287
|
+
perstackBaseSkillCommand: z.array(z.string()).optional(),
|
|
1288
|
+
envPath: z.array(z.string()).optional()
|
|
1289
|
+
});
|
|
1290
|
+
|
|
1291
|
+
//#endregion
|
|
1292
|
+
//#region src/schemas/run-command.ts
|
|
1293
|
+
const commandOptionsSchema = z.object({
|
|
1294
|
+
config: z.string().optional(),
|
|
1295
|
+
provider: providerNameSchema.optional(),
|
|
1296
|
+
model: z.string().optional(),
|
|
1297
|
+
reasoningBudget: z.string().optional().transform((value) => {
|
|
1298
|
+
if (value === void 0) return void 0;
|
|
1299
|
+
if ([
|
|
1300
|
+
"none",
|
|
1301
|
+
"minimal",
|
|
1302
|
+
"low",
|
|
1303
|
+
"medium",
|
|
1304
|
+
"high"
|
|
1305
|
+
].includes(value)) return value;
|
|
1306
|
+
const parsedValue = Number.parseInt(value, 10);
|
|
1307
|
+
if (Number.isNaN(parsedValue)) return void 0;
|
|
1308
|
+
return parsedValue;
|
|
1309
|
+
}).pipe(reasoningBudgetSchema.optional()),
|
|
1310
|
+
maxRetries: z.string().optional().transform((value) => {
|
|
1311
|
+
if (value === void 0) return void 0;
|
|
1312
|
+
const parsedValue = Number.parseInt(value, 10);
|
|
1313
|
+
if (Number.isNaN(parsedValue)) return void 0;
|
|
1314
|
+
return parsedValue;
|
|
1315
|
+
}),
|
|
1316
|
+
timeout: z.string().optional().transform((value) => {
|
|
1317
|
+
if (value === void 0) return void 0;
|
|
1318
|
+
const parsedValue = Number.parseInt(value, 10);
|
|
1319
|
+
if (Number.isNaN(parsedValue)) return void 0;
|
|
1320
|
+
return parsedValue;
|
|
1321
|
+
}),
|
|
1322
|
+
jobId: z.string().optional(),
|
|
1323
|
+
runId: z.string().optional(),
|
|
1324
|
+
envPath: z.array(z.string()).optional().transform((value) => value && value.length > 0 ? value : void 0),
|
|
1325
|
+
verbose: z.boolean().optional(),
|
|
1326
|
+
continue: z.boolean().optional(),
|
|
1327
|
+
continueJob: z.string().optional(),
|
|
1328
|
+
resumeFrom: z.string().optional(),
|
|
1329
|
+
interactiveToolCallResult: z.boolean().optional(),
|
|
1330
|
+
filter: z.string().optional().transform((value) => {
|
|
1331
|
+
if (value === void 0) return void 0;
|
|
1332
|
+
return value.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1333
|
+
}).pipe(z.array(z.string()).optional())
|
|
1334
|
+
});
|
|
1335
|
+
const runCommandInputSchema = z.object({
|
|
1336
|
+
expertKey: z.string(),
|
|
1337
|
+
query: z.string(),
|
|
1338
|
+
options: commandOptionsSchema
|
|
1339
|
+
});
|
|
1340
|
+
const startCommandInputSchema = z.object({
|
|
1341
|
+
expertKey: z.string().optional(),
|
|
1342
|
+
query: z.string().optional(),
|
|
1343
|
+
options: commandOptionsSchema
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
//#endregion
|
|
1347
|
+
//#region src/schemas/runtime.ts
|
|
1348
|
+
/** Parse an expert key into its components */
|
|
1349
|
+
function parseExpertKey(expertKey) {
|
|
1350
|
+
const match = expertKey.match(expertKeyRegex);
|
|
1351
|
+
if (!match) throw new PerstackError(`Invalid expert key format: ${expertKey}`);
|
|
1352
|
+
const [key, name, version, tag] = match;
|
|
1353
|
+
if (!name) throw new PerstackError(`Invalid expert key format: ${expertKey}`);
|
|
1354
|
+
return {
|
|
1355
|
+
key,
|
|
1356
|
+
name,
|
|
1357
|
+
version,
|
|
1358
|
+
tag
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
const runSettingSchema = z.object({
|
|
1362
|
+
model: z.string(),
|
|
1363
|
+
providerConfig: providerConfigSchema,
|
|
1364
|
+
jobId: z.string(),
|
|
1365
|
+
runId: z.string(),
|
|
1366
|
+
expertKey: z.string().min(1).regex(expertKeyRegex),
|
|
1367
|
+
input: z.object({
|
|
1368
|
+
text: z.string().optional(),
|
|
1369
|
+
interactiveToolCallResult: z.object({
|
|
1370
|
+
toolCallId: z.string(),
|
|
1371
|
+
toolName: z.string(),
|
|
1372
|
+
skillName: z.string(),
|
|
1373
|
+
text: z.string()
|
|
1374
|
+
}).optional()
|
|
1375
|
+
}),
|
|
1376
|
+
experts: z.record(z.string(), expertSchema),
|
|
1377
|
+
reasoningBudget: reasoningBudgetSchema.default(defaultReasoningBudget),
|
|
1378
|
+
maxRetries: z.number().min(0),
|
|
1379
|
+
timeout: z.number().min(0),
|
|
1380
|
+
startedAt: z.number(),
|
|
1381
|
+
updatedAt: z.number(),
|
|
1382
|
+
perstackApiBaseUrl: z.string().url(),
|
|
1383
|
+
perstackApiKey: z.string().optional(),
|
|
1384
|
+
perstackBaseSkillCommand: z.array(z.string()).optional(),
|
|
1385
|
+
env: z.record(z.string(), z.string()),
|
|
1386
|
+
proxyUrl: z.string().optional(),
|
|
1387
|
+
verbose: z.boolean().optional()
|
|
1388
|
+
});
|
|
1389
|
+
const runParamsSchema = z.object({
|
|
1390
|
+
setting: z.object({
|
|
1391
|
+
model: z.string(),
|
|
1392
|
+
providerConfig: providerConfigSchema,
|
|
1393
|
+
jobId: z.string().optional().default(() => createId()),
|
|
1394
|
+
runId: z.string().optional().default(() => createId()),
|
|
1395
|
+
expertKey: z.string().min(1).regex(expertKeyRegex),
|
|
1396
|
+
input: z.object({
|
|
1397
|
+
text: z.string().optional(),
|
|
1398
|
+
interactiveToolCallResult: z.object({
|
|
1399
|
+
toolCallId: z.string(),
|
|
1400
|
+
toolName: z.string(),
|
|
1401
|
+
skillName: z.string(),
|
|
1402
|
+
text: z.string()
|
|
1403
|
+
}).optional()
|
|
1404
|
+
}),
|
|
1405
|
+
experts: z.record(z.string().min(1).regex(expertKeyRegex), expertBaseSchema.omit({ key: true })).optional().default({}).transform((experts) => Object.fromEntries(Object.entries(experts).map(([key, expertWithoutKey]) => [key, expertSchema.parse({
|
|
1406
|
+
...expertWithoutKey,
|
|
1407
|
+
key
|
|
1408
|
+
})]))),
|
|
1409
|
+
reasoningBudget: reasoningBudgetSchema.optional().default(defaultReasoningBudget),
|
|
1410
|
+
maxRetries: z.number().min(0).optional().default(defaultMaxRetries),
|
|
1411
|
+
timeout: z.number().min(0).optional().default(defaultTimeout),
|
|
1412
|
+
startedAt: z.number().optional().default(Date.now()),
|
|
1413
|
+
updatedAt: z.number().optional().default(Date.now()),
|
|
1414
|
+
perstackApiBaseUrl: z.url().optional().default(defaultPerstackApiBaseUrl),
|
|
1415
|
+
perstackApiKey: z.string().optional(),
|
|
1416
|
+
perstackBaseSkillCommand: z.array(z.string()).optional(),
|
|
1417
|
+
env: z.record(z.string(), z.string()).optional().default({}),
|
|
1418
|
+
proxyUrl: z.string().optional(),
|
|
1419
|
+
verbose: z.boolean().optional()
|
|
1420
|
+
}),
|
|
1421
|
+
checkpoint: checkpointSchema.optional()
|
|
1422
|
+
});
|
|
1423
|
+
/** Factory function to create expert state events */
|
|
1424
|
+
function createEvent(type) {
|
|
1425
|
+
return (setting, checkpoint, data) => {
|
|
1426
|
+
return {
|
|
1427
|
+
type,
|
|
1428
|
+
id: createId(),
|
|
1429
|
+
expertKey: checkpoint.expert.key,
|
|
1430
|
+
timestamp: Date.now(),
|
|
1431
|
+
jobId: setting.jobId,
|
|
1432
|
+
runId: setting.runId,
|
|
1433
|
+
stepNumber: checkpoint.stepNumber,
|
|
1434
|
+
...data
|
|
1435
|
+
};
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
/** Factory function to create streaming events */
|
|
1439
|
+
function createStreamingEvent(type, setting, checkpoint, data) {
|
|
1440
|
+
return {
|
|
1441
|
+
type,
|
|
1442
|
+
id: createId(),
|
|
1443
|
+
expertKey: checkpoint.expert.key,
|
|
1444
|
+
timestamp: Date.now(),
|
|
1445
|
+
jobId: setting.jobId,
|
|
1446
|
+
runId: setting.runId,
|
|
1447
|
+
stepNumber: checkpoint.stepNumber,
|
|
1448
|
+
...data
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
const startRun = createEvent("startRun");
|
|
1452
|
+
const resumeFromStop = createEvent("resumeFromStop");
|
|
1453
|
+
const proceedToInteractiveTools = createEvent("proceedToInteractiveTools");
|
|
1454
|
+
const startGeneration = createEvent("startGeneration");
|
|
1455
|
+
const retry = createEvent("retry");
|
|
1456
|
+
const callTools = createEvent("callTools");
|
|
1457
|
+
const finishMcpTools = createEvent("finishMcpTools");
|
|
1458
|
+
const skipDelegates = createEvent("skipDelegates");
|
|
1459
|
+
const resolveToolResults = createEvent("resolveToolResults");
|
|
1460
|
+
const finishToolCall = createEvent("finishToolCall");
|
|
1461
|
+
const resumeToolCalls = createEvent("resumeToolCalls");
|
|
1462
|
+
const completeRun = createEvent("completeRun");
|
|
1463
|
+
const stopRunByInteractiveTool = createEvent("stopRunByInteractiveTool");
|
|
1464
|
+
const stopRunByDelegate = createEvent("stopRunByDelegate");
|
|
1465
|
+
const stopRunByError = createEvent("stopRunByError");
|
|
1466
|
+
const continueToNextStep = createEvent("continueToNextStep");
|
|
1467
|
+
/** Factory function to create runtime events */
|
|
1468
|
+
function createRuntimeEvent(type, jobId, runId, data) {
|
|
1469
|
+
return {
|
|
1470
|
+
type,
|
|
1471
|
+
id: createId(),
|
|
1472
|
+
timestamp: Date.now(),
|
|
1473
|
+
jobId,
|
|
1474
|
+
runId,
|
|
1475
|
+
...data
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Valid expert state event types (state machine transitions)
|
|
1480
|
+
*/
|
|
1481
|
+
const EXPERT_STATE_EVENT_TYPES = new Set([
|
|
1482
|
+
"startRun",
|
|
1483
|
+
"resumeFromStop",
|
|
1484
|
+
"proceedToInteractiveTools",
|
|
1485
|
+
"startGeneration",
|
|
1486
|
+
"retry",
|
|
1487
|
+
"callTools",
|
|
1488
|
+
"finishMcpTools",
|
|
1489
|
+
"skipDelegates",
|
|
1490
|
+
"resolveToolResults",
|
|
1491
|
+
"finishToolCall",
|
|
1492
|
+
"resumeToolCalls",
|
|
1493
|
+
"continueToNextStep",
|
|
1494
|
+
"stopRunByInteractiveTool",
|
|
1495
|
+
"stopRunByDelegate",
|
|
1496
|
+
"stopRunByError",
|
|
1497
|
+
"completeRun"
|
|
1498
|
+
]);
|
|
1499
|
+
/**
|
|
1500
|
+
* Valid streaming event types
|
|
1501
|
+
*/
|
|
1502
|
+
const STREAMING_EVENT_TYPES = new Set([
|
|
1503
|
+
"startStreamingReasoning",
|
|
1504
|
+
"streamReasoning",
|
|
1505
|
+
"completeStreamingReasoning",
|
|
1506
|
+
"startStreamingRunResult",
|
|
1507
|
+
"streamRunResult",
|
|
1508
|
+
"completeStreamingRunResult"
|
|
1509
|
+
]);
|
|
1510
|
+
/**
|
|
1511
|
+
* Valid runtime event types (infrastructure-level events)
|
|
1512
|
+
*/
|
|
1513
|
+
const RUNTIME_EVENT_TYPES = new Set([
|
|
1514
|
+
"initializeRuntime",
|
|
1515
|
+
"skillStarting",
|
|
1516
|
+
"skillConnected",
|
|
1517
|
+
"skillStderr",
|
|
1518
|
+
"skillDisconnected"
|
|
1519
|
+
]);
|
|
1520
|
+
/** Validate if a string is a valid RunEvent type (ExpertStateEvent or StreamingEvent) */
|
|
1521
|
+
function isValidEventType(type) {
|
|
1522
|
+
return EXPERT_STATE_EVENT_TYPES.has(type) || STREAMING_EVENT_TYPES.has(type);
|
|
1523
|
+
}
|
|
1524
|
+
/** Validate if a string is a valid RuntimeEvent type */
|
|
1525
|
+
function isValidRuntimeEventType(type) {
|
|
1526
|
+
return RUNTIME_EVENT_TYPES.has(type);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
//#endregion
|
|
1530
|
+
//#region src/schemas/step.ts
|
|
1531
|
+
const stepSchema = z.object({
|
|
1532
|
+
stepNumber: z.number(),
|
|
1533
|
+
inputMessages: z.array(z.union([
|
|
1534
|
+
instructionMessageSchema,
|
|
1535
|
+
userMessageSchema,
|
|
1536
|
+
toolMessageSchema
|
|
1537
|
+
])).optional(),
|
|
1538
|
+
newMessages: z.array(messageSchema),
|
|
1539
|
+
toolCalls: z.array(toolCallSchema).optional(),
|
|
1540
|
+
toolResults: z.array(toolResultSchema).optional(),
|
|
1541
|
+
pendingToolCalls: z.array(toolCallSchema).optional(),
|
|
1542
|
+
partialToolResults: z.array(toolResultSchema).optional(),
|
|
1543
|
+
usage: usageSchema,
|
|
1544
|
+
startedAt: z.number(),
|
|
1545
|
+
finishedAt: z.number().optional()
|
|
1546
|
+
});
|
|
1547
|
+
|
|
1548
|
+
//#endregion
|
|
1549
|
+
//#region src/utils/activity.ts
|
|
1550
|
+
const BASE_SKILL_PREFIX = "@perstack/base";
|
|
1551
|
+
/**
|
|
1552
|
+
* Extracts reasoning from Step.newMessages by finding thinkingParts.
|
|
1553
|
+
*/
|
|
1554
|
+
function extractReasoning(newMessages) {
|
|
1555
|
+
const thinkingParts = [];
|
|
1556
|
+
for (const message of newMessages) for (const content of message.contents) if (content.type === "thinkingPart") thinkingParts.push(content);
|
|
1557
|
+
if (thinkingParts.length === 0) return void 0;
|
|
1558
|
+
return thinkingParts.map((p) => p.thinking).join("\n\n");
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Wraps multiple activities into a ParallelActivitiesGroup when they share reasoning.
|
|
1562
|
+
* Single activities are returned as-is.
|
|
1563
|
+
*/
|
|
1564
|
+
function wrapInGroupIfParallel(activities, reasoning, expertKey, runId, stepNumber) {
|
|
1565
|
+
if (activities.length <= 1) return activities;
|
|
1566
|
+
const activitiesWithoutReasoning = activities.map((a) => {
|
|
1567
|
+
const { reasoning: _, ...rest } = a;
|
|
1568
|
+
return rest;
|
|
1569
|
+
});
|
|
1570
|
+
return [{
|
|
1571
|
+
type: "parallelGroup",
|
|
1572
|
+
id: `parallel-${runId}-step${stepNumber}`,
|
|
1573
|
+
expertKey,
|
|
1574
|
+
runId,
|
|
1575
|
+
reasoning,
|
|
1576
|
+
activities: activitiesWithoutReasoning
|
|
1577
|
+
}];
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Computes activities from a checkpoint and step.
|
|
1581
|
+
* Returns an array of activities or activity groups, supporting parallel tool calls and delegations.
|
|
1582
|
+
* When multiple activities are produced from a single step, they are wrapped in a ParallelActivitiesGroup
|
|
1583
|
+
* with shared reasoning.
|
|
1584
|
+
*/
|
|
1585
|
+
function getActivities(params) {
|
|
1586
|
+
const { checkpoint, step } = params;
|
|
1587
|
+
const { status, delegateTo, runId, stepNumber } = checkpoint;
|
|
1588
|
+
const expertKey = checkpoint.expert.key;
|
|
1589
|
+
const reasoning = extractReasoning(step.newMessages);
|
|
1590
|
+
let queryActivity;
|
|
1591
|
+
if (stepNumber === 1 && step.inputMessages) {
|
|
1592
|
+
const userMessage = step.inputMessages.find((m) => m.type === "userMessage");
|
|
1593
|
+
if (userMessage) {
|
|
1594
|
+
const textPart = userMessage.contents.find((c) => c.type === "textPart");
|
|
1595
|
+
if (textPart && "text" in textPart) queryActivity = {
|
|
1596
|
+
type: "query",
|
|
1597
|
+
id: "",
|
|
1598
|
+
expertKey,
|
|
1599
|
+
runId,
|
|
1600
|
+
text: textPart.text
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
const prependQuery = (result) => queryActivity ? [queryActivity, ...result] : result;
|
|
1605
|
+
if (status === "stoppedByError") return prependQuery([createErrorActivity(checkpoint, reasoning)]);
|
|
1606
|
+
if (status === "stoppedByDelegate") {
|
|
1607
|
+
if (!delegateTo || delegateTo.length === 0) return prependQuery([createRetryActivity(step.newMessages, reasoning, "Delegate status but no delegation targets")]);
|
|
1608
|
+
return prependQuery(wrapInGroupIfParallel(delegateTo.map((d) => createDelegateActivity(d, reasoning)), reasoning, expertKey, runId, stepNumber));
|
|
1609
|
+
}
|
|
1610
|
+
if (status === "stoppedByInteractiveTool") {
|
|
1611
|
+
const toolCalls = step.toolCalls ?? [];
|
|
1612
|
+
if (toolCalls.length === 0) return prependQuery([createRetryActivity(step.newMessages, reasoning)]);
|
|
1613
|
+
return prependQuery(wrapInGroupIfParallel(toolCalls.map((tc) => createInteractiveToolActivity(tc.skillName, tc.toolName, tc, reasoning)), reasoning, expertKey, runId, stepNumber));
|
|
1614
|
+
}
|
|
1615
|
+
const toolCalls = step.toolCalls ?? [];
|
|
1616
|
+
const toolResults = step.toolResults ?? [];
|
|
1617
|
+
if (toolCalls.length === 0) {
|
|
1618
|
+
if (status === "completed") return prependQuery([createCompleteActivity(step.newMessages, reasoning)]);
|
|
1619
|
+
if (status === "proceeding" || status === "init") return prependQuery([]);
|
|
1620
|
+
return prependQuery([createRetryActivity(step.newMessages, reasoning)]);
|
|
1621
|
+
}
|
|
1622
|
+
const toolActivities = [];
|
|
1623
|
+
for (const toolCall of toolCalls) {
|
|
1624
|
+
const toolResult = toolResults.find((tr) => tr.id === toolCall.id);
|
|
1625
|
+
if (!toolResult) continue;
|
|
1626
|
+
const { skillName, toolName } = toolCall;
|
|
1627
|
+
if (skillName.startsWith(BASE_SKILL_PREFIX)) toolActivities.push(createBaseToolActivity(toolName, toolCall, toolResult, reasoning));
|
|
1628
|
+
else toolActivities.push(createGeneralToolActivity(skillName, toolName, toolCall, toolResult, reasoning));
|
|
1629
|
+
}
|
|
1630
|
+
if (toolActivities.length === 0) {
|
|
1631
|
+
if (status === "completed") return prependQuery([createCompleteActivity(step.newMessages, reasoning)]);
|
|
1632
|
+
if (status === "proceeding" || status === "init") return prependQuery([]);
|
|
1633
|
+
return prependQuery([createRetryActivity(step.newMessages, reasoning)]);
|
|
1634
|
+
}
|
|
1635
|
+
const result = wrapInGroupIfParallel(toolActivities, reasoning, expertKey, runId, stepNumber);
|
|
1636
|
+
if (status === "completed") result.push(createCompleteActivity(step.newMessages, void 0));
|
|
1637
|
+
return prependQuery(result);
|
|
1638
|
+
}
|
|
1639
|
+
function createCompleteActivity(newMessages, reasoning) {
|
|
1640
|
+
return {
|
|
1641
|
+
type: "complete",
|
|
1642
|
+
id: "",
|
|
1643
|
+
expertKey: "",
|
|
1644
|
+
runId: "",
|
|
1645
|
+
reasoning,
|
|
1646
|
+
text: ([...newMessages].reverse().find((m) => m.type === "expertMessage")?.contents.find((c) => c.type === "textPart"))?.text ?? ""
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
function createDelegateActivity(delegate, reasoning) {
|
|
1650
|
+
return {
|
|
1651
|
+
type: "delegate",
|
|
1652
|
+
id: "",
|
|
1653
|
+
expertKey: "",
|
|
1654
|
+
runId: "",
|
|
1655
|
+
reasoning,
|
|
1656
|
+
delegateExpertKey: delegate.expert.key,
|
|
1657
|
+
query: delegate.query
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function createInteractiveToolActivity(skillName, toolName, toolCall, reasoning) {
|
|
1661
|
+
return {
|
|
1662
|
+
type: "interactiveTool",
|
|
1663
|
+
id: "",
|
|
1664
|
+
expertKey: "",
|
|
1665
|
+
runId: "",
|
|
1666
|
+
reasoning,
|
|
1667
|
+
skillName,
|
|
1668
|
+
toolName,
|
|
1669
|
+
args: toolCall.args
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
function createRetryActivity(newMessages, reasoning, customError) {
|
|
1673
|
+
const textPart = newMessages[newMessages.length - 1]?.contents.find((c) => c.type === "textPart");
|
|
1674
|
+
return {
|
|
1675
|
+
type: "retry",
|
|
1676
|
+
id: "",
|
|
1677
|
+
expertKey: "",
|
|
1678
|
+
runId: "",
|
|
1679
|
+
reasoning,
|
|
1680
|
+
error: customError ?? "No tool call or result found",
|
|
1681
|
+
message: textPart?.text ?? ""
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function createErrorActivity(checkpoint, reasoning) {
|
|
1685
|
+
const error = checkpoint.error;
|
|
1686
|
+
return {
|
|
1687
|
+
type: "error",
|
|
1688
|
+
id: "",
|
|
1689
|
+
expertKey: "",
|
|
1690
|
+
runId: "",
|
|
1691
|
+
reasoning,
|
|
1692
|
+
error: error?.message ?? "Unknown error",
|
|
1693
|
+
errorName: error?.name,
|
|
1694
|
+
isRetryable: error?.isRetryable
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
function createBaseToolActivity(toolName, toolCall, toolResult, reasoning) {
|
|
1698
|
+
const args = toolCall.args;
|
|
1699
|
+
const resultContents = toolResult.result;
|
|
1700
|
+
const errorText = getErrorFromResult(resultContents);
|
|
1701
|
+
const baseFields = {
|
|
1702
|
+
id: "",
|
|
1703
|
+
expertKey: "",
|
|
1704
|
+
runId: "",
|
|
1705
|
+
reasoning
|
|
1706
|
+
};
|
|
1707
|
+
switch (toolName) {
|
|
1708
|
+
case "attemptCompletion": {
|
|
1709
|
+
const remainingTodos = parseRemainingTodosFromResult(resultContents);
|
|
1710
|
+
return {
|
|
1711
|
+
type: "attemptCompletion",
|
|
1712
|
+
...baseFields,
|
|
1713
|
+
remainingTodos,
|
|
1714
|
+
error: errorText
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
case "todo": {
|
|
1718
|
+
const todos = parseTodosFromResult(resultContents);
|
|
1719
|
+
return {
|
|
1720
|
+
type: "todo",
|
|
1721
|
+
...baseFields,
|
|
1722
|
+
newTodos: Array.isArray(args["newTodos"]) ? args["newTodos"].map(String) : void 0,
|
|
1723
|
+
completedTodos: Array.isArray(args["completedTodos"]) ? args["completedTodos"].map(Number) : void 0,
|
|
1724
|
+
todos,
|
|
1725
|
+
error: errorText
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
case "clearTodo": return {
|
|
1729
|
+
type: "clearTodo",
|
|
1730
|
+
...baseFields,
|
|
1731
|
+
error: errorText
|
|
1732
|
+
};
|
|
1733
|
+
case "readImageFile": return {
|
|
1734
|
+
type: "readImageFile",
|
|
1735
|
+
...baseFields,
|
|
1736
|
+
path: String(args["path"] ?? ""),
|
|
1737
|
+
mimeType: parseStringField(resultContents, "mimeType"),
|
|
1738
|
+
size: parseNumberField(resultContents, "size"),
|
|
1739
|
+
error: errorText
|
|
1740
|
+
};
|
|
1741
|
+
case "readPdfFile": return {
|
|
1742
|
+
type: "readPdfFile",
|
|
1743
|
+
...baseFields,
|
|
1744
|
+
path: String(args["path"] ?? ""),
|
|
1745
|
+
mimeType: parseStringField(resultContents, "mimeType"),
|
|
1746
|
+
size: parseNumberField(resultContents, "size"),
|
|
1747
|
+
error: errorText
|
|
1748
|
+
};
|
|
1749
|
+
case "readTextFile": return {
|
|
1750
|
+
type: "readTextFile",
|
|
1751
|
+
...baseFields,
|
|
1752
|
+
path: String(args["path"] ?? ""),
|
|
1753
|
+
content: parseStringField(resultContents, "content"),
|
|
1754
|
+
from: typeof args["from"] === "number" ? args["from"] : void 0,
|
|
1755
|
+
to: typeof args["to"] === "number" ? args["to"] : void 0,
|
|
1756
|
+
error: errorText
|
|
1757
|
+
};
|
|
1758
|
+
case "editTextFile": return {
|
|
1759
|
+
type: "editTextFile",
|
|
1760
|
+
...baseFields,
|
|
1761
|
+
path: String(args["path"] ?? ""),
|
|
1762
|
+
newText: String(args["newText"] ?? ""),
|
|
1763
|
+
oldText: String(args["oldText"] ?? ""),
|
|
1764
|
+
error: errorText
|
|
1765
|
+
};
|
|
1766
|
+
case "writeTextFile": return {
|
|
1767
|
+
type: "writeTextFile",
|
|
1768
|
+
...baseFields,
|
|
1769
|
+
path: String(args["path"] ?? ""),
|
|
1770
|
+
text: String(args["text"] ?? ""),
|
|
1771
|
+
error: errorText
|
|
1772
|
+
};
|
|
1773
|
+
case "exec": return {
|
|
1774
|
+
type: "exec",
|
|
1775
|
+
...baseFields,
|
|
1776
|
+
command: String(args["command"] ?? ""),
|
|
1777
|
+
args: Array.isArray(args["args"]) ? args["args"].map(String) : [],
|
|
1778
|
+
cwd: String(args["cwd"] ?? ""),
|
|
1779
|
+
output: parseStringField(resultContents, "output"),
|
|
1780
|
+
error: errorText,
|
|
1781
|
+
stdout: parseStringField(resultContents, "stdout"),
|
|
1782
|
+
stderr: parseStringField(resultContents, "stderr")
|
|
1783
|
+
};
|
|
1784
|
+
case "addSkill": return {
|
|
1785
|
+
type: "addSkill",
|
|
1786
|
+
...baseFields,
|
|
1787
|
+
name: String(args["name"] ?? ""),
|
|
1788
|
+
skillType: String(args["type"] ?? ""),
|
|
1789
|
+
tools: parseStringArrayField(resultContents, "tools"),
|
|
1790
|
+
error: errorText
|
|
1791
|
+
};
|
|
1792
|
+
case "removeSkill": return {
|
|
1793
|
+
type: "removeSkill",
|
|
1794
|
+
...baseFields,
|
|
1795
|
+
skillName: String(args["skillName"] ?? ""),
|
|
1796
|
+
error: errorText
|
|
1797
|
+
};
|
|
1798
|
+
case "addDelegate": return {
|
|
1799
|
+
type: "addDelegate",
|
|
1800
|
+
...baseFields,
|
|
1801
|
+
targetExpertKey: String(args["expertKey"] ?? ""),
|
|
1802
|
+
delegateToolName: parseStringField(resultContents, "delegateToolName"),
|
|
1803
|
+
error: errorText
|
|
1804
|
+
};
|
|
1805
|
+
case "removeDelegate": return {
|
|
1806
|
+
type: "removeDelegate",
|
|
1807
|
+
...baseFields,
|
|
1808
|
+
expertName: String(args["expertName"] ?? ""),
|
|
1809
|
+
error: errorText
|
|
1810
|
+
};
|
|
1811
|
+
case "createExpert": return {
|
|
1812
|
+
type: "createExpert",
|
|
1813
|
+
...baseFields,
|
|
1814
|
+
targetKey: String(args["key"] ?? ""),
|
|
1815
|
+
description: typeof args["description"] === "string" ? args["description"] : void 0,
|
|
1816
|
+
resultExpertKey: parseStringField(resultContents, "expertKey"),
|
|
1817
|
+
error: errorText
|
|
1818
|
+
};
|
|
1819
|
+
default: return createGeneralToolActivity(toolCall.skillName, toolName, toolCall, toolResult, reasoning);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
function createGeneralToolActivity(skillName, toolName, toolCall, toolResult, reasoning) {
|
|
1823
|
+
const errorText = getErrorFromResult(toolResult.result);
|
|
1824
|
+
return {
|
|
1825
|
+
type: "generalTool",
|
|
1826
|
+
id: "",
|
|
1827
|
+
expertKey: "",
|
|
1828
|
+
runId: "",
|
|
1829
|
+
reasoning,
|
|
1830
|
+
skillName,
|
|
1831
|
+
toolName,
|
|
1832
|
+
args: toolCall.args,
|
|
1833
|
+
result: toolResult.result,
|
|
1834
|
+
error: errorText
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
function getErrorFromResult(result) {
|
|
1838
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1839
|
+
if (!textPart?.text) return void 0;
|
|
1840
|
+
try {
|
|
1841
|
+
const parsed = JSON.parse(textPart.text);
|
|
1842
|
+
if (typeof parsed.error === "string") return parsed.error;
|
|
1843
|
+
} catch {
|
|
1844
|
+
const trimmed = textPart.text.trim();
|
|
1845
|
+
if (trimmed.toLowerCase().startsWith("error:") || trimmed.toLowerCase().startsWith("error ")) return textPart.text;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
function parseStringField(result, field) {
|
|
1849
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1850
|
+
if (!textPart?.text) return void 0;
|
|
1851
|
+
try {
|
|
1852
|
+
const parsed = JSON.parse(textPart.text);
|
|
1853
|
+
return typeof parsed[field] === "string" ? parsed[field] : void 0;
|
|
1854
|
+
} catch {
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
function parseNumberField(result, field) {
|
|
1859
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1860
|
+
if (!textPart?.text) return void 0;
|
|
1861
|
+
try {
|
|
1862
|
+
const parsed = JSON.parse(textPart.text);
|
|
1863
|
+
return typeof parsed[field] === "number" ? parsed[field] : void 0;
|
|
1864
|
+
} catch {
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
function parseStringArrayField(result, field) {
|
|
1869
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1870
|
+
if (!textPart?.text) return void 0;
|
|
1871
|
+
try {
|
|
1872
|
+
const parsed = JSON.parse(textPart.text);
|
|
1873
|
+
if (Array.isArray(parsed[field])) return parsed[field].map(String);
|
|
1874
|
+
} catch {}
|
|
1875
|
+
}
|
|
1876
|
+
function parseRemainingTodosFromResult(result) {
|
|
1877
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1878
|
+
if (!textPart?.text) return void 0;
|
|
1879
|
+
try {
|
|
1880
|
+
const parsed = JSON.parse(textPart.text);
|
|
1881
|
+
if (Array.isArray(parsed.remainingTodos)) return parsed.remainingTodos.map((t, i) => ({
|
|
1882
|
+
id: typeof t.id === "number" ? t.id : i,
|
|
1883
|
+
title: typeof t.title === "string" ? t.title : "",
|
|
1884
|
+
completed: typeof t.completed === "boolean" ? t.completed : false
|
|
1885
|
+
}));
|
|
1886
|
+
} catch {}
|
|
1887
|
+
}
|
|
1888
|
+
function parseTodosFromResult(result) {
|
|
1889
|
+
const textPart = result.find((p) => p.type === "textPart");
|
|
1890
|
+
if (!textPart?.text) return [];
|
|
1891
|
+
try {
|
|
1892
|
+
const parsed = JSON.parse(textPart.text);
|
|
1893
|
+
if (Array.isArray(parsed.todos)) return parsed.todos.map((t, i) => ({
|
|
1894
|
+
id: typeof t.id === "number" ? t.id : i,
|
|
1895
|
+
title: typeof t.title === "string" ? t.title : "",
|
|
1896
|
+
completed: typeof t.completed === "boolean" ? t.completed : false
|
|
1897
|
+
}));
|
|
1898
|
+
} catch {}
|
|
1899
|
+
return [];
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/utils/env-filter.ts
|
|
1904
|
+
const SAFE_ENV_VARS = [
|
|
1905
|
+
"PATH",
|
|
1906
|
+
"HOME",
|
|
1907
|
+
"SHELL",
|
|
1908
|
+
"TERM",
|
|
1909
|
+
"NODE_PATH",
|
|
1910
|
+
"HTTP_PROXY",
|
|
1911
|
+
"HTTPS_PROXY",
|
|
1912
|
+
"http_proxy",
|
|
1913
|
+
"https_proxy",
|
|
1914
|
+
"NO_PROXY",
|
|
1915
|
+
"no_proxy",
|
|
1916
|
+
"PERSTACK_PROXY_URL",
|
|
1917
|
+
"NPM_CONFIG_PROXY",
|
|
1918
|
+
"NPM_CONFIG_HTTPS_PROXY"
|
|
1919
|
+
];
|
|
1920
|
+
const PROTECTED_ENV_VARS = new Set([
|
|
1921
|
+
"PATH",
|
|
1922
|
+
"HOME",
|
|
1923
|
+
"SHELL",
|
|
1924
|
+
"NODE_PATH",
|
|
1925
|
+
"LD_PRELOAD",
|
|
1926
|
+
"LD_LIBRARY_PATH",
|
|
1927
|
+
"DYLD_INSERT_LIBRARIES",
|
|
1928
|
+
"DYLD_LIBRARY_PATH",
|
|
1929
|
+
"NODE_OPTIONS",
|
|
1930
|
+
"PYTHONPATH",
|
|
1931
|
+
"PERL5LIB",
|
|
1932
|
+
"RUBYLIB"
|
|
1933
|
+
]);
|
|
1934
|
+
function getFilteredEnv(additional) {
|
|
1935
|
+
const filtered = {};
|
|
1936
|
+
for (const key of SAFE_ENV_VARS) if (process.env[key]) filtered[key] = process.env[key];
|
|
1937
|
+
if (additional) {
|
|
1938
|
+
for (const [key, value] of Object.entries(additional)) if (!PROTECTED_ENV_VARS.has(key.toUpperCase())) filtered[key] = value;
|
|
1939
|
+
}
|
|
1940
|
+
return filtered;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
//#endregion
|
|
1944
|
+
//#region src/utils/event-filter.ts
|
|
1945
|
+
/**
|
|
1946
|
+
* Validate and parse event filter option
|
|
1947
|
+
* @param filter - Array of event type strings to validate
|
|
1948
|
+
* @returns The validated filter array
|
|
1949
|
+
* @throws Error if any event type is invalid
|
|
1950
|
+
*/
|
|
1951
|
+
function validateEventFilter(filter) {
|
|
1952
|
+
const invalid = filter.filter((type) => !isValidEventType(type) && !isValidRuntimeEventType(type));
|
|
1953
|
+
if (invalid.length > 0) throw new PerstackError(`Invalid event type(s): ${invalid.join(", ")}. Valid event types are: startRun, completeRun, stopRunByError, callTools, etc. See documentation for full list.`);
|
|
1954
|
+
return filter;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Create a filtered event listener that only emits events of allowed types
|
|
1958
|
+
* @param listener - The original event listener to wrap
|
|
1959
|
+
* @param allowedTypes - Set of event types to allow through
|
|
1960
|
+
* @returns A filtered event listener
|
|
1961
|
+
*/
|
|
1962
|
+
function createFilteredEventListener(listener, allowedTypes) {
|
|
1963
|
+
return (event) => {
|
|
1964
|
+
if (allowedTypes.has(event.type)) listener(event);
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
//#endregion
|
|
1969
|
+
//#region src/utils/zod-error.ts
|
|
1970
|
+
function formatZodError(error) {
|
|
1971
|
+
return `Validation failed:\n${error.issues.map((issue) => {
|
|
1972
|
+
return ` - ${issue.path.length > 0 ? `${issue.path.join(".")}: ` : ""}${issue.message}`;
|
|
1973
|
+
}).join("\n")}`;
|
|
1974
|
+
}
|
|
1975
|
+
function parseWithFriendlyError(schema, data, context) {
|
|
1976
|
+
const result = schema.safeParse(data);
|
|
1977
|
+
if (result.success) return result.data;
|
|
1978
|
+
throw new PerstackError(`${context ? `${context}: ` : ""}${formatZodError(result.error)}`);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
//#endregion
|
|
1982
|
+
export { BASE_SKILL_PREFIX, PerstackError, SAFE_ENV_VARS, activityOrGroupSchema, activitySchema, addDelegateActivitySchema, addSkillActivitySchema, amazonBedrockProviderConfigSchema, anthropicProviderConfigSchema, anthropicProviderSkillSchema, anthropicProviderToolNameSchema, attemptCompletionActivitySchema, azureOpenAIProviderToolNameSchema, azureOpenAiProviderConfigSchema, basePartSchema, builtinAnthropicSkillSchema, callTools, checkpointSchema, checkpointStatusSchema, clearTodoActivitySchema, completeActivitySchema, completeRun, continueToNextStep, createBaseToolActivity, createCallToolsEvent, createCompleteRunEvent, createEmptyUsage, createEvent, createExpertActivitySchema, createFilteredEventListener, createGeneralToolActivity, createNormalizedCheckpoint, createResolveToolResultsEvent, createRuntimeEvent, createRuntimeInitEvent, createStartRunEvent, createStreamingEvent, createToolMessage, customAnthropicSkillSchema, deepseekProviderConfigSchema, defaultMaxRetries, defaultPerstackApiBaseUrl, defaultReasoningBudget, defaultTimeout, delegateActivitySchema, delegationCompleteActivitySchema, delegationTargetSchema, domainPatternSchema, editTextFileActivitySchema, errorActivitySchema, execActivitySchema, expertBaseSchema, expertKeyRegex, expertMessageSchema, expertNameRegex, expertSchema, expertVersionRegex, fileBinaryPartSchema, fileInlinePartSchema, fileSearchOptionsSchema, fileUrlPartSchema, finishMcpTools, finishToolCall, formatZodError, generalToolActivitySchema, getActivities, getExpertScope, getExpertShortName, getExpertType, getFilteredEnv, googleGenerativeAiProviderConfigSchema, googleProviderToolNameSchema, googleVertexProviderConfigSchema, headersSchema, imageBinaryPartSchema, imageInlinePartSchema, imageUrlPartSchema, instructionMessageSchema, interactiveSkillSchema, interactiveToolActivitySchema, interactiveToolSchema, isCoordinatorExpert, isDelegateExpert, isPrivateOrLocalIP, isValidEventType, isValidRuntimeEventType, jobSchema, jobStatusSchema, knownModels, lockfileExpertSchema, lockfileSchema, lockfileToolDefinitionSchema, maxExpertNameLength, maxSkillNameLength, maxSkillToolNameLength, mcpSseSkillSchema, mcpStdioSkillSchema, messagePartSchema, messageSchema, ollamaProviderConfigSchema, openAiProviderConfigSchema, openaiProviderToolNameSchema, parallelActivitiesGroupSchema, parseExpertKey, parseWithFriendlyError, perstackConfigSchema, proceedToInteractiveTools, providerConfigSchema, providerNameSchema, providerTableSchema, providerToolOptionsSchema, queryActivitySchema, readImageFileActivitySchema, readPdfFileActivitySchema, readTextFileActivitySchema, reasoningBudgetSchema, removeDelegateActivitySchema, removeSkillActivitySchema, resolveToolResults, resumeFromStop, resumeToolCalls, retry, retryActivitySchema, runCommandInputSchema, runParamsSchema, runSettingSchema, runtimeVersionSchema, skillSchema, skipDelegates, startCommandInputSchema, startGeneration, startRun, stepSchema, stopRunByDelegate, stopRunByError, stopRunByInteractiveTool, tagNameRegex, textPartSchema, thinkingPartSchema, todoActivitySchema, toolCallPartSchema, toolCallSchema, toolMessageSchema, toolResultPartSchema, toolResultSchema, usageSchema, userMessageSchema, validateAllDelegations, validateDelegation, validateEventFilter, vertexProviderToolNameSchema, webFetchOptionsSchema, webSearchOptionsSchema, writeTextFileActivitySchema };
|
|
29
1983
|
//# sourceMappingURL=index.js.map
|