@trailstep/core 0.1.0

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/index.js ADDED
@@ -0,0 +1,4654 @@
1
+ // src/contracts/failures/failure.ts
2
+ var TrailStepFailureError = class extends Error {
3
+ failure;
4
+ constructor(failure) {
5
+ super(failure.message);
6
+ this.name = "TrailStepFailureError";
7
+ this.failure = failure;
8
+ }
9
+ };
10
+ function validationFailure(message, details) {
11
+ return {
12
+ code: "validation_failed",
13
+ message,
14
+ ...details === void 0 ? {} : { details }
15
+ };
16
+ }
17
+
18
+ // src/agent-targeting/parse-trailstep-config/expand-agent-refs.ts
19
+ function expandAgentRefs(options) {
20
+ const expandedAgents = {};
21
+ const memo = /* @__PURE__ */ new Map();
22
+ for (const name of Object.keys(options.agents)) {
23
+ expandedAgents[name] = expandTopLevelAgent({
24
+ name,
25
+ agents: options.agents,
26
+ memo,
27
+ stack: [],
28
+ rootRefPath: void 0
29
+ });
30
+ }
31
+ const expandedWorkflows = options.workflows === void 0 ? void 0 : Object.fromEntries(
32
+ Object.entries(options.workflows).map(([workflowId, workflow]) => {
33
+ const workflowAgents = workflow.agents === void 0 ? void 0 : Object.fromEntries(
34
+ Object.entries(workflow.agents).map(([roleName, targets]) => [
35
+ roleName,
36
+ expandTargetArray({
37
+ path: `workflows.${workflowId}.agents.${roleName}`,
38
+ targets,
39
+ agents: options.agents,
40
+ memo,
41
+ stack: [],
42
+ rootRefPath: void 0
43
+ })
44
+ ])
45
+ );
46
+ return [
47
+ workflowId,
48
+ {
49
+ ...workflowAgents === void 0 ? {} : { agents: workflowAgents },
50
+ ...workflow.settings === void 0 ? {} : { settings: workflow.settings }
51
+ }
52
+ ];
53
+ })
54
+ );
55
+ return { agents: expandedAgents, workflows: expandedWorkflows };
56
+ }
57
+ function expandTopLevelAgent(options) {
58
+ const memoized = options.memo.get(options.name);
59
+ if (memoized !== void 0) {
60
+ return memoized;
61
+ }
62
+ const targets = options.agents[options.name];
63
+ if (targets === void 0) {
64
+ throwUnknownRef(`agents.${options.name}`, options.name);
65
+ }
66
+ const expanded = expandTargetArray({
67
+ path: `agents.${options.name}`,
68
+ targets,
69
+ agents: options.agents,
70
+ memo: options.memo,
71
+ stack: [...options.stack, options.name],
72
+ rootRefPath: options.rootRefPath
73
+ });
74
+ options.memo.set(options.name, expanded);
75
+ return expanded;
76
+ }
77
+ function expandTargetArray(options) {
78
+ return options.targets.flatMap((target, index) => {
79
+ if ("provider" in target) {
80
+ return [target];
81
+ }
82
+ const refPath = `${options.path}[${index}].ref`;
83
+ const referencedTargets = options.agents[target.ref];
84
+ if (referencedTargets === void 0) {
85
+ throwUnknownRef(refPath, target.ref);
86
+ }
87
+ const existingRefIndex = options.stack.indexOf(target.ref);
88
+ if (existingRefIndex !== -1) {
89
+ throwCycle(options.rootRefPath ?? refPath, [
90
+ ...options.stack.slice(existingRefIndex),
91
+ target.ref
92
+ ]);
93
+ }
94
+ const memoized = options.memo.get(target.ref);
95
+ if (memoized !== void 0) {
96
+ return memoized;
97
+ }
98
+ const expanded = expandTargetArray({
99
+ path: `agents.${target.ref}`,
100
+ targets: referencedTargets,
101
+ agents: options.agents,
102
+ memo: options.memo,
103
+ stack: [...options.stack, target.ref],
104
+ rootRefPath: options.rootRefPath ?? refPath
105
+ });
106
+ options.memo.set(target.ref, expanded);
107
+ return expanded;
108
+ });
109
+ }
110
+ function throwUnknownRef(path, ref) {
111
+ throw new TrailStepFailureError({
112
+ code: "agent_ref_unknown",
113
+ message: "One or more agent refs reference an unknown top-level agent.",
114
+ details: { diagnostics: [`${path} references unknown agent '${ref}'.`] }
115
+ });
116
+ }
117
+ function throwCycle(path, cycle) {
118
+ throw new TrailStepFailureError({
119
+ code: "agent_ref_cycle",
120
+ message: "One or more agent refs create a cycle.",
121
+ details: { diagnostics: [`${path} creates an agent ref cycle: ${cycle.join(" -> ")}.`] }
122
+ });
123
+ }
124
+
125
+ // src/agent-targeting/parse-trailstep-config/parse-utils.ts
126
+ function throwValidationFailure(diagnostics) {
127
+ throw new TrailStepFailureError(
128
+ validationFailure("Invalid .trailstep/config.json.", { diagnostics })
129
+ );
130
+ }
131
+ function isRecord(value) {
132
+ return typeof value === "object" && value !== null && !Array.isArray(value);
133
+ }
134
+ function parseOptionalStringArray(path, value, diagnostics) {
135
+ if (value === void 0) {
136
+ return void 0;
137
+ }
138
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
139
+ diagnostics.push(`${path} must be an array of strings when present.`);
140
+ return void 0;
141
+ }
142
+ return value;
143
+ }
144
+ function parseOptionalStringRecord(path, value, diagnostics) {
145
+ if (value === void 0) {
146
+ return void 0;
147
+ }
148
+ if (!isRecord(value)) {
149
+ diagnostics.push(`${path} must be an object when present.`);
150
+ return void 0;
151
+ }
152
+ const env = {};
153
+ for (const [key, envValue] of Object.entries(value)) {
154
+ if (typeof envValue !== "string") {
155
+ diagnostics.push(`${path}.${key} must be a string.`);
156
+ } else {
157
+ env[key] = envValue;
158
+ }
159
+ }
160
+ return env;
161
+ }
162
+
163
+ // src/agent-targeting/parse-trailstep-config/parse-agent-targets.ts
164
+ var THINKING_LEVELS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
165
+ function parseTargetArray(path, value, diagnostics) {
166
+ if (!Array.isArray(value)) {
167
+ diagnostics.push(`${path} must be an array.`);
168
+ return [];
169
+ }
170
+ return value.flatMap((target, index) => {
171
+ const targetPath = `${path}[${index}]`;
172
+ if (!isRecord(target)) {
173
+ diagnostics.push(`${targetPath} must be an object.`);
174
+ return [];
175
+ }
176
+ if (target.ref !== void 0) {
177
+ if (typeof target.ref !== "string" || target.ref.length === 0) {
178
+ diagnostics.push(`${targetPath}.ref must be a non-empty string when present.`);
179
+ return [];
180
+ }
181
+ return [{ ref: target.ref }];
182
+ }
183
+ if (typeof target.provider !== "string" || target.provider.length === 0) {
184
+ diagnostics.push(`${targetPath}.provider must be a non-empty string.`);
185
+ return [];
186
+ }
187
+ if (target.model !== void 0 && typeof target.model !== "string") {
188
+ diagnostics.push(`${targetPath}.model must be a string when present.`);
189
+ }
190
+ if (target.thinking !== void 0 && !THINKING_LEVELS.has(target.thinking)) {
191
+ diagnostics.push(
192
+ `${targetPath}.thinking must be one of low, medium, high, xhigh, or max when present.`
193
+ );
194
+ }
195
+ const args = parseOptionalStringArray(`${targetPath}.args`, target.args, diagnostics);
196
+ return [
197
+ {
198
+ provider: target.provider,
199
+ ...typeof target.model === "string" ? { model: target.model } : {},
200
+ ...typeof target.thinking === "string" && THINKING_LEVELS.has(target.thinking) ? { thinking: target.thinking } : {},
201
+ ...args === void 0 ? {} : { args }
202
+ }
203
+ ];
204
+ });
205
+ }
206
+
207
+ // src/agent-targeting/parse-trailstep-config/parse-agent-mappings.ts
208
+ function parseAgentMappings(path, value, diagnostics) {
209
+ const mappings = {};
210
+ if (!isRecord(value)) {
211
+ diagnostics.push(`${path} must be an object.`);
212
+ return mappings;
213
+ }
214
+ for (const [name, entry] of Object.entries(value)) {
215
+ mappings[name] = parseTargetArray(`${path}.${name}`, entry, diagnostics);
216
+ }
217
+ return mappings;
218
+ }
219
+
220
+ // src/agent-targeting/parse-trailstep-config/parse-custom-providers.ts
221
+ function parseCustomProviders(value, diagnostics) {
222
+ const customProviders = {};
223
+ if (!isRecord(value)) {
224
+ diagnostics.push("customProviders must be an object.");
225
+ return customProviders;
226
+ }
227
+ for (const [name, providerConfig] of Object.entries(value)) {
228
+ if (!isRecord(providerConfig)) {
229
+ diagnostics.push(`customProviders.${name} must be an object.`);
230
+ continue;
231
+ }
232
+ if (typeof providerConfig.binary !== "string" || providerConfig.binary.length === 0) {
233
+ diagnostics.push(`customProviders.${name}.binary must be a non-empty string.`);
234
+ continue;
235
+ }
236
+ const args = parseOptionalStringArray(
237
+ `customProviders.${name}.args`,
238
+ providerConfig.args,
239
+ diagnostics
240
+ );
241
+ const interactiveArgs = parseOptionalStringArray(
242
+ `customProviders.${name}.interactiveArgs`,
243
+ providerConfig.interactiveArgs,
244
+ diagnostics
245
+ );
246
+ const env = parseOptionalStringRecord(
247
+ `customProviders.${name}.env`,
248
+ providerConfig.env,
249
+ diagnostics
250
+ );
251
+ if (providerConfig.cwd !== void 0 && typeof providerConfig.cwd !== "string") {
252
+ diagnostics.push(`customProviders.${name}.cwd must be a string when present.`);
253
+ }
254
+ customProviders[name] = {
255
+ binary: providerConfig.binary,
256
+ ...args === void 0 ? {} : { args },
257
+ ...interactiveArgs === void 0 ? {} : { interactiveArgs },
258
+ ...typeof providerConfig.cwd === "string" ? { cwd: providerConfig.cwd } : {},
259
+ ...env === void 0 ? {} : { env }
260
+ };
261
+ }
262
+ return customProviders;
263
+ }
264
+
265
+ // src/agent-targeting/parse-trailstep-config/parse-settings.ts
266
+ function parseSettings(path, value, diagnostics) {
267
+ if (value === void 0) {
268
+ return void 0;
269
+ }
270
+ if (!isRecord(value)) {
271
+ diagnostics.push(`${path} must be an object when present.`);
272
+ return void 0;
273
+ }
274
+ const settings = { ...value };
275
+ if (value.retry !== void 0) {
276
+ if (!isRecord(value.retry)) {
277
+ diagnostics.push(`${path}.retry must be an object when present.`);
278
+ delete settings.retry;
279
+ } else if (value.retry.maxAttempts === void 0) {
280
+ delete settings.retry;
281
+ } else {
282
+ settings.retry = { maxAttempts: value.retry.maxAttempts };
283
+ }
284
+ }
285
+ if (value.timeout !== void 0 && typeof value.timeout !== "number") {
286
+ diagnostics.push(`${path}.timeout must be a number when present.`);
287
+ delete settings.timeout;
288
+ }
289
+ return settings;
290
+ }
291
+
292
+ // src/agent-targeting/parse-trailstep-config/parse-workflow-agent-mappings.ts
293
+ function parseWorkflows(value, diagnostics) {
294
+ if (value === void 0) {
295
+ return void 0;
296
+ }
297
+ const workflows = {};
298
+ if (!isRecord(value)) {
299
+ diagnostics.push("workflows must be an object when present.");
300
+ return workflows;
301
+ }
302
+ for (const [workflowId, workflow] of Object.entries(value)) {
303
+ if (!isRecord(workflow)) {
304
+ diagnostics.push(`workflows.${workflowId} must be an object.`);
305
+ continue;
306
+ }
307
+ const agents = workflow.agents === void 0 ? void 0 : parseAgentMappings(`workflows.${workflowId}.agents`, workflow.agents, diagnostics);
308
+ const settings = parseSettings(
309
+ `workflows.${workflowId}.settings`,
310
+ workflow.settings,
311
+ diagnostics
312
+ );
313
+ workflows[workflowId] = {
314
+ ...agents === void 0 ? {} : { agents },
315
+ ...settings === void 0 ? {} : { settings }
316
+ };
317
+ }
318
+ return workflows;
319
+ }
320
+
321
+ // src/known-cli-providers/providers/claude/claude-provider.ts
322
+ import { spawn } from "child_process";
323
+ import { writeFile } from "fs/promises";
324
+ import { performance } from "perf_hooks";
325
+
326
+ // src/known-cli-providers/envelopes/envelope.ts
327
+ function extractEnvelopeMetadata(rawStdout, options) {
328
+ const metadata = { harnessDurationMs: options.harnessDurationMs };
329
+ const envelope = parseEnvelopeObject(rawStdout);
330
+ if (envelope) {
331
+ const usage = camelCaseUsage(envelope.usage);
332
+ if (usage) {
333
+ metadata.usage = usage;
334
+ }
335
+ if (typeof envelope.total_cost_usd === "number") {
336
+ metadata.costUsd = envelope.total_cost_usd;
337
+ }
338
+ if (typeof envelope.duration_ms === "number") {
339
+ metadata.durationMs = envelope.duration_ms;
340
+ }
341
+ if (typeof envelope.num_turns === "number") {
342
+ metadata.turns = envelope.num_turns;
343
+ }
344
+ if (typeof envelope.session_id === "string") {
345
+ metadata.sessionId = envelope.session_id;
346
+ }
347
+ }
348
+ return metadata;
349
+ }
350
+ function extractEnvelopeOutput(rawStdout, options) {
351
+ const text = rawStdout.trim();
352
+ if (!text) {
353
+ throw new Error("Provider process returned empty stdout.");
354
+ }
355
+ const parsedEnvelope = parseJson(text);
356
+ if (isPlainObject(parsedEnvelope)) {
357
+ if (parsedEnvelope.is_error === true) {
358
+ throw new Error(
359
+ `Provider reported an error: ${String(parsedEnvelope[options.resultField] ?? text)}`
360
+ );
361
+ }
362
+ const resolved = resolveFieldValue(parsedEnvelope[options.resultField]);
363
+ if (resolved) {
364
+ return resolved;
365
+ }
366
+ if (typeof parsedEnvelope.content === "string") {
367
+ return parseObjectFromText(parsedEnvelope.content);
368
+ }
369
+ }
370
+ const streamResult = parseLastJsonLineResult(text, options.resultField);
371
+ if (streamResult) {
372
+ return streamResult;
373
+ }
374
+ return parseObjectFromText(text);
375
+ }
376
+ function extractEnvelopeText(rawStdout, options) {
377
+ const text = rawStdout.trim();
378
+ if (!text) {
379
+ throw new Error("Provider process returned empty stdout.");
380
+ }
381
+ const parsedEnvelope = parseJson(text);
382
+ if (isPlainObject(parsedEnvelope)) {
383
+ if (parsedEnvelope.is_error === true) {
384
+ throw new Error(
385
+ `Provider reported an error: ${String(parsedEnvelope[options.resultField] ?? text)}`
386
+ );
387
+ }
388
+ const resolved = resolveFieldText(parsedEnvelope[options.resultField]);
389
+ if (resolved !== void 0) {
390
+ return resolved;
391
+ }
392
+ if (typeof parsedEnvelope.content === "string") {
393
+ return parsedEnvelope.content;
394
+ }
395
+ }
396
+ const streamResult = parseLastJsonLineText(text, options.resultField);
397
+ if (streamResult !== void 0) {
398
+ return streamResult;
399
+ }
400
+ return text;
401
+ }
402
+ function parseLastJsonLineText(text, resultField) {
403
+ const lines = text.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
404
+ for (const line of [...lines].reverse()) {
405
+ const parsed = parseJson(line);
406
+ if (!isPlainObject(parsed)) {
407
+ continue;
408
+ }
409
+ const resolved = resolveFieldText(parsed[resultField]);
410
+ if (resolved !== void 0) {
411
+ return resolved;
412
+ }
413
+ if (typeof parsed.content === "string") {
414
+ return parsed.content;
415
+ }
416
+ }
417
+ return void 0;
418
+ }
419
+ function resolveFieldText(fieldValue) {
420
+ if (typeof fieldValue === "string") {
421
+ return fieldValue;
422
+ }
423
+ if (isPlainObject(fieldValue)) {
424
+ return extractTextFromContentBlocks(fieldValue);
425
+ }
426
+ return void 0;
427
+ }
428
+ function parseLastJsonLineResult(text, resultField) {
429
+ const lines = text.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
430
+ for (const line of [...lines].reverse()) {
431
+ const parsed = parseJson(line);
432
+ if (!isPlainObject(parsed)) {
433
+ continue;
434
+ }
435
+ const resolved = resolveFieldValue(parsed[resultField]);
436
+ if (resolved) {
437
+ return resolved;
438
+ }
439
+ if (typeof parsed.content === "string") {
440
+ return parseObjectFromText(parsed.content);
441
+ }
442
+ }
443
+ return void 0;
444
+ }
445
+ function resolveFieldValue(fieldValue) {
446
+ if (typeof fieldValue === "string") {
447
+ return parseObjectFromText(fieldValue);
448
+ }
449
+ if (isPlainObject(fieldValue)) {
450
+ const blockText = extractTextFromContentBlocks(fieldValue);
451
+ if (blockText !== void 0) {
452
+ return parseObjectFromText(blockText);
453
+ }
454
+ return fieldValue;
455
+ }
456
+ return void 0;
457
+ }
458
+ function extractTextFromContentBlocks(value) {
459
+ const content = value.content;
460
+ if (!Array.isArray(content)) {
461
+ return void 0;
462
+ }
463
+ const textParts = content.filter(isPlainObject).filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text);
464
+ if (textParts.length === 0) {
465
+ return void 0;
466
+ }
467
+ return textParts.join("");
468
+ }
469
+ function parseObjectFromText(text) {
470
+ const parsed = parseJson(text.trim());
471
+ if (isPlainObject(parsed)) {
472
+ return parsed;
473
+ }
474
+ const firstBrace = text.indexOf("{");
475
+ const lastBrace = text.lastIndexOf("}");
476
+ if (firstBrace === -1 || lastBrace === -1 || lastBrace <= firstBrace) {
477
+ throw new Error(`Provider response did not contain a JSON object: ${text}`);
478
+ }
479
+ const extracted = parseJson(text.slice(firstBrace, lastBrace + 1));
480
+ if (!isPlainObject(extracted)) {
481
+ throw new Error("Provider response JSON was not an object.");
482
+ }
483
+ return extracted;
484
+ }
485
+ function parseEnvelopeObject(rawStdout) {
486
+ const text = rawStdout.trim();
487
+ const parsed = parseJson(text);
488
+ if (isPlainObject(parsed)) {
489
+ return parsed;
490
+ }
491
+ const lines = text.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
492
+ for (const line of [...lines].reverse()) {
493
+ const parsedLine = parseJson(line);
494
+ if (isPlainObject(parsedLine)) {
495
+ return parsedLine;
496
+ }
497
+ }
498
+ return void 0;
499
+ }
500
+ function camelCaseUsage(value) {
501
+ if (!isPlainObject(value)) {
502
+ return void 0;
503
+ }
504
+ const usage = {};
505
+ for (const [key, fieldValue] of Object.entries(value)) {
506
+ if (typeof fieldValue === "number") {
507
+ usage[snakeToCamel(key)] = fieldValue;
508
+ }
509
+ }
510
+ return Object.keys(usage).length > 0 ? usage : void 0;
511
+ }
512
+ function snakeToCamel(value) {
513
+ return value.replace(/_([a-z])/gu, (_match, letter) => letter.toUpperCase());
514
+ }
515
+ function parseJson(text) {
516
+ try {
517
+ return JSON.parse(text);
518
+ } catch {
519
+ return void 0;
520
+ }
521
+ }
522
+ function isPlainObject(value) {
523
+ return typeof value === "object" && value !== null && !Array.isArray(value);
524
+ }
525
+
526
+ // src/known-cli-providers/providers/prompt-file-reference.ts
527
+ function promptFileReference(promptFile) {
528
+ return `@${promptFile}`;
529
+ }
530
+
531
+ // src/known-cli-providers/providers/claude/claude-provider.ts
532
+ var CLAUDE_BINARY = "claude";
533
+ async function runWorking(request, runner = spawnClaudeCapturingStdout) {
534
+ const args = buildClaudeWorkingArgs(request);
535
+ let result;
536
+ const startedAt = performance.now();
537
+ try {
538
+ result = await runner({
539
+ command: CLAUDE_BINARY,
540
+ args,
541
+ cwd: request.cwd,
542
+ signal: request.signal
543
+ });
544
+ } catch (error) {
545
+ throw new TrailStepFailureError({
546
+ code: "agent_provider_spawn_error",
547
+ message: "claude provider process could not be started.",
548
+ details: { cause: error instanceof Error ? error.message : String(error) }
549
+ });
550
+ }
551
+ const harnessDurationMs = Math.max(0, Math.round(performance.now() - startedAt));
552
+ if (result.exitCode !== 0) {
553
+ throw new TrailStepFailureError({
554
+ code: "agent_provider_failed",
555
+ message: `claude provider process exited with code ${result.exitCode}.`,
556
+ details: { exitCode: result.exitCode }
557
+ });
558
+ }
559
+ await writeCapturedOutput({
560
+ stdout: result.stdout,
561
+ outputFile: request.outputFile,
562
+ usageFile: request.usageFile,
563
+ captureMode: request.captureMode,
564
+ harnessDurationMs,
565
+ context: "working"
566
+ });
567
+ }
568
+ function extractFailureContext(rawStdout) {
569
+ const metadata = extractEnvelopeMetadata(rawStdout, { harnessDurationMs: 0 });
570
+ let rawResultText;
571
+ try {
572
+ rawResultText = extractEnvelopeText(rawStdout, { resultField: "result" });
573
+ } catch {
574
+ rawResultText = void 0;
575
+ }
576
+ return {
577
+ ...metadata.sessionId === void 0 ? {} : { sessionId: metadata.sessionId },
578
+ ...rawResultText === void 0 ? {} : { rawResultText }
579
+ };
580
+ }
581
+ async function writeCapturedOutput(options) {
582
+ const label = options.context === "repair" ? "repair " : "";
583
+ if (options.captureMode === "raw-text") {
584
+ let text;
585
+ try {
586
+ text = extractEnvelopeText(options.stdout, { resultField: "result" });
587
+ } catch (error) {
588
+ throw new TrailStepFailureError({
589
+ code: "agent_provider_output_invalid",
590
+ message: `claude provider ${label}stdout did not contain a usable result.`,
591
+ details: {
592
+ cause: error instanceof Error ? error.message : String(error),
593
+ ...extractFailureContext(options.stdout)
594
+ }
595
+ });
596
+ }
597
+ await writeFile(options.outputFile, text, "utf8");
598
+ } else {
599
+ let output;
600
+ try {
601
+ output = extractEnvelopeOutput(options.stdout, { resultField: "result" });
602
+ } catch (error) {
603
+ throw new TrailStepFailureError({
604
+ code: "agent_provider_output_invalid",
605
+ message: `claude provider ${label}stdout did not contain a usable JSON result.`,
606
+ details: {
607
+ cause: error instanceof Error ? error.message : String(error),
608
+ ...extractFailureContext(options.stdout)
609
+ }
610
+ });
611
+ }
612
+ await writeFile(options.outputFile, `${JSON.stringify(output, null, 2)}
613
+ `, "utf8");
614
+ }
615
+ if (options.usageFile) {
616
+ const metadata = extractEnvelopeMetadata(options.stdout, {
617
+ harnessDurationMs: options.harnessDurationMs
618
+ });
619
+ await writeFile(options.usageFile, `${JSON.stringify(metadata, null, 2)}
620
+ `, "utf8");
621
+ }
622
+ }
623
+ async function repairOutput(request, runner = spawnClaudeCapturingStdout) {
624
+ const prompt = buildClaudeRepairPrompt(request);
625
+ const args = buildClaudeRepairArgs(request);
626
+ let result;
627
+ const startedAt = performance.now();
628
+ try {
629
+ result = await runner({
630
+ command: CLAUDE_BINARY,
631
+ args,
632
+ cwd: request.cwd,
633
+ stdin: prompt,
634
+ signal: request.signal
635
+ });
636
+ } catch (error) {
637
+ throw new TrailStepFailureError({
638
+ code: "agent_provider_spawn_error",
639
+ message: "claude provider repair process could not be started.",
640
+ details: { cause: error instanceof Error ? error.message : String(error) }
641
+ });
642
+ }
643
+ const harnessDurationMs = Math.max(0, Math.round(performance.now() - startedAt));
644
+ if (result.exitCode !== 0) {
645
+ throw new TrailStepFailureError({
646
+ code: "agent_provider_failed",
647
+ message: `claude provider repair process exited with code ${result.exitCode}.`,
648
+ details: { exitCode: result.exitCode }
649
+ });
650
+ }
651
+ await writeCapturedOutput({
652
+ stdout: result.stdout,
653
+ outputFile: request.outputFile,
654
+ usageFile: request.usageFile,
655
+ captureMode: request.captureMode,
656
+ harnessDurationMs,
657
+ context: "repair"
658
+ });
659
+ }
660
+ function buildClaudeRepairArgs(request) {
661
+ const args = [
662
+ "--resume",
663
+ request.sessionId,
664
+ "-p",
665
+ "--output-format",
666
+ "json",
667
+ "--dangerously-skip-permissions"
668
+ ];
669
+ if (request.model) {
670
+ args.push("--model", request.model);
671
+ }
672
+ if (request.thinking) {
673
+ args.push("--effort", request.thinking);
674
+ }
675
+ return args;
676
+ }
677
+ function buildClaudeRepairPrompt(request) {
678
+ if (request.captureMode === "raw-text") {
679
+ return [
680
+ "# TrailStep output repair",
681
+ "",
682
+ "Your previous final answer in this session could not be used as-is.",
683
+ "Do not redo the task or make any further changes - the work is already done.",
684
+ "Reply with the document content only, as your entire final answer: no JSON wrapper, no surrounding commentary, no markdown fences unless they are literally part of the document content itself.",
685
+ "",
686
+ "## Your previous final answer",
687
+ "",
688
+ request.rawResultText,
689
+ ""
690
+ ].join("\n");
691
+ }
692
+ return [
693
+ "# TrailStep output repair",
694
+ "",
695
+ "Your previous final answer in this session was not valid JSON matching the required schema.",
696
+ "Do not redo the task, re-read files, or make any further changes - the work is already done.",
697
+ "Reply with exactly one JSON object as your entire final answer: no prose, no markdown fences, no multiple JSON values.",
698
+ "",
699
+ "The JSON object must match this output schema:",
700
+ "",
701
+ "```json",
702
+ JSON.stringify(request.outputSchema, null, 2),
703
+ "```",
704
+ "",
705
+ "## Your previous final answer",
706
+ "",
707
+ request.rawResultText,
708
+ ""
709
+ ].join("\n");
710
+ }
711
+ function buildClaudeWorkingArgs(request) {
712
+ const args = [
713
+ "-p",
714
+ promptFileReference(request.promptFile),
715
+ "--output-format",
716
+ "json",
717
+ "--dangerously-skip-permissions"
718
+ ];
719
+ if (request.model) {
720
+ args.push("--model", request.model);
721
+ }
722
+ if (request.thinking) {
723
+ args.push("--effort", request.thinking);
724
+ }
725
+ return args;
726
+ }
727
+ async function runInteractive(request, runner = spawnClaudeInteractive) {
728
+ const args = [];
729
+ if (request.model) {
730
+ args.push("--model", request.model);
731
+ }
732
+ if (request.permissionMode !== "prompt") {
733
+ args.push("--dangerously-skip-permissions");
734
+ }
735
+ if (!request.systemPromptFile) {
736
+ throw new TrailStepFailureError({
737
+ code: "agent_provider_invalid_request",
738
+ message: "claude provider's interactive mode requires systemPromptFile."
739
+ });
740
+ }
741
+ args.push("--append-system-prompt-file", request.systemPromptFile);
742
+ return await runner({
743
+ command: CLAUDE_BINARY,
744
+ args,
745
+ cwd: request.cwd,
746
+ shell: false,
747
+ stdio: "inherit",
748
+ env: request.env,
749
+ signal: request.signal
750
+ });
751
+ }
752
+ var spawnClaudeCapturingStdout = async ({
753
+ command,
754
+ args,
755
+ cwd,
756
+ stdin,
757
+ signal
758
+ }) => {
759
+ return await new Promise((resolve2, reject) => {
760
+ const hasStdin = stdin !== void 0;
761
+ const child = spawn(command, args, {
762
+ cwd,
763
+ shell: false,
764
+ stdio: [hasStdin ? "pipe" : "ignore", "pipe", "inherit"],
765
+ detached: process.platform !== "win32"
766
+ });
767
+ let stdout = "";
768
+ child.stdout?.on("data", (chunk) => {
769
+ stdout += chunk.toString();
770
+ });
771
+ child.stdin?.on("error", reject);
772
+ signal?.addEventListener("abort", () => terminateChildProcessTree(child), { once: true });
773
+ child.on("error", reject);
774
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1, stdout }));
775
+ if (hasStdin) {
776
+ child.stdin?.end(stdin);
777
+ }
778
+ });
779
+ };
780
+ var spawnClaudeInteractive = async ({
781
+ command,
782
+ args,
783
+ cwd,
784
+ env,
785
+ signal
786
+ }) => {
787
+ return await new Promise((resolve2, reject) => {
788
+ const child = spawn(command, args, {
789
+ cwd,
790
+ env,
791
+ signal,
792
+ detached: process.platform !== "win32",
793
+ shell: false,
794
+ stdio: "inherit"
795
+ });
796
+ signal?.addEventListener("abort", () => terminateChildProcessTree(child), { once: true });
797
+ child.on("error", reject);
798
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
799
+ });
800
+ };
801
+ function terminateChildProcessTree(child) {
802
+ if (!child.pid) {
803
+ return;
804
+ }
805
+ if (process.platform === "win32") {
806
+ spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
807
+ "error",
808
+ () => {
809
+ child.kill();
810
+ }
811
+ );
812
+ return;
813
+ }
814
+ try {
815
+ process.kill(-child.pid, "SIGTERM");
816
+ } catch {
817
+ child.kill();
818
+ }
819
+ }
820
+ var claudeProvider = {
821
+ id: "claude",
822
+ runWorking,
823
+ repairOutput,
824
+ runInteractive
825
+ };
826
+
827
+ // src/known-cli-providers/providers/codex/codex-provider.ts
828
+ import { spawn as spawn2 } from "child_process";
829
+ var CODEX_BINARY = "codex";
830
+ var SUPPORTED_CODEX_THINKING = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh"]);
831
+ async function runWorking2(request, runner = spawnCodexInheritingStdio) {
832
+ const args = buildCodexWorkingArgs(request);
833
+ let result;
834
+ try {
835
+ result = await runner({
836
+ command: CODEX_BINARY,
837
+ args,
838
+ cwd: request.cwd,
839
+ signal: request.signal
840
+ });
841
+ } catch (error) {
842
+ throw new TrailStepFailureError({
843
+ code: "agent_provider_spawn_error",
844
+ message: "codex provider process could not be started.",
845
+ details: { cause: error instanceof Error ? error.message : String(error) }
846
+ });
847
+ }
848
+ if (result.exitCode !== 0) {
849
+ throw new TrailStepFailureError({
850
+ code: "agent_provider_failed",
851
+ message: `codex provider process exited with code ${result.exitCode}.`,
852
+ details: { exitCode: result.exitCode }
853
+ });
854
+ }
855
+ }
856
+ function buildCodexWorkingArgs(request) {
857
+ const args = ["exec", "--dangerously-bypass-approvals-and-sandbox"];
858
+ if (request.model) {
859
+ args.push("-m", request.model);
860
+ }
861
+ if (request.thinking) {
862
+ if (!SUPPORTED_CODEX_THINKING.has(request.thinking)) {
863
+ throw new TrailStepFailureError({
864
+ code: "agent_provider_thinking_unsupported",
865
+ message: `codex provider does not support thinking level '${request.thinking}'. Codex only supports low|medium|high|xhigh (no "max" tier).`,
866
+ details: { thinking: request.thinking }
867
+ });
868
+ }
869
+ args.push("-c", `model_reasoning_effort="${request.thinking}"`);
870
+ }
871
+ args.push("-o", request.outputFile);
872
+ args.push(promptFileReference(request.promptFile));
873
+ return args;
874
+ }
875
+ async function runInteractive2(request, runner = spawnCodexInteractive) {
876
+ const args = [];
877
+ if (request.model) {
878
+ args.push("--model", request.model);
879
+ }
880
+ args.push(
881
+ request.systemPromptFile ? promptFileReference(request.systemPromptFile) : request.prompt
882
+ );
883
+ return await runner({
884
+ command: CODEX_BINARY,
885
+ args,
886
+ cwd: request.cwd,
887
+ shell: false,
888
+ stdio: "inherit",
889
+ env: request.env,
890
+ signal: request.signal
891
+ });
892
+ }
893
+ var spawnCodexInheritingStdio = async ({ command, args, cwd, signal }) => {
894
+ return await new Promise((resolve2, reject) => {
895
+ const child = spawn2(command, args, {
896
+ cwd,
897
+ shell: false,
898
+ stdio: "inherit",
899
+ detached: process.platform !== "win32"
900
+ });
901
+ signal?.addEventListener("abort", () => terminateChildProcessTree2(child), { once: true });
902
+ child.on("error", reject);
903
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1, stdout: "" }));
904
+ });
905
+ };
906
+ var spawnCodexInteractive = async ({
907
+ command,
908
+ args,
909
+ cwd,
910
+ env,
911
+ signal
912
+ }) => {
913
+ return await new Promise((resolve2, reject) => {
914
+ const child = spawn2(command, args, {
915
+ cwd,
916
+ env,
917
+ signal,
918
+ detached: process.platform !== "win32",
919
+ shell: false,
920
+ stdio: "inherit"
921
+ });
922
+ signal?.addEventListener("abort", () => terminateChildProcessTree2(child), { once: true });
923
+ child.on("error", reject);
924
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
925
+ });
926
+ };
927
+ function terminateChildProcessTree2(child) {
928
+ if (!child.pid) {
929
+ return;
930
+ }
931
+ if (process.platform === "win32") {
932
+ spawn2("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
933
+ "error",
934
+ () => {
935
+ child.kill();
936
+ }
937
+ );
938
+ return;
939
+ }
940
+ try {
941
+ process.kill(-child.pid, "SIGTERM");
942
+ } catch {
943
+ child.kill();
944
+ }
945
+ }
946
+ var codexProvider = {
947
+ id: "codex",
948
+ runWorking: runWorking2,
949
+ runInteractive: runInteractive2
950
+ };
951
+
952
+ // src/known-cli-providers/providers/gemini/gemini-provider.ts
953
+ import { spawn as spawn3 } from "child_process";
954
+ import { writeFile as writeFile2 } from "fs/promises";
955
+ var GEMINI_BINARY = "gemini";
956
+ var GEMINI_RESULT_FIELD = "response";
957
+ async function runWorking3(request, runner = spawnGeminiCapturingStdout) {
958
+ const args = buildGeminiWorkingArgs(request);
959
+ let result;
960
+ try {
961
+ result = await runner({
962
+ command: GEMINI_BINARY,
963
+ args,
964
+ cwd: request.cwd,
965
+ signal: request.signal
966
+ });
967
+ } catch (error) {
968
+ throw new TrailStepFailureError({
969
+ code: "agent_provider_spawn_error",
970
+ message: "gemini provider process could not be started.",
971
+ details: { cause: error instanceof Error ? error.message : String(error) }
972
+ });
973
+ }
974
+ if (result.exitCode !== 0) {
975
+ throw new TrailStepFailureError({
976
+ code: "agent_provider_failed",
977
+ message: `gemini provider process exited with code ${result.exitCode}.`,
978
+ details: { exitCode: result.exitCode }
979
+ });
980
+ }
981
+ if (request.captureMode === "raw-text") {
982
+ let text;
983
+ try {
984
+ text = extractEnvelopeText(result.stdout, { resultField: GEMINI_RESULT_FIELD });
985
+ } catch (error) {
986
+ throw new TrailStepFailureError({
987
+ code: "agent_provider_output_invalid",
988
+ message: "gemini provider stdout did not contain a usable result.",
989
+ details: { cause: error instanceof Error ? error.message : String(error) }
990
+ });
991
+ }
992
+ await writeFile2(request.outputFile, text, "utf8");
993
+ } else {
994
+ let output;
995
+ try {
996
+ output = extractEnvelopeOutput(result.stdout, { resultField: GEMINI_RESULT_FIELD });
997
+ } catch (error) {
998
+ throw new TrailStepFailureError({
999
+ code: "agent_provider_output_invalid",
1000
+ message: "gemini provider stdout did not contain a usable JSON result.",
1001
+ details: { cause: error instanceof Error ? error.message : String(error) }
1002
+ });
1003
+ }
1004
+ await writeFile2(request.outputFile, `${JSON.stringify(output, null, 2)}
1005
+ `, "utf8");
1006
+ }
1007
+ }
1008
+ function buildGeminiWorkingArgs(request) {
1009
+ const args = ["-p", promptFileReference(request.promptFile), "--yolo"];
1010
+ if (request.model) {
1011
+ args.push("-m", request.model);
1012
+ }
1013
+ args.push("--output-format", "json");
1014
+ return args;
1015
+ }
1016
+ async function runInteractive3(request, runner = spawnGeminiInteractive) {
1017
+ const args = [];
1018
+ if (request.model) {
1019
+ args.push("-m", request.model);
1020
+ }
1021
+ args.push(
1022
+ request.systemPromptFile ? promptFileReference(request.systemPromptFile) : request.prompt
1023
+ );
1024
+ return await runner({
1025
+ command: GEMINI_BINARY,
1026
+ args,
1027
+ cwd: request.cwd,
1028
+ shell: false,
1029
+ stdio: "inherit",
1030
+ env: request.env,
1031
+ signal: request.signal
1032
+ });
1033
+ }
1034
+ var spawnGeminiCapturingStdout = async ({
1035
+ command,
1036
+ args,
1037
+ cwd,
1038
+ signal
1039
+ }) => {
1040
+ return await new Promise((resolve2, reject) => {
1041
+ const child = spawn3(command, args, {
1042
+ cwd,
1043
+ shell: false,
1044
+ stdio: ["ignore", "pipe", "inherit"],
1045
+ detached: process.platform !== "win32"
1046
+ });
1047
+ let stdout = "";
1048
+ child.stdout?.on("data", (chunk) => {
1049
+ stdout += chunk.toString();
1050
+ });
1051
+ signal?.addEventListener("abort", () => terminateChildProcessTree3(child), { once: true });
1052
+ child.on("error", reject);
1053
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1, stdout }));
1054
+ });
1055
+ };
1056
+ var spawnGeminiInteractive = async ({
1057
+ command,
1058
+ args,
1059
+ cwd,
1060
+ env,
1061
+ signal
1062
+ }) => {
1063
+ return await new Promise((resolve2, reject) => {
1064
+ const child = spawn3(command, args, {
1065
+ cwd,
1066
+ env,
1067
+ signal,
1068
+ detached: process.platform !== "win32",
1069
+ shell: false,
1070
+ stdio: "inherit"
1071
+ });
1072
+ signal?.addEventListener("abort", () => terminateChildProcessTree3(child), { once: true });
1073
+ child.on("error", reject);
1074
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
1075
+ });
1076
+ };
1077
+ function terminateChildProcessTree3(child) {
1078
+ if (!child.pid) {
1079
+ return;
1080
+ }
1081
+ if (process.platform === "win32") {
1082
+ spawn3("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
1083
+ "error",
1084
+ () => {
1085
+ child.kill();
1086
+ }
1087
+ );
1088
+ return;
1089
+ }
1090
+ try {
1091
+ process.kill(-child.pid, "SIGTERM");
1092
+ } catch {
1093
+ child.kill();
1094
+ }
1095
+ }
1096
+ var geminiProvider = {
1097
+ id: "gemini",
1098
+ runWorking: runWorking3,
1099
+ runInteractive: runInteractive3
1100
+ };
1101
+
1102
+ // src/known-cli-providers/providers/pi/pi-provider.ts
1103
+ import { spawn as spawn4 } from "child_process";
1104
+ import { writeFile as writeFile3 } from "fs/promises";
1105
+ import { StringDecoder } from "string_decoder";
1106
+
1107
+ // src/known-cli-providers/process/resolve-cli-command.ts
1108
+ import { constants } from "fs";
1109
+ import { access, readFile } from "fs/promises";
1110
+ import { dirname, extname, join } from "path";
1111
+ var WINDOWS_EXECUTABLE_EXTENSIONS = [".cmd", ".exe", ".bat", ""];
1112
+ async function resolveCliCommandForSpawn(request, options = {}) {
1113
+ if ((options.platform ?? process.platform) !== "win32") {
1114
+ return request;
1115
+ }
1116
+ const commandPath = await findWindowsCommandPath(request.command, request.env ?? process.env);
1117
+ if (!commandPath) {
1118
+ return request;
1119
+ }
1120
+ if (isWindowsCommandShim(commandPath)) {
1121
+ const nodeScript = await readNpmCmdShimEntrypoint(commandPath);
1122
+ if (nodeScript) {
1123
+ return {
1124
+ command: options.execPath ?? process.execPath,
1125
+ args: [nodeScript, ...request.args]
1126
+ };
1127
+ }
1128
+ return {
1129
+ command: "cmd.exe",
1130
+ args: ["/d", "/s", "/c", commandPath, ...request.args]
1131
+ };
1132
+ }
1133
+ return { command: commandPath, args: request.args };
1134
+ }
1135
+ async function findWindowsCommandPath(command, env) {
1136
+ const commandCandidates = windowsCommandCandidates(command);
1137
+ if (hasPathSeparator(command)) {
1138
+ return await firstExistingPath(commandCandidates);
1139
+ }
1140
+ const pathValue = readCaseInsensitiveEnv(env, "PATH");
1141
+ if (!pathValue) {
1142
+ return await firstExistingPath(commandCandidates);
1143
+ }
1144
+ for (const directory of pathValue.split(windowsPathDelimiter()).filter(Boolean)) {
1145
+ const found = await firstExistingPath(
1146
+ commandCandidates.map((candidate) => join(directory, candidate))
1147
+ );
1148
+ if (found) {
1149
+ return found;
1150
+ }
1151
+ }
1152
+ return void 0;
1153
+ }
1154
+ function windowsCommandCandidates(command) {
1155
+ if (extname(command)) {
1156
+ return [command];
1157
+ }
1158
+ return WINDOWS_EXECUTABLE_EXTENSIONS.map((extension) => `${command}${extension}`);
1159
+ }
1160
+ async function firstExistingPath(paths) {
1161
+ for (const path of paths) {
1162
+ try {
1163
+ await access(path, constants.X_OK);
1164
+ return path;
1165
+ } catch {
1166
+ try {
1167
+ await access(path, constants.F_OK);
1168
+ return path;
1169
+ } catch {
1170
+ }
1171
+ }
1172
+ }
1173
+ return void 0;
1174
+ }
1175
+ async function readNpmCmdShimEntrypoint(commandPath) {
1176
+ let shim;
1177
+ try {
1178
+ shim = await readFile(commandPath, "utf8");
1179
+ } catch {
1180
+ return void 0;
1181
+ }
1182
+ const match = shim.match(/"%dp0%\\?([^"]+?\.js)"\s+%\*/i);
1183
+ if (!match?.[1]) {
1184
+ return void 0;
1185
+ }
1186
+ return join(dirname(commandPath), match[1].replace(/\\/g, "/"));
1187
+ }
1188
+ function isWindowsCommandShim(path) {
1189
+ const extension = extname(path).toLowerCase();
1190
+ return extension === ".cmd" || extension === ".bat";
1191
+ }
1192
+ function hasPathSeparator(command) {
1193
+ return command.includes("/") || command.includes("\\");
1194
+ }
1195
+ function readCaseInsensitiveEnv(env, key) {
1196
+ return Object.entries(env).find(([name]) => name.toLowerCase() === key.toLowerCase())?.[1];
1197
+ }
1198
+ function windowsPathDelimiter() {
1199
+ return ";";
1200
+ }
1201
+
1202
+ // src/known-cli-providers/providers/pi/pi-provider.ts
1203
+ var PI_BINARY = "pi";
1204
+ var PI_RESULT_FIELD = "message";
1205
+ var PI_STDOUT_FALLBACK_TAIL_CHARS = 128e3;
1206
+ async function runWorking4(request, runner = spawnPiCapturingStdout) {
1207
+ const args = buildPiWorkingArgs(request);
1208
+ let result;
1209
+ try {
1210
+ result = await runner({ command: PI_BINARY, args, cwd: request.cwd, signal: request.signal });
1211
+ } catch (error) {
1212
+ throw new TrailStepFailureError({
1213
+ code: "agent_provider_spawn_error",
1214
+ message: "pi provider process could not be started.",
1215
+ details: { cause: error instanceof Error ? error.message : String(error) }
1216
+ });
1217
+ }
1218
+ if (request.captureMode === "raw-text") {
1219
+ let text;
1220
+ try {
1221
+ text = extractEnvelopeText(result.stdout, { resultField: PI_RESULT_FIELD });
1222
+ } catch (error) {
1223
+ throwPiOutputFailure({ exitCode: result.exitCode, error, json: false });
1224
+ }
1225
+ await writeFile3(request.outputFile, text, "utf8");
1226
+ return;
1227
+ }
1228
+ let output;
1229
+ try {
1230
+ output = extractEnvelopeOutput(result.stdout, { resultField: PI_RESULT_FIELD });
1231
+ } catch (error) {
1232
+ throwPiOutputFailure({ exitCode: result.exitCode, error, json: true });
1233
+ }
1234
+ await writeFile3(request.outputFile, `${JSON.stringify(output, null, 2)}
1235
+ `, "utf8");
1236
+ }
1237
+ function throwPiOutputFailure(options) {
1238
+ if (options.exitCode !== 0) {
1239
+ throw new TrailStepFailureError({
1240
+ code: "agent_provider_failed",
1241
+ message: `pi provider process exited with code ${options.exitCode}.`,
1242
+ details: { exitCode: options.exitCode }
1243
+ });
1244
+ }
1245
+ throw new TrailStepFailureError({
1246
+ code: "agent_provider_output_invalid",
1247
+ message: options.json ? "pi provider stdout did not contain a usable JSON result." : "pi provider stdout did not contain a usable result.",
1248
+ details: {
1249
+ cause: options.error instanceof Error ? options.error.message : String(options.error)
1250
+ }
1251
+ });
1252
+ }
1253
+ function buildPiWorkingArgs(request) {
1254
+ const args = ["-p"];
1255
+ if (request.model) {
1256
+ args.push("--model", request.model);
1257
+ }
1258
+ if (request.thinking) {
1259
+ args.push("--thinking", request.thinking);
1260
+ }
1261
+ args.push(promptFileReference(request.promptFile));
1262
+ args.push("--mode", "json");
1263
+ return args;
1264
+ }
1265
+ async function runInteractive4(request, runner = spawnPiInteractive) {
1266
+ const args = [];
1267
+ if (request.model) {
1268
+ args.push("--model", request.model);
1269
+ }
1270
+ args.push(
1271
+ request.systemPromptFile ? promptFileReference(request.systemPromptFile) : request.prompt
1272
+ );
1273
+ return await runner({
1274
+ command: PI_BINARY,
1275
+ args,
1276
+ cwd: request.cwd,
1277
+ shell: false,
1278
+ stdio: "inherit",
1279
+ env: request.env,
1280
+ signal: request.signal
1281
+ });
1282
+ }
1283
+ var spawnPiCapturingStdout = async ({ command, args, cwd, signal }) => {
1284
+ const executable = await resolveCliCommandForSpawn({ command, args });
1285
+ return await new Promise((resolve2, reject) => {
1286
+ const child = spawn4(executable.command, executable.args, {
1287
+ cwd,
1288
+ shell: false,
1289
+ stdio: ["ignore", "pipe", "inherit"],
1290
+ detached: process.platform !== "win32"
1291
+ });
1292
+ const stdout = createPiJsonStreamStdoutCollector();
1293
+ child.stdout?.on("data", (chunk) => {
1294
+ stdout.append(chunk);
1295
+ });
1296
+ signal?.addEventListener("abort", () => terminateChildProcessTree4(child), { once: true });
1297
+ child.on("error", reject);
1298
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1, stdout: stdout.finish() }));
1299
+ });
1300
+ };
1301
+ function createPiJsonStreamStdoutCollector() {
1302
+ const decoder = new StringDecoder("utf8");
1303
+ let pendingLine = "";
1304
+ let latestResultLine;
1305
+ let fallbackTail = "";
1306
+ function appendText(text) {
1307
+ fallbackTail = trimFallbackTail(`${fallbackTail}${text}`);
1308
+ pendingLine += text;
1309
+ while (true) {
1310
+ const newlineIndex = pendingLine.indexOf("\n");
1311
+ if (newlineIndex === -1) {
1312
+ return;
1313
+ }
1314
+ const line = pendingLine.slice(0, newlineIndex).replace(/\r$/u, "");
1315
+ rememberLine(line);
1316
+ pendingLine = pendingLine.slice(newlineIndex + 1);
1317
+ }
1318
+ }
1319
+ return {
1320
+ append(chunk) {
1321
+ appendText(decoder.write(chunk));
1322
+ },
1323
+ finish() {
1324
+ appendText(decoder.end());
1325
+ rememberLine(pendingLine);
1326
+ pendingLine = "";
1327
+ return latestResultLine ?? fallbackTail;
1328
+ }
1329
+ };
1330
+ function rememberLine(line) {
1331
+ const trimmed = line.trim();
1332
+ if (trimmed && isPiResultCandidateLine(trimmed)) {
1333
+ latestResultLine = trimmed;
1334
+ }
1335
+ }
1336
+ }
1337
+ function trimFallbackTail(value) {
1338
+ if (value.length <= PI_STDOUT_FALLBACK_TAIL_CHARS) {
1339
+ return value;
1340
+ }
1341
+ return value.slice(-PI_STDOUT_FALLBACK_TAIL_CHARS);
1342
+ }
1343
+ function isPiResultCandidateLine(line) {
1344
+ return line.includes(`"${PI_RESULT_FIELD}"`);
1345
+ }
1346
+ var spawnPiInteractive = async ({
1347
+ command,
1348
+ args,
1349
+ cwd,
1350
+ env,
1351
+ signal
1352
+ }) => {
1353
+ const executable = await resolveCliCommandForSpawn({ command, args, env });
1354
+ return await new Promise((resolve2, reject) => {
1355
+ const child = spawn4(executable.command, executable.args, {
1356
+ cwd,
1357
+ env,
1358
+ signal,
1359
+ detached: process.platform !== "win32",
1360
+ shell: false,
1361
+ stdio: "inherit"
1362
+ });
1363
+ signal?.addEventListener("abort", () => terminateChildProcessTree4(child), { once: true });
1364
+ child.on("error", reject);
1365
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
1366
+ });
1367
+ };
1368
+ function terminateChildProcessTree4(child) {
1369
+ if (!child.pid) {
1370
+ return;
1371
+ }
1372
+ if (process.platform === "win32") {
1373
+ spawn4("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
1374
+ "error",
1375
+ () => {
1376
+ child.kill();
1377
+ }
1378
+ );
1379
+ return;
1380
+ }
1381
+ try {
1382
+ process.kill(-child.pid, "SIGTERM");
1383
+ } catch {
1384
+ child.kill();
1385
+ }
1386
+ }
1387
+ var piProvider = {
1388
+ id: "pi",
1389
+ runWorking: runWorking4,
1390
+ runInteractive: runInteractive4
1391
+ };
1392
+
1393
+ // src/known-cli-providers/registry/provider-registry.ts
1394
+ var providerRegistry = {
1395
+ claude: claudeProvider,
1396
+ codex: codexProvider,
1397
+ pi: piProvider,
1398
+ gemini: geminiProvider
1399
+ };
1400
+ function isProviderRegistryKey(provider) {
1401
+ return Object.hasOwn(providerRegistry, provider);
1402
+ }
1403
+
1404
+ // src/agent-targeting/parse-trailstep-config/validate-provider-references.ts
1405
+ function validateProviderReferences(options) {
1406
+ const providerViolations = [];
1407
+ validateAgentMappings("agents", options.agents, options.providerNames, providerViolations);
1408
+ if (options.workflows !== void 0) {
1409
+ for (const [workflowId, workflow] of Object.entries(options.workflows)) {
1410
+ validateAgentMappings(
1411
+ `workflows.${workflowId}.agents`,
1412
+ workflow.agents,
1413
+ options.providerNames,
1414
+ providerViolations
1415
+ );
1416
+ }
1417
+ }
1418
+ if (providerViolations.length > 0) {
1419
+ throw new TrailStepFailureError({
1420
+ code: "agent_provider_unknown",
1421
+ message: "One or more agent targets reference a provider that is neither a built-in provider nor a declared customProviders entry.",
1422
+ details: { diagnostics: providerViolations }
1423
+ });
1424
+ }
1425
+ }
1426
+ function validateAgentMappings(path, mappings, providerNames, diagnostics) {
1427
+ if (mappings === void 0) {
1428
+ return;
1429
+ }
1430
+ for (const [name, targets] of Object.entries(mappings)) {
1431
+ validateTargetReferences(`${path}.${name}`, targets, providerNames, diagnostics);
1432
+ }
1433
+ }
1434
+ function validateTargetReferences(path, targets, providerNames, diagnostics) {
1435
+ for (const [index, target] of targets.entries()) {
1436
+ if (!("provider" in target)) {
1437
+ continue;
1438
+ }
1439
+ if (!providerNames.has(target.provider) && !isProviderRegistryKey(target.provider)) {
1440
+ diagnostics.push(
1441
+ `${path}[${index}].provider references unknown provider '${target.provider}'. Declare it under customProviders or use a built-in provider.`
1442
+ );
1443
+ }
1444
+ }
1445
+ }
1446
+
1447
+ // src/agent-targeting/parse-trailstep-config/parse-trailstep-config.ts
1448
+ var parsedConfigs = /* @__PURE__ */ new WeakSet();
1449
+ function isParsedTrailStepConfig(value) {
1450
+ return isRecord(value) && parsedConfigs.has(value);
1451
+ }
1452
+ function parseTrailStepConfig(value) {
1453
+ const diagnostics = [];
1454
+ if (!isRecord(value)) {
1455
+ throwValidationFailure(["config must be an object."]);
1456
+ }
1457
+ if (value.version !== 1) {
1458
+ diagnostics.push("version must be 1.");
1459
+ }
1460
+ const customProviders = parseCustomProviders(value.customProviders, diagnostics);
1461
+ const providerNames = new Set(Object.keys(customProviders));
1462
+ const agents = parseAgentMappings("agents", value.agents, diagnostics);
1463
+ const settings = parseSettings("settings", value.settings, diagnostics);
1464
+ const workflows = parseWorkflows(value.workflows, diagnostics);
1465
+ if (diagnostics.length > 0) {
1466
+ throwValidationFailure(diagnostics);
1467
+ }
1468
+ validateProviderReferences({ agents, workflows, providerNames });
1469
+ const expanded = expandAgentRefs({ agents, workflows });
1470
+ const config = {
1471
+ version: 1,
1472
+ customProviders,
1473
+ agents: expanded.agents,
1474
+ ...settings === void 0 ? {} : { settings },
1475
+ ...expanded.workflows === void 0 ? {} : { workflows: expanded.workflows }
1476
+ };
1477
+ parsedConfigs.add(config);
1478
+ return config;
1479
+ }
1480
+
1481
+ // src/agent-targeting/resolve-agent-targets/resolve-agent-targets.ts
1482
+ function resolveAgentTargets(options) {
1483
+ const workflowMappings = options.config.workflows?.[options.workflowId]?.agents;
1484
+ const targetLists = [
1485
+ workflowMappings?.[options.roleName],
1486
+ options.config.agents[options.roleSize],
1487
+ options.config.agents.default
1488
+ ];
1489
+ const targets = targetLists.flatMap(
1490
+ (targets2) => targets2 && targets2.length > 0 ? [...targets2] : []
1491
+ );
1492
+ if (targets.length > 0) {
1493
+ return targets;
1494
+ }
1495
+ throw new TrailStepFailureError({
1496
+ code: "agent_targets_unavailable",
1497
+ message: `No agent targets found for role ${options.roleName} with size ${options.roleSize} in workflow ${options.workflowId}.`,
1498
+ details: {
1499
+ roleName: options.roleName,
1500
+ roleSize: options.roleSize,
1501
+ workflowId: options.workflowId
1502
+ }
1503
+ });
1504
+ }
1505
+
1506
+ // src/runtime/artifacts/run-storage.ts
1507
+ import { appendFile, mkdir, readFile as readFile2, writeFile as writeFile4 } from "fs/promises";
1508
+ import { basename, dirname as dirname2, join as join2 } from "path";
1509
+ function defaultRunsRoot(cwd) {
1510
+ return join2(cwd, ".trailstep", "runs");
1511
+ }
1512
+ async function createRunDirectory(options) {
1513
+ const runsRoot = options.runsRoot ?? defaultRunsRoot(options.cwd);
1514
+ await mkdir(runsRoot, { recursive: true });
1515
+ await ensureTrailStepGitignoreForRunsRoot(runsRoot);
1516
+ for (let suffix = 1; ; suffix += 1) {
1517
+ const runId = suffix === 1 ? options.runName : `${options.runName}-${suffix}`;
1518
+ const runDir = join2(runsRoot, runId);
1519
+ try {
1520
+ await mkdir(runDir);
1521
+ return { runId, runDir };
1522
+ } catch (error) {
1523
+ if (isNodeError(error) && error.code === "EEXIST") {
1524
+ continue;
1525
+ }
1526
+ throw error;
1527
+ }
1528
+ }
1529
+ }
1530
+ async function appendEvent(runDir, event) {
1531
+ await appendFile(join2(runDir, "events.jsonl"), `${JSON.stringify(event)}
1532
+ `, "utf8");
1533
+ }
1534
+ async function readRunEvents(runDir) {
1535
+ let contents;
1536
+ try {
1537
+ contents = await readFile2(join2(runDir, "events.jsonl"), "utf8");
1538
+ } catch (error) {
1539
+ if (isNodeError(error)) {
1540
+ throw new TrailStepFailureError({
1541
+ code: "resume_target_not_found",
1542
+ message: `Resume target not found or unreadable: ${join2(runDir, "events.jsonl")}.`,
1543
+ details: { runDir, causeCode: error.code }
1544
+ });
1545
+ }
1546
+ throw error;
1547
+ }
1548
+ const rawLines = contents.split("\n");
1549
+ const lines = rawLines.filter((line, index) => line.length > 0 || index < rawLines.length - 1);
1550
+ const events = [];
1551
+ for (let index = 0; index < lines.length; index += 1) {
1552
+ const line = lines[index];
1553
+ if (!line) {
1554
+ continue;
1555
+ }
1556
+ try {
1557
+ events.push(JSON.parse(line));
1558
+ } catch (error) {
1559
+ const isTrailingLine = index === lines.length - 1;
1560
+ if (isTrailingLine) {
1561
+ return events;
1562
+ }
1563
+ throw error;
1564
+ }
1565
+ }
1566
+ return events;
1567
+ }
1568
+ async function writeDocumentArtifact(dir, filename, content) {
1569
+ await mkdir(dir, { recursive: true });
1570
+ const documentPath = join2(dir, filename);
1571
+ await writeFile4(documentPath, content, "utf8");
1572
+ return documentPath;
1573
+ }
1574
+ async function readRunState(runDir) {
1575
+ try {
1576
+ const contents = await readFile2(join2(runDir, "state.json"), "utf8");
1577
+ const parsed = JSON.parse(contents);
1578
+ if (!isRunState(parsed)) {
1579
+ throw new Error("run state must be a JSON object");
1580
+ }
1581
+ return parsed;
1582
+ } catch (error) {
1583
+ if (isNodeError(error) && error.code === "ENOENT") {
1584
+ return {};
1585
+ }
1586
+ throw error;
1587
+ }
1588
+ }
1589
+ async function writeRunState(runDir, state2) {
1590
+ await writeFile4(join2(runDir, "state.json"), `${JSON.stringify(state2, null, 2)}
1591
+ `, "utf8");
1592
+ }
1593
+ function isNodeError(error) {
1594
+ return error instanceof Error && "code" in error;
1595
+ }
1596
+ function isRunState(value) {
1597
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
1598
+ }
1599
+ async function ensureTrailStepGitignoreForRunsRoot(runsRoot) {
1600
+ const trailstepRoot = dirname2(runsRoot);
1601
+ if (basename(trailstepRoot) !== ".trailstep") {
1602
+ return;
1603
+ }
1604
+ await ensureTrailStepGitignore(trailstepRoot);
1605
+ }
1606
+ async function ensureTrailStepGitignore(trailstepRoot) {
1607
+ try {
1608
+ await writeFile4(join2(trailstepRoot, ".gitignore"), "*\n!.gitignore\n", { flag: "wx" });
1609
+ } catch (error) {
1610
+ if (isNodeError(error) && error.code === "EEXIST") {
1611
+ return;
1612
+ }
1613
+ throw error;
1614
+ }
1615
+ }
1616
+
1617
+ // src/runtime/run-context/run-context-storage.ts
1618
+ import { AsyncLocalStorage } from "async_hooks";
1619
+ var RUN_CONTEXT_STORAGE_KEY = /* @__PURE__ */ Symbol.for("trailstep.core.runContextStorage");
1620
+ var globalScope = globalThis;
1621
+ function getOrCreateRunContextStorage() {
1622
+ const existing = globalScope[RUN_CONTEXT_STORAGE_KEY];
1623
+ if (existing) {
1624
+ return existing;
1625
+ }
1626
+ const created = new AsyncLocalStorage();
1627
+ globalScope[RUN_CONTEXT_STORAGE_KEY] = created;
1628
+ return created;
1629
+ }
1630
+ var runContextStorage = getOrCreateRunContextStorage();
1631
+ function currentRunContext() {
1632
+ const runContext = runContextStorage.getStore();
1633
+ if (!runContext) {
1634
+ throw new Error("state.* called outside an active TrailStep run.");
1635
+ }
1636
+ return runContext;
1637
+ }
1638
+
1639
+ // src/authoring/document/document.ts
1640
+ var Document = class _Document {
1641
+ constructor(content, path) {
1642
+ this.content = content;
1643
+ this.path = path;
1644
+ }
1645
+ content;
1646
+ path;
1647
+ static captureMode = "raw-text";
1648
+ static jsonSchema = {
1649
+ type: "string",
1650
+ description: "Document content, captured as raw text."
1651
+ };
1652
+ static validate(value) {
1653
+ return isDocumentLike(value);
1654
+ }
1655
+ static diagnostics(value) {
1656
+ if (isDocumentLike(value)) {
1657
+ return [];
1658
+ }
1659
+ return [
1660
+ { path: "/", message: "must be a document (an object with string content and path fields)" }
1661
+ ];
1662
+ }
1663
+ /**
1664
+ * Deliberately duck-types instead of checking `instanceof Document`: on
1665
+ * resume, a completed step's recorded output is replayed from
1666
+ * `events.jsonl` via `JSON.parse`, producing a plain deserialized object —
1667
+ * never a live `Document` instance — before `assert` is called on it. An
1668
+ * `instanceof` check would fail every such replay. `assert` reconstructs a
1669
+ * genuine `Document` from any content/path-shaped value, so every
1670
+ * consumer downstream — a live capture or a replayed value alike — always
1671
+ * receives a real `Document` instance with working prototype methods.
1672
+ */
1673
+ static assert(value, label = "value") {
1674
+ const valueDiagnostics = _Document.diagnostics(value);
1675
+ if (valueDiagnostics.length > 0 || !isDocumentLike(value)) {
1676
+ throw new TrailStepFailureError(
1677
+ validationFailure(
1678
+ `${label} failed schema validation: ${formatDiagnostics(valueDiagnostics)}`,
1679
+ {
1680
+ diagnostics: valueDiagnostics
1681
+ }
1682
+ )
1683
+ );
1684
+ }
1685
+ return new _Document(value.content, value.path);
1686
+ }
1687
+ };
1688
+ async function document(content) {
1689
+ const { currentStep } = currentRunContext();
1690
+ if (!currentStep) {
1691
+ throw new Error(
1692
+ "document(...) must be called from within a step's `.do()` callback; no active step context was found."
1693
+ );
1694
+ }
1695
+ const index = currentStep.nextDocumentIndex();
1696
+ const path = await writeDocumentArtifact(currentStep.dir, `document-${index}.md`, content);
1697
+ return new Document(content, path);
1698
+ }
1699
+ function isDocumentLike(value) {
1700
+ return typeof value === "object" && value !== null && typeof value.content === "string" && typeof value.path === "string";
1701
+ }
1702
+ function formatDiagnostics(diagnostics) {
1703
+ return diagnostics.map((diagnostic) => `${diagnostic.path} ${diagnostic.message}`).join("; ");
1704
+ }
1705
+
1706
+ // src/authoring/prompt-composition/prompt-composition.ts
1707
+ import { readFileSync } from "fs";
1708
+ import { join as join3 } from "path";
1709
+ function loadFragments(dir, files) {
1710
+ const fragments = {};
1711
+ for (const key of Object.keys(files)) {
1712
+ fragments[key] = readFileSync(join3(dir, files[key]), "utf8").trimEnd();
1713
+ }
1714
+ return fragments;
1715
+ }
1716
+ function section(title, body) {
1717
+ return body ? `## ${title}
1718
+
1719
+ ${body.trimEnd()}` : void 0;
1720
+ }
1721
+ function promptSections(...parts) {
1722
+ return parts.filter((part) => Boolean(part)).join("\n\n");
1723
+ }
1724
+ function list(items) {
1725
+ return items.map((item) => `- ${item}`).join("\n");
1726
+ }
1727
+
1728
+ // src/authoring/prompt-template/prompt-template.ts
1729
+ function promptTemplate(path) {
1730
+ return { kind: "promptTemplate", path };
1731
+ }
1732
+
1733
+ // src/authoring/shape/json-schema.ts
1734
+ import { Ajv } from "ajv/dist/ajv.js";
1735
+ var ajv = new Ajv({ allErrors: true });
1736
+ function normalizeShape(shapeInput) {
1737
+ if (isSchema(shapeInput)) {
1738
+ return shapeInput;
1739
+ }
1740
+ return shape(shapeInput);
1741
+ }
1742
+ function shape(shapeObject) {
1743
+ return jsonSchema({
1744
+ type: "object",
1745
+ properties: Object.fromEntries(
1746
+ Object.entries(shapeObject).map(([key, type]) => [key, { type }])
1747
+ ),
1748
+ required: Object.keys(shapeObject),
1749
+ additionalProperties: false
1750
+ });
1751
+ }
1752
+ function jsonSchema(schema) {
1753
+ const validateWithAjv = ajv.compile(schema);
1754
+ return {
1755
+ validate(value) {
1756
+ return validateWithAjv(value);
1757
+ },
1758
+ diagnostics(value) {
1759
+ if (!isPlainObject2(value)) {
1760
+ return [{ path: "/", message: "must be a plain object" }];
1761
+ }
1762
+ if (validateWithAjv(value)) {
1763
+ return [];
1764
+ }
1765
+ return diagnosticsFromAjvErrors(validateWithAjv.errors);
1766
+ },
1767
+ assert(value, label = "value") {
1768
+ const diagnostics = this.diagnostics(value);
1769
+ if (diagnostics.length > 0) {
1770
+ throw new TrailStepFailureError(
1771
+ validationFailure(
1772
+ `${label} failed schema validation: ${formatDiagnostics2(diagnostics)}`,
1773
+ { diagnostics }
1774
+ )
1775
+ );
1776
+ }
1777
+ return value;
1778
+ },
1779
+ jsonSchema: schema
1780
+ };
1781
+ }
1782
+ function isSchema(value) {
1783
+ if (value === null || typeof value !== "object" && typeof value !== "function") {
1784
+ return false;
1785
+ }
1786
+ const candidate = value;
1787
+ return typeof candidate.validate === "function" && typeof candidate.diagnostics === "function" && typeof candidate.assert === "function";
1788
+ }
1789
+ function isPlainObject2(value) {
1790
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
1791
+ }
1792
+ function diagnosticsFromAjvErrors(errors) {
1793
+ if (!errors || errors.length === 0) {
1794
+ return [{ path: "/", message: "is invalid" }];
1795
+ }
1796
+ return errors.map((error) => ({
1797
+ path: error.instancePath || "/",
1798
+ message: error.message ?? "is invalid"
1799
+ }));
1800
+ }
1801
+ function formatDiagnostics2(diagnostics) {
1802
+ return diagnostics.map((diagnostic) => `${diagnostic.path} ${diagnostic.message}`).join("; ");
1803
+ }
1804
+
1805
+ // src/authoring/state/state.ts
1806
+ var state = {
1807
+ async get(key) {
1808
+ return currentRunContext().state.get(key);
1809
+ },
1810
+ async set(key, value) {
1811
+ await currentRunContext().state.set(key, value);
1812
+ },
1813
+ get id() {
1814
+ return currentRunContext().id;
1815
+ },
1816
+ get name() {
1817
+ return currentRunContext().name;
1818
+ },
1819
+ get path() {
1820
+ return currentRunContext().path;
1821
+ },
1822
+ get cwd() {
1823
+ return currentRunContext().cwd;
1824
+ }
1825
+ };
1826
+
1827
+ // src/authoring/step/step-node.ts
1828
+ function step(config) {
1829
+ const buildFactory = (prompt, promptOptions, onOutput, onError) => {
1830
+ const factory = ((input) => ({
1831
+ kind: "step",
1832
+ config: {
1833
+ ...config,
1834
+ ...promptOptions,
1835
+ prompt,
1836
+ input: input ?? {}
1837
+ },
1838
+ onOutput,
1839
+ onError
1840
+ }));
1841
+ factory.catch = (nextOnError) => buildFactory(prompt, promptOptions, onOutput, nextOnError);
1842
+ return factory;
1843
+ };
1844
+ return {
1845
+ prompt(source, options) {
1846
+ if (hasRetryOption(options)) {
1847
+ throw new TypeError("Retry config belongs on step(...), not .prompt(...) options.");
1848
+ }
1849
+ if (hasTimeoutOption(options)) {
1850
+ throw new TypeError("Timeout config belongs on step(...), not .prompt(...) options.");
1851
+ }
1852
+ return {
1853
+ do: (onOutput) => buildFactory(source, options, onOutput)
1854
+ };
1855
+ },
1856
+ do(onOutput) {
1857
+ return buildFactory(void 0, void 0, onOutput);
1858
+ }
1859
+ };
1860
+ }
1861
+ function done(output) {
1862
+ return {
1863
+ kind: "done",
1864
+ output: output ?? {}
1865
+ };
1866
+ }
1867
+ function fail(failure) {
1868
+ return {
1869
+ kind: "fail",
1870
+ failure
1871
+ };
1872
+ }
1873
+ function isStepNode(value) {
1874
+ return isPlainObject3(value) && value.kind === "step";
1875
+ }
1876
+ function isDoneNode(value) {
1877
+ return isPlainObject3(value) && value.kind === "done";
1878
+ }
1879
+ function isFailNode(value) {
1880
+ return isPlainObject3(value) && value.kind === "fail";
1881
+ }
1882
+ function hasRetryOption(value) {
1883
+ return isPlainObject3(value) && "retry" in value;
1884
+ }
1885
+ function hasTimeoutOption(value) {
1886
+ return isPlainObject3(value) && "timeout" in value;
1887
+ }
1888
+ function isPlainObject3(value) {
1889
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
1890
+ }
1891
+
1892
+ // src/runtime/sub-prompts/run-sub-prompt.ts
1893
+ import { mkdir as mkdir4, writeFile as writeFile8 } from "fs/promises";
1894
+
1895
+ // src/agent-execution/adapter-agent/run-adapter-agent-step/run-adapter-agent-step.ts
1896
+ var PROVIDER_NEUTRAL_ADAPTER_KEY = "custom";
1897
+ var PROVIDER_NEUTRAL_MODEL = "configured-agent";
1898
+ async function runAgentStep(options) {
1899
+ const model = resolveAgentRequirements(options.step.requirements);
1900
+ const adapter = resolveAgentAdapter(options.step.adapter);
1901
+ let submittedOutput;
1902
+ const tools = [
1903
+ createSubmitOutputTool(options.step.output, async (output) => {
1904
+ options.step.output.assert(output, `agent step ${options.step.id} submit_output`);
1905
+ submittedOutput = output;
1906
+ await options.onToolCall?.({
1907
+ name: "submit_output",
1908
+ input: output,
1909
+ output: { accepted: true }
1910
+ });
1911
+ })
1912
+ ];
1913
+ const prompt = renderAgentPrompt(options.step.prompt, options.input);
1914
+ await adapter({
1915
+ messages: [{ role: "user", content: prompt }],
1916
+ tools,
1917
+ requirements: options.step.requirements,
1918
+ model,
1919
+ step: options.step,
1920
+ input: options.input
1921
+ });
1922
+ if (submittedOutput === void 0) {
1923
+ throw new TrailStepFailureError({
1924
+ code: "agent_output_missing",
1925
+ message: `Agent step ${options.step.id} did not call submit_output.`
1926
+ });
1927
+ }
1928
+ return submittedOutput;
1929
+ }
1930
+ function renderAgentPrompt(prompt, input) {
1931
+ return typeof prompt === "function" ? prompt({ input }) : prompt;
1932
+ }
1933
+ function resolveAgentRequirements(requirements) {
1934
+ return {
1935
+ adapterKey: PROVIDER_NEUTRAL_ADAPTER_KEY,
1936
+ model: requirements.name ?? requirements.size ?? PROVIDER_NEUTRAL_MODEL
1937
+ };
1938
+ }
1939
+ function resolveAgentAdapter(selection) {
1940
+ if (typeof selection === "function") {
1941
+ return selection;
1942
+ }
1943
+ if (typeof selection === "object" && selection !== null) {
1944
+ return (request) => selection.runAgentStep(request);
1945
+ }
1946
+ throw new TrailStepFailureError({
1947
+ code: "agent_adapter_unavailable",
1948
+ message: "Agent steps that do not use a configured command runner require a custom adapter function or adapter object."
1949
+ });
1950
+ }
1951
+ function createSubmitOutputTool(schema, onOutput) {
1952
+ return {
1953
+ name: "submit_output",
1954
+ description: "Submit the typed output for this TrailStep agent step.",
1955
+ schema,
1956
+ async call(input) {
1957
+ await onOutput(input);
1958
+ }
1959
+ };
1960
+ }
1961
+
1962
+ // src/agent-execution/dispatch-agent-step/dispatch-agent-step.ts
1963
+ import { readFile as readFile5 } from "fs/promises";
1964
+ import { resolve } from "path";
1965
+
1966
+ // src/runtime/artifacts/step-artifacts.ts
1967
+ import { join as join4 } from "path";
1968
+ function resolveStepArtifactPaths(options) {
1969
+ const artifactStepId = `${String(options.stepIndex).padStart(4, "0")}-${sanitizeStepId(
1970
+ options.stepId
1971
+ )}`;
1972
+ const runRelativeStepDir = `steps/${artifactStepId}`;
1973
+ const runRelativeSessionDescriptionFile = `${runRelativeStepDir}/session-description.md`;
1974
+ const stepDir = join4(options.runDir, "steps", artifactStepId);
1975
+ return {
1976
+ artifactStepId,
1977
+ stepDir,
1978
+ promptFile: join4(stepDir, "prompt.txt"),
1979
+ outputFile: join4(stepDir, "output.json"),
1980
+ usageFile: join4(stepDir, "usage.json"),
1981
+ interactiveFile: join4(stepDir, "interactive.json"),
1982
+ sessionDescriptionFile: join4(stepDir, "session-description.md"),
1983
+ runRelativeStepDir,
1984
+ runRelativeSessionDescriptionFile
1985
+ };
1986
+ }
1987
+ function sanitizeStepId(stepId) {
1988
+ return stepId.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "step";
1989
+ }
1990
+
1991
+ // src/runtime/events/create-run-event.ts
1992
+ import { randomUUID } from "crypto";
1993
+ function createEvent(options) {
1994
+ return {
1995
+ id: `evt_${randomUUID()}`,
1996
+ runId: options.runId,
1997
+ workflowId: options.workflowId,
1998
+ stepId: options.stepId,
1999
+ type: options.type,
2000
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2001
+ schemaVersion: "v0",
2002
+ payload: options.payload ?? {}
2003
+ };
2004
+ }
2005
+
2006
+ // src/agent-execution/interactive-agent/run-interactive-agent-command.ts
2007
+ import { spawn as spawn5 } from "child_process";
2008
+ import { mkdir as mkdir2, writeFile as writeFile5 } from "fs/promises";
2009
+ import { dirname as dirname3 } from "path";
2010
+
2011
+ // src/runtime/interactive-session/interactive-session-protocol.ts
2012
+ import { readFile as readFile3 } from "fs/promises";
2013
+ import { setTimeout as delay } from "timers/promises";
2014
+ async function waitForInteractiveCompletion(interactiveFile, signal) {
2015
+ while (!signal.aborted) {
2016
+ if (await isInteractiveCompleted(interactiveFile)) {
2017
+ return;
2018
+ }
2019
+ await delay(100, void 0, { signal }).catch(() => void 0);
2020
+ }
2021
+ }
2022
+ async function isInteractiveCompleted(interactiveFile) {
2023
+ try {
2024
+ const protocol = await readPlainJsonFile(interactiveFile, {
2025
+ readCode: "interactive_session_invalid",
2026
+ invalidCode: "interactive_session_invalid",
2027
+ message: "Interactive session protocol file is invalid."
2028
+ });
2029
+ return protocol.status === "completed" || protocol.status === "cancelled";
2030
+ } catch {
2031
+ return false;
2032
+ }
2033
+ }
2034
+ async function readCompletedInteractiveOutput(options) {
2035
+ const protocol = await readPlainJsonFile(options.interactiveFile, {
2036
+ readCode: "interactive_session_invalid",
2037
+ invalidCode: "interactive_session_invalid",
2038
+ message: `Interactive agent step ${options.stepId} has an invalid interactive.json file.`
2039
+ });
2040
+ if (protocol.status === "cancelled") {
2041
+ throw new TrailStepFailureError({
2042
+ code: "interactive_session_cancelled",
2043
+ message: `Interactive agent step ${options.stepId} was cancelled.`,
2044
+ details: {
2045
+ status: protocol.status,
2046
+ ...typeof protocol.reason === "string" ? { reason: protocol.reason } : {}
2047
+ }
2048
+ });
2049
+ }
2050
+ if (protocol.status !== "completed") {
2051
+ throw new TrailStepFailureError({
2052
+ code: "interactive_session_incomplete",
2053
+ message: `Interactive agent step ${options.stepId} did not complete the file-based protocol.`,
2054
+ details: { status: protocol.status }
2055
+ });
2056
+ }
2057
+ const outputFile = requireProtocolString(protocol.outputFile, "outputFile", options.stepId);
2058
+ const output = await readPlainJsonFile(outputFile, {
2059
+ readCode: "interactive_output_missing",
2060
+ invalidCode: "interactive_output_invalid",
2061
+ message: `Interactive agent step ${options.stepId} completed without a valid output.json file.`
2062
+ });
2063
+ const diagnostics = options.outputSchema.diagnostics(output);
2064
+ if (diagnostics.length > 0) {
2065
+ throw new TrailStepFailureError({
2066
+ code: "interactive_output_invalid",
2067
+ message: `Interactive agent step ${options.stepId} output.json failed schema validation: ${formatDiagnostics3(diagnostics)}`,
2068
+ details: { diagnostics }
2069
+ });
2070
+ }
2071
+ return output;
2072
+ }
2073
+ async function readPlainJsonFile(path, failure) {
2074
+ let contents;
2075
+ try {
2076
+ contents = await readFile3(path, "utf8");
2077
+ } catch (error) {
2078
+ throw new TrailStepFailureError({
2079
+ code: failure.readCode,
2080
+ message: failure.message,
2081
+ details: { path, cause: error instanceof Error ? error.message : String(error) }
2082
+ });
2083
+ }
2084
+ let parsed;
2085
+ try {
2086
+ parsed = JSON.parse(contents);
2087
+ } catch (error) {
2088
+ throw new TrailStepFailureError({
2089
+ code: failure.invalidCode,
2090
+ message: failure.message,
2091
+ details: { path, cause: error instanceof Error ? error.message : String(error) }
2092
+ });
2093
+ }
2094
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2095
+ throw new TrailStepFailureError({
2096
+ code: failure.invalidCode,
2097
+ message: `${failure.message} Expected a plain JSON object.`,
2098
+ details: { path }
2099
+ });
2100
+ }
2101
+ return parsed;
2102
+ }
2103
+ function requireProtocolString(value, field, stepId) {
2104
+ if (typeof value === "string" && value.length > 0) {
2105
+ return value;
2106
+ }
2107
+ throw new TrailStepFailureError({
2108
+ code: "interactive_session_invalid",
2109
+ message: `Interactive agent step ${stepId} interactive.json is missing ${field}.`
2110
+ });
2111
+ }
2112
+ function formatDiagnostics3(diagnostics) {
2113
+ return diagnostics.map((diagnostic) => `${diagnostic.path} ${diagnostic.message}`).join("; ");
2114
+ }
2115
+
2116
+ // src/agent-execution/interactive-agent/run-interactive-agent-command.ts
2117
+ async function runInteractiveAgentCommand(options) {
2118
+ const [target] = resolveAgentTargets({
2119
+ config: options.config,
2120
+ workflowId: options.workflowId,
2121
+ roleName: options.roleName,
2122
+ roleSize: options.role.size
2123
+ });
2124
+ if (!target) {
2125
+ throw new TrailStepFailureError({
2126
+ code: "agent_targets_unavailable",
2127
+ message: `No interactive agent targets found for role ${options.roleName} with size ${options.role.size} in workflow ${options.workflowId}.`
2128
+ });
2129
+ }
2130
+ return await runInteractiveAgentTarget({ ...options, target });
2131
+ }
2132
+ async function runInteractiveAgentTarget(options) {
2133
+ const files = options.artifactPaths;
2134
+ await prepareInteractiveArtifacts({
2135
+ files,
2136
+ runDir: options.runDir,
2137
+ stepId: options.stepId,
2138
+ outputSchema: options.outputSchema,
2139
+ outputMode: options.outputMode
2140
+ });
2141
+ const prompt = buildInteractivePrompt({
2142
+ renderedPrompt: options.renderedPrompt,
2143
+ files,
2144
+ outputSchema: options.outputSchema,
2145
+ outputMode: options.outputMode
2146
+ });
2147
+ const env = { ...definedProcessEnv(), TRAILSTEP_INTERACTIVE_FILE: files.interactiveFile };
2148
+ const abortController = new AbortController();
2149
+ options.signal?.addEventListener("abort", () => abortController.abort(), { once: true });
2150
+ const provider = providerRegistry[options.target.provider];
2151
+ if (provider) {
2152
+ return await runProcessUntilExitOrCompletion({
2153
+ stepId: options.stepId,
2154
+ target: options.target.provider,
2155
+ interactiveFile: files.interactiveFile,
2156
+ abortController,
2157
+ readOutput: async () => await readCompletedOutput(options),
2158
+ runProcess: async () => {
2159
+ await writeFile5(files.promptFile, prompt, "utf8");
2160
+ return await provider.runInteractive(
2161
+ {
2162
+ prompt,
2163
+ systemPromptFile: files.promptFile,
2164
+ cwd: files.stepDir,
2165
+ env,
2166
+ signal: abortController.signal,
2167
+ ...options.target.model === void 0 ? {} : { model: options.target.model },
2168
+ ...options.target.permissionMode === void 0 ? {} : { permissionMode: options.target.permissionMode }
2169
+ },
2170
+ options.runner
2171
+ );
2172
+ }
2173
+ });
2174
+ }
2175
+ const agentConfig = options.config.customProviders[options.target.provider];
2176
+ if (!agentConfig) {
2177
+ throw new TrailStepFailureError({
2178
+ code: "agent_provider_unavailable",
2179
+ message: `Interactive agent target '${options.target.provider}' does not reference a configured custom agent.`,
2180
+ details: { provider: options.target.provider }
2181
+ });
2182
+ }
2183
+ if (!agentConfig.interactiveArgs) {
2184
+ throw new TrailStepFailureError({
2185
+ code: "agent_provider_interactive_unsupported",
2186
+ message: `Custom provider '${options.target.provider}' cannot run interactive agent steps because customProviders.${options.target.provider}.interactiveArgs is not declared.`,
2187
+ details: { provider: options.target.provider }
2188
+ });
2189
+ }
2190
+ const args = await substitutePromptPlaceholders({
2191
+ argv: options.target.args ?? agentConfig.interactiveArgs,
2192
+ prompt,
2193
+ promptFile: files.promptFile,
2194
+ model: options.target.model
2195
+ });
2196
+ return await runProcessUntilExitOrCompletion({
2197
+ stepId: options.stepId,
2198
+ target: options.target.provider,
2199
+ interactiveFile: files.interactiveFile,
2200
+ abortController,
2201
+ readOutput: async () => await readCompletedOutput(options),
2202
+ mapSpawnError: (error) => new TrailStepFailureError({
2203
+ code: "interactive_command_spawn_error",
2204
+ message: `Interactive agent step ${options.stepId} could not start target '${options.target.provider}'.`,
2205
+ details: {
2206
+ target: options.target.provider,
2207
+ ...options.target.model === void 0 ? {} : { model: options.target.model },
2208
+ cause: error instanceof Error ? error.message : String(error)
2209
+ }
2210
+ }),
2211
+ runProcess: async () => await (options.runner ?? spawnInteractiveProcess)({
2212
+ command: agentConfig.binary,
2213
+ args,
2214
+ cwd: files.stepDir,
2215
+ shell: false,
2216
+ stdio: "inherit",
2217
+ env,
2218
+ signal: abortController.signal
2219
+ })
2220
+ });
2221
+ }
2222
+ function definedProcessEnv() {
2223
+ return Object.fromEntries(
2224
+ Object.entries(process.env).filter(
2225
+ (entry) => entry[1] !== void 0
2226
+ )
2227
+ );
2228
+ }
2229
+ async function runProcessUntilExitOrCompletion(options) {
2230
+ const processPromise = options.runProcess();
2231
+ void processPromise.catch(() => void 0);
2232
+ const watcherAbortController = new AbortController();
2233
+ const winner = await Promise.race([
2234
+ processPromise.then(
2235
+ (result) => ({ type: "process", result }),
2236
+ (error) => ({ type: "processError", error })
2237
+ ),
2238
+ waitForInteractiveCompletion(options.interactiveFile, watcherAbortController.signal).then(
2239
+ () => ({ type: "completed" })
2240
+ )
2241
+ ]);
2242
+ watcherAbortController.abort();
2243
+ if (winner.type === "processError") {
2244
+ throw options.mapSpawnError?.(winner.error) ?? winner.error;
2245
+ }
2246
+ if (winner.type === "process") {
2247
+ if (await isInteractiveCompleted(options.interactiveFile)) {
2248
+ return { exitCode: 0, output: await options.readOutput() };
2249
+ }
2250
+ if (winner.result.exitCode !== 0) {
2251
+ throw new TrailStepFailureError({
2252
+ code: "interactive_session_failed",
2253
+ message: `Interactive agent step ${options.stepId} exited with code ${winner.result.exitCode}.`,
2254
+ details: { exitCode: winner.result.exitCode, target: options.target }
2255
+ });
2256
+ }
2257
+ return { exitCode: winner.result.exitCode, output: await options.readOutput() };
2258
+ }
2259
+ options.abortController.abort();
2260
+ return { exitCode: 0, output: await options.readOutput() };
2261
+ }
2262
+ async function prepareInteractiveArtifacts(options) {
2263
+ await mkdir2(options.files.stepDir, { recursive: true });
2264
+ await writeFile5(
2265
+ options.files.interactiveFile,
2266
+ `${JSON.stringify(
2267
+ {
2268
+ status: "active",
2269
+ stepId: options.stepId,
2270
+ artifactStepId: options.files.artifactStepId,
2271
+ outputMode: options.outputMode,
2272
+ runDir: options.runDir,
2273
+ stepDir: options.files.stepDir,
2274
+ promptFile: options.files.promptFile,
2275
+ outputFile: options.files.outputFile,
2276
+ interactiveFile: options.files.interactiveFile,
2277
+ ...options.outputMode === "session-file" ? {
2278
+ sessionDescriptionFile: options.files.sessionDescriptionFile,
2279
+ runRelativeSessionDescriptionFile: options.files.runRelativeSessionDescriptionFile
2280
+ } : {},
2281
+ runRelativeStepDir: options.files.runRelativeStepDir,
2282
+ outputSchema: options.outputSchema.jsonSchema
2283
+ },
2284
+ null,
2285
+ 2
2286
+ )}
2287
+ `,
2288
+ "utf8"
2289
+ );
2290
+ }
2291
+ function buildInteractivePrompt(options) {
2292
+ const preamble = options.outputMode === "session-file" ? [
2293
+ "You are running inside a TrailStep interactive step.",
2294
+ "Write a dense session description to session-description.md in the current directory.",
2295
+ "preserve as much usable context as possible.",
2296
+ "describe the conversation rather than aggressively summarize it.",
2297
+ "include decisions, rejected options, tradeoffs, constraints, side comments, terminology, open questions, assumptions, file paths, commands, APIs, package names, examples, preferences, reasoning, and abandoned options.",
2298
+ "The goal is context preservation, not polish.",
2299
+ "Do not omit low-importance details merely because they seem minor.",
2300
+ "When complete, run: trailstep continue --session-file session-description.md"
2301
+ ] : [
2302
+ "You are running inside a TrailStep interactive step.",
2303
+ "Submit a JSON object matching this schema:",
2304
+ JSON.stringify(options.outputSchema.jsonSchema, null, 2),
2305
+ "When complete, run one of:",
2306
+ " trailstep continue --json-file output.json",
2307
+ ` trailstep continue --json '${JSON.stringify(exampleJsonObject(options.outputSchema.jsonSchema))}'`,
2308
+ "If validation fails, update the JSON so it matches the schema and run trailstep continue again."
2309
+ ];
2310
+ return [...preamble, "", "## Original prompt", options.renderedPrompt].join("\n");
2311
+ }
2312
+ async function readCompletedOutput(options) {
2313
+ return await readCompletedInteractiveOutput({
2314
+ stepId: options.stepId,
2315
+ interactiveFile: options.artifactPaths.interactiveFile,
2316
+ outputSchema: options.outputSchema
2317
+ });
2318
+ }
2319
+ function exampleJsonObject(schema) {
2320
+ const properties = typeof schema.properties === "object" && schema.properties !== null && !Array.isArray(schema.properties) ? schema.properties : {};
2321
+ return Object.fromEntries(
2322
+ Object.entries(properties).map(([key, property]) => {
2323
+ const type = typeof property === "object" && property !== null && "type" in property ? property.type : void 0;
2324
+ if (type === "boolean") {
2325
+ return [key, true];
2326
+ }
2327
+ if (type === "number") {
2328
+ return [key, 1];
2329
+ }
2330
+ return [key, "string"];
2331
+ })
2332
+ );
2333
+ }
2334
+ async function substitutePromptPlaceholders(options) {
2335
+ let needsPromptFile = false;
2336
+ const substituted = options.argv.map((arg) => {
2337
+ if (arg === "{{prompt}}") {
2338
+ return options.prompt;
2339
+ }
2340
+ if (arg === "{{promptFile}}") {
2341
+ needsPromptFile = true;
2342
+ return options.promptFile;
2343
+ }
2344
+ if (arg === "{{model}}") {
2345
+ return options.model ?? "";
2346
+ }
2347
+ if (arg.includes("{{prompt}}") || arg.includes("{{promptFile}}") || arg.includes("{{model}}")) {
2348
+ throw new TrailStepFailureError({
2349
+ code: "interactive_command_invalid",
2350
+ message: "Interactive prompt placeholders must be whole argv values."
2351
+ });
2352
+ }
2353
+ return arg;
2354
+ });
2355
+ if (needsPromptFile) {
2356
+ await mkdir2(dirname3(options.promptFile), { recursive: true });
2357
+ await writeFile5(options.promptFile, options.prompt, "utf8");
2358
+ }
2359
+ return substituted;
2360
+ }
2361
+ var spawnInteractiveProcess = async ({
2362
+ command,
2363
+ args,
2364
+ cwd,
2365
+ env,
2366
+ signal
2367
+ }) => {
2368
+ return await new Promise((resolve2, reject) => {
2369
+ const child = spawn5(command, args, {
2370
+ cwd,
2371
+ env,
2372
+ signal,
2373
+ detached: process.platform !== "win32",
2374
+ shell: false,
2375
+ stdio: "inherit"
2376
+ });
2377
+ signal?.addEventListener("abort", () => terminateChildProcessTree5(child), { once: true });
2378
+ child.on("error", reject);
2379
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
2380
+ });
2381
+ };
2382
+ function terminateChildProcessTree5(child) {
2383
+ if (!child.pid) {
2384
+ return;
2385
+ }
2386
+ if (process.platform === "win32") {
2387
+ spawn5("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
2388
+ "error",
2389
+ () => {
2390
+ child.kill();
2391
+ }
2392
+ );
2393
+ return;
2394
+ }
2395
+ try {
2396
+ process.kill(-child.pid, "SIGTERM");
2397
+ } catch {
2398
+ child.kill();
2399
+ }
2400
+ }
2401
+
2402
+ // src/agent-execution/working-agent/run-working-agent-command.ts
2403
+ import { mkdir as mkdir3, writeFile as writeFile7 } from "fs/promises";
2404
+
2405
+ // src/agent-execution/working-agent/artifacts/resolve-step-agent-files.ts
2406
+ import { join as join5 } from "path";
2407
+ function resolveStepAgentFiles(options) {
2408
+ const artifactPaths = resolveStepArtifactPaths(options);
2409
+ return {
2410
+ stepDir: artifactPaths.stepDir,
2411
+ promptFile: join5(artifactPaths.stepDir, "prompt.md"),
2412
+ outputFile: artifactPaths.outputFile,
2413
+ usageFile: artifactPaths.usageFile
2414
+ };
2415
+ }
2416
+
2417
+ // src/agent-execution/working-agent/prompts/build-working-agent-prompt.ts
2418
+ function buildWorkingAgentPrompt(options) {
2419
+ if (options.captureMode === "raw-text") {
2420
+ return [
2421
+ "# TrailStep working-agent task",
2422
+ "",
2423
+ "Run the task described below and write the document content to the output file.",
2424
+ "Print the document content directly as your entire response \u2014 no JSON wrapper, no surrounding commentary, no markdown fences unless they are literally part of the document content itself.",
2425
+ "",
2426
+ `Output file: ${options.outputFile}`,
2427
+ "",
2428
+ "## Original prompt",
2429
+ "",
2430
+ options.prompt,
2431
+ ""
2432
+ ].join("\n");
2433
+ }
2434
+ return [
2435
+ "# TrailStep working-agent task",
2436
+ "",
2437
+ "Run the task described below and write exactly one JSON object to the output file.",
2438
+ "Do not write prose, markdown fences, or multiple JSON values to the output file.",
2439
+ "",
2440
+ `Output file: ${options.outputFile}`,
2441
+ "",
2442
+ "The JSON object must match this output schema:",
2443
+ "",
2444
+ "```json",
2445
+ JSON.stringify(options.outputSchema, null, 2),
2446
+ "```",
2447
+ "",
2448
+ "## Original prompt",
2449
+ "",
2450
+ options.prompt,
2451
+ ""
2452
+ ].join("\n");
2453
+ }
2454
+
2455
+ // src/agent-execution/working-agent/targets/run-working-agent-target-attempt.ts
2456
+ import { rm } from "fs/promises";
2457
+
2458
+ // src/agent-execution/working-agent/output/read-working-agent-output.ts
2459
+ import { readFile as readFile4 } from "fs/promises";
2460
+ async function readWorkingAgentOutput(options) {
2461
+ let raw;
2462
+ try {
2463
+ raw = await readFile4(options.outputFile, "utf8");
2464
+ } catch (error) {
2465
+ throw new TrailStepFailureError({
2466
+ code: "agent_output_unreadable",
2467
+ message: `Working agent step ${options.stepId} output.json could not be read.`,
2468
+ details: error instanceof Error ? { path: options.outputFile, cause: error.message } : { path: options.outputFile }
2469
+ });
2470
+ }
2471
+ if (options.step.output.captureMode === "raw-text") {
2472
+ const capturedDocument = await document(raw);
2473
+ return options.step.output.assert(capturedDocument, `step ${options.stepId} output`);
2474
+ }
2475
+ let parsed;
2476
+ try {
2477
+ parsed = JSON.parse(raw);
2478
+ } catch (error) {
2479
+ throw new TrailStepFailureError({
2480
+ code: "agent_output_invalid_json",
2481
+ message: `Working agent step ${options.stepId} output.json must contain one JSON object.`,
2482
+ details: error instanceof Error ? { path: options.outputFile, cause: error.message } : { path: options.outputFile }
2483
+ });
2484
+ }
2485
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2486
+ throw new TrailStepFailureError({
2487
+ code: "agent_output_invalid_json",
2488
+ message: `Working agent step ${options.stepId} output.json must contain one JSON object.`,
2489
+ details: { path: options.outputFile }
2490
+ });
2491
+ }
2492
+ return options.step.output.assert(parsed, `step ${options.stepId} output`);
2493
+ }
2494
+
2495
+ // src/agent-execution/working-agent/targets/custom-provider/build-working-agent-args.ts
2496
+ function buildWorkingAgentArgs(options) {
2497
+ if (!options.argv) {
2498
+ return [
2499
+ "--prompt-file",
2500
+ options.promptFile,
2501
+ "--output-file",
2502
+ options.outputFile,
2503
+ ...options.model ? ["--model", options.model] : []
2504
+ ];
2505
+ }
2506
+ return options.argv.map((arg) => {
2507
+ switch (arg) {
2508
+ case "{{promptFile}}":
2509
+ return options.promptFile;
2510
+ case "{{outputFile}}":
2511
+ return options.outputFile;
2512
+ case "{{model}}":
2513
+ return options.model ?? "";
2514
+ default:
2515
+ if (arg.includes("{{promptFile}}") || arg.includes("{{outputFile}}") || arg.includes("{{model}}")) {
2516
+ throw new TrailStepFailureError({
2517
+ code: "agent_provider_invalid",
2518
+ message: "Working agent command placeholders must be whole argv values."
2519
+ });
2520
+ }
2521
+ return arg;
2522
+ }
2523
+ });
2524
+ }
2525
+
2526
+ // src/agent-execution/working-agent/targets/custom-provider/spawn-working-agent-process.ts
2527
+ import { spawn as spawn6 } from "child_process";
2528
+ var spawnWorkingAgentProcess = async ({
2529
+ command,
2530
+ args,
2531
+ cwd,
2532
+ signal
2533
+ }) => {
2534
+ return await new Promise((resolve2, reject) => {
2535
+ const child = spawn6(command, args, {
2536
+ cwd,
2537
+ shell: false,
2538
+ stdio: "inherit",
2539
+ detached: process.platform !== "win32"
2540
+ });
2541
+ signal?.addEventListener("abort", () => terminateChildProcessTree6(child), { once: true });
2542
+ child.on("error", reject);
2543
+ child.on("close", (code) => resolve2({ exitCode: code ?? 1 }));
2544
+ });
2545
+ };
2546
+ function terminateChildProcessTree6(child) {
2547
+ if (!child.pid) {
2548
+ return;
2549
+ }
2550
+ if (process.platform === "win32") {
2551
+ spawn6("taskkill", ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore" }).on(
2552
+ "error",
2553
+ () => {
2554
+ child.kill();
2555
+ }
2556
+ );
2557
+ return;
2558
+ }
2559
+ try {
2560
+ process.kill(-child.pid, "SIGTERM");
2561
+ } catch {
2562
+ child.kill();
2563
+ }
2564
+ }
2565
+
2566
+ // src/agent-execution/working-agent/targets/custom-provider/run-custom-working-provider.ts
2567
+ async function runCustomWorkingProvider(options) {
2568
+ const agentConfig = options.config.customProviders[options.target.provider];
2569
+ if (!agentConfig) {
2570
+ throw new TrailStepFailureError({
2571
+ code: "agent_provider_unavailable",
2572
+ message: `Working agent target '${options.target.provider}' does not reference a configured custom agent.`,
2573
+ details: { provider: options.target.provider }
2574
+ });
2575
+ }
2576
+ const args = buildWorkingAgentArgs({
2577
+ argv: options.target.args ?? agentConfig.args,
2578
+ promptFile: options.files.promptFile,
2579
+ outputFile: options.files.outputFile,
2580
+ model: options.target.model
2581
+ });
2582
+ let result;
2583
+ try {
2584
+ result = await (options.runner ?? spawnWorkingAgentProcess)({
2585
+ command: agentConfig.binary,
2586
+ args,
2587
+ cwd: options.cwd,
2588
+ shell: false,
2589
+ stdio: "inherit",
2590
+ promptFile: options.files.promptFile,
2591
+ outputFile: options.files.outputFile,
2592
+ ...options.target.model === void 0 ? {} : { model: options.target.model },
2593
+ signal: options.signal
2594
+ });
2595
+ } catch (error) {
2596
+ throw new TrailStepFailureError({
2597
+ code: "agent_provider_spawn_error",
2598
+ message: `Working agent step ${options.step.id} could not start target '${options.target.provider}'.`,
2599
+ details: {
2600
+ target: options.target.provider,
2601
+ ...options.target.model === void 0 ? {} : { model: options.target.model },
2602
+ cause: error instanceof Error ? error.message : String(error)
2603
+ }
2604
+ });
2605
+ }
2606
+ if (result.exitCode !== 0) {
2607
+ throw new TrailStepFailureError({
2608
+ code: "agent_provider_failed",
2609
+ message: `Working agent step ${options.step.id} target '${options.target.provider}' exited with code ${result.exitCode}.`,
2610
+ details: {
2611
+ exitCode: result.exitCode,
2612
+ provider: agentConfig.binary,
2613
+ target: options.target.provider,
2614
+ ...options.target.model === void 0 ? {} : { model: options.target.model }
2615
+ }
2616
+ });
2617
+ }
2618
+ return readWorkingAgentOutput({
2619
+ stepId: options.step.id,
2620
+ outputFile: options.files.outputFile,
2621
+ step: options.step
2622
+ });
2623
+ }
2624
+
2625
+ // src/agent-execution/working-agent/targets/registry-provider/run-registry-working-provider.ts
2626
+ import { writeFile as writeFile6 } from "fs/promises";
2627
+
2628
+ // src/agent-execution/working-agent/prompts/build-provider-working-prompt.ts
2629
+ function buildProviderWorkingPrompt(options) {
2630
+ if (options.captureMode === "raw-text") {
2631
+ return [
2632
+ "# TrailStep working-agent task",
2633
+ "",
2634
+ "Print the document content directly as your entire response \u2014 no JSON wrapper, no surrounding commentary, no markdown fences unless they are literally part of the document content itself.",
2635
+ "Do not write output to a file.",
2636
+ "",
2637
+ "## Original prompt",
2638
+ "",
2639
+ options.prompt,
2640
+ ""
2641
+ ].join("\n");
2642
+ }
2643
+ return [
2644
+ "# TrailStep working-agent task",
2645
+ "",
2646
+ "Respond with exactly one JSON object as your entire final answer, and nothing else.",
2647
+ "Do not write output to a file. Do not include prose, markdown fences, or multiple JSON values in your final answer - only the JSON object itself. This instruction applies to your literal final message, not just the work you do to get there.",
2648
+ "",
2649
+ "The JSON object must match this output schema:",
2650
+ "",
2651
+ "```json",
2652
+ JSON.stringify(options.outputSchema, null, 2),
2653
+ "```",
2654
+ "",
2655
+ "## Original prompt",
2656
+ "",
2657
+ options.prompt,
2658
+ ""
2659
+ ].join("\n");
2660
+ }
2661
+
2662
+ // src/agent-execution/working-agent/targets/registry-provider/repair-provider-output.ts
2663
+ var MAX_OUTPUT_REPAIR_ATTEMPTS = 1;
2664
+ async function attemptProviderOutputRepair(options) {
2665
+ if (!options.provider.repairOutput) {
2666
+ return false;
2667
+ }
2668
+ const repairable = extractRepairableFailure(options.error);
2669
+ if (!repairable || MAX_OUTPUT_REPAIR_ATTEMPTS < 1) {
2670
+ return false;
2671
+ }
2672
+ try {
2673
+ await options.provider.repairOutput(
2674
+ {
2675
+ sessionId: repairable.sessionId,
2676
+ rawResultText: repairable.rawResultText ?? "",
2677
+ outputFile: options.files.outputFile,
2678
+ usageFile: options.files.usageFile,
2679
+ cwd: options.cwd,
2680
+ ...options.model === void 0 ? {} : { model: options.model },
2681
+ ...options.thinking === void 0 ? {} : { thinking: options.thinking },
2682
+ outputSchema: options.outputSchema,
2683
+ captureMode: options.captureMode,
2684
+ signal: options.signal
2685
+ },
2686
+ options.providerWorkingRunner
2687
+ );
2688
+ return true;
2689
+ } catch {
2690
+ return false;
2691
+ }
2692
+ }
2693
+ function extractRepairableFailure(error) {
2694
+ if (!(error instanceof TrailStepFailureError)) {
2695
+ return void 0;
2696
+ }
2697
+ if (error.failure.code !== "agent_provider_output_invalid") {
2698
+ return void 0;
2699
+ }
2700
+ const details = error.failure.details;
2701
+ if (typeof details !== "object" || details === null) {
2702
+ return void 0;
2703
+ }
2704
+ const sessionId = details.sessionId;
2705
+ if (typeof sessionId !== "string") {
2706
+ return void 0;
2707
+ }
2708
+ const rawResultText = details.rawResultText;
2709
+ return {
2710
+ sessionId,
2711
+ ...typeof rawResultText === "string" ? { rawResultText } : {}
2712
+ };
2713
+ }
2714
+
2715
+ // src/agent-execution/working-agent/targets/registry-provider/run-registry-working-provider.ts
2716
+ async function runRegistryWorkingProvider(options) {
2717
+ await writeFile6(
2718
+ options.files.promptFile,
2719
+ buildProviderWorkingPrompt({
2720
+ prompt: options.renderedPrompt,
2721
+ outputSchema: options.step.output.jsonSchema,
2722
+ captureMode: options.step.output.captureMode
2723
+ }),
2724
+ "utf8"
2725
+ );
2726
+ const thinking = resolveWorkingThinking(options.target, options.role);
2727
+ try {
2728
+ await options.provider.runWorking(
2729
+ {
2730
+ promptFile: options.files.promptFile,
2731
+ outputFile: options.files.outputFile,
2732
+ usageFile: options.files.usageFile,
2733
+ cwd: options.cwd,
2734
+ ...options.target.model === void 0 ? {} : { model: options.target.model },
2735
+ ...thinking === void 0 ? {} : { thinking },
2736
+ captureMode: options.step.output.captureMode,
2737
+ signal: options.signal
2738
+ },
2739
+ options.providerWorkingRunner
2740
+ );
2741
+ } catch (error) {
2742
+ const repaired = await attemptProviderOutputRepair({
2743
+ provider: options.provider,
2744
+ error,
2745
+ model: options.target.model,
2746
+ thinking,
2747
+ outputSchema: options.step.output.jsonSchema,
2748
+ captureMode: options.step.output.captureMode,
2749
+ files: options.files,
2750
+ cwd: options.cwd,
2751
+ providerWorkingRunner: options.providerWorkingRunner,
2752
+ signal: options.signal
2753
+ });
2754
+ if (!repaired) {
2755
+ throw error;
2756
+ }
2757
+ }
2758
+ return readWorkingAgentOutput({
2759
+ stepId: options.step.id,
2760
+ outputFile: options.files.outputFile,
2761
+ step: options.step
2762
+ });
2763
+ }
2764
+ function resolveWorkingThinking(target, role) {
2765
+ return target.thinking ?? role.thinking;
2766
+ }
2767
+
2768
+ // src/agent-execution/working-agent/targets/run-working-agent-target-attempt.ts
2769
+ async function runWorkingAgentTargetAttempt(options) {
2770
+ await rm(options.files.outputFile, { force: true });
2771
+ await rm(options.files.usageFile, { force: true });
2772
+ const provider = providerRegistry[options.target.provider];
2773
+ if (provider) {
2774
+ return runRegistryWorkingProvider({
2775
+ provider,
2776
+ role: options.role,
2777
+ step: options.step,
2778
+ renderedPrompt: options.renderedPrompt,
2779
+ cwd: options.cwd,
2780
+ providerWorkingRunner: options.providerWorkingRunner,
2781
+ target: options.target,
2782
+ files: options.files,
2783
+ signal: options.signal
2784
+ });
2785
+ }
2786
+ return runCustomWorkingProvider({
2787
+ config: options.config,
2788
+ step: options.step,
2789
+ cwd: options.cwd,
2790
+ runner: options.runner,
2791
+ target: options.target,
2792
+ files: options.files,
2793
+ signal: options.signal
2794
+ });
2795
+ }
2796
+
2797
+ // src/agent-execution/working-agent/targets/target-failures.ts
2798
+ function summarizeWorkingAgentAttemptFailure(target, error) {
2799
+ if (error instanceof TrailStepFailureError) {
2800
+ return {
2801
+ target: target.provider,
2802
+ ...target.model === void 0 ? {} : { model: target.model },
2803
+ code: error.failure.code,
2804
+ message: error.failure.message,
2805
+ ...error.failure.details === void 0 ? {} : { details: error.failure.details }
2806
+ };
2807
+ }
2808
+ return {
2809
+ target: target.provider,
2810
+ ...target.model === void 0 ? {} : { model: target.model },
2811
+ code: "agent_target_failed",
2812
+ message: error instanceof Error ? error.message : "Working agent target failed."
2813
+ };
2814
+ }
2815
+
2816
+ // src/agent-execution/working-agent/run-working-agent-command.ts
2817
+ async function runWorkingAgentCommand(options) {
2818
+ const targets = resolveAgentTargets({
2819
+ config: options.config,
2820
+ workflowId: options.workflowId,
2821
+ roleName: options.roleName,
2822
+ roleSize: options.role.size
2823
+ });
2824
+ const files = options.files ?? resolveStepAgentFiles({
2825
+ runDir: options.runDir,
2826
+ stepId: options.step.id,
2827
+ stepIndex: options.stepIndex
2828
+ });
2829
+ await mkdir3(files.stepDir, { recursive: true });
2830
+ await writeFile7(
2831
+ files.promptFile,
2832
+ buildWorkingAgentPrompt({
2833
+ prompt: options.renderedPrompt,
2834
+ outputFile: files.outputFile,
2835
+ outputSchema: options.step.output.jsonSchema,
2836
+ captureMode: options.step.output.captureMode
2837
+ }),
2838
+ "utf8"
2839
+ );
2840
+ const failures = [];
2841
+ for (const target of targets) {
2842
+ try {
2843
+ return await runWorkingAgentTargetAttempt({ ...options, target, files });
2844
+ } catch (error) {
2845
+ failures.push(summarizeWorkingAgentAttemptFailure(target, error));
2846
+ }
2847
+ }
2848
+ throw new TrailStepFailureError({
2849
+ code: "agent_target_exhausted",
2850
+ message: `Working agent step ${options.step.id} for role ${options.roleName} exhausted ${failures.length} target(s).`,
2851
+ details: {
2852
+ stepId: options.step.id,
2853
+ roleName: options.roleName,
2854
+ attempts: failures
2855
+ }
2856
+ });
2857
+ }
2858
+
2859
+ // src/agent-execution/dispatch-agent-step/dispatch-agent-step.ts
2860
+ async function dispatchAgentStep(options) {
2861
+ const { config, outputSchema } = options;
2862
+ const resolvedRole = resolveWorkflowAgentRole({
2863
+ agent: config.agent,
2864
+ workflowAgents: options.workflowAgents,
2865
+ workflowId: options.workflowId,
2866
+ stepId: config.id
2867
+ });
2868
+ const renderedPrompt = await resolvePromptSource(config.prompt, config.input, options.cwd);
2869
+ const agentMode = config.mode ?? "working";
2870
+ const agentStep = {
2871
+ kind: "agent",
2872
+ id: config.id,
2873
+ output: outputSchema,
2874
+ prompt: renderedPrompt,
2875
+ requirements: resolvedRole.role,
2876
+ adapter: config.adapter
2877
+ };
2878
+ if (!options.trailstepConfig && config.adapter === void 0) {
2879
+ throwMissingAgentConfig({
2880
+ workflowId: options.workflowId,
2881
+ stepId: config.id,
2882
+ agent: resolvedRole.roleName,
2883
+ mode: agentMode
2884
+ });
2885
+ }
2886
+ if (options.trailstepConfig && agentMode === "interactive") {
2887
+ await options.emit(
2888
+ createEvent({
2889
+ runId: options.runId,
2890
+ workflowId: options.workflowId,
2891
+ stepId: config.id,
2892
+ type: "interactive.sessionStarted",
2893
+ payload: { roleName: resolvedRole.roleName, stepIndex: options.stepIndex }
2894
+ })
2895
+ );
2896
+ const artifactPaths = resolveStepArtifactPaths({
2897
+ runDir: options.runDir,
2898
+ stepId: config.id,
2899
+ stepIndex: options.stepIndex
2900
+ });
2901
+ const interactiveResult = await runInteractiveAgentCommand({
2902
+ config: options.trailstepConfig,
2903
+ workflowId: options.workflowId,
2904
+ roleName: resolvedRole.roleName,
2905
+ role: resolvedRole.role,
2906
+ stepId: config.id,
2907
+ renderedPrompt,
2908
+ runDir: options.runDir,
2909
+ runner: options.processRunner,
2910
+ outputSchema,
2911
+ outputMode: options.interactiveOutputMode,
2912
+ artifactPaths,
2913
+ signal: options.signal
2914
+ });
2915
+ await options.emit(
2916
+ createEvent({
2917
+ runId: options.runId,
2918
+ workflowId: options.workflowId,
2919
+ stepId: config.id,
2920
+ type: "interactive.sessionCompleted",
2921
+ payload: {
2922
+ exitCode: interactiveResult.exitCode,
2923
+ outputMode: options.interactiveOutputMode,
2924
+ stepDir: artifactPaths.runRelativeStepDir
2925
+ }
2926
+ })
2927
+ );
2928
+ return interactiveResult.output;
2929
+ }
2930
+ return options.trailstepConfig && agentMode === "working" ? await runWorkingAgentCommand({
2931
+ config: options.trailstepConfig,
2932
+ workflowId: options.workflowId,
2933
+ roleName: resolvedRole.roleName,
2934
+ role: resolvedRole.role,
2935
+ step: agentStep,
2936
+ renderedPrompt,
2937
+ runDir: options.runDir,
2938
+ cwd: options.cwd,
2939
+ runner: options.workingAgentProcessRunner,
2940
+ providerWorkingRunner: options.providerWorkingRunner,
2941
+ stepIndex: options.stepIndex,
2942
+ signal: options.signal
2943
+ }) : await runAgentStep({
2944
+ step: agentStep,
2945
+ input: config.input,
2946
+ onToolCall: async (toolCall) => {
2947
+ await options.emit(
2948
+ createEvent({
2949
+ runId: options.runId,
2950
+ workflowId: options.workflowId,
2951
+ stepId: config.id,
2952
+ type: "agent.toolCall",
2953
+ payload: { ...toolCall }
2954
+ })
2955
+ );
2956
+ }
2957
+ });
2958
+ }
2959
+ async function resolvePromptSource(source, input, cwd) {
2960
+ if (isPromptTemplateSource(source)) {
2961
+ return await readFile5(resolve(cwd, source.path), "utf8");
2962
+ }
2963
+ return renderAgentPrompt(source, input);
2964
+ }
2965
+ function isPromptTemplateSource(source) {
2966
+ return typeof source === "object" && source !== null && source.kind === "promptTemplate";
2967
+ }
2968
+ function throwMissingAgentConfig(options) {
2969
+ throw new TrailStepFailureError({
2970
+ code: "missing_agent_config",
2971
+ message: `Missing .trailstep/config.json: workflow ${options.workflowId} step ${options.stepId} needs configured ${options.mode} agent '${options.agent}'.`,
2972
+ details: {
2973
+ workflowId: options.workflowId,
2974
+ stepId: options.stepId,
2975
+ agent: options.agent,
2976
+ mode: options.mode
2977
+ }
2978
+ });
2979
+ }
2980
+ var DEFAULT_AGENT_ROLE_NAME = "default";
2981
+ var BUILTIN_DEFAULT_AGENT_ROLE = { size: "default" };
2982
+ function resolveWorkflowAgentRole(options) {
2983
+ const roleName = options.agent ?? DEFAULT_AGENT_ROLE_NAME;
2984
+ const role = options.workflowAgents[roleName];
2985
+ if (role) {
2986
+ return { role, roleName };
2987
+ }
2988
+ if (options.agent === void 0) {
2989
+ return { role: BUILTIN_DEFAULT_AGENT_ROLE, roleName: DEFAULT_AGENT_ROLE_NAME };
2990
+ }
2991
+ throw new TrailStepFailureError({
2992
+ code: "agent_role_unknown",
2993
+ message: `Step ${options.stepId} references unknown agent role '${options.agent}' in workflow ${options.workflowId}. Declare workflow.agents.${options.agent} before referencing it with step agent.`,
2994
+ details: {
2995
+ workflowId: options.workflowId,
2996
+ stepId: options.stepId,
2997
+ agent: options.agent,
2998
+ declaredAgents: Object.keys(options.workflowAgents)
2999
+ }
3000
+ });
3001
+ }
3002
+
3003
+ // src/runtime/sub-prompts/sub-prompt-artifacts.ts
3004
+ import { join as join6, relative } from "path";
3005
+ function resolveSubPromptArtifactPaths(options) {
3006
+ const artifactSubPromptId = `${String(options.ordinal).padStart(4, "0")}-${options.fingerprint.slice(0, 12)}`;
3007
+ const subPromptDir = join6(options.stepDir, "subPrompts", artifactSubPromptId);
3008
+ const promptFile = join6(subPromptDir, "prompt.txt");
3009
+ const outputFile = join6(subPromptDir, "output.json");
3010
+ const usageFile = join6(subPromptDir, "usage.json");
3011
+ return {
3012
+ subPromptDir,
3013
+ promptFile,
3014
+ outputFile,
3015
+ usageFile,
3016
+ runRelative: {
3017
+ subPromptDir: toRunRelativePath(options.runDir, subPromptDir),
3018
+ promptFile: toRunRelativePath(options.runDir, promptFile),
3019
+ outputFile: toRunRelativePath(options.runDir, outputFile)
3020
+ }
3021
+ };
3022
+ }
3023
+ function toRunRelativePath(runDir, path) {
3024
+ return relative(runDir, path).replaceAll("\\", "/");
3025
+ }
3026
+
3027
+ // src/runtime/failures/failure-like.ts
3028
+ function isFailureLikeError(error) {
3029
+ return typeof error === "object" && error !== null && "failure" in error && typeof error.failure === "object" && error.failure !== null && "code" in error.failure && typeof error.failure.code === "string" && "message" in error.failure && typeof error.failure.message === "string";
3030
+ }
3031
+
3032
+ // src/runtime/sub-prompts/sub-prompt-failures.ts
3033
+ async function emitSubPromptFailed(options) {
3034
+ await options.context.emit?.(
3035
+ createEvent({
3036
+ runId: options.context.id,
3037
+ workflowId: options.context.workflowId ?? "",
3038
+ stepId: options.parentStepId,
3039
+ type: "subPrompt.failed",
3040
+ payload: {
3041
+ parentStepId: options.parentStepId,
3042
+ ordinal: options.ordinal,
3043
+ ...options.fingerprint === void 0 ? {} : { fingerprint: options.fingerprint },
3044
+ ...options.artifactPaths === void 0 ? {} : { artifactPaths: options.artifactPaths },
3045
+ failure: options.failure
3046
+ }
3047
+ })
3048
+ );
3049
+ }
3050
+ function maxSubPromptsGuardError(maxSubPrompts) {
3051
+ return new TrailStepFailureError({
3052
+ code: "max_sub_prompts_exceeded",
3053
+ message: `workflow exceeded maxSubPrompts guard (${maxSubPrompts})`
3054
+ });
3055
+ }
3056
+ function throwMissingSubPromptAgentConfig(options) {
3057
+ throw new TrailStepFailureError({
3058
+ code: "missing_agent_config",
3059
+ message: `Missing .trailstep/config.json: workflow ${options.workflowId} parent step ${options.parentStepId} subPrompt ${options.subPromptId} needs configured working agent '${options.agent}'.`,
3060
+ details: {
3061
+ workflowId: options.workflowId,
3062
+ parentStepId: options.parentStepId,
3063
+ subPromptId: options.subPromptId,
3064
+ agent: options.agent,
3065
+ mode: "working"
3066
+ }
3067
+ });
3068
+ }
3069
+ function subPromptFailureError(error) {
3070
+ if (error instanceof TrailStepFailureError) {
3071
+ if (error.failure.code === "validation_failed") {
3072
+ return new TrailStepFailureError({
3073
+ ...error.failure,
3074
+ message: normalizeSubPromptValidationMessage(error.failure.message)
3075
+ });
3076
+ }
3077
+ return error;
3078
+ }
3079
+ if (isFailureLikeError(error)) {
3080
+ return new TrailStepFailureError(error.failure);
3081
+ }
3082
+ return new TrailStepFailureError({
3083
+ code: "sub_prompt_failed",
3084
+ message: error instanceof Error ? error.message : "subPrompt failed.",
3085
+ ...error instanceof Error ? { details: { name: error.name } } : error === void 0 ? {} : { details: { cause: error } }
3086
+ });
3087
+ }
3088
+ function normalizeSubPromptValidationMessage(message) {
3089
+ if (message.startsWith("subPrompt output failed schema validation")) {
3090
+ return message;
3091
+ }
3092
+ const diagnostics = message.match(/failed schema validation: (.*)$/)?.[1];
3093
+ return diagnostics === void 0 ? `subPrompt output failed schema validation: ${message}` : `subPrompt output failed schema validation: ${diagnostics}`;
3094
+ }
3095
+
3096
+ // src/runtime/sub-prompts/sub-prompt-fingerprint.ts
3097
+ import { createHash } from "crypto";
3098
+ function fingerprintSubPrompt(options) {
3099
+ return createHash("sha256").update(stableJsonStringify(options)).digest("hex");
3100
+ }
3101
+ function stableJsonStringify(value) {
3102
+ if (Array.isArray(value)) {
3103
+ return `[${value.map((item) => stableJsonStringify(item)).join(",")}]`;
3104
+ }
3105
+ if (isPlainObject4(value)) {
3106
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
3107
+ return `{${entries.map(([key, entryValue]) => `${JSON.stringify(key)}:${stableJsonStringify(entryValue)}`).join(",")}}`;
3108
+ }
3109
+ return JSON.stringify(value);
3110
+ }
3111
+ function isPlainObject4(value) {
3112
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
3113
+ }
3114
+
3115
+ // src/runtime/sub-prompts/sub-prompt-replay.ts
3116
+ function findCompletedSubPromptEvent(events, parentStepId, ordinal, fingerprint) {
3117
+ for (let index = events.length - 1; index >= 0; index -= 1) {
3118
+ const event = events[index];
3119
+ if (event?.type !== "subPrompt.completed") {
3120
+ continue;
3121
+ }
3122
+ const payload = event.payload;
3123
+ if (payload.parentStepId === parentStepId && payload.ordinal === ordinal && payload.fingerprint === fingerprint && isPlainObject5(payload.output)) {
3124
+ return event;
3125
+ }
3126
+ }
3127
+ return void 0;
3128
+ }
3129
+ function isPlainObject5(value) {
3130
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
3131
+ }
3132
+
3133
+ // src/runtime/sub-prompts/run-sub-prompt.ts
3134
+ function resolveMaxSubPrompts(value) {
3135
+ if (value === void 0) {
3136
+ return 25;
3137
+ }
3138
+ if (Number.isInteger(value) && typeof value === "number" && value > 0) {
3139
+ return value;
3140
+ }
3141
+ throw new TrailStepFailureError({
3142
+ code: "invalid_max_sub_prompts",
3143
+ message: "maxSubPrompts must be a positive integer."
3144
+ });
3145
+ }
3146
+ async function runSubPrompt(source, options, input) {
3147
+ const resolvedInput = input ?? {};
3148
+ const context = runContextStorage.getStore();
3149
+ if (!context?.currentStep) {
3150
+ throw new TrailStepFailureError({
3151
+ code: "sub_prompt_outside_step_context",
3152
+ message: "subPrompt requires an active TrailStep step run context."
3153
+ });
3154
+ }
3155
+ if (!context.workflowId || !context.workflowAgents || !context.cwd || !context.emit) {
3156
+ throw new TrailStepFailureError({
3157
+ code: "sub_prompt_context_unavailable",
3158
+ message: "subPrompt requires a full active TrailStep step run context."
3159
+ });
3160
+ }
3161
+ const parentStep = context.currentStep;
3162
+ if (options.output === void 0) {
3163
+ throw new TrailStepFailureError({
3164
+ code: "sub_prompt_output_required",
3165
+ message: `subPrompt in step ${parentStep.id} with a prompt requires an output shape`
3166
+ });
3167
+ }
3168
+ const outputSchema = normalizeShape(options.output);
3169
+ const ordinal = parentStep.nextSubPromptIndex();
3170
+ const maxSubPrompts = resolveMaxSubPrompts(options.maxSubPrompts ?? parentStep.maxSubPrompts);
3171
+ if (ordinal > maxSubPrompts) {
3172
+ const failureError = maxSubPromptsGuardError(maxSubPrompts);
3173
+ await emitSubPromptFailed({
3174
+ context,
3175
+ parentStepId: parentStep.id,
3176
+ ordinal,
3177
+ failure: failureError.failure
3178
+ });
3179
+ throw failureError;
3180
+ }
3181
+ const renderedPrompt = await resolvePromptSource(source, resolvedInput, context.cwd);
3182
+ const fingerprint = fingerprintSubPrompt({ input: resolvedInput, prompt: renderedPrompt });
3183
+ const cachedEvent = findCompletedSubPromptEvent(
3184
+ context.events?.() ?? [],
3185
+ parentStep.id,
3186
+ ordinal,
3187
+ fingerprint
3188
+ );
3189
+ if (cachedEvent) {
3190
+ return outputSchema.assert(cachedEvent.payload.output, "subPrompt output");
3191
+ }
3192
+ const artifactPaths = resolveSubPromptArtifactPaths({
3193
+ runDir: context.path,
3194
+ stepDir: parentStep.dir,
3195
+ ordinal,
3196
+ fingerprint
3197
+ });
3198
+ await mkdir4(artifactPaths.subPromptDir, { recursive: true });
3199
+ await writeFile8(artifactPaths.promptFile, renderedPrompt, "utf8");
3200
+ const resolvedRole = resolveWorkflowAgentRole({
3201
+ agent: options.agent,
3202
+ workflowAgents: context.workflowAgents,
3203
+ workflowId: context.workflowId,
3204
+ stepId: parentStep.id
3205
+ });
3206
+ await context.emit(
3207
+ createEvent({
3208
+ runId: context.id,
3209
+ workflowId: context.workflowId,
3210
+ stepId: parentStep.id,
3211
+ type: "subPrompt.started",
3212
+ payload: {
3213
+ parentStepId: parentStep.id,
3214
+ ordinal,
3215
+ fingerprint,
3216
+ artifactPaths: artifactPaths.runRelative
3217
+ }
3218
+ })
3219
+ );
3220
+ try {
3221
+ const agentStep = {
3222
+ kind: "agent",
3223
+ id: `${parentStep.id}.subPrompt.${ordinal}`,
3224
+ output: outputSchema,
3225
+ prompt: renderedPrompt,
3226
+ requirements: resolvedRole.role,
3227
+ adapter: options.adapter
3228
+ };
3229
+ if (!context.trailstepConfig && options.adapter === void 0) {
3230
+ throwMissingSubPromptAgentConfig({
3231
+ workflowId: context.workflowId,
3232
+ parentStepId: parentStep.id,
3233
+ subPromptId: agentStep.id,
3234
+ agent: resolvedRole.roleName
3235
+ });
3236
+ }
3237
+ const rawOutput = context.trailstepConfig && options.adapter === void 0 ? await runWorkingAgentCommand({
3238
+ config: context.trailstepConfig,
3239
+ workflowId: context.workflowId,
3240
+ roleName: resolvedRole.roleName,
3241
+ role: resolvedRole.role,
3242
+ step: agentStep,
3243
+ renderedPrompt,
3244
+ runDir: context.path,
3245
+ cwd: context.cwd,
3246
+ runner: context.workingAgentProcessRunner,
3247
+ providerWorkingRunner: context.providerWorkingRunner,
3248
+ stepIndex: ordinal,
3249
+ files: {
3250
+ stepDir: artifactPaths.subPromptDir,
3251
+ promptFile: artifactPaths.promptFile,
3252
+ outputFile: artifactPaths.outputFile,
3253
+ usageFile: artifactPaths.usageFile
3254
+ }
3255
+ }) : await runAgentStep({
3256
+ step: agentStep,
3257
+ input: resolvedInput,
3258
+ onToolCall: async (toolCall) => {
3259
+ await context.emit?.(
3260
+ createEvent({
3261
+ runId: context.id,
3262
+ workflowId: context.workflowId ?? "",
3263
+ stepId: parentStep.id,
3264
+ type: "agent.toolCall",
3265
+ payload: { ...toolCall }
3266
+ })
3267
+ );
3268
+ }
3269
+ });
3270
+ const output = outputSchema.assert(rawOutput, "subPrompt output");
3271
+ await writeFile8(artifactPaths.outputFile, `${JSON.stringify(output, null, 2)}
3272
+ `, "utf8");
3273
+ await context.emit(
3274
+ createEvent({
3275
+ runId: context.id,
3276
+ workflowId: context.workflowId,
3277
+ stepId: parentStep.id,
3278
+ type: "subPrompt.completed",
3279
+ payload: {
3280
+ parentStepId: parentStep.id,
3281
+ ordinal,
3282
+ fingerprint,
3283
+ output,
3284
+ artifactPaths: artifactPaths.runRelative
3285
+ }
3286
+ })
3287
+ );
3288
+ return output;
3289
+ } catch (error) {
3290
+ const failureError = subPromptFailureError(error);
3291
+ await emitSubPromptFailed({
3292
+ context,
3293
+ parentStepId: parentStep.id,
3294
+ ordinal,
3295
+ fingerprint,
3296
+ artifactPaths: artifactPaths.runRelative,
3297
+ failure: failureError.failure
3298
+ });
3299
+ throw failureError;
3300
+ }
3301
+ }
3302
+
3303
+ // src/authoring/step/sub-prompt.ts
3304
+ function subPrompt(source, options) {
3305
+ const run = (input) => runSubPrompt(source, options, input);
3306
+ return run;
3307
+ }
3308
+
3309
+ // src/deprecations/deprecation-manifest.ts
3310
+ import { lte } from "semver";
3311
+ var deprecationManifest = [];
3312
+ function findDeprecationsAsOf(manifest, query) {
3313
+ return manifest.filter((entry) => entry.package === query.package && lte(entry.deprecatedSince, query.version)).map((entry) => toDeprecationStatus(entry, query.version));
3314
+ }
3315
+ function toDeprecationStatus(entry, version) {
3316
+ const isRemoved = entry.removedIn !== void 0 && lte(entry.removedIn, version);
3317
+ return { ...entry, severity: isRemoved ? "blocking" : "warning" };
3318
+ }
3319
+
3320
+ // src/runtime/retry/latest-unresolved-failure.ts
3321
+ function selectLatestUnresolvedFailure(events) {
3322
+ const startedEvent = events.find((event) => event.type === "workflow.started");
3323
+ const workflowInput = startedEvent ? readPlainPayload(startedEvent, "input") : void 0;
3324
+ const resolvedFailures = events.flatMap(
3325
+ (event) => event.type === "workflow.retryStarted" ? readResolvedFailureIdentity(event) : []
3326
+ );
3327
+ let workflowFailureFallback;
3328
+ let laterWorkflowTerminalSeen = false;
3329
+ const laterTerminalStepIds = /* @__PURE__ */ new Set();
3330
+ for (let replayPosition = events.length - 1; replayPosition >= 0; replayPosition -= 1) {
3331
+ const event = events[replayPosition];
3332
+ if (!event) {
3333
+ continue;
3334
+ }
3335
+ if (event.type === "workflow.completed") {
3336
+ return void 0;
3337
+ }
3338
+ if (event.type === "step.completed") {
3339
+ rememberTerminalStep(event, laterTerminalStepIds);
3340
+ continue;
3341
+ }
3342
+ if (event.type === "step.failed") {
3343
+ rememberTerminalStep(event, laterTerminalStepIds);
3344
+ if (isResolvedFailure(event, replayPosition, resolvedFailures)) {
3345
+ continue;
3346
+ }
3347
+ return toLatestUnresolvedFailure(event, replayPosition, workflowInput);
3348
+ }
3349
+ if (event.type === "workflow.failed") {
3350
+ laterWorkflowTerminalSeen = true;
3351
+ if (!workflowFailureFallback) {
3352
+ if (isResolvedFailure(event, replayPosition, resolvedFailures)) {
3353
+ continue;
3354
+ }
3355
+ workflowFailureFallback = toLatestUnresolvedFailure(event, replayPosition, workflowInput);
3356
+ }
3357
+ continue;
3358
+ }
3359
+ if (event.type === "step.started" && event.stepId && !laterWorkflowTerminalSeen && !laterTerminalStepIds.has(event.stepId)) {
3360
+ if (isResolvedFailure(event, replayPosition, resolvedFailures)) {
3361
+ continue;
3362
+ }
3363
+ return toLatestUnresolvedFailure(event, replayPosition, workflowInput);
3364
+ }
3365
+ }
3366
+ return workflowFailureFallback;
3367
+ }
3368
+ function rememberTerminalStep(event, stepIds) {
3369
+ if (event.stepId) {
3370
+ stepIds.add(event.stepId);
3371
+ }
3372
+ }
3373
+ function readResolvedFailureIdentity(event) {
3374
+ const { sourceFailureEventId, sourceFailureReplayPosition } = event.payload;
3375
+ if (typeof sourceFailureEventId !== "string" && typeof sourceFailureReplayPosition !== "number") {
3376
+ return [];
3377
+ }
3378
+ return [
3379
+ {
3380
+ eventId: typeof sourceFailureEventId === "string" ? sourceFailureEventId : void 0,
3381
+ replayPosition: typeof sourceFailureReplayPosition === "number" ? sourceFailureReplayPosition : void 0
3382
+ }
3383
+ ];
3384
+ }
3385
+ function isResolvedFailure(event, replayPosition, resolvedFailures) {
3386
+ return resolvedFailures.some((resolvedFailure) => {
3387
+ if (resolvedFailure.replayPosition !== void 0) {
3388
+ return resolvedFailure.replayPosition === replayPosition;
3389
+ }
3390
+ return resolvedFailure.eventId !== void 0 && resolvedFailure.eventId === event.id;
3391
+ });
3392
+ }
3393
+ function toLatestUnresolvedFailure(event, replayPosition, workflowInput) {
3394
+ return {
3395
+ event,
3396
+ replayPosition,
3397
+ sourceFailureEventId: typeof event.id === "string" ? event.id : void 0,
3398
+ workflowId: event.workflowId,
3399
+ workflowInput,
3400
+ stepId: event.stepId
3401
+ };
3402
+ }
3403
+ function readPlainPayload(event, key) {
3404
+ const value = event.payload[key];
3405
+ return isPlainObject6(value) ? value : void 0;
3406
+ }
3407
+ function isPlainObject6(value) {
3408
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
3409
+ }
3410
+
3411
+ // src/runtime/retry/retry-policy.ts
3412
+ var BUILT_IN_RETRY_POLICY = { maxAttempts: 2 };
3413
+ function resolveRetryPolicy(options) {
3414
+ return validateRetryPolicy(
3415
+ firstConfiguredPolicy(options.step, options.workflow, options.global) ?? BUILT_IN_RETRY_POLICY
3416
+ );
3417
+ }
3418
+ function validateRetryPolicy(input) {
3419
+ const maxAttempts = input.maxAttempts ?? BUILT_IN_RETRY_POLICY.maxAttempts;
3420
+ if (typeof maxAttempts !== "number" || !Number.isInteger(maxAttempts) || maxAttempts < 1) {
3421
+ throw new TypeError("retry.maxAttempts must be an integer greater than or equal to 1.");
3422
+ }
3423
+ return { maxAttempts };
3424
+ }
3425
+ function firstConfiguredPolicy(...policies) {
3426
+ return policies.find((policy) => policy?.maxAttempts !== void 0);
3427
+ }
3428
+
3429
+ // src/runtime/run-workflow/run-workflow.ts
3430
+ import { basename as basename3 } from "path";
3431
+
3432
+ // src/runtime/failures/step-execution-failure.ts
3433
+ function stepExecutionFailure(error) {
3434
+ if (isFailureLikeError(error)) {
3435
+ return error.failure;
3436
+ }
3437
+ return {
3438
+ code: "step_execution_failed",
3439
+ message: error instanceof Error ? error.message : "Step execution failed.",
3440
+ ...error instanceof Error ? {
3441
+ details: {
3442
+ name: error.name
3443
+ }
3444
+ } : error === void 0 ? {} : { details: { cause: error } }
3445
+ };
3446
+ }
3447
+
3448
+ // src/runtime/run-context/with-step-context.ts
3449
+ async function withStepContext(stepId, stepDir, fn, options = {}) {
3450
+ const parentContext = runContextStorage.getStore();
3451
+ if (!parentContext) {
3452
+ return await fn();
3453
+ }
3454
+ let nextDocumentIndex = 0;
3455
+ let nextSubPromptIndex = 0;
3456
+ const stepContext = {
3457
+ ...parentContext,
3458
+ currentStep: {
3459
+ id: stepId,
3460
+ dir: stepDir,
3461
+ maxSubPrompts: options.maxSubPrompts,
3462
+ nextDocumentIndex() {
3463
+ nextDocumentIndex += 1;
3464
+ return nextDocumentIndex;
3465
+ },
3466
+ nextSubPromptIndex() {
3467
+ nextSubPromptIndex += 1;
3468
+ return nextSubPromptIndex;
3469
+ }
3470
+ }
3471
+ };
3472
+ return await runContextStorage.run(stepContext, fn);
3473
+ }
3474
+
3475
+ // src/runtime/timeout/timeout-policy.ts
3476
+ var BUILT_IN_TIMEOUT_POLICY = {};
3477
+ function resolveTimeoutPolicy(options) {
3478
+ const configured = firstConfiguredPolicy2(options.step, options.workflow, options.global);
3479
+ return configured !== void 0 ? validateTimeoutPolicy(configured) : BUILT_IN_TIMEOUT_POLICY;
3480
+ }
3481
+ function validateTimeoutPolicy(input) {
3482
+ if (typeof input !== "number" || !Number.isInteger(input) || input < 1) {
3483
+ throw new TypeError(
3484
+ "timeout must be an integer number of milliseconds greater than or equal to 1."
3485
+ );
3486
+ }
3487
+ return { timeoutMs: input };
3488
+ }
3489
+ function firstConfiguredPolicy2(...policies) {
3490
+ return policies.find((policy) => policy !== void 0);
3491
+ }
3492
+
3493
+ // src/runtime/interactive-session/default-interactive-output-shape.ts
3494
+ var DEFAULT_INTERACTIVE_OUTPUT_SHAPE = { sessionFile: "string" };
3495
+
3496
+ // src/runtime/continuation/resolve-step-output-schema/resolve-step-output-schema.ts
3497
+ function resolveStepOutputSchema(config) {
3498
+ const effectiveOutput = config.output ?? (config.mode === "interactive" ? DEFAULT_INTERACTIVE_OUTPUT_SHAPE : void 0);
3499
+ return effectiveOutput ? normalizeShape(effectiveOutput) : void 0;
3500
+ }
3501
+
3502
+ // src/runtime/continuation/run-continuation/run-continuation.ts
3503
+ async function runContinuation(options) {
3504
+ let node = options.node;
3505
+ let source = options.initialSource;
3506
+ let executedSteps = options.initialExecutedSteps ?? 0;
3507
+ const trailstepConfig = options.trailstepConfig;
3508
+ while (true) {
3509
+ if (isDoneNode(node)) {
3510
+ return { status: "success", output: node.output };
3511
+ }
3512
+ if (isFailNode(node)) {
3513
+ return { status: "failure", failure: node.failure };
3514
+ }
3515
+ if (!isStepNode(node)) {
3516
+ return {
3517
+ status: "failure",
3518
+ failure: continuationFailure(source)
3519
+ };
3520
+ }
3521
+ if (executedSteps >= options.maxSteps) {
3522
+ return {
3523
+ status: "failure",
3524
+ failure: stepExecutionFailure(
3525
+ new Error(`workflow exceeded maxSteps guard (${options.maxSteps})`)
3526
+ )
3527
+ };
3528
+ }
3529
+ executedSteps += 1;
3530
+ const stepIndex = executedSteps;
3531
+ const stepNode = node;
3532
+ const { config } = stepNode;
3533
+ const hasPrompt = config.prompt !== void 0;
3534
+ const timeoutPolicy = resolveTimeoutPolicy({
3535
+ global: trailstepConfig?.settings?.timeout,
3536
+ workflow: options.workflowTimeout ?? trailstepConfig?.workflows?.[options.workflowId]?.settings?.timeout,
3537
+ step: config.timeout
3538
+ });
3539
+ const maxSubPrompts = config.maxSubPrompts ?? trailstepConfig?.workflows?.[options.workflowId]?.settings?.maxSubPrompts;
3540
+ await options.emit(
3541
+ createEvent({
3542
+ runId: options.runId,
3543
+ workflowId: options.workflowId,
3544
+ stepId: config.id,
3545
+ type: "step.started",
3546
+ payload: { stepName: config.id, kind: hasPrompt ? "agent" : "code" }
3547
+ })
3548
+ );
3549
+ try {
3550
+ const stepDir = resolveStepArtifactPaths({
3551
+ runDir: options.runDir,
3552
+ stepId: config.id,
3553
+ stepIndex
3554
+ }).stepDir;
3555
+ const nextNode = await runWithStepTimeout({
3556
+ stepId: config.id,
3557
+ timeoutMs: timeoutPolicy.timeoutMs,
3558
+ run: async (signal) => await withStepContext(
3559
+ config.id,
3560
+ stepDir,
3561
+ async () => {
3562
+ let paramForNext;
3563
+ if (hasPrompt) {
3564
+ const outputSchema = resolveStepOutputSchema(config);
3565
+ if (!outputSchema) {
3566
+ throw new Error(`step ${config.id} with a prompt requires an output shape`);
3567
+ }
3568
+ const rawOutput = await dispatchAgentStep({
3569
+ config,
3570
+ outputSchema,
3571
+ interactiveOutputMode: config.mode === "interactive" && config.output !== void 0 ? "json" : "session-file",
3572
+ runId: options.runId,
3573
+ workflowId: options.workflowId,
3574
+ emit: options.emit,
3575
+ workflowAgents: options.workflowAgents,
3576
+ runDir: options.runDir,
3577
+ cwd: options.cwd,
3578
+ trailstepConfig,
3579
+ workingAgentProcessRunner: options.workingAgentProcessRunner,
3580
+ providerWorkingRunner: options.providerWorkingRunner,
3581
+ processRunner: options.processRunner,
3582
+ stepIndex,
3583
+ signal
3584
+ });
3585
+ throwIfStepTimedOut(signal, config.id, timeoutPolicy.timeoutMs);
3586
+ paramForNext = outputSchema.assert(rawOutput, `step ${config.id} output`);
3587
+ await options.emit(
3588
+ createEvent({
3589
+ runId: options.runId,
3590
+ workflowId: options.workflowId,
3591
+ stepId: config.id,
3592
+ type: "step.completed",
3593
+ payload: { output: paramForNext }
3594
+ })
3595
+ );
3596
+ } else {
3597
+ paramForNext = config.input;
3598
+ }
3599
+ const nextNode2 = await stepNode.onOutput(paramForNext, config.input);
3600
+ throwIfStepTimedOut(signal, config.id, timeoutPolicy.timeoutMs);
3601
+ if (isFailNode(nextNode2)) {
3602
+ await options.emit(
3603
+ createEvent({
3604
+ runId: options.runId,
3605
+ workflowId: options.workflowId,
3606
+ stepId: config.id,
3607
+ type: "step.failed",
3608
+ payload: { failure: nextNode2.failure }
3609
+ })
3610
+ );
3611
+ return nextNode2;
3612
+ }
3613
+ if (!hasPrompt) {
3614
+ await options.emit(
3615
+ createEvent({
3616
+ runId: options.runId,
3617
+ workflowId: options.workflowId,
3618
+ stepId: config.id,
3619
+ type: "step.completed",
3620
+ payload: {}
3621
+ })
3622
+ );
3623
+ }
3624
+ throwIfStepTimedOut(signal, config.id, timeoutPolicy.timeoutMs);
3625
+ return nextNode2;
3626
+ },
3627
+ { maxSubPrompts }
3628
+ )
3629
+ });
3630
+ if (!isStepNode(nextNode) && !isDoneNode(nextNode) && !isFailNode(nextNode)) {
3631
+ const failure = continuationFailure(`step ${config.id}`);
3632
+ await options.emit(
3633
+ createEvent({
3634
+ runId: options.runId,
3635
+ workflowId: options.workflowId,
3636
+ stepId: config.id,
3637
+ type: "step.failed",
3638
+ payload: { failure }
3639
+ })
3640
+ );
3641
+ return { status: "failure", failure };
3642
+ }
3643
+ if (isFailNode(nextNode)) {
3644
+ return { status: "failure", failure: nextNode.failure };
3645
+ }
3646
+ node = nextNode;
3647
+ source = `step ${config.id}`;
3648
+ } catch (error) {
3649
+ const failure = stepExecutionFailure(error);
3650
+ await options.emit(
3651
+ createEvent({
3652
+ runId: options.runId,
3653
+ workflowId: options.workflowId,
3654
+ stepId: config.id,
3655
+ type: "step.failed",
3656
+ payload: { failure }
3657
+ })
3658
+ );
3659
+ if (!stepNode.onError) {
3660
+ return { status: "failure", failure };
3661
+ }
3662
+ try {
3663
+ const nextNode = stepNode.onError(failure);
3664
+ if (!isStepNode(nextNode) && !isDoneNode(nextNode) && !isFailNode(nextNode)) {
3665
+ return {
3666
+ status: "failure",
3667
+ failure: continuationFailure(`error continuation for step ${config.id}`)
3668
+ };
3669
+ }
3670
+ node = nextNode;
3671
+ source = `error continuation for step ${config.id}`;
3672
+ } catch (errorContinuationError) {
3673
+ return {
3674
+ status: "failure",
3675
+ failure: stepExecutionFailure(
3676
+ new Error(
3677
+ `error continuation for step ${config.id} failed: ${errorMessage(errorContinuationError)}`
3678
+ )
3679
+ )
3680
+ };
3681
+ }
3682
+ }
3683
+ }
3684
+ }
3685
+ async function runWithStepTimeout(options) {
3686
+ if (options.timeoutMs === void 0) {
3687
+ return await options.run();
3688
+ }
3689
+ const timeoutMs = options.timeoutMs;
3690
+ const abortController = new AbortController();
3691
+ let timeout;
3692
+ const timeoutPromise = new Promise((_, reject) => {
3693
+ timeout = setTimeout(() => {
3694
+ abortController.abort();
3695
+ reject(stepTimeoutFailure(options.stepId, timeoutMs));
3696
+ }, timeoutMs);
3697
+ });
3698
+ try {
3699
+ return await Promise.race([options.run(abortController.signal), timeoutPromise]);
3700
+ } finally {
3701
+ if (timeout !== void 0) {
3702
+ clearTimeout(timeout);
3703
+ }
3704
+ }
3705
+ }
3706
+ function throwIfStepTimedOut(signal, stepId, timeoutMs) {
3707
+ if (signal?.aborted && timeoutMs !== void 0) {
3708
+ throw stepTimeoutFailure(stepId, timeoutMs);
3709
+ }
3710
+ }
3711
+ function stepTimeoutFailure(stepId, timeoutMs) {
3712
+ return new TrailStepFailureError({
3713
+ code: "step_timeout",
3714
+ message: `Step ${stepId} timed out after ${timeoutMs}ms.`,
3715
+ details: { stepId, timeoutMs }
3716
+ });
3717
+ }
3718
+ function errorMessage(error) {
3719
+ if (error instanceof TrailStepFailureError) {
3720
+ return error.message;
3721
+ }
3722
+ if (error instanceof Error) {
3723
+ return error.message;
3724
+ }
3725
+ return "Error continuation failed.";
3726
+ }
3727
+ function continuationFailure(source) {
3728
+ return {
3729
+ code: "invalid_continuation",
3730
+ message: `${source} returned an invalid continuation node.`
3731
+ };
3732
+ }
3733
+
3734
+ // src/runtime/failures/workflow-failure.ts
3735
+ function workflowFailure(error) {
3736
+ if (isFailureLikeError(error)) {
3737
+ return error.failure;
3738
+ }
3739
+ return {
3740
+ code: "workflow_failed",
3741
+ message: error instanceof Error ? error.message : "Unknown workflow failure.",
3742
+ ...error === void 0 ? {} : { details: { cause: error } }
3743
+ };
3744
+ }
3745
+
3746
+ // src/runtime/resume/replay-completed-steps/replay-completed-steps.ts
3747
+ async function replayCompletedSteps(options) {
3748
+ let node = options.workflow.start(options.input);
3749
+ const completedStepEvents = options.events.filter((event) => event.type === "step.completed");
3750
+ for (const [completedIndex, completedEvent] of completedStepEvents.entries()) {
3751
+ const stepIndex = completedIndex + 1;
3752
+ if (!isStepNode(node)) {
3753
+ return {
3754
+ status: "failure",
3755
+ failure: replayFailure(
3756
+ "resume_step_id_drift",
3757
+ "Completed history continues after the current workflow reaches done."
3758
+ )
3759
+ };
3760
+ }
3761
+ if (node.config.id !== completedEvent.stepId) {
3762
+ return {
3763
+ status: "failure",
3764
+ failure: replayFailure(
3765
+ "resume_step_id_drift",
3766
+ `Expected completed step ${node.config.id} but found ${completedEvent.stepId ?? "<missing>"}.`
3767
+ )
3768
+ };
3769
+ }
3770
+ if (node.config.prompt !== void 0) {
3771
+ const recordedOutput = readPlainPayload2(completedEvent, "output");
3772
+ if (!recordedOutput) {
3773
+ return {
3774
+ status: "failure",
3775
+ failure: replayFailure(
3776
+ "resume_missing_step_output",
3777
+ `Completed step ${node.config.id} has no recorded output to resume from.`
3778
+ )
3779
+ };
3780
+ }
3781
+ const outputSchema = resolveStepOutputSchema(node.config);
3782
+ if (!outputSchema) {
3783
+ return {
3784
+ status: "failure",
3785
+ failure: replayFailure(
3786
+ "resume_output_schema_unresolvable",
3787
+ `Step ${node.config.id} has no resolvable output schema to replay with.`
3788
+ )
3789
+ };
3790
+ }
3791
+ const validatedOutput = outputSchema.assert(recordedOutput, `step ${node.config.id} output`);
3792
+ const stepDir = resolveStepArtifactPaths({
3793
+ runDir: options.runDir,
3794
+ stepId: node.config.id,
3795
+ stepIndex
3796
+ }).stepDir;
3797
+ const completedNode = node;
3798
+ node = await withStepContext(
3799
+ completedNode.config.id,
3800
+ stepDir,
3801
+ async () => completedNode.onOutput(validatedOutput, completedNode.config.input)
3802
+ );
3803
+ } else {
3804
+ const stepDir = resolveStepArtifactPaths({
3805
+ runDir: options.runDir,
3806
+ stepId: node.config.id,
3807
+ stepIndex
3808
+ }).stepDir;
3809
+ const completedNode = node;
3810
+ node = await withStepContext(
3811
+ completedNode.config.id,
3812
+ stepDir,
3813
+ async () => completedNode.onOutput(completedNode.config.input, completedNode.config.input)
3814
+ );
3815
+ }
3816
+ }
3817
+ if (!isStepNode(node) || node.config.id !== options.targetStepId) {
3818
+ return {
3819
+ status: "failure",
3820
+ failure: replayFailure(
3821
+ "resume_step_id_drift",
3822
+ `Target step ${options.targetStepId} is not the next live step.`
3823
+ )
3824
+ };
3825
+ }
3826
+ return { status: "success", node };
3827
+ }
3828
+ function readPlainPayload2(event, key) {
3829
+ const value = event.payload[key];
3830
+ return isPlainObject7(value) ? value : void 0;
3831
+ }
3832
+ function isPlainObject7(value) {
3833
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
3834
+ }
3835
+ function replayFailure(code, message) {
3836
+ return { code, message };
3837
+ }
3838
+
3839
+ // src/runtime/resume/reattach-in-progress-step/reattach-in-progress-step.ts
3840
+ function findDanglingInteractiveSessionStart(events) {
3841
+ const anchor = events.at(-1);
3842
+ if (anchor?.type !== "interactive.sessionStarted" || !anchor.stepId) {
3843
+ return void 0;
3844
+ }
3845
+ const hasTerminalWorkflowEvent = events.some(
3846
+ (event) => event.type === "workflow.failed" || event.type === "workflow.completed"
3847
+ );
3848
+ return hasTerminalWorkflowEvent ? void 0 : anchor;
3849
+ }
3850
+ async function reattachInProgressStep(options) {
3851
+ const anchor = findDanglingInteractiveSessionStart(options.events);
3852
+ if (!anchor?.stepId) {
3853
+ return {
3854
+ status: "failure",
3855
+ failure: resumeFailure(
3856
+ "resume_target_not_dangling",
3857
+ "Resume target has no dangling interactive.sessionStarted event to reattach."
3858
+ )
3859
+ };
3860
+ }
3861
+ const stepIndex = anchor.payload.stepIndex;
3862
+ if (typeof stepIndex !== "number") {
3863
+ return {
3864
+ status: "failure",
3865
+ failure: resumeFailure(
3866
+ "resume_missing_step_index",
3867
+ `Dangling interactive.sessionStarted event for step ${anchor.stepId} has no recorded stepIndex.`
3868
+ )
3869
+ };
3870
+ }
3871
+ const startedEvent = options.events.find((event) => event.type === "workflow.started");
3872
+ if (!startedEvent) {
3873
+ return {
3874
+ status: "failure",
3875
+ failure: resumeFailure(
3876
+ "resume_target_not_found",
3877
+ "Resume target has no workflow.started event."
3878
+ )
3879
+ };
3880
+ }
3881
+ if (startedEvent.workflowId !== options.workflow.id) {
3882
+ return {
3883
+ status: "failure",
3884
+ failure: resumeFailure(
3885
+ "resume_workflow_mismatch",
3886
+ `Resume target workflow ${startedEvent.workflowId} does not match ${options.workflow.id}.`
3887
+ )
3888
+ };
3889
+ }
3890
+ const input = readPlainPayload3(startedEvent, "input");
3891
+ if (!input) {
3892
+ return {
3893
+ status: "failure",
3894
+ failure: resumeFailure(
3895
+ "resume_target_not_found",
3896
+ "workflow.started payload is missing input."
3897
+ )
3898
+ };
3899
+ }
3900
+ const replay = await replayCompletedSteps({
3901
+ workflow: options.workflow,
3902
+ events: options.events,
3903
+ input,
3904
+ targetStepId: anchor.stepId,
3905
+ runDir: options.runDir
3906
+ });
3907
+ if (replay.status === "failure") {
3908
+ return replay;
3909
+ }
3910
+ const { node } = replay;
3911
+ const artifactPaths = resolveStepArtifactPaths({
3912
+ runDir: options.runDir,
3913
+ stepId: anchor.stepId,
3914
+ stepIndex
3915
+ });
3916
+ const outputSchema = resolveStepOutputSchema(node.config);
3917
+ if (!outputSchema) {
3918
+ return {
3919
+ status: "failure",
3920
+ failure: resumeFailure(
3921
+ "resume_output_schema_unresolvable",
3922
+ `Step ${node.config.id} has no resolvable output schema to reattach with.`
3923
+ )
3924
+ };
3925
+ }
3926
+ const reattached = await readReattachedInteractiveOutput({
3927
+ stepId: anchor.stepId,
3928
+ interactiveFile: artifactPaths.interactiveFile,
3929
+ outputSchema
3930
+ });
3931
+ if (reattached.status === "failure") {
3932
+ return reattached;
3933
+ }
3934
+ const nextNode = await withStepContext(
3935
+ anchor.stepId,
3936
+ artifactPaths.stepDir,
3937
+ async () => node.onOutput(
3938
+ outputSchema.assert(reattached.output, `step ${node.config.id} output`),
3939
+ node.config.input
3940
+ )
3941
+ );
3942
+ return {
3943
+ status: "success",
3944
+ input,
3945
+ node: nextNode,
3946
+ resumedStepId: anchor.stepId,
3947
+ sourceFailureEventId: anchor.id
3948
+ };
3949
+ }
3950
+ async function readReattachedInteractiveOutput(options) {
3951
+ const first = await tryReadCompletedInteractiveOutput(options);
3952
+ if (first.status !== "still-active") {
3953
+ return first;
3954
+ }
3955
+ const abortController = new AbortController();
3956
+ await waitForInteractiveCompletion(options.interactiveFile, abortController.signal);
3957
+ const second = await tryReadCompletedInteractiveOutput(options);
3958
+ if (second.status !== "still-active") {
3959
+ return second;
3960
+ }
3961
+ throw new TrailStepFailureError({
3962
+ code: "interactive_session_incomplete",
3963
+ message: `Interactive agent step ${options.stepId} did not complete after reattaching.`
3964
+ });
3965
+ }
3966
+ async function tryReadCompletedInteractiveOutput(options) {
3967
+ try {
3968
+ return { status: "success", output: await readCompletedInteractiveOutput(options) };
3969
+ } catch (error) {
3970
+ if (error instanceof TrailStepFailureError) {
3971
+ if (error.failure.code === "interactive_session_incomplete") {
3972
+ return { status: "still-active" };
3973
+ }
3974
+ if (error.failure.code === "interactive_session_cancelled") {
3975
+ return {
3976
+ status: "failure",
3977
+ failure: resumeFailure(
3978
+ "resume_interactive_session_cancelled",
3979
+ `Interactive agent step ${options.stepId} was cancelled and cannot be resumed.`
3980
+ )
3981
+ };
3982
+ }
3983
+ if (error.failure.code === "interactive_session_invalid") {
3984
+ return {
3985
+ status: "failure",
3986
+ failure: resumeFailure(
3987
+ "resume_interactive_protocol_missing",
3988
+ `Interactive agent step ${options.stepId} has no readable interactive.json to reattach to.`
3989
+ )
3990
+ };
3991
+ }
3992
+ }
3993
+ throw error;
3994
+ }
3995
+ }
3996
+ function readPlainPayload3(event, key) {
3997
+ const value = event.payload[key];
3998
+ return isPlainObject8(value) ? value : void 0;
3999
+ }
4000
+ function isPlainObject8(value) {
4001
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
4002
+ }
4003
+ function resumeFailure(code, message) {
4004
+ return { code, message };
4005
+ }
4006
+
4007
+ // src/runtime/resume/replay-to-failed-step/replay-to-failed-step.ts
4008
+ async function replayToFailedStep(options) {
4009
+ const startedEvent = options.events.find((event) => event.type === "workflow.started");
4010
+ if (!startedEvent) {
4011
+ return {
4012
+ status: "failure",
4013
+ failure: resumeFailure2(
4014
+ "resume_target_not_found",
4015
+ "Resume target has no workflow.started event."
4016
+ )
4017
+ };
4018
+ }
4019
+ if (startedEvent.workflowId !== options.workflow.id) {
4020
+ return {
4021
+ status: "failure",
4022
+ failure: resumeFailure2(
4023
+ "resume_workflow_mismatch",
4024
+ `Resume target workflow ${startedEvent.workflowId} does not match ${options.workflow.id}.`
4025
+ )
4026
+ };
4027
+ }
4028
+ const terminalEvent = options.events.at(-1);
4029
+ if (terminalEvent?.type !== "workflow.failed") {
4030
+ return {
4031
+ status: "failure",
4032
+ failure: resumeFailure2(
4033
+ "resume_target_not_failed",
4034
+ "Resume target must end in workflow.failed."
4035
+ )
4036
+ };
4037
+ }
4038
+ const recoveredFailedStep = options.events.find(
4039
+ (event, index) => event.type === "step.failed" && options.events.slice(index + 1).some(
4040
+ (laterEvent) => laterEvent.type === "step.completed" && laterEvent.stepId === event.stepId
4041
+ )
4042
+ );
4043
+ if (recoveredFailedStep) {
4044
+ return {
4045
+ status: "failure",
4046
+ failure: resumeFailure2(
4047
+ "resume_unsupported_history",
4048
+ `Resume does not support recovered onError history for step ${recoveredFailedStep.stepId ?? "<missing>"}.`
4049
+ )
4050
+ };
4051
+ }
4052
+ const failedStepEvents = options.events.filter((event) => event.type === "step.failed");
4053
+ if (failedStepEvents.length !== 1) {
4054
+ return {
4055
+ status: "failure",
4056
+ failure: resumeFailure2(
4057
+ "resume_multiple_failed_steps",
4058
+ "Resume target must contain exactly one failed step."
4059
+ )
4060
+ };
4061
+ }
4062
+ const failedStepEvent = failedStepEvents[0];
4063
+ if (!failedStepEvent) {
4064
+ return {
4065
+ status: "failure",
4066
+ failure: resumeFailure2("resume_target_not_failed", "Resume target has no failed step.")
4067
+ };
4068
+ }
4069
+ if (!failedStepEvent.stepId) {
4070
+ return {
4071
+ status: "failure",
4072
+ failure: resumeFailure2("resume_target_not_failed", "Failed step event has no step id.")
4073
+ };
4074
+ }
4075
+ const input = readPlainPayload4(startedEvent, "input");
4076
+ if (!input) {
4077
+ return {
4078
+ status: "failure",
4079
+ failure: resumeFailure2(
4080
+ "resume_target_not_found",
4081
+ "workflow.started payload is missing input."
4082
+ )
4083
+ };
4084
+ }
4085
+ const replay = await replayCompletedSteps({
4086
+ workflow: options.workflow,
4087
+ events: options.events,
4088
+ input,
4089
+ targetStepId: failedStepEvent.stepId,
4090
+ runDir: options.runDir
4091
+ });
4092
+ if (replay.status === "failure") {
4093
+ return replay;
4094
+ }
4095
+ const { node } = replay;
4096
+ if (node.onError) {
4097
+ return {
4098
+ status: "failure",
4099
+ failure: resumeFailure2(
4100
+ "resume_unsupported_history",
4101
+ `Resume does not support onError history for step ${node.config.id}.`
4102
+ )
4103
+ };
4104
+ }
4105
+ return {
4106
+ status: "success",
4107
+ input,
4108
+ node,
4109
+ resumedStepId: failedStepEvent.stepId,
4110
+ sourceFailureEventId: failedStepEvent.id
4111
+ };
4112
+ }
4113
+ function readPlainPayload4(event, key) {
4114
+ const value = event.payload[key];
4115
+ return isPlainObject9(value) ? value : void 0;
4116
+ }
4117
+ function isPlainObject9(value) {
4118
+ return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
4119
+ }
4120
+ function resumeFailure2(code, message) {
4121
+ return { code, message };
4122
+ }
4123
+
4124
+ // src/runtime/retry/replay-to-retry-failure.ts
4125
+ async function replayToRetryFailure(options) {
4126
+ const failure = selectLatestUnresolvedFailure(options.events);
4127
+ if (!failure) {
4128
+ return {
4129
+ status: "failure",
4130
+ failure: retryFailure(
4131
+ "retry_target_not_failed",
4132
+ "Retry target has no latest unresolved failure."
4133
+ )
4134
+ };
4135
+ }
4136
+ if (failure.workflowId !== options.workflow.id) {
4137
+ return {
4138
+ status: "failure",
4139
+ failure: retryFailure(
4140
+ "retry_workflow_mismatch",
4141
+ `Retry target workflow ${failure.workflowId} does not match ${options.workflow.id}.`
4142
+ )
4143
+ };
4144
+ }
4145
+ if (!failure.workflowInput) {
4146
+ return {
4147
+ status: "failure",
4148
+ failure: retryFailure("retry_target_not_found", "workflow.started payload is missing input.")
4149
+ };
4150
+ }
4151
+ if (!failure.stepId) {
4152
+ return {
4153
+ status: "failure",
4154
+ failure: retryFailure(
4155
+ "retry_target_not_failed",
4156
+ failure.event.type === "workflow.failed" ? "Workflow failure has no associated step ID. This run may use unsupported historical retry metadata." : "Failed retry target event has no step ID."
4157
+ )
4158
+ };
4159
+ }
4160
+ const replay = await replayCompletedSteps({
4161
+ workflow: options.workflow,
4162
+ events: eventsBeforeRetryTarget(options.events, failure.replayPosition, failure.stepId),
4163
+ input: failure.workflowInput,
4164
+ targetStepId: failure.stepId,
4165
+ runDir: options.runDir
4166
+ });
4167
+ if (replay.status === "failure") {
4168
+ return replay;
4169
+ }
4170
+ const { node } = replay;
4171
+ if (node.onError) {
4172
+ return {
4173
+ status: "failure",
4174
+ failure: retryFailure(
4175
+ "retry_unsupported_history",
4176
+ `Retry does not support onError history for step ${node.config.id}.`
4177
+ )
4178
+ };
4179
+ }
4180
+ return {
4181
+ status: "success",
4182
+ input: failure.workflowInput,
4183
+ node,
4184
+ retriedStepId: failure.stepId,
4185
+ sourceFailureEventId: failure.sourceFailureEventId ?? failure.event.id,
4186
+ sourceFailureReplayPosition: failure.replayPosition
4187
+ };
4188
+ }
4189
+ function eventsBeforeRetryTarget(events, replayPosition, targetStepId) {
4190
+ const eventsBeforeFailure = events.slice(0, replayPosition);
4191
+ let targetAttemptStartPosition = -1;
4192
+ for (let index = eventsBeforeFailure.length - 1; index >= 0; index -= 1) {
4193
+ const event = eventsBeforeFailure[index];
4194
+ if (event?.type === "step.started" && event.stepId === targetStepId) {
4195
+ targetAttemptStartPosition = index;
4196
+ break;
4197
+ }
4198
+ }
4199
+ if (targetAttemptStartPosition === -1) {
4200
+ return eventsBeforeFailure;
4201
+ }
4202
+ return eventsBeforeFailure.filter(
4203
+ (event, index) => index <= targetAttemptStartPosition || event.type !== "step.completed" || event.stepId !== targetStepId
4204
+ );
4205
+ }
4206
+ function retryFailure(code, message) {
4207
+ return { code, message };
4208
+ }
4209
+
4210
+ // src/runtime/run-context/create-run-context.ts
4211
+ function createRunContext(options) {
4212
+ let cache;
4213
+ let loadPromise;
4214
+ let writeQueue = Promise.resolve();
4215
+ function ensureLoaded() {
4216
+ if (cache) return Promise.resolve(cache);
4217
+ loadPromise ??= readRunState(options.runDir).then((state2) => cache = state2);
4218
+ return loadPromise;
4219
+ }
4220
+ function enqueueWrite() {
4221
+ const next = writeQueue.then(async () => {
4222
+ const state2 = await ensureLoaded();
4223
+ await writeRunState(options.runDir, state2);
4224
+ });
4225
+ writeQueue = next.catch(() => {
4226
+ });
4227
+ return next;
4228
+ }
4229
+ return {
4230
+ id: options.runId,
4231
+ name: options.runName,
4232
+ path: options.runDir,
4233
+ workflowId: options.workflowId,
4234
+ workflowAgents: options.workflowAgents,
4235
+ cwd: options.cwd,
4236
+ trailstepConfig: options.trailstepConfig,
4237
+ workingAgentProcessRunner: options.workingAgentProcessRunner,
4238
+ providerWorkingRunner: options.providerWorkingRunner,
4239
+ emit: options.emit,
4240
+ events: options.events,
4241
+ state: {
4242
+ async get(key) {
4243
+ const state2 = await ensureLoaded();
4244
+ return state2[key];
4245
+ },
4246
+ async set(key, value) {
4247
+ const state2 = await ensureLoaded();
4248
+ state2[key] = value;
4249
+ await enqueueWrite();
4250
+ }
4251
+ }
4252
+ };
4253
+ }
4254
+
4255
+ // src/runtime/run-workflow/initialize-run.ts
4256
+ import { basename as basename2 } from "path";
4257
+ async function initializeRun(options) {
4258
+ const existingRunDir = options.resume?.runDir ?? options.retry?.runDir;
4259
+ if (existingRunDir) {
4260
+ const previousEvents = await readRunEvents(existingRunDir);
4261
+ const startedEvent = previousEvents.find((event) => event.type === "workflow.started");
4262
+ return {
4263
+ runId: startedEvent?.runId ?? basename2(existingRunDir),
4264
+ runName: startedEvent?.runId ?? basename2(existingRunDir),
4265
+ runDir: existingRunDir,
4266
+ previousEvents
4267
+ };
4268
+ }
4269
+ const cwd = options.cwd ?? process.cwd();
4270
+ const runName = options.runName;
4271
+ if (runName === void 0) {
4272
+ throw new Error("Expected runName for a new workflow run.");
4273
+ }
4274
+ const { runId, runDir } = await createRunDirectory({
4275
+ cwd,
4276
+ runName,
4277
+ runsRoot: options.runsRoot
4278
+ });
4279
+ return { runId, runName, runDir, previousEvents: [] };
4280
+ }
4281
+
4282
+ // src/runtime/run-workflow/trailstep-config-input.ts
4283
+ function parseTrailStepConfigInput(value) {
4284
+ if (isParsedTrailStepConfig(value) || isFlattenedTrailStepConfig(value)) {
4285
+ return value;
4286
+ }
4287
+ return parseTrailStepConfig(value);
4288
+ }
4289
+ function isFlattenedTrailStepConfig(value) {
4290
+ if (!isPlainRecord(value) || value.version !== 1) {
4291
+ return false;
4292
+ }
4293
+ if (!isPlainRecord(value.customProviders) || !isFlattenedAgentMappings(value.agents)) {
4294
+ return false;
4295
+ }
4296
+ if (value.settings !== void 0 && !isFlattenedSettings(value.settings)) {
4297
+ return false;
4298
+ }
4299
+ if (value.workflows === void 0) {
4300
+ return true;
4301
+ }
4302
+ if (!isPlainRecord(value.workflows)) {
4303
+ return false;
4304
+ }
4305
+ return Object.values(value.workflows).every(
4306
+ (workflow) => isPlainRecord(workflow) && (workflow.agents === void 0 || isFlattenedAgentMappings(workflow.agents)) && (workflow.settings === void 0 || isFlattenedSettings(workflow.settings))
4307
+ );
4308
+ }
4309
+ function isFlattenedSettings(value) {
4310
+ return isPlainRecord(value) && (value.retry === void 0 || isPlainRecord(value.retry)) && (value.timeout === void 0 || typeof value.timeout === "number");
4311
+ }
4312
+ function isFlattenedAgentMappings(value) {
4313
+ return isPlainRecord(value) && Object.values(value).every(
4314
+ (targets) => Array.isArray(targets) && targets.every(isFlattenedAgentTarget)
4315
+ );
4316
+ }
4317
+ function isFlattenedAgentTarget(value) {
4318
+ return isPlainRecord(value) && typeof value.provider === "string";
4319
+ }
4320
+ function isPlainRecord(value) {
4321
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4322
+ }
4323
+
4324
+ // src/runtime/run-workflow/run-workflow.ts
4325
+ async function runWorkflow(options) {
4326
+ const maxSteps = options.maxSteps ?? 1e3;
4327
+ const isResume = options.resume !== void 0;
4328
+ const isRetry = options.retry !== void 0;
4329
+ const initialized = await initializeRun(options).catch((error) => {
4330
+ const existingRunDir = options.resume?.runDir ?? options.retry?.runDir;
4331
+ if (existingRunDir && isFailureLikeError(error)) {
4332
+ return {
4333
+ status: "failure",
4334
+ runId: basename3(existingRunDir),
4335
+ runName: basename3(existingRunDir),
4336
+ runDir: existingRunDir,
4337
+ previousEvents: [],
4338
+ failure: workflowFailure(error)
4339
+ };
4340
+ }
4341
+ throw error;
4342
+ });
4343
+ if ("status" in initialized && initialized.status === "failure") {
4344
+ return {
4345
+ status: "failure",
4346
+ runId: initialized.runId,
4347
+ runDir: initialized.runDir,
4348
+ failure: initialized.failure,
4349
+ events: initialized.previousEvents
4350
+ };
4351
+ }
4352
+ const { runId, runName, runDir, previousEvents } = initialized;
4353
+ const trailstepConfig = options.trailstepConfig === void 0 ? void 0 : parseTrailStepConfigInput(options.trailstepConfig);
4354
+ const cwd = options.cwd ?? process.cwd();
4355
+ const events = [...previousEvents];
4356
+ const emit = async (event) => {
4357
+ events.push(event);
4358
+ await appendEvent(runDir, event);
4359
+ await options.eventSink?.(event);
4360
+ };
4361
+ const runContext = createRunContext({
4362
+ runId,
4363
+ runName,
4364
+ runDir,
4365
+ workflowId: options.workflow.id,
4366
+ workflowAgents: options.workflow.agents ?? {},
4367
+ cwd,
4368
+ trailstepConfig,
4369
+ workingAgentProcessRunner: options.workingAgentProcessRunner,
4370
+ providerWorkingRunner: options.providerWorkingRunner,
4371
+ emit,
4372
+ events: () => events
4373
+ });
4374
+ const failWorkflow = async (failure) => {
4375
+ await emit(
4376
+ createEvent({
4377
+ runId,
4378
+ workflowId: options.workflow.id,
4379
+ type: "workflow.failed",
4380
+ payload: { failure }
4381
+ })
4382
+ );
4383
+ return {
4384
+ status: "failure",
4385
+ runId,
4386
+ runDir,
4387
+ failure,
4388
+ events
4389
+ };
4390
+ };
4391
+ const failResumeValidation = (failure) => ({
4392
+ status: "failure",
4393
+ runId,
4394
+ runDir,
4395
+ failure,
4396
+ events
4397
+ });
4398
+ try {
4399
+ return await runContextStorage.run(runContext, () => runWorkflowBody());
4400
+ } catch (error) {
4401
+ return await failWorkflow(workflowFailure(error));
4402
+ }
4403
+ async function runWorkflowBody() {
4404
+ const inputSchema = options.workflow.inputShape ? normalizeShape(options.workflow.inputShape) : options.workflow.input;
4405
+ let workflowInput;
4406
+ let startNode;
4407
+ if (isResume) {
4408
+ const danglingAnchor = findDanglingInteractiveSessionStart(previousEvents);
4409
+ const replay = danglingAnchor ? await reattachInProgressStep({
4410
+ workflow: options.workflow,
4411
+ events: previousEvents,
4412
+ runDir
4413
+ }) : await replayToFailedStep({
4414
+ workflow: options.workflow,
4415
+ events: previousEvents,
4416
+ runDir
4417
+ });
4418
+ if (replay.status === "failure") {
4419
+ return failResumeValidation(replay.failure);
4420
+ }
4421
+ workflowInput = inputSchema ? inputSchema.assert(replay.input, "workflow input") : replay.input;
4422
+ startNode = replay.node;
4423
+ await emit(
4424
+ createEvent({
4425
+ runId,
4426
+ workflowId: options.workflow.id,
4427
+ type: "workflow.resumed",
4428
+ payload: {
4429
+ resumedFromRunDir: runDir,
4430
+ resumedStepId: replay.resumedStepId,
4431
+ sourceFailureEventId: replay.sourceFailureEventId
4432
+ }
4433
+ })
4434
+ );
4435
+ } else if (isRetry) {
4436
+ const replay = await replayToRetryFailure({
4437
+ workflow: options.workflow,
4438
+ events: previousEvents,
4439
+ runDir
4440
+ });
4441
+ if (replay.status === "failure") {
4442
+ return failResumeValidation(replay.failure);
4443
+ }
4444
+ workflowInput = inputSchema ? inputSchema.assert(replay.input, "workflow input") : replay.input;
4445
+ startNode = replay.node;
4446
+ await emit(
4447
+ createEvent({
4448
+ runId,
4449
+ workflowId: options.workflow.id,
4450
+ type: "workflow.retryStarted",
4451
+ payload: {
4452
+ retryKind: options.retry.kind,
4453
+ retriedFromRunDir: runDir,
4454
+ retriedStepId: replay.retriedStepId,
4455
+ sourceFailureEventId: replay.sourceFailureEventId,
4456
+ sourceFailureReplayPosition: replay.sourceFailureReplayPosition
4457
+ }
4458
+ })
4459
+ );
4460
+ } else {
4461
+ workflowInput = inputSchema ? inputSchema.assert(options.input, "workflow input") : options.input;
4462
+ await emit(
4463
+ createEvent({
4464
+ runId,
4465
+ workflowId: options.workflow.id,
4466
+ type: "workflow.started",
4467
+ payload: { input: workflowInput }
4468
+ })
4469
+ );
4470
+ }
4471
+ const continuationResult = await runContinuation({
4472
+ node: startNode ?? options.workflow.start(workflowInput),
4473
+ runId,
4474
+ workflowId: options.workflow.id,
4475
+ emit,
4476
+ maxSteps,
4477
+ initialSource: isRetry ? `retry for workflow ${options.workflow.id}` : isResume ? `resume for workflow ${options.workflow.id}` : `workflow.start for workflow ${options.workflow.id}`,
4478
+ // The original run already used one step-index slot per step.started
4479
+ // event ever recorded (successful or failed) -- newly-dispatched steps
4480
+ // after resume must continue that sequence, not restart at 1, or their
4481
+ // artifact directories collide with the pre-resume steps' directories.
4482
+ initialExecutedSteps: isResume || isRetry ? previousEvents.filter((event) => event.type === "step.started").length : void 0,
4483
+ workflowAgents: options.workflow.agents ?? {},
4484
+ workflowTimeout: options.workflow.timeout,
4485
+ runDir,
4486
+ cwd,
4487
+ trailstepConfig,
4488
+ workingAgentProcessRunner: options.workingAgentProcessRunner,
4489
+ providerWorkingRunner: options.providerWorkingRunner,
4490
+ processRunner: options.processRunner
4491
+ });
4492
+ if (continuationResult.status === "failure") {
4493
+ return await failWorkflow(continuationResult.failure);
4494
+ }
4495
+ const current = continuationResult.output;
4496
+ const outputSchema = options.workflow.outputShape ? normalizeShape(options.workflow.outputShape) : options.workflow.output;
4497
+ const output = outputSchema ? outputSchema.assert(current, "workflow output") : current;
4498
+ await emit(
4499
+ createEvent({
4500
+ runId,
4501
+ workflowId: options.workflow.id,
4502
+ type: "workflow.completed",
4503
+ payload: { output }
4504
+ })
4505
+ );
4506
+ return {
4507
+ status: "success",
4508
+ runId,
4509
+ runDir,
4510
+ output,
4511
+ events
4512
+ };
4513
+ }
4514
+ }
4515
+
4516
+ // src/runtime/runs/run-summaries.ts
4517
+ import { readdir } from "fs/promises";
4518
+ import { join as join7 } from "path";
4519
+ async function listRunSummaries(options) {
4520
+ const runsRoot = options.runsRoot ?? defaultRunsRoot(options.cwd);
4521
+ let entries;
4522
+ try {
4523
+ entries = await readdir(runsRoot, { withFileTypes: true });
4524
+ } catch (error) {
4525
+ if (isNodeError2(error) && error.code === "ENOENT") {
4526
+ return [];
4527
+ }
4528
+ throw error;
4529
+ }
4530
+ const summaries = [];
4531
+ for (const entry of entries) {
4532
+ if (!entry.isDirectory()) {
4533
+ continue;
4534
+ }
4535
+ const runId = entry.name;
4536
+ const runDir = join7(runsRoot, runId);
4537
+ try {
4538
+ summaries.push(summarizeReadableRun({ runId, runDir, events: await readRunEvents(runDir) }));
4539
+ } catch (error) {
4540
+ summaries.push({
4541
+ runId,
4542
+ runDir,
4543
+ status: "unknown",
4544
+ warning: `Warning: Could not read run ${runId}: ${readErrorMessage(error)}`
4545
+ });
4546
+ }
4547
+ }
4548
+ return summaries.sort(newestFirst);
4549
+ }
4550
+ function selectRecentFailedRunSummaries(summaries, options = {}) {
4551
+ const cutoffMs = (options.now ?? /* @__PURE__ */ new Date()).getTime() - 7 * 24 * 60 * 60 * 1e3;
4552
+ return summaries.filter((summary) => {
4553
+ if (summary.status !== "failed" || !summary.latestFailure) {
4554
+ return false;
4555
+ }
4556
+ const failureTime = Date.parse(summary.latestFailure.event.timestamp);
4557
+ return Number.isFinite(failureTime) && failureTime >= cutoffMs;
4558
+ }).sort(newestFirst).slice(0, 10);
4559
+ }
4560
+ function newestFirst(left, right) {
4561
+ return compareTimestampDescending(left.lastTimestamp, right.lastTimestamp) || left.runId.localeCompare(right.runId);
4562
+ }
4563
+ function summarizeReadableRun(options) {
4564
+ const latestFailure = selectLatestUnresolvedFailure(options.events);
4565
+ const terminalStatus = selectTerminalStatus(options.events);
4566
+ const lastEvent = options.events.at(-1);
4567
+ const workflowId = lastEvent?.workflowId ?? options.events.find((event) => event.workflowId)?.workflowId;
4568
+ if (terminalStatus === "completed") {
4569
+ return {
4570
+ ...options,
4571
+ status: "completed",
4572
+ workflowId,
4573
+ lastTimestamp: lastEvent?.timestamp
4574
+ };
4575
+ }
4576
+ if (latestFailure) {
4577
+ return {
4578
+ ...options,
4579
+ status: "failed",
4580
+ workflowId: latestFailure.workflowId,
4581
+ lastTimestamp: latestFailure.event.timestamp,
4582
+ latestFailure
4583
+ };
4584
+ }
4585
+ return {
4586
+ ...options,
4587
+ status: terminalStatus ?? "active",
4588
+ workflowId,
4589
+ lastTimestamp: lastEvent?.timestamp
4590
+ };
4591
+ }
4592
+ function selectTerminalStatus(events) {
4593
+ for (let index = events.length - 1; index >= 0; index -= 1) {
4594
+ const event = events[index];
4595
+ if (event?.type === "workflow.completed") {
4596
+ return "completed";
4597
+ }
4598
+ if (event?.type === "workflow.failed") {
4599
+ return "failed";
4600
+ }
4601
+ }
4602
+ return void 0;
4603
+ }
4604
+ function compareTimestampDescending(left, right) {
4605
+ const leftTime = left ? Date.parse(left) : Number.NEGATIVE_INFINITY;
4606
+ const rightTime = right ? Date.parse(right) : Number.NEGATIVE_INFINITY;
4607
+ return rightTime - leftTime;
4608
+ }
4609
+ function readErrorMessage(error) {
4610
+ return error instanceof Error ? error.message : String(error);
4611
+ }
4612
+ function isNodeError2(error) {
4613
+ return error instanceof Error && "code" in error;
4614
+ }
4615
+ export {
4616
+ Document,
4617
+ TrailStepFailureError,
4618
+ defaultRunsRoot,
4619
+ deprecationManifest,
4620
+ document,
4621
+ done,
4622
+ fail,
4623
+ findDeprecationsAsOf,
4624
+ isDoneNode,
4625
+ isFailNode,
4626
+ isStepNode,
4627
+ jsonSchema,
4628
+ list,
4629
+ listRunSummaries,
4630
+ loadFragments,
4631
+ newestFirst,
4632
+ normalizeShape,
4633
+ parseTrailStepConfig,
4634
+ promptSections,
4635
+ promptTemplate,
4636
+ providerRegistry,
4637
+ readRunEvents,
4638
+ readRunState,
4639
+ resolveAgentTargets,
4640
+ resolveRetryPolicy,
4641
+ resolveTimeoutPolicy,
4642
+ runWorkflow,
4643
+ section,
4644
+ selectLatestUnresolvedFailure,
4645
+ selectRecentFailedRunSummaries,
4646
+ shape,
4647
+ state,
4648
+ step,
4649
+ subPrompt,
4650
+ validateRetryPolicy,
4651
+ validateTimeoutPolicy,
4652
+ writeRunState
4653
+ };
4654
+ //# sourceMappingURL=index.js.map