@perstack/core 0.0.54 → 0.0.55

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.
Files changed (121) hide show
  1. package/dist/src/index.d.ts +6363 -28
  2. package/dist/src/index.js +1982 -28
  3. package/dist/src/index.js.map +1 -1
  4. package/package.json +2 -2
  5. package/dist/src/adapters/event-creators.d.ts +0 -26
  6. package/dist/src/adapters/event-creators.d.ts.map +0 -1
  7. package/dist/src/adapters/event-creators.js +0 -126
  8. package/dist/src/adapters/event-creators.js.map +0 -1
  9. package/dist/src/adapters/index.d.ts +0 -2
  10. package/dist/src/adapters/index.d.ts.map +0 -1
  11. package/dist/src/adapters/index.js +0 -2
  12. package/dist/src/adapters/index.js.map +0 -1
  13. package/dist/src/constants/constants.d.ts +0 -11
  14. package/dist/src/constants/constants.d.ts.map +0 -1
  15. package/dist/src/constants/constants.js +0 -13
  16. package/dist/src/constants/constants.js.map +0 -1
  17. package/dist/src/errors.d.ts +0 -4
  18. package/dist/src/errors.d.ts.map +0 -1
  19. package/dist/src/errors.js +0 -7
  20. package/dist/src/errors.js.map +0 -1
  21. package/dist/src/index.d.ts.map +0 -1
  22. package/dist/src/known-models/index.d.ts +0 -9
  23. package/dist/src/known-models/index.d.ts.map +0 -1
  24. package/dist/src/known-models/index.js +0 -216
  25. package/dist/src/known-models/index.js.map +0 -1
  26. package/dist/src/schemas/activity.d.ts +0 -2159
  27. package/dist/src/schemas/activity.d.ts.map +0 -1
  28. package/dist/src/schemas/activity.js +0 -209
  29. package/dist/src/schemas/activity.js.map +0 -1
  30. package/dist/src/schemas/checkpoint.d.ts +0 -338
  31. package/dist/src/schemas/checkpoint.d.ts.map +0 -1
  32. package/dist/src/schemas/checkpoint.js +0 -69
  33. package/dist/src/schemas/checkpoint.js.map +0 -1
  34. package/dist/src/schemas/expert.d.ts +0 -309
  35. package/dist/src/schemas/expert.d.ts.map +0 -1
  36. package/dist/src/schemas/expert.js +0 -70
  37. package/dist/src/schemas/expert.js.map +0 -1
  38. package/dist/src/schemas/job.d.ts +0 -44
  39. package/dist/src/schemas/job.d.ts.map +0 -1
  40. package/dist/src/schemas/job.js +0 -22
  41. package/dist/src/schemas/job.js.map +0 -1
  42. package/dist/src/schemas/lockfile.d.ts +0 -143
  43. package/dist/src/schemas/lockfile.d.ts.map +0 -1
  44. package/dist/src/schemas/lockfile.js +0 -26
  45. package/dist/src/schemas/lockfile.js.map +0 -1
  46. package/dist/src/schemas/message-part.d.ts +0 -239
  47. package/dist/src/schemas/message-part.d.ts.map +0 -1
  48. package/dist/src/schemas/message-part.js +0 -80
  49. package/dist/src/schemas/message-part.js.map +0 -1
  50. package/dist/src/schemas/message.d.ts +0 -236
  51. package/dist/src/schemas/message.d.ts.map +0 -1
  52. package/dist/src/schemas/message.js +0 -44
  53. package/dist/src/schemas/message.js.map +0 -1
  54. package/dist/src/schemas/perstack-toml.d.ts +0 -366
  55. package/dist/src/schemas/perstack-toml.d.ts.map +0 -1
  56. package/dist/src/schemas/perstack-toml.js +0 -175
  57. package/dist/src/schemas/perstack-toml.js.map +0 -1
  58. package/dist/src/schemas/provider-config.d.ts +0 -216
  59. package/dist/src/schemas/provider-config.d.ts.map +0 -1
  60. package/dist/src/schemas/provider-config.js +0 -86
  61. package/dist/src/schemas/provider-config.js.map +0 -1
  62. package/dist/src/schemas/provider-tools.d.ts +0 -93
  63. package/dist/src/schemas/provider-tools.d.ts.map +0 -1
  64. package/dist/src/schemas/provider-tools.js +0 -60
  65. package/dist/src/schemas/provider-tools.js.map +0 -1
  66. package/dist/src/schemas/run-command.d.ts +0 -127
  67. package/dist/src/schemas/run-command.d.ts.map +0 -1
  68. package/dist/src/schemas/run-command.js +0 -82
  69. package/dist/src/schemas/run-command.js.map +0 -1
  70. package/dist/src/schemas/runtime-version.d.ts +0 -4
  71. package/dist/src/schemas/runtime-version.d.ts.map +0 -1
  72. package/dist/src/schemas/runtime-version.js +0 -6
  73. package/dist/src/schemas/runtime-version.js.map +0 -1
  74. package/dist/src/schemas/runtime.d.ts +0 -1317
  75. package/dist/src/schemas/runtime.d.ts.map +0 -1
  76. package/dist/src/schemas/runtime.js +0 -205
  77. package/dist/src/schemas/runtime.js.map +0 -1
  78. package/dist/src/schemas/skill-manager.d.ts +0 -64
  79. package/dist/src/schemas/skill-manager.d.ts.map +0 -1
  80. package/dist/src/schemas/skill-manager.js +0 -2
  81. package/dist/src/schemas/skill-manager.js.map +0 -1
  82. package/dist/src/schemas/skill.d.ts +0 -147
  83. package/dist/src/schemas/skill.d.ts.map +0 -1
  84. package/dist/src/schemas/skill.js +0 -99
  85. package/dist/src/schemas/skill.js.map +0 -1
  86. package/dist/src/schemas/step.d.ts +0 -370
  87. package/dist/src/schemas/step.d.ts.map +0 -1
  88. package/dist/src/schemas/step.js +0 -21
  89. package/dist/src/schemas/step.js.map +0 -1
  90. package/dist/src/schemas/tool-call.d.ts +0 -19
  91. package/dist/src/schemas/tool-call.d.ts.map +0 -1
  92. package/dist/src/schemas/tool-call.js +0 -10
  93. package/dist/src/schemas/tool-call.js.map +0 -1
  94. package/dist/src/schemas/tool-result.d.ts +0 -86
  95. package/dist/src/schemas/tool-result.d.ts.map +0 -1
  96. package/dist/src/schemas/tool-result.js +0 -11
  97. package/dist/src/schemas/tool-result.js.map +0 -1
  98. package/dist/src/schemas/usage.d.ts +0 -22
  99. package/dist/src/schemas/usage.d.ts.map +0 -1
  100. package/dist/src/schemas/usage.js +0 -10
  101. package/dist/src/schemas/usage.js.map +0 -1
  102. package/dist/src/utils/activity.d.ts +0 -20
  103. package/dist/src/utils/activity.d.ts.map +0 -1
  104. package/dist/src/utils/activity.js +0 -449
  105. package/dist/src/utils/activity.js.map +0 -1
  106. package/dist/src/utils/env-filter.d.ts +0 -4
  107. package/dist/src/utils/env-filter.d.ts.map +0 -1
  108. package/dist/src/utils/env-filter.js +0 -50
  109. package/dist/src/utils/env-filter.js.map +0 -1
  110. package/dist/src/utils/event-filter.d.ts +0 -16
  111. package/dist/src/utils/event-filter.d.ts.map +0 -1
  112. package/dist/src/utils/event-filter.js +0 -31
  113. package/dist/src/utils/event-filter.js.map +0 -1
  114. package/dist/src/utils/expert-type.d.ts +0 -38
  115. package/dist/src/utils/expert-type.d.ts.map +0 -1
  116. package/dist/src/utils/expert-type.js +0 -88
  117. package/dist/src/utils/expert-type.js.map +0 -1
  118. package/dist/src/utils/zod-error.d.ts +0 -4
  119. package/dist/src/utils/zod-error.d.ts.map +0 -1
  120. package/dist/src/utils/zod-error.js +0 -17
  121. package/dist/src/utils/zod-error.js.map +0 -1
package/dist/src/index.js CHANGED
@@ -1,29 +1,1983 @@
1
- export * from "./adapters/index.js";
2
- export * from "./constants/constants.js";
3
- export * from "./errors.js";
4
- export * from "./known-models/index.js";
5
- export * from "./schemas/activity.js";
6
- export * from "./schemas/checkpoint.js";
7
- export * from "./schemas/expert.js";
8
- export * from "./schemas/job.js";
9
- export * from "./schemas/lockfile.js";
10
- export * from "./schemas/message.js";
11
- export * from "./schemas/message-part.js";
12
- export * from "./schemas/perstack-toml.js";
13
- export * from "./schemas/provider-config.js";
14
- export * from "./schemas/provider-tools.js";
15
- export * from "./schemas/run-command.js";
16
- export * from "./schemas/runtime.js";
17
- export * from "./schemas/runtime-version.js";
18
- export * from "./schemas/skill.js";
19
- export * from "./schemas/skill-manager.js";
20
- export * from "./schemas/step.js";
21
- export * from "./schemas/tool-call.js";
22
- export * from "./schemas/tool-result.js";
23
- export * from "./schemas/usage.js";
24
- export * from "./utils/activity.js";
25
- export * from "./utils/env-filter.js";
26
- export * from "./utils/event-filter.js";
27
- export * from "./utils/expert-type.js";
28
- export * from "./utils/zod-error.js";
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