@hyperdrive.bot/paseo-protocol 0.3.41 → 0.3.43

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/messages.js CHANGED
@@ -101,7 +101,9 @@ export const MutableDaemonConfigSchema = z
101
101
  .passthrough(),
102
102
  browserTools: MutableBrowserToolsConfigSchema.default({ enabled: false }),
103
103
  providers: z.record(z.string(), MutableDaemonProviderConfigSchema).default({}),
104
- metadataGeneration: MutableMetadataGenerationConfigSchema.default({ providers: [] }),
104
+ metadataGeneration: MutableMetadataGenerationConfigSchema.default({
105
+ providers: [],
106
+ }),
105
107
  autoArchiveAfterMerge: z.boolean().default(false),
106
108
  enableTerminalAgentHooks: z.boolean().default(false),
107
109
  appendSystemPrompt: z.string().default(""),
@@ -154,6 +156,38 @@ export const WorkflowPhaseSchema = z.object({
154
156
  storyCount: z.number().int().nonnegative().optional(),
155
157
  completedStoryCount: z.number().int().nonnegative().optional(),
156
158
  });
159
+ /**
160
+ * Per-step agent config (Epic 2, Story 2.1). A CLOSED subset of the daemon's
161
+ * `AgentSessionConfig`: every key optional, and `.strict()` so an unknown key is a
162
+ * parse ERROR rather than a silent strip. Silent-strip is the failure this schema
163
+ * exists to prevent: a typo'd `provider:` would otherwise spawn the default agent
164
+ * with no signal, which is exactly the "silent fallback to the default" the epic forbids.
165
+ *
166
+ * Deliberately excluded, each for a reason:
167
+ * - `cwd`: workflow-owned. A step must not relocate its child. Also forced at merge time.
168
+ * - `extra` / `mcpServers` / `featureValues`: arbitrary nested structure, i.e. the
169
+ * "deterministic via code" escape hatch this PRD exists to close.
170
+ * - `title`: owned by `initialTitle`, which the manager sets from `node.title`.
171
+ * - `daemonAppendSystemPrompt`: runtime-only, never persisted (`agent-sdk-types.ts:565-568`).
172
+ * - `internal`: workflow children must stay listable (`AgentManager.listAgents` filters
173
+ * on `!agent.internal`, `agent-manager.ts:810-813`).
174
+ *
175
+ * Widening this vocabulary requires a schema edit AND a new test. It must never widen
176
+ * by accident from a config file.
177
+ */
178
+ export const WorkflowAgentPresetSchema = z
179
+ .object({
180
+ provider: z.string().optional(),
181
+ model: z.string().optional(),
182
+ modeId: z.string().optional(),
183
+ systemPrompt: z.string().optional(),
184
+ thinkingOptionId: z.string().optional(),
185
+ approvalPolicy: z.string().optional(),
186
+ sandboxMode: z.string().optional(),
187
+ networkAccess: z.boolean().optional(),
188
+ webSearch: z.boolean().optional(),
189
+ })
190
+ .strict();
157
191
  // Live/wire snapshot of a workflow. New fields must be additive (.optional() with a
158
192
  // sensible default) to preserve back-compat — a 6-month-old client must still parse this.
159
193
  export const WorkflowSnapshotSchema = z.object({
@@ -162,6 +196,12 @@ export const WorkflowSnapshotSchema = z.object({
162
196
  status: WorkflowStatusSchema,
163
197
  title: z.string().nullable().optional(),
164
198
  labels: z.record(z.string(), z.string()).default({}),
199
+ // Epic 2, Story 2.2: named agent presets a task's `agentType` selects. `.optional()`
200
+ // with NO `.default({})` on purpose: a default would stamp an empty map onto every
201
+ // record written before this field existed, and would make "an old record has no
202
+ // presets" untestable. Placed on the SNAPSHOT rather than only on the stored record so
203
+ // `STORED_WORKFLOW_SCHEMA` stays a bare alias and `persist` needs no change.
204
+ agentPresets: z.record(z.string(), WorkflowAgentPresetSchema).optional(),
165
205
  phases: z.array(WorkflowPhaseSchema).default([]),
166
206
  childAgentIds: z.array(z.string()).default([]),
167
207
  totalStoryCount: z.number().int().nonnegative().optional(),
@@ -268,6 +308,10 @@ export const WorkflowEventsResponseSchema = z.object({
268
308
  // by BOTH this client-facing `workflow.start` RPC and the `workflow_start` MCP
269
309
  // tool (no second divergent copy). The CLI's `child_process` / `claude -p`
270
310
  // spawning is deliberately NOT modelled — `agentManager.createAgent` replaces it.
311
+ // `WorkflowAgentPresetSchema` / `WorkflowAgentPreset` are declared ABOVE
312
+ // `WorkflowSnapshotSchema` (Epic 2, Story 2.2): the snapshot references the preset
313
+ // schema, and a `const` referenced before its initialiser runs is a runtime TDZ
314
+ // `ReferenceError` at module load, which `tsc --noEmit` does not catch.
271
315
  /** One task node — becomes one child agent when the workflow starts. */
272
316
  export const WorkflowTaskNodeSchema = z.object({
273
317
  id: z.string(),
@@ -279,6 +323,7 @@ export const WorkflowTaskNodeSchema = z.object({
279
323
  parallelizable: z.boolean(),
280
324
  estimatedMinutes: z.number(),
281
325
  agentType: z.string().optional(),
326
+ agentConfig: WorkflowAgentPresetSchema.optional(),
282
327
  targetFiles: z.array(z.string()).optional(),
283
328
  });
284
329
  /** Decomposed task graph driving child-agent spawning. */
@@ -316,6 +361,7 @@ export const WorkflowStartRequestSchema = z.object({
316
361
  model: z.string().optional(),
317
362
  title: z.string().optional(),
318
363
  labels: z.record(z.string(), z.string()).optional(),
364
+ agentPresets: z.record(z.string(), WorkflowAgentPresetSchema).optional(),
319
365
  });
320
366
  export const WorkflowStartResponseSchema = z.object({
321
367
  type: z.literal("workflow.start.response"),
@@ -1184,8 +1230,25 @@ const WorkspaceStateBucketSchema = z.enum([
1184
1230
  "failed",
1185
1231
  "running",
1186
1232
  "attention",
1233
+ // COMPAT(pendingBucket): added in 0.3.42. A settled agent that still owns
1234
+ // armed background work (monitor / cron / background shell / live subagent).
1235
+ "pending",
1187
1236
  "done",
1188
1237
  ]);
1238
+ /**
1239
+ * Client-side tolerant form of {@link WorkspaceStateBucketSchema}.
1240
+ *
1241
+ * A newer daemon may send a bucket an older app build has never heard of. A
1242
+ * bare `z.enum` THROWS on that, which fails the whole workspace-descriptor
1243
+ * parse and blanks the list — a total outage from an additive change. The
1244
+ * `.catch` degrades one field instead.
1245
+ *
1246
+ * The fallback is deliberately `running`, not `done`: an unknown bucket means
1247
+ * "this daemon knows about a state we don't", and the safe direction is to keep
1248
+ * the workspace VISIBLE rather than silently filing it under finished. Guessing
1249
+ * "done" would reproduce the exact class of bug the pending bucket exists to fix.
1250
+ */
1251
+ const IncomingWorkspaceStateBucketSchema = WorkspaceStateBucketSchema.catch("running");
1189
1252
  export const FetchWorkspacesRequestMessageSchema = z.object({
1190
1253
  type: z.literal("fetch_workspaces_request"),
1191
1254
  requestId: z.string(),
@@ -3126,7 +3189,7 @@ export const WorkspaceDescriptorPayloadSchema = z
3126
3189
  // is derived from the branch/directory.
3127
3190
  title: z.string().nullable().optional(),
3128
3191
  archivingAt: z.string().nullable().optional().default(null),
3129
- status: WorkspaceStateBucketSchema,
3192
+ status: IncomingWorkspaceStateBucketSchema,
3130
3193
  // Best-effort workspace status entry timestamp. Old daemons omit the
3131
3194
  // field; old clients treat missing and null equivalently. The transform
3132
3195
  // coerces a missing field to `null` so downstream code never has to
@@ -489,13 +489,14 @@ export declare const WSOutboundMessageSchema: {
489
489
  name: import("zod").ZodString;
490
490
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
491
491
  archivingAt: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
492
- status: import("zod").ZodEnum<{
492
+ status: import("zod").ZodCatch<import("zod").ZodEnum<{
493
493
  running: "running";
494
494
  attention: "attention";
495
495
  needs_input: "needs_input";
496
496
  failed: "failed";
497
+ pending: "pending";
497
498
  done: "done";
498
- }>;
499
+ }>>;
499
500
  statusEnteredAt: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, import("zod").ZodTransform<string | null, string | null | undefined>>;
500
501
  activityAt: import("zod").ZodNullable<import("zod").ZodString>;
501
502
  diffStat: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
@@ -673,7 +674,7 @@ export declare const WSOutboundMessageSchema: {
673
674
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
674
675
  name: string;
675
676
  archivingAt: string | null;
676
- status: "running" | "attention" | "needs_input" | "failed" | "done";
677
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
677
678
  statusEnteredAt: string | null;
678
679
  activityAt: string | null;
679
680
  scripts: {
@@ -776,7 +777,7 @@ export declare const WSOutboundMessageSchema: {
776
777
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
777
778
  name: string;
778
779
  archivingAt: string | null;
779
- status: "running" | "attention" | "needs_input" | "failed" | "done";
780
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
780
781
  statusEnteredAt: string | null;
781
782
  activityAt: string | null;
782
783
  scripts: {
@@ -1560,13 +1561,14 @@ export declare const WSOutboundMessageSchema: {
1560
1561
  name: import("zod").ZodString;
1561
1562
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
1562
1563
  archivingAt: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
1563
- status: import("zod").ZodEnum<{
1564
+ status: import("zod").ZodCatch<import("zod").ZodEnum<{
1564
1565
  running: "running";
1565
1566
  attention: "attention";
1566
1567
  needs_input: "needs_input";
1567
1568
  failed: "failed";
1569
+ pending: "pending";
1568
1570
  done: "done";
1569
- }>;
1571
+ }>>;
1570
1572
  statusEnteredAt: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, import("zod").ZodTransform<string | null, string | null | undefined>>;
1571
1573
  activityAt: import("zod").ZodNullable<import("zod").ZodString>;
1572
1574
  diffStat: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
@@ -1744,7 +1746,7 @@ export declare const WSOutboundMessageSchema: {
1744
1746
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
1745
1747
  name: string;
1746
1748
  archivingAt: string | null;
1747
- status: "running" | "attention" | "needs_input" | "failed" | "done";
1749
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
1748
1750
  statusEnteredAt: string | null;
1749
1751
  activityAt: string | null;
1750
1752
  scripts: {
@@ -1847,7 +1849,7 @@ export declare const WSOutboundMessageSchema: {
1847
1849
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
1848
1850
  name: string;
1849
1851
  archivingAt: string | null;
1850
- status: "running" | "attention" | "needs_input" | "failed" | "done";
1852
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
1851
1853
  statusEnteredAt: string | null;
1852
1854
  activityAt: string | null;
1853
1855
  scripts: {
@@ -2005,13 +2007,14 @@ export declare const WSOutboundMessageSchema: {
2005
2007
  name: import("zod").ZodString;
2006
2008
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
2007
2009
  archivingAt: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
2008
- status: import("zod").ZodEnum<{
2010
+ status: import("zod").ZodCatch<import("zod").ZodEnum<{
2009
2011
  running: "running";
2010
2012
  attention: "attention";
2011
2013
  needs_input: "needs_input";
2012
2014
  failed: "failed";
2015
+ pending: "pending";
2013
2016
  done: "done";
2014
- }>;
2017
+ }>>;
2015
2018
  statusEnteredAt: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, import("zod").ZodTransform<string | null, string | null | undefined>>;
2016
2019
  activityAt: import("zod").ZodNullable<import("zod").ZodString>;
2017
2020
  diffStat: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
@@ -2189,7 +2192,7 @@ export declare const WSOutboundMessageSchema: {
2189
2192
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
2190
2193
  name: string;
2191
2194
  archivingAt: string | null;
2192
- status: "running" | "attention" | "needs_input" | "failed" | "done";
2195
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
2193
2196
  statusEnteredAt: string | null;
2194
2197
  activityAt: string | null;
2195
2198
  scripts: {
@@ -2292,7 +2295,7 @@ export declare const WSOutboundMessageSchema: {
2292
2295
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
2293
2296
  name: string;
2294
2297
  archivingAt: string | null;
2295
- status: "running" | "attention" | "needs_input" | "failed" | "done";
2298
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
2296
2299
  statusEnteredAt: string | null;
2297
2300
  activityAt: string | null;
2298
2301
  scripts: {
@@ -3005,13 +3008,14 @@ export declare const WSOutboundMessageSchema: {
3005
3008
  name: import("zod").ZodString;
3006
3009
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
3007
3010
  archivingAt: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
3008
- status: import("zod").ZodEnum<{
3011
+ status: import("zod").ZodCatch<import("zod").ZodEnum<{
3009
3012
  running: "running";
3010
3013
  attention: "attention";
3011
3014
  needs_input: "needs_input";
3012
3015
  failed: "failed";
3016
+ pending: "pending";
3013
3017
  done: "done";
3014
- }>;
3018
+ }>>;
3015
3019
  statusEnteredAt: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, import("zod").ZodTransform<string | null, string | null | undefined>>;
3016
3020
  activityAt: import("zod").ZodNullable<import("zod").ZodString>;
3017
3021
  diffStat: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
@@ -3189,7 +3193,7 @@ export declare const WSOutboundMessageSchema: {
3189
3193
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
3190
3194
  name: string;
3191
3195
  archivingAt: string | null;
3192
- status: "running" | "attention" | "needs_input" | "failed" | "done";
3196
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
3193
3197
  statusEnteredAt: string | null;
3194
3198
  activityAt: string | null;
3195
3199
  scripts: {
@@ -3292,7 +3296,7 @@ export declare const WSOutboundMessageSchema: {
3292
3296
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
3293
3297
  name: string;
3294
3298
  archivingAt: string | null;
3295
- status: "running" | "attention" | "needs_input" | "failed" | "done";
3299
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
3296
3300
  statusEnteredAt: string | null;
3297
3301
  activityAt: string | null;
3298
3302
  scripts: {
@@ -3475,6 +3479,17 @@ export declare const WSOutboundMessageSchema: {
3475
3479
  }>;
3476
3480
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
3477
3481
  labels: import("zod").ZodDefault<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>;
3482
+ agentPresets: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
3483
+ provider: import("zod").ZodOptional<import("zod").ZodString>;
3484
+ model: import("zod").ZodOptional<import("zod").ZodString>;
3485
+ modeId: import("zod").ZodOptional<import("zod").ZodString>;
3486
+ systemPrompt: import("zod").ZodOptional<import("zod").ZodString>;
3487
+ thinkingOptionId: import("zod").ZodOptional<import("zod").ZodString>;
3488
+ approvalPolicy: import("zod").ZodOptional<import("zod").ZodString>;
3489
+ sandboxMode: import("zod").ZodOptional<import("zod").ZodString>;
3490
+ networkAccess: import("zod").ZodOptional<import("zod").ZodBoolean>;
3491
+ webSearch: import("zod").ZodOptional<import("zod").ZodBoolean>;
3492
+ }, import("zod/v4/core").$strict>>>;
3478
3493
  phases: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
3479
3494
  id: import("zod").ZodString;
3480
3495
  title: import("zod").ZodString;
@@ -3514,6 +3529,17 @@ export declare const WSOutboundMessageSchema: {
3514
3529
  }>;
3515
3530
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
3516
3531
  labels: import("zod").ZodDefault<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>;
3532
+ agentPresets: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
3533
+ provider: import("zod").ZodOptional<import("zod").ZodString>;
3534
+ model: import("zod").ZodOptional<import("zod").ZodString>;
3535
+ modeId: import("zod").ZodOptional<import("zod").ZodString>;
3536
+ systemPrompt: import("zod").ZodOptional<import("zod").ZodString>;
3537
+ thinkingOptionId: import("zod").ZodOptional<import("zod").ZodString>;
3538
+ approvalPolicy: import("zod").ZodOptional<import("zod").ZodString>;
3539
+ sandboxMode: import("zod").ZodOptional<import("zod").ZodString>;
3540
+ networkAccess: import("zod").ZodOptional<import("zod").ZodBoolean>;
3541
+ webSearch: import("zod").ZodOptional<import("zod").ZodBoolean>;
3542
+ }, import("zod/v4/core").$strict>>>;
3517
3543
  phases: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
3518
3544
  id: import("zod").ZodString;
3519
3545
  title: import("zod").ZodString;
@@ -3553,6 +3579,17 @@ export declare const WSOutboundMessageSchema: {
3553
3579
  }>;
3554
3580
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
3555
3581
  labels: import("zod").ZodDefault<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>;
3582
+ agentPresets: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
3583
+ provider: import("zod").ZodOptional<import("zod").ZodString>;
3584
+ model: import("zod").ZodOptional<import("zod").ZodString>;
3585
+ modeId: import("zod").ZodOptional<import("zod").ZodString>;
3586
+ systemPrompt: import("zod").ZodOptional<import("zod").ZodString>;
3587
+ thinkingOptionId: import("zod").ZodOptional<import("zod").ZodString>;
3588
+ approvalPolicy: import("zod").ZodOptional<import("zod").ZodString>;
3589
+ sandboxMode: import("zod").ZodOptional<import("zod").ZodString>;
3590
+ networkAccess: import("zod").ZodOptional<import("zod").ZodBoolean>;
3591
+ webSearch: import("zod").ZodOptional<import("zod").ZodBoolean>;
3592
+ }, import("zod/v4/core").$strict>>>;
3556
3593
  phases: import("zod").ZodDefault<import("zod").ZodArray<import("zod").ZodObject<{
3557
3594
  id: import("zod").ZodString;
3558
3595
  title: import("zod").ZodString;
@@ -5009,13 +5046,14 @@ export declare const WSOutboundMessageSchema: {
5009
5046
  name: import("zod").ZodString;
5010
5047
  title: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
5011
5048
  archivingAt: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>>;
5012
- status: import("zod").ZodEnum<{
5049
+ status: import("zod").ZodCatch<import("zod").ZodEnum<{
5013
5050
  running: "running";
5014
5051
  attention: "attention";
5015
5052
  needs_input: "needs_input";
5016
5053
  failed: "failed";
5054
+ pending: "pending";
5017
5055
  done: "done";
5018
- }>;
5056
+ }>>;
5019
5057
  statusEnteredAt: import("zod").ZodPipe<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, import("zod").ZodTransform<string | null, string | null | undefined>>;
5020
5058
  activityAt: import("zod").ZodNullable<import("zod").ZodString>;
5021
5059
  diffStat: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodObject<{
@@ -5193,7 +5231,7 @@ export declare const WSOutboundMessageSchema: {
5193
5231
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
5194
5232
  name: string;
5195
5233
  archivingAt: string | null;
5196
- status: "running" | "attention" | "needs_input" | "failed" | "done";
5234
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
5197
5235
  statusEnteredAt: string | null;
5198
5236
  activityAt: string | null;
5199
5237
  scripts: {
@@ -5296,7 +5334,7 @@ export declare const WSOutboundMessageSchema: {
5296
5334
  workspaceKind: "worktree" | "checkout" | "directory" | "local_checkout";
5297
5335
  name: string;
5298
5336
  archivingAt: string | null;
5299
- status: "running" | "attention" | "needs_input" | "failed" | "done";
5337
+ status: "running" | "attention" | "needs_input" | "failed" | "pending" | "done";
5300
5338
  statusEnteredAt: string | null;
5301
5339
  activityAt: string | null;
5302
5340
  scripts: {
@@ -0,0 +1,33 @@
1
+ import { type LoopParamValues } from "../fleet/params.js";
2
+ import type { WorkflowAgentPreset, WorkflowTaskGraph } from "../messages.js";
3
+ import { type WorkflowDefinition } from "./definition.js";
4
+ /** Ambient run identity. Passed IN so the compiler stays a pure function of its
5
+ * arguments; the CLI stamps these. Never produced here. */
6
+ export interface WorkflowCompileSession {
7
+ id: string;
8
+ createdAt: string;
9
+ outputDirectory: string;
10
+ }
11
+ export interface CompileWorkflowArgs {
12
+ definition: WorkflowDefinition;
13
+ values: LoopParamValues;
14
+ session: WorkflowCompileSession;
15
+ }
16
+ export interface CompiledWorkflow {
17
+ graph: WorkflowTaskGraph;
18
+ agentPresets: Record<string, WorkflowAgentPreset>;
19
+ }
20
+ /**
21
+ * Compile one definition against one set of input values.
22
+ *
23
+ * Validates BEFORE it emits, and reports EVERY issue in one throw rather than stopping
24
+ * at the first: a definition can carry a bad input value, an undeclared token and an
25
+ * unknown agent name simultaneously, and three sequential throws would hide the second
26
+ * and third until the first is fixed. On any issue nothing is returned and no partial
27
+ * graph is observable.
28
+ *
29
+ * @throws {WorkflowDefinitionError} carrying the union of the value issues, the
30
+ * definition issues, and the unresolved-reference issues this module adds.
31
+ */
32
+ export declare function compileWorkflowDefinition(args: CompileWorkflowArgs): CompiledWorkflow;
33
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1,172 @@
1
+ import { defaultLoopParams, validateLoopParams } from "../fleet/params.js";
2
+ import { createWorkflowTokenPattern, renderWorkflowInputValue, SUBSTITUTABLE_STEP_FIELDS, validateWorkflowDefinition, WorkflowDefinitionError, } from "./definition.js";
3
+ /**
4
+ * Compile one definition against one set of input values.
5
+ *
6
+ * Validates BEFORE it emits, and reports EVERY issue in one throw rather than stopping
7
+ * at the first: a definition can carry a bad input value, an undeclared token and an
8
+ * unknown agent name simultaneously, and three sequential throws would hide the second
9
+ * and third until the first is fixed. On any issue nothing is returned and no partial
10
+ * graph is observable.
11
+ *
12
+ * @throws {WorkflowDefinitionError} carrying the union of the value issues, the
13
+ * definition issues, and the unresolved-reference issues this module adds.
14
+ */
15
+ export function compileWorkflowDefinition(args) {
16
+ const { definition, session, values } = args;
17
+ // Defaults on the LEFT so a supplied value always wins over a declared default.
18
+ // `defaultLoopParams` only emits a key when the field's `default` parses as a
19
+ // `LoopParamValue`, so a field with no default contributes nothing here - which is
20
+ // exactly the gap the declared-but-unresolved issue below closes.
21
+ const merged = {
22
+ ...defaultLoopParams({ fields: definition.inputs }),
23
+ ...values,
24
+ };
25
+ const declaredKeys = new Set(definition.inputs.map((field) => field.key));
26
+ const valueIssues = validateLoopParams({ fields: definition.inputs }, merged);
27
+ // Each of the three failure shapes has exactly ONE reporter. A required input that is
28
+ // missing is already located at `inputs[<key>]`, so the reference pass below stays
29
+ // silent about it rather than restating the same root cause at every step that
30
+ // mentions it.
31
+ const reportedKeys = new Set(valueIssues.map((issue) => issue.key));
32
+ const rendered = definition.steps.map((step) => renderStepFields({ step, values: merged, declaredKeys, reportedKeys }));
33
+ const issues = [
34
+ ...valueIssues.map((issue) => ({
35
+ path: `inputs[${issue.key}]`,
36
+ message: issue.message,
37
+ })),
38
+ // Deliberately duplicated with `parseWorkflowDefinition`, and it stays: the two
39
+ // guard different entry points - a CLI that parsed a file, versus any caller
40
+ // invoking this compiler directly with a hand-built object.
41
+ ...validateWorkflowDefinition(definition),
42
+ ...rendered.flatMap((entry) => entry.issues),
43
+ ];
44
+ if (issues.length > 0) {
45
+ throw new WorkflowDefinitionError(issues);
46
+ }
47
+ const graph = {
48
+ // What the manager's `title ?? graph.goal` default reads, so `--title` stays optional.
49
+ goal: definition.name,
50
+ // Ported ballast: the CLI's reusable master-prompt template. Zero readers on
51
+ // 25fe57635 (only the TS interface and the Zod schema mention it).
52
+ masterPrompt: "",
53
+ metadata: {
54
+ // Emitted EMPTY on purpose. `resolveExecutionLayers` honours a declared list only
55
+ // when non-empty, then falls through to Kahn layering over `dependencies`. A
56
+ // precomputed list here would be a second derivation that can disagree with `needs`.
57
+ executionLayers: [],
58
+ // Upper bound, not a cap - nothing enforces it. Zero readers.
59
+ maxParallelism: definition.steps.length,
60
+ totalTasks: definition.steps.length,
61
+ // Unknown. Zero readers.
62
+ estimatedDuration: 0,
63
+ // Per-file task generation is a CLI concept with no paseo reader.
64
+ perFileMode: false,
65
+ // `storyFormat` and `totalFiles` are omitted, not defaulted: both are optional and
66
+ // neither has a definition-side vocabulary, so `false`/`0` would be a claim where
67
+ // an absent key is honestly "not specified".
68
+ },
69
+ // Ambient run identity, supplied by the caller so this stays a pure function.
70
+ // `outputDirectory` has zero readers on 25fe57635 but is schema-required.
71
+ session,
72
+ tasks: rendered.map(buildTask),
73
+ };
74
+ // Shallow copy: a caller mutating the returned presets must not reach back into the
75
+ // definition. Populating them onto the wire is Story 3.3's job.
76
+ return { graph, agentPresets: { ...definition.agents } };
77
+ }
78
+ /** Substitute every token in one field's text, iterating Story 3.1's scanner. */
79
+ function substituteTokens(args) {
80
+ const issues = [];
81
+ // A FRESH pattern per call. A module-level `/g` regex carries `lastIndex` between
82
+ // calls, which would make the result depend on scan order.
83
+ const text = args.text.replace(createWorkflowTokenPattern(), (raw, body) => {
84
+ const name = body.trim();
85
+ if (!args.declaredKeys.has(name)) {
86
+ // Undeclared, or malformed (a body that is not a bare identifier is never a
87
+ // declared key). `validateWorkflowDefinition` already reports both, located at
88
+ // this same path, so reporting again here would double-count one mistake. The
89
+ // raw token is returned unrendered; the union above is non-empty, so the caller
90
+ // throws and this interim string is never observable.
91
+ return raw;
92
+ }
93
+ const value = args.values[name];
94
+ if (value === undefined) {
95
+ if (args.reportedKeys.has(name)) {
96
+ // Already located at `inputs[<name>]` by the value pass (required and missing).
97
+ return raw;
98
+ }
99
+ // Declared, referenced, and has no value. `validateLoopParams` cannot catch this
100
+ // (a non-required field with no value is legal to it) and Story 3.1 cannot
101
+ // (the input IS declared). Only the compiler knows the token is referenced.
102
+ // Reporting it is what keeps the token from rendering as "" or "undefined".
103
+ issues.push({
104
+ path: args.path,
105
+ message: `step "${args.stepId}" references ${raw}, which is a declared input with no value: supply --input ${name}=... or give the field a default`,
106
+ });
107
+ return raw;
108
+ }
109
+ return renderWorkflowInputValue(value);
110
+ });
111
+ return { text, issues };
112
+ }
113
+ /**
114
+ * Render every substitutable field of one step.
115
+ *
116
+ * Iterates `SUBSTITUTABLE_STEP_FIELDS` rather than a second hard-coded list, so this
117
+ * module and Story 3.1's validator cannot disagree about which fields accept a token.
118
+ * `id` and `needs` are absent from that constant on purpose: a topology that depends on
119
+ * runtime values could not be validated at authoring time.
120
+ */
121
+ function renderStepFields(args) {
122
+ const fields = {};
123
+ const issues = [];
124
+ for (const field of SUBSTITUTABLE_STEP_FIELDS) {
125
+ const text = args.step[field];
126
+ if (text === undefined) {
127
+ continue;
128
+ }
129
+ const result = substituteTokens({
130
+ text,
131
+ values: args.values,
132
+ declaredKeys: args.declaredKeys,
133
+ reportedKeys: args.reportedKeys,
134
+ path: `steps[${args.step.id}].${field}`,
135
+ stepId: args.step.id,
136
+ });
137
+ fields[field] = result.text;
138
+ issues.push(...result.issues);
139
+ }
140
+ return { step: args.step, fields, issues };
141
+ }
142
+ /** One task node per step, in definition order. */
143
+ function buildTask(rendered) {
144
+ const { fields, step } = rendered;
145
+ // `step.id` never carries a token, so a fallback that came from it needs no rendering.
146
+ const title = fields.title ?? step.id;
147
+ return {
148
+ id: step.id, // never substituted
149
+ dependencies: step.needs, // never substituted
150
+ // Falls back through the SUBSTITUTED title before the id, so a token in the title
151
+ // reaches the description rather than the description inheriting an unrendered one.
152
+ description: fields.description ?? title,
153
+ // `prompt` is required by `WorkflowStepSchema`, so `renderStepFields` always renders
154
+ // it; the fallback exists only to keep the expression total.
155
+ prompt: fields.prompt ?? step.prompt,
156
+ title,
157
+ // `""` is the schema-satisfying empty, and `outputFile` has no reader anywhere.
158
+ outputFile: fields.outputFile ?? "",
159
+ // Ported ballast from the bmad-workflow CLI. Verified zero readers on 25fe57635:
160
+ // the only occurrences of `parallelizable` in the tree are its two declarations.
161
+ parallelizable: true,
162
+ // Same: no reader. The definition has no duration vocabulary and this epic does not
163
+ // invent one.
164
+ estimatedMinutes: 0,
165
+ // Conditional spread so the key is ABSENT rather than present-with-`undefined`: the
166
+ // schema says optional, and absent is the honest encoding of "this step names no agent".
167
+ ...(step.agent === undefined ? {} : { agentType: step.agent }),
168
+ // `targetFiles` is never emitted: the definition has no vocabulary for it and this
169
+ // epic does not invent one.
170
+ };
171
+ }
172
+ //# sourceMappingURL=compile.js.map