@runcofactor/cli 0.10.0 → 0.13.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.
Files changed (148) hide show
  1. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
  2. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
  3. package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
  4. package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
  5. package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
  6. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  7. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  8. package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
  9. package/dist/chunk-CQE46HSM.js.map +1 -0
  10. package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
  11. package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
  12. package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
  13. package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
  14. package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
  15. package/dist/chunk-G4UYGRZR.js +2 -0
  16. package/dist/chunk-JZMJUPZP.js +92 -0
  17. package/dist/chunk-JZMJUPZP.js.map +1 -0
  18. package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
  19. package/dist/chunk-NL7CVG56.js.map +1 -0
  20. package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
  21. package/dist/chunk-PZ4LCN6K.js.map +1 -0
  22. package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
  23. package/dist/chunk-QGI4UCJX.js.map +1 -0
  24. package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
  25. package/dist/chunk-QQVWGWPI.js.map +1 -0
  26. package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
  27. package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
  28. package/dist/chunk-YN763NWQ.js.map +1 -0
  29. package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
  30. package/dist/chunk-Z5LUTUDN.js.map +1 -0
  31. package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
  32. package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
  33. package/dist/credential-LO2SEEHU.js.map +1 -0
  34. package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
  35. package/dist/credential-template-ZN7W7734.js.map +1 -0
  36. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
  37. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
  38. package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
  39. package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
  40. package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
  41. package/dist/doctor-NZPUSCRE.js.map +1 -0
  42. package/dist/environment-RQVU4P5M.js +221 -0
  43. package/dist/environment-RQVU4P5M.js.map +1 -0
  44. package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
  45. package/dist/feature-flag-XG26RURR.js.map +1 -0
  46. package/dist/index.js +57 -48
  47. package/dist/index.js.map +1 -1
  48. package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
  49. package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
  50. package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
  51. package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
  52. package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
  53. package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
  54. package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
  55. package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
  56. package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
  57. package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
  58. package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
  59. package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
  60. package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
  61. package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
  62. package/dist/mcp-B5Y5CDMQ.js.map +1 -0
  63. package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
  64. package/dist/property-NJ4SRXQG.js.map +1 -0
  65. package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
  66. package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
  67. package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
  68. package/dist/record-WAO36O7G.js.map +1 -0
  69. package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
  70. package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
  71. package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
  72. package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
  73. package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
  74. package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
  75. package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
  76. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
  77. package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
  78. package/dist/session-Y5RHU54D.js.map +1 -0
  79. package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
  80. package/dist/skill-23WAHUOB.js.map +1 -0
  81. package/dist/skills/debug-run/SKILL.md +33 -22
  82. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  83. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  84. package/dist/skills/debug-run/version.json +1 -1
  85. package/dist/skills/optimize-cache/SKILL.md +206 -0
  86. package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
  87. package/dist/spec-LM5XHOQ5.js.map +1 -0
  88. package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
  89. package/dist/src-22RI5VP2.js.map +1 -0
  90. package/dist/src-BGDRMOKC.js +5748 -0
  91. package/dist/src-BGDRMOKC.js.map +1 -0
  92. package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
  93. package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
  94. package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
  95. package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
  96. package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
  97. package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
  98. package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
  99. package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
  100. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
  101. package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
  102. package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
  103. package/dist/triage-TT4BMDBU.js.map +1 -0
  104. package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
  105. package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
  106. package/dist/workflow-2CX3ZNOQ.js.map +1 -0
  107. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
  108. package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
  109. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
  110. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
  111. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
  112. package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
  113. package/package.json +2 -1
  114. package/dist/chunk-4PNOV7OV.js.map +0 -1
  115. package/dist/chunk-A4OFTUVV.js.map +0 -1
  116. package/dist/chunk-B2VISN2V.js.map +0 -1
  117. package/dist/chunk-GKY2KXO5.js.map +0 -1
  118. package/dist/chunk-JICSYFRJ.js +0 -46
  119. package/dist/chunk-JICSYFRJ.js.map +0 -1
  120. package/dist/chunk-QFDOWABX.js.map +0 -1
  121. package/dist/chunk-SJJW77WN.js.map +0 -1
  122. package/dist/chunk-WOHL4FTB.js.map +0 -1
  123. package/dist/credential-COLTBV6D.js.map +0 -1
  124. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  125. package/dist/doctor-OTSIWV6G.js.map +0 -1
  126. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  127. package/dist/mcp-54IJEOUA.js.map +0 -1
  128. package/dist/property-UXLOLPTD.js.map +0 -1
  129. package/dist/record-4TR2UIIS.js.map +0 -1
  130. package/dist/session-MYFNZPRP.js.map +0 -1
  131. package/dist/skill-KM34LDMO.js.map +0 -1
  132. package/dist/spec-RA3CFEKG.js.map +0 -1
  133. package/dist/src-LB4HBTJQ.js.map +0 -1
  134. package/dist/workflow-XYFH5LTP.js.map +0 -1
  135. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  136. /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
  137. /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
  138. /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
  139. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
  140. /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
  141. /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
  142. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
  143. /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
  144. /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
  145. /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
  146. /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
  147. /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
  148. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
2
  import {
3
3
  packageName
4
- } from "./chunk-JICSYFRJ.js";
4
+ } from "./chunk-JZMJUPZP.js";
5
5
 
6
6
  // src/update-check.ts
7
7
  import fs from "fs/promises";
@@ -85,4 +85,4 @@ export {
85
85
  checkForUpdate,
86
86
  clearUpdateCache
87
87
  };
88
- //# sourceMappingURL=chunk-N7HFALX3.js.map
88
+ //# sourceMappingURL=chunk-VGTKUOYS.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
2
  import {
3
3
  InlineAssertionSchema
4
- } from "./chunk-SJJW77WN.js";
4
+ } from "./chunk-QGI4UCJX.js";
5
5
 
6
6
  // ../canary-spec/src/parse.ts
7
7
  import yaml from "yaml";
@@ -26,7 +26,9 @@ var FrontmatterSchema = z.object({
26
26
  * create. On recompile, the compiler keys the diff on this ID.
27
27
  */
28
28
  workflowId: z.string().uuid().optional(),
29
- status: z.enum(["draft", "published"]).optional(),
29
+ // 'published' is a deprecated legacy value (pre approval/promotion split);
30
+ // accepted for old spec files and normalized to 'draft'.
31
+ status: z.enum(["draft", "approved", "published"]).transform((s) => s === "published" ? "draft" : s).optional(),
30
32
  flowType: z.enum(["standard", "setup", "seed", "teardown"]).optional(),
31
33
  /**
32
34
  * Credential the journey runs as. Required for authenticated specs (the
@@ -41,7 +43,7 @@ var FrontmatterSchema = z.object({
41
43
  sandboxTemplate: z.string().optional(),
42
44
  environments: z.union([
43
45
  z.object({ only: z.array(z.string()).min(1) }),
44
- z.object({ types: z.array(z.enum(["permanent", "ephemeral", "local"])).min(1) })
46
+ z.object({ types: z.array(z.enum(["permanent", "ephemeral", "local", "sandbox"])).min(1) })
45
47
  ]).optional(),
46
48
  /**
47
49
  * Teardown linkage for `flowType: seed` workflows. Two surface forms:
@@ -145,7 +147,7 @@ var ActionNodeSchema = NodeBase.extend({
145
147
  var AssertionNodeSchema = NodeBase.extend({
146
148
  kind: z.literal("assertion"),
147
149
  condition: z.string().min(1),
148
- severity: z.enum(["warn", "fail"]).default("warn"),
150
+ severity: z.enum(["warn", "fail", "fatal"]).default("fatal"),
149
151
  expectedOutcome: z.string().optional(),
150
152
  alwaysUseAgent: z.boolean().optional()
151
153
  });
@@ -158,7 +160,14 @@ var SeedNodeSchema = NodeBase.extend({
158
160
  /** Per-request timeout (seconds) for the seed/teardown api_sequence calls (API mode only). */
159
161
  apiTimeoutSeconds: z.number().int().positive().optional(),
160
162
  scope: z.enum(["inline", "suite"]).optional(),
161
- runTeardown: z.boolean().optional()
163
+ runTeardown: z.boolean().optional(),
164
+ /**
165
+ * Per-node overrides for the seed flow's `kind: 'value'` workflow inputs,
166
+ * keyed by input name. Only honored for inline-scoped seeds — suite-scoped
167
+ * seeds run with the seed flow's input defaults. Spec syntax:
168
+ * `- input.<name>: <value>` sub-bullets.
169
+ */
170
+ inputs: z.record(z.string(), z.string()).optional()
162
171
  });
163
172
  var SetupNodeSchema = NodeBase.extend({
164
173
  kind: z.literal("setup"),
@@ -195,7 +204,10 @@ var AudioPlayNodeSchema = NodeBase.extend({
195
204
  var AudioAssertionNodeSchema = NodeBase.extend({
196
205
  kind: z.literal("audioAssertion"),
197
206
  label: z.string().min(1),
198
- severity: z.enum(["warn", "fail"]).default("fail"),
207
+ // Audio default stays 'fail' (not 'fatal' like standalone assertions):
208
+ // audio assertions never halt mid-run, so fail ≈ fatal here and fail is the
209
+ // historical default.
210
+ severity: z.enum(["warn", "fail", "fatal"]).default("fail"),
199
211
  prompt: z.string().optional(),
200
212
  model: z.enum(["gpt-audio", "gemini-2.x"]).optional(),
201
213
  minDurationMs: z.number().int().nonnegative().optional(),
@@ -221,6 +233,21 @@ var ApiSequenceNodeSchema = NodeBase.extend({
221
233
  */
222
234
  sidecar: z.string().min(1).optional()
223
235
  });
236
+ var SandboxActionNodeSchema = NodeBase.extend({
237
+ kind: z.literal("sandboxAction"),
238
+ /** Slug of the sandbox action; resolved against the run env's template at run time. */
239
+ actionSlug: z.string().min(1),
240
+ /** Cosmetic display label. */
241
+ label: z.string().optional(),
242
+ /** Input params; values may carry `$var.*` / macro tokens (passed verbatim). */
243
+ params: z.record(z.string(), z.string()).optional(),
244
+ /**
245
+ * Output variable keys the action produces (denormalized from its
246
+ * outputSchema). Optional — compile resolves them from the action when
247
+ * omitted and a verified template declares the slug.
248
+ */
249
+ outputs: z.array(z.string().min(1)).optional()
250
+ });
224
251
  var EndNodeSchema = NodeBase.extend({
225
252
  kind: z.literal("end"),
226
253
  outcome: z.enum(["pass", "fail"]).default("pass")
@@ -238,6 +265,7 @@ var JourneyNodeSchema = z.union([
238
265
  AudioPlayNodeSchema,
239
266
  AudioAssertionNodeSchema,
240
267
  ApiSequenceNodeSchema,
268
+ SandboxActionNodeSchema,
241
269
  EndNodeSchema
242
270
  ]);
243
271
  var SpecAstSchema = z.object({
@@ -604,6 +632,7 @@ function parseJourneyBlock(lines, start, end, baseIndent, startLine, issues) {
604
632
  const subBullets = readBullets(lines, i + 1 + descLineCount, end, baseIndent);
605
633
  consumedMain += subBullets.consumed;
606
634
  const opts = parseKvBullets(subBullets.bullets);
635
+ const inputs = parseSeedInputBullets(subBullets.bullets);
607
636
  nodes.push({
608
637
  ...idMeta,
609
638
  kind: "seed",
@@ -614,7 +643,8 @@ function parseJourneyBlock(lines, start, end, baseIndent, startLine, issues) {
614
643
  mode: opts.mode,
615
644
  apiTimeoutSeconds: opts.apiTimeoutSeconds === void 0 ? void 0 : Number(opts.apiTimeoutSeconds),
616
645
  scope: opts.scope,
617
- runTeardown: typeof opts.runTeardown === "string" ? opts.runTeardown !== "false" : void 0
646
+ runTeardown: typeof opts.runTeardown === "string" ? opts.runTeardown !== "false" : void 0,
647
+ inputs
618
648
  });
619
649
  i += consumedMain;
620
650
  continue;
@@ -683,7 +713,7 @@ function parseJourneyBlock(lines, start, end, baseIndent, startLine, issues) {
683
713
  kind: "assertion",
684
714
  condition: stripPunctuation(condition),
685
715
  description,
686
- severity: opts.severity ?? "warn",
716
+ severity: opts.severity ?? "fatal",
687
717
  expectedOutcome: typeof opts.expectedOutcome === "string" ? opts.expectedOutcome : void 0,
688
718
  alwaysUseAgent: opts.alwaysUseAgent === "true" ? true : void 0
689
719
  });
@@ -728,6 +758,26 @@ function parseJourneyBlock(lines, start, end, baseIndent, startLine, issues) {
728
758
  i += consumedMain;
729
759
  continue;
730
760
  }
761
+ if (lower.startsWith("sandbox action")) {
762
+ const slug = stripPunctuation(
763
+ leadIn.replace(/^sandbox action\s*[—-]\s*/i, "").trim() || remainder
764
+ );
765
+ const subBullets = readBullets(lines, i + 1 + descLineCount, end, baseIndent);
766
+ consumedMain += subBullets.consumed;
767
+ const opts = parseKvBullets(subBullets.bullets);
768
+ const params = parseActionParamBullets(subBullets.bullets);
769
+ nodes.push({
770
+ ...idMeta,
771
+ kind: "sandboxAction",
772
+ actionSlug: slug,
773
+ description,
774
+ label: typeof opts.label === "string" ? opts.label : void 0,
775
+ params,
776
+ outputs: typeof opts.outputs === "string" ? opts.outputs.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0
777
+ });
778
+ i += consumedMain;
779
+ continue;
780
+ }
731
781
  if (lower.startsWith("api sequence")) {
732
782
  const title = leadIn.replace(/^api sequence\s*[—-]\s*/i, "").trim();
733
783
  const subBullets = readBullets(lines, i + 1 + descLineCount, end, baseIndent);
@@ -847,10 +897,11 @@ function parseActionBullets(bullets, lineNo, issues) {
847
897
  let captureExtraMs;
848
898
  let alwaysUseAgent;
849
899
  for (const b of bullets) {
850
- const fieldM = /^field\s+(?:"([^"]+)"|([A-Za-z0-9_-]+))\s*(?:\(([^)]+)\))?\s*:\s*(.+)$/i.exec(b);
900
+ const fieldM = /^field\s+(?:"([^"]+)"|(.+?))\s*(?:\(([^)]+)\))?\s*:\s*(.+)$/i.exec(b);
851
901
  if (fieldM) {
902
+ const name = (fieldM[1] ?? fieldM[2] ?? "").trim();
852
903
  fields.push({
853
- name: fieldM[1] ?? fieldM[2],
904
+ name,
854
905
  type: fieldM[3],
855
906
  value: stripQuotes(fieldM[4].trim())
856
907
  });
@@ -864,7 +915,7 @@ function parseActionBullets(bullets, lineNo, issues) {
864
915
  const assertM = /^assert\s*\(([^)]+)\)\s*:\s*(.+)$/i.exec(b);
865
916
  if (assertM) {
866
917
  const flags = assertM[1].split(",").map((s) => s.trim().toLowerCase());
867
- const severity = flags.includes("fail") ? "fail" : "warn";
918
+ const severity = flags.includes("fatal") ? "fatal" : flags.includes("fail") ? "fail" : flags.includes("warn") ? "warn" : "fatal";
868
919
  const agentic = flags.includes("agentic") || void 0;
869
920
  assertions.push({ condition: stripQuotes(assertM[2].trim()), severity, agentic });
870
921
  continue;
@@ -893,6 +944,26 @@ function parseKvBullets(bullets) {
893
944
  }
894
945
  return out;
895
946
  }
947
+ function parseSeedInputBullets(bullets) {
948
+ let out;
949
+ for (const b of bullets) {
950
+ const m = /^input\.([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/.exec(b);
951
+ if (!m) continue;
952
+ out ??= {};
953
+ out[m[1]] = stripQuotes(m[2].trim());
954
+ }
955
+ return out;
956
+ }
957
+ function parseActionParamBullets(bullets) {
958
+ let out;
959
+ for (const b of bullets) {
960
+ const m = /^param\.([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/.exec(b);
961
+ if (!m) continue;
962
+ out ??= {};
963
+ out[m[1]] = stripQuotes(m[2].trim());
964
+ }
965
+ return out;
966
+ }
896
967
  function numOpt(v) {
897
968
  if (v === void 0) return void 0;
898
969
  const n = Number(v);
@@ -923,17 +994,18 @@ var RESERVED_MACROS = /* @__PURE__ */ new Set([
923
994
  ]);
924
995
  function lintSpec(ast) {
925
996
  const issues = [];
997
+ const isRunFailing = (severity) => severity === "fail" || severity === "fatal";
926
998
  const hasFailAssertion = anyNode(ast.journey, (n) => {
927
- if (n.kind === "action") return n.assertions.some((a) => a.severity === "fail");
928
- if (n.kind === "assertion") return n.severity === "fail";
929
- if (n.kind === "audioAssertion") return n.severity === "fail";
999
+ if (n.kind === "action") return n.assertions.some((a) => isRunFailing(a.severity));
1000
+ if (n.kind === "assertion") return isRunFailing(n.severity);
1001
+ if (n.kind === "audioAssertion") return isRunFailing(n.severity);
930
1002
  return false;
931
1003
  });
932
1004
  if (!hasFailAssertion) {
933
1005
  issues.push({
934
1006
  severity: "warn",
935
- message: "Spec has no fail-severity assertions \u2014 the case will pass even if the feature is broken.",
936
- hint: "Add `- assert (fail): \u2026` under at least one action."
1007
+ message: "Spec has no fail- or fatal-severity assertions \u2014 the case will pass even if the feature is broken.",
1008
+ hint: "Add `- assert (fail): \u2026` (or `(fatal)` for must-stop preconditions) under at least one action."
937
1009
  });
938
1010
  }
939
1011
  if (!ast.frontmatter.requirementRef) {
@@ -944,6 +1016,7 @@ function lintSpec(ast) {
944
1016
  }
945
1017
  const declaredInputs = new Set(ast.inputs.map((i) => i.name));
946
1018
  const promotedVars = /* @__PURE__ */ new Set();
1019
+ declaredInputs.forEach((input) => promotedVars.add(input));
947
1020
  walkNodes(ast.journey, (n) => {
948
1021
  if (n.kind === "action") {
949
1022
  n.capture.forEach((c) => promotedVars.add(c));
@@ -958,7 +1031,7 @@ function lintSpec(ast) {
958
1031
  if (!promotedVars.has(name)) {
959
1032
  issues.push({
960
1033
  severity: "warn",
961
- message: `\`$var.${name}\` referenced in ${ctx} but not declared via \`- capture:\` or a field.`,
1034
+ message: `\`$var.${name}\` referenced in ${ctx} but not declared in \`## Inputs\`, via \`- capture:\`, or as a field.`,
962
1035
  hint: "If this is authored data, add `- capture: " + name + "` to the action that produces it."
963
1036
  });
964
1037
  }
@@ -1086,11 +1159,29 @@ function extractMacroRefs(s) {
1086
1159
  return out;
1087
1160
  }
1088
1161
 
1162
+ // ../canary-spec/src/input-refs.ts
1163
+ function normalizeDeclaredInputReferences(value, inputNames) {
1164
+ if (inputNames.size === 0 || !value.includes("$")) return value;
1165
+ return value.replace(
1166
+ /(^|[^\\])\$([A-Za-z_][A-Za-z0-9_-]*)/g,
1167
+ (match, prefix, name, offset, full) => {
1168
+ if (!inputNames.has(name)) return match;
1169
+ const next = full[offset + match.length];
1170
+ const afterDot = full[offset + match.length + 1];
1171
+ if (next === "." && afterDot !== void 0 && /[A-Za-z0-9_-]/.test(afterDot)) {
1172
+ return match;
1173
+ }
1174
+ return `${prefix}$var.${name}`;
1175
+ }
1176
+ );
1177
+ }
1178
+
1089
1179
  // ../canary-spec/src/emit.ts
1090
1180
  function emitSpec(ast, opts = {}) {
1091
1181
  const { appendDefaultEnd = true } = opts;
1092
1182
  const nodes = [];
1093
- for (const n of ast.journey) emitNode(n, nodes);
1183
+ const inputNames = new Set(ast.inputs.map((i) => i.name));
1184
+ for (const n of ast.journey) emitNode(n, nodes, inputNames);
1094
1185
  if (ast.frontmatter.authenticated) {
1095
1186
  let insertAt = 0;
1096
1187
  while (insertAt < nodes.length && (nodes[insertAt].nodeType === "seed" || nodes[insertAt].nodeType === "setup")) {
@@ -1214,24 +1305,27 @@ function loginConfig(ref, opts) {
1214
1305
  ...performLogin ? { performLogin: true } : {}
1215
1306
  };
1216
1307
  }
1217
- function emitNode(n, out) {
1308
+ function emitNode(n, out, inputNames) {
1309
+ const inputRef = (value) => normalizeDeclaredInputReferences(value, inputNames);
1218
1310
  const meta = {
1219
1311
  ...n.nodeId ? { nodeId: n.nodeId } : {},
1220
1312
  ...n.sourceLine !== void 0 ? { sourceLine: n.sourceLine } : {}
1221
1313
  };
1222
1314
  switch (n.kind) {
1223
- case "navigate":
1315
+ case "navigate": {
1316
+ const pageUrl = inputRef(n.pageUrl);
1224
1317
  out.push({
1225
1318
  ...meta,
1226
1319
  nodeType: "navigate",
1227
1320
  configJson: {
1228
- title: `Go to ${shortPath(n.pageUrl)}`,
1229
- description: n.description ?? `Navigate to ${n.pageUrl}`,
1230
- pageUrl: n.pageUrl,
1321
+ title: `Go to ${shortPath(pageUrl)}`,
1322
+ description: inputRef(n.description ?? `Navigate to ${pageUrl}`),
1323
+ pageUrl,
1231
1324
  ...n.openInNewTab ? { openInNewTab: true } : {}
1232
1325
  }
1233
1326
  });
1234
1327
  return;
1328
+ }
1235
1329
  case "switchRole": {
1236
1330
  const ref = n.credentialRef;
1237
1331
  const fallbackDesc = ref.kind === "credential" ? `Re-authenticate as ${ref.name}` : ref.kind === "seedOutput" ? `Re-authenticate as seed-provisioned ${ref.ref}` : `Re-authenticate as ${ref.role}`;
@@ -1240,7 +1334,7 @@ function emitNode(n, out) {
1240
1334
  nodeType: "login",
1241
1335
  configJson: loginConfig(ref, {
1242
1336
  titlePrefix: "Switch role to",
1243
- description: n.description ?? fallbackDesc,
1337
+ description: inputRef(n.description ?? fallbackDesc),
1244
1338
  performLogin: true
1245
1339
  })
1246
1340
  });
@@ -1251,7 +1345,7 @@ function emitNode(n, out) {
1251
1345
  name: f.name,
1252
1346
  label: humanizeLabel(f.name),
1253
1347
  type: f.type ?? "text",
1254
- value: f.value
1348
+ value: inputRef(f.value)
1255
1349
  }));
1256
1350
  const inputValues = fieldEntries.length > 0 ? Object.fromEntries(
1257
1351
  fieldEntries.filter((f) => f.value !== void 0 && f.value !== "").map((f) => [f.name, f.value])
@@ -1260,8 +1354,8 @@ function emitNode(n, out) {
1260
1354
  ...meta,
1261
1355
  nodeType: "action",
1262
1356
  configJson: {
1263
- customActionTitle: n.title,
1264
- actionDescription: n.description ?? n.title,
1357
+ customActionTitle: inputRef(n.title),
1358
+ actionDescription: inputRef(n.description ?? n.title),
1265
1359
  ...fieldEntries.length > 0 ? {
1266
1360
  formFields: fieldEntries,
1267
1361
  dataRequirements: fieldEntries.map(({ name, label, type }) => ({
@@ -1275,7 +1369,7 @@ function emitNode(n, out) {
1275
1369
  ...n.capture.length > 0 ? { declaredOutputs: n.capture } : {},
1276
1370
  ...n.assertions.length > 0 ? {
1277
1371
  inlineAssertions: n.assertions.map((a) => ({
1278
- condition: a.condition,
1372
+ condition: inputRef(a.condition),
1279
1373
  severity: a.severity,
1280
1374
  ...a.agentic ? { agentic: true } : {}
1281
1375
  }))
@@ -1296,8 +1390,8 @@ function emitNode(n, out) {
1296
1390
  nodeType: "assertion",
1297
1391
  configJson: {
1298
1392
  title: "Assert",
1299
- description: n.description ?? n.condition,
1300
- condition: n.condition,
1393
+ description: inputRef(n.description ?? n.condition),
1394
+ condition: inputRef(n.condition),
1301
1395
  severity: n.severity,
1302
1396
  ...n.expectedOutcome ? { expectedOutcome: n.expectedOutcome } : {},
1303
1397
  ...n.alwaysUseAgent ? { alwaysUseAgent: true } : {}
@@ -1323,7 +1417,10 @@ function emitNode(n, out) {
1323
1417
  ...n.mode ? { executionMode: n.mode } : {},
1324
1418
  ...n.apiTimeoutSeconds ? { apiTimeoutSeconds: n.apiTimeoutSeconds } : {},
1325
1419
  ...n.scope ? { scope: n.scope } : {},
1326
- ...n.runTeardown !== void 0 ? { runTeardown: n.runTeardown } : {}
1420
+ ...n.runTeardown !== void 0 ? { runTeardown: n.runTeardown } : {},
1421
+ // Spec `inputs` → runtime config key `inputValues` (same remap
1422
+ // convention as the other canonical-key mappings above).
1423
+ ...n.inputs && Object.keys(n.inputs).length > 0 ? { inputValues: n.inputs } : {}
1327
1424
  }
1328
1425
  });
1329
1426
  return;
@@ -1359,26 +1456,26 @@ function emitNode(n, out) {
1359
1456
  nodeType: "condition",
1360
1457
  configJson: {
1361
1458
  title: "If",
1362
- description: n.description ?? n.expression,
1459
+ description: inputRef(n.description ?? n.expression),
1363
1460
  // Canonical runtime key is `conditionDescription`.
1364
- conditionDescription: n.expression,
1365
- ...n.hints ? { evaluationHints: n.hints } : {}
1461
+ conditionDescription: inputRef(n.expression),
1462
+ ...n.hints ? { evaluationHints: inputRef(n.hints) } : {}
1366
1463
  }
1367
1464
  });
1368
- n.thenBranch.forEach((c) => emitNode(c, out));
1369
- n.elseBranch.forEach((c) => emitNode(c, out));
1465
+ n.thenBranch.forEach((c) => emitNode(c, out, inputNames));
1466
+ n.elseBranch.forEach((c) => emitNode(c, out, inputNames));
1370
1467
  return;
1371
1468
  case "notification":
1372
1469
  out.push({
1373
1470
  ...meta,
1374
1471
  nodeType: "notification",
1375
1472
  configJson: {
1376
- title: `Notify ${n.to}`,
1377
- description: n.description ?? n.body,
1473
+ title: `Notify ${inputRef(n.to)}`,
1474
+ description: inputRef(n.description ?? n.body),
1378
1475
  channel: n.channel,
1379
- to: n.to,
1380
- body: n.body,
1381
- ...n.subject ? { subject: n.subject } : {},
1476
+ to: inputRef(n.to),
1477
+ body: inputRef(n.body),
1478
+ ...n.subject ? { subject: inputRef(n.subject) } : {},
1382
1479
  ...n.attach ? { attachmentRef: n.attach } : {}
1383
1480
  }
1384
1481
  });
@@ -1403,10 +1500,15 @@ function emitNode(n, out) {
1403
1500
  nodeType: "audio-assertion",
1404
1501
  configJson: {
1405
1502
  title: `Assert audio: ${n.label}`,
1406
- description: n.description ?? n.label,
1503
+ description: inputRef(n.description ?? n.label),
1407
1504
  label: n.label,
1408
1505
  severity: n.severity,
1409
- ...n.prompt ? { naturalLanguage: { prompt: n.prompt, ...n.model ? { model: n.model } : {} } } : {},
1506
+ ...n.prompt ? {
1507
+ naturalLanguage: {
1508
+ prompt: inputRef(n.prompt),
1509
+ ...n.model ? { model: n.model } : {}
1510
+ }
1511
+ } : {},
1410
1512
  ...hasDeterministicAudioAssertion(n) ? {
1411
1513
  deterministic: {
1412
1514
  ...n.minDurationMs !== void 0 ? { minDurationMs: n.minDurationMs } : {},
@@ -1425,9 +1527,9 @@ function emitNode(n, out) {
1425
1527
  ...meta,
1426
1528
  nodeType: "api_sequence",
1427
1529
  configJson: {
1428
- customActionTitle: n.title,
1429
- description: n.description ?? n.title,
1430
- apiEndpointContext: n.apiEndpointContext,
1530
+ customActionTitle: inputRef(n.title),
1531
+ description: inputRef(n.description ?? n.title),
1532
+ apiEndpointContext: inputRef(n.apiEndpointContext),
1431
1533
  ...n.credentialInputName ? { credentialInputName: n.credentialInputName } : {},
1432
1534
  // Sidecar reference is metadata for the CLI sync layer — runtime
1433
1535
  // ignores unknown configJson keys, and the node-hash projection
@@ -1437,6 +1539,22 @@ function emitNode(n, out) {
1437
1539
  }
1438
1540
  });
1439
1541
  return;
1542
+ case "sandboxAction":
1543
+ out.push({
1544
+ ...meta,
1545
+ nodeType: "sandbox_action",
1546
+ configJson: {
1547
+ title: n.label ?? `Sandbox action: ${n.actionSlug}`,
1548
+ description: inputRef(n.description ?? n.label ?? n.actionSlug),
1549
+ actionSlug: n.actionSlug,
1550
+ ...n.label ? { actionLabel: n.label } : {},
1551
+ // Param values pass through verbatim — $var.* / macro tokens resolve
1552
+ // at run time against the execution state.
1553
+ ...n.params && Object.keys(n.params).length > 0 ? { params: n.params } : {},
1554
+ ...n.outputs && n.outputs.length > 0 ? { outputKeys: n.outputs } : {}
1555
+ }
1556
+ });
1557
+ return;
1440
1558
  case "end":
1441
1559
  out.push({
1442
1560
  ...meta,
@@ -1523,7 +1641,9 @@ function authoredProjection(nodeType, raw) {
1523
1641
  onFailure: str(c.onFailure) ?? str(c.seedOnFailure) ?? null,
1524
1642
  executionMode: str(c.executionMode) ?? str(c.seedExecutionMode) ?? null,
1525
1643
  scope: str(c.scope) ?? null,
1526
- runTeardown: typeof c.runTeardown === "boolean" ? c.runTeardown : null
1644
+ runTeardown: typeof c.runTeardown === "boolean" ? c.runTeardown : null,
1645
+ // Input overrides change what the seed provisions — authored identity.
1646
+ inputValues: seedInputValues(c)
1527
1647
  };
1528
1648
  case "setup":
1529
1649
  return {
@@ -1573,6 +1693,20 @@ function authoredProjection(nodeType, raw) {
1573
1693
  }
1574
1694
  case "api_sequence":
1575
1695
  return {};
1696
+ case "sandbox_action": {
1697
+ const params = obj(c.params) ?? {};
1698
+ const paramEntries = {};
1699
+ for (const key of Object.keys(params).sort()) {
1700
+ paramEntries[key] = str(params[key]) ?? null;
1701
+ }
1702
+ const outputKeys = Array.isArray(c.outputKeys) ? c.outputKeys.filter((k) => typeof k === "string").sort() : [];
1703
+ return {
1704
+ actionSlug: str(c.actionSlug) ?? null,
1705
+ label: str(c.actionLabel) ?? null,
1706
+ params: paramEntries,
1707
+ outputs: outputKeys
1708
+ };
1709
+ }
1576
1710
  case "end":
1577
1711
  return { outcome: str(c.outcome) ?? "pass" };
1578
1712
  default:
@@ -1638,6 +1772,13 @@ function trimText(v) {
1638
1772
  function str(v) {
1639
1773
  return typeof v === "string" ? v : void 0;
1640
1774
  }
1775
+ function seedInputValues(c) {
1776
+ if (!c.inputValues || typeof c.inputValues !== "object") return null;
1777
+ const entries = Object.entries(c.inputValues).filter(
1778
+ (e) => typeof e[1] === "string"
1779
+ );
1780
+ return entries.length > 0 ? Object.fromEntries(entries) : null;
1781
+ }
1641
1782
  function num(v) {
1642
1783
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
1643
1784
  }
@@ -1978,6 +2119,8 @@ function reconcileLinearEdges(finalNodeIds, liveEdges) {
1978
2119
  function workflowToSpec(workflow, opts = {}) {
1979
2120
  const lines = [];
1980
2121
  const sorted = [...workflow.nodes].sort((a, b) => a.orderIndex - b.orderIndex);
2122
+ const inputs = readInputs(workflow.inputsJson);
2123
+ const inputNames = new Set(inputs.map((i) => i.name));
1981
2124
  let firstNonSeed = 0;
1982
2125
  while (firstNonSeed < sorted.length && (sorted[firstNonSeed].nodeType === "seed" || sorted[firstNonSeed].nodeType === "setup")) {
1983
2126
  firstNonSeed++;
@@ -1988,7 +2131,7 @@ function workflowToSpec(workflow, opts = {}) {
1988
2131
  lines.push("---");
1989
2132
  lines.push(`name: ${yamlScalar(workflow.name)}`);
1990
2133
  lines.push(`workflowId: ${workflow.id}`);
1991
- if (workflow.status === "draft" || workflow.status === "quarantined") {
2134
+ if (workflow.status === "draft" || workflow.status === "approved") {
1992
2135
  lines.push(`status: ${workflow.status}`);
1993
2136
  }
1994
2137
  if (workflow.flowType && workflow.flowType !== "standard") {
@@ -2026,7 +2169,6 @@ function workflowToSpec(workflow, opts = {}) {
2026
2169
  lines.push(workflow.cleanup.trim());
2027
2170
  lines.push("");
2028
2171
  }
2029
- const inputs = readInputs(workflow.inputsJson);
2030
2172
  if (inputs.length > 0) {
2031
2173
  lines.push("## Inputs");
2032
2174
  for (const i of inputs) {
@@ -2072,7 +2214,7 @@ function workflowToSpec(workflow, opts = {}) {
2072
2214
  lines.push("");
2073
2215
  for (const node of sorted) {
2074
2216
  if (node === authLogin) continue;
2075
- const block = renderNode(node);
2217
+ const block = renderNode(node, inputNames);
2076
2218
  if (block.length === 0) continue;
2077
2219
  block[0] = `${block[0]} <!-- @id ${node.id} -->`;
2078
2220
  lines.push(...block);
@@ -2082,23 +2224,24 @@ function workflowToSpec(workflow, opts = {}) {
2082
2224
  lines.push("");
2083
2225
  return lines.join("\n");
2084
2226
  }
2085
- function renderNode(node) {
2227
+ function renderNode(node, inputNames) {
2086
2228
  const c = node.configJson ?? {};
2229
+ const inputRef = (value) => normalizeDeclaredInputReferences(value, inputNames);
2087
2230
  switch (node.nodeType) {
2088
2231
  case "login": {
2089
2232
  return [`**Switch role to ${renderLoginRef(c)}**`];
2090
2233
  }
2091
2234
  case "navigate":
2092
- return [`**Navigate to** \`${str2(c.pageUrl) ?? "/"}\`.`];
2235
+ return [`**Navigate to** \`${inputRef(str2(c.pageUrl) ?? "/")}\`.`];
2093
2236
  case "action": {
2094
- const title = authoredActionTitle(c);
2237
+ const title = inputRef(authoredActionTitle(c));
2095
2238
  const out = [`**Action \u2014 ${title}**`];
2096
2239
  const desc = str2(c.actionDescription);
2097
- if (desc && desc !== title) out.push(blockquote(desc));
2240
+ if (desc && inputRef(desc) !== title) out.push(blockquote(inputRef(desc)));
2098
2241
  for (const f of authoredActionFields(c)) {
2099
2242
  const typeSuffix = f.type && f.type !== "text" ? ` (${f.type})` : "";
2100
2243
  const name = /^[A-Za-z0-9_-]+$/.test(f.name) ? f.name : `"${f.name}"`;
2101
- out.push(`- field ${name}${typeSuffix}: ${inputValue(f.value)}`);
2244
+ out.push(`- field ${name}${typeSuffix}: ${inputValue(inputRef(f.value))}`);
2102
2245
  }
2103
2246
  for (const cap of arr2(c.declaredOutputs)) {
2104
2247
  if (typeof cap === "string") out.push(`- capture: ${cap}`);
@@ -2108,7 +2251,7 @@ function renderNode(node) {
2108
2251
  if (!cond) continue;
2109
2252
  const sev = str2(prop2(a, "severity")) ?? "fail";
2110
2253
  const flags = prop2(a, "agentic") === true ? `${sev}, agentic` : sev;
2111
- out.push(`- assert (${flags}): ${cond}`);
2254
+ out.push(`- assert (${flags}): ${inputRef(cond)}`);
2112
2255
  }
2113
2256
  if (c.captureAudio === true) {
2114
2257
  out.push("- captureAudio: true");
@@ -2120,10 +2263,10 @@ function renderNode(node) {
2120
2263
  return out;
2121
2264
  }
2122
2265
  case "assertion": {
2123
- const cond = str2(c.condition) ?? "?";
2266
+ const cond = inputRef(str2(c.condition) ?? "?");
2124
2267
  const out = [`**Assert** ${cond}`];
2125
2268
  const desc = str2(c.description);
2126
- if (desc && desc !== cond) out.push(blockquote(desc));
2269
+ if (desc && inputRef(desc) !== cond) out.push(blockquote(inputRef(desc)));
2127
2270
  const severity = str2(c.severity) ?? (c.strict === true ? "fail" : "warn");
2128
2271
  out.push(`- severity: ${severity}`);
2129
2272
  if (str2(c.expectedOutcome)) out.push(`- expectedOutcome: ${str2(c.expectedOutcome)}`);
@@ -2141,6 +2284,11 @@ function renderNode(node) {
2141
2284
  if (mode) out.push(`- mode: ${mode}`);
2142
2285
  if (str2(c.scope)) out.push(`- scope: ${str2(c.scope)}`);
2143
2286
  if (typeof c.runTeardown === "boolean") out.push(`- runTeardown: ${c.runTeardown}`);
2287
+ if (c.inputValues && typeof c.inputValues === "object") {
2288
+ for (const [key, value] of Object.entries(c.inputValues)) {
2289
+ if (typeof value === "string") out.push(`- input.${key}: ${value}`);
2290
+ }
2291
+ }
2144
2292
  return out;
2145
2293
  }
2146
2294
  case "setup": {
@@ -2156,14 +2304,15 @@ function renderNode(node) {
2156
2304
  }
2157
2305
  case "condition": {
2158
2306
  const expr = str2(c.conditionDescription) ?? str2(c.conditionExpression) ?? str2(c.description) ?? "?";
2159
- return expr.includes("\n") ? ["**If**", blockquote(expr)] : [`**If ${expr}**`];
2307
+ const normalized = inputRef(expr);
2308
+ return normalized.includes("\n") ? ["**If**", blockquote(normalized)] : [`**If ${normalized}**`];
2160
2309
  }
2161
2310
  case "notification": {
2162
2311
  const channel = str2(c.channel) ?? "email";
2163
- const to = str2(c.to) ?? "?";
2312
+ const to = inputRef(str2(c.to) ?? "?");
2164
2313
  const out = [`**Notify ${channel}: ${to}**`];
2165
- if (str2(c.body)) out.push(blockquote(str2(c.body)));
2166
- if (str2(c.subject)) out.push(`- subject: ${str2(c.subject)}`);
2314
+ if (str2(c.body)) out.push(blockquote(inputRef(str2(c.body))));
2315
+ if (str2(c.subject)) out.push(`- subject: ${inputRef(str2(c.subject))}`);
2167
2316
  if (str2(c.attachmentRef)) out.push(`- attach: ${str2(c.attachmentRef)}`);
2168
2317
  return out;
2169
2318
  }
@@ -2182,7 +2331,7 @@ function renderNode(node) {
2182
2331
  const model = str2(nl?.model) ?? str2(c.model);
2183
2332
  const out = [`**Assert audio** ${str2(c.label) ?? "?"}`];
2184
2333
  out.push(`- severity: ${str2(c.severity) ?? "fail"}`);
2185
- if (prompt) out.push(`- prompt: ${prompt}`);
2334
+ if (prompt) out.push(`- prompt: ${inputRef(prompt)}`);
2186
2335
  if (model) out.push(`- model: ${model}`);
2187
2336
  for (const [cfgKey, bulletKey] of [
2188
2337
  ["minDurationMs", "minDurationMs"],
@@ -2198,15 +2347,30 @@ function renderNode(node) {
2198
2347
  return out;
2199
2348
  }
2200
2349
  case "api_sequence": {
2201
- const title = str2(c.customActionTitle) ?? str2(c.title) ?? "API sequence";
2350
+ const title = inputRef(str2(c.customActionTitle) ?? str2(c.title) ?? "API sequence");
2202
2351
  const out = [`**API sequence \u2014 ${title}**`];
2203
- if (str2(c.apiEndpointContext)) out.push(blockquote(str2(c.apiEndpointContext)));
2352
+ if (str2(c.apiEndpointContext)) out.push(blockquote(inputRef(str2(c.apiEndpointContext))));
2204
2353
  if (str2(c.credentialInputName)) out.push(`- credential: ${str2(c.credentialInputName)}`);
2205
2354
  if (str2(c.__apiSequenceSidecar)) {
2206
2355
  out.push(`- apiSequence: ${str2(c.__apiSequenceSidecar)}`);
2207
2356
  }
2208
2357
  return out;
2209
2358
  }
2359
+ case "sandbox_action": {
2360
+ const slug = str2(c.actionSlug) ?? "?";
2361
+ const out = [`**Sandbox action \u2014 ${slug}**`];
2362
+ if (str2(c.description)) out.push(blockquote(inputRef(str2(c.description))));
2363
+ if (str2(c.actionLabel)) out.push(`- label: ${str2(c.actionLabel)}`);
2364
+ const params = obj2(c.params);
2365
+ if (params) {
2366
+ for (const [key, value] of Object.entries(params)) {
2367
+ if (typeof value === "string") out.push(`- param.${key}: ${value}`);
2368
+ }
2369
+ }
2370
+ const outputKeys = Array.isArray(c.outputKeys) ? c.outputKeys.filter((k) => typeof k === "string") : [];
2371
+ if (outputKeys.length > 0) out.push(`- outputs: ${outputKeys.join(", ")}`);
2372
+ return out;
2373
+ }
2210
2374
  case "end":
2211
2375
  return [str2(c.outcome) === "fail" ? "**End (fail)**." : "**End**."];
2212
2376
  default:
@@ -2456,4 +2620,4 @@ export {
2456
2620
  writeWorkflowId,
2457
2621
  writeNodeIds
2458
2622
  };
2459
- //# sourceMappingURL=chunk-B2VISN2V.js.map
2623
+ //# sourceMappingURL=chunk-YN763NWQ.js.map