deepline 0.3.88 → 0.3.90

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 (280) hide show
  1. package/contracts/install-commands.json +29 -0
  2. package/dist/bundling-sources/sdk/src/client.ts +652 -39
  3. package/dist/bundling-sources/sdk/src/config.ts +77 -0
  4. package/dist/bundling-sources/sdk/src/http.ts +76 -30
  5. package/dist/bundling-sources/sdk/src/index.ts +10 -0
  6. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
  7. package/dist/bundling-sources/sdk/src/pagination.ts +25 -0
  8. package/dist/bundling-sources/sdk/src/play.ts +15 -1
  9. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +3 -3
  10. package/dist/bundling-sources/sdk/src/release.ts +16 -9
  11. package/dist/bundling-sources/sdk/src/types.ts +106 -4
  12. package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
  13. package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
  14. package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
  15. package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
  16. package/dist/bundling-sources/shared_libs/monitors/analytics-public.ts +10 -0
  17. package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
  18. package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
  19. package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
  20. package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
  21. package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
  22. package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
  23. package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
  24. package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
  25. package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
  26. package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
  27. package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
  28. package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
  29. package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
  30. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +270 -0
  31. package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
  32. package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
  33. package/dist/bundling-sources/shared_libs/monitors/inventory-reconciliation.ts +59 -0
  34. package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +471 -0
  35. package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
  36. package/dist/bundling-sources/shared_libs/monitors/package.json +36 -0
  37. package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
  38. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +305 -0
  39. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
  40. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
  41. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
  42. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +535 -0
  43. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
  44. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
  45. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
  46. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
  47. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
  48. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
  49. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
  50. package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
  51. package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
  52. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-dead-letter.ts +245 -0
  53. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-message.ts +38 -0
  54. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-ack-probe.ts +94 -0
  55. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue-delivery.ts +60 -0
  56. package/dist/bundling-sources/shared_libs/monitors/signal-radars/inbound-queue.ts +60 -0
  57. package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
  58. package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
  59. package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
  60. package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
  61. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
  62. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
  63. package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
  64. package/dist/bundling-sources/shared_libs/observability/build-context.ts +13 -0
  65. package/dist/bundling-sources/shared_libs/observability/log-metadata.ts +160 -0
  66. package/dist/bundling-sources/shared_libs/observability/request-context.ts +68 -0
  67. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +654 -0
  68. package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +35 -0
  69. package/dist/bundling-sources/shared_libs/observability/scheduled-work.ts +5 -0
  70. package/dist/bundling-sources/shared_libs/play-data-plane/neon-constants.ts +9 -0
  71. package/dist/bundling-sources/shared_libs/play-data-plane/r2.ts +272 -0
  72. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
  73. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +135 -12
  74. package/dist/bundling-sources/shared_libs/play-runtime/async-operation.ts +66 -1
  75. package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +77 -0
  76. package/dist/bundling-sources/shared_libs/play-runtime/bounceban-batching.ts +137 -0
  77. package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +1159 -166
  79. package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
  80. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +103 -0
  81. package/dist/bundling-sources/shared_libs/play-runtime/daytona-payload-contract.ts +68 -0
  82. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-janitor.ts +103 -0
  83. package/dist/bundling-sources/shared_libs/play-runtime/daytona-sandbox-reaper.ts +725 -0
  84. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
  85. package/dist/bundling-sources/shared_libs/play-runtime/durable-call-cache.ts +29 -0
  86. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +29 -0
  87. package/dist/bundling-sources/shared_libs/play-runtime/execution-gateway-invocation-fence.ts +442 -0
  88. package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
  89. package/dist/bundling-sources/shared_libs/play-runtime/fixture-behavior.ts +18 -0
  90. package/dist/bundling-sources/shared_libs/play-runtime/governor/app-runtime-rate-state-backend.ts +9 -0
  91. package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +1 -1
  92. package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +1 -1
  93. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/contracts.ts +66 -0
  94. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/job.ts +145 -0
  95. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/ports.ts +21 -0
  96. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/postgres-store.ts +170 -0
  97. package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +1322 -0
  98. package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +2 -2
  99. package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
  100. package/dist/bundling-sources/shared_libs/play-runtime/launch-source-policy.ts +112 -0
  101. package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +8 -0
  102. package/dist/bundling-sources/shared_libs/play-runtime/perf-trace.ts +127 -0
  103. package/dist/bundling-sources/shared_libs/play-runtime/play-artifact-runtime-contract.ts +5 -0
  104. package/dist/bundling-sources/shared_libs/play-runtime/play-run-recovery-policy.ts +24 -1
  105. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/contracts.ts +56 -0
  106. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/index.ts +13 -0
  107. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/job.ts +41 -0
  108. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/ports.ts +17 -0
  109. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/admission/postgres-adapter.ts +54 -0
  110. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/contracts.ts +114 -0
  111. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/engine-suspension.ts +108 -0
  112. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/index.ts +36 -0
  113. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/job.ts +97 -0
  114. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/ports.ts +104 -0
  115. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/postgres-fence-adapter.ts +263 -0
  116. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/attempts/worker-terminal-projection.ts +64 -0
  117. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/contracts.ts +29 -0
  118. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/gateway-adapter.ts +56 -0
  119. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/index.ts +15 -0
  120. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/job.ts +65 -0
  121. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/projection/ports.ts +17 -0
  122. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/contracts.ts +42 -0
  123. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/index.ts +17 -0
  124. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/job.ts +142 -0
  125. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/ports.ts +42 -0
  126. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/sandbox-cleanup/postgres-adapter.ts +102 -0
  127. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/contracts.ts +60 -0
  128. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/index.ts +16 -0
  129. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/job.ts +78 -0
  130. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/ports.ts +54 -0
  131. package/dist/bundling-sources/shared_libs/play-runtime/play-runs/terminal/postgres-adapter.ts +71 -0
  132. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-batching-registry.ts +4 -0
  133. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
  134. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +4 -0
  135. package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +5 -8
  136. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -2
  137. package/dist/bundling-sources/shared_libs/play-runtime/run-identity.ts +168 -0
  138. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
  139. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +90 -13
  140. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-payload-transport.ts +6 -12
  141. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
  142. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +21 -0
  143. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/bundle.ts +0 -1
  144. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +14 -43
  145. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +67 -213
  146. package/dist/bundling-sources/shared_libs/play-runtime/runtime-authority.ts +201 -0
  147. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
  148. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
  149. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +12 -0
  150. package/dist/bundling-sources/shared_libs/play-runtime/runtime-operation-contract.ts +67 -0
  151. package/dist/bundling-sources/shared_libs/play-runtime/runtime-release-environment.ts +2 -0
  152. package/dist/bundling-sources/shared_libs/play-runtime/runtime-sandbox-contract.ts +68 -0
  153. package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +38 -2
  154. package/dist/bundling-sources/shared_libs/play-runtime/runtime-telemetry.ts +108 -0
  155. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/contract.ts +4 -0
  156. package/dist/bundling-sources/shared_libs/play-runtime/sandbox-runs/runner-backend-adapter.ts +3 -0
  157. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +37 -1
  158. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-deadline-reconciliation.ts +88 -0
  159. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-deploy-task.ts +124 -0
  160. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-play-run-task.ts +68 -0
  161. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-pool.ts +193 -0
  162. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-lifecycle.ts +269 -0
  163. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-queue-suffix.ts +73 -0
  164. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-runtime-worker.ts +6642 -0
  165. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-shared.ts +897 -0
  166. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-metrics.ts +120 -0
  167. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd-worker-required-env.ts +76 -0
  168. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/absurd.ts +476 -0
  169. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-heavy-request-admission.ts +47 -0
  170. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/gateway-progress-registry.ts +147 -0
  171. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/index.ts +27 -0
  172. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/lazy.ts +50 -0
  173. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/play-run-recovery.ts +98 -0
  174. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-budget-state.ts +260 -0
  175. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +1416 -0
  176. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-progress.ts +1255 -0
  177. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-rate-state.ts +942 -0
  178. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +14636 -0
  179. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +888 -0
  180. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-ledger-outbox-recovery.ts +355 -0
  181. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/run-terminal-transition.ts +46 -0
  182. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runner-push.ts +60 -0
  183. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +33 -0
  184. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-receipt-gateway-health.ts +64 -0
  185. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-capacity.ts +835 -0
  186. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-ledger-reconciliation.ts +1218 -0
  187. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-sandbox-operation.ts +207 -0
  188. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-scheduler-schema.ts +10 -0
  189. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-capacity.ts +123 -0
  190. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/scheduler-launch-read-observability.ts +78 -0
  191. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/submit-admission.ts +169 -0
  192. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/terminal-contract.ts +20 -0
  193. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-queue-metrics.ts +34 -0
  194. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/worker-resource-metrics.ts +249 -0
  195. package/dist/bundling-sources/shared_libs/play-runtime/sheet-attempt-sql.ts +7 -0
  196. package/dist/bundling-sources/shared_libs/play-runtime/step-progress.ts +296 -0
  197. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/native-batch.ts +3 -3
  198. package/dist/bundling-sources/shared_libs/play-runtime/tool-call/runtime-step-receipts-adapter.ts +20 -1
  199. package/dist/bundling-sources/shared_libs/play-runtime/tool-execute-retry-policy.ts +60 -15
  200. package/dist/bundling-sources/shared_libs/play-runtime/tool-execution-outcome.ts +20 -0
  201. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
  202. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
  203. package/dist/bundling-sources/shared_libs/play-runtime/transient-service-error.ts +12 -0
  204. package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +14 -1
  205. package/dist/bundling-sources/shared_libs/play-runtime/work-receipt-storage-contract.ts +165 -0
  206. package/dist/bundling-sources/shared_libs/play-runtime/work-receipts.ts +15 -23
  207. package/dist/bundling-sources/shared_libs/play-runtime/workflow-executor-auth.ts +169 -0
  208. package/dist/bundling-sources/shared_libs/play-runtime/zerobounce-batching.ts +122 -0
  209. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +8 -3
  210. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +454 -17
  211. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
  212. package/dist/bundling-sources/shared_libs/plays/contracts.ts +8 -14
  213. package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
  214. package/dist/bundling-sources/shared_libs/plays/deployment-status.ts +1 -1
  215. package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
  216. package/dist/bundling-sources/shared_libs/plays/docflow.ts +1 -1
  217. package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
  218. package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +39 -0
  219. package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
  220. package/dist/bundling-sources/shared_libs/plays/share/copy.ts +271 -0
  221. package/dist/bundling-sources/shared_libs/plays/share/metrics.ts +140 -0
  222. package/dist/bundling-sources/shared_libs/plays/share/public-read-model.ts +86 -0
  223. package/dist/bundling-sources/shared_libs/plays/share/seo-quality.ts +181 -0
  224. package/dist/bundling-sources/shared_libs/plays/share/static-pipeline-summary.ts +658 -0
  225. package/dist/bundling-sources/shared_libs/plays/share/types.ts +197 -0
  226. package/dist/bundling-sources/shared_libs/plays/slack-hitl-timeout-error.ts +37 -0
  227. package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
  228. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +50 -15
  229. package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
  230. package/dist/bundling-sources/shared_libs/plays/tool-result-types.ts +1 -1
  231. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
  232. package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
  233. package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +566 -0
  234. package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
  235. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +60 -0
  236. package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
  237. package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
  238. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
  239. package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
  240. package/dist/bundling-sources/shared_libs/product-notifications/overflow.ts +18 -0
  241. package/dist/bundling-sources/shared_libs/product-notifications/rolling-limit.ts +24 -0
  242. package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +624 -0
  243. package/dist/bundling-sources/shared_libs/product-notifications/slack-payload.ts +140 -0
  244. package/dist/bundling-sources/shared_libs/product-notifications/slack-transport.ts +75 -0
  245. package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
  246. package/dist/bundling-sources/shared_libs/runtime-env.ts +12 -0
  247. package/dist/bundling-sources/shared_libs/security/email-blacklist.ts +42 -0
  248. package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +35 -0
  249. package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
  250. package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
  251. package/dist/bundling-sources/shared_libs/security/slack-sanitize.ts +133 -0
  252. package/dist/cli/getting-started.d.mts +52 -0
  253. package/dist/cli/getting-started.d.ts +52 -0
  254. package/dist/cli/getting-started.js +65 -0
  255. package/dist/cli/getting-started.mjs +36 -0
  256. package/dist/cli/index.d.mts +9 -0
  257. package/dist/cli/index.d.ts +9 -0
  258. package/dist/cli/index.js +1611 -453
  259. package/dist/cli/index.mjs +1596 -447
  260. package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-BaJfE-5H.d.mts} +136 -10
  261. package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-BaJfE-5H.d.ts} +136 -10
  262. package/dist/index.d.mts +910 -155
  263. package/dist/index.d.ts +910 -155
  264. package/dist/index.js +550 -64
  265. package/dist/index.mjs +550 -64
  266. package/dist/install-integrity.json +193 -4
  267. package/dist/monitor-fleet-contract.d.mts +138 -0
  268. package/dist/monitor-fleet-contract.d.ts +138 -0
  269. package/dist/monitor-fleet-contract.js +404 -0
  270. package/dist/monitor-fleet-contract.mjs +367 -0
  271. package/dist/plays/bundle-play-file.d.mts +8 -3
  272. package/dist/plays/bundle-play-file.d.ts +8 -3
  273. package/dist/plays/bundle-play-file.mjs +100 -20
  274. package/dist/release.d.mts +255 -0
  275. package/dist/release.d.ts +255 -0
  276. package/dist/release.js +215 -0
  277. package/dist/release.mjs +188 -0
  278. package/package.json +23 -4
  279. /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
  280. /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
@@ -163,6 +163,26 @@ import { join as join24 } from "path";
163
163
  import { tmpdir as tmpdir6 } from "os";
164
164
  import { Command as Command5 } from "commander";
165
165
 
166
+ // src/pagination.ts
167
+ async function* iterateCursorPages(fetchPage) {
168
+ let cursor = null;
169
+ const seen = /* @__PURE__ */ new Set();
170
+ for (; ; ) {
171
+ const page = await fetchPage(cursor);
172
+ if (page.hasMore) {
173
+ if (!page.cursor || seen.has(page.cursor)) {
174
+ throw new Error(
175
+ "Pagination reported more results without an advancing cursor"
176
+ );
177
+ }
178
+ seen.add(page.cursor);
179
+ }
180
+ yield page.items;
181
+ if (!page.hasMore) return;
182
+ cursor = page.cursor;
183
+ }
184
+ }
185
+
166
186
  // src/config.ts
167
187
  import {
168
188
  existsSync,
@@ -572,6 +592,7 @@ var HOST_URL_ENV = "DEEPLINE_HOST_URL";
572
592
  var API_KEY_ENV = "DEEPLINE_API_KEY";
573
593
  var ACTIVE_ORG_ID_ENV = "DEEPLINE_ACTIVE_ORG_ID";
574
594
  var ACTIVE_ORG_NAME_ENV = "DEEPLINE_ACTIVE_ORG_NAME";
595
+ var FEATURES_ENV = "DEEPLINE_FEATURES";
575
596
  var PROD_URL = "https://code.deepline.com";
576
597
  var DEFAULT_TIMEOUT = 6e4;
577
598
  var DEFAULT_MAX_RETRIES = 3;
@@ -596,8 +617,40 @@ var CLI_ENV_ALLOWED_KEYS = /* @__PURE__ */ new Set([
596
617
  HOST_URL_ENV,
597
618
  API_KEY_ENV,
598
619
  ACTIVE_ORG_ID_ENV,
599
- ACTIVE_ORG_NAME_ENV
620
+ ACTIVE_ORG_NAME_ENV,
621
+ FEATURES_ENV
600
622
  ]);
623
+ function featureEnvValues(data) {
624
+ if (!Array.isArray(data.features)) return {};
625
+ return {
626
+ [FEATURES_ENV]: data.features.filter(
627
+ (key) => typeof key === "string" && key.trim() !== ""
628
+ ).map((key) => key.trim()).join(",")
629
+ };
630
+ }
631
+ function storedHostApiKey(baseUrl) {
632
+ return loadCliEnv(normalizeBaseUrl(baseUrl) || baseUrl)[API_KEY_ENV] ?? "";
633
+ }
634
+ function storedHostFeatures(baseUrl) {
635
+ return loadCliEnv(normalizeBaseUrl(baseUrl) || baseUrl)[FEATURES_ENV];
636
+ }
637
+ function envHasFeature(env, feature) {
638
+ return (env[FEATURES_ENV] ?? "").split(",").map((key) => key.trim()).includes(feature);
639
+ }
640
+ function cliFeatureEnabled(feature) {
641
+ const processEnv = process.env;
642
+ if (envHasFeature(processEnv, feature)) return true;
643
+ if (processEnv[API_KEY_ENV]?.trim()) return false;
644
+ try {
645
+ const baseUrl = resolveBaseUrlOnly();
646
+ const projectEnv = loadProjectEnvCandidates().find(
647
+ ({ env }) => normalizeBaseUrl(env[HOST_URL_ENV] ?? "") === baseUrl && Boolean(env[API_KEY_ENV])
648
+ );
649
+ return envHasFeature(projectEnv?.env ?? loadCliEnv(baseUrl), feature);
650
+ } catch {
651
+ return false;
652
+ }
653
+ }
601
654
  function baseUrlSlug(baseUrl) {
602
655
  let url;
603
656
  try {
@@ -797,6 +850,10 @@ function mergePersistedEnvValues(existing, values) {
797
850
  if (nextApiKey !== void 0 && nextApiKey !== existing[API_KEY_ENV] && values[ACTIVE_ORG_ID_ENV] === void 0) {
798
851
  delete merged[ACTIVE_ORG_ID_ENV];
799
852
  delete merged[ACTIVE_ORG_NAME_ENV];
853
+ delete merged[FEATURES_ENV];
854
+ }
855
+ if (values[FEATURES_ENV] === "") {
856
+ delete merged[FEATURES_ENV];
800
857
  }
801
858
  return merged;
802
859
  }
@@ -920,7 +977,10 @@ function resolveConfig(options) {
920
977
  apiKey,
921
978
  baseUrl,
922
979
  timeout: options?.timeout ?? DEFAULT_TIMEOUT,
923
- maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
980
+ maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
981
+ // Pass through untouched: the transport owns case-insensitive merging so
982
+ // there is exactly one place that decides which header wins.
983
+ ...options?.headers ? { headers: { ...options.headers } } : {}
924
984
  };
925
985
  }
926
986
  function mergeProjectEnvFile(filePath, values) {
@@ -1210,20 +1270,27 @@ var SDK_RELEASE = {
1210
1270
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1211
1271
  // getters keep their established compatibility behavior.
1212
1272
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
1213
- version: "0.3.88",
1214
- updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
1273
+ // 0.3.90 is the first deliberately versioned SDK release for API v3. Play
1274
+ // grep hard-cuts to the single Convex full-text index; it does not retain a
1275
+ // registry/scanner reader for any SDK/API major.
1276
+ version: "0.3.90",
1277
+ updateSummary: "Play grep now uses one source-only Convex full-text index. It no longer scans the registry, pages through the complete indexed match set, and rejects retired grep modes. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
1215
1278
  packageCapabilities: {
1216
1279
  updatePreferences: 1
1217
1280
  },
1218
1281
  contracts: {
1219
1282
  api: {
1220
1283
  name: "sdk-http-api",
1221
- // API v2 is append-only. A breaking public request/response change needs
1222
- // v3 and an explicit v2 support-window decision, never a patch release.
1223
- currentMajor: 2,
1224
- supportedMajors: [2],
1225
- // Accepted only to bridge SDKs released before X-Deepline-API-Major.
1284
+ // API v3 hard-cuts Play grep to the canonical Convex index. API v2 stays
1285
+ // admitted for the explicit SDK support window, but it selects neither a
1286
+ // registry scanner nor another grep reader: every accepted grep request
1287
+ // uses the same indexed query and retired flags fail at the API boundary.
1288
+ currentMajor: 3,
1289
+ supportedMajors: [3, 2],
1290
+ // The first wire id is emitted by the current SDK. Remaining ids are
1291
+ // accepted only while the API v2 support window is open.
1226
1292
  legacyWireIds: [
1293
+ "2026-09-play-grep-convex-index-hard-cutover",
1227
1294
  "2026-07-admin-cli-local-cutover",
1228
1295
  "2026-07-native-monitor-launch-hard-cutover"
1229
1296
  ]
@@ -1764,11 +1831,33 @@ function normalizeRequestAbortError(error, input2) {
1764
1831
  }
1765
1832
  return normalized;
1766
1833
  }
1834
+ function applyExplicitHeaders(headers, explicit) {
1835
+ if (!explicit) return headers;
1836
+ for (const [rawName, value] of Object.entries(explicit)) {
1837
+ const name = rawName.trim().toLowerCase();
1838
+ if (!name) continue;
1839
+ for (const existing of Object.keys(headers)) {
1840
+ if (existing.toLowerCase() === name) delete headers[existing];
1841
+ }
1842
+ headers[name] = value;
1843
+ }
1844
+ return headers;
1845
+ }
1767
1846
  function isAbortLikeError(error) {
1768
1847
  const name = error.name.toLowerCase();
1769
1848
  const message = error.message.toLowerCase();
1770
1849
  return name === "aborterror" || message.includes("operation was aborted") || message === "aborted";
1771
1850
  }
1851
+ function withErrorCause(error, cause) {
1852
+ if (cause !== null && cause !== void 0) {
1853
+ Object.defineProperty(error, "cause", {
1854
+ value: cause,
1855
+ configurable: true,
1856
+ writable: true
1857
+ });
1858
+ }
1859
+ return error;
1860
+ }
1772
1861
  function mapNetworkError(baseUrl, error, targetLabel2) {
1773
1862
  const tagged = error;
1774
1863
  const timeoutMs = tagged?.[REQUEST_TIMEOUT_MARKER];
@@ -1885,7 +1974,7 @@ var HttpClient = class {
1885
1974
  if (coordinatorInternalToken?.trim() && (coordinatorUrl?.trim() || workerCallbackUrl?.trim() || runtimeTestFault?.trim() || absurdReleaseOverride?.trim() || preferredReceiptGatewayMachineId?.trim())) {
1886
1975
  headers[COORDINATOR_INTERNAL_TOKEN_HEADER] = coordinatorInternalToken.trim();
1887
1976
  }
1888
- return headers;
1977
+ return applyExplicitHeaders(headers, this.config.headers);
1889
1978
  }
1890
1979
  /**
1891
1980
  * Send an HTTP request with automatic retries and error handling.
@@ -2039,32 +2128,38 @@ var HttpClient = class {
2039
2128
  );
2040
2129
  if (options?.toolId) {
2041
2130
  const code = mappedNetworkError.code;
2042
- throw new ToolExecutionError(
2043
- withCoworkNetworkHint(mappedNetworkError.message),
2044
- {
2045
- toolId: options.toolId,
2046
- provider: providerLabelFromToolId(options.toolId),
2047
- operation: options.toolId,
2048
- code,
2049
- origin: "deepline",
2050
- category: "network",
2051
- // A direct client timeout has ambiguous delivery. Fail closed unless
2052
- // the server returns the authoritative action delivery contract.
2053
- retryable: false,
2054
- statusCode: null,
2055
- requestId: null,
2056
- retryAfterMs: null,
2057
- networkKind: code === "NETWORK_TIMEOUT" ? "timeout" : code === "NETWORK_ABORTED" ? "unknown" : "unavailable",
2058
- networkScope: "client_to_deepline",
2059
- details: mappedNetworkError.details
2060
- }
2131
+ throw withErrorCause(
2132
+ new ToolExecutionError(
2133
+ withCoworkNetworkHint(mappedNetworkError.message),
2134
+ {
2135
+ toolId: options.toolId,
2136
+ provider: providerLabelFromToolId(options.toolId),
2137
+ operation: options.toolId,
2138
+ code,
2139
+ origin: "deepline",
2140
+ category: "network",
2141
+ // A direct client timeout has ambiguous delivery. Fail closed unless
2142
+ // the server returns the authoritative action delivery contract.
2143
+ retryable: false,
2144
+ statusCode: null,
2145
+ requestId: null,
2146
+ retryAfterMs: null,
2147
+ networkKind: code === "NETWORK_TIMEOUT" ? "timeout" : code === "NETWORK_ABORTED" ? "unknown" : "unavailable",
2148
+ networkScope: "client_to_deepline",
2149
+ details: mappedNetworkError.details
2150
+ }
2151
+ ),
2152
+ lastError
2061
2153
  );
2062
2154
  }
2063
- throw new DeeplineError(
2064
- withCoworkNetworkHint(mappedNetworkError.message),
2065
- void 0,
2066
- mappedNetworkError.code,
2067
- mappedNetworkError.details
2155
+ throw withErrorCause(
2156
+ new DeeplineError(
2157
+ withCoworkNetworkHint(mappedNetworkError.message),
2158
+ void 0,
2159
+ mappedNetworkError.code,
2160
+ mappedNetworkError.details
2161
+ ),
2162
+ lastError
2068
2163
  );
2069
2164
  }
2070
2165
  /**
@@ -2438,6 +2533,17 @@ var PRODUCT_NOTIFICATION_DELIVERY_LEASE_MS = 2 * 6e4;
2438
2533
  var PRODUCT_NOTIFICATION_SUCCESS_TTL_MS = 15 * 6e4;
2439
2534
  var PRODUCT_NOTIFICATION_FAILURE_TTL_MS = 24 * 60 * 6e4;
2440
2535
 
2536
+ // ../product-notifications/configuration.ts
2537
+ var PRODUCT_NOTIFICATION_EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2538
+ function normalizeProductNotificationEmail(value) {
2539
+ const email = typeof value === "string" ? value.trim().toLowerCase() : "";
2540
+ return email && email.length <= 320 && PRODUCT_NOTIFICATION_EMAIL_PATTERN.test(email) ? email : null;
2541
+ }
2542
+ var DEFAULT_PRODUCT_NOTIFICATION_DELIVERY_TIMING = {
2543
+ mode: "rolling_limit",
2544
+ windowMs: 36e5
2545
+ };
2546
+
2441
2547
  // src/stream-reconnect.ts
2442
2548
  var STREAM_RECONNECT_BASE_DELAY_MS = 500;
2443
2549
  var STREAM_RECONNECT_MAX_DELAY_MS = 15e3;
@@ -2966,7 +3072,7 @@ function isRecord4(value) {
2966
3072
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
2967
3073
  }
2968
3074
  function isPlayActivityObservation(value) {
2969
- if (!isRecord4(value) || value.schemaVersion !== 1 || typeof value.activityId !== "string" || !value.activityId.trim() || typeof value.observedAt !== "number" || !Number.isFinite(value.observedAt) || !isRecord4(value.target) || !isRecord4(value.state)) {
3075
+ if (!isRecord4(value) || value.schemaVersion !== void 0 && value.schemaVersion !== 1 || typeof value.activityId !== "string" || !value.activityId.trim() || typeof value.observedAt !== "number" || !Number.isFinite(value.observedAt) || !isRecord4(value.target) || !isRecord4(value.state)) {
2970
3076
  return false;
2971
3077
  }
2972
3078
  const targetKinds = /* @__PURE__ */ new Set([
@@ -3073,7 +3179,6 @@ function projectPlayRunActivity(input2) {
3073
3179
  if (input2.waitKind === "detached_runner") {
3074
3180
  return projection(
3075
3181
  {
3076
- schemaVersion: 1,
3077
3182
  activityId: "runner:managed",
3078
3183
  target: {
3079
3184
  kind: "runner",
@@ -3089,7 +3194,6 @@ function projectPlayRunActivity(input2) {
3089
3194
  if (input2.waitKind === "sleep") {
3090
3195
  return projection(
3091
3196
  {
3092
- schemaVersion: 1,
3093
3197
  activityId: "run:sleep",
3094
3198
  target: {
3095
3199
  kind: "step",
@@ -3109,7 +3213,6 @@ function projectPlayRunActivity(input2) {
3109
3213
  if (input2.waitKind === "integration_event" || input2.waitKind === "integration_event_batch") {
3110
3214
  return projection(
3111
3215
  {
3112
- schemaVersion: 1,
3113
3216
  activityId: `event:${input2.activeNodeId ?? "run"}`,
3114
3217
  stepId: input2.activeNodeId ?? void 0,
3115
3218
  target: {
@@ -3133,7 +3236,6 @@ function projectPlayRunActivity(input2) {
3133
3236
  if (normalizedStatus === "waiting") {
3134
3237
  return projection(
3135
3238
  {
3136
- schemaVersion: 1,
3137
3239
  activityId: "run:legacy-wait",
3138
3240
  target: {
3139
3241
  kind: "step",
@@ -3160,7 +3262,6 @@ function projectPlayRunActivity(input2) {
3160
3262
  const datasetProgress = datasetStep?.progress ?? null;
3161
3263
  return projection(
3162
3264
  {
3163
- schemaVersion: 1,
3164
3265
  activityId: `dataset:${pendingDataset.datasetId ?? pendingDataset.tableNamespace}`,
3165
3266
  target: {
3166
3267
  kind: "dataset",
@@ -3188,7 +3289,6 @@ function projectPlayRunActivity(input2) {
3188
3289
  const provider = toolId?.split(/[._]/)[0]?.trim() || null;
3189
3290
  return projection(
3190
3291
  {
3191
- schemaVersion: 1,
3192
3292
  activityId: `step:${activeStep.nodeId}`,
3193
3293
  stepId: activeStep.nodeId,
3194
3294
  target: toolId && provider ? {
@@ -3213,7 +3313,6 @@ function projectPlayRunActivity(input2) {
3213
3313
  if (normalizedStatus === "queued") {
3214
3314
  return projection(
3215
3315
  {
3216
- schemaVersion: 1,
3217
3316
  activityId: "capacity:worker",
3218
3317
  target: { kind: "capacity", pool: "worker" },
3219
3318
  state: { kind: "queued", reason: "capacity" },
@@ -3223,7 +3322,6 @@ function projectPlayRunActivity(input2) {
3223
3322
  );
3224
3323
  }
3225
3324
  const fallback = {
3226
- schemaVersion: 1,
3227
3325
  activityId: "run:legacy",
3228
3326
  target: {
3229
3327
  kind: "step",
@@ -4338,7 +4436,7 @@ async function* observeRunEvents(options) {
4338
4436
  }
4339
4437
  }
4340
4438
 
4341
- // ../../shared_libs/integrations/theirstack-execution-policy.ts
4439
+ // ../integration-contracts/theirstack/execution-policy.ts
4342
4440
  var THEIRSTACK_SLOW_JOB_SEARCH_PROVIDER_TIMEOUT_MS = 135e3;
4343
4441
  var THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS = 6e4 + THEIRSTACK_SLOW_JOB_SEARCH_PROVIDER_TIMEOUT_MS + 15e3;
4344
4442
  var THEIRSTACK_COMPANY_SEARCH_CLIENT_TIMEOUT_MS = 21e4;
@@ -4392,7 +4490,7 @@ function resolveTheirstackClientTimeoutMs(endpointId, payload) {
4392
4490
  return usesExtendedTheirstackJobSearchBudget(endpointId, payload) ? THEIRSTACK_SLOW_JOB_SEARCH_CLIENT_TIMEOUT_MS : null;
4393
4491
  }
4394
4492
 
4395
- // ../../shared_libs/integrations/bettercontact-execution-policy.ts
4493
+ // ../integration-contracts/bettercontact/execution-policy.ts
4396
4494
  var BETTERCONTACT_LAUNCHER_CLIENT_TIMEOUT_MS = 12e4;
4397
4495
  var BETTERCONTACT_LAUNCHER_TOOL_IDS = /* @__PURE__ */ new Set([
4398
4496
  "bettercontact_enrich",
@@ -4483,6 +4581,42 @@ function providerMetaFromToolResponseRawV2(rawV2, view) {
4483
4581
  }
4484
4582
 
4485
4583
  // src/client.ts
4584
+ var MAX_NOTIFICATION_GROUP_CREATE_TARGETS = 50;
4585
+ function parseNotificationGroupTarget(value) {
4586
+ const normalized = value.trim();
4587
+ if (normalized.toLowerCase().startsWith("email:")) {
4588
+ const email = normalizeProductNotificationEmail(normalized.slice(6));
4589
+ if (!email) {
4590
+ throw new Error(
4591
+ `Invalid email notification target "${value}". Use --to email:name@example.com.`
4592
+ );
4593
+ }
4594
+ return { kind: "email", email };
4595
+ }
4596
+ const slack = /^slack:(.+)$/i.exec(normalized)?.[1]?.trim();
4597
+ if (!slack) {
4598
+ const provider = normalized.split(":", 1)[0] || normalized;
4599
+ throw new Error(
4600
+ `Unsupported notification destination "${provider}". Use email: or slack:.`
4601
+ );
4602
+ }
4603
+ const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(slack);
4604
+ if (member?.[1]) {
4605
+ return { kind: "slack", memberId: member[1].toUpperCase() };
4606
+ }
4607
+ if (slack.toLowerCase().startsWith("member:")) {
4608
+ throw new Error(
4609
+ "Slack member IDs must look like slack:member:U0123456789."
4610
+ );
4611
+ }
4612
+ if (!slack.replace(/^#/, "").trim()) {
4613
+ throw new Error("Use --to slack:#channel.");
4614
+ }
4615
+ return { kind: "slack", channel: slack };
4616
+ }
4617
+ function notificationObjectKey(kind) {
4618
+ return `${kind}.${globalThis.crypto.randomUUID()}`;
4619
+ }
4486
4620
  var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
4487
4621
  var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
4488
4622
  var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
@@ -5002,6 +5136,7 @@ var DeeplineClient = class {
5002
5136
  this.runs = {
5003
5137
  get: (runId, options2) => this.getRunStatus(runId, options2),
5004
5138
  list: (options2) => this.listRuns(options2),
5139
+ listPage: (options2) => this.listRunsPage(options2),
5005
5140
  tail: (runId, options2) => this.tailRun(runId, options2),
5006
5141
  logs: (runId, options2) => this.getRunLogs(runId, options2),
5007
5142
  input: (runId) => this.getRunInput(runId),
@@ -5052,6 +5187,10 @@ var DeeplineClient = class {
5052
5187
  audit: (options2) => this.auditMonitors(options2),
5053
5188
  repair: (options2) => this.repairMonitors(options2),
5054
5189
  health: (options2) => this.readMonitorsHealth(options2),
5190
+ batch: {
5191
+ submit: (options2) => this.submitMonitorBatch(options2),
5192
+ get: (operationId, options2) => this.getMonitorBatch(operationId, options2)
5193
+ },
5055
5194
  fleets: {
5056
5195
  sync: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
5057
5196
  get: (fleetId, options2) => fleetId === void 0 ? this.listMonitorFleets() : this.getMonitorFleet(fleetId, options2),
@@ -5184,6 +5323,16 @@ var DeeplineClient = class {
5184
5323
  (secret) => secret.name === normalized && secret.status === "active" && secret.hasValue
5185
5324
  ) ?? null;
5186
5325
  }
5326
+ /** Check effective scope and active-version health without reading the value.
5327
+ * Without playName, checks the organization-scoped secret only. With a Play
5328
+ * name, an active Play secret takes precedence; a deleted/disabled Play
5329
+ * secret falls back to the organization-scoped secret.
5330
+ */
5331
+ async inspectSecret(name, options = {}) {
5332
+ const params = new URLSearchParams({ name: name.trim().toUpperCase() });
5333
+ if (options.playName !== void 0) params.set("play", options.playName);
5334
+ return this.http.get(`/api/v2/secrets?${params.toString()}`);
5335
+ }
5187
5336
  // ——————————————————————————————————————————————————————————
5188
5337
  // Tools
5189
5338
  // ——————————————————————————————————————————————————————————
@@ -5275,6 +5424,10 @@ var DeeplineClient = class {
5275
5424
  `/api/v2/integrations/${encodeURIComponent(toolId)}/get`,
5276
5425
  {
5277
5426
  method: "GET",
5427
+ // Tool metadata is a read-only catalog lookup. Keep the metadata
5428
+ // header while opting into the same transient edge-response retries as
5429
+ // other GET catalog methods such as searchTools().
5430
+ retryApiErrors: true,
5278
5431
  headers: {
5279
5432
  "x-deepline-tool-meta-only": "1"
5280
5433
  }
@@ -6197,6 +6350,10 @@ var DeeplineClient = class {
6197
6350
  * ```
6198
6351
  */
6199
6352
  async listRuns(options) {
6353
+ return (await this.listRunsPage(options)).runs;
6354
+ }
6355
+ /** Read one run page without discarding server pagination metadata. */
6356
+ async listRunsPage(options) {
6200
6357
  const playName = options.play?.trim();
6201
6358
  const params = new URLSearchParams();
6202
6359
  if (playName) {
@@ -6227,7 +6384,7 @@ var DeeplineClient = class {
6227
6384
  const response = await this.http.get(
6228
6385
  `/api/v2/runs?${params.toString()}`
6229
6386
  );
6230
- return response.runs ?? [];
6387
+ return { ...response, runs: response.runs ?? [] };
6231
6388
  }
6232
6389
  /**
6233
6390
  * Observe one run's live events. Uses the Convex Run Snapshot subscription
@@ -6622,13 +6779,33 @@ var DeeplineClient = class {
6622
6779
  );
6623
6780
  }
6624
6781
  /**
6625
- * List callable plays visible to the workspace.
6782
+ * List one page of callable plays; grep retains exhaustive matching results.
6783
+ * Use listPlaysPage to retain pagination metadata.
6626
6784
  *
6627
6785
  * Pass `origin: "prebuilt"` for Deepline-managed prebuilts or
6628
6786
  * `origin: "owned"` for org-owned plays.
6629
6787
  */
6630
6788
  async listPlays(options) {
6789
+ if (!options?.grep?.trim()) {
6790
+ return (await this.requestPlaysPage(options, false)).plays;
6791
+ }
6792
+ const plays = [];
6793
+ for await (const items of iterateCursorPages(async (cursor) => {
6794
+ const page = await this.listPlaysPage({ ...options, cursor });
6795
+ return { items: page.plays, cursor: page.cursor, hasMore: page.hasMore };
6796
+ })) {
6797
+ plays.push(...items);
6798
+ }
6799
+ return plays;
6800
+ }
6801
+ /** Read one bounded inventory page; cursor continues the same filters. */
6802
+ async listPlaysPage(options) {
6803
+ return this.requestPlaysPage(options);
6804
+ }
6805
+ async requestPlaysPage(options, paginateInventory = true) {
6631
6806
  const params = new URLSearchParams();
6807
+ if (paginateInventory && !options?.grep?.trim())
6808
+ params.set("inventory_paginated", "1");
6632
6809
  if (options?.origin) params.set("origin", options.origin);
6633
6810
  if (options?.categories) {
6634
6811
  params.set(
@@ -6640,26 +6817,27 @@ var DeeplineClient = class {
6640
6817
  params.set("include_tool_categories", "1");
6641
6818
  }
6642
6819
  if (options?.includeArchived) params.set("include_archived", "1");
6820
+ if (options?.includeCostEstimates) {
6821
+ params.set("include_cost_estimates", "1");
6822
+ }
6643
6823
  if (options?.grep?.trim()) {
6644
6824
  params.set("grep", options.grep.trim());
6645
- params.set("grep_mode", options.grepMode ?? "all");
6646
- params.set("grep_paginated", "1");
6647
6825
  params.set("limit", "60");
6648
6826
  }
6649
- const plays = [];
6650
- let cursor = null;
6651
- let hasMore = true;
6652
- do {
6653
- if (cursor) params.set("cursor", cursor);
6654
- const suffix = params.toString() ? `?${params.toString()}` : "";
6655
- const response = await this.http.get(
6656
- `/api/v2/plays${suffix}`
6657
- );
6658
- plays.push(...response.plays ?? []);
6659
- cursor = response.pagination?.cursor ?? response.cursor ?? null;
6660
- hasMore = Boolean(response.pagination?.hasMore ?? response.hasMore);
6661
- } while (Boolean(options?.grep?.trim()) && hasMore && cursor);
6662
- return plays;
6827
+ if (options?.limit !== void 0) {
6828
+ if (!Number.isSafeInteger(options.limit) || options.limit < 1) {
6829
+ throw new Error("Play page limit must be a positive integer");
6830
+ }
6831
+ params.set("limit", String(options.limit));
6832
+ }
6833
+ if (options?.cursor) params.set("cursor", options.cursor);
6834
+ const suffix = params.size ? `?${params.toString()}` : "";
6835
+ const response = await this.http.get(`/api/v2/plays${suffix}`);
6836
+ return {
6837
+ plays: response.plays ?? [],
6838
+ cursor: response.pagination?.cursor ?? response.cursor ?? null,
6839
+ hasMore: Boolean(response.pagination?.hasMore ?? response.hasMore)
6840
+ };
6663
6841
  }
6664
6842
  /** Set whether an org-owned Play sorts before unpinned Plays. */
6665
6843
  async setPlayPinned(playName, pinned) {
@@ -6730,6 +6908,197 @@ var DeeplineClient = class {
6730
6908
  async getNotifications() {
6731
6909
  return this.http.get("/api/v2/notifications");
6732
6910
  }
6911
+ /** Read the group-based product-notification configuration and rule catalog. */
6912
+ async getNotificationConfiguration() {
6913
+ return this.http.get("/api/v2/notifications/configuration");
6914
+ }
6915
+ /** Atomically apply one retry-safe desired configuration change. */
6916
+ async changeNotificationConfiguration(expectedWorkspaceVersion, change) {
6917
+ return this.http.put("/api/v2/notifications/configuration", {
6918
+ expectedWorkspaceVersion,
6919
+ change
6920
+ });
6921
+ }
6922
+ /** Create one notification group and its destinations as a retry-safe unit. */
6923
+ async createNotificationGroup(input2) {
6924
+ const name = input2.name.trim().replace(/\s+/g, " ");
6925
+ if (!name) throw new Error("Notification group name is required.");
6926
+ if (input2.targets.length < 1 || input2.targets.length > MAX_NOTIFICATION_GROUP_CREATE_TARGETS) {
6927
+ throw new Error(
6928
+ `Choose between 1 and ${MAX_NOTIFICATION_GROUP_CREATE_TARGETS} notification targets.`
6929
+ );
6930
+ }
6931
+ const targets = input2.targets.map(parseNotificationGroupTarget);
6932
+ const legacyEvents = input2.legacyEvents ?? [];
6933
+ const legacyTarget = targets.length === 1 && targets[0]?.kind === "slack" && legacyEvents.length > 0 ? targets[0] : null;
6934
+ if (legacyEvents.length > 0 && !legacyTarget) {
6935
+ throw new Error(
6936
+ "--for is supported only by the legacy one-Slack-target form. Create the group first, then add rules in notification configuration."
6937
+ );
6938
+ }
6939
+ if (!legacyTarget && name.toLowerCase() === "default") {
6940
+ throw new Error(
6941
+ "Default is reserved for the workspace default notification group."
6942
+ );
6943
+ }
6944
+ const normalizedTargets = targets.map(
6945
+ (target) => target.kind === "email" ? `email:${target.email}` : "memberId" in target ? `slack:member:${target.memberId}` : `slack:${target.channel}`
6946
+ );
6947
+ if (input2.dryRun) {
6948
+ return {
6949
+ mode: "dry_run",
6950
+ compatibilityMode: legacyTarget !== null,
6951
+ name: legacyTarget ? input2.name : name,
6952
+ targets: normalizedTargets,
6953
+ legacyEvents
6954
+ };
6955
+ }
6956
+ if (legacyTarget) {
6957
+ const notification = await this.createNotification({
6958
+ name: input2.name,
6959
+ provider: "slack",
6960
+ ..."channel" in legacyTarget ? { channel: legacyTarget.channel } : { memberId: legacyTarget.memberId },
6961
+ eventTypes: legacyEvents
6962
+ });
6963
+ return { mode: "legacy", notification };
6964
+ }
6965
+ const current = await this.getNotificationConfiguration();
6966
+ const existingGroup = current.configuration.groups.find(
6967
+ (candidate) => candidate.name.toLowerCase() === name.toLowerCase()
6968
+ );
6969
+ const groupKey = existingGroup?.groupKey ?? notificationObjectKey("group");
6970
+ const group = existingGroup ?? {
6971
+ groupKey,
6972
+ name,
6973
+ enabled: true,
6974
+ deliveryTiming: DEFAULT_PRODUCT_NOTIFICATION_DELIVERY_TIMING,
6975
+ source: "workspace",
6976
+ configMode: "custom",
6977
+ version: 0
6978
+ };
6979
+ const destinations = current.configuration.destinations.filter(
6980
+ (destination) => destination.groupKey === groupKey
6981
+ );
6982
+ const emails = [
6983
+ ...new Set(
6984
+ targets.flatMap(
6985
+ (target) => target.kind === "email" ? [target.email] : []
6986
+ )
6987
+ )
6988
+ ].sort();
6989
+ const existingEmails = new Set(
6990
+ destinations.flatMap(
6991
+ (destination) => destination.settings.kind === "email_addresses" ? destination.settings.to : []
6992
+ )
6993
+ );
6994
+ const newEmails = emails.filter((email) => !existingEmails.has(email));
6995
+ if (newEmails.length > 0) {
6996
+ destinations.push({
6997
+ destinationKey: notificationObjectKey("destination"),
6998
+ groupKey,
6999
+ name: "Email recipients",
7000
+ enabled: true,
7001
+ source: "workspace",
7002
+ configMode: "custom",
7003
+ version: 0,
7004
+ status: "active",
7005
+ settings: { kind: "email_addresses", to: newEmails }
7006
+ });
7007
+ }
7008
+ const slackTargets = /* @__PURE__ */ new Map();
7009
+ for (const target of targets) {
7010
+ if (target.kind !== "slack") continue;
7011
+ const targetKey = "memberId" in target ? `member:${target.memberId}` : `channel:${target.channel.replace(/^#/, "").toLowerCase()}`;
7012
+ slackTargets.set(targetKey, target);
7013
+ }
7014
+ const connectionId = current.slackConnection.connectionId;
7015
+ if (slackTargets.size > 0 && !connectionId) {
7016
+ throw new Error(
7017
+ "Slack is not connected to this workspace. Connect it in Dashboard \u2192 Integrations, then retry."
7018
+ );
7019
+ }
7020
+ const resolvedSlackTargets = await Promise.all(
7021
+ [...slackTargets.values()].map(async (target) => {
7022
+ if ("memberId" in target) {
7023
+ return {
7024
+ targetKind: "member",
7025
+ targetId: target.memberId,
7026
+ targetName: target.memberId
7027
+ };
7028
+ }
7029
+ const channel = target.channel;
7030
+ const requested = channel.replace(/^#/, "");
7031
+ const response = await this.listNotificationChannels(channel);
7032
+ const matches = response.channels.filter(
7033
+ (candidate) => candidate.id === requested || candidate.name.toLowerCase() === requested.toLowerCase()
7034
+ );
7035
+ if (matches.length !== 1) {
7036
+ throw new Error(
7037
+ matches.length === 0 ? `Slack channel ${channel} was not found or is not visible to the Deepline app.` : `Slack channel ${channel} is ambiguous. Use its channel ID.`
7038
+ );
7039
+ }
7040
+ return {
7041
+ targetKind: "channel",
7042
+ targetId: matches[0].id,
7043
+ targetName: matches[0].name
7044
+ };
7045
+ })
7046
+ );
7047
+ const uniqueSlackTargets = new Map(
7048
+ resolvedSlackTargets.map((target) => [
7049
+ `${target.targetKind}:${target.targetId}`,
7050
+ target
7051
+ ])
7052
+ );
7053
+ for (const target of uniqueSlackTargets.values()) {
7054
+ if (destinations.some(
7055
+ (destination) => destination.settings.kind === "slack" && destination.settings.targetKind === target.targetKind && destination.settings.targetId === target.targetId
7056
+ )) {
7057
+ continue;
7058
+ }
7059
+ destinations.push({
7060
+ destinationKey: notificationObjectKey("destination"),
7061
+ groupKey,
7062
+ name: `${target.targetKind === "channel" ? "#" : "@"}${target.targetName}`,
7063
+ enabled: true,
7064
+ source: "workspace",
7065
+ configMode: "custom",
7066
+ version: 0,
7067
+ status: "pending_verification",
7068
+ settings: {
7069
+ kind: "slack",
7070
+ ...target,
7071
+ oauthConnectionId: connectionId
7072
+ }
7073
+ });
7074
+ }
7075
+ const draft = {
7076
+ group,
7077
+ destinations,
7078
+ rules: current.configuration.rules.filter(
7079
+ (rule) => rule.groupKey === groupKey
7080
+ )
7081
+ };
7082
+ const result = await this.changeNotificationConfiguration(
7083
+ current.configuration.workspaceVersion,
7084
+ { kind: "save_group", draft }
7085
+ );
7086
+ const storedGroup = result.configuration.groups.find(
7087
+ (candidate) => candidate.groupKey === groupKey
7088
+ );
7089
+ if (!storedGroup) {
7090
+ throw new Error("Notification group creation returned no saved group.");
7091
+ }
7092
+ return {
7093
+ mode: "native",
7094
+ groupKey,
7095
+ targets: normalizedTargets,
7096
+ group: storedGroup,
7097
+ destinations: result.configuration.destinations.filter(
7098
+ (destination) => destination.groupKey === groupKey
7099
+ )
7100
+ };
7101
+ }
6733
7102
  /** List Slack channels available to an already-connected Slack integration. */
6734
7103
  async listNotificationChannels(query) {
6735
7104
  const suffix = query ? `?search=${encodeURIComponent(query)}` : "";
@@ -7274,6 +7643,78 @@ var DeeplineClient = class {
7274
7643
  { method: "GET" }
7275
7644
  );
7276
7645
  }
7646
+ /** Submit durable monitor work; transport retries use the same idempotency key. */
7647
+ async submitMonitorBatch(options) {
7648
+ const idempotencyKey = options.idempotencyKey.trim();
7649
+ if (!idempotencyKey) {
7650
+ throw new DeeplineError(
7651
+ "Monitor batch idempotencyKey is required.",
7652
+ void 0,
7653
+ "INVALID_MONITOR_BATCH_IDEMPOTENCY_KEY"
7654
+ );
7655
+ }
7656
+ if (options.kind === "deploy") {
7657
+ if (!options.manifestNdjson.trim()) {
7658
+ throw new DeeplineError(
7659
+ "Monitor batch deploy requires a non-empty NDJSON manifest.",
7660
+ void 0,
7661
+ "INVALID_MONITOR_BATCH_MANIFEST"
7662
+ );
7663
+ }
7664
+ return this.http.request(
7665
+ "/api/v2/monitors/batch",
7666
+ {
7667
+ method: "POST",
7668
+ body: {
7669
+ kind: "deploy",
7670
+ manifest_ndjson: options.manifestNdjson,
7671
+ dry_run: options.dryRun === true,
7672
+ idempotency_key: idempotencyKey
7673
+ },
7674
+ headers: { "Idempotency-Key": idempotencyKey },
7675
+ maxRetries: 0,
7676
+ exactUrlOnly: true
7677
+ }
7678
+ );
7679
+ }
7680
+ const scope = options.scope.kind === "keys" ? { kind: "keys", monitor_keys: options.scope.monitorKeys } : options.scope.kind === "fleet" ? { kind: "fleet", fleet_id: options.scope.fleetId } : { kind: "target", target: options.scope.target };
7681
+ return this.http.request(
7682
+ "/api/v2/monitors/batch",
7683
+ {
7684
+ method: "POST",
7685
+ body: {
7686
+ kind: options.kind,
7687
+ scope,
7688
+ dry_run: options.dryRun === true,
7689
+ idempotency_key: idempotencyKey,
7690
+ ...options.deactivationReason === void 0 ? {} : { deactivation_reason: options.deactivationReason }
7691
+ },
7692
+ headers: { "Idempotency-Key": idempotencyKey },
7693
+ // Durable submission is itself idempotent, but an ambiguous local retry
7694
+ // must retain the exact key and never turn a CLI attempt into two runs.
7695
+ maxRetries: 0,
7696
+ exactUrlOnly: true
7697
+ }
7698
+ );
7699
+ }
7700
+ /** Get durable batch progress and a bounded retained evidence page. */
7701
+ async getMonitorBatch(operationId, options) {
7702
+ const runId = operationId.trim();
7703
+ if (!runId) {
7704
+ throw new DeeplineError(
7705
+ "Monitor batch operation id is required.",
7706
+ void 0,
7707
+ "INVALID_MONITOR_BATCH_OPERATION_ID"
7708
+ );
7709
+ }
7710
+ const params = new URLSearchParams();
7711
+ if (options?.cursor) params.set("cursor", options.cursor);
7712
+ const suffix = params.size > 0 ? `?${params}` : "";
7713
+ return this.http.request(
7714
+ `/api/v2/monitors/batch/${encodeURIComponent(runId)}${suffix}`,
7715
+ { method: "GET" }
7716
+ );
7717
+ }
7277
7718
  /**
7278
7719
  * The deployable monitor tools catalog. Pass a tool id (positional or
7279
7720
  * `{ tool }`) to describe one tool's full payload/stream contract, or no tool
@@ -7758,13 +8199,6 @@ function getAuthedHttpClient() {
7758
8199
  const config = resolveConfig();
7759
8200
  return { config, http: new HttpClient(config) };
7760
8201
  }
7761
- async function writeOutputFile(filename, content) {
7762
- const outputDir = resolve2(process.cwd(), "deepline", "data");
7763
- await mkdir(outputDir, { recursive: true });
7764
- const fullPath = join4(outputDir, filename);
7765
- await writeFile(fullPath, content, "utf-8");
7766
- return fullPath;
7767
- }
7768
8202
  function stableOsUserId() {
7769
8203
  try {
7770
8204
  const info = userInfo();
@@ -8407,13 +8841,6 @@ function createTimestampedName(prefix, extension) {
8407
8841
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
8408
8842
  return `${prefix}-${timestamp}.${extension}`;
8409
8843
  }
8410
- async function writeCsvRowsFile(prefix, rows) {
8411
- const path = await writeOutputFile(
8412
- createTimestampedName(prefix, "csv"),
8413
- csvStringFromRows(rows)
8414
- );
8415
- return path;
8416
- }
8417
8844
  function clip(value, maxLength) {
8418
8845
  return value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
8419
8846
  }
@@ -8591,7 +9018,9 @@ function saveEnvValues(values, baseUrl, scope) {
8591
9018
  ...values[HOST_URL_ENV] ? { [HOST_URL_ENV]: values[HOST_URL_ENV] } : {},
8592
9019
  ...values[API_KEY_ENV] ? { [API_KEY_ENV]: values[API_KEY_ENV] } : {},
8593
9020
  ...values[ACTIVE_ORG_ID_ENV] ? { [ACTIVE_ORG_ID_ENV]: values[ACTIVE_ORG_ID_ENV] } : {},
8594
- ...Object.prototype.hasOwnProperty.call(values, ACTIVE_ORG_NAME_ENV) ? { [ACTIVE_ORG_NAME_ENV]: values[ACTIVE_ORG_NAME_ENV] } : {}
9021
+ ...Object.prototype.hasOwnProperty.call(values, ACTIVE_ORG_NAME_ENV) ? { [ACTIVE_ORG_NAME_ENV]: values[ACTIVE_ORG_NAME_ENV] } : {},
9022
+ // An explicit empty list must reach the store: it clears a stale line.
9023
+ ...Object.prototype.hasOwnProperty.call(values, FEATURES_ENV) ? { [FEATURES_ENV]: values[FEATURES_ENV] } : {}
8595
9024
  };
8596
9025
  if (scope === "folder") {
8597
9026
  saveProjectDeeplineEnvValues(filtered);
@@ -8605,7 +9034,8 @@ function activeOrgEnvValues(data) {
8605
9034
  const orgName = typeof data.org_name === "string" ? data.org_name.trim() : "";
8606
9035
  return {
8607
9036
  [ACTIVE_ORG_ID_ENV]: orgId,
8608
- [ACTIVE_ORG_NAME_ENV]: orgName
9037
+ [ACTIVE_ORG_NAME_ENV]: orgName,
9038
+ ...featureEnvValues(data)
8609
9039
  };
8610
9040
  }
8611
9041
  async function httpJson(method, url, apiKey, body, timeoutMs = AUTH_HTTP_TIMEOUT_MS) {
@@ -9130,6 +9560,8 @@ async function handleStatus(args) {
9130
9560
  savedApiKeyPath = envFilePath(baseUrl);
9131
9561
  }
9132
9562
  }
9563
+ } else if (resolvedAuthScope === "folder" || storedHostApiKey(baseUrl) === apiKey) {
9564
+ saveEnvValues(featureEnvValues(data), baseUrl, resolvedAuthScope);
9133
9565
  }
9134
9566
  printCommandEnvelope(
9135
9567
  {
@@ -9193,7 +9625,7 @@ Notes:
9193
9625
  workspace. To switch, use deepline org list and deepline org set <org-id>.
9194
9626
  `
9195
9627
  );
9196
- auth.command("workspaces").description("Deprecated. Use deepline org list to see workspaces.").action(() => {
9628
+ auth.command("workspaces", { hidden: true }).description("Deprecated. Use deepline org list to see workspaces.").action(() => {
9197
9629
  console.error(
9198
9630
  "Workspaces live under `deepline org`.\n List: deepline org list\n Switch: deepline org set <org-id>\n`deepline auth register` re-approves this device; it does not change the active workspace."
9199
9631
  );
@@ -9290,7 +9722,6 @@ var SUBSCRIPTION_STATUS_NEXT_COMMAND = "deepline billing subscription status --j
9290
9722
  var SUBSCRIPTION_CANCEL_PATH = "/api/v2/billing/subscription/cancel";
9291
9723
  var BILLING_TOP_UP_PATH = "/api/v2/billing/top-up";
9292
9724
  var DEFAULT_BALANCE_RECOVERY_CREDITS = 100;
9293
- var BILLING_HISTORY_EXPORT_LIMIT = 5e3;
9294
9725
  function billingFailureFromError(error, options) {
9295
9726
  if (error instanceof AuthError) {
9296
9727
  return {
@@ -9581,6 +10012,9 @@ async function handleBalance(options) {
9581
10012
  );
9582
10013
  return;
9583
10014
  }
10015
+ function quoteShellArgument(value) {
10016
+ return `'${value.replace(/'/g, `'\\''`)}'`;
10017
+ }
9584
10018
  async function handleUsage(options) {
9585
10019
  const { http } = getAuthedHttpClient();
9586
10020
  const params = new URLSearchParams();
@@ -9597,6 +10031,7 @@ async function handleUsage(options) {
9597
10031
  const recent = payload.recent ?? {};
9598
10032
  const last30Usage = usage.last_30_days_usage_credits ?? usage.month_spent_credits;
9599
10033
  const temporaryHolds = Number(usage.temporary_hold_credits ?? 0);
10034
+ const nextPage = typeof recent.next_cursor === "string" && recent.next_cursor.length > 0 ? `deepline billing usage --cursor ${quoteShellArgument(recent.next_cursor)}${options.limit ? ` --limit ${options.limit}` : ""}${options.runId ? ` --run-id ${quoteShellArgument(options.runId)}` : ""} --json` : null;
9600
10035
  const lines = [
9601
10036
  `Balance: ${payload.balance ?? "(unknown)"}`,
9602
10037
  `Last 30 days usage: ${last30Usage ?? "(unknown)"}`,
@@ -9608,13 +10043,20 @@ async function handleUsage(options) {
9608
10043
  ...recentUsageLines(
9609
10044
  Array.isArray(recent.entries) ? recent.entries : []
9610
10045
  ),
9611
- ...typeof recent.next_cursor === "string" && recent.next_cursor.length > 0 ? [
9612
- `Next page: deepline billing usage --cursor ${recent.next_cursor}${options.limit ? ` --limit ${options.limit}` : ""}${options.runId ? ` --run-id ${options.runId}` : ""}`
10046
+ ...recent.has_more === true || recent.incomplete === true ? ["List truncated \u2014 more recent calls match this query."] : [],
10047
+ ...nextPage ? [`Next page: ${nextPage}`] : [],
10048
+ ...!nextPage && (recent.has_more === true || recent.incomplete === true) ? [
10049
+ "No continuation is available for this view. Use deepline billing ledger export all --json for the full ledger."
9613
10050
  ] : []
9614
10051
  ];
9615
10052
  printCommandEnvelope(
9616
10053
  {
9617
10054
  ...payload,
10055
+ recent: {
10056
+ ...recent,
10057
+ is_truncated: recent.has_more === true || recent.incomplete === true
10058
+ },
10059
+ next: { page: nextPage },
9618
10060
  render: { sections: [{ title: "billing usage", lines }] }
9619
10061
  },
9620
10062
  { json: options.json }
@@ -9678,6 +10120,39 @@ async function handleLimitOff(options) {
9678
10120
  { json: options.json }
9679
10121
  );
9680
10122
  }
10123
+ async function exportLedger(http, outputPath, options) {
10124
+ let summary = { row_count: 0, net_delta_credits: 0 };
10125
+ let initialized = false;
10126
+ for await (const entries of iterateCursorPages(async (cursor) => {
10127
+ const params = new URLSearchParams({ limit: "5000" });
10128
+ if (cursor) params.set("cursor", cursor);
10129
+ if (options.sinceAt !== null)
10130
+ params.set("since_at", String(options.sinceAt));
10131
+ if (options.runId) params.set("run_id", options.runId);
10132
+ if (options.operation) params.set("operation", options.operation);
10133
+ const payload = await http.get(
10134
+ `/api/v2/billing/ledger?${params.toString()}`
10135
+ );
10136
+ if (!Array.isArray(payload.entries) || typeof payload.has_more !== "boolean") {
10137
+ throw new Error("Billing ledger response is missing entries");
10138
+ }
10139
+ return {
10140
+ items: payload.entries,
10141
+ hasMore: payload.has_more === true,
10142
+ cursor: typeof payload.next_cursor === "string" ? payload.next_cursor : null
10143
+ };
10144
+ })) {
10145
+ if (!initialized) {
10146
+ await mkdir2(dirname6(outputPath), { recursive: true });
10147
+ await writeFile2(outputPath, ledgerRowsToCsv([], true), "utf-8");
10148
+ initialized = true;
10149
+ }
10150
+ const rows = entries.map(ledgerApiEntryToRow);
10151
+ await appendFile(outputPath, ledgerRowsToCsv(rows, false), "utf-8");
10152
+ summary = summarizeLedgerRows(summary, rows);
10153
+ }
10154
+ return summary;
10155
+ }
9681
10156
  async function handleHistory(options) {
9682
10157
  const { http } = getAuthedHttpClient();
9683
10158
  const windows = {
@@ -9687,41 +10162,20 @@ async function handleHistory(options) {
9687
10162
  "1y": 31536e3
9688
10163
  };
9689
10164
  const sinceAt = Math.max(0, Math.floor(Date.now() / 1e3) - windows[options.time]) * 1e3;
9690
- const entries = [];
9691
- let cursor = null;
9692
- while (entries.length < BILLING_HISTORY_EXPORT_LIMIT) {
9693
- const params = new URLSearchParams({
9694
- since_at: String(sinceAt),
9695
- limit: String(BILLING_HISTORY_EXPORT_LIMIT - entries.length)
9696
- });
9697
- if (cursor !== null) params.set("cursor", cursor);
9698
- const payload = await http.get(
9699
- `/api/v2/billing/ledger?${params.toString()}`
9700
- );
9701
- if (Array.isArray(payload.entries)) {
9702
- entries.push(
9703
- ...payload.entries.slice(
9704
- 0,
9705
- BILLING_HISTORY_EXPORT_LIMIT - entries.length
9706
- )
9707
- );
9708
- }
9709
- const nextCursor = typeof payload.next_cursor === "string" && payload.next_cursor.length > 0 ? payload.next_cursor : null;
9710
- if (payload.has_more !== true || nextCursor === null) break;
9711
- if (nextCursor === cursor) {
9712
- throw new Error("Billing history pagination cursor did not advance");
9713
- }
9714
- cursor = nextCursor;
9715
- }
9716
- const rows = entries.map(ledgerApiEntryToRow);
9717
- const outputPath = await writeCsvRowsFile(
9718
- `billing-history-${options.time}`,
9719
- rows
10165
+ const outputPath = resolve3(
10166
+ process.cwd(),
10167
+ "deepline",
10168
+ "data",
10169
+ createTimestampedName(`billing-history-${options.time}`, "csv")
9720
10170
  );
10171
+ const summary = await exportLedger(http, outputPath, { sinceAt });
9721
10172
  printCommandEnvelope(
9722
10173
  {
9723
10174
  output_path: outputPath,
9724
- row_count: rows.length,
10175
+ row_count: summary.row_count,
10176
+ has_more: false,
10177
+ is_truncated: false,
10178
+ next_cursor: null,
9725
10179
  time_window: options.time,
9726
10180
  render: {
9727
10181
  sections: [
@@ -9729,7 +10183,7 @@ async function handleHistory(options) {
9729
10183
  title: "billing history",
9730
10184
  lines: [
9731
10185
  `Billing history written to ${outputPath}`,
9732
- `${rows.length} row(s) exported.`
10186
+ `${summary.row_count} row(s) exported.`
9733
10187
  ]
9734
10188
  }
9735
10189
  ]
@@ -9754,40 +10208,18 @@ async function handleLedgerExportAll(options) {
9754
10208
  }
9755
10209
  const { http } = getAuthedHttpClient();
9756
10210
  const outputPath = options.output ? resolve3(String(options.output)) : defaultLedgerExportPath();
9757
- let summary = { row_count: 0, net_delta_credits: 0 };
9758
- let cursor = null;
9759
- let initializedOutput = false;
9760
- for (; ; ) {
9761
- const params = new URLSearchParams({ limit: "5000" });
9762
- if (cursor !== null) params.set("cursor", cursor);
9763
- if (options.runId) params.set("run_id", options.runId);
9764
- if (options.operation) params.set("operation", options.operation);
9765
- if (sinceAt !== null) params.set("since_at", String(sinceAt));
9766
- const payload = await http.get(
9767
- `/api/v2/billing/ledger?${params.toString()}`
9768
- );
9769
- const entries = Array.isArray(payload.entries) ? payload.entries : [];
9770
- const rows = entries.map(ledgerApiEntryToRow);
9771
- if (!initializedOutput) {
9772
- await mkdir2(dirname6(outputPath), { recursive: true });
9773
- await writeFile2(outputPath, ledgerRowsToCsv([], true), "utf-8");
9774
- initializedOutput = true;
9775
- }
9776
- if (rows.length > 0) {
9777
- await appendFile(outputPath, ledgerRowsToCsv(rows, false), "utf-8");
9778
- summary = summarizeLedgerRows(summary, rows);
9779
- }
9780
- const nextCursor = typeof payload.next_cursor === "string" && payload.next_cursor.length > 0 ? payload.next_cursor : null;
9781
- if (payload.has_more !== true || nextCursor === null) {
9782
- break;
9783
- }
9784
- if (nextCursor === cursor) break;
9785
- cursor = nextCursor;
9786
- }
10211
+ const summary = await exportLedger(http, outputPath, {
10212
+ sinceAt,
10213
+ runId: options.runId,
10214
+ operation: options.operation
10215
+ });
9787
10216
  printCommandEnvelope(
9788
10217
  {
9789
10218
  output_path: outputPath,
9790
10219
  row_count: summary.row_count,
10220
+ has_more: false,
10221
+ is_truncated: false,
10222
+ next_cursor: null,
9791
10223
  net_delta_credits: summary.net_delta_credits,
9792
10224
  scope: "workspace",
9793
10225
  ...options.runId ? { run_id: options.runId } : {},
@@ -10334,12 +10766,12 @@ async function handleBuyCredits(creditsRaw, options) {
10334
10766
  );
10335
10767
  }
10336
10768
  async function handleTargetPlan(planSku, options) {
10337
- if (planSku !== "payg-v1" && planSku !== "builder-v1" && planSku !== "team-v1") {
10769
+ if (planSku !== "payg-v1" && planSku !== "builder-v1" && planSku !== "growth-v1" && planSku !== "growth-canary-v1" && planSku !== "team-v1") {
10338
10770
  reportBillingFailure(
10339
10771
  {
10340
10772
  exitCode: 2,
10341
10773
  code: "INVALID_PLAN",
10342
- message: "Plan must be payg-v1, builder-v1, or team-v1."
10774
+ message: "Plan must be payg-v1, builder-v1, growth-v1, growth-canary-v1, or team-v1."
10343
10775
  },
10344
10776
  options
10345
10777
  );
@@ -10532,7 +10964,9 @@ Examples:
10532
10964
  `
10533
10965
  Notes:
10534
10966
  Read-only server query that writes a local CSV file. With --json, stdout is a
10535
- summary containing the output path and row count.
10967
+ summary containing the output path, row count, and completeness metadata.
10968
+ Exports the complete window using bounded pages. For an exact start timestamp:
10969
+ deepline billing ledger export all --since <ISO-8601> --json
10536
10970
 
10537
10971
  Examples:
10538
10972
  deepline billing history --time 1m
@@ -10600,7 +11034,10 @@ Examples:
10600
11034
  "Disable automatic recharge without clearing saved amounts."
10601
11035
  ).option("--idempotency-key <key>", "Stable retry key").option("--dry-run", "Print the planned update without applying it").option("--json", "Emit JSON output").action(handleAutoRechargeOff)
10602
11036
  );
10603
- billing.command("change-plan").description("Start or change the target billing plan.").argument("<plan_sku>", "payg-v1, builder-v1, or team-v1").option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleTargetPlan);
11037
+ billing.command("change-plan").description("Start or change the target billing plan.").argument(
11038
+ "<plan_sku>",
11039
+ "payg-v1, builder-v1, growth-v1, growth-canary-v1, or team-v1"
11040
+ ).option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleTargetPlan);
10604
11041
  billing.command("cancel-plan").description("Cancel a target subscription at period end, or undo it.").option("--undo", "Undo a pending period-end cancellation").option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleTargetPlanCancellation);
10605
11042
  billing.command("portal").description("Open the Stripe-hosted billing recovery portal.").option("--no-open", "Print the URL without opening a browser").option("--json", "Emit JSON output").action(handleTargetPortal);
10606
11043
  billing.command("plans").description("Show published plans and the plan you are on.").addHelpText(
@@ -10894,6 +11331,12 @@ function mergeCanonicalRowsInfo(left, right) {
10894
11331
  const exportUnavailableReason = left.exportUnavailableReason === "shared_table_namespace" || right.exportUnavailableReason === "shared_table_namespace" ? "shared_table_namespace" : left.exportUnavailableReason ?? right.exportUnavailableReason;
10895
11332
  const unavailableSource = left.exportUnavailableReason === exportUnavailableReason ? left : right.exportUnavailableReason === exportUnavailableReason ? right : null;
10896
11333
  const columns = preferred.columns.length > 0 ? preferred.columns : fallback.columns;
11334
+ const recoveredReturnPaths2 = [
11335
+ .../* @__PURE__ */ new Set([
11336
+ ...left.recoveredReturnPaths ?? [],
11337
+ ...right.recoveredReturnPaths ?? []
11338
+ ])
11339
+ ];
10897
11340
  return {
10898
11341
  ...fallback,
10899
11342
  ...preferred,
@@ -10906,6 +11349,7 @@ function mergeCanonicalRowsInfo(left, right) {
10906
11349
  datasetId: preferred.datasetId ?? fallback.datasetId,
10907
11350
  tableNamespace: preferred.tableNamespace ?? fallback.tableNamespace,
10908
11351
  ...left.recovered || right.recovered ? { recovered: true } : {},
11352
+ ...recoveredReturnPaths2.length ? { recoveredReturnPaths: recoveredReturnPaths2 } : {},
10909
11353
  ...exportUnavailableReason ? {
10910
11354
  exportUnavailableReason,
10911
11355
  ...unavailableSource?.exportUnavailableMessage ? {
@@ -10966,6 +11410,16 @@ function isSerializedDataset(value) {
10966
11410
  function isPackagedDatasetOutput(value) {
10967
11411
  return isRecord8(value) && value.kind === "dataset" && isRecord8(value.preview) && Array.isArray(value.preview.rows);
10968
11412
  }
11413
+ function recoveredReturnPaths(value) {
11414
+ if (!Array.isArray(value)) return [];
11415
+ return [
11416
+ ...new Set(
11417
+ value.filter(
11418
+ (path) => typeof path === "string" && /^result\.[A-Za-z_$][A-Za-z0-9_$]*$/.test(path)
11419
+ ).slice(0, 200)
11420
+ )
11421
+ ];
11422
+ }
10969
11423
  function pathParts(path) {
10970
11424
  return path.split(".").map((part) => part.trim()).filter(Boolean);
10971
11425
  }
@@ -11062,6 +11516,11 @@ function canonicalRowsInfoFromCandidate(input2) {
11062
11516
  datasetId: typeof candidate.value.datasetId === "string" ? candidate.value.datasetId : null,
11063
11517
  tableNamespace: typeof candidate.value.tableNamespace === "string" ? candidate.value.tableNamespace : null,
11064
11518
  ...candidate.value.recovered === true ? { recovered: true } : {},
11519
+ ...recoveredReturnPaths(candidate.value.recoveredReturnPaths).length ? {
11520
+ recoveredReturnPaths: recoveredReturnPaths(
11521
+ candidate.value.recoveredReturnPaths
11522
+ )
11523
+ } : {},
11065
11524
  ...isRecord8(candidate.value.exportUnavailable) && (candidate.value.exportUnavailable.reason === "empty_dataset" || candidate.value.exportUnavailable.reason === "shared_table_namespace") ? {
11066
11525
  exportUnavailableReason: candidate.value.exportUnavailable.reason,
11067
11526
  ...typeof candidate.value.exportUnavailable.message === "string" ? {
@@ -15217,6 +15676,82 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
15217
15676
  description: "Unsupported legacy child-workflow timeout.",
15218
15677
  errorMessage: "ctx.runPlay timeoutMs is unsupported because child Plays execute inline."
15219
15678
  },
15679
+ "ctx.runPlayAsync.key": {
15680
+ schema: Type.String({ minLength: 1 }),
15681
+ fixtures: {
15682
+ valid: "request-approval",
15683
+ invalid: "",
15684
+ absent: void 0,
15685
+ unresolved: { expression: "callKey" },
15686
+ edition1: "request-approval"
15687
+ },
15688
+ referenceType: "string",
15689
+ required: true,
15690
+ resolution: "runtime-dynamic",
15691
+ issueCode: "play_authoring_run_play_option_invalid",
15692
+ description: "Stable identity for one independent child Play Run.",
15693
+ errorMessage: "ctx.runPlayAsync key must be a non-empty string."
15694
+ },
15695
+ "ctx.runPlayAsync.playRef": {
15696
+ schema: Type.Union([
15697
+ Type.String({ minLength: 1 }),
15698
+ Type.Union([
15699
+ Type.Object(
15700
+ { playName: Type.String({ minLength: 1 }) },
15701
+ { additionalProperties: true }
15702
+ ),
15703
+ Type.Object(
15704
+ { name: Type.String({ minLength: 1 }) },
15705
+ { additionalProperties: true }
15706
+ )
15707
+ ])
15708
+ ]),
15709
+ fixtures: {
15710
+ valid: "request-approval",
15711
+ invalid: {},
15712
+ absent: void 0,
15713
+ unresolved: { expression: "playRef" },
15714
+ edition1: "request-approval"
15715
+ },
15716
+ referenceType: "string | PlayReferenceLike",
15717
+ required: true,
15718
+ resolution: "runtime-dynamic",
15719
+ issueCode: "play_authoring_run_play_option_invalid",
15720
+ description: "Independently published scalar child Play name or strongly typed definition handle.",
15721
+ errorMessage: "ctx.runPlayAsync playRef must be a non-empty Play name or definition handle."
15722
+ },
15723
+ "ctx.runPlayAsync.input": {
15724
+ schema: Type.Record(Type.String(), Type.Unknown()),
15725
+ fixtures: {
15726
+ valid: { draftId: "draft-1" },
15727
+ invalid: "draft-1",
15728
+ absent: void 0,
15729
+ unresolved: "childInput",
15730
+ edition1: {}
15731
+ },
15732
+ referenceType: "Record<string, unknown>",
15733
+ required: true,
15734
+ resolution: "runtime-dynamic",
15735
+ issueCode: "play_authoring_run_play_option_invalid",
15736
+ description: "Scalar input object submitted to the independent child Run.",
15737
+ errorMessage: "ctx.runPlayAsync input must be an object."
15738
+ },
15739
+ "ctx.runPlayAsync.options.description": {
15740
+ schema: Type.String({ minLength: 1 }),
15741
+ fixtures: {
15742
+ valid: "Request approval.",
15743
+ invalid: "",
15744
+ absent: void 0,
15745
+ unresolved: { expression: "description" },
15746
+ edition1: "Request approval."
15747
+ },
15748
+ referenceType: "string",
15749
+ required: true,
15750
+ resolution: "runtime-dynamic",
15751
+ issueCode: "play_authoring_run_play_option_invalid",
15752
+ description: "Non-empty purpose for one independent child Play Run.",
15753
+ errorMessage: "ctx.runPlayAsync options.description must be non-empty."
15754
+ },
15220
15755
  "runtime.timeout": {
15221
15756
  schema: Type.String({ pattern: "^\\d+\\s*[mh]$" }),
15222
15757
  fixtures: {
@@ -15504,6 +16039,10 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
15504
16039
  `export type CsvOptions = { description?: ${cloudReferenceType("ctx.csv.options.description")}; columns?: ${cloudReferenceType("ctx.csv.options.columns")}; rename?: ${cloudReferenceType("ctx.csv.options.rename")}; required?: ${cloudReferenceType("ctx.csv.options.required")} };`,
15505
16040
  "export type PlayCallExecution = 'inline';",
15506
16041
  `export type PlayCallOptions = { description: ${cloudReferenceType("ctx.runPlay.options.description")}; execution?: ${cloudReferenceType("ctx.runPlay.options.execution")}; timeoutMs?: ${cloudReferenceType("ctx.runPlay.options.timeoutMs")} };`,
16042
+ `export type PlayAsyncCallOptions = { description: ${cloudReferenceType("ctx.runPlayAsync.options.description")} };`,
16043
+ "declare const PLAY_RUN_ID_BRAND: unique symbol;",
16044
+ "export type PlayRunId = string & { readonly [PLAY_RUN_ID_BRAND]: 'PlayRunId' };",
16045
+ "export type AsyncPlayRunRef = Readonly<{ runId: PlayRunId }>;",
15507
16046
  `export type RuntimeStepOptions = { semanticKey?: ${cloudReferenceType("ctx.step.semanticKey")}; staleAfterSeconds?: ${cloudReferenceType("ctx.step.staleAfterSeconds")} };`,
15508
16047
  `export type FetchOptions = { staleAfterSeconds?: ${cloudReferenceType("ctx.fetch.staleAfterSeconds")}; transient?: boolean };`,
15509
16048
  "export type PlayFetchResponse = { ok: boolean; status: number; statusText: string; url: string; headers: Record<string, string>; bodyText: string; json: unknown | null };",
@@ -15529,11 +16068,11 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
15529
16068
  " withColumn<Name extends string, Value>(name: Name, definition: DatasetColumnDefinition<OutputRow, Value> & { readonly runIf: (row: OutputRow, index: number) => boolean | Promise<boolean> }): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;",
15530
16069
  " withColumn<Name extends string, Value>(name: Name, definition: DatasetColumnDefinition<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;",
15531
16070
  " withColumn<Name extends string, Value>(name: Name, resolver: StepResolver<OutputRow, Value> | RunnableColumnStepProgram<Value>, options: StepOptions<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;",
15532
- " withColumns<Program extends StepProgram<OutputRow, object, unknown>>(program: Program): DatasetBuilder<InputRow, StepProgramOutput<Program>>;",
16071
+ " withColumns<ProgramOutput extends OutputRow, Return>(program: StepProgram<OutputRow, ProgramOutput, Return>): DatasetBuilder<InputRow, ProgramOutput>;",
15533
16072
  " step<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): never;",
15534
16073
  " run(options?: DatasetRunOptions<InputRow>): Promise<PlayDataset<OutputRow>>;",
15535
16074
  "};",
15536
- "export type PlayReferenceLike = { readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string };",
16075
+ "export type PlayReferenceLike<TInput extends Record<string, unknown> = Record<string, unknown>> = ({ readonly playName: string; readonly name?: string } | { readonly name: string; readonly playName?: string }) & { readonly __inputType?: TInput };",
15537
16076
  "export interface DeeplinePlayRuntimeContext {",
15538
16077
  " csv<T = Record<string, unknown>>(path: string | CsvInput<T & object>, options?: CsvOptions): Promise<PlayDataset<T>>;",
15539
16078
  " dataset<TSource extends PlayDatasetInput<object>>(key: string, items: TSource): DatasetBuilder<PlayDatasetRow<TSource> & object, PlayDatasetRow<TSource> & object>;",
@@ -15547,6 +16086,8 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
15547
16086
  " fetch(key: string, url: string | URL, init?: RequestInit & { headers?: HeadersInit & { 'Idempotency-Key'?: string; 'X-Idempotency-Key'?: string }; auth?: SecretAuthInput }, options?: FetchOptions): Promise<PlayFetchResponse>;",
15548
16087
  " secrets: { get(name: string): SecretPromise; bearer(secret: string | SecretPromise | SecretHandle): SecretAuth; header(header: string, secret: string | SecretPromise | SecretHandle): SecretAuth };",
15549
16088
  ` runPlay<TOutput = unknown>(key: string, playRef: ${cloudReferenceType("ctx.runPlay.playRef")}, input: ${cloudReferenceType("ctx.runPlay.input")}, options: PlayCallOptions): Promise<TOutput>;`,
16089
+ " runPlayAsync<TInput extends Record<string, unknown>>(key: string, playRef: PlayReferenceLike<TInput>, input: NoInfer<TInput>, options: PlayAsyncCallOptions): Promise<AsyncPlayRunRef>;",
16090
+ " runPlayAsync(key: string, playRef: string, input: Record<string, unknown>, options: PlayAsyncCallOptions): Promise<AsyncPlayRunRef>;",
15550
16091
  " log(message: string, options?: { level?: 'debug' | 'info' | 'warn' | 'error' }): void;",
15551
16092
  ` sleep(ms: ${cloudReferenceType("ctx.sleep.ms")}): Promise<void>;`,
15552
16093
  "}",
@@ -15821,7 +16362,7 @@ function collectInlineSecretFindings(sourceCode) {
15821
16362
  var MAX_PLAY_BUNDLE_BYTES = 30 * 1024 * 1024;
15822
16363
 
15823
16364
  // ../plays/bundling/index.ts
15824
- var PLAY_BUNDLE_CACHE_VERSION = 36;
16365
+ var PLAY_BUNDLE_CACHE_VERSION = 37;
15825
16366
  var PLAY_ARTIFACT_CACHE_DIR = join7(
15826
16367
  tmpdir2(),
15827
16368
  `deepline-play-artifacts-v${PLAY_BUNDLE_CACHE_VERSION}`
@@ -16560,7 +17101,7 @@ var RunDataset = class _RunDataset {
16560
17101
  return this.#info.exportUnavailableMessage;
16561
17102
  }
16562
17103
  matches(path) {
16563
- return this.source === path || this.datasetId === path || this.tableNamespace === path;
17104
+ return this.source === path || this.datasetId === path || this.tableNamespace === path || this.#info.recoveredReturnPaths?.includes(path) === true;
16564
17105
  }
16565
17106
  /** Return a new Handle when its preview has been replaced by fetched rows. */
16566
17107
  withRows(info) {
@@ -17070,7 +17611,7 @@ function resolveDaytonaSandboxComputeItem(input2) {
17070
17611
  };
17071
17612
  }
17072
17613
 
17073
- // ../../shared_libs/billing/compute-pricing.ts
17614
+ // ../billing/compute-pricing-contract.ts
17074
17615
  var COMPUTE_BILLING_MARKUP_MULTIPLIER = 5;
17075
17616
  var DEFAULT_USD_PER_CUSTOMER_CREDIT = 0.1;
17076
17617
  var DEFAULT_PROVIDER_MARKUP_MULTIPLIER = 1.4;
@@ -17405,6 +17946,39 @@ var PLAY_RUN_RESERVED_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
17405
17946
  "--open",
17406
17947
  "--debug-map-latency"
17407
17948
  ]);
17949
+ var PLAY_RUN_HEADER_FLAG_USAGE = `--header requires "<name>: <value>", for example: --header 'x-deepline-absurd-release: 2026-09-07.1'`;
17950
+ var HTTP_HEADER_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9a-z]+$/;
17951
+ function parsePlayRunHeaderFlag(raw) {
17952
+ const separator = raw.indexOf(":");
17953
+ if (separator < 0) {
17954
+ throw new Error(
17955
+ `${PLAY_RUN_HEADER_FLAG_USAGE}. Got ${JSON.stringify(raw)} with no ":" separator.`
17956
+ );
17957
+ }
17958
+ const name = raw.slice(0, separator).trim().toLowerCase();
17959
+ const value = raw.slice(separator + 1).trim();
17960
+ if (!name) {
17961
+ throw new Error(
17962
+ `${PLAY_RUN_HEADER_FLAG_USAGE}. Got ${JSON.stringify(raw)} with an empty header name.`
17963
+ );
17964
+ }
17965
+ if (!HTTP_HEADER_NAME_PATTERN.test(name)) {
17966
+ throw new Error(
17967
+ `${PLAY_RUN_HEADER_FLAG_USAGE}. ${JSON.stringify(name)} is not a valid HTTP header name.`
17968
+ );
17969
+ }
17970
+ if (!value) {
17971
+ throw new Error(
17972
+ `${PLAY_RUN_HEADER_FLAG_USAGE}. Header ${JSON.stringify(name)} was given an empty value.`
17973
+ );
17974
+ }
17975
+ if (/[\u0000-\u001f\u007f]/.test(value)) {
17976
+ throw new Error(
17977
+ `${PLAY_RUN_HEADER_FLAG_USAGE}. Header ${JSON.stringify(name)} contains a control character.`
17978
+ );
17979
+ }
17980
+ return { name, value };
17981
+ }
17408
17982
  function isBarePlayRunDebugFlag(args, index) {
17409
17983
  return args[index] === "--debug" && (args[index + 1] === void 0 || args[index + 1].startsWith("--"));
17410
17984
  }
@@ -19018,6 +19592,9 @@ function assertPlayWaitNotTimedOut(input2) {
19018
19592
  }
19019
19593
  }
19020
19594
  var PLAY_DURABLE_STATUS_PROBE_INTERVAL_MS = 15e3;
19595
+ function isAdmittedRunVisibilityDelay(error) {
19596
+ return error instanceof DeeplineError && error.statusCode === 404 && error.message.trim() === "Workflow run not found.";
19597
+ }
19021
19598
  async function waitForPlayCompletionByStream(input2) {
19022
19599
  let lastPhase = null;
19023
19600
  let reconnectAttempt = 0;
@@ -19031,7 +19608,7 @@ async function waitForPlayCompletionByStream(input2) {
19031
19608
  billing: false
19032
19609
  });
19033
19610
  } catch (error) {
19034
- if (isTransientPlayStreamError(error)) {
19611
+ if (isTransientPlayStreamError(error) || isAdmittedRunVisibilityDelay(error)) {
19035
19612
  return null;
19036
19613
  }
19037
19614
  throw error;
@@ -19142,7 +19719,7 @@ async function waitForPlayCompletionByStream(input2) {
19142
19719
  if (timedOut) {
19143
19720
  return { kind: "timed_out", sawEvent };
19144
19721
  }
19145
- if (!isTransientPlayStreamError(error)) {
19722
+ if (!isTransientPlayStreamError(error) && !isAdmittedRunVisibilityDelay(error)) {
19146
19723
  throw error;
19147
19724
  }
19148
19725
  return {
@@ -19310,6 +19887,7 @@ async function startAndWaitForPlayCompletionByStreamOnce(input2) {
19310
19887
  runId: workflowId,
19311
19888
  playName: input2.playName,
19312
19889
  dashboardUrl,
19890
+ inputFieldCount: Object.keys(input2.request.input ?? {}).length,
19313
19891
  jsonOutput: false,
19314
19892
  progress: input2.progress
19315
19893
  });
@@ -21378,7 +21956,7 @@ function writeStartedPlayRun(input2) {
21378
21956
  );
21379
21957
  }
21380
21958
  function parsePlayRunOptions(args) {
21381
- const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--json] [--full] [--<input> value]\n Runs use the Absurd CJS runtime.\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotent call caching and ctx.dataset guidance.";
21959
+ const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--run-id-file <path>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--open] [--json] [--full] [--<input> value]\n Runs use the Absurd CJS runtime.\n --header '<name>: <value>' may repeat; each is sent on every request for this run.\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotent call caching and ctx.dataset guidance.";
21382
21960
  let filePath = null;
21383
21961
  let playName = null;
21384
21962
  let input2 = null;
@@ -21399,8 +21977,20 @@ function parsePlayRunOptions(args) {
21399
21977
  let profile = null;
21400
21978
  let fixtureBehavior = null;
21401
21979
  let runIdFile = null;
21980
+ let headers = null;
21402
21981
  for (let index = 0; index < args.length; index += 1) {
21403
21982
  const arg = args[index];
21983
+ if (arg === "--header" || arg.startsWith("--header=")) {
21984
+ const raw = arg === "--header" ? args[index + 1] : arg.slice("--header=".length);
21985
+ if (raw === void 0 || arg === "--header" && raw.startsWith("--")) {
21986
+ throw new Error(`${PLAY_RUN_HEADER_FLAG_USAGE}. No value was given.`);
21987
+ }
21988
+ const header = parsePlayRunHeaderFlag(raw);
21989
+ headers ??= {};
21990
+ headers[header.name] = header.value;
21991
+ if (arg === "--header") index += 1;
21992
+ continue;
21993
+ }
21404
21994
  if (arg === "--file" && args[index + 1]) {
21405
21995
  filePath = args[++index];
21406
21996
  continue;
@@ -21639,7 +22229,8 @@ function parsePlayRunOptions(args) {
21639
22229
  profile,
21640
22230
  debugMapLatency,
21641
22231
  fixtureBehavior,
21642
- runIdFile
22232
+ runIdFile,
22233
+ headers
21643
22234
  };
21644
22235
  }
21645
22236
  function parsePlayCheckOptions(args) {
@@ -22395,11 +22986,14 @@ function printPlayCheckOutcomes(outcomes, target) {
22395
22986
  printPlayCheckOutcome(outcome, target, `[${outcome.exportName}] `);
22396
22987
  }
22397
22988
  }
22989
+ function playRunClientOptions(options) {
22990
+ return options.headers ? { headers: options.headers } : void 0;
22991
+ }
22398
22992
  async function handleFileBackedRun(options, hooks) {
22399
22993
  if (options.target.kind !== "file") {
22400
22994
  throw new Error("Expected a file-backed play run target.");
22401
22995
  }
22402
- const client2 = new DeeplineClient();
22996
+ const client2 = new DeeplineClient(playRunClientOptions(options));
22403
22997
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
22404
22998
  const absolutePlayPath = resolve11(options.target.path);
22405
22999
  progress.phase("compiling play");
@@ -22607,7 +23201,7 @@ async function handleNamedRun(options, hooks) {
22607
23201
  if (options.target.kind !== "name") {
22608
23202
  throw new Error("Expected a named play run target.");
22609
23203
  }
22610
- const client2 = new DeeplineClient();
23204
+ const client2 = new DeeplineClient(playRunClientOptions(options));
22611
23205
  const progress = getActiveCliProgress() ?? createCliProgress(!options.jsonOutput);
22612
23206
  const playName = options.target.name;
22613
23207
  const runtimeInput = options.input ? { ...options.input } : {};
@@ -22937,12 +23531,13 @@ async function handleRunsList(args) {
22937
23531
  return 1;
22938
23532
  }
22939
23533
  const client2 = new DeeplineClient();
22940
- const runs = (await client2.runs.list({
23534
+ const page = await client2.runs.listPage({
22941
23535
  ...playName ? { play: playName } : {},
22942
23536
  ...statusFilter ? { status: statusFilter } : {},
22943
23537
  ...limit !== void 0 ? { limit } : {},
22944
23538
  ...offset !== void 0 ? { offset } : {}
22945
- })).map((run) => ({
23539
+ });
23540
+ const runs = page.runs.map((run) => ({
22946
23541
  runId: run.workflowId,
22947
23542
  // `workflowId` is the legacy SDK name for the same public Deepline run
22948
23543
  // identity. Keep it during the compatibility window, but never label the
@@ -22980,11 +23575,24 @@ async function handleRunsList(args) {
22980
23575
  if (run.rootRunId) lines2.push(` root: ${run.rootRunId}`);
22981
23576
  return lines2.join("\n");
22982
23577
  });
23578
+ const nextOffset = (page.offset ?? offset ?? 0) + runs.length;
23579
+ const nextPage = page.hasMore && playName ? `deepline runs list --play ${shellQuote2(playName)}${statusFilter ? ` --status ${shellQuote2(statusFilter)}` : ""} --limit ${page.limit ?? limit ?? 20} --offset ${nextOffset} --json` : null;
23580
+ if (page.hasMore) {
23581
+ lines.push("List truncated \u2014 more runs match this filter.");
23582
+ lines.push(
23583
+ nextPage ? `Next page: ${nextPage}` : "Narrow with --play <play-name> to page through runs."
23584
+ );
23585
+ }
22983
23586
  printCommandEnvelope(
22984
23587
  {
22985
23588
  runs,
22986
23589
  count: runs.length,
23590
+ ...page.total !== void 0 ? { total: page.total } : {},
23591
+ offset: page.offset ?? offset ?? 0,
23592
+ limit: page.limit ?? limit ?? (playName ? 20 : 100),
23593
+ ...page.hasMore !== void 0 ? { has_more: page.hasMore, is_truncated: page.hasMore } : {},
22987
23594
  next: {
23595
+ page: nextPage,
22988
23596
  get: runs[0]?.runId ? `deepline runs get ${runs[0].runId} --json` : null
22989
23597
  },
22990
23598
  render: { sections: [{ title: "runs", lines }] }
@@ -23632,23 +24240,47 @@ async function handlePlayVersions(args) {
23632
24240
  }
23633
24241
  return 0;
23634
24242
  }
24243
+ function formatPlayCostEstimate(estimate) {
24244
+ if (!estimate || estimate.status === "unavailable") {
24245
+ return `unavailable${estimate ? `: ${estimate.message}` : ""}`;
24246
+ }
24247
+ const formatCredits = (value) => value.toLocaleString("en-US", {
24248
+ maximumFractionDigits: value < 1 ? 4 : 2
24249
+ });
24250
+ const credits = estimate.lowCredits === estimate.highCredits ? `${estimate.source === "observed" ? "~" : ""}${formatCredits(estimate.lowCredits)}` : `${formatCredits(estimate.lowCredits)}\u2013${formatCredits(estimate.highCredits)}`;
24251
+ const unit = estimate.unit === "row" ? "output row" : "run";
24252
+ const source = estimate.source === "observed" ? `observed from ${estimate.sampleRunCount} ${estimate.sampleRunCount === 1 ? "run" : "runs"}` : "static estimate";
24253
+ return `${credits} Deepline credits per ${unit} (${source})`;
24254
+ }
23635
24255
  async function handlePlayList(args) {
23636
24256
  const jsonOutput = argsWantJson(args);
24257
+ const showCost = args.includes("--show-cost");
23637
24258
  const client2 = new DeeplineClient();
23638
24259
  const categoriesIndex = args.indexOf("--categories");
23639
24260
  const categories = categoriesIndex >= 0 ? args[categoriesIndex + 1]?.trim() : void 0;
23640
- const plays = await client2.listPlays({
24261
+ const limitIndex = args.indexOf("--limit");
24262
+ const cursorIndex = args.indexOf("--cursor");
24263
+ const limit = limitIndex >= 0 ? Number(args[limitIndex + 1]) : void 0;
24264
+ const cursor = cursorIndex >= 0 ? args[cursorIndex + 1] : void 0;
24265
+ if (limitIndex >= 0 && (!Number.isSafeInteger(limit) || limit < 1) || cursorIndex >= 0 && !cursor?.trim()) {
24266
+ console.error("Pass a positive --limit and a non-empty --cursor.");
24267
+ return 2;
24268
+ }
24269
+ const page = await client2.listPlaysPage({
23641
24270
  categories,
23642
- includeToolCategories: true
23643
- });
23644
- if (jsonOutput) {
23645
- process.stdout.write(`${JSON.stringify(plays)}
23646
- `);
23647
- return 0;
24271
+ includeToolCategories: true,
24272
+ ...showCost ? { includeCostEstimates: true } : {},
24273
+ limit,
24274
+ cursor
24275
+ });
24276
+ const plays = page.plays;
24277
+ const nextPage = page.hasMore && page.cursor ? `deepline plays list${categories ? ` --categories ${shellQuote2(categories)}` : ""}${limit ? ` --limit ${limit}` : ""}${showCost ? " --show-cost" : ""} --cursor ${shellQuote2(page.cursor)} --json` : null;
24278
+ if (page.hasMore && !nextPage) {
24279
+ throw new Error(
24280
+ "Play list reported more results without a continuation cursor"
24281
+ );
23648
24282
  }
23649
- process.stdout.write(`${plays.length} plays available:
23650
-
23651
- `);
24283
+ const lines = [`${plays.length} plays in this page:`];
23652
24284
  for (const play of plays) {
23653
24285
  const flags = [
23654
24286
  play.pinned ? "pinned" : null,
@@ -23658,32 +24290,48 @@ async function handlePlayList(args) {
23658
24290
  play.isDraftDirty ? "draft-dirty" : null
23659
24291
  ].filter(Boolean).join(", ");
23660
24292
  const reference = formatPlayListReference(play);
23661
- process.stdout.write(` ${reference}${flags ? ` [${flags}]` : ""}
23662
- `);
24293
+ lines.push(` ${reference}${flags ? ` [${flags}]` : ""}`);
23663
24294
  if (play.inputSchema || play.hasInputSchema) {
23664
- process.stdout.write(" inputSchema: yes\n");
24295
+ lines.push(" inputSchema: yes");
23665
24296
  }
23666
24297
  if (play.triggerStatus?.cron) {
23667
- process.stdout.write(
23668
- ` scheduled execution: ${cronExecutionLabel(play.triggerStatus.cron)}
23669
- `
24298
+ lines.push(
24299
+ ` scheduled execution: ${cronExecutionLabel(play.triggerStatus.cron)}`
23670
24300
  );
23671
24301
  if (play.triggerStatus.blockedReason) {
23672
- process.stdout.write(
23673
- ` schedule issue: ${play.triggerStatus.blockedReason}
23674
- `
23675
- );
24302
+ lines.push(` schedule issue: ${play.triggerStatus.blockedReason}`);
23676
24303
  }
23677
24304
  } else {
23678
- process.stdout.write(" scheduled execution: not configured\n");
24305
+ lines.push(" scheduled execution: not configured");
23679
24306
  }
23680
24307
  if (play.triggerStatus?.webhook) {
23681
- process.stdout.write(` webhook: ${play.triggerStatus.webhook}
23682
- `);
24308
+ lines.push(` webhook: ${play.triggerStatus.webhook}`);
23683
24309
  }
23684
- process.stdout.write(` run: deepline plays run ${reference} --watch
23685
- `);
24310
+ if (showCost) {
24311
+ lines.push(
24312
+ ` cost estimate: ${formatPlayCostEstimate(play.costEstimate)}`
24313
+ );
24314
+ }
24315
+ lines.push(` run: deepline plays run ${reference} --watch`);
24316
+ }
24317
+ if (page.hasMore) {
24318
+ lines.push("List truncated \u2014 more plays are available.");
24319
+ lines.push(`Next page: ${nextPage}`);
23686
24320
  }
24321
+ printCommandEnvelope(
24322
+ {
24323
+ plays,
24324
+ count: plays.length,
24325
+ has_more: page.hasMore,
24326
+ is_truncated: page.hasMore,
24327
+ next_cursor: page.cursor,
24328
+ next: { page: nextPage },
24329
+ render: {
24330
+ sections: [{ title: "plays", lines }]
24331
+ }
24332
+ },
24333
+ { json: jsonOutput }
24334
+ );
23687
24335
  return 0;
23688
24336
  }
23689
24337
  async function handlePlayPin(args, pinned) {
@@ -23947,76 +24595,23 @@ async function handlePlaySearch(args) {
23947
24595
  }
23948
24596
  return 0;
23949
24597
  }
23950
- function normalizePlayGrepText(value) {
23951
- if (value === null || value === void 0) return "";
23952
- if (typeof value === "string") return value.toLowerCase();
23953
- if (typeof value === "number" || typeof value === "boolean") {
23954
- return String(value).toLowerCase();
23955
- }
23956
- if (Array.isArray(value)) return value.map(normalizePlayGrepText).join(" ");
23957
- if (typeof value === "object") {
23958
- return Object.values(value).map(normalizePlayGrepText).join(" ");
23959
- }
23960
- return "";
23961
- }
23962
- function parsePlayGrepTerms(query, mode) {
23963
- const trimmed = query.trim().toLowerCase();
23964
- if (!trimmed) return [];
23965
- if (mode === "phrase") return [trimmed];
23966
- const matches = trimmed.match(/"([^"]+)"|'([^']+)'|\S+/g) ?? [];
23967
- return matches.map((term) => term.replace(/^["']|["']$/g, "").trim()).filter(Boolean);
23968
- }
23969
- function matchesPlayGrepQuery(value, query, mode) {
23970
- const terms = parsePlayGrepTerms(query, mode);
23971
- if (terms.length === 0) return true;
23972
- const haystack = normalizePlayGrepText(value);
23973
- if (mode === "any") return terms.some((term) => haystack.includes(term));
23974
- return terms.every((term) => haystack.includes(term));
23975
- }
23976
24598
  async function handlePlayGrep(args) {
23977
24599
  const query = args[0]?.trim();
23978
24600
  if (!query) {
23979
- console.error(
23980
- "Usage: deepline plays grep <query> [--mode all|any|phrase] [--compact] [--json]"
23981
- );
24601
+ console.error("Usage: deepline plays grep <query> [--compact] [--json]");
23982
24602
  return 1;
23983
24603
  }
23984
- let mode = "all";
23985
- for (let index = 1; index < args.length; index += 1) {
23986
- const arg = args[index];
23987
- if (arg === "--mode" && args[index + 1]) {
23988
- const rawMode = args[++index].trim().toLowerCase();
23989
- mode = rawMode === "any" || rawMode === "phrase" ? rawMode : "all";
23990
- }
23991
- }
23992
24604
  const compact = args.includes("--compact");
23993
24605
  const client2 = new DeeplineClient();
23994
- const plays = (await client2.listPlays({
23995
- grep: query,
23996
- grepMode: mode
23997
- })).filter(
23998
- (play) => matchesPlayGrepQuery(
23999
- {
24000
- name: play.name,
24001
- reference: play.reference,
24002
- displayName: play.displayName,
24003
- origin: play.origin,
24004
- ownerType: play.ownerType,
24005
- aliases: play.aliases,
24006
- toolCategories: play.toolCategories,
24007
- inputSchema: play.inputSchema,
24008
- outputSchema: play.outputSchema
24009
- },
24010
- query,
24011
- mode
24012
- )
24013
- ).map((play) => summarizePlayListItemForCli(play, { compact }));
24606
+ const plays = (await client2.listPlays({ grep: query })).map(
24607
+ (play) => summarizePlayListItemForCli(play, { compact })
24608
+ );
24014
24609
  if (argsWantJson(args)) {
24015
24610
  printCompactJson({
24016
24611
  plays,
24017
24612
  count: plays.length,
24018
24613
  query,
24019
- grep: { mode, terms: parsePlayGrepTerms(query, mode) }
24614
+ grep: { engine: "convex_full_text" }
24020
24615
  });
24021
24616
  return 0;
24022
24617
  }
@@ -24724,7 +25319,7 @@ Pass-through input flags:
24724
25319
  if (effectiveTarget && passthroughArgs[0] === effectiveTarget) {
24725
25320
  passthroughArgs.shift();
24726
25321
  }
24727
- process.exitCode = await handlePlayRun([
25322
+ const exitCode = await handlePlayRun([
24728
25323
  ...effectiveTarget ? [effectiveTarget] : [],
24729
25324
  ...options.file ? ["--file", options.file] : [],
24730
25325
  ...options.name ? ["--name", options.name] : [],
@@ -24754,6 +25349,11 @@ Pass-through input flags:
24754
25349
  ...options.full ? ["--full"] : [],
24755
25350
  ...passthroughArgs
24756
25351
  ]);
25352
+ recordCliTrace({
25353
+ phase: "cli.play_command_exit",
25354
+ exitCode
25355
+ });
25356
+ process.exitCode = exitCode;
24757
25357
  });
24758
25358
  registerPlayBootstrapCommand(play);
24759
25359
  play.command("get <target>").description(
@@ -24795,17 +25395,22 @@ Examples:
24795
25395
  `
24796
25396
  Notes:
24797
25397
  Inventory command for saved org plays and prebuilt plays. Use search for
24798
- ranked discovery by task or schema.
25398
+ ranked discovery by task or schema. Returns one bounded page with has_more,
25399
+ is_truncated, next_cursor, and a next-page command. Use --limit to choose
25400
+ the page size and --cursor to continue with the same filters.
24799
25401
 
24800
25402
  Examples:
24801
25403
  deepline plays list
24802
25404
  deepline plays search email --json
24803
25405
  `
24804
- ).option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
25406
+ ).option("--limit <count>", "Page size").option("--cursor <cursor>", "Continue a previous page").option("--show-cost", "Include the per-row or per-run cost estimate").option("--json", "Emit JSON output. Also automatic when stdout is piped").option(
24805
25407
  "--categories <categories>",
24806
25408
  "Filter by comma-separated canonical tool categories"
24807
25409
  ).action(async (options) => {
24808
25410
  process.exitCode = await handlePlayList([
25411
+ ...options.limit ? ["--limit", options.limit] : [],
25412
+ ...options.cursor ? ["--cursor", options.cursor] : [],
25413
+ ...options.showCost ? ["--show-cost"] : [],
24809
25414
  ...options.categories ? ["--categories", options.categories] : [],
24810
25415
  ...options.json ? ["--json"] : []
24811
25416
  ]);
@@ -24844,8 +25449,8 @@ Notes:
24844
25449
  because they are trusted, robust plays with maintained contracts. Use describe
24845
25450
  on a result before running it.
24846
25451
  Add --all only when you are specifically looking for a play you created.
24847
- The grep alias is the same ranked retrieval surface with a more literal name
24848
- for agents that are filtering the play registry.
25452
+ Use plays grep to search active org-owned Plays through Convex's indexed
25453
+ authored corpus. It does not search prebuilt or archived Plays.
24849
25454
 
24850
25455
  Examples:
24851
25456
  deepline plays search email
@@ -24863,25 +25468,24 @@ Examples:
24863
25468
  ]);
24864
25469
  });
24865
25470
  addPlaySearchCommand(play.command("search <query>"));
24866
- play.command("grep <query>").description("Literal grep over play names, schemas, and descriptions.").addHelpText(
25471
+ play.command("grep <query>").description(
25472
+ "Search active org-owned Plays through Convex full-text search."
25473
+ ).addHelpText(
24867
25474
  "after",
24868
25475
  `
24869
25476
  Notes:
24870
- Literal registry filtering. Terms are matched case-insensitively against play
24871
- names, references, display names, ownership, and schemas. Use
24872
- --mode phrase for exact phrase matching, --mode any for OR, and the default
24873
- --mode all for AND.
25477
+ Queries use Convex token semantics over the indexed authored Play corpus.
25478
+ Prebuilt and archived Plays are not part of this index.
24874
25479
 
24875
25480
  Examples:
24876
25481
  deepline plays grep email --json
24877
- deepline plays grep "company contact" --mode all --json
25482
+ deepline plays grep "company contact" --compact --json
24878
25483
  deepline plays describe prebuilt/company-to-contact --json
24879
25484
  `
24880
- ).option("--compact", "Emit compact schemas").option("--mode <mode>", "Grep matching mode: all, any, or phrase").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
25485
+ ).option("--compact", "Emit compact schemas").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(async (query, options) => {
24881
25486
  process.exitCode = await handlePlayGrep([
24882
25487
  query,
24883
25488
  ...options.compact ? ["--compact"] : [],
24884
- ...options.mode ? ["--mode", options.mode] : [],
24885
25489
  ...options.json ? ["--json"] : []
24886
25490
  ]);
24887
25491
  });
@@ -30479,7 +31083,7 @@ async function compileConfig(input2) {
30479
31083
  })).config;
30480
31084
  }
30481
31085
  function registerEnrichCommand(program) {
30482
- program.command("enrich").allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").configureHelp({
31086
+ program.command("enrich", { hidden: true }).allowUnknownOption(true).description("Run v1-style CSV enrichment through the V2 play runner.").configureHelp({
30483
31087
  optionTerm(option) {
30484
31088
  return option.long === "--timeout" ? "--timeout SECONDS" : option.flags;
30485
31089
  }
@@ -31542,7 +32146,7 @@ function loadViewerAssets() {
31542
32146
  "viewer"
31543
32147
  )
31544
32148
  ] : [],
31545
- join12(process.cwd(), "src", "lib", "cli", "viewer")
32149
+ join12(process.cwd(), "apps", "deepline-api", "src", "lib", "cli", "viewer")
31546
32150
  ];
31547
32151
  for (const root of candidateRoots2) {
31548
32152
  try {
@@ -31759,7 +32363,7 @@ function addDeprecatedSubcommand(parent, family, subcommand, description) {
31759
32363
  });
31760
32364
  }
31761
32365
  function registerDeprecatedCommands(program) {
31762
- const session = program.command("session").description("Deprecated legacy session command namespace.").allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
32366
+ const session = program.command("session", { hidden: true }).description("Deprecated legacy session command namespace.").allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
31763
32367
  "after",
31764
32368
  `
31765
32369
  Notes:
@@ -31791,7 +32395,7 @@ Examples:
31791
32395
  `Deprecated legacy "deepline session ${subcommand}" command.`
31792
32396
  );
31793
32397
  }
31794
- const backend = program.command("backend").description("Deprecated legacy local backend command namespace.").allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
32398
+ const backend = program.command("backend", { hidden: true }).description("Deprecated legacy local backend command namespace.").allowUnknownOption(true).allowExcessArguments(true).option("--json", "Emit JSON output").argument("[args...]").addHelpText(
31795
32399
  "after",
31796
32400
  `
31797
32401
  Notes:
@@ -32702,7 +33306,7 @@ function renderDeployedListText(payload, requestedStatus) {
32702
33306
  }
32703
33307
  const applied = asString(payload.status_filter_applied) ?? requestedStatus ?? "active";
32704
33308
  lines.push(
32705
- `Status filter: ${applied}${requestedStatus === void 0 && applied === "active" ? " (default)" : ""} \u2014 use --status all to include disabled monitors.`
33309
+ `Status filter: ${applied}${requestedStatus === void 0 && applied === "active" ? " (default)" : ""} \u2014 use --status all to include inactive monitors.`
32706
33310
  );
32707
33311
  const summary = Array.isArray(payload.status_summary) ? payload.status_summary.flatMap((raw) => {
32708
33312
  const item = asRecord2(raw);
@@ -32710,7 +33314,7 @@ function renderDeployedListText(payload, requestedStatus) {
32710
33314
  const count = item ? asFiniteNumber(item.count) : void 0;
32711
33315
  return status && count !== void 0 ? [`${status}: ${count}`] : [];
32712
33316
  }).join(" ") : "";
32713
- if (summary) lines.push(`All lifecycle states: ${summary}`);
33317
+ if (summary) lines.push(`All monitor states: ${summary}`);
32714
33318
  if (payload.has_more === true || payload.is_truncated === true) {
32715
33319
  const nextCursor = asString(payload.next_cursor);
32716
33320
  lines.push(
@@ -33799,11 +34403,16 @@ async function handleMonitorsReactivate(key, options) {
33799
34403
  }
33800
34404
  var MONITOR_VERB_POLL_INTERVAL_MS = 2e3;
33801
34405
  function monitorHealthLine(health) {
34406
+ if (health.refresh?.state === "running") {
34407
+ return `refreshing whole scope \xB7 scanned ${health.scanned} so far`;
34408
+ }
34409
+ const alignmentRequired = health.alignment_required ?? 0;
33802
34410
  return [
33803
34411
  `converged ${health.converged}/${health.scanned}`,
33804
34412
  `needs_repair ${health.needs_repair}`,
33805
34413
  `missing ${health.observed.missing}`,
33806
34414
  `unaudited ${health.observed.unknown + health.observed.unset}`,
34415
+ ...alignmentRequired > 0 ? [`needs_alignment ${alignmentRequired}`] : [],
33807
34416
  `silent ${health.silent}`
33808
34417
  ].join(" \xB7 ");
33809
34418
  }
@@ -33837,7 +34446,11 @@ async function watchMonitorHealth(input2) {
33837
34446
  let last = await input2.client.monitors.health(scope);
33838
34447
  input2.onProgress(last);
33839
34448
  while (Date.now() < deadlineAt) {
33840
- if (last.needs_repair === 0 && last.observed.unknown === 0) return last;
34449
+ const unresolved = last.observed.unknown + last.observed.unset;
34450
+ const alignmentRequired = last.alignment_required ?? 0;
34451
+ if (last.refresh?.state !== "running" && !last.truncated && last.needs_repair === 0 && (unresolved === 0 || alignmentRequired === unresolved)) {
34452
+ return last;
34453
+ }
33841
34454
  await new Promise(
33842
34455
  (resolve21) => setTimeout(resolve21, MONITOR_VERB_POLL_INTERVAL_MS)
33843
34456
  );
@@ -33847,11 +34460,22 @@ async function watchMonitorHealth(input2) {
33847
34460
  return last;
33848
34461
  }
33849
34462
  function monitorHealthText(health) {
33850
- const partial = health.truncated ? " (partial \u2014 scope larger than one read)" : "";
34463
+ const partial = health.refresh?.state === "running" ? " (refreshing \u2014 counts below are not final)" : health.truncated ? " (partial \u2014 scope larger than one read)" : "";
34464
+ const unauditedMonitors = health.unaudited_monitors ?? [];
33851
34465
  const lines = [
33852
34466
  `scope: ${health.scope.fleet_id ?? "workspace"}${partial}`,
33853
34467
  monitorHealthLine(health)
33854
34468
  ];
34469
+ if ((health.batch_operations?.length ?? 0) > 0) {
34470
+ lines.push("");
34471
+ lines.push("batch work in progress:");
34472
+ for (const operation of health.batch_operations ?? []) {
34473
+ const retry = operation.next_attempt_at === void 0 ? "" : ` \xB7 next ${new Date(operation.next_attempt_at).toISOString()}`;
34474
+ lines.push(
34475
+ ` ${operation.kind} ${operation.run_id} \xB7 ${operation.state}${retry}`
34476
+ );
34477
+ }
34478
+ }
33855
34479
  if (health.deactivation_reasons.length > 0) {
33856
34480
  lines.push("");
33857
34481
  lines.push("deactivated by Deepline:");
@@ -33860,6 +34484,29 @@ function monitorHealthText(health) {
33860
34484
  lines.push(` ${entry.reason} (${entry.count})${how}`);
33861
34485
  }
33862
34486
  }
34487
+ if (unauditedMonitors.length > 0) {
34488
+ const reason = {
34489
+ not_yet_audited: "has not been observed yet",
34490
+ missing_provider_resource_id: "has no stored upstream resource id",
34491
+ provider_observation_unknown: "the provider observation is still unknown"
34492
+ };
34493
+ lines.push("");
34494
+ lines.push("unresolved monitor observations:");
34495
+ for (const entry of unauditedMonitors) {
34496
+ lines.push(
34497
+ ` ${entry.monitor_key} ${entry.tool} \u2014 ${reason[entry.reason]}`
34498
+ );
34499
+ }
34500
+ }
34501
+ if ((health.alignment_required ?? 0) > 0) {
34502
+ lines.push("");
34503
+ lines.push(
34504
+ `${health.alignment_required} monitor(s) need provider identity alignment; normal repair intentionally will not guess or create a paid replacement.`
34505
+ );
34506
+ lines.push(
34507
+ `Run the exact inventory audit, then review one safe adoption candidate: deepline-admin monitors reconcile ${health.scope.org_id} --monitor <monitor-key> --environment production --json`
34508
+ );
34509
+ }
33863
34510
  if (health.worst_offenders.length > 0) {
33864
34511
  lines.push("");
33865
34512
  lines.push(
@@ -33924,78 +34571,143 @@ async function handleMonitorsAudit(options) {
33924
34571
  );
33925
34572
  }
33926
34573
  async function handleMonitorsRepair(options) {
33927
- const client2 = new DeeplineClient();
34574
+ return await handleMonitorBatch("repair", {
34575
+ ...options,
34576
+ ...options.fleet?.trim() ? {} : { target: "needs-repair" }
34577
+ });
34578
+ }
34579
+ async function handleMonitorsHealth(options) {
33928
34580
  const fleetId = options.fleet?.trim() || void 0;
33929
- const scope = fleetId ? { fleetId } : {};
33930
- const result = await client2.monitors.repair({
33931
- ...scope,
33932
- ...options.dryRun ? { dryRun: true } : {}
34581
+ const health = await new DeeplineClient().monitors.health(
34582
+ fleetId ? { fleetId } : {}
34583
+ );
34584
+ printCommandEnvelope(health, {
34585
+ json: options.json,
34586
+ text: monitorHealthText(health)
33933
34587
  });
33934
- if (options.dryRun) {
33935
- assertMonitorDryRunAcknowledged(
33936
- result,
33937
- { command: "monitors repair", mutation: "repair" }
34588
+ }
34589
+ function monitorBatchScope(options, kind) {
34590
+ const selectors = [
34591
+ options.target ? "target" : null,
34592
+ options.fleet?.trim() ? "fleet" : null,
34593
+ options.keys?.trim() ? "keys" : null
34594
+ ].filter((value) => value !== null);
34595
+ if (selectors.length === 0 && kind === "audit") {
34596
+ return { kind: "target", target: "all" };
34597
+ }
34598
+ if (selectors.length !== 1) {
34599
+ throw new MonitorsUsageError(
34600
+ `monitors batch ${kind} needs exactly one selector: --target, --fleet, or --keys.`
33938
34601
  );
33939
34602
  }
33940
- let health = result.health;
33941
- if (options.watch && !options.dryRun) {
33942
- const progress = monitorVerbProgressReporter();
33943
- try {
33944
- health = await watchMonitorHealth({
33945
- client: client2,
33946
- ...scope,
33947
- timeoutMs: fleetTimeoutMs(options.timeout),
33948
- onProgress: progress.report
33949
- });
33950
- } finally {
33951
- progress.finish();
33952
- }
34603
+ if (options.target) return { kind: "target", target: options.target };
34604
+ if (options.fleet?.trim())
34605
+ return { kind: "fleet", fleetId: options.fleet.trim() };
34606
+ const monitorKeys = (options.keys ?? "").split(",").map((key) => key.trim()).filter(Boolean);
34607
+ if (monitorKeys.length === 0) {
34608
+ throw new MonitorsUsageError(
34609
+ "--keys needs one or more comma-separated monitor keys."
34610
+ );
33953
34611
  }
33954
- const verb = options.dryRun ? "would queue" : "queued";
33955
- const lines = [
33956
- `${verb} ${result.repair.queued} of ${result.repair.scanned} monitors`
33957
- ];
33958
- for (const item of result.repair.plan.slice(0, REPAIR_PLAN_PREVIEW)) {
33959
- const why = item.deactivation_reason ? ` \xB7 ${item.deactivation_reason}` : "";
33960
- lines.push(
33961
- ` ${item.action.padEnd(6)} ${item.monitor_key} desired ${item.desired} \xB7 observed ${item.observed}${why}`
34612
+ return { kind: "keys", monitorKeys };
34613
+ }
34614
+ function monitorBatchIdempotencyKey(kind, value) {
34615
+ return value?.trim() || `monitor-batch-${kind}-${randomUUID5()}`;
34616
+ }
34617
+ async function handleMonitorBatchDeploy(options) {
34618
+ if (!options.file?.trim()) {
34619
+ throw new MonitorsUsageError(
34620
+ "monitors batch deploy requires --file <monitors.ndjson>."
33962
34621
  );
33963
34622
  }
33964
- if (result.repair.plan.length > REPAIR_PLAN_PREVIEW) {
33965
- lines.push(` \u2026 ${result.repair.plan.length - REPAIR_PLAN_PREVIEW} more`);
34623
+ if (options.dryRun !== true && options.yes !== true) {
34624
+ throw new MonitorsUsageError(
34625
+ "monitors batch deploy changes provider monitors asynchronously. Preview with --dry-run, then confirm with --yes."
34626
+ );
33966
34627
  }
33967
- if (result.repair.unaudited.length > 0) {
33968
- lines.push(
33969
- `${result.repair.unaudited.length} monitors have never been observed \u2014 run 'deepline monitors audit' first.`
34628
+ let manifestNdjson;
34629
+ try {
34630
+ manifestNdjson = readFileSync11(resolve14(options.file), "utf8");
34631
+ } catch (error) {
34632
+ throw new MonitorsUsageError(
34633
+ `Could not read monitor batch manifest '${options.file}': ${error instanceof Error ? error.message : String(error)}`
33970
34634
  );
33971
34635
  }
33972
- if (result.repair.unowned.length > 0) {
33973
- lines.push(
33974
- `${result.repair.unowned.length} monitors disagree but no fleet owns them; repair those with 'deepline monitors deploy' or 'deepline monitors delete'.`
34636
+ if (!manifestNdjson.trim()) {
34637
+ throw new MonitorsUsageError("Monitor batch manifest is empty.");
34638
+ }
34639
+ const idempotencyKey = monitorBatchIdempotencyKey(
34640
+ "deploy",
34641
+ options.idempotencyKey
34642
+ );
34643
+ const result = await new DeeplineClient().monitors.batch.submit({
34644
+ kind: "deploy",
34645
+ manifestNdjson,
34646
+ dryRun: options.dryRun === true,
34647
+ idempotencyKey
34648
+ });
34649
+ const operation = result.operation;
34650
+ printCommandEnvelope(
34651
+ { ...result, idempotency_key: idempotencyKey },
34652
+ {
34653
+ json: options.json,
34654
+ text: [
34655
+ `${result.rejoined ? "rejoined" : "submitted"} deploy batch ${operation.runId}`,
34656
+ `state ${operation.state} \xB7 retry key ${idempotencyKey}`,
34657
+ `inspect: deepline monitors batch get ${operation.runId} --json`
34658
+ ].join("\n") + "\n"
34659
+ }
34660
+ );
34661
+ }
34662
+ async function handleMonitorBatch(kind, options) {
34663
+ if ((kind === "deactivate" || kind === "reactivate" || kind === "repair") && options.dryRun !== true && options.yes !== true) {
34664
+ throw new MonitorsUsageError(
34665
+ `monitors batch ${kind} changes provider monitors asynchronously. Preview with --dry-run, then confirm with --yes.`
33975
34666
  );
33976
34667
  }
33977
- if (result.repair.fenced.length > 0) {
33978
- lines.push(
33979
- `${result.repair.fenced.length} monitors have a provider operation in flight; Deepline is finishing those and repair left them alone.`
34668
+ if (kind === "deactivate" && !options.reason?.trim()) {
34669
+ throw new MonitorsUsageError(
34670
+ "monitors batch deactivate requires --reason (for example insufficient_balance or billing_failure)."
33980
34671
  );
33981
34672
  }
33982
- lines.push("");
33983
- lines.push(monitorHealthText(health));
34673
+ const idempotencyKey = monitorBatchIdempotencyKey(
34674
+ kind,
34675
+ options.idempotencyKey
34676
+ );
34677
+ const result = await new DeeplineClient().monitors.batch.submit({
34678
+ kind,
34679
+ scope: monitorBatchScope(options, kind),
34680
+ dryRun: options.dryRun === true,
34681
+ ...options.reason?.trim() ? { deactivationReason: options.reason.trim() } : {},
34682
+ idempotencyKey
34683
+ });
34684
+ const operation = result.operation;
34685
+ const text = [
34686
+ `${result.rejoined ? "rejoined" : "submitted"} ${operation.kind} batch ${operation.runId}`,
34687
+ `state ${operation.state} \xB7 retry key ${idempotencyKey}`,
34688
+ `inspect: deepline monitors batch get ${operation.runId} --json`
34689
+ ].join("\n");
33984
34690
  printCommandEnvelope(
33985
- { ...result, health },
33986
- { json: options.json, text: lines.join("\n") }
34691
+ { ...result, idempotency_key: idempotencyKey },
34692
+ { json: options.json, text: `${text}
34693
+ ` }
33987
34694
  );
33988
34695
  }
33989
- var REPAIR_PLAN_PREVIEW = 20;
33990
- async function handleMonitorsHealth(options) {
33991
- const fleetId = options.fleet?.trim() || void 0;
33992
- const health = await new DeeplineClient().monitors.health(
33993
- fleetId ? { fleetId } : {}
33994
- );
33995
- printCommandEnvelope(health, {
33996
- json: options.json,
33997
- text: monitorHealthText(health)
34696
+ async function handleMonitorBatchGet(operationId, options) {
34697
+ const result = await new DeeplineClient().monitors.batch.get(operationId, {
34698
+ ...options.cursor ? { cursor: options.cursor } : {}
33998
34699
  });
34700
+ const summary = result.operation.summary ?? {};
34701
+ const text = [
34702
+ `${result.operation.kind} batch ${result.operation.runId}`,
34703
+ `state ${result.operation.state}`,
34704
+ `progress ${JSON.stringify(summary)}`,
34705
+ ...result.results ? [
34706
+ `results ${result.results.page.length}${result.results.isDone ? "" : " (more; pass --cursor)"}`
34707
+ ] : []
34708
+ ].join("\n");
34709
+ printCommandEnvelope(result, { json: options.json, text: `${text}
34710
+ ` });
33999
34711
  }
34000
34712
  function registerMonitorsCommands(program) {
34001
34713
  const monitors = program.command("monitors").description("Discover, deploy, and manage Deepline monitors.").showSuggestionAfterError(true).addHelpText(
@@ -34019,9 +34731,8 @@ Exit codes:
34019
34731
 
34020
34732
  Examples:
34021
34733
  deepline monitors status --json
34022
- deepline monitors available --json # compact list by default
34023
- deepline monitors available --full --json # complete catalog
34024
- deepline monitors available deepline_native.company_radar --json
34734
+ deepline tools list --categories monitors --json
34735
+ deepline tools get deepline_native.company_radar --json
34025
34736
  deepline monitors check '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'
34026
34737
  deepline monitors deploy --dry-run --file monitor.json
34027
34738
  deepline monitors deploy --file monitor.json
@@ -34060,25 +34771,29 @@ Examples:
34060
34771
  withJsonOption(
34061
34772
  monitors.command("repair").description(
34062
34773
  "Converge the monitors whose desired and observed states disagree."
34063
- ).option("--fleet <fleet-id>", "Repair one fleet instead of every monitor").option("--dry-run", "Show what would be queued without queueing it").option("--watch", "Keep polling health until the scope settles").option(
34064
- "--timeout <duration>",
34065
- "How long --watch waits, e.g. 90s, 5m, 1h (default 10m)"
34774
+ ).option("--fleet <fleet-id>", "Repair one fleet instead of every monitor").option(
34775
+ "--dry-run",
34776
+ "Record the bounded repair plan without provider changes"
34777
+ ).option("--yes", "Confirm the asynchronous provider repair").option(
34778
+ "--idempotency-key <key>",
34779
+ "Stable retry key (generated when omitted)"
34066
34780
  ).addHelpText(
34067
34781
  "after",
34068
34782
  `
34069
34783
  Notes:
34070
- Queues the monitors that disagree with the provider onto the same convergence
34071
- loop a fleet sync uses; the provider work happens on Deepline's own schedule.
34072
- Idempotent \u2014 the queue IS the disagreement, so a repeat run queues only what
34073
- is still wrong.
34784
+ This is the short spelling of 'monitors batch repair --target needs-repair'.
34785
+ It handles both direct monitors and Fleet members through one durable
34786
+ service-account worker. An active historical monitor without an upstream id
34787
+ is selected for repair; current Convex writes cannot create that state.
34074
34788
 
34075
- Monitors nobody has observed are never repaired: run 'deepline monitors audit'
34076
- first. Monitors no fleet owns are reported rather than queued; deploy or delete
34077
- those directly.
34789
+ It returns a run id immediately. Inspect retained results with
34790
+ 'deepline monitors batch get <run-id> --json'. Reuse --idempotency-key after
34791
+ a lost response to rejoin the exact run without another provider request.
34078
34792
 
34079
34793
  Examples:
34080
34794
  deepline monitors repair --dry-run
34081
- deepline monitors repair --fleet accounts-eu --watch
34795
+ deepline monitors repair --yes
34796
+ deepline monitors repair --fleet accounts-eu --yes --idempotency-key repair-eu-2026-09-09
34082
34797
  `
34083
34798
  )
34084
34799
  ).action(monitorsAction(handleMonitorsRepair));
@@ -34101,6 +34816,75 @@ Examples:
34101
34816
  `
34102
34817
  )
34103
34818
  ).action(monitorsAction(handleMonitorsHealth));
34819
+ const batch = monitors.command("batch").description("Submit or inspect durable bulk monitor operations.");
34820
+ const batchSelectorOptions = (command) => command.option(
34821
+ "--target <target>",
34822
+ "One of: all, all-active, billing-suspended, needs-repair"
34823
+ ).option("--fleet <fleet-id>", "Select one Fleet").option("--keys <keys>", "Up to 100 comma-separated monitor keys").option(
34824
+ "--dry-run",
34825
+ "Record the same bounded plan without provider changes"
34826
+ ).option(
34827
+ "--idempotency-key <key>",
34828
+ "Stable retry key (generated when omitted)"
34829
+ );
34830
+ withJsonOption(
34831
+ batch.command("deploy").description(
34832
+ "Durably create complete NDJSON monitor definitions through provider bulk create."
34833
+ ).requiredOption(
34834
+ "--file <path>",
34835
+ "NDJSON file of complete monitor definitions"
34836
+ ).option("--dry-run", "Validate and record the durable deploy plan").option("--yes", "Confirm asynchronous provider creation").option(
34837
+ "--idempotency-key <key>",
34838
+ "Stable retry key (generated when omitted)"
34839
+ )
34840
+ ).action(
34841
+ monitorsAction(
34842
+ (options) => handleMonitorBatchDeploy(options)
34843
+ )
34844
+ );
34845
+ withJsonOption(
34846
+ batchSelectorOptions(
34847
+ batch.command("audit").description("Durably audit a selection; defaults to --target all.")
34848
+ )
34849
+ ).action(
34850
+ monitorsAction(
34851
+ (options) => handleMonitorBatch("audit", options)
34852
+ )
34853
+ );
34854
+ withJsonOption(
34855
+ batchSelectorOptions(
34856
+ batch.command("repair").description(
34857
+ "Durably re-audit and converge a selection through the existing lifecycle fences."
34858
+ )
34859
+ ).option("--yes", "Confirm the asynchronous provider repair")
34860
+ ).action(
34861
+ monitorsAction(
34862
+ (options) => handleMonitorBatch("repair", options)
34863
+ )
34864
+ );
34865
+ withJsonOption(
34866
+ batchSelectorOptions(
34867
+ batch.command("deactivate").description(
34868
+ "Durably deactivate a selection through provider batch deletes."
34869
+ )
34870
+ ).requiredOption("--reason <reason>", "Declared deactivation reason").option("--yes", "Confirm the asynchronous provider deactivation")
34871
+ ).action(
34872
+ monitorsAction(
34873
+ (options) => handleMonitorBatch("deactivate", options)
34874
+ )
34875
+ );
34876
+ withJsonOption(
34877
+ batchSelectorOptions(
34878
+ batch.command("reactivate").description("Durably reactivate a selection after a suspension.")
34879
+ ).option("--yes", "Confirm provider reactivation and possible billing")
34880
+ ).action(
34881
+ monitorsAction(
34882
+ (options) => handleMonitorBatch("reactivate", options)
34883
+ )
34884
+ );
34885
+ withJsonOption(
34886
+ batch.command("get <operation-id>").description("Read batch progress and one bounded evidence page.").option("--cursor <cursor>", "Read the next retained evidence page")
34887
+ ).action(monitorsAction(handleMonitorBatchGet));
34104
34888
  withJsonOption(
34105
34889
  monitors.command("status").description("Check whether you have access to Deepline monitors.").addHelpText(
34106
34890
  "after",
@@ -34118,7 +34902,7 @@ Examples:
34118
34902
  )
34119
34903
  ).action(monitorsAction(handleMonitorsStatus));
34120
34904
  withJsonOption(
34121
- monitors.command("available [tool-id]").alias("tools").description(
34905
+ monitors.command("available [tool-id]", { hidden: true }).alias("tools").description(
34122
34906
  "List the monitor types you can deploy, or describe one by tool id."
34123
34907
  ).addHelpText(
34124
34908
  "after",
@@ -34152,8 +34936,9 @@ Examples:
34152
34936
  "after",
34153
34937
  `
34154
34938
  Notes:
34155
- Read-only. --status filters by monitor status: active (default), disabled, or
34156
- all. The output echoes the status filter that was applied. The response reports
34939
+ Read-only. A monitor state is active or inactive; provider disagreement is
34940
+ reported separately by audit/health. The output echoes the state filter that
34941
+ was applied. The response reports
34157
34942
  total (the true registry count, not the page size), returned, has_more, and
34158
34943
  next_cursor. When has_more is true, page with --cursor <next_cursor> until it
34159
34944
  is false \u2014 a "no matching
@@ -34171,7 +34956,7 @@ Examples:
34171
34956
  `
34172
34957
  ).option(
34173
34958
  "--status <status>",
34174
- "Filter by monitor status: active (default), disabled, or all"
34959
+ "Filter by monitor state: active (default), inactive, or all"
34175
34960
  ).option("--limit <n>", "Limit the number of deployed monitors returned").option(
34176
34961
  "--cursor <cursor>",
34177
34962
  "Page past a truncated result using the next_cursor from a prior list response"
@@ -34234,7 +35019,7 @@ Notes:
34234
35019
  Play delivery. The JSON response reports this validation scope explicitly.
34235
35020
  Provider-specific grammar, precedence, enum applicability, examples, and
34236
35021
  update semantics are returned by the live monitor contract. Inspect it with
34237
- \`deepline monitors available deepline_native.company_radar --json\`.
35022
+ \`deepline tools get deepline_native.company_radar --json\`.
34238
35023
  For a bounded urgent Deepline Native preview, use
34239
35024
  controls.execution_type="priority". Deepline injects the upstream marker;
34240
35025
  do not author custom_fields yourself. Priority is capped at ten upstream or
@@ -34272,6 +35057,10 @@ Notes:
34272
35057
  no separate repair command or retry queue: rerun the same deploy/update.
34273
35058
  Scheduled reconciliation can also finish that exact saved cleanup; it never
34274
35059
  creates another monitor.
35060
+ For TamRadar, deliberately changing payload.updates_since is an explicit
35061
+ historical replay request, including when redeploying a disabled monitor.
35062
+ Deepline preserves that selected boundary; filter-only deploys and ordinary
35063
+ reactivation start from a fresh future-only source boundary instead.
34275
35064
  For a bounded urgent Deepline Native preview, set
34276
35065
  controls.execution_type="priority". Deepline injects the provider custom
34277
35066
  field and enforces a ten-slot per-org cap; do not use it for regular or bulk
@@ -34301,6 +35090,11 @@ positionally, via --file <path>, or from stdin with --file -.
34301
35090
  When <patch> includes payload, its top-level fields are merged with the stored
34302
35091
  payload before validation. This lets you change one filter without resending
34303
35092
  domain or radar_type; nested values and arrays replace atomically.
35093
+ For TamRadar, changing payload.updates_since is an explicit historical replay
35094
+ request. Deepline passes that requested boundary upstream, but previously
35095
+ delivered canonical findings are deduplicated before dispatch and billing; only
35096
+ previously unseen historical findings are chargeable. Filter-only updates,
35097
+ repair, and reactivation instead use a fresh future-only source boundary.
34304
35098
  Use {"controls":{"execution_type":"priority"}} only for a bounded urgent
34305
35099
  Deepline Native preview. The ten-radar priority cap applies on update too.
34306
35100
  Use {"controls":{"execution_type":null}} to release priority without
@@ -34387,7 +35181,7 @@ ${MONITOR_FLEET_HELP_EPILOG}`
34387
35181
  Creates a ready-to-review JSON definition. Fleets always sync ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.cadence} and retain
34388
35182
  a missing member for ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.removalGrace} before it is ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.onRemoved}d; those
34389
35183
  policies are not authored in the file. ${MONITOR_FLEET_DOCUMENTATION.stickyMembership} The payload remains the actual monitor payload for the chosen tool, so use
34390
- \`deepline monitors available\` to discover a tool first.
35184
+ \`deepline tools list --categories monitors\` to discover a tool first.
34391
35185
 
34392
35186
  Example:
34393
35187
  deepline monitors fleets init account-job-openings \\
@@ -34564,7 +35358,7 @@ Example:
34564
35358
  const deployed = withJsonOption(
34565
35359
  monitors.command("deployed", { hidden: true }).description("Alias of `monitors list` (plus get/update/delete aliases).").option(
34566
35360
  "--status <status>",
34567
- "Filter by monitor status: active (default), disabled, or all"
35361
+ "Filter by monitor state: active (default), inactive, or all"
34568
35362
  ).option("--limit <n>", "Limit the number of deployed monitors returned").option(
34569
35363
  "--cursor <cursor>",
34570
35364
  "Page past a truncated result using the next_cursor from a prior list response"
@@ -34712,7 +35506,8 @@ function organizationAuthValues(input2) {
34712
35506
  [HOST_URL_ENV]: input2.baseUrl,
34713
35507
  [API_KEY_ENV]: input2.apiKey,
34714
35508
  [ACTIVE_ORG_ID_ENV]: input2.orgId,
34715
- [ACTIVE_ORG_NAME_ENV]: input2.orgName
35509
+ [ACTIVE_ORG_NAME_ENV]: input2.orgName,
35510
+ ...featureEnvValues(input2)
34716
35511
  };
34717
35512
  }
34718
35513
  function organizationCommandContext(input2) {
@@ -34928,6 +35723,7 @@ async function handleOrgSwitch(selection, options) {
34928
35723
  if (!selection && !options.orgId && authScope === "folder" && provenance.scope === "global" && provenance.project.state === "project") {
34929
35724
  const commandContext = resolveCliCommandContext(config);
34930
35725
  const activeOrganization = commandContext.active_organization;
35726
+ const pinnedFeatures = storedHostFeatures(config.baseUrl);
34931
35727
  const project_env_paths2 = saveProjectDeeplineEnvValues(
34932
35728
  {
34933
35729
  [HOST_URL_ENV]: config.baseUrl,
@@ -34935,7 +35731,8 @@ async function handleOrgSwitch(selection, options) {
34935
35731
  ...activeOrganization ? {
34936
35732
  [ACTIVE_ORG_ID_ENV]: activeOrganization.org_id,
34937
35733
  [ACTIVE_ORG_NAME_ENV]: activeOrganization.org_name ?? ""
34938
- } : {}
35734
+ } : {},
35735
+ ...pinnedFeatures !== void 0 ? { [FEATURES_ENV]: pinnedFeatures } : {}
34939
35736
  },
34940
35737
  provenance.project.dir
34941
35738
  );
@@ -35015,11 +35812,15 @@ async function handleOrgSwitch(selection, options) {
35015
35812
  const authTarget = resolveOrgSwitchAuthTarget(authScope, config);
35016
35813
  if (target.is_current) {
35017
35814
  let project_env_paths2 = [];
35815
+ const features = await http.post("/api/v2/auth/cli/status", {
35816
+ api_key: config.apiKey
35817
+ }).then((status) => status.features).catch(() => void 0);
35018
35818
  const authValues2 = organizationAuthValues({
35019
35819
  baseUrl: config.baseUrl,
35020
35820
  apiKey: config.apiKey,
35021
35821
  orgId: target.org_id,
35022
- orgName: target.name
35822
+ orgName: target.name,
35823
+ features
35023
35824
  });
35024
35825
  if (authTarget.kind === "folder") {
35025
35826
  project_env_paths2 = saveProjectDeeplineEnvValues(authValues2);
@@ -35086,7 +35887,8 @@ async function handleOrgSwitch(selection, options) {
35086
35887
  baseUrl: config.baseUrl,
35087
35888
  apiKey: switched.api_key,
35088
35889
  orgId: switched.org_id,
35089
- orgName: switched.org_name
35890
+ orgName: switched.org_name,
35891
+ features: switched.features
35090
35892
  });
35091
35893
  if (authTarget.kind === "folder") {
35092
35894
  project_env_paths = saveProjectDeeplineEnvValues(authValues);
@@ -35158,14 +35960,20 @@ async function handleOrgCreate(name, options) {
35158
35960
  const identity = await fetchWorkspaceCreationIdentity(http, config.apiKey);
35159
35961
  let created;
35160
35962
  let workspaceApiKey;
35963
+ let workspaceFeatures;
35161
35964
  let pendingIntentPath = null;
35162
35965
  if (!identity.orgId) {
35163
35966
  const firstWorkspace = await http.post("/api/v2/auth/cli/org-create", {
35164
35967
  api_key: config.apiKey,
35165
35968
  name: normalizedName
35166
35969
  });
35167
- const { api_key: apiKey, ...publicFirstWorkspace } = firstWorkspace;
35970
+ const {
35971
+ api_key: apiKey,
35972
+ features,
35973
+ ...publicFirstWorkspace
35974
+ } = firstWorkspace;
35168
35975
  workspaceApiKey = apiKey;
35976
+ workspaceFeatures = features;
35169
35977
  created = publicFirstWorkspace;
35170
35978
  } else {
35171
35979
  const pending = loadOrCreatePendingOrgCreate({
@@ -35186,6 +35994,7 @@ async function handleOrgCreate(name, options) {
35186
35994
  org_id: workspace.org_id
35187
35995
  });
35188
35996
  workspaceApiKey = switched.api_key;
35997
+ workspaceFeatures = switched.features;
35189
35998
  created = { ...workspace };
35190
35999
  pendingIntentPath = pending.path;
35191
36000
  }
@@ -35193,7 +36002,8 @@ async function handleOrgCreate(name, options) {
35193
36002
  baseUrl: config.baseUrl,
35194
36003
  apiKey: workspaceApiKey,
35195
36004
  orgId: created.org_id,
35196
- orgName: created.org_name
36005
+ orgName: created.org_name,
36006
+ features: workspaceFeatures
35197
36007
  });
35198
36008
  saveHostEnvValues(config.baseUrl, authValues);
35199
36009
  if (pendingIntentPath) unlinkSync(pendingIntentPath);
@@ -35364,7 +36174,7 @@ Examples:
35364
36174
  )
35365
36175
  ).action(handleOrgSwitch);
35366
36176
  addOrgSetOptions(
35367
- org.command("switch [selection]").description(
36177
+ org.command("switch [selection]", { hidden: true }).description(
35368
36178
  "Deprecated alias for org set. Set the organization for the selected auth scope."
35369
36179
  ).addHelpText(
35370
36180
  "after",
@@ -35390,6 +36200,10 @@ Examples:
35390
36200
  }
35391
36201
 
35392
36202
  // src/cli/commands/git.ts
36203
+ var GIT_DEPLOY_FEATURE = "github-deploy";
36204
+ function shellQuote3(value) {
36205
+ return `'${value.replace(/'/g, `'\\''`)}'`;
36206
+ }
35393
36207
  function renderProject(project) {
35394
36208
  const root = project.rootDirectory ? ` (${project.rootDirectory})` : "";
35395
36209
  const flags = [
@@ -35428,6 +36242,98 @@ async function handleList(options) {
35428
36242
  { json: options.json }
35429
36243
  );
35430
36244
  }
36245
+ function describeInstallationStatus(installation) {
36246
+ switch (installation.status) {
36247
+ case "active":
36248
+ return [
36249
+ `Connected to ${installation.accountLogin} (installation ${installation.installationId}).`
36250
+ ];
36251
+ case "uninstalled":
36252
+ return [
36253
+ `The Deepline GitHub App was uninstalled from ${installation.accountLogin}${installation.uninstalledAt ? ` at ${new Date(installation.uninstalledAt).toISOString()}` : ""}.`,
36254
+ "Deploys are paused for every linked repository. Run `deepline git connect`, then `deepline git link <owner/repo>` again for each repository; links from the old installation cannot be resumed."
36255
+ ];
36256
+ case "suspended":
36257
+ return [
36258
+ `GitHub suspended the Deepline App on ${installation.accountLogin}${installation.suspendedAt ? ` at ${new Date(installation.suspendedAt).toISOString()}` : ""}.`,
36259
+ "Pushes are not delivered while it is suspended; unsuspend it in GitHub settings."
36260
+ ];
36261
+ }
36262
+ }
36263
+ async function handleStatus2(options) {
36264
+ const { http } = getAuthedHttpClient();
36265
+ const payload = await http.get("/api/v2/github/installation");
36266
+ const installation = payload.installation;
36267
+ const others = (payload.installations ?? []).filter(
36268
+ (row) => row.installationId !== installation?.installationId
36269
+ );
36270
+ const lines = installation ? describeInstallationStatus(installation) : [
36271
+ "No GitHub installation is connected to this workspace. Run `deepline git connect`."
36272
+ ];
36273
+ for (const other of others) {
36274
+ lines.push(
36275
+ `Also: ${other.accountLogin} (installation ${other.installationId}) is ${other.status}.`
36276
+ );
36277
+ }
36278
+ const byId = new Map(
36279
+ (payload.installations ?? []).map((row) => [row.installationId, row])
36280
+ );
36281
+ const { projects } = await http.get(
36282
+ "/api/v2/play-projects?includeDisabled=1"
36283
+ );
36284
+ const affected_links = projects.map((project) => {
36285
+ const own = byId.get(project.installationId);
36286
+ const verdict = !own ? "unknown" : own.status === "active" ? null : own.status;
36287
+ return verdict ? {
36288
+ id: project.id,
36289
+ repoId: project.repoId,
36290
+ repoFullName: project.repoFullName,
36291
+ rootDirectory: project.rootDirectory,
36292
+ productionBranch: project.productionBranch,
36293
+ manifestPath: project.manifestPath,
36294
+ checkOnPullRequest: project.checkOnPullRequest,
36295
+ disabled: project.disabled,
36296
+ installationId: project.installationId,
36297
+ installationStatus: verdict
36298
+ } : null;
36299
+ }).filter((row) => row !== null);
36300
+ const activeInstallations = (payload.installations ?? []).filter(
36301
+ (row) => row.status === "active"
36302
+ );
36303
+ const relinkCommand = (link2) => [
36304
+ // The numeric repo id, not the name: a repository renamed while the
36305
+ // App was gone keeps a stale name here, and `git link` accepts the id.
36306
+ `deepline git link ${link2.repoId}`,
36307
+ // Stored values are admin-controlled text and this line is
36308
+ // copy-pasted into a shell by whoever reads it, so every
36309
+ // interpolated value below is quoted.
36310
+ `--branch ${shellQuote3(link2.productionBranch)}`,
36311
+ link2.rootDirectory ? `--root-dir ${shellQuote3(link2.rootDirectory)}` : "",
36312
+ link2.manifestPath && link2.manifestPath !== "deepline.json" ? `--manifest ${shellQuote3(link2.manifestPath)}` : "",
36313
+ link2.checkOnPullRequest ? "" : "--no-pr-checks",
36314
+ // With several active installations no id is guessed: the reader picks.
36315
+ activeInstallations.length > 1 ? "--installation <id>" : ""
36316
+ ].filter(Boolean).join(" ");
36317
+ for (const link2 of affected_links) {
36318
+ const label = `${link2.repoFullName}${link2.rootDirectory ? ` (${link2.rootDirectory})` : ""}`;
36319
+ lines.push(
36320
+ link2.installationStatus !== "suspended" ? `Link again: ${label} \u2014 its installation ${link2.installationId} is ${link2.installationStatus}; run \`${relinkCommand(link2)}\`${activeInstallations.length > 1 ? ` with <id> being the installation that reaches this repo: ${activeInstallations.map((row) => `${row.installationId} (${row.accountLogin})`).join(", ")}` : ""}.` : (
36321
+ // A link disabled during the suspension stays disabled; only an
36322
+ // enabled one picks up again on its own when GitHub unsuspends.
36323
+ link2.disabled ? `Paused and disabled: ${label} \u2014 its installation is suspended, and the link stays disabled after GitHub unsuspends it until you run \`deepline git set ${link2.id} --enabled\`.` : `Paused: ${label} \u2014 its installation is suspended; pushes resume when GitHub unsuspends it.`
36324
+ )
36325
+ );
36326
+ }
36327
+ printCommandEnvelope(
36328
+ {
36329
+ installation,
36330
+ installations: payload.installations ?? [],
36331
+ affected_links,
36332
+ render: { sections: [{ title: "github installation", lines }] }
36333
+ },
36334
+ { json: options.json }
36335
+ );
36336
+ }
35431
36337
  async function handleRepos(options) {
35432
36338
  const { http } = getAuthedHttpClient();
35433
36339
  const query = options.installation ? `?installationId=${encodeURIComponent(options.installation)}` : "";
@@ -35642,7 +36548,7 @@ async function handleDeploymentGet(id, options) {
35642
36548
  );
35643
36549
  }
35644
36550
  function registerGitCommands(program) {
35645
- const git = program.command("git").alias("projects").description(
36551
+ const git = program.command("git", { hidden: !cliFeatureEnabled(GIT_DEPLOY_FEATURE) }).alias("projects").description(
35646
36552
  "Git deploys: link repositories to this workspace and deploy plays on push."
35647
36553
  ).addHelpText(
35648
36554
  "after",
@@ -35658,6 +36564,9 @@ Examples:
35658
36564
  `
35659
36565
  );
35660
36566
  git.command("connect").description("Connect a GitHub installation (browser step).").option("--json", "Emit JSON output").action(async (options) => handleConnect(options));
36567
+ git.command("status").description(
36568
+ "Show the workspace's GitHub installation and whether it is still usable."
36569
+ ).option("--json", "Emit JSON output").action(async (options) => handleStatus2(options));
35661
36570
  git.command("repos").description("List repos the workspace's GitHub installation can reach.").option(
35662
36571
  "--installation <id>",
35663
36572
  "GitHub installation id (required when several are connected)"
@@ -35768,7 +36677,7 @@ async function handleQuickstart(options) {
35768
36677
  return 0;
35769
36678
  }
35770
36679
  function registerQuickstartCommands(program) {
35771
- program.command("quickstart").description(
36680
+ program.command("quickstart", { hidden: true }).description(
35772
36681
  "Deprecated compatibility command. Use /deepline-gtm in your agent."
35773
36682
  ).addHelpText(
35774
36683
  "after",
@@ -35963,18 +36872,25 @@ async function handleList2(options) {
35963
36872
  async function handleCheck(nameInput, options) {
35964
36873
  const name = normalizeSecretName(nameInput);
35965
36874
  const client2 = new DeeplineClient();
35966
- const secret = await client2.checkSecret(name);
36875
+ const check = options.play !== void 0 ? await client2.inspectSecret(name, { playName: options.play }) : null;
36876
+ const secret = check ? check.secret : await client2.checkSecret(name);
36877
+ const ok = check ? check.ok : Boolean(secret);
36878
+ const quotedPlay = `'${(options.play ?? "PLAY").replace(/'/g, `'"'"'`)}'`;
36879
+ const scopeHint = check ? secret?.scope === "play" ? check.reason === "disabled" ? `The Play-scoped secret is disabled and no active org fallback is available. Restore it with: deepline secrets set ${name} --scope play --play ${quotedPlay}` : `The active Play-scoped secret takes precedence over the org secret.${ok ? "" : ` Restore it with: deepline secrets set ${name} --scope play --play ${quotedPlay}`}` : `No Play override.${ok ? " The org secret is available." : ` Set or rotate the org secret with: deepline secrets set ${name}`}` : `Metadata only. Verify effective scope and active version with: deepline secrets check ${name} --play <play-name>`;
35967
36880
  printCommandEnvelope(
35968
36881
  {
35969
- ok: Boolean(secret),
36882
+ ok,
35970
36883
  name,
35971
36884
  secret: secret ?? null,
36885
+ check: check ? { playName: check.playName, reason: check.reason } : null,
36886
+ scopeHint,
35972
36887
  render: {
35973
36888
  sections: [
35974
36889
  {
35975
36890
  title: "secret check",
35976
36891
  lines: [
35977
- secret ? `${name}: active \u2014 ${renderSecret(secret)}` : `${name}: missing, disabled, or empty`
36892
+ check ? `${name}: ${check.reason} for ${check.playName}${secret ? ` (${secret.scope})` : ""}` : secret ? `${name}: active metadata \u2014 ${renderSecret(secret)}` : `${name}: missing, disabled, or empty`,
36893
+ scopeHint
35978
36894
  ]
35979
36895
  }
35980
36896
  ]
@@ -35982,7 +36898,7 @@ async function handleCheck(nameInput, options) {
35982
36898
  },
35983
36899
  { json: options.json }
35984
36900
  );
35985
- if (!secret) process.exitCode = 4;
36901
+ if (!ok) process.exitCode = 4;
35986
36902
  }
35987
36903
  async function handleSet2(nameInput, forbidden, options) {
35988
36904
  preventShellHistoryLeak(forbidden);
@@ -36036,6 +36952,7 @@ Notes:
36036
36952
  Examples:
36037
36953
  deepline secrets list
36038
36954
  deepline secrets check HUBSPOT_TOKEN
36955
+ deepline secrets check HUBSPOT_TOKEN --play hubspot-sync
36039
36956
  deepline secrets set HUBSPOT_TOKEN
36040
36957
  printf %s "$HUBSPOT_TOKEN" | deepline secrets set HUBSPOT_TOKEN --value-stdin
36041
36958
  deepline secrets set HUBSPOT_TOKEN --from-env HUBSPOT_TOKEN
@@ -36045,7 +36962,12 @@ Examples:
36045
36962
  secrets.command("list").description("List secret metadata only.").option("--json", "Emit JSON output").action(async (options) => {
36046
36963
  await handleList2(options);
36047
36964
  });
36048
- secrets.command("check").description("Check whether a secret exists and is active.").argument("<name>", "Secret name").option("--json", "Emit JSON output").action(async (name, options) => {
36965
+ secrets.command("check").description(
36966
+ "Check secret metadata, or effective availability with --play."
36967
+ ).argument("<name>", "Secret name").option(
36968
+ "--play <name>",
36969
+ "Validate the effective secret for a bare Play name and active version"
36970
+ ).option("--json", "Emit JSON output").action(async (name, options) => {
36049
36971
  await handleCheck(name, options);
36050
36972
  });
36051
36973
  secrets.command("set").description("Set or rotate a secret without exposing its value in argv.").argument("<name>", "Secret name").argument("[forbidden...]", "Do not pass secret values here").option("--json", "Emit JSON output").option("--scope <scope>", "Secret scope: org or play", "org").option("--play <name>", "Play name for play-scoped secrets").option("--value-stdin", "Read the value from piped stdin").option("--from-env <name>", "Read the value from an environment variable").option("--from-file <path>", "Read the value from a file").action(
@@ -36202,7 +37124,7 @@ import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as r
36202
37124
  import { homedir as homedir8 } from "os";
36203
37125
  import { dirname as dirname15, join as join16 } from "path";
36204
37126
 
36205
- // ../../shared_libs/cli/install-commands.json
37127
+ // contracts/install-commands.json
36206
37128
  var install_commands_default = {
36207
37129
  skills: {
36208
37130
  index_path: "/.well-known/skills/index.json",
@@ -36310,10 +37232,15 @@ function buildSkillsAddArgs(baseUrl, skillName, options = {}) {
36310
37232
 
36311
37233
  // src/cli/skills-sync.ts
36312
37234
  import { spawn as spawn2, spawnSync } from "child_process";
37235
+ import { randomUUID as randomUUID7 } from "crypto";
36313
37236
  import {
36314
37237
  existsSync as existsSync13,
36315
37238
  mkdirSync as mkdirSync10,
37239
+ readdirSync as readdirSync4,
36316
37240
  readFileSync as readFileSync14,
37241
+ renameSync,
37242
+ rmdirSync,
37243
+ statSync as statSync5,
36317
37244
  unlinkSync as unlinkSync2,
36318
37245
  writeFileSync as writeFileSync14
36319
37246
  } from "fs";
@@ -36438,6 +37365,20 @@ function classifyNetworkFailure(error) {
36438
37365
  }
36439
37366
  return "network_error";
36440
37367
  }
37368
+ function classifyNetworkCauseCategory(error) {
37369
+ switch (classifyNetworkFailure(error)) {
37370
+ case "network_dns_resolution_failed":
37371
+ return "dns";
37372
+ case "network_ssl_error":
37373
+ return "tls";
37374
+ case "network_connection_reset":
37375
+ return "reset";
37376
+ case "network_timeout":
37377
+ return "connect_timeout";
37378
+ default:
37379
+ return "unknown";
37380
+ }
37381
+ }
36441
37382
  function isNetworkFailure(error) {
36442
37383
  if (!(error instanceof Error)) return false;
36443
37384
  if (error instanceof DeeplineError && error.statusCode) return false;
@@ -36607,6 +37548,7 @@ function buildFailureReport(input2) {
36607
37548
  });
36608
37549
  const code = failureCode({ exitCode: input2.exitCode, error: input2.error });
36609
37550
  const errorBody = input2.error === void 0 ? `SDK CLI command exited ${input2.exitCode ?? "unknown"}` : errorMessage3(input2.error);
37551
+ const networkCauseCategory = input2.error !== void 0 && isNetworkFailure(input2.error) ? classifyNetworkCauseCategory(input2.error) : null;
36610
37552
  return {
36611
37553
  command: sanitizeCommand(input2.argv),
36612
37554
  subcommand: subcommandFromArgv(input2.argv),
@@ -36628,7 +37570,8 @@ function buildFailureReport(input2) {
36628
37570
  failure_code: code,
36629
37571
  failure_stage: input2.error === void 0 ? "command_exit" : "cli_main",
36630
37572
  duration_ms: durationMs,
36631
- ...input2.exitCode !== null ? { exit_code: input2.exitCode } : {}
37573
+ ...input2.exitCode !== null ? { exit_code: input2.exitCode } : {},
37574
+ ...networkCauseCategory ? { network_cause_category: networkCauseCategory } : {}
36632
37575
  }
36633
37576
  };
36634
37577
  }
@@ -36656,6 +37599,7 @@ async function maybeReportSdkCliFailure(input2) {
36656
37599
 
36657
37600
  // src/cli/skills-sync.ts
36658
37601
  var CHECK_TIMEOUT_MS2 = 3e3;
37602
+ var SKILLS_SYNC_CLAIM_MAX_AGE_MS = 60 * 60 * 1e3;
36659
37603
  function shouldSkipSkillsSync() {
36660
37604
  if (detectAgentRuntime() === "claude_cowork") {
36661
37605
  return true;
@@ -36672,6 +37616,60 @@ function failedSkillsSyncPath(baseUrl, agents) {
36672
37616
  `skills-sync-failed-${agents.join("-")}-version`
36673
37617
  );
36674
37618
  }
37619
+ function inProgressSkillsSyncPath(baseUrl, agents) {
37620
+ return join15(
37621
+ sdkCliStateDirPath(baseUrl),
37622
+ `skills-sync-in-progress-${agents.join("-")}`
37623
+ );
37624
+ }
37625
+ function removeSkillsSyncClaim(path, token) {
37626
+ try {
37627
+ unlinkSync2(join15(path, token));
37628
+ rmdirSync(path);
37629
+ } catch {
37630
+ }
37631
+ }
37632
+ function claimSkillsSync(baseUrl, agents) {
37633
+ const path = inProgressSkillsSyncPath(baseUrl, agents);
37634
+ try {
37635
+ mkdirSync10(dirname14(path), { recursive: true });
37636
+ } catch {
37637
+ return { status: "unavailable" };
37638
+ }
37639
+ const token = randomUUID7();
37640
+ for (let attempt = 0; attempt < 2; attempt += 1) {
37641
+ const pendingPath = `${path}-pending-${token}`;
37642
+ try {
37643
+ mkdirSync10(pendingPath);
37644
+ writeFileSync14(join15(pendingPath, token), "", { flag: "wx" });
37645
+ renameSync(pendingPath, path);
37646
+ return { status: "claimed", token };
37647
+ } catch {
37648
+ removeSkillsSyncClaim(pendingPath, token);
37649
+ if (!existsSync13(path)) continue;
37650
+ }
37651
+ if (attempt > 0) return { status: "held" };
37652
+ try {
37653
+ if (Date.now() - statSync5(path).mtimeMs <= SKILLS_SYNC_CLAIM_MAX_AGE_MS) {
37654
+ return { status: "held" };
37655
+ }
37656
+ const owners = readdirSync4(path);
37657
+ if (owners.length === 0) {
37658
+ rmdirSync(path);
37659
+ continue;
37660
+ }
37661
+ if (owners.length !== 1) return { status: "held" };
37662
+ renameSync(path, `${path}-stale-${owners[0]}`);
37663
+ } catch {
37664
+ return { status: "held" };
37665
+ }
37666
+ }
37667
+ return { status: "held" };
37668
+ }
37669
+ function clearSkillsSyncClaim(baseUrl, agents, token) {
37670
+ const path = inProgressSkillsSyncPath(baseUrl, agents);
37671
+ removeSkillsSyncClaim(path, token);
37672
+ }
36675
37673
  function hasMarkedSkillsSyncVersion(path, version) {
36676
37674
  return Boolean(version) && readMarkedSkillsSyncVersion(path) === version;
36677
37675
  }
@@ -36807,7 +37805,7 @@ function hasCommand(command) {
36807
37805
  });
36808
37806
  return result.status === 0;
36809
37807
  }
36810
- function shellQuote3(arg) {
37808
+ function shellQuote4(arg) {
36811
37809
  return `'${arg.replace(/'/g, `'\\''`)}'`;
36812
37810
  }
36813
37811
  function temporarySkillsSyncSkipCommand() {
@@ -36826,7 +37824,7 @@ function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NA
36826
37824
  commands.push({
36827
37825
  command: "bunx",
36828
37826
  args: bunxArgs,
36829
- manualCommand: `bunx ${bunxArgs.map(shellQuote3).join(" ")}`
37827
+ manualCommand: `bunx ${bunxArgs.map(shellQuote4).join(" ")}`
36830
37828
  });
36831
37829
  }
36832
37830
  if (hasCommand("npx")) {
@@ -36834,7 +37832,7 @@ function resolveSkillsInstallCommands(baseUrl, skillNames = DEFAULT_SDK_SKILL_NA
36834
37832
  commands.push({
36835
37833
  command: "npx",
36836
37834
  args: npxArgs,
36837
- manualCommand: `npx ${npxArgs.map(shellQuote3).join(" ")}`
37835
+ manualCommand: `npx ${npxArgs.map(shellQuote4).join(" ")}`
36838
37836
  });
36839
37837
  }
36840
37838
  return commands;
@@ -37001,30 +37999,43 @@ async function syncSdkSkillsIfNeeded(baseUrl, options = {}) {
37001
37999
  writeUnavailableSkillsNotice(baseUrl, update.remoteVersion, skillNames);
37002
38000
  return;
37003
38001
  }
37004
- writeSdkSkillsStatusLine("Deepline skills changed; syncing agent skills...");
37005
- const installResult = await runSkillsInstall(installs, agents);
37006
- if (!installResult.installed) {
37007
- const markerWritten = markFailedSkillsSync(
37008
- baseUrl,
37009
- update.remoteVersion,
37010
- agents
38002
+ const claim = claimSkillsSync(baseUrl, agents);
38003
+ if (claim.status === "held") return;
38004
+ try {
38005
+ if (hasFailedSkillsSync(baseUrl, update.remoteVersion, agents) || readSdkSkillsLocalVersion(baseUrl) === update.remoteVersion) {
38006
+ return;
38007
+ }
38008
+ writeSdkSkillsStatusLine(
38009
+ "Deepline skills changed; syncing agent skills..."
37011
38010
  );
37012
- void maybeReportAutomaticSkillsSyncFailure({
37013
- baseUrl,
37014
- remoteSkillsVersion: update.remoteVersion,
37015
- agents,
37016
- skillsPackage: SKILLS_NPX_PACKAGE,
37017
- targetSkillCount: skillNames.length,
37018
- markerWritten,
37019
- attempts: installResult.attempts
37020
- });
37021
- return;
38011
+ const installResult = await runSkillsInstall(installs, agents);
38012
+ if (!installResult.installed) {
38013
+ const markerWritten = markFailedSkillsSync(
38014
+ baseUrl,
38015
+ update.remoteVersion,
38016
+ agents
38017
+ );
38018
+ void maybeReportAutomaticSkillsSyncFailure({
38019
+ baseUrl,
38020
+ remoteSkillsVersion: update.remoteVersion,
38021
+ agents,
38022
+ skillsPackage: SKILLS_NPX_PACKAGE,
38023
+ targetSkillCount: skillNames.length,
38024
+ markerWritten,
38025
+ attempts: installResult.attempts
38026
+ });
38027
+ return;
38028
+ }
38029
+ runLegacySkillsCleanup(agents);
38030
+ writeSdkSkillsLocalVersion(baseUrl, update.remoteVersion, agents);
38031
+ clearUnavailableSkillsNotice(baseUrl);
38032
+ clearFailedSkillsSync(baseUrl, agents);
38033
+ writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
38034
+ } finally {
38035
+ if (claim.status === "claimed") {
38036
+ clearSkillsSyncClaim(baseUrl, agents, claim.token);
38037
+ }
37022
38038
  }
37023
- runLegacySkillsCleanup(agents);
37024
- writeSdkSkillsLocalVersion(baseUrl, update.remoteVersion, agents);
37025
- clearUnavailableSkillsNotice(baseUrl);
37026
- clearFailedSkillsSync(baseUrl, agents);
37027
- writeSdkSkillsStatusLine("Deepline agent skills are up to date.");
37028
38039
  }
37029
38040
 
37030
38041
  // src/cli/commands/skills.ts
@@ -38333,12 +39344,15 @@ Examples:
38333
39344
  });
38334
39345
  }
38335
39346
 
39347
+ // src/cli/commands/settings.ts
39348
+ import { readFileSync as readFileSync20 } from "fs";
39349
+
38336
39350
  // src/cli/update-preferences.ts
38337
39351
  import {
38338
39352
  existsSync as existsSync16,
38339
39353
  mkdirSync as mkdirSync13,
38340
39354
  readFileSync as readFileSync17,
38341
- renameSync,
39355
+ renameSync as renameSync2,
38342
39356
  rmSync as rmSync5,
38343
39357
  writeFileSync as writeFileSync17
38344
39358
  } from "fs";
@@ -38407,7 +39421,7 @@ function writeCliUpdatePreferences(preferences, homeDir2 = homedir10()) {
38407
39421
  encoding: "utf8",
38408
39422
  mode: 384
38409
39423
  });
38410
- renameSync(tempPath, path);
39424
+ renameSync2(tempPath, path);
38411
39425
  } finally {
38412
39426
  rmSync5(tempPath, { force: true });
38413
39427
  }
@@ -38464,7 +39478,7 @@ import {
38464
39478
  mkdirSync as mkdirSync14,
38465
39479
  realpathSync as realpathSync5,
38466
39480
  readFileSync as readFileSync19,
38467
- renameSync as renameSync2,
39481
+ renameSync as renameSync3,
38468
39482
  rmSync as rmSync6,
38469
39483
  unlinkSync as unlinkSync3,
38470
39484
  writeFileSync as writeFileSync18
@@ -38481,7 +39495,7 @@ import {
38481
39495
 
38482
39496
  // src/cli/install-integrity.ts
38483
39497
  import { createRequire } from "module";
38484
- import { existsSync as existsSync17, readFileSync as readFileSync18, statSync as statSync5 } from "fs";
39498
+ import { existsSync as existsSync17, readFileSync as readFileSync18, statSync as statSync6 } from "fs";
38485
39499
  import { isAbsolute as isAbsolute6, join as join19, relative as relative6, resolve as resolve17 } from "path";
38486
39500
  var SDK_SIDECAR_CRITICAL_PACKAGE_FILES = [
38487
39501
  "dist/cli/index.mjs",
@@ -38489,6 +39503,8 @@ var SDK_SIDECAR_CRITICAL_PACKAGE_FILES = [
38489
39503
  "dist/index.d.ts",
38490
39504
  "dist/plays/bundle-play-file.mjs",
38491
39505
  "dist/bundling-sources/shared_libs/observability/telemetry.ts",
39506
+ "dist/bundling-sources/shared_libs/integration-contracts/bettercontact/execution-policy.ts",
39507
+ "dist/bundling-sources/shared_libs/integration-contracts/theirstack/execution-policy.ts",
38492
39508
  "dist/bundling-sources/shared_libs/play-runtime/backend.ts",
38493
39509
  "dist/bundling-sources/shared_libs/plays/bundling/index.ts",
38494
39510
  "dist/bundling-sources/shared_libs/product-notifications/contract.ts",
@@ -38519,7 +39535,7 @@ function parseJson(path) {
38519
39535
  }
38520
39536
  function isFile(path) {
38521
39537
  try {
38522
- return statSync5(path).isFile();
39538
+ return statSync6(path).isFile();
38523
39539
  } catch {
38524
39540
  return false;
38525
39541
  }
@@ -38666,14 +39682,14 @@ function posixShellQuote(value) {
38666
39682
  function windowsCmdQuote(value) {
38667
39683
  return `"${value.replace(/"/g, '""')}"`;
38668
39684
  }
38669
- function shellQuote4(value) {
39685
+ function shellQuote5(value) {
38670
39686
  if (process.platform === "win32") {
38671
39687
  return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : windowsCmdQuote(value);
38672
39688
  }
38673
39689
  return posixShellQuote(value);
38674
39690
  }
38675
39691
  function buildSourceUpdateCommand(sourceRoot) {
38676
- const quotedRoot = shellQuote4(sourceRoot);
39692
+ const quotedRoot = shellQuote5(sourceRoot);
38677
39693
  const cdCommand = process.platform === "win32" ? `cd /d ${quotedRoot}` : `cd ${quotedRoot}`;
38678
39694
  return `${cdCommand} && git fetch origin main --tags && git merge --ff-only origin/main`;
38679
39695
  }
@@ -38685,7 +39701,7 @@ function buildSidecarProjectConfigCommand(versionDir, nodeBin) {
38685
39701
  "fs.mkdirSync(dir,{recursive:true});",
38686
39702
  `fs.writeFileSync(path.join(dir,'package.json'),${JSON.stringify(NPM_SDK_SIDECAR_PACKAGE_JSON)});`
38687
39703
  ].join("");
38688
- return `${shellQuote4(nodeBin)} -e ${shellQuote4(script)} ${shellQuote4(versionDir)}`;
39704
+ return `${shellQuote5(nodeBin)} -e ${shellQuote5(script)} ${shellQuote5(versionDir)}`;
38689
39705
  }
38690
39706
  function sidecarStateDir(input2) {
38691
39707
  const scope = input2.env.DEEPLINE_CONFIG_SCOPE?.trim();
@@ -38748,7 +39764,7 @@ function resolvePythonSidecarUpdatePlan(options) {
38748
39764
  const npmCommand = "npm";
38749
39765
  const registryUrl = sidecarRegistryUrl(hostUrl);
38750
39766
  const versionDir = join20(stateDir, "versions", "<version>");
38751
- const manualCommand = `${buildSidecarProjectConfigCommand(versionDir, nodeBin)} && ${npmCommand} install --prefix ${shellQuote4(versionDir)} --registry ${shellQuote4(registryUrl)} ${NPM_SDK_INSTALL_COMMON_FLAGS.map(shellQuote4).join(" ")} ${shellQuote4(packageSpec)}`;
39767
+ const manualCommand = `${buildSidecarProjectConfigCommand(versionDir, nodeBin)} && ${npmCommand} install --prefix ${shellQuote5(versionDir)} --registry ${shellQuote5(registryUrl)} ${NPM_SDK_INSTALL_COMMON_FLAGS.map(shellQuote5).join(" ")} ${shellQuote5(packageSpec)}`;
38752
39768
  return {
38753
39769
  kind: "python-sidecar",
38754
39770
  stateDir,
@@ -38895,7 +39911,7 @@ function resolveUpdatePlan(options = {}) {
38895
39911
  args,
38896
39912
  ...installPrefix ? { installPrefix } : {},
38897
39913
  ...fallbackRegistryUrl ? { fallbackRegistryUrl } : {},
38898
- manualCommand: `${command} ${args.map(shellQuote4).join(" ")}`
39914
+ manualCommand: `${command} ${args.map(shellQuote5).join(" ")}`
38899
39915
  };
38900
39916
  }
38901
39917
  var AUTO_UPDATE_FAILURE_FILE = ".auto-update-failure.json";
@@ -39163,23 +40179,23 @@ function writeSidecarLauncher(input2) {
39163
40179
  input2.path,
39164
40180
  [
39165
40181
  "#!/usr/bin/env sh",
39166
- `export DEEPLINE_HOST_URL=${shellQuote4(input2.hostUrl)}`,
39167
- `export DEEPLINE_CONFIG_SCOPE=${shellQuote4(input2.scope)}`,
39168
- `if ${criticalPaths.map((path) => `[ ! -f ${shellQuote4(path)} ]`).join(" || ")}; then`,
40182
+ `export DEEPLINE_HOST_URL=${shellQuote5(input2.hostUrl)}`,
40183
+ `export DEEPLINE_CONFIG_SCOPE=${shellQuote5(input2.scope)}`,
40184
+ `if ${criticalPaths.map((path) => `[ ! -f ${shellQuote5(path)} ]`).join(" || ")}; then`,
39169
40185
  ' if [ -n "${DEEPLINE_REAL_BINARY:-}" ] && [ -x "$DEEPLINE_REAL_BINARY" ]; then',
39170
40186
  ' exec "$DEEPLINE_REAL_BINARY" --version=v2 "$@"',
39171
40187
  " fi",
39172
40188
  ' printf "%s\\n" "Deepline SDK CLI install is incomplete. Run \\`deepline update\\` to repair it." >&2',
39173
40189
  " exit 1",
39174
40190
  "fi",
39175
- `if ! ${shellQuote4(input2.nodeBin)} -e ${shellQuote4(esbuildProbe)} ${shellQuote4(versionDir)} >/dev/null 2>&1; then`,
40191
+ `if ! ${shellQuote5(input2.nodeBin)} -e ${shellQuote5(esbuildProbe)} ${shellQuote5(versionDir)} >/dev/null 2>&1; then`,
39176
40192
  ' if [ -n "${DEEPLINE_REAL_BINARY:-}" ] && [ -x "$DEEPLINE_REAL_BINARY" ]; then',
39177
40193
  ' exec "$DEEPLINE_REAL_BINARY" --version=v2 "$@"',
39178
40194
  " fi",
39179
40195
  ' printf "%s\\n" "Deepline SDK CLI install is incomplete. Run \\`deepline update\\` to repair it." >&2',
39180
40196
  " exit 1",
39181
40197
  "fi",
39182
- `exec ${shellQuote4(input2.nodeBin)} ${shellQuote4(input2.entryPath)} "$@"`,
40198
+ `exec ${shellQuote5(input2.nodeBin)} ${shellQuote5(input2.entryPath)} "$@"`,
39183
40199
  ""
39184
40200
  ].join("\n"),
39185
40201
  { encoding: "utf8", mode: 493 }
@@ -39247,7 +40263,7 @@ async function runPythonSidecarUpdatePlan(plan) {
39247
40263
  `.backup-${installedVersion}-${process.pid}-${Date.now()}`
39248
40264
  );
39249
40265
  try {
39250
- renameSync2(finalDir, backupDir);
40266
+ renameSync3(finalDir, backupDir);
39251
40267
  } catch (error) {
39252
40268
  const concurrentlyPublishedFailure = sidecarInstallFailure(finalDir);
39253
40269
  if (!concurrentlyPublishedFailure) {
@@ -39266,7 +40282,7 @@ async function runPythonSidecarUpdatePlan(plan) {
39266
40282
  }
39267
40283
  if (shouldPublishTemp) {
39268
40284
  try {
39269
- renameSync2(tempDir, finalDir);
40285
+ renameSync3(tempDir, finalDir);
39270
40286
  } catch (error) {
39271
40287
  rmSync6(tempDir, { recursive: true, force: true });
39272
40288
  const concurrentlyPublishedFailure = sidecarInstallFailure(finalDir);
@@ -39277,7 +40293,7 @@ async function runPythonSidecarUpdatePlan(plan) {
39277
40293
  let restoreFailure = "";
39278
40294
  if (backupDir && existsSync18(backupDir) && !existsSync18(finalDir)) {
39279
40295
  try {
39280
- renameSync2(backupDir, finalDir);
40296
+ renameSync3(backupDir, finalDir);
39281
40297
  backupDir = null;
39282
40298
  } catch (restoreError) {
39283
40299
  restoreFailure = `; failed to restore previous install: ${restoreError.message}`;
@@ -39297,7 +40313,7 @@ async function runPythonSidecarUpdatePlan(plan) {
39297
40313
  if (backupDir && existsSync18(backupDir)) {
39298
40314
  rmSync6(finalDir, { recursive: true, force: true });
39299
40315
  try {
39300
- renameSync2(backupDir, finalDir);
40316
+ renameSync3(backupDir, finalDir);
39301
40317
  backupDir = null;
39302
40318
  } catch {
39303
40319
  }
@@ -40045,21 +41061,104 @@ function displaySlackTarget(target) {
40045
41061
  function collectEvent(value, previous = []) {
40046
41062
  return [...previous, value];
40047
41063
  }
41064
+ function collectNotificationTarget(value, previous = []) {
41065
+ return [...previous, value];
41066
+ }
41067
+ async function readNotificationConfigurationChange(source, read = (path, encoding) => readFileSync20(path, encoding)) {
41068
+ const label = source === "-" ? "stdin" : source;
41069
+ let text;
41070
+ try {
41071
+ text = await read(source === "-" ? 0 : source, "utf8");
41072
+ } catch (error) {
41073
+ throw new Error(
41074
+ `Could not read notification configuration from ${label}: ${error instanceof Error ? error.message : String(error)}`
41075
+ );
41076
+ }
41077
+ let parsed;
41078
+ try {
41079
+ parsed = JSON.parse(text);
41080
+ } catch (error) {
41081
+ throw new Error(
41082
+ `Notification configuration from ${label} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
41083
+ );
41084
+ }
41085
+ if (typeof parsed.expectedWorkspaceVersion !== "number" || !Number.isInteger(parsed.expectedWorkspaceVersion) || !parsed.change || typeof parsed.change !== "object") {
41086
+ throw new Error(
41087
+ `Notification configuration from ${label} must contain an integer expectedWorkspaceVersion and a change object.`
41088
+ );
41089
+ }
41090
+ return {
41091
+ expectedWorkspaceVersion: parsed.expectedWorkspaceVersion,
41092
+ change: parsed.change
41093
+ };
41094
+ }
40048
41095
  function registerNotificationCommands(program) {
40049
- const notifications = program.command("notifications").description("Choose which Play outcomes notify which people and channels.").addHelpText(
41096
+ const notifications = program.command("notifications").description("Configure notification groups, destinations, and rules.").addHelpText(
40050
41097
  "after",
40051
41098
  `
40052
41099
  Examples:
41100
+ deepline notifications add oncall --to email:oncall@example.com --to slack:#oncall
40053
41101
  deepline notifications events
40054
41102
  deepline notifications slack channels --search pipeline
40055
41103
  deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
40056
41104
  deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
40057
41105
  deepline notifications test pipeline-watchdog
40058
41106
 
40059
- Slack connections are managed in Dashboard \u2192 Integrations. This command only
40060
- chooses the connected Slack channel or member and the events it receives.
41107
+ Each named notification is a group. Repeat --to to give it email and Slack
41108
+ destinations. Add rules from the dashboard or notification configuration. The
41109
+ existing one-Slack-target form with --for remains supported and creates the
41110
+ equivalent compatibility group and rules. Slack connections are managed in
41111
+ Dashboard \u2192 Integrations.
40061
41112
  `
40062
41113
  );
41114
+ const configuration = notifications.command("configuration").description("Read or atomically update notification groups and rules.").addHelpText(
41115
+ "after",
41116
+ `
41117
+ Examples:
41118
+ deepline notifications configuration get --json
41119
+ deepline notifications configuration apply change.json --dry-run --json
41120
+ jq '{expectedWorkspaceVersion: .configuration.workspaceVersion, change: {kind: "delete_group", groupKey: "oncall"}}' current.json | deepline notifications configuration apply - --json
41121
+ `
41122
+ );
41123
+ configuration.command("get").description("Show the effective group-based notification configuration.").option("--json", "Emit JSON output").action(async (options) => {
41124
+ const result = await new DeeplineClient().getNotificationConfiguration();
41125
+ printCommandEnvelope(
41126
+ {
41127
+ ...result,
41128
+ render: {
41129
+ sections: [
41130
+ {
41131
+ title: "notification groups",
41132
+ lines: result.configuration.groups.length ? result.configuration.groups.map(
41133
+ (group) => `${group.name}: ${group.enabled ? "active" : "paused"}`
41134
+ ) : ["No notification groups are configured."]
41135
+ }
41136
+ ]
41137
+ }
41138
+ },
41139
+ { json: options.json }
41140
+ );
41141
+ });
41142
+ configuration.command("apply").argument(
41143
+ "<file>",
41144
+ "JSON file, or - for stdin, containing expectedWorkspaceVersion and one desired change"
41145
+ ).description(
41146
+ "Atomically apply a configuration draft exported by an agent or UI."
41147
+ ).option("--dry-run", "Validate and display the file without saving it").option("--json", "Emit JSON output").action(async (file, options) => {
41148
+ const input2 = await readNotificationConfigurationChange(file);
41149
+ if (options.dryRun) {
41150
+ printCommandEnvelope(
41151
+ { dryRun: true, ...input2 },
41152
+ { json: options.json }
41153
+ );
41154
+ return;
41155
+ }
41156
+ const result = await new DeeplineClient().changeNotificationConfiguration(
41157
+ input2.expectedWorkspaceVersion,
41158
+ input2.change
41159
+ );
41160
+ printCommandEnvelope(result, { json: options.json });
41161
+ });
40063
41162
  notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
40064
41163
  const settings = await new DeeplineClient().getNotifications();
40065
41164
  printCommandEnvelope(
@@ -40115,42 +41214,82 @@ chooses the connected Slack channel or member and the events it receives.
40115
41214
  );
40116
41215
  printCommandEnvelope({ notification: rule }, { json: options.json });
40117
41216
  });
40118
- notifications.command("add").argument("<name>", "Short stable name, for example pipeline-watchdog").requiredOption(
41217
+ notifications.command("add").argument("<name>", "Group name, for example oncall").requiredOption(
40119
41218
  "--to <target>",
40120
- "Target, for example slack:#pipeline-alerts or slack:member:U0123456789"
41219
+ "Target; repeat email:name@example.com, slack:#channel, or slack:member:U0123456789",
41220
+ collectNotificationTarget,
41221
+ []
40121
41222
  ).option(
40122
41223
  "--for <event>",
40123
- "Event ID; repeat for more events. Run notifications events to list them",
41224
+ "Legacy one-Slack-target event ID; repeat for more events",
40124
41225
  collectEvent,
40125
41226
  []
40126
41227
  ).option("--dry-run", "Describe the notification without saving it").option("--json", "Emit JSON output").action(
40127
41228
  async (name, options) => {
40128
- if (!options.for.length) {
40129
- throw new Error("Choose at least one event with --for <event>.");
41229
+ const result = await new DeeplineClient().createNotificationGroup({
41230
+ name,
41231
+ targets: options.to,
41232
+ legacyEvents: options.for,
41233
+ dryRun: options.dryRun
41234
+ });
41235
+ if (result.mode === "dry_run") {
41236
+ printCommandEnvelope(
41237
+ result.compatibilityMode ? {
41238
+ dryRun: true,
41239
+ name: result.name,
41240
+ target: options.to[0],
41241
+ eventTypes: result.legacyEvents
41242
+ } : {
41243
+ dryRun: true,
41244
+ group: result.name,
41245
+ targets: result.targets,
41246
+ compatibilityMode: false
41247
+ },
41248
+ { json: options.json }
41249
+ );
41250
+ return;
40130
41251
  }
40131
- const target = parseSlackTarget(options.to);
40132
- if (options.dryRun) {
41252
+ if (result.mode === "legacy") {
40133
41253
  printCommandEnvelope(
40134
- { dryRun: true, name, target: options.to, eventTypes: options.for },
41254
+ {
41255
+ notification: result.notification,
41256
+ render: {
41257
+ sections: [
41258
+ {
41259
+ title: "notification saved",
41260
+ lines: [name]
41261
+ }
41262
+ ],
41263
+ actions: [
41264
+ {
41265
+ label: "Send a test",
41266
+ command: `deepline notifications test ${name}`
41267
+ }
41268
+ ]
41269
+ }
41270
+ },
40135
41271
  { json: options.json }
40136
41272
  );
40137
41273
  return;
40138
41274
  }
40139
- const result = await new DeeplineClient().createNotification({
40140
- name,
40141
- provider: "slack",
40142
- ...target,
40143
- eventTypes: options.for
40144
- });
40145
41275
  printCommandEnvelope(
40146
41276
  {
40147
- notification: result,
41277
+ ...result,
40148
41278
  render: {
40149
- sections: [{ title: "notification saved", lines: [name] }],
41279
+ sections: [
41280
+ {
41281
+ title: "notification group saved",
41282
+ lines: [
41283
+ result.group.name,
41284
+ `${result.destinations.length} destination${result.destinations.length === 1 ? "" : "s"}`,
41285
+ "0 rules"
41286
+ ]
41287
+ }
41288
+ ],
40150
41289
  actions: [
40151
41290
  {
40152
- label: "Send a test",
40153
- command: `deepline notifications test ${name}`
41291
+ label: "View configuration",
41292
+ command: "deepline notifications configuration get"
40154
41293
  }
40155
41294
  ]
40156
41295
  }
@@ -40268,7 +41407,7 @@ import {
40268
41407
  chmodSync,
40269
41408
  existsSync as existsSync19,
40270
41409
  mkdtempSync,
40271
- readFileSync as readFileSync20,
41410
+ readFileSync as readFileSync21,
40272
41411
  writeFileSync as writeFileSync20
40273
41412
  } from "fs";
40274
41413
  import { tmpdir as tmpdir5 } from "os";
@@ -41137,6 +42276,9 @@ async function findPlayForToolId(client2, toolId) {
41137
42276
  const plays = await client2.searchPlays({ query: requested, compact: true });
41138
42277
  return plays.find((play) => playIdentifiers(play).includes(requested)) ?? null;
41139
42278
  }
42279
+ function isMissingToolMetadataError(error) {
42280
+ return error instanceof DeeplineError && error.statusCode === 404;
42281
+ }
41140
42282
  function playAliasToolErrorMessage(toolId, play) {
41141
42283
  const playName = play.reference ?? play.name;
41142
42284
  return `${toolId} is a play, not a tool.
@@ -41418,10 +42560,12 @@ async function getTool(toolId, options = {}) {
41418
42560
  try {
41419
42561
  tool = await client2.getTool(toolId);
41420
42562
  } catch (error) {
41421
- const play = await findPlayForToolId(client2, toolId);
41422
- if (play) {
41423
- printPlayAliasToolError(toolId, play);
41424
- return 2;
42563
+ if (isMissingToolMetadataError(error)) {
42564
+ const play = await findPlayForToolId(client2, toolId);
42565
+ if (play) {
42566
+ printPlayAliasToolError(toolId, play);
42567
+ return 2;
42568
+ }
41425
42569
  }
41426
42570
  throw error;
41427
42571
  }
@@ -42321,9 +43465,9 @@ function apifySyncRecoveryNext(rawResponse) {
42321
43465
  const getDatasetItemsTool = stringField2(getDatasetItems, "tool");
42322
43466
  const getDatasetItemsPayload = recordField2(getDatasetItems, "payload");
42323
43467
  return {
42324
- getActorRun: `deepline tools execute ${getActorRunTool} --input ${shellQuote5(JSON.stringify(getActorRunPayload))} --json`,
43468
+ getActorRun: `deepline tools execute ${getActorRunTool} --input ${shellQuote6(JSON.stringify(getActorRunPayload))} --json`,
42325
43469
  ...getDatasetItemsTool && Object.keys(getDatasetItemsPayload).length > 0 ? {
42326
- getDatasetItems: `deepline tools execute ${getDatasetItemsTool} --input ${shellQuote5(JSON.stringify(getDatasetItemsPayload))} --json`
43470
+ getDatasetItems: `deepline tools execute ${getDatasetItemsTool} --input ${shellQuote6(JSON.stringify(getDatasetItemsPayload))} --json`
42327
43471
  } : {}
42328
43472
  };
42329
43473
  }
@@ -42409,7 +43553,7 @@ function readJsonArgument(raw, flagName) {
42409
43553
  throw new Error(`Invalid ${flagName} value: empty @file path.`);
42410
43554
  }
42411
43555
  try {
42412
- return readFileSync20(resolveAtFilePath(filePath), "utf8").replace(
43556
+ return readFileSync21(resolveAtFilePath(filePath), "utf8").replace(
42413
43557
  /^\uFEFF/,
42414
43558
  ""
42415
43559
  );
@@ -42496,7 +43640,7 @@ function parseExecuteOptions(args) {
42496
43640
  function safeFileStem(value) {
42497
43641
  return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
42498
43642
  }
42499
- function shellQuote5(value) {
43643
+ function shellQuote6(value) {
42500
43644
  return `'${value.replace(/'/g, `'\\''`)}'`;
42501
43645
  }
42502
43646
  function powerShellQuote(value) {
@@ -42566,7 +43710,7 @@ export default definePlay(${JSON.stringify(playName)}, async (ctx) => {
42566
43710
  path: scriptPath,
42567
43711
  sourceCode: script,
42568
43712
  projectDir,
42569
- macCopyCommand: `mkdir -p ${shellQuote5(projectDir)} && cp ${shellQuote5(scriptPath)} ${shellQuote5(`${projectDir}/${fileName}`)}`,
43713
+ macCopyCommand: `mkdir -p ${shellQuote6(projectDir)} && cp ${shellQuote6(scriptPath)} ${shellQuote6(`${projectDir}/${fileName}`)}`,
42570
43714
  windowsCopyCommand: `New-Item -ItemType Directory -Force -Path ${powerShellQuote(projectDir.replace(/\//g, "\\"))} | Out-Null; Copy-Item -LiteralPath ${powerShellQuote(scriptPath)} -Destination ${powerShellQuote(`${projectDir.replace(/\//g, "\\")}\\${fileName}`)}`
42571
43715
  };
42572
43716
  }
@@ -42593,7 +43737,7 @@ function buildToolExecuteBaseEnvelope(input2) {
42593
43737
  envelope,
42594
43738
  "output"
42595
43739
  );
42596
- const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote5(JSON.stringify(input2.params))} --json`;
43740
+ const inspectCommand = `deepline tools execute ${input2.toolId} --input ${shellQuote6(JSON.stringify(input2.params))} --json`;
42597
43741
  const actions = input2.listConversion ? [
42598
43742
  {
42599
43743
  label: "next",
@@ -42760,19 +43904,18 @@ async function executeTool(args) {
42760
43904
  try {
42761
43905
  metadata = await client2.getTool(parsed.toolId);
42762
43906
  } catch (error) {
42763
- const play = await findPlayForToolId(client2, parsed.toolId);
42764
- if (play) {
42765
- if (argsWantJson(args)) {
42766
- printJsonError(
42767
- new Error(playAliasToolErrorMessage(parsed.toolId, play))
42768
- );
42769
- } else {
42770
- printPlayAliasToolError(parsed.toolId, play);
43907
+ if (isMissingToolMetadataError(error)) {
43908
+ const play = await findPlayForToolId(client2, parsed.toolId);
43909
+ if (play) {
43910
+ if (argsWantJson(args)) {
43911
+ printJsonError(
43912
+ new Error(playAliasToolErrorMessage(parsed.toolId, play))
43913
+ );
43914
+ } else {
43915
+ printPlayAliasToolError(parsed.toolId, play);
43916
+ }
43917
+ return 2;
42771
43918
  }
42772
- return 2;
42773
- }
42774
- if (error instanceof DeeplineError) {
42775
- throw error;
42776
43919
  }
42777
43920
  throw error;
42778
43921
  }
@@ -43414,7 +44557,7 @@ async function handleTail(id, runId, options) {
43414
44557
  }
43415
44558
  }
43416
44559
  function registerWorkflowCommands(program) {
43417
- const workflows = program.command("workflows").description(
44560
+ const workflows = program.command("workflows", { hidden: true }).description(
43418
44561
  "Legacy cloud workflows (now becoming plays). Commands still work; use `workflows transform` to migrate one to a play. " + TRIGGER_REDIRECT
43419
44562
  ).addHelpText(
43420
44563
  "after",
@@ -44234,4 +45377,10 @@ Examples:
44234
45377
  }
44235
45378
  process.exitCode = process.exitCode ?? 0;
44236
45379
  }
44237
- main();
45380
+ var DEV_CLI_AWAIT_MAIN = Symbol.for("@deepline/sdk/dev-cli-await-main");
45381
+ if (globalThis[DEV_CLI_AWAIT_MAIN] !== true) {
45382
+ void main();
45383
+ }
45384
+ export {
45385
+ main
45386
+ };