firedrill-run 2.2.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +178 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +465 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1622 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +61 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,239 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { api } from './auth.js';
3
+ import { CliError } from './http.js';
4
+ import { selectOne, terminalText } from './prompts.js';
5
+ import { selectedWorld } from './world-access.js';
6
+ import { identifier } from './world-contracts.js';
7
+ import { requestInput } from './request-input.js';
8
+ import { choosePaged } from './configuration.js';
9
+ import { compatibilityPageMatches, protocolProfileLines } from '../../contracts/dependency-protocol-contracts.js';
10
+ import { dependencyDecisionCanonical, dependencyDecisionDigest, dependencyDecisionMatches, dependencyDecisionReceiptMatches } from '../../contracts/dependency-decision-contracts.js';
11
+ const actions = new Set(['prepare', 'preparations', 'preparation', 'preparation-decision', 'choose-preparation', 'continue-preparation', 'cancel-preparation', 'compatibility']);
12
+ const object = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
13
+ const interactive = (options) => !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY;
14
+ const invalid = () => new CliError('dependency_response_unconfirmed', 'The dependency response could not be confirmed. Inspect the original preparation before submitting a different decision.');
15
+ const sha = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
16
+ export const isDependencyPreparationAction = (action) => !!action && actions.has(action);
17
+ function preparation(raw, scope) {
18
+ if (!object(raw) || raw.schema !== 'firedrill.dependency-preparation.v1' || raw.world_id !== scope.world || raw.workspace_id !== scope.workspace
19
+ || typeof raw.service_name !== 'string' || typeof raw.state !== 'string' || typeof raw.configuration_changed !== 'boolean'
20
+ || typeof raw.generated_authoring_supported !== 'boolean' || typeof raw.catalog_scan_complete !== 'boolean'
21
+ || !Array.isArray(raw.unresolved_semantics) || raw.unresolved_semantics.some(one => typeof one !== 'string')
22
+ || !Array.isArray(raw.candidates) || raw.candidates.length > 10)
23
+ throw invalid();
24
+ for (const key of ['id', 'actor_id', 'service_id', 'request_key'])
25
+ identifier(String(raw[key]));
26
+ for (const one of raw.candidates)
27
+ if (!object(one) || !sha(one.candidate_id) || typeof one.name !== 'string' || typeof one.version !== 'string'
28
+ || typeof one.all_declared_operations_match !== 'boolean' || !object(one.counts)
29
+ || Object.values(one.counts).some(count => typeof count !== 'number' || !Number.isSafeInteger(count) || count < 0))
30
+ throw invalid();
31
+ return raw;
32
+ }
33
+ function summary(value) {
34
+ return `${terminalText(value.service_name)}: ${terminalText(value.state)}\nPreparation: ${value.id}\n`
35
+ + value.candidates.map(one => `${one.candidate_id} ${terminalText(one.name)} ${terminalText(one.version)} ${one.counts.declaration_match ?? 0} matching operation declarations${one.all_declared_operations_match ? '' : ' (partial)'}`).join('\n')
36
+ + (value.problem ? `\n${terminalText(value.problem)}` : '')
37
+ + (value.configuration_changed ? '\nConnection settings changed. Request a current review before approving.' : '')
38
+ + '\nNo target run or real-account access is started by this preparation.';
39
+ }
40
+ function decisionBody(raw) {
41
+ if (!dependencyDecisionMatches(raw))
42
+ throw new CliError('dependency_decision_invalid', 'Use the exact catalog/generated decision with acknowledgement, continue with its original continuation_digest, or withdraw. No credentials or free text belong in this file.', 2);
43
+ return raw;
44
+ }
45
+ export async function dependencyPreparationCommand(args, options, output, selected) {
46
+ const action = args[0];
47
+ if (!isDependencyPreparationAction(action) || args.length > (['compatibility', 'preparation-decision'].includes(action) ? 3 : action === 'preparations' ? 1 : 2)
48
+ || !['choose-preparation', 'continue-preparation', 'cancel-preparation'].includes(action) && options.file)
49
+ throw new CliError('arguments_invalid', 'Use connections preparation-decision <id> <decision-digest> to read the original decision, or prepare, preparations, preparation, choose-preparation, continue-preparation, cancel-preparation or compatibility.', 2);
50
+ const scope = selected ?? await selectedWorld({ ...options, interactive: interactive(options) });
51
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
52
+ const page = options.page ?? '1';
53
+ if (!/^[1-9][0-9]{0,8}$/.test(page) || (options.search ?? '').length > 200)
54
+ throw new CliError('arguments_invalid', 'Choose a positive page and a short search.', 2);
55
+ const base = scope.prefix + '/dependency-preparation';
56
+ async function readDecision(preparation, digest, expected) {
57
+ const value = await api(`${base}/${preparation}/decisions/${digest}`, auth);
58
+ if (!dependencyDecisionReceiptMatches(value, { actor: scope.owner, workspace: scope.workspace, world: scope.world, preparation }, digest)
59
+ || expected && value.action !== null && value.action !== expected.action)
60
+ throw invalid();
61
+ return value;
62
+ }
63
+ const receiptSummary = (value) => value.state === 'accepted'
64
+ ? `The original decision was accepted (${value.accepted_state}). Preparation may have progressed since then.`
65
+ : value.state === 'not_observed' ? 'No acceptance is recorded yet. A delayed request may still arrive. Continue only with the exact original decision file.'
66
+ : 'The original decision history is unavailable. Keep its digest. Do not replace an uncertain decision.';
67
+ async function freshReviewGuidance(preparationId) {
68
+ const inspect = `firedrill connections preparation ${preparationId} --world ${scope.world} --workspace ${scope.workspace}`;
69
+ let original;
70
+ try {
71
+ original = preparation(await api(`${base}/${preparationId}`, auth), scope);
72
+ if (original.id !== preparationId)
73
+ throw invalid();
74
+ }
75
+ catch (error) {
76
+ if (error instanceof CliError && error.httpStatus === 404)
77
+ return { data: { preparation_id: preparationId, service_id: null,
78
+ preparation_state: null, inspect_command: inspect, fresh_review_command: null },
79
+ readable: 'The original preparation is no longer available. Keep its decision reference; no replacement was started.' };
80
+ throw error;
81
+ }
82
+ const active = ['queued', 'scanning', 'adopting'].includes(original.state);
83
+ const fresh = active ? null : `firedrill connections prepare ${original.service_id} --world ${scope.world} --workspace ${scope.workspace}`;
84
+ return { data: { preparation_id: preparationId, service_id: original.service_id, preparation_state: original.state,
85
+ inspect_command: inspect, fresh_review_command: fresh },
86
+ readable: `Original preparation: ${preparationId}\nService: ${original.service_id}\nInspect: ${inspect}\n`
87
+ + (active
88
+ ? 'The original review or adoption is still active. Resolve its status before starting a fresh compatibility review; do not race it with another catalog choice.'
89
+ : `If current connection access permits it, request a fresh read-only compatibility review: ${fresh}\nThis does not clear the original decision or approve a catalog/generated choice.`) };
90
+ }
91
+ async function outputReceipt(value) {
92
+ output('dependency_decision_receipt', value, receiptSummary(value));
93
+ if (value.state === 'unavailable') {
94
+ const guidance = await freshReviewGuidance(value.preparation_id);
95
+ output('dependency_preparation_recovery', guidance.data, guidance.readable);
96
+ }
97
+ }
98
+ if (action === 'preparation-decision') {
99
+ const preparationId = identifier(args[1]);
100
+ if (!sha(args[2]))
101
+ throw new CliError('arguments_invalid', 'Use connections preparation-decision <preparation-id> <original-decision-digest>.', 2);
102
+ const value = await readDecision(preparationId, args[2]);
103
+ await outputReceipt(value);
104
+ return;
105
+ }
106
+ if (action === 'preparations') {
107
+ const query = new URLSearchParams({ page, q: options.search ?? '', current: 'true' });
108
+ if (options.request)
109
+ query.set('request_key', identifier(options.request));
110
+ const raw = await api(`${base}?${query}`, auth);
111
+ if (!object(raw) || !Array.isArray(raw.results) || raw.results.length > 100 || !Number.isSafeInteger(raw.count))
112
+ throw invalid();
113
+ const rows = raw.results.map(one => preparation(one, scope));
114
+ output('dependency_preparations', { ...raw, results: rows }, (rows.map(one => `${one.id} ${terminalText(one.service_name)} ${terminalText(one.state)}`).join('\n') || 'No matching dependency preparations.') + `\nPage ${raw.page}, ${raw.count} records.`);
115
+ return;
116
+ }
117
+ if (action === 'prepare') {
118
+ const key = options.request ? identifier(options.request) : randomUUID();
119
+ if (options.request) {
120
+ const retained = await api(`${base}?request_key=${key}`, auth);
121
+ if (!object(retained) || !Array.isArray(retained.results) || retained.results.length > 1)
122
+ throw invalid();
123
+ if (retained.results.length) {
124
+ const original = preparation(retained.results[0], scope);
125
+ if (args[1] && original.service_id !== identifier(args[1]))
126
+ throw new CliError('idempotency_conflict', 'This original request belongs to another dependency.', 2);
127
+ output('dependency_preparation', original, summary(original));
128
+ return;
129
+ }
130
+ }
131
+ const service = args[1] ? identifier(args[1]) : interactive(options)
132
+ ? (await choosePaged('Simulated connection', scope.prefix + '/dependencies?mode=simulated', scope, options, one => ({ label: one.name })))?.id : null;
133
+ if (!service)
134
+ throw new CliError('dependency_required', 'Choose a service with connections prepare <service-id>. Select simulated mode before requesting this review.', 2);
135
+ const current = await api(`${scope.prefix}/dependencies/${identifier(service)}`, auth);
136
+ if (!object(current) || !object(current.dependency) || current.dependency.id !== service || current.dependency.mode !== 'simulated'
137
+ || !Number.isSafeInteger(current.configuration_revision) || !Number.isSafeInteger(current.dependency.service_revision))
138
+ throw new CliError('dependency_simulation_choice_required', 'Select simulated mode for this exact current service before requesting preparation.', 2);
139
+ const body = { service_id: service, service_revision: current.dependency.service_revision, configuration_revision: current.configuration_revision };
140
+ output('dependency_preparation_intent', { request_key: key, world_id: scope.world, body }, `Original request: ${key}\nThis compares declarations only. Keep --request-key ${key} for recovery.`);
141
+ const value = preparation(await api(base, { ...auth, method: 'POST', body, idempotencyKey: key }), scope);
142
+ output('dependency_preparation', value, summary(value));
143
+ return;
144
+ }
145
+ const id = identifier(args[1]);
146
+ let originalBody;
147
+ if (options.file) {
148
+ originalBody = decisionBody(JSON.parse(await requestInput(options.file, options.signal, 8192)));
149
+ if (action === 'continue-preparation' && originalBody.action !== 'continue'
150
+ || action === 'cancel-preparation' && originalBody.action !== 'withdraw'
151
+ || action === 'choose-preparation' && !['catalog', 'generated'].includes(originalBody.action))
152
+ throw new CliError('dependency_decision_invalid', 'The decision file does not match this preparation command.', 2);
153
+ const original = await readDecision(id, await dependencyDecisionDigest(originalBody), originalBody);
154
+ if (original.state !== 'not_observed') {
155
+ await outputReceipt(original);
156
+ if (original.state === 'unavailable')
157
+ throw new CliError('dependency_decision_unavailable', 'Keep the original decision for recovery. Nothing was submitted.', 2);
158
+ return;
159
+ }
160
+ }
161
+ const value = preparation(await api(`${base}/${id}`, auth), scope);
162
+ if (value.id !== id)
163
+ throw invalid();
164
+ if (action === 'preparation') {
165
+ output('dependency_preparation', value, summary(value));
166
+ return;
167
+ }
168
+ if (action === 'compatibility') {
169
+ if (!sha(args[2]))
170
+ throw new CliError('arguments_invalid', 'Choose the exact candidate digest shown by the preparation.', 2);
171
+ const raw = await api(`${base}/${id}/compatibility?candidate_id=${args[2]}&page=${page}`, auth);
172
+ if (!compatibilityPageMatches(raw, id, args[2], Number(page)))
173
+ throw invalid();
174
+ const lines = raw.results.map(one => {
175
+ return [`${terminalText(one.name ?? 'Incomplete operation declaration')} ${terminalText(one.state)}${one.reason ? ` ${terminalText(one.reason)}` : ''}`,
176
+ ...one.gaps.map(gap => ' ' + terminalText(gap)),
177
+ ...(one.protocol_metadata ? [
178
+ ` Explicit source mapping: ${one.protocol_metadata.explicit_expectations_match === null ? 'not declared' : one.protocol_metadata.explicit_expectations_match ? 'declarations match, not behavior proof' : 'differs'}`,
179
+ ...protocolProfileLines(one.protocol_metadata.source).map(line => ' Source: ' + terminalText(line)),
180
+ ...protocolProfileLines(one.protocol_metadata.package).map(line => ' Package: ' + terminalText(line)),
181
+ ] : [])].join('\n');
182
+ });
183
+ output('dependency_compatibility', raw, (lines.join('\n') || 'No operation declarations on this page.') + `\nPage ${raw.page}, ${raw.count} declarations. Runtime behavior remains unvalidated.`);
184
+ return;
185
+ }
186
+ let body;
187
+ if (originalBody)
188
+ body = originalBody;
189
+ else if (action === 'continue-preparation') {
190
+ if (!interactive(options))
191
+ throw new CliError('request_file_required', 'Use --file with {"action":"continue","continuation_digest":"<original digest>"}. The current cursor will not be silently substituted during recovery.', 2);
192
+ const cursor = value.continuation_digest;
193
+ if (!sha(cursor))
194
+ throw invalid();
195
+ body = { action: 'continue', continuation_digest: cursor };
196
+ }
197
+ else if (action === 'cancel-preparation')
198
+ body = { action: 'withdraw' };
199
+ else {
200
+ if (!interactive(options))
201
+ throw new CliError('request_file_required', 'Read the preparation and operation declarations, then use --file with an exact decision and --approve-configuration.', 2);
202
+ output('dependency_preparation_review', value, summary(value) + '\n' + value.unresolved_semantics.map(terminalText).join('\n'));
203
+ const choices = value.candidates.filter(one => one.all_declared_operations_match).map(one => ({ value: one.candidate_id, label: `${one.name} ${one.version}`,
204
+ detail: `${one.counts.declaration_match ?? 0} operation declarations match. Behavioral validation remains required.` }));
205
+ if (value.generated_authoring_supported)
206
+ choices.push({ value: 'generated', label: 'Authorize bounded service authoring', detail: 'Only the declared external HTTP/MCP surface. No replacement of agent business logic.' });
207
+ choices.push({ value: 'cancel', label: 'Not now', detail: '' });
208
+ const chosen = await selectOne('Service preparation', choices, options.signal);
209
+ if (chosen === 'cancel')
210
+ return;
211
+ body = decisionBody({ action: chosen === 'generated' ? 'generated' : 'catalog', ...(chosen === 'generated' ? {} : { candidate_id: chosen }), acknowledge_coverage_gaps: true });
212
+ }
213
+ const digest = await dependencyDecisionDigest(body);
214
+ const readCommand = `firedrill connections preparation-decision ${id} ${digest} --world ${scope.world} --workspace ${scope.workspace}`;
215
+ output('dependency_decision_review', { preparation_id: id, decision_digest: digest, decision: body }, `Preparation: ${id}\nDecision: ${digest}\n${dependencyDecisionCanonical(body)}\nCheck original: ${readCommand}\nKeep this exact metadata in a decision file for continuation. Only this connection changes. No simulation starts.`);
216
+ const original = await readDecision(id, digest, body);
217
+ if (original.state !== 'not_observed') {
218
+ await outputReceipt(original);
219
+ if (original.state === 'unavailable')
220
+ throw new CliError('dependency_decision_unavailable', 'Keep the original decision for recovery. Nothing was submitted.', 2);
221
+ return;
222
+ }
223
+ if (!options.approve && (!interactive(options) || !await selectOne('Apply this exact reviewed decision?', [{ value: false, label: 'Not now' }, { value: true, label: 'Apply decision' }], options.signal)))
224
+ throw new CliError('approval_required', 'No decision was submitted. Use --approve-configuration only after reviewing this exact scope.', 2);
225
+ try {
226
+ const result = preparation(await api(`${base}/${id}`, { ...auth, method: 'POST', body }), scope);
227
+ if (result.id !== id || result.actor_id !== scope.owner)
228
+ throw invalid();
229
+ const accepted = await readDecision(id, digest, body);
230
+ if (accepted.state !== 'accepted')
231
+ throw invalid();
232
+ output('dependency_decision_receipt', accepted, receiptSummary(accepted));
233
+ output('dependency_preparation', result, summary(result));
234
+ }
235
+ catch (error) {
236
+ output('dependency_decision_recovery', { preparation_id: id, decision_digest: digest, decision: body }, `The original reply was not confirmed. Do not choose another decision. Read original: ${readCommand}\nContinue only with the exact original decision file after readback.`);
237
+ throw error;
238
+ }
239
+ }
@@ -0,0 +1,104 @@
1
+ import { api } from './auth.js';
2
+ import { CliError } from './http.js';
3
+ import { selectOne, terminalText } from './prompts.js';
4
+ import { identifier } from './world-contracts.js';
5
+ import { checkedToolSetupProposal, toolSetupRequestDigest } from '../../contracts/tool-setup-proposal.js';
6
+ import { startToolGroup } from './tool-groups.js';
7
+ import { toolGroupReads } from './tool-group-reads.js';
8
+ const invalid = () => new CliError('tool_setup_proposal_unconfirmed', 'The original tool selection could not be confirmed for this account and world. No tools were started.', 2);
9
+ export { toolSetupProposalId } from '../../contracts/tool-setup-proposal.js';
10
+ export async function toolSetupProposal(context, id, signal) {
11
+ const key = identifier(id);
12
+ const raw = await api(`${context.prefix}/tool-setup-proposals/${key}`, { actor: context.owner, workspace: context.workspace, signal });
13
+ let value;
14
+ try {
15
+ value = checkedToolSetupProposal(raw);
16
+ }
17
+ catch {
18
+ throw invalid();
19
+ }
20
+ if (value.id !== key || value.actor_id !== context.owner || value.world_id !== context.world
21
+ || value.workspace_id !== context.workspace
22
+ || await toolSetupRequestDigest(value.request) !== value.request_sha256)
23
+ throw invalid();
24
+ return value;
25
+ }
26
+ function draft(value) {
27
+ return { name: value.request.name, duration_seconds: value.request.duration_seconds,
28
+ selections: value.request.selections.map(one => one.release_id !== null
29
+ ? { release_id: identifier(one.release_id), dataset_id: null }
30
+ : { implementation_id: identifier(one.implementation_id), dataset_id: one.dataset_id }) };
31
+ }
32
+ async function existing(context, value, signal, output) {
33
+ const reads = toolGroupReads(context, signal);
34
+ let row;
35
+ try {
36
+ row = await reads.original(value.id);
37
+ }
38
+ catch (error) {
39
+ if (error instanceof CliError && error.httpStatus === 404)
40
+ return false;
41
+ throw new CliError('tool_group_acceptance_unconfirmed', `The original setup could not be read. No replacement was submitted. Use tools setup-status ${value.id} --workspace ${context.workspace}.`, 2);
42
+ }
43
+ if (row.name !== value.request.name || row.duration_seconds !== value.request.duration_seconds
44
+ || row.counts.selected !== value.request.selections.length)
45
+ throw invalid();
46
+ let seen = 0, page = 1;
47
+ while (seen < value.request.selections.length) {
48
+ const members = await reads.members(row.id, page);
49
+ if (members.count !== value.request.selections.length || !members.results.length)
50
+ throw invalid();
51
+ for (const member of members.results) {
52
+ const selected = value.request.selections[seen];
53
+ if (!selected || member.position !== seen || (selected.release_id !== null
54
+ ? member.release_id !== selected.release_id || member.dataset !== null
55
+ : member.implementation_id !== selected.implementation_id || member.release_id !== null
56
+ || (member.dataset?.id ?? null) !== selected.dataset_id))
57
+ throw invalid();
58
+ seen++;
59
+ }
60
+ if (Boolean(members.next) !== (seen < value.request.selections.length))
61
+ throw invalid();
62
+ page++;
63
+ }
64
+ output('tool_setup', row, `${terminalText(row.name)}\nSetup: ${row.id}\n`
65
+ + `${row.counts.selected} selected · ${row.counts.running} running. Original setup retained; no new tools were started.\n`
66
+ + `Use tools setup-members ${row.id} to inspect the original instances.`);
67
+ return true;
68
+ }
69
+ export async function reviewToolSetupProposal(context, id, options, output) {
70
+ const value = await toolSetupProposal(context, id, options.signal);
71
+ // This key belongs to the original recommendation in both the portal and
72
+ // terminal. Reopening its review never substitutes another setup intent.
73
+ if (await existing(context, value, options.signal, output))
74
+ return;
75
+ const selected = draft(value);
76
+ output('tool_setup_proposal', value, [value.request.name,
77
+ ...value.selection_choices.map((one, index) => `${index + 1}. ${one.name} ${one.version}`
78
+ + (one.dataset_id ? ` · ${one.dataset_name ?? 'Saved dataset'}` : ' · packaged data')),
79
+ `Runtime: ${value.request.duration_seconds} seconds.`,
80
+ value.current && value.can_review ? 'Review the combined runtime budget before starting tools.'
81
+ : 'This selection or its access changed. No tools were started.',
82
+ `Original setup request: ${value.id}`,
83
+ ].map(terminalText).join('\n'));
84
+ if (!value.current || !value.can_review)
85
+ return;
86
+ if (options.json || !process.stdin.isTTY || !process.stderr.isTTY) {
87
+ output('tool_setup_review_required', { request_key: value.id, selection: selected, requires_runtime_quotes: true,
88
+ requires_explicit_start_consent: true }, `Review this selection in an interactive conversation with /start-tools ${value.id}. `
89
+ + `For automation, obtain normal runtime quotes for these exact selections, add each usage_quote_id to a setup file and use tools start --file <file> --request-key ${value.id} --approve-tool-change --workspace ${context.workspace} --json. No tools were started.`);
90
+ return;
91
+ }
92
+ const review = await selectOne('Review the selected tools and runtime budget?', [
93
+ { value: false, label: 'Not now' }, { value: true, label: 'Review budget', detail: 'Starting still needs your explicit budget approval.' },
94
+ ], options.signal);
95
+ if (!review) {
96
+ output('tool_setup_kept', { proposal_id: value.id }, `Selection kept. Use /start-tools ${value.id} when ready.`);
97
+ return;
98
+ }
99
+ await startToolGroup(context, selected, { ...options, workspace: context.workspace, request: value.id }, output, async () => {
100
+ const current = await toolSetupProposal(context, value.id, options.signal);
101
+ if (!current.current || !current.can_review || current.request_sha256 !== value.request_sha256)
102
+ throw new CliError('tool_setup_proposal_changed', 'The selected inputs or access changed during review. No setup was submitted.', 2);
103
+ });
104
+ }
@@ -0,0 +1,225 @@
1
+ import { api } from './auth.js';
2
+ import { CliError } from './http.js';
3
+ import { selectOne } from './prompts.js';
4
+ import { selectedWorld } from './world-access.js';
5
+ import { identifier } from './world-contracts.js';
6
+ import { workspaceLink } from './browser-links.js';
7
+ import { nativeMetadataCanonical } from '../../contracts/local-value-source-contracts.js';
8
+ import { planningDriverMatches } from '../../contracts/planning-contracts.js';
9
+ import { driverReviewMatches, driverReviewActionsMatch, driverReviewSourcesMatch } from '../../contracts/driver-review-contracts.js';
10
+ const interactive = (options) => !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY;
11
+ const auth = (context, signal) => ({ actor: context.owner, workspace: context.workspace, signal });
12
+ const problem = () => new CliError('driver_review_changed', 'The saved input recipe could not be confirmed. Read its current review before approving input.', 2);
13
+ const display = (value, omitted = false) => omitted ? 'Omitted from this presentation'
14
+ : value === null ? 'Not declared' : value.length > 600 ? value.slice(0, 600) + ' [display shortened; full authorized presentation is available with --json]' : value;
15
+ const command = (context, review, view = 'scope', baseline) => `firedrill driver review ${review.id} --world ${context.world} --workspace ${context.workspace}`
16
+ + (view === 'scope' ? '' : ` --view ${view}`) + (baseline ? ` --baseline ${baseline.id}` : '');
17
+ const reviewLink = (context, review, baseline) => workspaceLink(context.workspace, `/worlds/${context.world}/plan#driver-recipe`, {
18
+ driver_review: review.id, driver_review_sha256: review.review_sha256, ...(baseline ? { driver_baseline: baseline.id } : {})
19
+ });
20
+ function locator(value) {
21
+ if (!value)
22
+ return '';
23
+ return `${value.kind}: ${display(value.value, value.value === null && value.presentation_omitted)}`
24
+ + (value.name === null ? '' : ` (${value.name})`) + (value.exact ? ' [exact]' : '')
25
+ + (value.frames.length ? `; frames: ${value.frames.join(' > ')}` : '')
26
+ + (value.presentation_omitted ? '; some presentation fields omitted' : '');
27
+ }
28
+ function completion(review) {
29
+ const value = review.completion;
30
+ if (!value)
31
+ return 'Completion details unavailable.';
32
+ return [`Completion: ${value.label}`, value.locator ? `Marker: ${locator(value.locator)}` : '',
33
+ value.result_locator ? `Result: ${locator(value.result_locator)}` : '',
34
+ value.page === null ? '' : `Completion page: ${value.page + 1}`,
35
+ value.attribute === null ? '' : `Marker attribute: ${value.attribute}`,
36
+ value.literal_value_omitted ? 'Literal completion values are omitted.' : ''].filter(Boolean).join('\n');
37
+ }
38
+ export function driverReviewText(review) {
39
+ return [`Prepared input: ${review.kind.replaceAll('_', ' ')} · ${review.revision}`,
40
+ `${review.current ? 'Current' : 'Historical'} recipe${review.available ? '' : ' · details unavailable'}`,
41
+ ...review.scope.map(one => `${one.label}: ${display(one.value, one.omitted)}`), completion(review),
42
+ `${review.action_count} declared actions · ${review.source_count} source references`,
43
+ review.omitted_count ? 'Some literal or private fields are omitted. A hidden difference is not proof that the complete recipe is unchanged.' : '',
44
+ 'Reading this recipe does not approve input or execution.'].filter(Boolean).join('\n');
45
+ }
46
+ function scopeChanges(review, baseline) {
47
+ const before = new Map(baseline.scope.map(one => [one.name, one]));
48
+ const after = new Map(review.scope.map(one => [one.name, one]));
49
+ const lines = [];
50
+ for (const name of new Set([...before.keys(), ...after.keys()])) {
51
+ const old = before.get(name), current = after.get(name);
52
+ if (nativeMetadataCanonical(old ?? null) === nativeMetadataCanonical(current ?? null))
53
+ continue;
54
+ lines.push(`${current?.label ?? old.label}: ${old ? display(old.value, old.omitted) : 'Not declared'} -> ${current ? display(current.value, current.omitted) : 'Not declared'}`);
55
+ }
56
+ if (nativeMetadataCanonical(baseline.completion) !== nativeMetadataCanonical(review.completion))
57
+ lines.push(`Completion changed.\nBefore: ${completion(baseline)}\nAfter: ${completion(review)}`);
58
+ if (baseline.kind !== review.kind)
59
+ lines.push(`Driver: ${baseline.kind} -> ${review.kind}`);
60
+ return ['Scope and completion changes', ...(lines.length ? lines : ['No change in the visible scope or completion fields.']),
61
+ 'Comparison covers retained declarations, not observed behavior or an applied source change. Omitted values may differ.'].join('\n');
62
+ }
63
+ async function savedReview(context, id, signal) {
64
+ const reviewId = identifier(id);
65
+ const value = await api(`${context.prefix}/driver-reviews/${reviewId}`, auth(context, signal));
66
+ if (!driverReviewMatches(value, context.world, { id: reviewId }))
67
+ throw problem();
68
+ return value;
69
+ }
70
+ export async function currentDriverReview(context, driver, configurationRevision, signal) {
71
+ const value = await api(`${context.prefix}/driver-review?contract_sha256=${encodeURIComponent(driver.contract_sha256)}`, auth(context, signal));
72
+ if (!driverReviewMatches(value, context.world, { contract: driver.contract_sha256 }) || !value.current || !value.available
73
+ || value.kind !== driver.kind || value.revision !== driver.revision || value.configuration.revision !== configurationRevision
74
+ || nativeMetadataCanonical(value.basis) !== nativeMetadataCanonical(driver.basis))
75
+ throw problem();
76
+ return value;
77
+ }
78
+ async function selectedBaseline(context, review, id, signal) {
79
+ if (!id)
80
+ return null;
81
+ const baseline = await savedReview(context, id, signal);
82
+ if (!baseline.available || baseline.id === review.id)
83
+ throw new CliError('driver_review_baseline_invalid', 'Choose a different earlier retained recipe from the same world.', 2);
84
+ return baseline;
85
+ }
86
+ async function entries(context, review, kind, options, signal) {
87
+ if (!review.available)
88
+ throw new CliError('driver_review_unavailable', 'The original input recipe details are no longer available.', 2);
89
+ const query = new URLSearchParams({ review_sha256: review.review_sha256, page: String(options.page), page_size: String(options.pageSize),
90
+ q: options.search, change: options.change });
91
+ if (options.baseline)
92
+ query.set('baseline', options.baseline.id);
93
+ const value = await api(`${context.prefix}/driver-reviews/${review.id}/${kind}?${query}`, auth(context, signal));
94
+ const selection = { baseline: options.baseline?.id ?? null, query: options.search, change: options.change, page: options.page };
95
+ if (!(kind === 'actions' ? driverReviewActionsMatch(value, context.world, review, selection)
96
+ : driverReviewSourcesMatch(value, context.world, review, selection)))
97
+ throw problem();
98
+ const page = value;
99
+ if (page.page_size !== options.pageSize || page.baseline_review_sha256 !== (options.baseline?.review_sha256 ?? null))
100
+ throw problem();
101
+ return page;
102
+ }
103
+ function actionText(value) {
104
+ return [`${value.position}. ${value.id}: ${value.action} (${value.effect}, page ${value.page + 1}, ${value.timeout_ms}ms)`,
105
+ value.locator ? locator(value.locator) : '', value.path === null ? '' : `Path: ${value.path}`,
106
+ value.input_path === null ? '' : `Input field: ${value.input_path}`, value.output === null ? '' : `Output field: ${value.output}`,
107
+ value.point === null ? '' : `Point: ${value.point.join(', ')}`,
108
+ value.has_literal_value ? 'Literal input value omitted.' : '', value.presentation_omitted ? 'Some presentation fields omitted.' : ''].filter(Boolean).join('\n');
109
+ }
110
+ function sourceText(value) {
111
+ return `${value.root ?? '[root omitted]'}/${value.path ?? '[path omitted]'}${value.line === null ? '' : ':' + value.line}`
112
+ + ` · ${value.sha256.slice(0, 12)}` + (value.presentation_omitted ? ' · details omitted' : '');
113
+ }
114
+ function pageText(kind, page) {
115
+ const rows = page.results.map(row => {
116
+ const render = (value) => 'action' in value ? actionText(value) : sourceText(value);
117
+ return [`${row.key} · ${row.change}`, row.before ? 'Before\n' + render(row.before) : '',
118
+ row.after ? (row.before ? 'After\n' : '') + render(row.after) : '',
119
+ row.omitted_change ? 'The retained definition changed in omitted fields.' : '',
120
+ 'order_changed' in row && row.order_changed ? 'Action order changed.' : ''].filter(Boolean).join('\n');
121
+ });
122
+ return [`${kind === 'actions' ? 'Declared actions' : 'Source references'} · page ${page.page} · ${page.count} matching entries`,
123
+ ...(rows.length ? rows : ['No entries match this page and filter.'])].join('\n\n');
124
+ }
125
+ async function browse(context, review, kind, baseline, options, output) {
126
+ let page = 1;
127
+ while (true) {
128
+ const result = await entries(context, review, kind, { page, pageSize: 20, search: '', change: 'all', baseline }, options.signal);
129
+ output('driver_review_' + kind, { review_id: review.id, review_sha256: review.review_sha256, page: result }, pageText(kind, result));
130
+ const choices = [{ value: 'back', label: 'Back to input review' }];
131
+ if (result.next)
132
+ choices.push({ value: 'next', label: 'Next page' });
133
+ if (page > 1)
134
+ choices.push({ value: 'previous', label: 'Previous page' });
135
+ const choice = await selectOne('Recipe details', choices, options.signal);
136
+ if (choice === 'back')
137
+ return;
138
+ page += choice === 'next' ? 1 : -1;
139
+ }
140
+ }
141
+ export async function reviewDriverInput(context, setup, options, output) {
142
+ const driver = setup.execution_driver;
143
+ if (!driver?.requires_input)
144
+ return;
145
+ const inspectCommand = `firedrill driver review --world ${context.world} --workspace ${context.workspace} --contract-sha256 ${driver.contract_sha256}`;
146
+ let review;
147
+ try {
148
+ review = await currentDriverReview(context, driver, setup.configuration_revision, options.signal);
149
+ }
150
+ catch (error) {
151
+ if (error instanceof CliError && ['driver_review_unavailable', 'driver_review_changed'].includes(error.code))
152
+ output('driver_review_required', { world_id: context.world, contract_sha256: driver.contract_sha256, command: inspectCommand }, `The current readable input recipe is unavailable or changed.\n${inspectCommand}`);
153
+ throw error;
154
+ }
155
+ output('driver_review', { review, command: inspectCommand, actions_command: command(context, review, 'actions'),
156
+ sources_command: command(context, review, 'sources'), web_url: reviewLink(context, review) }, driverReviewText(review) + '\n\n' + command(context, review, 'actions') + '\n' + command(context, review, 'sources'));
157
+ if (!interactive(options) || options.approveAgentInput)
158
+ return;
159
+ while (true) {
160
+ const choices = [{ value: 'continue', label: 'Continue to input approval' },
161
+ { value: 'actions', label: 'Inspect declared actions' }, { value: 'sources', label: 'Inspect source references' }];
162
+ if (review.previous?.available)
163
+ choices.push({ value: 'before', label: 'Compare with the previous retained recipe' });
164
+ choices.push({ value: 'cancel', label: 'Cancel' });
165
+ const choice = await selectOne('Prepared input recipe', choices, options.signal);
166
+ if (choice === 'continue')
167
+ return;
168
+ if (choice === 'cancel')
169
+ throw new CliError('canceled', 'No input approval or new plan was saved.', 130);
170
+ if (choice === 'before') {
171
+ const baseline = await selectedBaseline(context, review, review.previous.id, options.signal);
172
+ if (!baseline)
173
+ throw problem();
174
+ output('driver_review_comparison', { review, baseline }, 'Before\n' + driverReviewText(baseline) + '\n\nAfter\n' + driverReviewText(review) + '\n\n' + scopeChanges(review, baseline));
175
+ const detail = await selectOne('Recipe changes', [{ value: 'back', label: 'Back to current recipe' },
176
+ { value: 'actions', label: 'Inspect action changes' }, { value: 'sources', label: 'Inspect source changes' }], options.signal);
177
+ if (detail !== 'back')
178
+ await browse(context, review, detail, baseline, options, output);
179
+ }
180
+ else
181
+ await browse(context, review, choice, null, options, output);
182
+ }
183
+ }
184
+ function integer(value, fallback, maximum) {
185
+ if (value === undefined)
186
+ return fallback;
187
+ if (!/^[1-9][0-9]*$/.test(value) || !Number.isSafeInteger(Number(value)) || Number(value) > maximum)
188
+ throw new CliError('arguments_invalid', `Choose a page value from 1 to ${maximum}.`, 2);
189
+ return Number(value);
190
+ }
191
+ export async function driverReviewCommand(args, options, output) {
192
+ if (args[0] !== 'review' || args.length > 2 || args[1] && options.contract)
193
+ throw new CliError('arguments_invalid', 'Use driver review [review-id]. Use --contract-sha256 only for the current recipe.', 2);
194
+ const view = options.view ?? 'scope', change = options.change ?? 'all', search = (options.search ?? '').trim();
195
+ if (!['scope', 'actions', 'sources'].includes(view) || !['all', 'added', 'removed', 'changed', 'unchanged'].includes(change)
196
+ || search.length > 200 || /[\x00-\x1f\x7f]/.test(search)
197
+ || change !== 'all' && !options.baseline
198
+ || view === 'scope' && (options.page !== undefined || options.pageSize !== undefined || options.search !== undefined || options.change !== undefined))
199
+ throw new CliError('arguments_invalid', 'Choose --view scope, actions or sources. Page/search filters apply only to actions or sources; change filters also need an explicit --baseline.', 2);
200
+ const context = await selectedWorld({ workspace: options.workspace, world: options.world, interactive: interactive(options),
201
+ openBrowser: options.openBrowser, signal: options.signal });
202
+ let review;
203
+ if (args[1])
204
+ review = await savedReview(context, args[1], options.signal);
205
+ else {
206
+ if (options.contract && !/^[a-f0-9]{64}$/.test(options.contract))
207
+ throw new CliError('arguments_invalid', 'Use the exact canonical driver digest from current planning.', 2);
208
+ const setup = await api(`${context.prefix}/simulation-plan`, auth(context, options.signal));
209
+ if (!planningDriverMatches(setup) || !setup.execution_driver || options.contract && setup.execution_driver.contract_sha256 !== options.contract)
210
+ throw problem();
211
+ review = await currentDriverReview(context, setup.execution_driver, setup.configuration_revision, options.signal);
212
+ }
213
+ const baseline = await selectedBaseline(context, review, options.baseline, options.signal);
214
+ const data = { world_id: context.world, review, baseline, web_url: reviewLink(context, review, baseline),
215
+ actions_command: command(context, review, 'actions', baseline), sources_command: command(context, review, 'sources', baseline) };
216
+ const readable = (baseline ? 'Before\n' + driverReviewText(baseline) + '\n\nAfter\n' : '') + driverReviewText(review)
217
+ + (baseline ? '\n\n' + scopeChanges(review, baseline) : '');
218
+ if (view === 'scope') {
219
+ output('driver_review', data, readable + '\n\n' + data.actions_command + '\n' + data.sources_command);
220
+ return;
221
+ }
222
+ const result = await entries(context, review, view, { page: integer(options.page, 1, 10000),
223
+ pageSize: integer(options.pageSize, 20, 100), search, change, baseline }, options.signal);
224
+ output('driver_review_' + view, { ...data, page: result }, readable + '\n\n' + pageText(view, result));
225
+ }