@runcofactor/cli 0.15.1 → 0.16.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 (123) hide show
  1. package/dist/{account-62I7FG5G.js → account-ZMV7NEFM.js} +4 -4
  2. package/dist/account-ZMV7NEFM.js.map +1 -0
  3. package/dist/{api-catalog-CAJUASVC.js → api-catalog-6HNOGVPA.js} +3 -3
  4. package/dist/{attachment-RL4AZUJE.js → attachment-QAHR54HQ.js} +3 -3
  5. package/dist/{augmentor-GAF3MFY6.js → augmentor-7T3L6ZR4.js} +3 -3
  6. package/dist/{chunk-26U3DGUM.js → chunk-7YJTQCPX.js} +3 -5
  7. package/dist/chunk-7YJTQCPX.js.map +1 -0
  8. package/dist/{chunk-PXFDH6YL.js → chunk-A4ISG3VX.js} +55 -15
  9. package/dist/{chunk-PXFDH6YL.js.map → chunk-A4ISG3VX.js.map} +1 -1
  10. package/dist/{chunk-5R4HMSNX.js → chunk-AI3Y3AZH.js} +24834 -24813
  11. package/dist/chunk-AI3Y3AZH.js.map +1 -0
  12. package/dist/{chunk-HKQ5XQAQ.js → chunk-CEXV3MOC.js} +3 -3
  13. package/dist/{chunk-VKBTI5EF.js → chunk-D6AVOPOS.js} +2 -2
  14. package/dist/{chunk-ZXRVFFCY.js → chunk-DT2CEH2V.js} +2 -2
  15. package/dist/{chunk-IWKJQZJQ.js → chunk-GZVCNYR4.js} +55 -55
  16. package/dist/chunk-GZVCNYR4.js.map +1 -0
  17. package/dist/{chunk-CJEKEW7R.js → chunk-LFFWD5JZ.js} +2 -2
  18. package/dist/{chunk-UZ2ZA5A6.js → chunk-RGDIVW3O.js} +2 -2
  19. package/dist/{chunk-CFJXYPTF.js → chunk-UAJUZLDE.js} +11 -3
  20. package/dist/chunk-UAJUZLDE.js.map +1 -0
  21. package/dist/{credential-JGVSEGVH.js → credential-X6QKX473.js} +3 -3
  22. package/dist/{credential-template-CWWMOKB2.js → credential-template-GD3D4MBI.js} +3 -3
  23. package/dist/{debug-workflow-BXRU72YZ.js → debug-workflow-AAEOAME2.js} +6 -6
  24. package/dist/{src-CV57ZD6I.js → dist-QLTVBUMD.js} +7 -9
  25. package/dist/{docs-PVBXPTGR.js → docs-BVUEXBY3.js} +3 -3
  26. package/dist/{environment-WZKIO3R3.js → environment-WE7VVE6I.js} +3 -3
  27. package/dist/{feature-flag-K5REGGDY.js → feature-flag-VJO2TJFT.js} +9 -8
  28. package/dist/feature-flag-VJO2TJFT.js.map +1 -0
  29. package/dist/index.js +129 -44
  30. package/dist/index.js.map +1 -1
  31. package/dist/{init-FFUZO5D7.js → init-RQHHKJJD.js} +8 -8
  32. package/dist/{install-XRKEQAFZ.js → install-CQLHNZ6U.js} +3 -3
  33. package/dist/{issues-I3V6GEXU.js → issues-HK2JA5JS.js} +3 -3
  34. package/dist/{knobs-6WLJMB44.js → knobs-ETJAV6YM.js} +3 -3
  35. package/dist/{list-PHC7VYNS.js → list-F6DGGW3Z.js} +3 -3
  36. package/dist/{local-VGYRSW22.js → local-CMAY54I5.js} +5 -5
  37. package/dist/{login-QPU2BKG2.js → login-6CKG664C.js} +8 -8
  38. package/dist/{mcp-LA452TOA.js → mcp-GC2WOOZO.js} +5 -6
  39. package/dist/{mcp-LA452TOA.js.map → mcp-GC2WOOZO.js.map} +1 -1
  40. package/dist/metrics-IXICURAA.js +367 -0
  41. package/dist/metrics-IXICURAA.js.map +1 -0
  42. package/dist/{product-RSZV5HMB.js → product-BUJWPYKB.js} +4 -3
  43. package/dist/product-BUJWPYKB.js.map +1 -0
  44. package/dist/{property-YP4GHQAR.js → property-4EXPWRNT.js} +3 -3
  45. package/dist/{record-KWKHP2ZY.js → record-HL4QA7OX.js} +4 -4
  46. package/dist/{record-KWKHP2ZY.js.map → record-HL4QA7OX.js.map} +1 -1
  47. package/dist/{release-NDRHPW6V.js → release-2SMK5Y3V.js} +3 -3
  48. package/dist/{sandbox-OOCANWOG.js → sandbox-ORAPKOH3.js} +3 -3
  49. package/dist/{scenarios-2HRLCGFY.js → scenarios-DM5FDCAF.js} +3 -3
  50. package/dist/{session-M4ULYDYP.js → session-X6IYGWYG.js} +14 -16
  51. package/dist/{session-M4ULYDYP.js.map → session-X6IYGWYG.js.map} +1 -1
  52. package/dist/{skill-4DGPRDUC.js → skill-ZU5V2EE5.js} +24 -14
  53. package/dist/skill-ZU5V2EE5.js.map +1 -0
  54. package/dist/skills/optimize-cache/SKILL.md +9 -3
  55. package/dist/{spec-XZ2TKUFZ.js → spec-KO5QD6AY.js} +3 -3
  56. package/dist/{src-HL7HEJIW.js → src-DSD34JIX.js} +2 -2
  57. package/dist/{src-2FK3JHGW.js → src-HTGNKBUF.js} +15 -7
  58. package/dist/{src-2FK3JHGW.js.map → src-HTGNKBUF.js.map} +1 -1
  59. package/dist/{src-OU7544PM.js → src-PDYXACRP.js} +2 -2
  60. package/dist/{start-IYKF3C3X.js → start-2ECN4LX5.js} +6 -6
  61. package/dist/{tag-T45MMSCH.js → tag-R5I72HJ6.js} +4 -4
  62. package/dist/{task-MCUHAVTD.js → task-V3JMSJIM.js} +3 -3
  63. package/dist/{test-suite-BICBRSBP.js → test-suite-NOYVK6YX.js} +5 -5
  64. package/dist/{trace-2BAPMYAS.js → trace-NOYEKNO4.js} +4 -5
  65. package/dist/{triage-M3RZ2ASB.js → triage-4RFATHBE.js} +4 -4
  66. package/dist/utilities-3WMXE743.js +441 -0
  67. package/dist/utilities-3WMXE743.js.map +1 -0
  68. package/dist/{workflow-4J23FV7K.js → workflow-DC7WOTTW.js} +13 -17
  69. package/dist/workflow-DC7WOTTW.js.map +1 -0
  70. package/dist/{workflow-cache-XPSK6KC7.js → workflow-cache-SDSI6SMF.js} +6 -6
  71. package/dist/{workflow-cache-XPSK6KC7.js.map → workflow-cache-SDSI6SMF.js.map} +1 -1
  72. package/dist/{workflow-debug-W4USZH35.js → workflow-debug-6UQYP67Q.js} +6 -7
  73. package/dist/{workflow-debug-W4USZH35.js.map → workflow-debug-6UQYP67Q.js.map} +1 -1
  74. package/dist/{workflow-proposals-5E3DTYZB.js → workflow-proposals-67AVGMHX.js} +6 -6
  75. package/package.json +1 -1
  76. package/dist/account-62I7FG5G.js.map +0 -1
  77. package/dist/chunk-26U3DGUM.js.map +0 -1
  78. package/dist/chunk-5R4HMSNX.js.map +0 -1
  79. package/dist/chunk-CFJXYPTF.js.map +0 -1
  80. package/dist/chunk-IWKJQZJQ.js.map +0 -1
  81. package/dist/chunk-KRWRUU6R.js +0 -114
  82. package/dist/chunk-KRWRUU6R.js.map +0 -1
  83. package/dist/feature-flag-K5REGGDY.js.map +0 -1
  84. package/dist/pdf-extract-YHE54XU4.js +0 -13
  85. package/dist/product-RSZV5HMB.js.map +0 -1
  86. package/dist/skill-4DGPRDUC.js.map +0 -1
  87. package/dist/triage-M3RZ2ASB.js.map +0 -1
  88. package/dist/workflow-4J23FV7K.js.map +0 -1
  89. /package/dist/{api-catalog-CAJUASVC.js.map → api-catalog-6HNOGVPA.js.map} +0 -0
  90. /package/dist/{attachment-RL4AZUJE.js.map → attachment-QAHR54HQ.js.map} +0 -0
  91. /package/dist/{augmentor-GAF3MFY6.js.map → augmentor-7T3L6ZR4.js.map} +0 -0
  92. /package/dist/{chunk-HKQ5XQAQ.js.map → chunk-CEXV3MOC.js.map} +0 -0
  93. /package/dist/{chunk-VKBTI5EF.js.map → chunk-D6AVOPOS.js.map} +0 -0
  94. /package/dist/{chunk-ZXRVFFCY.js.map → chunk-DT2CEH2V.js.map} +0 -0
  95. /package/dist/{chunk-CJEKEW7R.js.map → chunk-LFFWD5JZ.js.map} +0 -0
  96. /package/dist/{chunk-UZ2ZA5A6.js.map → chunk-RGDIVW3O.js.map} +0 -0
  97. /package/dist/{credential-JGVSEGVH.js.map → credential-X6QKX473.js.map} +0 -0
  98. /package/dist/{credential-template-CWWMOKB2.js.map → credential-template-GD3D4MBI.js.map} +0 -0
  99. /package/dist/{debug-workflow-BXRU72YZ.js.map → debug-workflow-AAEOAME2.js.map} +0 -0
  100. /package/dist/{pdf-extract-YHE54XU4.js.map → dist-QLTVBUMD.js.map} +0 -0
  101. /package/dist/{docs-PVBXPTGR.js.map → docs-BVUEXBY3.js.map} +0 -0
  102. /package/dist/{environment-WZKIO3R3.js.map → environment-WE7VVE6I.js.map} +0 -0
  103. /package/dist/{init-FFUZO5D7.js.map → init-RQHHKJJD.js.map} +0 -0
  104. /package/dist/{install-XRKEQAFZ.js.map → install-CQLHNZ6U.js.map} +0 -0
  105. /package/dist/{issues-I3V6GEXU.js.map → issues-HK2JA5JS.js.map} +0 -0
  106. /package/dist/{knobs-6WLJMB44.js.map → knobs-ETJAV6YM.js.map} +0 -0
  107. /package/dist/{list-PHC7VYNS.js.map → list-F6DGGW3Z.js.map} +0 -0
  108. /package/dist/{local-VGYRSW22.js.map → local-CMAY54I5.js.map} +0 -0
  109. /package/dist/{login-QPU2BKG2.js.map → login-6CKG664C.js.map} +0 -0
  110. /package/dist/{property-YP4GHQAR.js.map → property-4EXPWRNT.js.map} +0 -0
  111. /package/dist/{release-NDRHPW6V.js.map → release-2SMK5Y3V.js.map} +0 -0
  112. /package/dist/{sandbox-OOCANWOG.js.map → sandbox-ORAPKOH3.js.map} +0 -0
  113. /package/dist/{scenarios-2HRLCGFY.js.map → scenarios-DM5FDCAF.js.map} +0 -0
  114. /package/dist/{spec-XZ2TKUFZ.js.map → spec-KO5QD6AY.js.map} +0 -0
  115. /package/dist/{src-HL7HEJIW.js.map → src-DSD34JIX.js.map} +0 -0
  116. /package/dist/{src-CV57ZD6I.js.map → src-PDYXACRP.js.map} +0 -0
  117. /package/dist/{start-IYKF3C3X.js.map → start-2ECN4LX5.js.map} +0 -0
  118. /package/dist/{src-OU7544PM.js.map → tag-R5I72HJ6.js.map} +0 -0
  119. /package/dist/{task-MCUHAVTD.js.map → task-V3JMSJIM.js.map} +0 -0
  120. /package/dist/{test-suite-BICBRSBP.js.map → test-suite-NOYVK6YX.js.map} +0 -0
  121. /package/dist/{tag-T45MMSCH.js.map → trace-NOYEKNO4.js.map} +0 -0
  122. /package/dist/{trace-2BAPMYAS.js.map → triage-4RFATHBE.js.map} +0 -0
  123. /package/dist/{workflow-proposals-5E3DTYZB.js.map → workflow-proposals-67AVGMHX.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../types/src/workflows.ts","../../types/src/sandbox-instance-size.ts","../../types/src/node-type-labels.ts","../../types/src/identifier-stability.ts","../../types/src/index.ts","../../types/src/credentials.ts","../../types/src/issues.ts","../../types/src/workflow-cache.ts","../../types/src/workflow-node-schemas.ts","../../types/src/api-sequence.ts","../../types/src/local-browser.ts","../../types/src/benchmark.ts","../../types/src/sandbox-tasks.ts","../../types/src/macros.ts","../../types/src/browser-viewport.ts"],"sourcesContent":["export const WORKFLOW_RUN_SEGMENT_JOB_TYPE = 'workflows.run-segment' as const;\n\n// Suite Runner Orchestrator Pattern\nexport const WORKFLOW_TEST_SUITE_ORCHESTRATOR_JOB_TYPE = 'workflows.test-suite-orchestrator' as const;\nexport const WORKFLOW_TEST_SUITE_WORKFLOW_JOB_TYPE = 'workflows.test-suite-workflow' as const;\nexport const WORKFLOW_SUITE_SETUP_JOB_TYPE = 'workflows.suite-setup' as const;\nexport const WORKFLOW_SEED_JOB_TYPE = 'workflow.seed' as const;\nexport const WORKFLOW_TEARDOWN_JOB_TYPE = 'workflow.teardown' as const;\n\nexport type WorkflowSuiteRunPayload = {\n workflowId?: string; // For single-workflow authoring\n /** Filter to specific workflow IDs (e.g. CSV import auto-run) */\n workflowIds?: string[];\n /**\n * Restrict the suite to workflows whose credential references all resolve\n * to this property. When set, login nodes' `credentialTemplateId` /\n * `credentialId` are resolved to their backing property and the suite skips\n * (a) workflows with zero resolvable credentials, and (b) workflows whose\n * credentials resolve to any other property. Null/undefined = no filter.\n */\n propertyId?: string | null;\n /** Target environment for credential resolution and base-URL substitution. */\n environmentId?: string | null;\n /**\n * How the suite picks an environment per flow:\n * - `'mixed'` (default) — each flow runs against its own preferred env\n * (`workflows.defaultEnvironmentId`, else the property default). Nothing is\n * skipped for env-eligibility reasons.\n * - `'force'` — every flow runs against `environmentId`; flows ineligible for\n * it are skipped (legacy single-env behavior).\n * Omitted ⇒ `'force'` when an `environmentId` is supplied (back-compat with\n * existing single-env callers), else `'mixed'`.\n */\n environmentMode?: 'mixed' | 'force';\n triggeredByUserId?: string | null;\n triggeredReason?: string | null;\n /**\n * Filter workflows by tag(s) — any-of semantics. A workflow runs if it has\n * at least one of the supplied tag names. Empty/undefined means \"no tag filter\".\n */\n tags?: string[] | null;\n /** Filter workflows by name pattern (case-insensitive substring match) */\n namePattern?: string | null;\n /**\n * Run a persisted, user-curated suite. When set, the orchestrator loads the\n * suite's members (ignoring tag/name/id selectors) and runs them in the\n * suite's layer order — layer N+1 spawns only after every workflow in layer N\n * reaches a terminal state.\n */\n suiteId?: string;\n};\n\n/** @deprecated Use WorkflowSuiteRunPayload — kept as alias during in-flight job migration. */\nexport type WorkflowPropertyTestRunPayload = WorkflowSuiteRunPayload;\n\nexport type WorkflowRunSegmentPayload = {\n runId: string;\n};\n\nexport type WorkflowTestStatus =\n | 'queued'\n | 'running'\n | 'waiting'\n | 'success'\n | 'failed'\n | 'skipped'\n | 'flaky_success'\n | 'canceled'\n // Not run because the workflow's health is degraded (suite runs only).\n | 'degraded';\n\n/**\n * Canonical set of **test-run** statuses that are terminal (no further\n * transitions). Import this instead of re-declaring it — the orchestrator's\n * layer gating and the suite run-detail \"layer complete\" check share it.\n * NOTE: distinct from terminal *execution* statuses (completed /\n * completed_with_errors / …), which are a separate enum.\n */\nexport const TERMINAL_TEST_RUN_STATUSES: ReadonlySet<string> = new Set([\n 'success',\n 'failed',\n 'skipped',\n 'flaky_success',\n 'canceled',\n 'timeout',\n 'degraded',\n]);\n\nexport const isTerminalTestRunStatus = (status: string): boolean =>\n TERMINAL_TEST_RUN_STATUSES.has(status);\n\n/**\n * Workflow reliability dimension, orthogonal to {@link WorkflowDefinitionStatus}\n * (lifecycle). Recomputed after each suite run from the workflow's test-run history.\n *\n * - `unverified` — no terminal test runs yet\n * - `never_passed` — has run, never a single pass (authoritative via firstPassedAt)\n * - `passing` — recent window all green, streak below the reliable threshold\n * - `reliable` — enough consecutive clean passes\n * - `flaky` — latest run passed, but the recent window had failures / a flaky_success\n * - `failing` — latest run failed but NOT auto-skipped (< DEGRADE_AFTER failures, or not promoted)\n * - `degraded` — promoted + ever-passed + enough consecutive failures; skipped in suite runs\n * (\"degraded — not run\") until the workflow is edited or manually re-enabled\n */\nexport const WORKFLOW_HEALTH = [\n 'unverified',\n 'never_passed',\n 'passing',\n 'reliable',\n 'flaky',\n 'failing',\n 'degraded',\n] as const;\n\nexport type WorkflowHealth = (typeof WORKFLOW_HEALTH)[number];\n\n/**\n * Approval-axis states for a workflow definition. This axis answers \"is the test\n * testing the right thing?\" — suite inclusion is the orthogonal {@link WorkflowPromotion}\n * axis, and reliability lives in {@link WorkflowHealth}.\n *\n * User-settable via the API/CLI: `draft`, `approved`\n * (see {@link USER_SETTABLE_WORKFLOW_STATUSES}).\n *\n * Lifecycle-internal (set by the system, not by users):\n * - `generating` — the workflow is being built from a recording/import\n * - `building` — a builder validation run is in progress\n * - `diagnostic` — builder validation failed; the workflow is parked for inspection\n * - `deleted` — soft-deleted\n *\n */\nexport const WORKFLOW_STATUSES = [\n 'draft',\n 'generating',\n 'building',\n 'deleted',\n 'diagnostic',\n 'approved',\n] as const;\n\nexport type WorkflowDefinitionStatus = (typeof WORKFLOW_STATUSES)[number];\n\n/**\n * Statuses a user (or agent) can set directly via the API or CLI.\n *\n * - `draft` — not yet approved; a human has not signed off on the spec\n * - `approved` — a human verified the test checks the right thing\n *\n * Approval does NOT put a flow into suites — that's the separate {@link WorkflowPromotion}\n * axis. Reliability (trusted, flaky, degraded) is the {@link WorkflowHealth} dimension,\n * computed automatically — not user-set here.\n */\nexport const USER_SETTABLE_WORKFLOW_STATUSES = [\n 'draft',\n 'approved',\n] as const satisfies readonly WorkflowDefinitionStatus[];\n\nexport type UserSettableWorkflowStatus =\n (typeof USER_SETTABLE_WORKFLOW_STATUSES)[number];\n\n/**\n * Suite-inclusion axis, orthogonal to approval ({@link WorkflowDefinitionStatus}) and\n * reliability ({@link WorkflowHealth}). Stored on a nullable column:\n *\n * - `null` — never promoted (default; shows no indicator in the UI)\n * - `'promoted'` — runs when a test suite runs\n * - `'paused'` — explicitly excluded from suites (post-promotion state only)\n *\n * Promoting requires approval; pausing/re-promoting toggles inclusion without\n * touching approval.\n */\nexport const WORKFLOW_PROMOTIONS = ['promoted', 'paused'] as const;\n\nexport type WorkflowPromotion = (typeof WORKFLOW_PROMOTIONS)[number];\n\n/**\n * The promotion column's full value space: `null` = never promoted (the default,\n * rendered with no indicator). Single-sourced here so repos and the web app don't\n * re-declare drifting `'promoted' | 'paused' | null` literals.\n */\nexport type WorkflowPromotionState = WorkflowPromotion | null;\n\n/** Context provided by the agent when a workflow step fails */\nexport type WorkflowFailureContext = {\n /** What the agent observed happening when the step failed */\n observedBehavior: string;\n /** What was expected to happen */\n expectedBehavior: string;\n /** Steps to reproduce the failure */\n reproSteps: string;\n /** Specific questions the agent poses to help improve instructions (debug mode only) */\n questions?: string[];\n};\n\nexport type WorkspaceRedactionReason = 'secret' | 'binary' | 'large' | 'system';\n\nexport type WorkspaceManifestEntry = {\n path: string;\n type: 'file' | 'dir';\n size?: number;\n mtime?: string;\n language?: string;\n redacted?: boolean;\n redactionReason?: WorkspaceRedactionReason;\n};\n\nexport type WorkspaceManifest = {\n version: string;\n root: string;\n generatedAt: string;\n entries: WorkspaceManifestEntry[];\n};\n\nexport type WorkspaceStreamEvent =\n | {\n type: 'workspace-manifest';\n data: WorkspaceManifest;\n }\n | {\n type: 'workspace-file-created';\n data: WorkspaceManifestEntry;\n }\n | {\n type: 'workspace-file-updated';\n data: {\n path: string;\n size?: number;\n mtime?: string;\n version?: string;\n };\n }\n | {\n type: 'workspace-file-deleted';\n data: { path: string };\n }\n | {\n type: 'workspace-file-content';\n data: {\n path: string;\n content: string;\n encoding?: 'utf-8';\n version?: string;\n };\n };\n\nexport type WorkflowTestRunSummary = {\n id: string;\n workflowId: string;\n status: WorkflowTestStatus;\n snapshotVersion: number;\n errorMessage: string | null;\n linkedIssueId: string | null;\n startedAt: string | null;\n finishedAt: string | null;\n createdAt: string;\n};\n\nexport type WorkflowTestSuiteStatus = 'queued' | 'running' | 'completed';\n\nexport type WorkflowTestEvent = {\n type: 'workflow-test';\n suiteId: string;\n emittedAt: string;\n workflowId: string;\n testRunId: string;\n workflowRunId: string | null;\n status: WorkflowTestStatus;\n startedAt: string | null;\n finishedAt: string | null;\n durationMs: number | null;\n errorMessage?: string | null;\n skipReason?: string | null;\n linkedIssueId?: string | null;\n message?: string;\n /** The workflow's reliability dimension at emit time (drives the UI health badge). */\n health?: WorkflowHealth;\n // Wait/segment tracking\n resumeAt?: string | null;\n segmentIndex?: number | null;\n totalSegments?: number | null;\n /** Monotonic sequence number for ordering concurrent events */\n _seq?: number;\n};\n\nexport type WorkflowTestSuiteEvent = {\n type: 'workflow-test-suite';\n suiteId: string;\n emittedAt: string;\n status: WorkflowTestSuiteStatus;\n startedAt: string;\n finishedAt: string | null;\n durationMs: number | null;\n totalWorkflows: number;\n completedWorkflows: number;\n failedWorkflows: number;\n successfulWorkflows: number;\n skippedWorkflows: number;\n waitingWorkflows: number;\n /** @deprecated Quarantine removed; always 0. Drop with the column in a follow-up. */\n quarantinedWorkflows: number;\n /** Workflows skipped because their health is `degraded` (shown \"degraded — not run\"). */\n degradedWorkflows: number;\n seedTotal: number;\n seedSucceeded: number;\n seedFailed: number;\n teardownTotal: number;\n teardownSucceeded: number;\n teardownFailed: number;\n errorMessage?: string | null;\n message?: string;\n /** Monotonic sequence number for ordering concurrent events */\n _seq?: number;\n};\n\nexport type WorkflowTestStreamEvent = WorkflowTestEvent | WorkflowTestSuiteEvent;\n","/**\n * Sandbox Instance Sizing\n *\n * Closed preset enum of EC2 sizes a template can run on, plus a per-size\n * resource spec consumed by provisioning, compose generation, and prompts.\n *\n * Single source of truth for: launch-template instance type override,\n * docker compose mem_limit/cpus per role, prompt-template variables.\n *\n * @module types/sandbox-instance-size\n */\n\nexport const INSTANCE_SIZES = ['small', 'medium', 'large', 'xlarge', '2xlarge'] as const;\nexport type InstanceSize = (typeof INSTANCE_SIZES)[number];\n\nexport const DEFAULT_INSTANCE_SIZE: InstanceSize = 'medium';\n\nexport interface InstanceSizeSpec {\n /** EC2 instance type for `RunInstancesCommand` override. */\n ec2InstanceType: string;\n vcpu: number;\n memoryMiB: number;\n /**\n * Per-role memory caps (MiB) consumed by `resourceLimitsFor()`. Must cover\n * every key in compose-helpers.ts STATIC_LIMITS.\n *\n * Sum × per-role multiplicity should fit within `memoryMiB × 1.5` to keep\n * swap pressure bounded. See INSTANCE_SIZE_SUM_LIMIT_RATIO.\n */\n agentCapMiB: number;\n appSoftCapMiB: number;\n proxyCapMiB: number;\n emulationCapMiB: number;\n storageCapMiB: number;\n /**\n * Per-size hard CPU quota (cgroup `cpus`) for the user app tier. Other\n * roles (agent, proxy, emulation, storage) keep fixed CPU caps from\n * STATIC_LIMITS — their workloads don't scale with host vCPU. App is the\n * only role that sees a real benefit from a larger instance, so it's the\n * only one keyed here.\n *\n * Formula: max('1.0', vcpu - 1.5) — reserve 1.5 vCPU for the agent.\n * Stored as a string so docker compose receives `'1.0'` (not `'1'`),\n * matching the format of STATIC_LIMITS.cpus values.\n */\n appCpus: string;\n}\n\n/** Cap on (sum of role caps) ÷ memoryMiB — anything above this swaps too hard. */\nexport const INSTANCE_SIZE_SUM_LIMIT_RATIO = 1.5;\n\nexport const INSTANCE_SIZE_SPEC: Record<InstanceSize, InstanceSizeSpec> = {\n small: {\n ec2InstanceType: 't3.small',\n vcpu: 2,\n memoryMiB: 2048,\n // 2 GiB instance — agent capped at min(2048, 0.4 × 2048) = 819\n agentCapMiB: 819,\n appSoftCapMiB: 1024,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 256,\n appCpus: '1.0',\n },\n medium: {\n ec2InstanceType: 't3.medium',\n vcpu: 2,\n memoryMiB: 4096,\n agentCapMiB: 1536,\n appSoftCapMiB: 1536,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 256,\n appCpus: '1.0',\n },\n large: {\n ec2InstanceType: 't3.large',\n vcpu: 2,\n memoryMiB: 8192,\n agentCapMiB: 2048,\n appSoftCapMiB: 5120,\n proxyCapMiB: 256,\n emulationCapMiB: 128,\n storageCapMiB: 512,\n // t3.large is memory-only — same 2 vCPU as medium.\n appCpus: '1.0',\n },\n xlarge: {\n ec2InstanceType: 't3.xlarge',\n vcpu: 4,\n memoryMiB: 16384,\n agentCapMiB: 2048,\n appSoftCapMiB: 12288,\n proxyCapMiB: 256,\n emulationCapMiB: 256,\n storageCapMiB: 512,\n appCpus: '2.5',\n },\n '2xlarge': {\n ec2InstanceType: 't3.2xlarge',\n vcpu: 8,\n memoryMiB: 32768,\n agentCapMiB: 2048,\n appSoftCapMiB: 28672,\n proxyCapMiB: 512,\n emulationCapMiB: 512,\n storageCapMiB: 1024,\n appCpus: '6.5',\n },\n};\n\nconst INSTANCE_SIZE_SET = new Set<string>(INSTANCE_SIZES);\n\n/**\n * Narrowing helper for the repo boundary. Returns null on unknown input so\n * callers can surface a typed user error without `as never` / `as any`.\n */\nexport function parseInstanceSize(s: string | null | undefined): InstanceSize | null {\n return s && INSTANCE_SIZE_SET.has(s) ? (s as InstanceSize) : null;\n}\n\nconst RECOMMENDATION_MAP: Record<string, InstanceSize> = {\n // Exact preset names\n small: 'small',\n medium: 'medium',\n large: 'large',\n xlarge: 'xlarge',\n '2xlarge': '2xlarge',\n // Common AWS instance types the authoring agent may emit\n 't3.nano': 'small',\n 't3.micro': 'small',\n 't3.small': 'small',\n 't3.medium': 'medium',\n 't3.large': 'large',\n 't3.xlarge': 'xlarge',\n 't3.2xlarge': '2xlarge',\n 't2.small': 'small',\n 't2.medium': 'medium',\n 't2.large': 'large',\n 't2.xlarge': 'xlarge',\n 't2.2xlarge': '2xlarge',\n};\n\n/**\n * Maps a free-form `recommendedInstanceType` string (often produced by the\n * authoring agent before this enum existed) to the closest preset.\n *\n * Strategy: exact match against known names first, then fall back to nearest\n * memoryMiB. Ties broken toward cheaper (lower `memoryMiB`).\n */\nexport function inferInstanceSizeFromRecommendation(\n s: string | null | undefined\n): InstanceSize | null {\n if (!s) return null;\n const trimmed = s.trim().toLowerCase();\n if (!trimmed) return null;\n const exact = RECOMMENDATION_MAP[trimmed];\n if (exact) return exact;\n\n // Last-resort heuristic: parse a memory hint like \"4 GiB\" / \"8GB\" / \"2g\".\n const match = trimmed.match(/(\\d+(?:\\.\\d+)?)\\s*(g|gi|gb|gib|m|mi|mb|mib)\\b/);\n if (!match) return null;\n const value = Number.parseFloat(match[1]!);\n if (!Number.isFinite(value)) return null;\n const memMiB = match[2]!.startsWith('g') ? value * 1024 : value;\n\n let best: InstanceSize | null = null;\n let bestDelta = Number.POSITIVE_INFINITY;\n for (const size of INSTANCE_SIZES) {\n const delta = Math.abs(INSTANCE_SIZE_SPEC[size].memoryMiB - memMiB);\n if (delta < bestDelta) {\n best = size;\n bestDelta = delta;\n }\n }\n return best;\n}\n","/**\n * Display labels for workflow node types. The JSON / persisted `node.type`\n * discriminator stays as-is (e.g. `'login'`) — only the human-facing label\n * changes here. Centralizing this lets us rename copy in one place.\n */\nexport const NODE_TYPE_LABEL = {\n login: 'Acting As',\n navigate: 'Navigate',\n action: 'Action',\n assertion: 'Assertion',\n condition: 'Condition',\n wait: 'Wait',\n api_sequence: 'API Sequence',\n seed: 'Seed Data',\n setup: 'Setup Flow',\n notification: 'Notification',\n audio: 'Audio input',\n 'audio-assertion': 'Audio assertion',\n sandbox_action: 'Sandbox action',\n end: 'End',\n} as const;\n\nexport type NodeTypeKey = keyof typeof NODE_TYPE_LABEL;\nexport type NodeTypeLabel = (typeof NODE_TYPE_LABEL)[NodeTypeKey];\n\n/**\n * Normalizes legacy/alias node-type keywords to the canonical persisted type.\n * Accepts `'acting-as'` / `'acting_as'` as aliases for `'login'` so CLI\n * callers can use the new name while persisted data stays `'login'`.\n */\nexport function normalizeNodeType(input: string): string {\n if (input === 'acting-as' || input === 'acting_as') return 'login';\n return input;\n}\n","/**\n * Canonical classification of a DOM identifier's stability for use as a locator\n * anchor. Single source of truth — replaces the divergent, disagreeing\n * volatile/generated-id checks that used to live in (and drift between):\n * - apps/api/src/workflows/cache/build-locator.ts (`findVolatileTestIdToken`)\n * - packages/browser-core/src/snapshot-formatter-sections.ts (duplicate)\n * - packages/recording/src/scope.ts (`looksGeneratedId`)\n * - apps/api/src/workflows/cache/deterministic-locator-builder.ts (`isStableId`)\n *\n * Stability means **survives a re-seed**, NOT \"points at a real record\". The\n * canary app is re-seeded between runs, so any value derived from seeded data —\n * UUIDs, ULIDs, entity numbers (`invoice-DI-80368`), the `<timestamp>` in\n * `__new__1778798465222` — is VOLATILE even when it reliably identifies a record\n * within a single run. Only structural / framework-state tokens survive: a state\n * marker like `__new__`, a hard-coded structural `data-testid`, a landmark role.\n *\n * Output:\n * - `stable` → use the value verbatim (`[attr=\"value\"]` / `#id`).\n * - `stable-prefix`→ a structural head precedes a volatile token; anchor on\n * `[attr^=\"prefix\"]` (a non-unique prefix is then rejected\n * by the caller's global-uniqueness check).\n * - `volatile` → unusable as an anchor.\n *\n * This is locator-time classification (a context-dependent judgment), kept out\n * of the substrate per substrate-v2 principle #3 (\"synthesis is tagged, never\n * laundered\").\n */\nexport type IdentifierStability =\n | { kind: 'stable' }\n | { kind: 'stable-prefix'; prefix: string }\n | { kind: 'volatile' };\n\n// ── Whole-value generated patterns (no reusable structural prefix) ──────────\n// Framework-generated id prefixes (React useId, Radix, HeadlessUI, Mantine,\n// React Aria / Select, rc-, Vue menu-v-N).\nconst FRAMEWORK_PREFIX_RE =\n /^(?::r[0-9a-z]+:?$|radix-|headlessui-|mantine-|react-aria-|react-select-|rc-|menu-v-\\d)/i;\n// A bare hash (styled-components / content hash), e.g. `deadbeef`, `a1b2c3d4e5`.\nconst BARE_HASH_RE = /^[a-f0-9]{8,}$/i;\n// `prefix-<hex6+>` styled-component-style class/id, e.g. `sc-bdVaJa` → `foo-a1b2c3d4`.\nconst PREFIX_HASH_RE = /^[a-z]+-[0-9a-f]{6,}$/i;\n\n// ── Embedded volatile tokens (a structural head may precede them) ───────────\n// Any 8-4-4-4-12 hex value is a UUID regardless of version/variant nibble — keep\n// it version-agnostic so v7/v8 (and non-RFC) ids are caught, not just v1-5.\nconst UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;\n// ULID (Crockford base32, excludes I/L/O/U). Case-insensitive so lowercase\n// ULIDs are caught too.\nconst ULID_RE = /\\b[0-9A-HJKMNP-TV-Z]{26}\\b/i;\n// A 6+-digit run delimited by a boundary or `_` (so `__new__1778798465222`\n// is caught even though `_`→digit is not a `\\b`). Trailing/leading boundary\n// keeps `abc123456def` from matching (preserves the legacy `\\b\\d{6,}\\b`).\nconst DIGIT_RUN_RE = /(?:^|[^0-9A-Za-z])(\\d{6,})(?![0-9A-Za-z])/;\n// A trailing `-`/`_`-joined 3+-digit counter/number (`widget-123`,\n// `invoice-DI-80368`, the timestamp tail of `__new__<ts>`). 1–2 trailing digits\n// stay stable (`step-1`, `tab-42`).\nconst TRAILING_COUNTER_RE = /[-_](\\d{3,})$/;\n\nconst MIN_PREFIX_LEN = 3;\n\nfunction usablePrefix(prefix: string): boolean {\n return prefix.length >= MIN_PREFIX_LEN && /[a-z]/i.test(prefix);\n}\n\n/** Earliest index at which a volatile token begins, or null. */\nfunction earliestVolatileTokenIndex(value: string): number | null {\n let best: number | null = null;\n const consider = (idx: number | undefined) => {\n if (idx !== undefined && idx >= 0 && (best === null || idx < best)) best = idx;\n };\n consider(UUID_RE.exec(value)?.index);\n consider(ULID_RE.exec(value)?.index);\n const digit = DIGIT_RUN_RE.exec(value);\n if (digit) consider(digit.index + (digit[0].length - digit[1].length)); // start of the digits\n const counter = TRAILING_COUNTER_RE.exec(value);\n if (counter) consider(counter.index + 1); // after the `-`/`_` separator\n return best;\n}\n\nexport function classifyIdentifier(value: string): IdentifierStability {\n if (!value) return { kind: 'volatile' };\n\n // Whole-value generated → no reusable prefix.\n if (FRAMEWORK_PREFIX_RE.test(value) || BARE_HASH_RE.test(value) || PREFIX_HASH_RE.test(value)) {\n return { kind: 'volatile' };\n }\n\n const tokenIdx = earliestVolatileTokenIndex(value);\n if (tokenIdx !== null) {\n const prefix = value.slice(0, tokenIdx);\n return usablePrefix(prefix) ? { kind: 'stable-prefix', prefix } : { kind: 'volatile' };\n }\n\n return { kind: 'stable' };\n}\n\n/** True when the value is not safe to anchor on verbatim (volatile or prefix-only). */\nexport function isVolatileIdentifier(value: string): boolean {\n return classifyIdentifier(value).kind !== 'stable';\n}\n\n/** The structural prefix preceding a volatile token, or null. */\nexport function stableIdentifierPrefix(value: string): string | null {\n const c = classifyIdentifier(value);\n return c.kind === 'stable-prefix' ? c.prefix : null;\n}\n\nfunction escapeCssAttrValue(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * CSS attribute selector for `value` on `attr` (`[attr=\"value\"]` for a stable\n * value, `[attr^=\"prefix\"]` for a structural prefix), or null when too volatile.\n */\nexport function stableAttrSelector(attr: string, value: string): string | null {\n const c = classifyIdentifier(value);\n if (c.kind === 'stable') return `[${attr}=\"${escapeCssAttrValue(value)}\"]`;\n if (c.kind === 'stable-prefix') return `[${attr}^=\"${escapeCssAttrValue(c.prefix)}\"]`;\n return null;\n}\n","import { z } from 'zod';\nimport type { AssertionSeverity } from './workflow-node-schemas.js';\nimport type { WorkflowHealth } from './workflows.js';\n\nexport type ActionInputType =\n | 'text'\n | 'email'\n | 'password'\n | 'number'\n | 'textarea'\n | 'select'\n | 'checkbox'\n | 'multiselect'\n | 'file';\n\nexport interface ActionInputOption {\n value: string;\n label: string;\n}\n\nexport interface ActionDataRequirement {\n /**\n * Unique field identifier used to store the provided input value.\n */\n name: string;\n /**\n * Human-friendly label to display in the UI.\n */\n label: string;\n /**\n * Expected input type. Defaults to 'text' if omitted.\n */\n type?: ActionInputType;\n /**\n * Whether the field is required for the action to succeed.\n */\n required?: boolean;\n /**\n * Helpful description shown to the operator.\n */\n description?: string;\n /**\n * Placeholder text for the input.\n */\n placeholder?: string;\n /**\n * Suggested example value provided by the agent.\n */\n exampleValue?: string;\n /**\n * Optional select options for dropdown-style inputs.\n */\n options?: ActionInputOption[];\n}\n\nexport type ActionInputValues = Record<string, string>;\n\n// ── Workflow-level input definitions ──\n\n/** Value input — a simple typed field (text, number, email, select, etc.) */\nexport interface WorkflowValueInputDefinition {\n kind: 'value';\n /** Variable name (snake_case, e.g. \"user_email\") */\n name: string;\n /** Human-friendly label */\n label: string;\n /** Expected input type. Defaults to 'text' if omitted. */\n type?: ActionInputType;\n required?: boolean;\n description?: string;\n placeholder?: string;\n /** Pre-filled default value */\n defaultValue?: string;\n options?: ActionInputOption[];\n}\n\n/** Credential input — represents a login identity (future: full credential provisioning) */\nexport interface WorkflowCredentialInputDefinition {\n kind: 'credential';\n /** Namespace for credential vars (e.g. \"admin_user\" → $var.admin_user.username) */\n name: string;\n /** Human-friendly label */\n label: string;\n required?: boolean;\n description?: string;\n /** Which credential fields this input needs */\n credentialFields: {\n username?: boolean;\n password?: boolean;\n loginUrl?: boolean;\n mfaSecret?: boolean;\n };\n /** Default credential values (for scenarios) */\n defaults?: {\n username?: string;\n password?: string;\n loginUrl?: string;\n };\n /** Default logical credential template used when a scenario does not override it */\n defaultCredentialTemplateId?: string;\n}\n\nexport type WorkflowInputDefinition =\n | WorkflowValueInputDefinition\n | WorkflowCredentialInputDefinition;\n\n// ── Workflow scenarios (named input-value sets) ──\n\n/** Value-input overrides keyed by WorkflowValueInputDefinition.name */\nexport type WorkflowScenarioValueInputs = Record<string, string>;\n\n/** Credential-input overrides keyed by WorkflowCredentialInputDefinition.name */\nexport type WorkflowScenarioCredentialInputs = Record<\n string,\n {\n /** Logical credential template to resolve against the run environment */\n credentialTemplateId?: string;\n /** Legacy inline fields; retained for backward-compatible imports/storage */\n username?: string;\n password?: string;\n loginUrl?: string;\n mfaSecret?: string;\n }\n>;\n\n/**\n * A named set of workflow input values — parameterized test case for a workflow.\n * Scenarios reference existing `inputsJson` entries by name; orphan keys are\n * surfaced as warnings in the UI but are tolerated in storage.\n */\nexport interface WorkflowScenario {\n /** Stable uuid (survives renames) */\n id: string;\n /** Human-friendly name, e.g. \"Happy path\" */\n name: string;\n description?: string;\n /** At most one scenario per workflow may be default; UI enforces this. */\n isDefault?: boolean;\n /**\n * When true (or undefined — defaults to true for backwards compatibility),\n * this scenario is included when the workflow runs as part of a test suite,\n * causing the suite to fan the workflow out to one run per included scenario.\n */\n includeInSuite?: boolean;\n values?: WorkflowScenarioValueInputs;\n credentials?: WorkflowScenarioCredentialInputs;\n}\n\n// ── Workflow environment eligibility ──\n\nexport type WorkflowEnvironmentType = 'permanent' | 'ephemeral' | 'local' | 'sandbox';\n\n/**\n * Client-facing readiness of a `type='sandbox'` environment's backing\n * flow-authoring instance. Shared by the API status/ensure/heartbeat routes\n * and the web `useSandboxEnvironment` hook so the union can't drift.\n */\nexport type SandboxEnvironmentStatus =\n | 'asleep'\n | 'warming'\n | 'ready'\n | 'stopping'\n | 'failed';\n\n/** Status-route payload for a sandbox environment's backing instance. */\nexport interface SandboxEnvironmentState {\n status: SandboxEnvironmentStatus;\n templateId: string;\n instanceId: string | null;\n /** Raw `environment_instances.status` when an instance exists. */\n instanceStatus: string | null;\n url: string | null;\n /** Instance creation time — anchors the provisioning elapsed timer. */\n createdAt: string | null;\n expiresAt: string | null;\n /** Provisioning failure detail when `status === 'failed'`. */\n failureReason: string | null;\n}\n\n/**\n * Explicit environment eligibility constraints for a workflow.\n * Null/absent means \"any\" — workflow can run against any environment.\n * `only`: pin to specific environment IDs (e.g. prod smoke).\n * `envTypes`: allow only listed environment types (e.g. permanent only).\n */\nexport type WorkflowEnvironmentConstraints =\n | { mode: 'only'; environmentIds: string[] }\n | { mode: 'envTypes'; types: WorkflowEnvironmentType[] };\n\n/**\n * Per-environment pass history for a workflow — \"does this flow pass on this\n * env/template?\". Keyed on the STABLE target environment (`workflow_runs.environment_id`),\n * so sandbox runs attribute to the persistent sandbox env, not the throwaway\n * ephemeral instance. Computed read-time over the recent terminal-run window.\n */\nexport interface PassByEnvironment {\n environmentId: string;\n environmentName: string;\n environmentType: WorkflowEnvironmentType;\n /**\n * Whether any terminal pass (success/flaky_success) appears in the recent\n * fetched window for this env — NOT an all-time signal. This is a read-time\n * aggregate over the same bounded window the flow-health badge uses, so a flow\n * that passed long ago but only has recent failures reads as `everPassed:false`\n * here. (The authoritative all-time \"has ever passed\" lives on the workflow's\n * `firstPassedAt`, which is global, not per-env.)\n */\n everPassed: boolean;\n /** Passes within the recent window. */\n passed: number;\n /** Terminal runs within the recent window. */\n total: number;\n /** classifyHealth verdict for this env's window (e.g. passing/flaky/failing). */\n health: WorkflowHealth;\n}\n\n/**\n * Metadata about input values, including whether synthetic data was used.\n * @deprecated Synthetic tracking has been removed. Form field reporting is\n * now unified via {@link ActionFormField} in debug mode only.\n */\nexport interface ActionInputMetadata {\n /**\n * True if any input values were synthetically generated by the agent\n * rather than provided by the user.\n */\n usedSyntheticData?: boolean;\n /**\n * List of field names that were synthetically generated.\n */\n syntheticFields?: string[];\n}\n\n/**\n * Unified form field representation for debug-mode agent reporting.\n * Combines field metadata (schema) with the value used, so the agent\n * reports everything in a single array instead of separate\n * `dataRequirements` + `inputData` fields.\n */\nexport interface ActionFormField {\n /** Unique field identifier (snake_case, e.g. task_title). */\n name: string;\n /** Human-friendly label from the UI. */\n label: string;\n /** Input type. Defaults to 'text' if omitted. */\n type?: ActionInputType;\n /** Whether the field is required. */\n required?: boolean;\n /** The value entered/used for this field. */\n value: string;\n /** Select/multiselect options, if applicable. */\n options?: ActionInputOption[];\n}\n\nexport const PLAYBOOK_STEP_ACTIONS = [\n 'click',\n 'fill',\n 'fill_form',\n 'select',\n 'navigate',\n 'wait',\n 'scroll',\n 'hover',\n 'upload',\n 'verify',\n 'press_key',\n 'drag',\n 'resize',\n 'capture',\n 'extract_table',\n 'python_execute',\n 'verify_download',\n 'verify_download_content',\n 'screenshot',\n 'agent_loop',\n] as const;\n\nexport type PlaybookStepAction = (typeof PLAYBOOK_STEP_ACTIONS)[number];\nexport type LegacyPlaybookStepAction =\n | PlaybookStepAction\n | 'other'\n | 'handle_native_dialog'\n | 'toggle'\n | 'focus';\n\nfunction isPositiveIntegerString(value: string | undefined): value is string {\n if (!value || !/^\\d+$/.test(value)) return false;\n return Number(value) > 0;\n}\n\n/**\n * Zod schema for the agent-settable fields of a playbook step. This is the\n * single source of truth for what the LLM can author via the\n * `updateWorkflowStep` tool — `apps/api` imports this directly instead of\n * keeping a parallel hard-coded schema (which used to drift, e.g. duplicate\n * action enum and missing `expectedDialogs`).\n *\n * Internal fields the *runtime* attaches (actionRefsSource, screenshotS3Key)\n * are NOT in this schema — they belong on the wider {@link PlaybookStep}\n * type below. Keeping them out keeps the agent's input surface tight and\n * prevents prompt injection of internal-only fields.\n *\n * Field descriptions are LLM-tuned: they are read directly into the tool's\n * function-calling schema. Don't shorten them without considering the agent.\n */\nexport const playbookStepAgentSchema = z\n .object({\n order: z.number().describe('Step number (1-based).'),\n action: z\n .enum(PLAYBOOK_STEP_ACTIONS)\n .describe(\n 'The type of browser interaction. PREFER DETERMINISTIC VERBS even when verbose — three deterministic steps beat one agentic step, because deterministic steps replay via Playwright with no agent dispatch. ' +\n 'Each replayable playbook step must be 1:1 with an exposed browser tool you actually used and must correspond to zero or one recorded actionRef. If you cannot accomplish the intent with the browser tools you have, do not add a playbook step for it; report the limitation outside playbookSteps. Do not summarize several recorded actions into one \"other\" step; report the same 1:1 browser primitives you actually used. The only multi-field exception is one fill_form step for one browser_fill_form action. ' +\n 'Use \"verify\" for assertion checks — both inline assertions and completion assertions proving the action succeeded. ' +\n 'Use \"verify\" for inspection language such as \"inspect whether\", \"confirm\", \"observe\", or \"check\" when the result is visible in the DOM. Use \"capture\" instead only when the observed value is needed by later steps. ' +\n 'Use \"fill\" for single-field inputs. Use \"fill_form\" when browser_fill_form was called to fill multiple fields at once — one fill_form step per call. ' +\n 'Use \"click\" for checkboxes, switches, toggle buttons, and click-to-focus interactions. There is no separate \"toggle\" or \"focus\" action. ' +\n 'Use \"upload\" for file upload actions, including generated CSV/file, attach file, choose file, import file, and document/image attachment intents (browser_action action=\"upload\"). Set params.path to the staged attachment handle, generated file variable, or filename reference used by the upload tool (e.g. {path: \"$var.csv_file\"}), and include actionRefs pointing to the recorded file_upload action. Do NOT use \"other\" for file attachment/upload/import intents. ' +\n 'Use \"scroll\" only when scrolling is the user-visible action being tested; routine browser_scroll calls for visibility, inspection, or video framing are observation steps and should usually be omitted from playbookSteps. ' +\n 'Use \"press_key\" for keyboard actions (Enter, Escape, Tab, arrows). ' +\n 'Use \"press_key\" with params.key=\"Escape\" to dismiss a popover, dropdown, panel, or modal when Escape was the recorded action; use \"click\" if the recorded action clicked a close button or outside target. There is no separate \"dismiss\" action. ' +\n 'Use \"drag\" for drag-and-drop interactions. ' +\n 'Use \"resize\" when browser_resize was used to resize the viewport. Set params.width and params.height to positive integer pixel values and include the actionRef pointing to the recorded resize action. ' +\n 'Use \"capture\" to extract app-generated values (IDs, codes, URLs) for later steps via $var.name, but only from a visible DOM target or the current URL. Do not capture from performance/network timing, storage, hidden DOM, or app-global state. ' +\n 'Use \"screenshot\" only when browser_screenshot itself is the requested evidence capture. Set params.label when a label was used, optionally params.fullPage=\"true\". Do not use screenshot for routine visual checks after actions; use verify when a browser_assert proves visible state. ' +\n 'Use \"extract_table\" when browser_extract_table was used to extract tabular data from the page. ' +\n 'Use \"python_execute\" when python_execute was used with cache_label to run a data validation script. ' +\n 'Use \"verify_download\" when browser_download(action=\"list\") / browser_list_downloads proves that a file matching some pattern was downloaded. Set params.pattern to a glob (supports *, ?) or substring; optionally params.timeoutMs. This is currently agent-only at replay, so do not set actionRefs. ' +\n 'Use \"verify_download_content\" when browser_download(action=\"read\") / browser_read_download content proves CSV headers, PDF text, exported rows, or similar downloaded-file content. Set params.contains or params.pattern to the expected content predicate. This is currently agent-only at replay, so do not set actionRefs. ' +\n 'Use \"agent_loop\" ONLY when the action set is genuinely unknown until runtime — variable count of items (e.g. \"toggle row checkboxes until 3 selected\"), conditional retry that depends on UI state, or a runtime predicate over non-DOM state. Set params.goal to a one-sentence checkable predicate (e.g. params.goal = \"3 grid row checkboxes are selected\"). DO NOT use agent_loop for: known-N iteration (write N deterministic steps), single click that might double-fire (use \"click\"), retry-on-flake (the runtime handles that). agent_loop steps replay via a Playwright override authored after first run; they are non-deterministic on first run, so the bar is high. ' +\n 'Network/devtools/API inspections and unsupported browser environment mutations are diagnostic-only unless there is an exposed browser tool for the action. If visible page state proves the condition, record that as \"verify\"; otherwise omit the diagnostic observation from playbookSteps. ' +\n 'Do not use \"other\". If no concrete browser-tool-backed action above fits, omit the observation from playbookSteps and report the limitation outside the playbook.'\n ),\n target: z\n .string()\n .describe(\n 'Human-readable element description using visible labels, placeholders, or aria-labels. ' +\n 'Examples: \"\\'Submit\\' button\", \"\\'Email\\' input field\", \"\\'Status\\' dropdown\". ' +\n 'For upload actions, describe the file input and include the attachment name (e.g. \"CSV file input for \\'testing-checklist.csv\\'\"). ' +\n 'Never use coordinates, ref IDs, or internal identifiers.'\n ),\n params: z\n .record(z.string(), z.string())\n .optional()\n .describe(\n 'Data values using $var.name references and $MACRO() expressions — NEVER hard-coded literals. ' +\n 'Common keys: value (for fill: {value: \"$EMAIL()\"}), option (for select: {option: \"Admin\"} — ' +\n 'fixed app constants like dropdown options are allowed as literals), url (for navigate: {url: \"$var.target_url\"}), ' +\n 'path (for upload: {path: \"$var.file_path\"}), key (for press_key: {key: \"Enter\"}, {key: \"Escape\"}), ' +\n 'width and height (for resize: {width: \"390\", height: \"844\"}), ' +\n 'label and fullPage (for screenshot: {label: \"seats-table\", fullPage: \"true\"}), ' +\n 'pattern or contains (for download verification: {pattern: \"report-*.csv\"} or {contains: \"Expected Header\"}), ' +\n 'expression + saveAs (for capture: prefer a visible DOM ref expression or \"() => window.location.href\"; do not use performance.getEntries() or other network/storage/global state; e.g. {saveAs: \"order_confirmation_id\"}). ' +\n 'For fill_form: keys are field names from the fill_form call, values are $var.name or $MACRO() (e.g. {company: \"$var.company\", email: \"$EMAIL()\"}). ' +\n 'Available macros: $UUID(), $EMAIL(), $FIRST_NAME(), $LAST_NAME(), $PHONE(), $RANDOM_STRING(n), ' +\n '$RANDOM_INT(min,max), $TODAY(), $DATE(days), $NOW(), $TIMESTAMP(), $SEQUENCE(\"name\").'\n ),\n note: z\n .string()\n .optional()\n .describe(\n 'Timing or quirk notes ONLY (e.g. \"Wait 1s for modal animation\", \"Scroll into view first\"). ' +\n 'Do NOT put data values, optional markers, or verification notes here.'\n ),\n actionRefs: z\n .array(z.number())\n .max(1)\n .optional()\n .describe(\n 'Zero or one recorded browser action index that corresponds to this playbook step. ' +\n 'Interaction tools, browser_resize, capture evaluates (browser_evaluate with saveAs), and browser_assert produce [Action #N] tags — collect those indices here. ' +\n 'Read-only browser_snapshot and plain browser_evaluate without saveAs do NOT produce action indices. browser_scroll and browser_screenshot may produce action indices when they are the primary requested tool call; omit actionRefs when no action tag was emitted. ' +\n 'If you took multiple cacheable UI actions, report multiple 1:1 playbook steps instead of putting multiple refs on one step or summarizing them as \"other\". ' +\n 'A fill_form call is a single action — use one fill_form playbook step with one actionRef. ' +\n 'For a click that triggers a native dialog, keep one click actionRef on the click step and set expectedDialogs rather than adding a separate dialog actionRef. ' +\n 'Actions not referenced by any playbook step are considered exploratory and will be discarded by the cache compiler.'\n ),\n expectedDialogs: z\n .enum(['accept', 'dismiss'])\n .optional()\n .describe(\n 'Set when this step is known to fire a native browser dialog (alert/confirm/prompt) — ' +\n 'e.g. clicking a \"Delete\" button that prompts \"Are you sure?\". On replay the runtime ' +\n 'pre-arms the dialog handler with this action automatically. In free-form runs, you ' +\n 'should call the dialog tool (browser_dialog for consolidated tools, browser_handle_dialog for legacy tools) with this action immediately before the step. ' +\n 'Do not add a separate dialog step — keeping the pre-arm on the triggering step keeps the ' +\n 'pre-arm and the triggering action in one place.'\n ),\n })\n .superRefine((step, ctx) => {\n if (step.action === 'agent_loop') {\n const goal = step.params?.goal;\n if (!goal || goal.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'goal'],\n message:\n 'agent_loop steps must set params.goal to a one-sentence checkable predicate (e.g. \"3 grid row checkboxes are selected\").',\n });\n }\n }\n if (step.action === 'verify_download') {\n const pattern = step.params?.pattern;\n if (!pattern || pattern.trim().length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'pattern'],\n message:\n 'verify_download steps must set params.pattern to a glob (supports *, ?) or substring matching the expected filename.',\n });\n }\n }\n if (step.action === 'verify_download_content') {\n const pattern = step.params?.pattern;\n const contains = step.params?.contains;\n if (\n (!pattern || pattern.trim().length === 0) &&\n (!contains || contains.trim().length === 0)\n ) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params'],\n message:\n 'verify_download_content steps must set params.contains or params.pattern for the expected downloaded content.',\n });\n }\n }\n if (step.action === 'resize') {\n const width = step.params?.width;\n const height = step.params?.height;\n if (!isPositiveIntegerString(width)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'width'],\n message: 'resize steps must set params.width to a positive integer pixel value.',\n });\n }\n if (!isPositiveIntegerString(height)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['params', 'height'],\n message: 'resize steps must set params.height to a positive integer pixel value.',\n });\n }\n }\n });\n\n/** Inferred type of the agent-authored fields of a playbook step. */\nexport type PlaybookStepAgentInput = z.infer<typeof playbookStepAgentSchema>;\n\n/**\n * A single step in a workflow playbook.\n *\n * Composed of {@link PlaybookStepAgentInput} (everything the agent can set\n * via `updateWorkflowStep`) plus internal fields the runtime attaches during\n * compilation/healing. Splitting these keeps the agent's input surface\n * tight while letting the runtime carry provenance + screenshot keys.\n */\nexport interface PlaybookStep extends Omit<PlaybookStepAgentInput, 'action'> {\n /** Legacy persisted rows may still contain `other`; new agent-authored steps cannot. */\n action: LegacyPlaybookStepAction;\n\n /**\n * Provenance of `actionRefs`:\n * `'recording'` (default when omitted) — emitted by the initial recording\n * compilation; transient session-specific indices that should be stripped\n * after the cache row is built.\n * `'healing'` — emitted by the heal agent during cache-failure recovery and\n * proven correct by replay. These should be preserved across compiles so\n * the next compile lands the entry as `matchStrategy='actionRef'`.\n */\n actionRefsSource?: 'recording' | 'healing';\n /** S3 key for screenshot captured after this playbook step completed. */\n screenshotS3Key?: string;\n /**\n * Where this playbook came from.\n * `'production'` (default when omitted) — emitted by a production-mode run\n * and eligible for cache replay if it carries `actionRefs`.\n * `'validation'` — emitted by a validation-mode discovery run; advisory\n * only. Never replayed deterministically (no `actionRefs`); used as a\n * seed for the next production run on this node, which can then promote\n * the playbook by attaching `actionRefs` on success.\n */\n playbookSource?: 'production' | 'validation';\n}\n\n/**\n * Execution-time snapshot of one playbook step, persisted into\n * `workflow_run_steps.metadataJson.executedPlaybook` when step-driven cache\n * replay ran for a step (full hit or partial replay before agent fallback).\n *\n * Deliberately slim: only the display-relevant agent-authored fields. Runtime\n * plumbing (`actionRefs`, `actionRefsSource`, `screenshotS3Key`,\n * `playbookSource`, `expectedDialogs`) is stripped so run rows stay small.\n * Values are sanitized back to `$var.`/`$MACRO` tokens before persistence —\n * the cache row is mutable, so this snapshot is the only exact record of what\n * replayed for a historical run.\n */\nexport interface ExecutedPlaybookStep {\n /** Step number (1-based) — joins against `StepOutcome.stepOrder`. */\n order: number;\n action: LegacyPlaybookStepAction;\n /** Human-readable element description. */\n target: string;\n /** Data values, with variable references preserved as `$var.`/`$MACRO` tokens. */\n params?: Record<string, string>;\n /** Optional human context authored alongside the step. */\n note?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Validation run findings\n// ---------------------------------------------------------------------------\n\n/**\n * One assumption an agent made during a validation run. Surfaces as a finding\n * the test author can review and either bake into the spec or reject.\n */\nexport interface ValidationAssumption {\n /** Plain-language description of what was assumed. */\n description: string;\n /** Why the agent thought this was the right interpretation. */\n basis: string;\n /** What might go wrong on a future run if the assumption is incorrect. */\n riskIfWrong: string;\n}\n\n/**\n * One open question for the test author surfaced during a validation run.\n */\nexport interface ValidationQuestion {\n /** The specific question. */\n question: string;\n /**\n * When true, this is a question that — if unanswered — will likely cause\n * future production runs to fail or behave inconsistently. UIs should\n * surface these prominently in the post-run proposal.\n */\n blockingFutureRuns: boolean;\n}\n\n/**\n * Suggested edit to a workflow node's config, produced by a validation run.\n *\n * Intentionally narrow: validation findings should propose targeted patches\n * (e.g. \"add a precondition\", \"tighten this assertion\", \"rename the data\n * requirement\"), not free-form rewrites. The shape of `value` depends on\n * `field`. This is consumed by the workflow_proposals UI for accept/reject.\n */\nexport interface NodeConfigPatch {\n /** Which area of the node config this patch touches. */\n field:\n | 'instructions'\n | 'assertions'\n | 'preconditions'\n | 'dataRequirements'\n | 'description'\n | 'other';\n /** What kind of edit is being proposed. */\n op: 'add' | 'replace' | 'remove';\n /** Human-readable summary of the proposed change. */\n summary: string;\n /**\n * Structured patch payload. Shape varies with `field` + `op`. The proposal\n * UI knows how to render each combination; the type is intentionally\n * `unknown` here so the catalog can grow without a types package change.\n */\n value?: unknown;\n}\n\n/**\n * The validation-run-specific result for a single workflow step. Persisted\n * onto `workflow_run_steps.metadataJson.validation` and aggregated into the\n * post-run `validation_findings` proposal.\n */\nexport interface ValidationFindings {\n outcome: 'valid' | 'passed_with_concerns' | 'could_not_complete';\n /** True/false branch decision for conditional nodes. */\n branch?: 'true' | 'false';\n assumptions: ValidationAssumption[];\n questions: ValidationQuestion[];\n /**\n * Concrete config edits the agent suggests for this node. Empty for\n * `valid` outcomes; required for `passed_with_concerns`; optional for\n * `could_not_complete` (the failure itself is the primary signal).\n */\n suggestedNodeConfigPatches: NodeConfigPatch[];\n}\n\n/**\n * An assertion attached directly to an action node.\n * Verified after the action completes; cacheable assertions are replayed mechanically.\n */\nexport interface InlineAssertion {\n /** Unique ID within this node */\n id: string;\n /** Freeform description of what to verify */\n condition: string;\n /** fatal = stop the test now; fail = mark the run failed but continue; warn = log and continue */\n severity: AssertionSeverity;\n /** When true, requires agent evaluation (e.g. computer vision) and cannot be cached */\n agentic?: boolean;\n}\n\nexport type BrowserMode = 'headed' | 'headless';\n\nexport * from './crawler/analysis.js';\nexport * from './api-catalog.js';\nexport * from './credentials.js';\nexport * from './environment-kinds.js';\nexport * from './trace-uploads.js';\nexport * from './qa/testing.js';\nexport * from './qa/events.js';\nexport * from './issues.js';\nexport * from './pagination.js';\nexport * from './workflows.js';\nexport * from './browser-stream.js';\nexport * from './attachments.js';\nexport * from './permissions.js';\nexport * from './workflow-cache.js';\nexport * from './cache-optimizer-events.js';\nexport * from './cache-telemetry.js';\nexport * from './instance-logs.js';\nexport * from './local-browser.js';\nexport * from './chaos.js';\nexport * from './benchmark.js';\n\nexport * from './api-sequence.js';\nexport * from './diagnostics.js';\nexport * from './conversation-stream.js';\nexport * from './sandbox-tasks.js';\nexport * from './sandbox-instance-size.js';\nexport * from './routes/index.js';\nexport * from './macros.js';\nexport * from './recording.js';\nexport * from './variable-utils.js';\nexport * from './node-outputs.js';\nexport * from './sandbox-action-node.js';\nexport * from './credential-output.js';\nexport * from './adhoc-test.js';\nexport * from './mentions.js';\nexport * from './element-hints.js';\nexport * from './ai-routing.js';\nexport * from './workflow-node-schemas.js';\nexport * from './node-type-labels.js';\nexport * from './browser-viewport.js';\nexport * from './identifier-stability.js';\nexport * from './workflow-review.js';\nexport * from './fast-video.js';\n","/**\n * Credential Types\n *\n * Strongly-typed credential data structures for each auth provider\n */\n\nimport { z } from 'zod';\n\nexport type AuthProvider =\n | 'basic'\n | 'google'\n | 'github'\n | 'microsoft'\n | 'api_key'\n | 'twitter'\n | 'custom'\n | 'api_login'\n | 'api_basic';\n\nexport type CredentialRole = 'admin' | 'user' | 'viewer' | 'custom';\n\n/**\n * Custom HTTP headers for browser automation.\n * Stored encrypted alongside credential data.\n * Useful for bypassing bot detection (e.g., User-Agent, Accept-Language).\n */\nexport type CustomHttpHeaders = Record<string, string>;\n\n/**\n * HTTP Basic Authentication credentials for browser-level auth popups.\n * Passed to Playwright via browser.newContext({ httpCredentials }).\n */\nexport type HttpCredentials = {\n username: string;\n password: string;\n};\n\n// Individual credential data types\n\nexport type BasicAuthCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type GoogleCredential = {\n email: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type GithubCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type MicrosoftCredential = {\n email: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiKeyCredential = {\n key: string;\n prefix?: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type TwitterCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiLoginCredential = {\n loginFields: Record<string, string>;\n cachedToken?: string;\n cachedTokenExpiresAt?: string;\n cachedTokenObtainedAt?: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type ApiBasicCredential = {\n username: string;\n password: string;\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\nexport type CustomCredential = Record<string, any> & {\n customHeaders?: CustomHttpHeaders;\n httpCredentials?: HttpCredentials;\n};\n\n// Union type for all credential data\nexport type CredentialData =\n | { provider: 'basic'; data: BasicAuthCredential }\n | { provider: 'google'; data: GoogleCredential }\n | { provider: 'github'; data: GithubCredential }\n | { provider: 'microsoft'; data: MicrosoftCredential }\n | { provider: 'api_key'; data: ApiKeyCredential }\n | { provider: 'twitter'; data: TwitterCredential }\n | { provider: 'api_login'; data: ApiLoginCredential }\n | { provider: 'api_basic'; data: ApiBasicCredential }\n | { provider: 'custom'; data: CustomCredential };\n\n/**\n * Login validity tracking data for session duration prediction\n */\nexport type LoginValidityInfo = {\n /** When a login was last attempted */\n lastLoginAt: Date | null;\n /** When a login last succeeded */\n lastLoginSucceededAt: Date | null;\n /** When the session was detected as expired (requiring re-login) */\n nextLoginRequiredAt: Date | null;\n /** Calculated session validity duration in minutes */\n loginValidityDurationMinutes: number | null;\n /** Estimated session expiry based on last login + known validity */\n estimatedExpiresAt: Date | null;\n};\n\n/**\n * HeaderExtractor: declarative way to derive an HTTP header value from a\n * credential's saved storage state (Playwright cookies + localStorage +\n * extracted IndexedDB). Materialized at request time by the executor and\n * merged into the same `extraHeaders` map as `customHeaders`.\n *\n * Modern SPAs (Firebase Auth, Auth0, Clerk, …) stash JWTs in localStorage or\n * IndexedDB and rely on page JS to forward them as request headers. Playwright\n * APIRequestContext only carries cookies, so those headers go missing when we\n * fetch through the browser context. Extractors close that gap.\n */\nexport const tokenRefreshPolicySchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('firebase'),\n refreshTokenPath: z.string().default('$.value.stsTokenManager.refreshToken'),\n expirationPath: z.string().default('$.value.stsTokenManager.expirationTime'),\n accessTokenPath: z.string().default('$.value.stsTokenManager.accessToken'),\n apiKeyPath: z.string().default('$.value.apiKey'),\n }),\n]);\n\nexport type TokenRefreshPolicy = z.infer<typeof tokenRefreshPolicySchema>;\n\nconst localStorageSourceSchema = z.object({\n kind: z.literal('localStorage'),\n origin: z.string().min(1),\n key: z.string().min(1),\n jsonPath: z.string().optional(),\n});\n\nconst indexedDBSourceSchema = z.object({\n kind: z.literal('indexedDB'),\n database: z.string().min(1),\n store: z.string().min(1),\n key: z.string().min(1),\n jsonPath: z.string().min(1),\n});\n\nconst cookieSourceSchema = z.object({\n kind: z.literal('cookie'),\n domain: z.string().min(1),\n name: z.string().min(1),\n});\n\nexport const headerExtractorSourceSchema = z.discriminatedUnion('kind', [\n localStorageSourceSchema,\n indexedDBSourceSchema,\n cookieSourceSchema,\n]);\n\nexport const headerExtractorSchema = z.object({\n name: z.string().min(1),\n header: z.string().min(1).optional(),\n prefix: z.string().optional(),\n source: headerExtractorSourceSchema,\n refresh: tokenRefreshPolicySchema.optional(),\n});\n\nexport type HeaderExtractorSource = z.infer<typeof headerExtractorSourceSchema>;\nexport type HeaderExtractor = z.infer<typeof headerExtractorSchema>;\n\n/**\n * Typed view of credential.metadata. Existing fields (headerName / token*)\n * predate extractors and remain supported. `headerExtractors` is the new\n * generalized lane.\n */\nexport const credentialMetadataSchema = z\n .object({\n headerName: z.string().optional(),\n tokenHeaderName: z.string().optional(),\n tokenHeaderPrefix: z.string().optional(),\n headerExtractors: z.array(headerExtractorSchema).optional(),\n })\n .passthrough();\n\nexport type CredentialMetadata = z.infer<typeof credentialMetadataSchema>;\n\n// Extract email from credential data if available\nexport function extractEmail(provider: AuthProvider, data: any): string | null {\n switch (provider) {\n case 'basic':\n return data.username || null;\n case 'google':\n case 'microsoft':\n return data.email || null;\n case 'github':\n case 'twitter': {\n // May be username or email\n const val = data.username || null;\n return val && val.includes('@') ? val : null;\n }\n case 'api_login':\n return data.loginFields?.username || data.loginFields?.email || null;\n case 'api_basic': {\n const val = data.username || null;\n return val && val.includes('@') ? val : null;\n }\n default:\n return null;\n }\n}\n","import { z } from 'zod';\nimport type { Pagination } from './pagination.js';\nexport type { Pagination } from './pagination.js';\n\nconst clamp = (value: string, max: number): string =>\n value.length > max ? value.slice(0, max) : value;\n\nconst requiredLimitedString = (label: string, max: number) =>\n z\n .string()\n .transform((value) => value.trim())\n .pipe(z.string().min(1, `${label} is required`))\n .transform((value) => clamp(value, max));\n\nconst optionalLimitedString = (max: number) =>\n z\n .string()\n .optional()\n .nullable()\n .transform((value) => {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n if (!trimmed) return null;\n return clamp(trimmed, max);\n });\n\nconst ISO_DATETIME = z.string().datetime({ offset: true });\nconst UUID = z.string().uuid();\n\nexport const IssueSeveritySchema = z.enum(['low', 'medium', 'high', 'unknown']);\nexport type IssueSeverity = z.infer<typeof IssueSeveritySchema>;\n\nexport const IssueStatusSchema = z.enum(['open', 'closed', 'not_a_bug']);\nexport type IssueStatus = z.infer<typeof IssueStatusSchema>;\n\nexport interface IssueCategorySummary {\n id: string;\n key: string;\n label: string;\n description: string | null;\n instructions: string | null;\n}\n\nexport interface IssueCategorySetting extends IssueCategorySummary {\n isActive: boolean;\n isSystem: boolean;\n sortOrder: number;\n}\n\nexport const IssueSourceKindSchema = z.enum([\n 'crawler_analysis',\n 'exploratory_attempt',\n 'qa_page_test',\n 'workflow_test',\n 'chaos_test',\n 'adhoc_test',\n]);\nexport type IssueSourceKind = z.infer<typeof IssueSourceKindSchema>;\n\n/**\n * A single network request captured from a Playwright trace.\n * Bodies are truncated to 100KB.\n */\nexport interface NetworkRequestEntry {\n url: string;\n method: string;\n status: number | null;\n durationMs: number | null;\n requestBody: string | null;\n responseBody: string | null;\n requestHeaders: Record<string, string> | null;\n responseHeaders: Record<string, string> | null;\n contentType: string | null;\n}\n\n/**\n * A single console log entry captured from a Playwright trace.\n */\nexport interface ConsoleLogEntry {\n type: 'log' | 'warning' | 'error' | 'info' | 'debug';\n text: string;\n timestamp: number | null;\n location: string | null;\n}\n\n/**\n * Category of failure root cause — add new values to this array (type is derived).\n *\n * `app_changed` is distinct from both `software_bug` and `test_underspecified`: the\n * application's behavior/labels/flow changed versus the last passing run while the\n * authored test stayed the same, and the agent cannot verify from inside the sandbox\n * whether the change was intentional. The recommendation pattern is \"X appears to have\n * changed to Y; update the test to match if intentional.\" Reserve `software_bug` for\n * behavior deltas that are clearly defects (error page, 5xx, broken layout).\n */\nexport const FAILURE_DIAGNOSTIC_CATEGORIES = ['software_bug', 'dependency_issue', 'agent_confusion', 'platform_error', 'test_underspecified', 'data_collision', 'app_changed'] as const;\nexport type FailureDiagnosticCategory = typeof FAILURE_DIAGNOSTIC_CATEGORIES[number];\n\n/** Evidence item from diagnostic analysis */\nexport interface FailureDiagnosticEvidence {\n type:\n | 'network_error'\n | 'console_error'\n | 'visual'\n | 'agent_thought'\n | 'timeline_event'\n | 'history_comparison'\n | 'before_after';\n timestamp?: number;\n description: string;\n detail?: string;\n /** S3 storage key for an extracted video frame (visual evidence only) */\n frameStorageKey?: string;\n\n /** Before/after comparison fields */\n beforeFrameStorageKey?: string;\n beforeCaption?: string;\n beforeTimestamp?: number;\n beforeRunId?: string;\n afterFrameStorageKey?: string;\n afterCaption?: string;\n afterTimestamp?: number;\n afterRunId?: string;\n}\n\n/** A structured per-node improvement suggestion produced by the diagnostic agent */\nexport interface FlowSuggestion {\n id: string;\n orderIndex: number;\n nodeId: string;\n nodeType: string;\n title: string;\n rationale: string;\n configPatch: Record<string, unknown> | null;\n sourceRunId?: string;\n sourceAuditIds?: string[];\n evidenceIndexes?: number[];\n status: 'pending' | 'accepted' | 'dismissed';\n decidedAt?: string;\n decidedByUserId?: string;\n resolutionSource?: 'user' | 'system';\n resolutionReason?: 'accepted' | 'dismissed' | 'node_deleted' | 'workflow_stale';\n}\n\n/**\n * One key moment in the diagnostic's root-cause timeline — an ordered \"what to\n * watch\" event the QA tester can jump to in the run video. Surfaced both as\n * markers on the video's diagnostic-timeline track and as the timestamped\n * key-evidence list at the top of the run Summary.\n */\nexport interface RootCauseTimelineEvent {\n /**\n * Milliseconds from the start of the video recording — the same scale as the\n * run's `videoTimelineJson` entries and `FailureDiagnosticEvidence.timestamp`\n * (the `relativeTimestampMs` column in the diagnostic workspace's\n * `timeline/actions.jsonl`, NOT the absolute wall-clock `timestampMs`).\n */\n timestampMs: number;\n /** Short marker/row label (≤8 words). */\n title: string;\n /** Optional one-line elaboration shown on hover / under the row. */\n detail?: string;\n /** Visual weight of the marker. Defaults to `info` when omitted. */\n severity?: 'info' | 'warning' | 'critical';\n}\n\n/** Structured diagnostic analysis produced by the failure diagnostic agent */\nexport interface FailureDiagnostic {\n category: FailureDiagnosticCategory;\n confidence: number;\n /** One-sentence thesis — the QA tester reads only this 80% of the time. */\n title: string;\n /**\n * Legacy freeform Markdown narrative (kept for back-compat with already-persisted rows).\n * The current prompt emits `failurePoints` bullets instead; new diagnostics may omit `analysis`.\n */\n analysis?: string;\n /** 2–4 concise bullets describing the failure (current report shape). */\n failurePoints?: string[];\n evidence: FailureDiagnosticEvidence[];\n smokingGun?: {\n type: string;\n description: string;\n detail?: string;\n };\n recommendation: string;\n comparisonWithHistory?: string;\n /** Things the agent could not determine from available evidence (display-only for now). */\n openQuestions?: string[];\n /**\n * Whether the agent materialized a green-baseline comparison workspace before this\n * verdict (v2 `materialize_run` tool). `false` on an `app_changed` verdict means the\n * comparison gate was bypassed (second submission or step-cap salvage) — treat the\n * verdict as low-trust. Absent on diagnoses that predate the field.\n */\n comparisonPerformed?: boolean;\n completedAt: string;\n agentRunId?: string;\n /** Optional structured per-node improvement suggestions */\n suggestions?: FlowSuggestion[];\n /**\n * Optional ordered root-cause timeline — key moments to watch in the run\n * video, each anchored to a video-relative timestamp so the UI can mark them\n * on the player and let the tester jump to them. Absent on older diagnoses.\n */\n rootCauseTimeline?: RootCauseTimelineEvent[];\n}\n\nexport interface IssueOccurrence {\n id: string;\n issueId: string;\n orgId: string;\n propertyId: string;\n canonicalPageId: string | null;\n credentialId: string | null;\n sourceKind: IssueSourceKind;\n sourceId: string;\n detectedAt: string;\n primaryUrl: string;\n tldr: string | null;\n summary: string | null;\n errorDetails: string | null;\n screenshotStorageKey: string | null;\n screenshotCaption: string | null;\n traceStorageKey: string | null;\n videoStorageKey: string | null;\n /** Action timeline for video overlay synchronization */\n videoTimelineJson: unknown | null;\n consoleLogExcerpt: string | null;\n networkFailureUrl: string | null;\n networkFailureStatus: number | null;\n ticketReproSteps: string | null;\n ticketUserFacingSummary: string | null;\n ticketDiagnosticsAnalysis: string | null;\n dedupReasoning: string | null;\n /** Status of async failure diagnostic analysis */\n diagnosticStatus: string | null;\n /** Structured diagnostic analysis from the failure diagnostic agent */\n diagnosticJson: FailureDiagnostic | null;\n /** Resolved workflow IDs when sourceKind is 'workflow_test' */\n workflowSourceInfo?: { workflowId: string; workflowRunId: string } | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface IssueAssignee {\n id: string;\n displayName: string | null;\n primaryEmail: string | null;\n}\n\nexport interface IssueSummary {\n id: string;\n orgId: string;\n propertyId: string;\n credentialId: string | null;\n title: string;\n tldr: string | null;\n severity: IssueSeverity;\n category: IssueCategorySummary;\n confidence: number | null;\n firstSeenAt: string;\n lastSeenAt: string;\n occurrenceCount: number;\n status: IssueStatus;\n statusUpdatedAt: string;\n closedAt: string | null;\n isArchived: boolean;\n assignedToId: string | null;\n assignedTo: IssueAssignee | null;\n createdAt: string;\n updatedAt: string;\n latestOccurrence: IssueOccurrence | null;\n tags?: Array<{ id: string; name: string; color: string }>;\n}\n\nexport interface IssueDetail extends IssueSummary {\n occurrences: IssueOccurrence[];\n}\n\nexport interface PaginatedIssuesResponse {\n data: IssueSummary[];\n pagination: Pagination;\n}\n\nexport interface IssueDetailResponse {\n issue: IssueSummary;\n occurrences: IssueOccurrence[];\n occurrencesPagination: Pagination;\n commentCount: number;\n}\n\nexport interface PaginatedIssueOccurrencesResponse {\n data: IssueOccurrence[];\n pagination: Pagination;\n}\n\nexport interface IssueCommentAuthor {\n id: string;\n displayName: string | null;\n primaryEmail: string | null;\n}\n\nexport interface IssueComment {\n id: string;\n issueId: string;\n orgId: string;\n propertyId: string;\n authorId: string;\n author: IssueCommentAuthor | null;\n body: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface PaginatedIssueCommentsResponse {\n data: IssueComment[];\n pagination: Pagination;\n}\n\nconst TITLE_LIMIT = 512;\nconst TLDR_LIMIT = 512;\nconst URL_LIMIT = 2048;\nconst STORAGE_KEY_LIMIT = 1024;\nconst DIAGNOSTIC_LIMIT = 4000;\n\nexport const RecordIssueOccurrencePayloadSchema = z.object({\n orgId: UUID,\n propertyId: UUID,\n canonicalPageId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n credentialId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n issueTitle: requiredLimitedString('Issue title', TITLE_LIMIT),\n issueSeverity: IssueSeveritySchema,\n issueCategoryId: UUID.optional()\n .nullable()\n .transform((value) => value ?? null),\n issueCategoryKey: z\n .string()\n .min(1)\n .max(64)\n .optional()\n .nullable()\n .transform((value) => {\n if (typeof value !== 'string') return null;\n const trimmed = value.trim();\n return trimmed.length ? trimmed : null;\n }),\n issueConfidence: z\n .number()\n .int()\n .min(0)\n .max(100)\n .optional()\n .nullable()\n .transform((value) => {\n if (value === null || value === undefined) return null;\n return value;\n }),\n detectedAt: ISO_DATETIME,\n sourceKind: IssueSourceKindSchema,\n sourceId: requiredLimitedString('Source identifier', STORAGE_KEY_LIMIT),\n primaryUrl: requiredLimitedString('Primary URL', URL_LIMIT),\n summary: requiredLimitedString('Summary', DIAGNOSTIC_LIMIT),\n tldr: requiredLimitedString('TLDR', TLDR_LIMIT),\n screenshotStorageKey: requiredLimitedString('Screenshot storage key', STORAGE_KEY_LIMIT),\n screenshotCaption: optionalLimitedString(DIAGNOSTIC_LIMIT),\n traceStorageKey: optionalLimitedString(STORAGE_KEY_LIMIT),\n videoStorageKey: optionalLimitedString(STORAGE_KEY_LIMIT),\n /** Action timeline for video overlay synchronization */\n videoTimelineJson: z.unknown().optional().nullable(),\n consoleLogExcerpt: optionalLimitedString(DIAGNOSTIC_LIMIT),\n networkFailureUrl: optionalLimitedString(URL_LIMIT),\n networkFailureStatus: z\n .number()\n .int()\n .optional()\n .nullable()\n .transform((value) => value ?? null),\n /** Structured network requests captured from Playwright trace */\n networkRequestsJson: z.array(z.unknown()).optional().nullable(),\n /** Structured console logs captured from Playwright trace */\n consoleLogsJson: z.array(z.unknown()).optional().nullable(),\n ticketReproSteps: optionalLimitedString(DIAGNOSTIC_LIMIT),\n ticketUserFacingSummary: requiredLimitedString('Ticket summary', DIAGNOSTIC_LIMIT),\n ticketDiagnosticsAnalysis: requiredLimitedString('Ticket diagnostics analysis', DIAGNOSTIC_LIMIT),\n});\n\nexport type RecordIssueOccurrencePayload = z.infer<typeof RecordIssueOccurrencePayloadSchema>;\n","/**\n * Workflow Action Cache Types\n *\n * Types for caching browser actions from workflow runs for fast replay\n * without the AI agent.\n */\n\nimport { z } from 'zod';\nimport { AssertionSeverityEnum, type AssertionSeverity } from './workflow-node-schemas.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Selector Strategy Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Role-based selector (most stable for accessibility-focused apps)\n */\nexport type RoleSelector = {\n role: string;\n name?: string;\n exact?: boolean;\n};\n\n/**\n * Text content selector\n */\nexport type TextSelector = {\n text: string;\n exact?: boolean;\n};\n\n/**\n * Parent context for disambiguating elements with similar selectors\n */\n/**\n * Dynamic-text annotation for parent-context fields whose disambiguating text\n * originates from a workflow variable and must be re-resolved at replay time.\n */\nexport type ParentContextDynamicText = {\n source: 'inputValue' | 'workflowState';\n key: string;\n originalText: string;\n};\n\n/**\n * A single node in a parent-scoping chain.\n */\nexport type ParentContextNode = {\n role: string;\n name?: string;\n css?: string;\n nameFromHeading?: boolean;\n /**\n * Filter the parent by text it contains. Used to anchor on row-like\n * ancestors (role=row/listitem/gridcell) where the row has no accessible\n * name of its own but contains a stable cell value (e.g. a variable name).\n * Applied at replay as `.filter({ hasText: containsText })`.\n */\n containsText?: string;\n /** Dynamic binding for containsText when it originated from a workflow variable. */\n containsTextDynamic?: ParentContextDynamicText;\n};\n\nexport type ParentContext = {\n /** Single parent role (backward compatible) */\n role?: string;\n /** Single parent name (backward compatible) */\n name?: string;\n /** Optional CSS selector for parent scoping when role-based lookup is unavailable */\n css?: string;\n /** True when name was extracted from a child heading, not the parent's own accessible name */\n nameFromHeading?: boolean;\n /**\n * Filter the parent by text it contains. Used to anchor on row-like\n * ancestors that lack a unique accessible name (see ParentContextNode).\n */\n containsText?: string;\n /** Dynamic binding for containsText when it originated from a workflow variable. */\n containsTextDynamic?: ParentContextDynamicText;\n\n /** Recursive parent chain for deep disambiguation (innermost to outermost) */\n parentChain?: ParentContextNode[];\n\n /** Fallback: index of matched element when no unique parent found */\n matchIndex?: number;\n};\n\n/**\n * Frame/container context for elements inside iframes or shadow DOM.\n * Captured at recording time to ensure deterministic replay.\n */\nexport type FrameContext = {\n /** Type of container */\n type: 'iframe' | 'shadow' | 'named-frame';\n /** Selector to locate the container (e.g., 'iframe.designer-client-frame') */\n selector: string;\n /** Optional: frame name attribute for named frames */\n name?: string;\n /** Optional: frame ID attribute */\n id?: string;\n};\n\n/** Who authored the active Playwright locator. */\nexport type SelectorAuthor = 'deterministic' | 'agentic';\n\n/** Alternate Playwright locator candidate stored for comparison/debugging. */\nexport type PlaywrightLocatorCandidate = {\n /** Locator chain string. */\n locator: string;\n /** Whether the locator contains variable placeholders. */\n usesVariableBinding?: boolean;\n /** Variable key used by the locator, if any. */\n boundVariableKey?: string;\n};\n\n/**\n * Multiple selector strategies for fallback during replay.\n * Tried in order: testId -> role -> ariaLabel -> placeholder -> text -> css\n */\nexport type SelectorStrategy = {\n /** data-testid selector (most stable when available) */\n testId?: string;\n /** Role-based selector (most stable) */\n role?: RoleSelector;\n /** Label text selector (getByLabel) */\n ariaLabel?: string;\n /** Placeholder selector (getByPlaceholder) */\n placeholder?: string;\n /** Text content selector */\n text?: TextSelector;\n /** Additional text parts to require within the resolved element */\n containsText?: string[];\n /** CSS selector (least stable, last resort) */\n css?: string;\n /** Parent context for disambiguation */\n parentContext?: ParentContext;\n /** Frame/container context - element lives inside this container */\n frameContext?: FrameContext;\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Data attribute disambiguation (for list/table elements)\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** Item index for list disambiguation (data-item-index, data-selection-index) */\n itemIndex?: number;\n /** Row index for table/grid disambiguation (aria-rowindex) */\n rowIndex?: number;\n /** Automation ID for verification (data-automationid) */\n automationId?: string;\n\n /** Filter to disambiguate by a child element's role + name (e.g., child img alt text) */\n childFilter?: {\n role: string;\n name: string;\n };\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Playwright locator chain (authored by cache selector agent)\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Active Playwright locator chain used during replay.\n * Supports variable placeholders: {{var.project_name}} hydrated at replay time.\n * Example: \"getByRole('row').filter({ hasText: '{{var.project_name}}' }).getByRole('button', { name: 'Edit' })\"\n */\n playwrightLocator?: string;\n\n /** Alternate deterministic/agentic locator candidates preserved for comparison. */\n playwrightLocatorCandidates?: Partial<Record<SelectorAuthor, PlaywrightLocatorCandidate>>;\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Dynamic text binding (for selectors containing run-specific content)\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** Dynamic text binding — selector text that varies between runs */\n dynamicText?: {\n /** Data source for the dynamic value */\n source: 'inputValue' | 'workflowState';\n /** Key in the data source */\n key: string;\n /** The literal text at recording time (for token replacement) */\n originalText: string;\n };\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Context Extractor Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A small JS extraction script that runs via page.evaluate() after cache replay\n * to extract fresh application-generated values (IDs, codes, computed results) from the live page.\n *\n * Should only target data produced by the application (auto-generated IDs, confirmation codes,\n * computed totals), NOT data the agent entered (form inputs, search terms).\n *\n * Authored by agents in debug mode when step notes contain run-specific data.\n */\nexport type ContextExtractor = {\n /** Snake_case key for the extracted value (e.g., \"project_id\") */\n key: string;\n /** JS expression for page.evaluate(). Must return string | null. */\n expression: string;\n /** Human-readable description of what this extractor captures */\n description?: string;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cached Data Script Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A Python script cached for automatic replay alongside browser action cache.\n * When the agent uses python_execute with cache_label, the script is stored here\n * so it can be re-run without the agent during cached replay.\n */\nexport type CachedDataScript = {\n /** Short descriptive label (e.g., \"validate-row-count\") */\n label: string;\n /** Python code to execute */\n code: string;\n /** Table name from the preceding browser_extract_table call */\n tableName: string;\n /** Optional ref for a specific table (matches browser_extract_table ref parameter) */\n tableRef?: string;\n /** ISO timestamp when this script was cached */\n createdAt: string;\n /** Index of the corresponding CachedActionRecord (for playbook linkage). */\n actionIndex?: number;\n};\n\n/**\n * Result of replaying a cached data script during cache replay.\n */\nexport interface DataScriptReplayResult {\n label: string;\n success: boolean;\n stdout?: string;\n stderr?: string;\n error?: string;\n durationMs: number;\n}\n\nexport type CacheCaptureWarningReason =\n | 'empty'\n | 'invalid'\n | 'error'\n | 'timeout'\n | 'missing_callback';\n\nexport type CacheCaptureWarning = {\n key: string;\n required: boolean;\n reason: CacheCaptureWarningReason;\n actionIndex?: number;\n expression?: string;\n currentUrl?: string;\n lastValue?: string;\n validationReason?: string;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cached Action Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Types of browser actions that can be cached\n */\nexport type CachedActionType =\n | 'navigate'\n | 'click'\n | 'type'\n | 'press_key'\n | 'select_option'\n | 'fill_form'\n | 'wait_for'\n | 'hover'\n | 'drag'\n | 'resize'\n | 'scroll'\n | 'screenshot'\n | 'file_upload'\n | 'handle_dialog'\n | 'snapshot'\n | 'evaluate'\n | 'annotation'\n | 'assert'\n | 'move'\n | 'capture'\n | 'extract_table'\n | 'python_execute';\n\n/**\n * Types of wait strategies before executing an action\n */\nexport type WaitStrategyType = 'network_idle' | 'dom_stable' | 'selector' | 'delay';\n\n/**\n * Wait strategy configuration\n */\nexport type WaitStrategy = {\n type: WaitStrategyType;\n /** CSS selector to wait for (when type is 'selector') */\n selector?: string;\n /** Delay in milliseconds (when type is 'delay') */\n delayMs?: number;\n};\n\n/**\n * Types of assertions that can be cached\n */\nexport type AssertionType =\n | 'text_present'\n | 'text_absent'\n | 'element_visible'\n | 'element_hidden'\n | 'evaluate'\n | 'toast_present';\n\n/**\n * Assertion specification for cache replay verification\n */\nexport type AssertionSpec = {\n type: AssertionType;\n /** The condition to check (text content, selector, or JS expression) */\n condition: string;\n /** Expected value for evaluate assertions */\n expected?: unknown;\n /** When true, assertion is validated during cache replay. When false/undefined (loose), skipped during replay. */\n strict?: boolean;\n /** Severity from an inline assertion: fatal halts the run, fail marks it failed but continues, warn logs only */\n severity?: AssertionSeverity;\n /** Source inline assertion ID for correlating results back to the node config */\n inlineAssertionId?: string;\n /** Short human-readable description supplied by the agent at browser_assert time */\n label?: string;\n};\n\n/**\n * Zod schema for `AssertionSpec` — single source for every surface that\n * accepts assertion specs from clients (the optimizer agent's commit tool and\n * the CLI cache-recipe route). The `satisfies` bind fails the build if the\n * schema and the documented type ever drift.\n */\nexport const assertionSpecSchema = z.object({\n type: z.enum([\n 'text_present',\n 'text_absent',\n 'element_visible',\n 'element_hidden',\n 'evaluate',\n 'toast_present',\n ]),\n condition: z.string(),\n expected: z.unknown().optional(),\n strict: z.boolean().optional(),\n severity: AssertionSeverityEnum.optional(),\n inlineAssertionId: z.string().optional(),\n label: z.string().optional(),\n}) satisfies z.ZodType<AssertionSpec>;\n\n/**\n * A single cached browser action with stable selectors\n */\nexport type CachedAction = {\n /** Sequence number within the cache */\n index: number;\n /** Original tool name from agent (e.g., 'browser_click') */\n toolName: string;\n /** Normalized action type category */\n actionType: CachedActionType;\n /** Workflow node this action belongs to */\n nodeId: string;\n /** Stable selector strategies (multiple for fallback) */\n selectors?: SelectorStrategy;\n /** Original tool arguments (sanitized - no ephemeral refs) */\n args: Record<string, unknown>;\n /** Assertion to verify during replay (for assertion nodes) */\n assertion?: AssertionSpec;\n /** Wait strategy to execute before this action */\n waitBefore?: WaitStrategy;\n /** Key from variable registry for variable-aware data substitution */\n variableKey?: string;\n /** Original element description for debugging */\n elementDescription?: string;\n /** Duration of original action in ms (for timing hints) */\n originalDurationMs?: number;\n /** Flag for actions that require agent fallback (missing selectors) */\n requiresAgent?: boolean;\n /** Who authored the active selector: deterministic or agentic */\n selectorAuthor?: SelectorAuthor;\n /** Why the AI locator agent failed, if attempted but unsuccessful */\n selectorAgentFailureReason?: string;\n /** True for injected annotations (vs browser actions) */\n isAnnotation?: boolean;\n /** Annotation type (e.g., 'step-completed', 'milestone') */\n annotationType?: string;\n /** Hex color or named color for annotation UI rendering */\n annotationColor?: string;\n /**\n * Maps arg paths to their original macro expressions.\n * At replay time, these are resolved fresh instead of using the literal in args.\n * e.g. { \"text\": \"$UUID()\", \"fields.1.value\": \"user-$UUID()@test.com\" }\n */\n macroBindings?: Record<string, string>;\n /**\n * Maps arg paths to variable registry keys for fill_form per-field substitution.\n * At replay time, these are resolved fresh from the variable bag.\n * e.g. { \"fields.0.value\": \"company\", \"fields.1.value\": \"display_name\" }\n */\n variableBindings?: Record<string, string>;\n /** For decomposed fill_form fields: the field index within the original fill_form */\n decomposedFieldIndex?: number;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cache Formation Diagnostics\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type CacheFormationStepMatchStrategy =\n | 'actionRef'\n | 'heuristic'\n | 'agent'\n | 'gap'\n | 'missing';\n\nexport type CacheFormationPlaybookStepDiagnostic = {\n order: number;\n action: string;\n target: string;\n actionRefCount: number;\n resolvedActionRefCount: number;\n matchStrategy: CacheFormationStepMatchStrategy;\n matchedActionIndex?: number;\n};\n\nexport type CacheFormationPlaybookDiagnostics = {\n stepCount: number;\n stepsWithActionRefs: number;\n stepsWithoutActionRefs: number;\n stepsWithResolvedActionRefs: number;\n stepsWithUnresolvedActionRefs: number;\n stepsMatchedByActionRef: number;\n stepsMatchedHeuristically: number;\n gapCount: number;\n steps: CacheFormationPlaybookStepDiagnostic[];\n};\n\nexport type CacheFormationActionDiagnostic = {\n index: number;\n actionType: string;\n elementDescription?: string;\n hasSelectors: boolean;\n hasPlaywrightLocator: boolean;\n selectorAuthor?: SelectorAuthor;\n selectorAgentFailureReason?: string;\n requiresAgent: boolean;\n selectorTaskCount: number;\n};\n\nexport type CacheFormationLocatorDiagnostics = {\n totalActions: number;\n selectorCapableActions: number;\n actionsWithSelectors: number;\n actionsWithPlaywrightLocator: number;\n agenticLocatorActions: number;\n deterministicLocatorActions: number;\n requiresAgentActions: number;\n actionsWithSelectorTasks: number;\n actions: CacheFormationActionDiagnostic[];\n};\n\nexport type CacheFormationSelectorAgentTaskSource = 'live-queue' | 'proactive';\n\nexport type CacheFormationSelectorAgentTaskDiagnostic = {\n actionIndex: number;\n fieldIndex?: number;\n source: CacheFormationSelectorAgentTaskSource;\n hasHeuristicSelectors: boolean;\n};\n\nexport type CacheFormationSelectorAgentStatus =\n | 'not-needed'\n | 'pending'\n | 'queued'\n | 'completed'\n | 'failed';\n\nexport type CacheFormationSelectorAgentDiagnostics = {\n liveQueueTaskCount: number;\n proactiveTaskCount: number;\n totalTaskCount: number;\n actionTaskCount: number;\n fieldTaskCount: number;\n bundleUploaded: boolean;\n drainJobEnqueued: boolean;\n status: CacheFormationSelectorAgentStatus;\n successCount?: number;\n failureCount?: number;\n failureReasons?: string[];\n tasks: CacheFormationSelectorAgentTaskDiagnostic[];\n};\n\nexport type CacheFormationDiagnostics = {\n playbook: CacheFormationPlaybookDiagnostics;\n locators: CacheFormationLocatorDiagnostics;\n selectorAgent: CacheFormationSelectorAgentDiagnostics;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cache Metadata Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Metadata about a workflow cache for eligibility and debugging\n */\nexport type WorkflowCacheMetadata = {\n /** Whether the inbox tool was used (disqualifies caching) */\n usedInboxTool: boolean;\n /** Total number of cached actions */\n actionCount: number;\n /** Node types that were executed */\n nodeTypes: string[];\n /** Whether all nodes completed successfully */\n allNodesSuccessful: boolean;\n /** Keys from inputValues used for dynamic data */\n inputValueKeys: string[];\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Eligibility Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Result of cache eligibility check\n */\nexport type CacheEligibilityResult = { eligible: true } | { eligible: false; reason: string };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Replay Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Per-action timing breakdown for cache replay telemetry.\n * Captures where time is spent within each replayed action.\n */\nexport interface ReplayActionTiming {\n /** Action index within the replay sequence */\n index: number;\n /** Type of action (click, type, navigate, etc.) */\n actionType: string;\n /** Time spent in page stabilization (DOM delay + network idle + overlay check) */\n stabilizationMs: number;\n /** Time spent in pre-action wait strategy */\n waitStrategyMs: number;\n /** Time spent resolving the element (selector fallback chain + visibility wait) */\n resolutionMs: number;\n /** Subset of resolutionMs spent in verifyElementReady visibility wait */\n visibilityWaitMs: number;\n /** Subset of visibilityWaitMs spent waiting for position to stabilize (animations) */\n stabilityWaitMs: number;\n /** Time spent in the Playwright action itself (click, fill, etc.) */\n actionMs: number;\n /** Total wall-clock time for this action */\n totalMs: number;\n /** Whether this action was retried */\n retried?: boolean;\n /** Which selector strategy resolved the element */\n selectorUsed?: string;\n /**\n * Locator author that resolved (or attempted to resolve) the element.\n * 'deterministic' for role/testId/label/text/css strategies; 'agentic' for\n * the selector-agent-authored Playwright locator. Powers the cache-telemetry\n * deterministic-vs-agentic step success breakdown.\n */\n selectorAuthor?: SelectorAuthor;\n /**\n * Whether this action completed successfully during replay. False for the\n * action that triggered a replay failure; undefined when not classified.\n */\n actionSucceeded?: boolean;\n /** Short reason when actionSucceeded === false (mirrors the step cacheOutcomeReason). */\n failureReason?: string;\n /** Time spent waiting for a disabled element to become enabled (pre-click guard) */\n enabledWaitMs?: number;\n /** Pre-click actionability diagnostics (present when click fell back to coordinates) */\n clickDiagnostics?: {\n visible: boolean;\n enabled: boolean;\n stable: boolean;\n receivesPointerEvents: boolean;\n coveringElement?: { tag: string; className: string; id: string };\n /** True when pointer-event interception was detected before calling locator.click() */\n proactiveCoordinateClick?: boolean;\n };\n /** Debug-mode snapshot of the click target element state at click time (CACHE_REPLAY_DEBUG=true) */\n clickTargetState?: {\n tagName: string;\n outerHTML: string;\n disabled: boolean | null;\n ariaDisabled: string | null;\n className: string;\n id: string;\n };\n}\n\n/** Result of an inline assertion evaluation during replay */\nexport interface InlineAssertionResult {\n assertionId: string;\n condition: string;\n severity: AssertionSeverity;\n passed: boolean;\n reason?: string;\n source: 'cache' | 'agent';\n /** The assertion mechanism type, when available from the AssertionSpec */\n assertionType?: AssertionType;\n /** L2 verification verdict. When 'uncertain', Phase 3 cascades to L3 for re-evaluation. */\n verdict?: 'passed' | 'failed' | 'uncertain';\n /**\n * True when this confirmed-failed verdict caused a cascade to a fresh agent\n * and is open to re-evaluation against the live DOM. Disputed results are\n * rendered in the agent prompt as \"re-evaluate and report\" instead of\n * \"do NOT re-evaluate\", and the fresh agent's verdict supersedes this one.\n *\n * EPHEMERAL prompt-composition hint: set only on the L2 assertion-eject\n * handoff path, consumed only by the prompt renderer, never persisted to\n * cache rows or step metadata. Do not gate runtime logic on it — the\n * runtime supersede keys off `source`, not this flag.\n */\n disputed?: boolean;\n /** Short human-readable description supplied by the agent at browser_assert time */\n label?: string;\n}\n\n/**\n * Normalized assertion result for non-blocking assertion accumulation.\n * Persisted to workflowRunSteps.metadataJson.assertionResults.\n */\nexport interface WorkflowAssertionResult {\n /** Assertion key assigned by the orchestrator (e.g., \"assert-0\", \"assert-1\") */\n key: string;\n /** Workflow node this assertion belongs to */\n nodeId: string;\n /** Workflow run step ID */\n stepId: string;\n /** Whether the assertion originated from a standalone assertion node or an inline assertion on an action node */\n origin: 'assertion_node' | 'inline_assertion';\n /** The human-readable assertion condition */\n condition: string;\n /** The assertion mechanism used */\n assertionType?: AssertionType | 'agentic';\n /** What a failure means: fatal halts the run, fail marks it failed but continues, warn logs only */\n severity: AssertionSeverity;\n /** Whether the assertion passed */\n passed: boolean;\n /**\n * False when the assertion was configured on the node but never evaluated\n * because the step failed before assertions ran. Omitted/true = evaluated.\n * Not-checked results are excluded from pass/warn/failure counts.\n */\n checked?: boolean;\n /** Human-readable reason for failure */\n reason?: string;\n /** How the assertion was evaluated */\n source: 'cache' | 'L2' | 'agent';\n /** ISO-8601 timestamp of evaluation */\n evaluatedAt: string;\n /** Source inline assertion ID for correlation back to node config */\n inlineAssertionId?: string;\n /** Short human-readable description supplied by the agent at browser_assert time */\n label?: string;\n /**\n * True when the agent judged a non-agentic-flagged inline assertion agentically,\n * because the condition is not programmatically assertable (e.g. vision-based\n * claim). Signal that the assertion should be re-authored as agentic; never set\n * by the browser/cache path.\n */\n downgradedFromDeterministic?: boolean;\n}\n\n/**\n * Summary of assertion results for a workflow run.\n * Persisted to workflowRuns.metadataJson.assertionSummary.\n * Derived from accumulated WorkflowAssertionResult records at finalization.\n */\nexport interface WorkflowAssertionSummary {\n total: number;\n passed: number;\n warnings: number;\n failures: number;\n /** Assertions configured on a failed step that were never evaluated (step broke before assertions ran). */\n notChecked?: number;\n /** True when any fail- or fatal-severity assertion failed (determines run failure) */\n hasFailSeverityFailures: boolean;\n /** Step ID of the first fail/fatal-severity failure (for UI navigation) */\n firstFailureStepId?: string;\n}\n\n/**\n * Result of cache replay execution\n */\nexport type ReplayResult =\n | {\n success: true;\n durationMs: number;\n actionTimings?: ReplayActionTiming[];\n /** Time spent in pre-replay page readiness wait (ms) */\n preReplayWaitMs?: number;\n /** Whether the networkidle wait was skipped (adaptive detection) */\n networkIdleSkipped?: boolean;\n /** Inline assertion results evaluated during replay (pass and fail) */\n assertionResults?: InlineAssertionResult[];\n /** Non-blocking capture failures for opportunistic captures */\n captureWarnings?: CacheCaptureWarning[];\n }\n | {\n success: false;\n failedAtIndex: number;\n error: string;\n /** Whether to fall back to agent execution */\n shouldFallback: boolean;\n /** Classification of the failure for cache invalidation decisions */\n failureCategory?: 'timing_hidden' | 'element_absent' | 'structural' | 'network' | 'other';\n /** Per-action timing for actions completed before failure */\n actionTimings?: ReplayActionTiming[];\n /** Time spent in pre-replay page readiness wait (ms) */\n preReplayWaitMs?: number;\n /** Whether the networkidle wait was skipped (adaptive detection) */\n networkIdleSkipped?: boolean;\n /** Inline assertion results collected before the failure */\n assertionResults?: InlineAssertionResult[];\n /** Capture warnings collected before the failure */\n captureWarnings?: CacheCaptureWarning[];\n };\n\n/**\n * Element info extracted from accessibility snapshot\n */\nexport type ElementInfo = {\n /** Ephemeral ref from snapshot (e.g., 'e5') */\n ref: string;\n /** ARIA role */\n role?: string;\n /** Accessible name */\n accessibleName?: string;\n /** Text content */\n textContent?: string;\n /** Text segments (individual visible text nodes) */\n textSegments?: string[];\n /** Aria label attribute */\n ariaLabel?: string;\n /** Label text (from <label>, aria-labelledby, or aria-label) */\n labelText?: string;\n /** Placeholder text (inputs) */\n placeholder?: string;\n /** Title attribute */\n title?: string;\n /** Alt text (images) */\n altText?: string;\n /** Form field name attribute */\n nameAttr?: string;\n /** data-testid or similar */\n dataTestId?: string;\n /** HTML tag name */\n tagName?: string;\n /** CSS class name */\n className?: string;\n /** Element ID */\n id?: string;\n /** Parent element role for context */\n parentRole?: string;\n /** Parent element name for context */\n parentName?: string;\n /** True when parentName was extracted from a child heading, not the parent's own accessible name */\n parentNameFromHeading?: boolean;\n /** Center X coordinate (from enriched snapshot) */\n centerX?: number;\n /** Center Y coordinate (from enriched snapshot) */\n centerY?: number;\n /** Frame context if element is inside an iframe or shadow DOM */\n frameContext?: FrameContext;\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Data attributes for disambiguation\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** data-item-index or data-selection-index for list items */\n itemIndex?: number;\n /** aria-rowindex for grid/table rows */\n ariaRowIndex?: number;\n /** data-automationid for Microsoft controls */\n automationId?: string;\n /** Generic data-* attributes for disambiguation */\n dataAttributes?: Record<string, string>;\n\n /** Child img alt text for disambiguation of generic containers */\n childImgAlt?: string;\n};\n","/**\n * Shared zod schemas for workflow node config primitives.\n *\n * Lives in @chatsdet/types so both `apps/api` (server-side validation +\n * tool schemas) and `packages/canary-cli` (the spec compiler) can import\n * the same source of truth without canary-cli taking a dependency on apps/api.\n *\n * @module workflow-node-schemas\n */\n\nimport { z } from 'zod';\nimport { ValidationOperatorSchema } from './api-sequence.js';\n\nexport const FormFieldSchema = z.object({\n name: z\n .string()\n .describe('Unique field identifier (snake_case, e.g., \"search_query\", \"recipient_email\")'),\n label: z.string().describe('Human-friendly label (e.g., \"Search Query\", \"Recipient Email\")'),\n type: z\n .enum([\n 'text',\n 'email',\n 'password',\n 'number',\n 'textarea',\n 'select',\n 'checkbox',\n 'multiselect',\n 'file',\n ])\n .optional()\n .describe('Input type, defaults to \"text\"'),\n required: z.boolean().optional().describe('Whether this field is required'),\n value: z.string().describe('The value to use for this field'),\n options: z\n .array(z.object({ value: z.string(), label: z.string() }))\n .optional()\n .describe('If type is select/multiselect, provide allowed options here'),\n});\n\n/**\n * Assertion severity — what happens when the assertion fails:\n * - `fatal`: the test must end now. The run halts immediately at this step.\n * - `fail`: the test may continue. The step is marked failed and the run\n * finishes marked failed, but later steps still execute.\n * - `warn`: the test should continue. The failure is recorded as a warning\n * and the run still passes.\n */\nexport const AssertionSeverityEnum = z.enum(['warn', 'fail', 'fatal']);\n\nexport type AssertionSeverity = z.infer<typeof AssertionSeverityEnum>;\n\nexport const InlineAssertionSchema = z.object({\n condition: z.string().describe('What to verify after the action'),\n severity: AssertionSeverityEnum.describe(\n 'fatal = stop the test immediately on failure, fail = mark the run failed but keep going, warn = log a warning and continue (run still passes)'\n ),\n agentic: z\n .boolean()\n .optional()\n .describe('true if requires visual/complex evaluation that cannot be cached'),\n});\n\n/**\n * Subject of a deterministic condition predicate — the left-hand operand.\n *\n * - `variable`: a workflow variable referenced by its input name (resolved from\n * the run's variable registry as `$var.<name>` at evaluation time).\n * - `acting_as`: the credential identity currently logged in. Compared against a\n * chosen credential template id; resolved from `currentActingAsTemplateId` on\n * the run's workflow state.\n */\nexport const ConditionSubjectSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('variable'),\n name: z.string().describe('Input/variable name, e.g. \"count\" for $var.count'),\n }),\n z.object({\n kind: z.literal('acting_as'),\n }),\n]);\n\nexport type ConditionSubject = z.infer<typeof ConditionSubjectSchema>;\n\n/**\n * A single typed comparison that decides a condition node's branch\n * deterministically — no LLM agent. When a condition node has a predicate it is\n * evaluated in the orchestrator; without one it falls back to agentic evaluation\n * of `conditionDescription`. v1 is a single comparison; the shape leaves room to\n * grow into compound AND/OR later.\n */\nexport const ConditionPredicateSchema = z.object({\n subject: ConditionSubjectSchema,\n operator: ValidationOperatorSchema,\n /**\n * Right-hand operand. For a `variable` subject this is a literal (or `$var`\n * reference); for `acting_as` it is a credential template id. Omitted for the\n * unary operators `is_set` / `is_not_set`.\n */\n value: z.string().optional(),\n});\n\nexport type ConditionPredicate = z.infer<typeof ConditionPredicateSchema>;\n\nexport const NodeTypeEnum = z.enum([\n 'login',\n 'navigate',\n 'action',\n 'assertion',\n 'seed',\n 'setup',\n 'wait',\n 'condition',\n 'api_sequence',\n 'end',\n 'notification',\n 'audio',\n 'audio-assertion',\n 'sandbox_action',\n]);\n\nexport type NodeType = z.infer<typeof NodeTypeEnum>;\nexport type FormField = z.infer<typeof FormFieldSchema>;\nexport type InlineAssertion = z.infer<typeof InlineAssertionSchema>;\n","/**\n * API Sequence Types\n *\n * Types for configuring and executing API sequence nodes in workflows.\n * An API sequence node executes multiple API calls in order, with support\n * for template variables that reference previous step responses.\n */\n\nimport { z } from 'zod';\n\n/**\n * Comparison operators shared by API-sequence validations and deterministic\n * workflow condition predicates. Single source of truth — both the validation\n * schemas and the condition predicate schema derive from this enum.\n */\nexport const ValidationOperatorSchema = z.enum([\n 'equals',\n 'not_equals',\n 'gt',\n 'lt',\n 'gte',\n 'lte',\n 'contains',\n 'not_contains',\n 'is_set',\n 'is_not_set',\n]);\n\nexport type ValidationOperator = z.infer<typeof ValidationOperatorSchema>;\n\n/**\n * Pure operator evaluation shared by API-sequence validations and deterministic\n * workflow conditions. `actual` / `expected` are pre-resolved string values\n * (`undefined` means \"not set\"). Returns whether the comparison passed plus a\n * human-readable message. Numeric operators coerce both operands via `Number()`;\n * non-numeric input fails with a descriptive message rather than throwing.\n */\nexport function evaluateOperator(args: {\n actual: string | undefined;\n operator: ValidationOperator;\n expected?: string;\n /** Label for the left operand in messages (e.g. a ref or variable name). */\n label?: string;\n}): { passed: boolean; message: string } {\n const { actual, operator, expected } = args;\n const label = args.label ?? 'value';\n const isSet = actual !== undefined && actual !== null;\n\n switch (operator) {\n case 'is_set':\n return {\n passed: isSet,\n message: isSet ? `${label} is set (value: ${actual})` : `${label} is not set`,\n };\n case 'is_not_set':\n return {\n passed: !isSet,\n message: !isSet ? `${label} is not set` : `${label} is set (value: ${actual})`,\n };\n case 'equals':\n return {\n passed: actual === expected,\n message:\n actual === expected\n ? `${label} equals \"${expected}\"`\n : `Expected ${label} to equal \"${expected}\", got \"${actual}\"`,\n };\n case 'not_equals':\n return {\n passed: actual !== expected,\n message:\n actual !== expected\n ? `${label} does not equal \"${expected}\"`\n : `Expected ${label} to not equal \"${expected}\"`,\n };\n case 'contains': {\n const pass = isSet && expected !== undefined && expected !== null && actual.includes(expected);\n return {\n passed: pass,\n message: pass\n ? `${label} contains \"${expected}\"`\n : `Expected ${label} to contain \"${expected}\", got \"${actual}\"`,\n };\n }\n case 'not_contains': {\n const pass =\n !isSet || expected === undefined || expected === null || !actual.includes(expected);\n return {\n passed: pass,\n message: pass\n ? `${label} does not contain \"${expected}\"`\n : `Expected ${label} to not contain \"${expected}\", got \"${actual}\"`,\n };\n }\n case 'gt':\n case 'lt':\n case 'gte':\n case 'lte': {\n const numActual = Number(actual);\n const numExpected = Number(expected);\n if (isNaN(numActual) || isNaN(numExpected)) {\n return {\n passed: false,\n message: `Cannot compare non-numeric values: \"${actual}\" ${operator} \"${expected}\"`,\n };\n }\n const ops = {\n gt: (a: number, b: number) => a > b,\n lt: (a: number, b: number) => a < b,\n gte: (a: number, b: number) => a >= b,\n lte: (a: number, b: number) => a <= b,\n } as const;\n const passed = ops[operator](numActual, numExpected);\n return {\n passed,\n message: passed\n ? `${label} (${numActual}) ${operator} ${numExpected}`\n : `Expected ${label} (${numActual}) ${operator} ${numExpected}`,\n };\n }\n default:\n return { passed: false, message: `Unknown operator: ${operator as string}` };\n }\n}\n\nexport interface ApiSequenceValidation {\n /** Reference to a step value: \"stepId.path.to.value\" (e.g., \"step3.data.accountId\") */\n left: string;\n operator: ValidationOperator;\n /** Literal value or step reference. Not needed for is_set/is_not_set. */\n right?: string;\n description?: string;\n}\n\nexport interface ApiSequenceValidationResult {\n validation: ApiSequenceValidation;\n passed: boolean;\n actualValue?: string;\n expectedValue?: string;\n message?: string;\n}\n\n/**\n * A single step in an API sequence.\n */\nexport interface ApiSequenceStep {\n /**\n * Unique identifier for this step (used for variable references).\n * If not provided, defaults to `step{index}`.\n */\n stepId?: string;\n\n /**\n * Reference to an API request in the api_requests table.\n * Provides default method, URL pattern, and example request/response data.\n */\n apiRequestId: string;\n\n /**\n * Which credential to use for this request.\n * If null, the request is made without authentication.\n */\n credentialId: string | null;\n\n /**\n * Override the HTTP method (GET, POST, PUT, PATCH, DELETE, etc.).\n * If not specified, uses the method from the referenced API request.\n */\n method?: string;\n\n /**\n * URL template with optional variable placeholders.\n * Supports {{stepN.response.path.to.value}} syntax.\n * If not specified, backfilled from the API catalog at execution time.\n *\n * @example \"https://api.example.com/users/{{step1.response.data.userId}}\"\n */\n urlTemplate?: string;\n\n /**\n * Query parameters as key-value pairs. Values support template variables.\n * These are merged into the URL at execution time (appended to any existing query string).\n */\n queryParams?: Array<{ key: string; value: string }>;\n\n /**\n * Request body template (JSON string) with optional variable placeholders.\n * Supports {{stepN.response.path.to.value}} syntax.\n * If null/undefined, uses the sample from the API request for POST/PUT/PATCH,\n * or sends no body for GET/DELETE.\n */\n bodyTemplate?: string | null;\n\n /**\n * Request header overrides with optional variable placeholders.\n * These are merged with headers from the credential and API request sample.\n */\n headersTemplate?: Record<string, string> | null;\n\n /**\n * Name to store this step's response under for use in subsequent steps.\n * Defaults to `step{index}` if not specified.\n *\n * @example \"createUserResponse\"\n */\n responseVariableName?: string;\n\n /**\n * Expected HTTP status codes for validation.\n * If specified, the step fails if the response status is not in this list.\n * If not specified, any successful response (2xx) is accepted.\n *\n * @example [200, 201]\n */\n expectedStatusCodes?: number[];\n\n /**\n * Optional description for this step (for UI display).\n */\n description?: string;\n\n /**\n * Assertions to run after this step's HTTP request completes.\n * Each validation checks a value from step results against a condition.\n */\n validations?: ApiSequenceValidation[];\n}\n\n/**\n * A variable export declaration that extracts a value from a step's response\n * and registers it in the workflow's variable registry for downstream steps.\n */\nexport interface ApiSequenceExport {\n /** Variable name using resource_field convention (e.g., \"project_id\", \"project_name\", \"user_email\") */\n variableName: string;\n /** Which step's response to extract from */\n stepId: string;\n /** Dot-notation path in the JSON body (e.g., \"data.id\") */\n responsePath: string;\n /** Whether to extract from the request body or response body. Defaults to 'response'. */\n source?: 'request' | 'response';\n /** Description for UI display */\n description?: string;\n}\n\n/**\n * Configuration for an API sequence workflow node.\n */\nexport interface ApiSequenceConfig {\n /**\n * Ordered list of API calls to execute.\n */\n steps: ApiSequenceStep[];\n\n /**\n * Human-readable description of what this sequence does.\n */\n description?: string;\n\n /**\n * If true, stop execution on the first failed step.\n * If false, continue executing remaining steps even if one fails.\n * Defaults to true.\n */\n stopOnFailure?: boolean;\n\n /**\n * Variable exports to extract from step responses and register\n * in the workflow's variable registry for downstream nodes.\n */\n exports?: ApiSequenceExport[];\n}\n\n/**\n * Result of executing a single API sequence step.\n */\nexport interface ApiSequenceStepResult {\n stepId: string;\n success: boolean;\n statusCode?: number;\n statusText?: string;\n headers?: Record<string, string>;\n body?: string | null;\n durationMs?: number;\n error?: string;\n timestamp: string;\n /** The fully resolved URL that was actually requested */\n resolvedUrl?: string;\n /** The HTTP method used */\n method?: string;\n /** The request body sent (if any) */\n requestBody?: string | null;\n /** The request headers sent */\n requestHeaders?: Record<string, string>;\n /** Results of validation assertions run after this step */\n validationResults?: ApiSequenceValidationResult[];\n}\n\n/**\n * Verification result comparing declared seed outputs against actually exported variables.\n */\nexport interface SeedOutputVerification {\n /** Whether all declared seed outputs were satisfied by exports */\n allSatisfied: boolean;\n /** dataKeys that were found in exportedVariables */\n satisfied: string[];\n /** dataKeys that were declared but missing from exportedVariables */\n missing: string[];\n}\n\n/**\n * How an api_sequence_runs row was initiated. Drives the \"trigger\" chip in the\n * flow designer's Runs panel and lets us distinguish manual designer-button\n * runs from runs spawned by a workflow execution or test suite.\n */\nexport type ApiSequenceTriggeredVia = 'designer' | 'workflow' | 'seed' | 'test-suite';\n\n/**\n * Outbound HTTP header names attached to every request issued by an\n * api_sequence step (and to Playwright browser contexts when the run\n * originates from a workflow). Together they reconstruct a deep link to the\n * failing step:\n * `https://app.trycanary.ai/flows/{flowId}?runId={runId}`\n *\n * NOTE: these headers are sent to **third-party hosts** that the step targets.\n * Values are opaque UUIDs (workflows.id / workflow_runs.id) — no credentials,\n * no PII — but be aware they cross our perimeter intentionally so customers\n * can correlate canary traffic in their own server logs.\n */\nexport const CANARY_TRACE_HEADERS = {\n flowId: 'x-canary-flow-id',\n runId: 'x-canary-run-id',\n} as const;\n\n/**\n * Build the canary trace header pair from a run's identifiers. Returns an\n * empty object when no context is available so callers can spread freely.\n */\nexport function buildCanaryTraceHeaders(\n context: { flowId: string; runId: string } | undefined\n): Record<string, string> {\n if (!context) return {};\n return {\n [CANARY_TRACE_HEADERS.flowId]: context.flowId,\n [CANARY_TRACE_HEADERS.runId]: context.runId,\n };\n}\n\n/**\n * Result of executing an entire API sequence.\n */\nexport interface ApiSequenceResult {\n success: boolean;\n stepResults: ApiSequenceStepResult[];\n totalDurationMs: number;\n failedStepId?: string;\n /** Variables extracted from step responses via the exports config */\n exportedVariables?: Record<string, string>;\n /** Verification of declared seed outputs against actual exports (seed workflows only) */\n seedOutputVerification?: SeedOutputVerification;\n}\n","/**\n * Local Browser Protocol Types\n *\n * WebSocket message protocol for communication between the CLI (LocalBrowserHost)\n * and the cloud API (RemoteBrowserClient). Enables cloud agents to control a\n * browser running on the user's local machine.\n */\n\n// ============================================================================\n// Session Types\n// ============================================================================\n\nexport type LocalBrowserMode = 'playwright' | 'cdp';\n\nexport type LocalBrowserSessionStatus =\n | 'pending' // Session created, waiting for CLI to connect\n | 'connecting' // CLI is connecting\n | 'connected' // CLI connected, browser ready\n | 'running' // Agent is actively using browser\n | 'disconnected' // CLI disconnected\n | 'expired' // Session timed out\n | 'error'; // Error occurred\n\nexport type LocalBrowserSession = {\n id: string;\n orgId: string;\n createdBy: string | null;\n status: LocalBrowserSessionStatus;\n browserMode: LocalBrowserMode;\n instructions: string | null;\n startUrl: string | null;\n jobId: string | null;\n connectedAt: string | null;\n disconnectedAt: string | null;\n expiresAt: string | null;\n createdAt: string;\n updatedAt: string;\n};\n\n// ============================================================================\n// WebSocket Message Types\n// ============================================================================\n\n/**\n * Base message envelope for all WebSocket messages.\n */\ninterface BaseMessage {\n /** Unique message ID for request/response correlation */\n id: string;\n /** Unix timestamp in milliseconds */\n timestamp: number;\n}\n\n/**\n * Command sent from cloud API to CLI to execute a browser action.\n */\nexport interface BrowserCommand extends BaseMessage {\n type: 'command';\n /** Method name from IBrowserClient interface */\n method: string;\n /** Serialized method arguments */\n args: unknown[];\n /** Optional context ID for multi-context support. Omitted = default context. */\n contextId?: string;\n}\n\n/**\n * Response from CLI to cloud API with execution result.\n */\nexport interface BrowserResponse extends BaseMessage {\n type: 'response';\n /** ID of the original command */\n requestId: string;\n /** Whether the command executed successfully */\n success: boolean;\n /** Return value from the method (if success=true) */\n result?: unknown;\n /** Error message (if success=false) */\n error?: string;\n /** Error stack trace for debugging */\n stack?: string;\n /** Echoed context ID from the originating command */\n contextId?: string;\n}\n\n/**\n * Binary data message for large payloads (screenshots, traces).\n * Data is base64-encoded to work over JSON WebSocket.\n */\nexport interface BinaryDataMessage extends BaseMessage {\n type: 'binary';\n /** ID of the original command this data belongs to */\n requestId: string;\n /** Type of binary data */\n dataType: 'screenshot' | 'video' | 'trace';\n /** Base64-encoded binary data */\n data: string;\n /** MIME type of the data */\n mimeType: string;\n}\n\n/**\n * Heartbeat message for connection health monitoring.\n */\nexport interface HeartbeatMessage extends BaseMessage {\n type: 'heartbeat';\n /** Direction of the heartbeat */\n direction: 'ping' | 'pong';\n}\n\n/**\n * Session lifecycle event message.\n */\nexport interface SessionMessage extends BaseMessage {\n type: 'session';\n /** Type of session event */\n event:\n | 'connected' // CLI successfully connected\n | 'disconnected' // CLI disconnected\n | 'browser_ready' // Browser launched and ready\n | 'browser_closed' // Browser was closed\n | 'error'; // Session error occurred\n /** Browser mode being used */\n browserMode?: LocalBrowserMode;\n /** Error message if event='error' */\n error?: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Union of all WebSocket message types.\n */\nexport type LocalBrowserMessage =\n | BrowserCommand\n | BrowserResponse\n | BinaryDataMessage\n | HeartbeatMessage\n | SessionMessage;\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\nexport function isBrowserCommand(msg: unknown): msg is BrowserCommand {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BrowserCommand).type === 'command' &&\n typeof (msg as BrowserCommand).method === 'string'\n );\n}\n\nexport function isBrowserResponse(msg: unknown): msg is BrowserResponse {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BrowserResponse).type === 'response' &&\n typeof (msg as BrowserResponse).requestId === 'string'\n );\n}\n\nexport function isBinaryDataMessage(msg: unknown): msg is BinaryDataMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as BinaryDataMessage).type === 'binary' &&\n typeof (msg as BinaryDataMessage).data === 'string'\n );\n}\n\nexport function isHeartbeatMessage(msg: unknown): msg is HeartbeatMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as HeartbeatMessage).type === 'heartbeat'\n );\n}\n\nexport function isSessionMessage(msg: unknown): msg is SessionMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n (msg as SessionMessage).type === 'session'\n );\n}\n\n// ============================================================================\n// API Request/Response Types\n// ============================================================================\n\nexport type CreateLocalBrowserSessionRequest = {\n /** Browser mode: 'playwright' for fresh browser, 'cdp' for existing Chrome */\n browserMode?: LocalBrowserMode;\n /** Instructions for the cloud agent */\n instructions?: string | null;\n /** Initial URL to navigate to */\n startUrl?: string | null;\n};\n\nexport type CreateLocalBrowserSessionResponse = {\n /** Session ID */\n sessionId: string;\n /** WebSocket token for CLI connection */\n wsToken: string;\n /** WebSocket URL for CLI to connect to */\n wsUrl: string;\n /** Session expiration time */\n expiresAt: string;\n};\n\nexport type LocalBrowserSessionDetail = {\n session: LocalBrowserSession;\n createdBy: { id: string; name: string | null; email: string | null } | null;\n};\n\nexport type ListLocalBrowserSessionsResponse = {\n items: LocalBrowserSessionDetail[];\n pagination: {\n page: number;\n pageSize: number;\n totalItems: number;\n totalPages: number;\n };\n};\n\n// ============================================================================\n// Job Types\n// ============================================================================\n\nexport const LOCAL_BROWSER_JOB_TYPE = 'local-browser.run' as const;\n\nexport type LocalBrowserJobPayload = {\n sessionId: string;\n instructions: string;\n startUrl?: string | null;\n};\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default timeout for browser commands in milliseconds */\nexport const BROWSER_COMMAND_TIMEOUT_MS = 60_000;\n\n/** Timeout for navigation commands in milliseconds */\nexport const BROWSER_NAVIGATION_TIMEOUT_MS = 120_000;\n\n/** Timeout for screenshot commands in milliseconds */\nexport const BROWSER_SCREENSHOT_TIMEOUT_MS = 30_000;\n\n/** Session expiration time in milliseconds (1 hour) */\nexport const SESSION_EXPIRATION_MS = 60 * 60 * 1000;\n\n/** Heartbeat interval in milliseconds */\nexport const HEARTBEAT_INTERVAL_MS = 30_000;\n","/**\n * Benchmark System Types\n *\n * Shared TypeScript types for the benchmark system. Used by:\n * - `@chatsdet/benchmark` package (expectations)\n * - `@chatsdet/db` (repository types)\n * - `apps/api/src/benchmark` (service, routes, scoring)\n * - `apps/web/src/ui/pages/superadmin` (frontend UI)\n *\n * @module @chatsdet/types/benchmark\n * @see docs/api/services/benchmark.md\n */\n\n// Job types that can be benchmarked\nexport type BenchmarkJobType = 'workflow_suite';\n\n// Run status\nexport type BenchmarkRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'timeout';\n\n// Trigger type\nexport type BenchmarkTriggerType = 'scheduled' | 'manual';\n\n// ---------------------------------------------------------------------------\n// Workflow Suite Benchmark Types\n// ---------------------------------------------------------------------------\n\nexport type BenchmarkWorkflowRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';\n\n/**\n * Configuration for workflow suite benchmarks (stored in jobConfig jsonb)\n */\nexport interface BenchmarkWorkflowSuiteConfig {\n runsPerWorkflow?: number;\n runTimeoutMinutes?: number;\n suiteTimeoutHours?: number;\n sendSlackSummary?: boolean;\n}\n\nimport { z } from 'zod';\n\nexport const benchmarkWorkflowSuiteConfigSchema = z.object({\n runsPerWorkflow: z.number().int().min(1).max(20).optional(),\n runTimeoutMinutes: z.number().int().min(1).max(720).optional(),\n suiteTimeoutHours: z.number().int().min(1).max(24).optional(),\n sendSlackSummary: z.boolean().optional(),\n});\n\n/**\n * Per-node metrics captured during a benchmark workflow run\n */\nexport interface BenchmarkNodeMetrics {\n nodeId: string;\n nodeName: string;\n nodeType: string;\n durationMs: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n status: string;\n cacheOutcome: 'hit' | 'miss' | 'fallback' | 'skipped' | null;\n cacheHitSource: 'replay' | 'recovery' | 'l2_gap' | 'wait_shortcut' | null;\n cacheOutcomeReason: string | null;\n compilationScore?: number | null;\n gapCount?: number | null;\n repairApplied?: boolean | null;\n maxInputTokens: number | null;\n contextWindowTokens: number | null;\n}\n\n/**\n * Aggregated metrics for a single benchmark workflow run\n */\nexport interface BenchmarkWorkflowRunMetrics {\n id: string;\n benchmarkRunId: string;\n sourceWorkflowId: string;\n copyWorkflowId: string;\n workflowName: string;\n runNumber: number;\n workflowRunId: string | null;\n status: BenchmarkWorkflowRunStatus;\n isCached: boolean;\n durationMs: number | null;\n totalTokens: number | null;\n inputTokens: number | null;\n outputTokens: number | null;\n totalNodes: number | null;\n successfulNodes: number | null;\n failedNodes: number | null;\n nodeMetrics: BenchmarkNodeMetrics[] | null;\n errorMessage: string | null;\n startedAt: string | null;\n completedAt: string | null;\n createdAt: string;\n}\n\n/**\n * Result of importing a workflow into the benchmark org\n */\nexport interface BenchmarkImportResult {\n workflowId: string;\n workflowName: string;\n propertyId: string;\n propertyName: string;\n credentialMappings: Array<{\n sourceCredentialId: string;\n newCredentialId: string;\n credentialName: string;\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// Environment Snapshot (captured at suite run time for comparison)\n// ---------------------------------------------------------------------------\n\n/**\n * Structured snapshot of the full execution environment captured at benchmark run time.\n * Enables multi-dimensional comparison between runs to attribute result changes.\n */\nexport interface EnvironmentSnapshot {\n /** Workflow-relevant prompt versions and content hashes */\n prompts: Record<string, { version: number; contentHash: string }>;\n /** Resolved AI provider/model configuration */\n modelConfig: {\n baseProvider: string;\n defaultModel: string;\n contextWindowTokens: number;\n providerOverride: string | null;\n modelOverride: string | null;\n };\n /** Per-workflow flow graph metadata (keyed by source workflow ID) */\n workflows: Record<\n string,\n {\n name: string;\n snapshotVersion: number;\n snapshotHash: string;\n nodeCount: number;\n edgeCount: number;\n }\n >;\n /** Benchmark config metadata at run time */\n benchmarkConfig: {\n version: number;\n provider: string | null;\n model: string | null;\n promptVersion: string | null;\n jobConfig: Record<string, unknown>;\n };\n}\n\nexport interface BenchmarkWorkflowTrendPoint {\n date: string;\n benchmarkRunId: string;\n configId: string;\n workflowName: string;\n runNumber: number;\n isCached: boolean;\n status: BenchmarkWorkflowRunStatus;\n triggerType: BenchmarkTriggerType;\n durationMs: number | null;\n totalTokens: number | null;\n successfulNodes: number | null;\n failedNodes: number | null;\n totalNodes: number | null;\n cachedSteps: number | null;\n l2RecoverySteps: number | null;\n ejectedSteps: number | null;\n agentSteps: number | null;\n skippedSteps: number | null;\n}\n","/**\n * Sandbox Task & Checklist Catalog\n *\n * Code-defined task/checklist definitions shared between frontend and backend.\n * The legacy SANDBOX_TASKS is preserved for backward compat.\n * The new checklist system uses profile-driven, DAG-ordered items.\n *\n * @module types/sandbox-tasks\n */\n\n// ---------------------------------------------------------------------------\n// Artifact types\n// ---------------------------------------------------------------------------\n\n/** Artifact types with special platform behavior (template sync, file path mapping). */\nexport type WellKnownArtifactType =\n | 'docker-compose'\n | 'dockerfile'\n | 'startup-script'\n | 'setup-script'\n | 'seed-script'\n | 'environment-config';\n\n/** Any string is a valid artifact type. Well-known types get special treatment. */\nexport type SandboxArtifactType = WellKnownArtifactType | (string & {});\n\n// ---------------------------------------------------------------------------\n// Legacy task types (backward compat)\n// ---------------------------------------------------------------------------\n\nexport interface SandboxTaskDefinition {\n id: string;\n label: string;\n description: string;\n icon: string;\n producesArtifacts: SandboxArtifactType[];\n order: number;\n /** Prompt template ID in apps/api/src/ai/prompts/ */\n promptTemplate: string;\n}\n\nexport interface SandboxTaskStatus {\n status: 'pending' | 'running' | 'completed' | 'failed';\n startedAt?: string;\n completedAt?: string;\n summary?: string;\n error?: string;\n}\n\n/** A task definition merged with its runtime status and artifact info */\nexport interface SandboxTaskWithStatus extends SandboxTaskDefinition {\n taskStatus: SandboxTaskStatus;\n artifacts: Array<{\n artifactType: string;\n filename: string;\n }>;\n}\n\n/** Task SSE events are defined in conversation-stream.ts */\nimport type { TaskArtifactPublishedEvent, TaskCompletedEvent } from './conversation-stream.js';\n\nexport type TaskStreamEvent =\n | TaskArtifactPublishedEvent\n | TaskCompletedEvent;\n\nexport const SANDBOX_TASKS: SandboxTaskDefinition[] = [\n {\n id: 'setup-environment',\n label: 'Setup Environment',\n description: 'Create Docker Compose, Dockerfile, and startup script to run the app',\n icon: 'GearSix',\n producesArtifacts: ['docker-compose', 'dockerfile', 'startup-script', 'seed-script'],\n order: 1,\n promptTemplate: 'env-template-setup-environment',\n },\n];\n\n// ---------------------------------------------------------------------------\n// Checklist system — profile-driven, DAG-ordered setup items\n// ---------------------------------------------------------------------------\n\nexport type ChecklistItemId =\n | 'provisioned'\n | 'code-cloned'\n | 'environment-analyzed'\n | 'setup-script-created'\n | 'dockerfile-created'\n | 'docker-compose-created'\n | 'server-running'\n | 'server-accessible'\n | 'adhoc-test-ready'\n | 'seed-script-created'\n | 'unit-tests-runnable'\n | 'integration-tests-runnable'\n | 'template-verified';\n\nexport interface ChecklistItemDefinition {\n id: ChecklistItemId;\n label: string;\n description: string;\n dependsOn: ChecklistItemId[];\n producesArtifacts: SandboxArtifactType[];\n agentAutomatable: boolean;\n /** Whether this item must be completed/skipped before the template can flip to verified. */\n countsTowardVerification?: boolean;\n /** Prompt template ID in apps/api/src/ai/prompts/ (only for agent-automatable items) */\n promptTemplate?: string;\n}\n\nexport type ChecklistProfile = 'minimal' | 'ci-ready' | 'full-stack';\n\nexport interface ChecklistProfileDefinition {\n id: ChecklistProfile;\n label: string;\n description: string;\n items: ChecklistItemId[];\n}\n\nexport interface ChecklistItemStatus {\n status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';\n startedAt?: string;\n completedAt?: string;\n summary?: string;\n error?: string;\n}\n\n/** A checklist item definition merged with its runtime status and artifact info */\nexport interface ChecklistItemWithStatus extends ChecklistItemDefinition {\n itemStatus: ChecklistItemStatus;\n /** True when all deps are met and item is not yet completed/running/skipped */\n actionable: boolean;\n artifacts: Array<{ artifactType: string; filename: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// Checklist item definitions (DAG)\n// ---------------------------------------------------------------------------\n\nexport const CHECKLIST_ITEMS: ChecklistItemDefinition[] = [\n {\n id: 'provisioned',\n label: 'Provisioned',\n description: 'Sandbox instance has been provisioned',\n dependsOn: [],\n producesArtifacts: [],\n agentAutomatable: false,\n },\n {\n id: 'code-cloned',\n label: 'Code Cloned',\n description: 'Repository has been cloned into the sandbox',\n dependsOn: ['provisioned'],\n producesArtifacts: [],\n agentAutomatable: false,\n },\n {\n id: 'environment-analyzed',\n label: 'Environment Analyzed',\n description: 'Repository analyzed for runtime, services, and dependencies',\n dependsOn: ['code-cloned'],\n producesArtifacts: ['environment-config'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-analyze-environment',\n },\n {\n id: 'setup-script-created',\n label: 'Setup Script Created',\n description: 'Environment setup script for installing dependencies and build prerequisites',\n dependsOn: ['environment-analyzed'],\n producesArtifacts: ['setup-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-setup-script',\n },\n {\n id: 'unit-tests-runnable',\n label: 'Unit Tests Runnable',\n description: 'Unit test suite can be executed successfully',\n dependsOn: ['setup-script-created'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-unit-tests',\n },\n {\n id: 'dockerfile-created',\n label: 'Dockerfile Created',\n description: 'Dockerfile generated from environment analysis',\n dependsOn: ['environment-analyzed'],\n producesArtifacts: ['dockerfile'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-dockerfile',\n },\n {\n id: 'docker-compose-created',\n label: 'Docker Compose Created',\n description: 'Docker Compose file with application and service containers',\n dependsOn: ['dockerfile-created'],\n producesArtifacts: ['docker-compose'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-docker-compose',\n },\n {\n id: 'integration-tests-runnable',\n label: 'Integration Tests Runnable',\n description: 'Integration tests can connect to services and run',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-integration-tests',\n },\n {\n id: 'seed-script-created',\n label: 'Seed Script Created',\n description: 'Database seed script for initial data population',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: ['seed-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-create-seed-script',\n },\n {\n id: 'server-running',\n label: 'Server Running',\n description: 'Application server started via startup script',\n dependsOn: ['docker-compose-created'],\n producesArtifacts: ['startup-script'],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-start-server',\n },\n {\n id: 'server-accessible',\n label: 'Server Accessible',\n description: 'Application server responds to health check requests',\n dependsOn: ['server-running'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-server',\n },\n {\n id: 'adhoc-test-ready',\n label: 'Ad Hoc Test Ready',\n description:\n 'Credential-producing sandbox action validated against a running instance for sandbox-backed ad hoc tests',\n dependsOn: ['server-accessible'],\n producesArtifacts: [],\n agentAutomatable: true,\n countsTowardVerification: false,\n promptTemplate: 'env-checklist-enable-adhoc-testing',\n },\n {\n id: 'template-verified',\n label: 'Template Verified',\n description: 'A fresh instance provisioned from this template comes up healthy',\n dependsOn: ['server-accessible'],\n producesArtifacts: [],\n agentAutomatable: true,\n promptTemplate: 'env-checklist-verify-template',\n },\n];\n\n/** O(1) lookup by checklist item ID */\nexport const CHECKLIST_ITEM_MAP: Record<ChecklistItemId, ChecklistItemDefinition> =\n Object.fromEntries(CHECKLIST_ITEMS.map((item) => [item.id, item])) as Record<\n ChecklistItemId,\n ChecklistItemDefinition\n >;\n\n// ---------------------------------------------------------------------------\n// Profile definitions\n// ---------------------------------------------------------------------------\n\nconst ALL_CHECKLIST_ITEM_IDS: ChecklistItemId[] = CHECKLIST_ITEMS.map((item) => item.id);\n\nexport const CHECKLIST_PROFILES: Record<ChecklistProfile, ChecklistProfileDefinition> = {\n minimal: {\n id: 'minimal',\n label: 'Minimal',\n description: 'Provisioned sandbox with code cloned — git access only',\n items: ['provisioned', 'code-cloned'],\n },\n 'ci-ready': {\n id: 'ci-ready',\n label: 'CI Ready',\n description: 'Code cloned with unit tests runnable — commit PRs and let CI handle the rest',\n items: ['provisioned', 'code-cloned', 'environment-analyzed', 'setup-script-created', 'unit-tests-runnable'],\n },\n 'full-stack': {\n id: 'full-stack',\n label: 'Full Stack',\n description: 'Complete containerized environment with all services, tests, and server running',\n items: ALL_CHECKLIST_ITEM_IDS,\n },\n};\n\nexport function getVerificationChecklistItemIds(profile: ChecklistProfile): ChecklistItemId[] {\n const profileItems = new Set(CHECKLIST_PROFILES[profile].items);\n return CHECKLIST_ITEMS.filter(\n (item) => profileItems.has(item.id) && item.countsTowardVerification !== false\n ).map((item) => item.id);\n}\n","/**\n * Macro system shared types and catalog.\n *\n * @module macros\n * @see apps/docs/src/content/docs/reference/macros.mdx\n *\n * Macros allow dynamic data generation in workflow test inputs.\n * Format: $MACRO_NAME(args) — resolved at execution time to fresh values.\n */\n\nexport interface MacroCatalogEntry {\n /** Macro name (uppercase, e.g. \"UUID\") */\n name: string;\n /** Human-readable description */\n description: string;\n /** Argument definitions (empty for no-arg macros) */\n args: Array<{\n name: string;\n type: 'number' | 'string';\n description: string;\n }>;\n /** Example resolved output */\n example: string;\n}\n\n/**\n * Full catalog of available macros — shared by backend resolver and frontend typeahead.\n */\nexport const MACRO_CATALOG: MacroCatalogEntry[] = [\n {\n name: 'UUID',\n description: 'Generate a random UUID v4',\n args: [],\n example: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',\n },\n {\n name: 'NOW',\n description: 'Current ISO 8601 timestamp',\n args: [],\n example: '2026-02-19T14:30:00.000Z',\n },\n {\n name: 'TODAY',\n description: 'Current date (YYYY-MM-DD)',\n args: [],\n example: '2026-02-19',\n },\n {\n name: 'DATE',\n description: 'Date offset from today by N days (negative for past, positive for future)',\n args: [{ name: 'days', type: 'number', description: 'Number of days offset (e.g. -7, +30)' }],\n example: '2026-02-26',\n },\n {\n name: 'TIMESTAMP',\n description: 'Current Unix timestamp in milliseconds',\n args: [],\n example: '1771520400000',\n },\n {\n name: 'RANDOM_INT',\n description: 'Random integer in range (inclusive)',\n args: [\n { name: 'min', type: 'number', description: 'Minimum value' },\n { name: 'max', type: 'number', description: 'Maximum value' },\n ],\n example: '42',\n },\n {\n name: 'RANDOM_STRING',\n description: 'Random alphanumeric string',\n args: [{ name: 'length', type: 'number', description: 'Character count' }],\n example: 'xK9mQ',\n },\n {\n name: 'EMAIL',\n description: 'Generate a unique test email address',\n args: [],\n example: 'test-a1b2@example-test.com',\n },\n {\n name: 'FIRST_NAME',\n description: 'Random first name',\n args: [],\n example: 'Jordan',\n },\n {\n name: 'LAST_NAME',\n description: 'Random last name',\n args: [],\n example: 'Chen',\n },\n {\n name: 'PHONE',\n description: 'Random US phone number',\n args: [],\n example: '+1-555-382-7491',\n },\n {\n name: 'SEQUENCE',\n description: 'Auto-incrementing counter (resets per run)',\n args: [{ name: 'name', type: 'string', description: 'Counter name' }],\n example: '1',\n },\n {\n name: 'AI',\n description: 'AI-generated string for the given prompt (resolved once per run, cached by prompt)',\n args: [{ name: 'prompt', type: 'string', description: 'Quoted description of what to generate, e.g. \"email subject\"' }],\n example: 'Quick question about your refund',\n },\n];\n\n/** Names of all valid macros, derived from MACRO_CATALOG. */\nexport const VALID_MACRO_NAMES = new Set(MACRO_CATALOG.map((m) => m.name));\n\n/**\n * Validate all macro expressions in a string. Returns array of invalid macro names.\n * Empty array = all macros valid (or no macros present).\n */\nexport function findInvalidMacros(value: string): string[] {\n const pattern = new RegExp(MACRO_PATTERN.source, MACRO_PATTERN.flags);\n const invalid: string[] = [];\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(value)) !== null) {\n const name = match[1]!;\n if (!VALID_MACRO_NAMES.has(name)) {\n invalid.push(name);\n }\n }\n return [...new Set(invalid)];\n}\n\n/** Expected arg count per macro, derived from MACRO_CATALOG. */\nconst MACRO_ARG_COUNTS = new Map(MACRO_CATALOG.map((m) => [m.name, m.args.length]));\n\n/**\n * Normalize macro expressions in a string by stripping extra arguments\n * beyond what the catalog defines. LLMs sometimes add extra hints\n * (e.g. \"$RANDOM_STRING(10, alphanumeric)\") that crash the resolver.\n */\nexport function normalizeMacroArgs(value: string): string {\n const pattern = new RegExp(MACRO_PATTERN.source, MACRO_PATTERN.flags);\n return value.replace(pattern, (fullMatch, name: string, argsStr: string) => {\n const expectedArgCount = MACRO_ARG_COUNTS.get(name);\n if (expectedArgCount === undefined) return fullMatch;\n const args = argsStr.length > 0 ? argsStr.split(',') : [];\n if (args.length <= expectedArgCount) return fullMatch;\n const trimmed = args.slice(0, expectedArgCount).join(',');\n return `$${name}(${trimmed})`;\n });\n}\n\n/** Regex for matching macro expressions. Negative lookbehind for \\ handles escaping. */\nexport const MACRO_PATTERN = /(?<!\\\\)\\$([A-Z_]+)\\(([^)]*)\\)/g;\n\n/** Regex for matching $var.{name} variable references (simple, dotted, and step-qualified). */\nexport const VARIABLE_PATTERN = /(?<!\\\\)\\$var\\.(\\w+(?:\\.\\w+)*)/g;\n\n/**\n * Regex for matching step-qualified variable references: `$var.StepN_Label.name`.\n * Group 1 = step qualifier (e.g. \"Step2_Create_Client\"), Group 2 = variable name.\n */\nexport const QUALIFIED_VARIABLE_PATTERN = /(?<!\\\\)\\$var\\.(\\w+)\\.(\\w+)/g;\n\n// ── Typeahead entry types (used by macro/variable dropdown) ──\n\nexport type VariableSource = 'inputValue' | 'macro' | 'extracted' | 'credential' | 'seed' | 'workflowInput' | 'unknown';\n\nexport interface VariableTypeaheadEntry {\n kind: 'variable';\n /** Variable name without $var. prefix (e.g. \"project_id\") */\n name: string;\n source: VariableSource;\n description: string;\n /** Step-qualified name for disambiguation (e.g. \"Step2_Create_Client.client\") */\n qualifiedName?: string;\n /** Human-readable step label (e.g. \"Step 2: Create Client\") */\n stepLabel?: string;\n /** Design-time template or expression (e.g. \"Acme Test - $RANDOM_STRING(6)\") */\n designTimeValue?: string;\n}\n\nexport interface MacroTypeaheadEntry extends MacroCatalogEntry {\n kind: 'macro';\n}\n\nexport type TypeaheadEntry = MacroTypeaheadEntry | VariableTypeaheadEntry;\n\n/** Map macro names to the variable keys they produce at runtime. */\nexport const MACRO_TO_VAR_KEY: Record<string, string> = {\n UUID: 'uuid',\n EMAIL: 'email',\n FIRST_NAME: 'first_name',\n LAST_NAME: 'last_name',\n PHONE: 'phone',\n NOW: 'now',\n TODAY: 'today',\n DATE: 'date',\n TIMESTAMP: 'timestamp',\n RANDOM_INT: 'random_int',\n RANDOM_STRING: 'random_string',\n SEQUENCE: 'sequence',\n AI: 'ai',\n};\n\n/**\n * Scan a string for all `$var.{name}` references, returning the variable names.\n * Also handles step-qualified references like `$var.Step2_Create_Client.name`,\n * extracting just the variable name for backward-compatible tracking.\n */\nexport function scanStringForVarRefs(text: string): string[] {\n const names: string[] = [];\n\n // First extract step-qualified references ($var.Step2_Label.name)\n const qualifiedPositions = new Set<number>();\n QUALIFIED_VARIABLE_PATTERN.lastIndex = 0;\n let qMatch: RegExpExecArray | null;\n while ((qMatch = QUALIFIED_VARIABLE_PATTERN.exec(text)) !== null) {\n if (/^Step\\d+/.test(qMatch[1]!)) {\n names.push(qMatch[2]!);\n qualifiedPositions.add(qMatch.index);\n }\n }\n\n // Then extract all references, skipping positions already matched as step-qualified\n VARIABLE_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = VARIABLE_PATTERN.exec(text)) !== null) {\n if (!qualifiedPositions.has(match.index)) {\n names.push(match[1]!);\n }\n }\n return names;\n}\n\n/** Scan a string for all `$MACRO()` expressions, returning the macro names. */\nexport function scanStringForMacros(text: string): string[] {\n const names: string[] = [];\n MACRO_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = MACRO_PATTERN.exec(text)) !== null) {\n names.push(match[1]!);\n }\n return names;\n}\n","/**\n * Browser viewport presets for agent browser sessions.\n *\n * Organizations pick a default preset (Settings → Agent Browser) and individual\n * runs may override it (Flow Designer → Run with config). The stored value is\n * the preset *key*; `resolveBrowserViewport` maps it to concrete dimensions\n * before reaching `PlaywrightClient.connect({ viewport })`.\n *\n * The `desktop` default (1280×800) matches `DEFAULT_VIEWPORT` in\n * `@chatsdet/browser-core` — the resolution recommended for AI computer-use\n * models.\n *\n * @module browser-viewport\n */\n\n/** Device family for a preset — drives the icon shown in the visual picker. */\nexport type BrowserViewportDevice = 'desktop' | 'laptop' | 'tablet' | 'mobile';\n\nexport interface BrowserViewportPresetSpec {\n /** Human-friendly label shown in the UI. */\n label: string;\n width: number;\n height: number;\n device: BrowserViewportDevice;\n}\n\nexport const BROWSER_VIEWPORT_PRESETS = {\n desktop: { label: 'Desktop', width: 1280, height: 800, device: 'desktop' },\n desktop_large: { label: 'Large desktop', width: 1920, height: 1080, device: 'desktop' },\n laptop: { label: 'Laptop', width: 1440, height: 900, device: 'laptop' },\n tablet: { label: 'Tablet', width: 768, height: 1024, device: 'tablet' },\n mobile: { label: 'Mobile', width: 390, height: 844, device: 'mobile' },\n} as const satisfies Record<string, BrowserViewportPresetSpec>;\n\nexport type BrowserViewportPreset = keyof typeof BROWSER_VIEWPORT_PRESETS;\n\nexport const DEFAULT_BROWSER_VIEWPORT_PRESET: BrowserViewportPreset = 'desktop';\n\nexport const BROWSER_VIEWPORT_PRESET_KEYS = Object.keys(\n BROWSER_VIEWPORT_PRESETS\n) as BrowserViewportPreset[];\n\n/** Type guard: is `value` a known preset key? */\nexport function isBrowserViewportPreset(value: unknown): value is BrowserViewportPreset {\n return typeof value === 'string' && value in BROWSER_VIEWPORT_PRESETS;\n}\n\n/**\n * Map a stored preset key (possibly null/unknown) to concrete viewport\n * dimensions, falling back to the desktop default.\n */\nexport function resolveBrowserViewport(preset: string | null | undefined): {\n width: number;\n height: number;\n} {\n const spec = isBrowserViewportPreset(preset)\n ? BROWSER_VIEWPORT_PRESETS[preset]\n : BROWSER_VIEWPORT_PRESETS[DEFAULT_BROWSER_VIEWPORT_PRESET];\n return { width: spec.width, height: spec.height };\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z"]}
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from "module"; const require = __cr(import.meta.url);
2
2
  import {
3
3
  isTriageEnabledForExecution
4
- } from "./chunk-ZXRVFFCY.js";
4
+ } from "./chunk-DT2CEH2V.js";
5
5
  import {
6
6
  resolveConfig
7
7
  } from "./chunk-IGHDIWZU.js";
@@ -395,4 +395,4 @@ async function runRemoteTest(argv) {
395
395
  export {
396
396
  runRemoteTest
397
397
  };
398
- //# sourceMappingURL=chunk-CJEKEW7R.js.map
398
+ //# sourceMappingURL=chunk-LFFWD5JZ.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
  PlaywrightClient
4
- } from "./chunk-5R4HMSNX.js";
4
+ } from "./chunk-AI3Y3AZH.js";
5
5
 
6
6
  // src/local-browser/host.ts
7
7
  var HEARTBEAT_INTERVAL_MS = 3e4;
@@ -410,4 +410,4 @@ export {
410
410
  LocalBrowserHost,
411
411
  USER_PAUSED_ERROR
412
412
  };
413
- //# sourceMappingURL=chunk-UZ2ZA5A6.js.map
413
+ //# sourceMappingURL=chunk-RGDIVW3O.js.map
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-JWHYXEA2.js";
13
13
  import {
14
14
  VALID_MACRO_NAMES
15
- } from "./chunk-IWKJQZJQ.js";
15
+ } from "./chunk-GZVCNYR4.js";
16
16
 
17
17
  // src/cli-helpers.ts
18
18
  import fs from "fs/promises";
@@ -27,6 +27,8 @@ function toLifecycleLabel(stage) {
27
27
  switch (stage) {
28
28
  case "baking":
29
29
  return "baking";
30
+ case "internal":
31
+ return "internal";
30
32
  case "deprecated":
31
33
  return "deprecated";
32
34
  case "ready_for_cleanup":
@@ -35,7 +37,13 @@ function toLifecycleLabel(stage) {
35
37
  return "active";
36
38
  }
37
39
  }
38
- function parseLifecycleStage(argv, allowedStages = ["active", "baking", "deprecated", "ready_for_cleanup"]) {
40
+ function parseLifecycleStage(argv, allowedStages = [
41
+ "active",
42
+ "baking",
43
+ "internal",
44
+ "deprecated",
45
+ "ready_for_cleanup"
46
+ ]) {
39
47
  const stage = getArgValue(argv, "--stage");
40
48
  if (!stage || !allowedStages.includes(stage)) {
41
49
  console.error(`Error: --stage is required and must be one of: ${allowedStages.join(", ")}`);
@@ -356,4 +364,4 @@ export {
356
364
  uploadStorageState,
357
365
  fetchList
358
366
  };
359
- //# sourceMappingURL=chunk-CFJXYPTF.js.map
367
+ //# sourceMappingURL=chunk-UAJUZLDE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getCanaryTmpDir } from '@chatsdet/tmp';\nimport { VALID_MACRO_NAMES } from '@chatsdet/types';\nimport { getArgValue } from './auth.js';\nimport { apiFetch, exitOnFailure, isTerminalFailure } from './api-fetch.js';\n\nexport type LifecycleStage =\n | 'active'\n | 'baking'\n | 'internal'\n | 'deprecated'\n | 'ready_for_cleanup';\n\n/** Stages knobs may use — `baking` (the oven) and `internal` are flag-only. */\nexport const KNOB_LIFECYCLE_STAGES: LifecycleStage[] = [\n 'active',\n 'deprecated',\n 'ready_for_cleanup',\n];\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case 'baking':\n return 'baking';\n case 'internal':\n return 'internal';\n case 'deprecated':\n return 'deprecated';\n case 'ready_for_cleanup':\n return 'ready_for_cleanup';\n default:\n return 'active';\n }\n}\n\nexport function parseLifecycleStage(\n argv: string[],\n allowedStages: LifecycleStage[] = [\n 'active',\n 'baking',\n 'internal',\n 'deprecated',\n 'ready_for_cleanup',\n ]\n): LifecycleStage {\n const stage = getArgValue(argv, '--stage');\n if (!stage || !allowedStages.includes(stage as LifecycleStage)) {\n console.error(`Error: --stage is required and must be one of: ${allowedStages.join(', ')}`);\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>,\n options?: { timeoutMs?: number }\n): Promise<T> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n ...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!result.ok) {\n const { failure } = result;\n // Terminal kinds (auth/proxy/TLS) surface the full diagnostic and exit;\n // http-error falls through so the caller can pretty-print structured\n // user errors (unresolved vars, invalid macros, etc.).\n if (isTerminalFailure(failure)) exitOnFailure(failure);\n const text = failure.body ?? '';\n let errorMessage = failure.message;\n let parsed: Record<string, unknown> | null = null;\n try {\n const data = JSON.parse(text);\n if (data && typeof data === 'object') {\n parsed = data as Record<string, unknown>;\n if (typeof parsed.error === 'string') errorMessage = parsed.error;\n }\n } catch {\n /* keep failure.message */\n }\n return { ...parsed, ok: false, error: errorMessage } as T;\n }\n\n try {\n return (await result.response.json()) as T;\n } catch {\n return { ok: false, error: `Invalid JSON response (${result.response.status})` } as T;\n }\n}\n\ntype UnresolvedRef = {\n nodeId?: string;\n nodeType?: string;\n field?: string;\n variable: string;\n};\n\n/**\n * Render a structured `userError` response from the workflow API into a\n * multi-line, actionable message. Recognizes `WORKFLOW_UNRESOLVED_VARIABLES`\n * and falls back to `✖ {code}: {message}` for other codes. Returns `null` if\n * the response body isn't a recognized structured error.\n */\nexport function renderWorkflowUserError(result: Record<string, unknown>): string | null {\n const code = typeof result.code === 'string' ? result.code : null;\n const message = typeof result.error === 'string' ? result.error : null;\n if (!code) return null;\n\n if (code === 'WORKFLOW_VALIDATION_FAILED') {\n type ValidationIssue = {\n nodeId?: string | null;\n nodeType?: string | null;\n field?: string;\n message?: string;\n severity?: 'error' | 'warning';\n };\n const issues = Array.isArray(result.issues) ? (result.issues as ValidationIssue[]) : [];\n const errors = issues.filter((i) => (i.severity ?? 'error') === 'error');\n const warnings = issues.filter((i) => i.severity === 'warning');\n\n const lines: string[] = [\n `✖ Validation failed (${errors.length} error${errors.length === 1 ? '' : 's'}` +\n (warnings.length > 0\n ? `, ${warnings.length} warning${warnings.length === 1 ? '' : 's'}`\n : '') +\n ')',\n ];\n if (message) lines.push(` ${message}`);\n\n // Group by nodeId (null = flow-level).\n const byNode = new Map<string, ValidationIssue[]>();\n for (const issue of issues) {\n const key = issue.nodeId ?? '__flow__';\n const arr = byNode.get(key) ?? [];\n arr.push(issue);\n byNode.set(key, arr);\n }\n for (const [nodeKey, nodeIssues] of byNode) {\n const heading =\n nodeKey === '__flow__'\n ? 'Flow'\n : `Node ${nodeKey}${nodeIssues[0]?.nodeType ? ` (${nodeIssues[0].nodeType})` : ''}`;\n lines.push(` ${heading}:`);\n for (const issue of nodeIssues) {\n const sev = issue.severity === 'warning' ? '⚠' : '•';\n const field = issue.field ? `${issue.field} — ` : '';\n lines.push(` ${sev} ${field}${issue.message ?? ''}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_UNRESOLVED_VARIABLES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const refs = Array.isArray(result.refs) ? (result.refs as UnresolvedRef[]) : [];\n const variables = Array.isArray(result.variables)\n ? (result.variables as string[])\n : refs.map((r) => r.variable);\n\n if (refs.length > 0) {\n lines.push(' Unresolved references:');\n for (const ref of refs) {\n const loc = ref.field && ref.nodeId ? ` (node ${ref.nodeId}, ${ref.field})` : '';\n lines.push(` • $var.${ref.variable}${loc}`);\n }\n } else if (variables.length > 0) {\n lines.push(' Unresolved:');\n for (const v of variables) lines.push(` • $var.${v}`);\n }\n\n const macroHints = variables.filter((v) => VALID_MACRO_NAMES.has(v.toUpperCase()));\n if (macroHints.length > 0) {\n lines.push(' Hint: these look like macro names, not variables:');\n for (const v of macroHints) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_INPUT_VALUES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const issues = Array.isArray(result.issues)\n ? (result.issues as Array<{\n key?: string;\n value?: string;\n reason?: string;\n variables?: string[];\n }>)\n : [];\n for (const issue of issues) {\n const key = issue.key ?? '?';\n const value = issue.value ?? '';\n if (issue.reason === 'self_referential') {\n lines.push(` • ${key}: self-referential → ${value}`);\n lines.push(` Use a literal or point at a different upstream variable.`);\n } else if (issue.reason === 'macro_name_used_as_var') {\n lines.push(` • ${key}: macro name used as $var → ${value}`);\n for (const v of issue.variables ?? []) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n } else {\n lines.push(` • ${key}: ${issue.reason ?? 'invalid'} → ${value}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_MACRO') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const invalid = Array.isArray(result.invalid) ? (result.invalid as string[]) : [];\n const valid = Array.isArray(result.valid) ? (result.valid as string[]) : [];\n if (invalid.length > 0) {\n lines.push(` Invalid macro(s): ${invalid.map((n) => `$${n}()`).join(', ')}`);\n }\n if (valid.length > 0) {\n lines.push(` Valid macros: ${valid.map((n) => `$${n}()`).join(', ')}`);\n }\n return lines.join('\\n');\n }\n\n return `✖ ${code}${message ? `: ${message}` : ''}`;\n}\n\nexport async function downloadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n prefix?: string;\n}): Promise<string | undefined> {\n const tmpFile = path.join(getCanaryTmpDir(), `${opts.prefix ?? 'canary-ss'}-${Date.now()}.json`);\n try {\n const res = await fetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/download`,\n {\n headers: { Authorization: `Bearer ${opts.token}` },\n redirect: 'follow',\n }\n );\n if (res.ok) {\n const body = await res.text();\n await fs.writeFile(tmpFile, body, 'utf-8');\n return tmpFile;\n }\n } catch {\n // Caller handles missing storage state\n }\n return undefined;\n}\n\n/* ── Credential selection ─────────────────────────────────────────────── */\n\nexport interface CredentialListItem {\n id: string;\n name: string;\n propertyId: string;\n propertyName?: string;\n propertyBaseUrl?: string;\n environmentId?: string | null;\n environmentName?: string | null;\n storageStateS3Key?: string | null;\n isDefault?: boolean;\n isEphemeral?: boolean;\n}\n\n/**\n * Fetch org credentials and let the user pick one interactively.\n *\n * @param credentialArg - Credential name or ID to auto-select, or undefined for interactive\n * @returns The selected credential, or null if none found / invalid selection\n */\nexport async function selectCredential(\n apiUrl: string,\n token: string,\n credentialArg?: string\n): Promise<CredentialListItem | null> {\n const credentials = await fetchList<CredentialListItem>(\n apiUrl,\n token,\n '/org/credentials',\n 'items'\n );\n\n if (credentials.length === 0) {\n console.error('No credentials found. Create one first in the web app.');\n return null;\n }\n\n if (credentialArg) {\n const match = credentials.find(\n (c) => c.id === credentialArg || c.name.toLowerCase() === credentialArg.toLowerCase()\n );\n if (!match) {\n console.error(`Credential \"${credentialArg}\" not found.`);\n console.error('Available credentials:');\n for (const c of credentials) {\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n console.error(` - ${c.name} [${c.propertyName ?? c.propertyId}${env}${baseUrl}]`);\n }\n return null;\n }\n return match;\n }\n\n // Interactive selection\n console.log('\\nSelect a credential:\\n');\n const labels = credentials.map((c) => {\n const property = c.propertyName ?? c.propertyId;\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n return `${c.name} [${property}${env}${baseUrl}]`;\n });\n\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n for (let i = 0; i < labels.length; i++) {\n console.log(` ${i + 1}. ${labels[i]}`);\n }\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= credentials.length) {\n console.error('Invalid selection.');\n return null;\n }\n\n return credentials[idx];\n}\n\n/* ── Property selection ──────────────────────────────────────────────── */\n\ninterface PropertyListItem {\n id: string;\n name: string;\n baseUrl: string;\n}\n\nexport async function fetchProperties(apiUrl: string, token: string): Promise<PropertyListItem[]> {\n return fetchList<PropertyListItem>(apiUrl, token, '/org/properties', 'items');\n}\n\n/**\n * Read the application/property selector flag. `--application` is the user-facing\n * name; `--property` is kept as a backwards-compat alias (read second). Internally\n * this is still a \"property\" — the rename is cosmetic.\n */\nexport function getApplicationArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application') ?? getArgValue(argv, '--property');\n}\n\n/**\n * Read the application/property UUID flag. `--application-id` is the user-facing\n * name; `--property-id` is kept as a backwards-compat alias.\n */\nexport function getApplicationIdArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application-id') ?? getArgValue(argv, '--property-id');\n}\n\nexport async function selectProperty(\n apiUrl: string,\n token: string,\n hint?: string\n): Promise<PropertyListItem | null> {\n const properties = await fetchProperties(apiUrl, token);\n\n if (properties.length === 0) {\n console.error('No applications found. Create one first in the web app.');\n return null;\n }\n\n if (hint) {\n const match = properties.find(\n (p) => p.id === hint || p.name.toLowerCase() === hint.toLowerCase()\n );\n if (!match) {\n console.error(`Application \"${hint}\" not found.`);\n console.error('Available applications:');\n for (const p of properties) {\n console.error(` - ${p.name} (${p.baseUrl})`);\n }\n return null;\n }\n return match;\n }\n\n if (properties.length === 1) {\n console.log(`Auto-selected application: ${properties[0].name}`);\n return properties[0];\n }\n\n return promptChoice(\n 'Select an application:',\n properties.map((p) => ({ label: `${p.name} — ${p.baseUrl}`, value: p }))\n );\n}\n\n/* ── Interactive prompt helpers ──────────────────────────────────────── */\n\nexport async function promptInput(question: string, defaultValue?: string): Promise<string> {\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;\n const answer = await new Promise<string>((resolve) => {\n rl.question(prompt, (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n return answer || defaultValue || '';\n}\n\nexport async function promptChoice<T>(\n question: string,\n options: Array<{ label: string; value: T }>\n): Promise<T | null> {\n console.log(`\\n${question}\\n`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}. ${options[i].label}`);\n }\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n console.error('Invalid selection.');\n return null;\n }\n return options[idx].value;\n}\n\nexport async function uploadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n storageState: unknown;\n}): Promise<{ ok: boolean; s3Key?: string; error?: string }> {\n const result = await apiFetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/upload`,\n {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${opts.token}`,\n 'Content-Type': 'application/json',\n },\n token: opts.token,\n body: JSON.stringify({ storageState: opts.storageState }),\n }\n );\n\n if (!result.ok) exitOnFailure(result.failure);\n\n return (await result.response.json()) as { ok: boolean; s3Key?: string; error?: string };\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n\n if (!result.ok) exitOnFailure(result.failure);\n\n const json = (await result.response.json()) as Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":"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","names":["path"]}
@@ -3,7 +3,7 @@ import {
3
3
  apiRequest,
4
4
  fetchProperties,
5
5
  getApplicationArg
6
- } from "./chunk-CFJXYPTF.js";
6
+ } from "./chunk-UAJUZLDE.js";
7
7
  import {
8
8
  apiFetch,
9
9
  exitOnFailure
@@ -15,7 +15,7 @@ import {
15
15
  } from "./chunk-IGHDIWZU.js";
16
16
  import "./chunk-32P26BZT.js";
17
17
  import "./chunk-JWHYXEA2.js";
18
- import "./chunk-IWKJQZJQ.js";
18
+ import "./chunk-GZVCNYR4.js";
19
19
  import "./chunk-VKVL7WBN.js";
20
20
 
21
21
  // src/credential.ts
@@ -451,4 +451,4 @@ async function runCredential(argv) {
451
451
  export {
452
452
  runCredential
453
453
  };
454
- //# sourceMappingURL=credential-JGVSEGVH.js.map
454
+ //# sourceMappingURL=credential-X6QKX473.js.map