firedrill-run 2.2.4 → 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,311 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open } from 'node:fs/promises';
4
+ import { resolve } from 'node:path';
5
+ import { api } from './auth.js';
6
+ import { CliError } from './http.js';
7
+ import { selectMany, selectOne, terminalText } from './prompts.js';
8
+ import { identifier } from './world-contracts.js';
9
+ import { selectedWorkspace } from './world-access.js';
10
+ import { catalogRead, catalogPageMatches } from './catalog-contracts.js';
11
+ import { packageRead, toolVersionPageMatches } from './tool-package-contracts.js';
12
+ import { datasetReads } from './dataset-reads.js';
13
+ import { toolLifecycleReads } from './tool-lifecycle-reads.js';
14
+ import { allocationQuote } from './tool-runtime.js';
15
+ import { creditAmount } from './credit-format.js';
16
+ import { toolGroupReads } from './tool-group-reads.js';
17
+ import { toolGroupRead, toolGroupMatches, toolGroupAccepted } from './tool-group-contracts.js';
18
+ const tty = (options) => !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
19
+ const selectionKey = (one) => 'release_id' in one ? `release:${one.release_id}` : `version:${one.implementation_id}`;
20
+ function text(row) {
21
+ return `${terminalText(row.name)}\nSetup: ${row.id}\nRequest: ${row.request_key}\n${row.counts.selected} selected · ${row.counts.running} running`
22
+ + (row.counts.pending ? ` · ${row.counts.pending} awaiting setup` : '')
23
+ + (row.counts.failed + row.counts.refused ? ` · ${row.counts.failed + row.counts.refused} failed` : '')
24
+ + (row.counts.closing ? ` · ${row.counts.closing} stopping` : '')
25
+ + (row.counts.closed ? ` · ${row.counts.closed} closed` : '')
26
+ + (row.counts.canceled ? ` · ${row.counts.canceled} canceled before startup` : '')
27
+ + `\nExpires: ${row.expires_at}${row.stop_requested_at ? '\nStop requested. Inspect each instance for observed cleanup.' : ''}`;
28
+ }
29
+ async function inputFile(path, signal) {
30
+ const filename = resolve(path), before = await lstat(filename);
31
+ if (!before.isFile() || before.isSymbolicLink() || before.size < 1 || before.size > 4 * 1024 ** 2) {
32
+ throw new CliError('tool_group_input_invalid', 'Choose a regular setup JSON file within request capacity.', 2);
33
+ }
34
+ const file = await open(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
35
+ try {
36
+ const held = await file.stat();
37
+ if (held.dev !== before.dev || held.ino !== before.ino || held.size !== before.size || held.mtimeMs !== before.mtimeMs)
38
+ throw new Error();
39
+ const chunks = [];
40
+ let bytes = 0;
41
+ for await (const chunk of file.createReadStream({ autoClose: false })) {
42
+ signal.throwIfAborted();
43
+ bytes += chunk.length;
44
+ if (bytes > 4 * 1024 ** 2)
45
+ throw new Error();
46
+ chunks.push(Buffer.from(chunk));
47
+ }
48
+ const after = await file.stat();
49
+ if (bytes !== held.size || after.size !== held.size || after.mtimeMs !== held.mtimeMs || after.ctimeMs !== held.ctimeMs)
50
+ throw new Error();
51
+ const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(chunks)));
52
+ if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).sort().join(',') !== 'duration_seconds,name,selections')
53
+ throw new Error();
54
+ const row = value;
55
+ if (typeof row.name !== 'string' || row.name !== row.name.trim() || !row.name || row.name.length > 120 || /[\u0000-\u001f\u007f]/.test(row.name)
56
+ || !Number.isSafeInteger(row.duration_seconds) || row.duration_seconds < 60 || !Array.isArray(row.selections) || !row.selections.length
57
+ || !row.selections.every(one => one && (Object.keys(one).sort().join(',') === 'dataset_id,implementation_id,usage_quote_id'
58
+ && 'implementation_id' in one && identifier(one.implementation_id) && (one.dataset_id === null || identifier(one.dataset_id))
59
+ || Object.keys(one).sort().join(',') === 'dataset_id,release_id,usage_quote_id' && 'release_id' in one && identifier(one.release_id) && one.dataset_id === null)
60
+ && typeof one.usage_quote_id === 'string' && identifier(one.usage_quote_id))
61
+ || new Set(row.selections.map(selectionKey)).size !== row.selections.length)
62
+ throw new Error();
63
+ return row;
64
+ }
65
+ catch {
66
+ signal.throwIfAborted();
67
+ throw new CliError('tool_group_input_invalid', 'Provide name, duration_seconds and selections of implementation_id/dataset_id or release_id with dataset_id null. No tools were requested.', 2);
68
+ }
69
+ finally {
70
+ await file.close();
71
+ }
72
+ }
73
+ async function chooseVersions(prefix, auth, options, maximum) {
74
+ const selected = new Map();
75
+ let page = 1, source = 'release';
76
+ while (true) {
77
+ const rows = source === 'release'
78
+ ? await api(`${prefix}/library?page=${page}&q=${encodeURIComponent(options.search ?? '')}`, auth).then(value => catalogRead(value, catalogPageMatches(value, prefix.split('/')[3], options.search ?? '', page)))
79
+ : await api(`${prefix}/catalog?state=inspected&page=${page}&q=${encodeURIComponent(options.search ?? '')}`, auth).then(value => packageRead(value, toolVersionPageMatches(value, prefix.split('/')[3], { page, query: options.search ?? '' })));
80
+ const available = rows.results.filter(one => source === 'release' ? one.available && one.saved_version?.state !== 'revoked' : one.state === 'inspected');
81
+ const availableIds = new Set(available.map(one => one.id));
82
+ for (const row of rows.results)
83
+ if (!availableIds.has(row.id))
84
+ selected.delete(`${source}:${row.id}`);
85
+ if (available.length) {
86
+ const checked = new Set(await selectMany(`${source === 'release' ? 'Catalog' : 'Saved versions'} · page ${page}`, available.map(one => ({ value: `${source}:${one.id}`,
87
+ label: one.name, detail: `Version ${one.version}` })), options.signal, [...selected.keys()]));
88
+ const proposed = new Map(selected);
89
+ for (const row of rows.results) {
90
+ const key = `${source}:${row.id}`;
91
+ if (checked.has(key))
92
+ proposed.set(key, { id: row.id, name: row.name, version: row.version, source });
93
+ else
94
+ proposed.delete(key);
95
+ }
96
+ if (proposed.size > maximum) {
97
+ process.stderr.write(`Choose at most ${maximum} tools in this setup. Your previous selection is unchanged.\n`);
98
+ continue;
99
+ }
100
+ selected.clear();
101
+ for (const [key, version] of proposed)
102
+ selected.set(key, version);
103
+ }
104
+ const action = await selectOne(`${selected.size} selected`, [
105
+ ...(selected.size ? [{ value: 'finish', label: 'Continue with selected tools' }] : []),
106
+ ...(rows.next ? [{ value: 'next', label: 'Next page' }] : []), ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : []),
107
+ { value: 'source', label: source === 'release' ? 'Browse saved versions' : 'Browse catalog' },
108
+ { value: 'cancel', label: 'Cancel' }
109
+ ], options.signal);
110
+ if (action === 'cancel')
111
+ throw new CliError('canceled', 'No tools were requested.', 130);
112
+ if (action === 'finish')
113
+ return [...selected.values()];
114
+ if (action === 'source') {
115
+ source = source === 'release' ? 'version' : 'release';
116
+ page = 1;
117
+ continue;
118
+ }
119
+ page += action === 'next' ? 1 : -1;
120
+ }
121
+ }
122
+ async function chooseDataset(prefix, version, auth, signal) {
123
+ let page = 1;
124
+ if (!auth?.actor || !auth.workspace)
125
+ throw new CliError('tool_dataset_scope_invalid', 'Choose a workspace before selecting data.', 2);
126
+ const reads = datasetReads(prefix, { owner: auth.actor, workspace: auth.workspace }, signal);
127
+ while (true) {
128
+ const rows = await reads.page({ implementation: version, state: 'retained', page });
129
+ const value = await selectOne('Starting data', [{ value: 'packaged', label: 'Packaged data' },
130
+ ...rows.results.filter(one => one.available).map(one => ({ value: one.id, label: one.name, detail: `Version ${one.version}` })),
131
+ ...(rows.next ? [{ value: 'next', label: 'Next page' }] : []), ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : [])], signal);
132
+ if (value === 'packaged')
133
+ return null;
134
+ if (value === 'next')
135
+ page++;
136
+ else if (value === 'previous')
137
+ page--;
138
+ else
139
+ return identifier(value);
140
+ }
141
+ }
142
+ export async function toolGroupCommand(args, options, output) {
143
+ const action = args[0] === 'start' ? 'start-many' : args[0], starting = action === 'start-many', paged = ['setups', 'setup-members', 'start-many'].includes(action);
144
+ if (!['setups', 'setup', 'setup-members', 'setup-status', 'start-many', 'stop-setup'].includes(action)
145
+ || args.length > (['setups', 'start-many'].includes(action) ? 1 : 2) || options.upload || options.service !== undefined
146
+ || !paged && (options.page !== undefined || options.search !== undefined) || starting && options.page !== undefined
147
+ || !starting && (options.file !== undefined || options.request !== undefined || options.seconds !== undefined)
148
+ || !starting && action !== 'stop-setup' && options.change || starting && options.file && options.seconds !== undefined
149
+ || options.usageQuote !== undefined
150
+ || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
151
+ throw new CliError('arguments_invalid', 'Use tools start [--file setup.json], setups, setup <id>, setup-members <id>, setup-status <request>, or stop-setup <id>. Setup files carry one approved usage_quote_id per tool.', 2);
152
+ }
153
+ const scope = await selectedWorkspace({ ...options, interactive: tty(options) });
154
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal }, prefix = `/api/workspaces/${scope.workspace}/tools`;
155
+ const reads = toolGroupReads(scope, options.signal), owner = { workspace: scope.workspace, actor: scope.owner };
156
+ const continuation = (row, command) => {
157
+ const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
158
+ return `\nPage ${row.page}; ${row.count} matching items.${row.next ? `\nNext: firedrill tools ${command} --workspace ${scope.workspace} --page ${row.page + 1}${options.search ? ' --search ' + quote(options.search) : ''}` : ''}`;
159
+ };
160
+ if (action === 'setups') {
161
+ const rows = await reads.page(Number(options.page ?? 1), options.search ?? '');
162
+ output('tool_setups', rows, (rows.results.map(text).join('\n\n') || 'No tool setups in this workspace.') + continuation(rows, 'setups'));
163
+ return;
164
+ }
165
+ if (action === 'setup-status') {
166
+ const row = await reads.original(identifier(args[1] ?? ''));
167
+ output('tool_setup', row, text(row));
168
+ return;
169
+ }
170
+ if (!starting) {
171
+ let chosen = args[1], page = 1;
172
+ while (!chosen && tty(options)) {
173
+ const rows = await reads.page(page);
174
+ const value = await selectOne('Tool setup', [...rows.results.map(one => ({ value: one.id, label: one.name,
175
+ detail: `${one.counts.selected} tools · ${one.counts.running} running · ${one.counts.failed + one.counts.refused} failed` })),
176
+ ...(rows.next ? [{ value: 'next', label: 'Next page' }] : []), ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : []),
177
+ { value: 'cancel', label: 'Cancel' }], options.signal);
178
+ if (value === 'cancel')
179
+ throw new CliError('canceled', 'Nothing changed.', 130);
180
+ if (value === 'next')
181
+ page++;
182
+ else if (value === 'previous')
183
+ page--;
184
+ else
185
+ chosen = identifier(value);
186
+ }
187
+ const id = identifier(chosen ?? ''), path = `${prefix}/setups/${id}`;
188
+ if (action === 'setup-members') {
189
+ const rows = await reads.members(id, Number(options.page ?? 1), options.search ?? '');
190
+ output('tool_setup_members', rows, (rows.results.map(one => `${terminalText(one.name)} ${terminalText(one.version)}: ${one.instance?.removal ? one.instance.removal.completed_at ? 'removed' : 'removing'
191
+ : one.instance?.state === 'active' && !one.instance.observation_current ? 'awaiting observation' : one.instance?.state ?? one.state}`
192
+ + `${one.instance ? '\nInstance: ' + one.instance.id : ''}\nStarting data: ${one.dataset ? terminalText(one.dataset.name) + ' version ' + one.dataset.version : 'packaged data'}`
193
+ + (one.catalog_request_key ? `\nCatalog setup: tools catalog-status ${one.catalog_request_key}` : '')
194
+ + (one.problem || one.instance?.problem ? '\n' + terminalText(one.problem || one.instance?.problem || '') : '')).join('\n\n') || 'No matching tools in this setup.') + continuation(rows, `setup-members ${id}`));
195
+ return;
196
+ }
197
+ const original = await reads.detail(id);
198
+ if (action === 'setup') {
199
+ output('tool_setup', original, text(original));
200
+ return;
201
+ }
202
+ const consent = options.change || tty(options) && await selectOne(`Stop ${original.counts.selected} tools in ${original.name}? Pending setups will be canceled. Saved history remains.`, [{ value: false, label: 'Cancel' }, { value: true, label: 'Stop tools' }], options.signal);
203
+ if (!consent)
204
+ throw new CliError('tool_stop_consent_required', 'Nothing changed. Use --approve-tool-change for explicit automation consent.', 2);
205
+ const row = await api(path, { ...auth, method: 'DELETE', body: { stop_consent: true } });
206
+ toolGroupRead(row, toolGroupMatches(row, owner, { id, digest: original.request_digest, stopped: true }));
207
+ const recorded = await reads.detail(id, { digest: original.request_digest, stopped: true });
208
+ output('tool_setup', recorded, text(recorded));
209
+ return;
210
+ }
211
+ const { setup } = await toolLifecycleReads(scope, options.signal).instances();
212
+ let value;
213
+ if (options.file)
214
+ value = await inputFile(options.file, options.signal);
215
+ else {
216
+ if (!tty(options))
217
+ throw new CliError('tool_group_input_required', 'Use --file setup.json with name, duration_seconds and selections. No tools were requested.', 2);
218
+ const versions = await chooseVersions(prefix, auth, options, setup.maximum_tools);
219
+ value = { name: versions.slice(0, 2).map(one => terminalText(one.name)).join(', ').trim().slice(0, 120) || 'Tools', duration_seconds: options.seconds === undefined ? setup.default_duration_seconds : Number(options.seconds),
220
+ selections: versions.map(one => one.source === 'release' ? { release_id: one.id, dataset_id: null } : { implementation_id: one.id, dataset_id: null }) };
221
+ const savedVersions = versions.filter(one => one.source === 'version');
222
+ const data = savedVersions.length ? await selectOne('Starting data', [{ value: 'packaged', label: 'Packaged data for all tools' }, { value: 'saved', label: 'Choose saved datasets for saved versions' }], options.signal) : 'packaged';
223
+ if (data === 'saved') {
224
+ let page = 1;
225
+ while (true) {
226
+ const rows = savedVersions.slice((page - 1) * 25, page * 25);
227
+ const selected = await selectOne('Choose a tool to change its starting data', [
228
+ { value: 'finish', label: 'Continue' }, ...rows.map(one => ({ value: one.id, label: one.name, detail: `Version ${one.version}` })),
229
+ ...(page * 25 < savedVersions.length ? [{ value: 'next', label: 'Next page' }] : []), ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : [])
230
+ ], options.signal);
231
+ if (selected === 'finish')
232
+ break;
233
+ if (selected === 'next')
234
+ page++;
235
+ else if (selected === 'previous')
236
+ page--;
237
+ else {
238
+ const choice = value.selections.find(one => 'implementation_id' in one && one.implementation_id === selected);
239
+ if (!choice || !('implementation_id' in choice))
240
+ throw new CliError('tool_group_input_invalid', 'The selected saved version changed. No tools were requested.', 2);
241
+ choice.dataset_id = await chooseDataset(prefix, selected, auth, options.signal);
242
+ }
243
+ }
244
+ }
245
+ }
246
+ return startToolGroup(scope, value, options, output);
247
+ }
248
+ export async function startToolGroup(scope, value, options, output, beforeDispatch) {
249
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
250
+ const prefix = `/api/workspaces/${scope.workspace}/tools`, owner = { workspace: scope.workspace, actor: scope.owner };
251
+ const reads = toolGroupReads(scope, options.signal);
252
+ const { setup } = await toolLifecycleReads(scope, options.signal).instances();
253
+ if (!value.selections.length || value.selections.length > setup.maximum_tools || value.duration_seconds > setup.maximum_duration_seconds || !Number.isSafeInteger(value.duration_seconds) || value.duration_seconds < 60) {
254
+ throw new CliError('tool_group_capacity', 'Choose a tool count and duration within configured setup capacity. No tools were requested.', 2);
255
+ }
256
+ if (!tty(options) && !options.change)
257
+ throw new CliError('tool_start_consent_required', 'No tools were requested. Use --approve-tool-change for explicit automation consent.', 2);
258
+ const key = identifier(options.request ?? randomUUID());
259
+ output('tool_setup_intent', { request_key: key, selection_count: value.selections.length, duration_seconds: value.duration_seconds }, `Setup request: ${key}\nIf interrupted, use tools setup-status ${key}. Never create a replacement request to recover an uncertain reply.`);
260
+ const selections = [];
261
+ const quotes = [];
262
+ for (const chosen of value.selections) {
263
+ const quoteId = chosen.usage_quote_id;
264
+ // File-based automation must carry one previously approved quote per child.
265
+ // Interactive users review the combined bounded budget before one dispatch.
266
+ let launchKey = randomUUID();
267
+ if (quoteId) {
268
+ const quoted = await api(`/api/workspaces/${scope.workspace}/billing/usage/quotes/${identifier(quoteId)}`, auth);
269
+ if (!quoted?.scope || typeof quoted.scope.launch_request_key !== 'string')
270
+ throw new CliError('billing_scope_mismatch', 'Review a current runtime quote for each selected tool. No tools were requested.', 2);
271
+ launchKey = identifier(quoted.scope.launch_request_key);
272
+ }
273
+ const quote = await allocationQuote(scope, { ...options, usageQuote: quoteId }, {
274
+ implementation_id: 'implementation_id' in chosen ? chosen.implementation_id : null,
275
+ release_id: 'release_id' in chosen ? chosen.release_id : null, dataset_id: chosen.dataset_id,
276
+ restore: null, duration_seconds: value.duration_seconds, launch_request_key: launchKey
277
+ });
278
+ if (quotes.length && (quote.credit_scale !== quotes[0].credit_scale || quote.denomination !== quotes[0].denomination))
279
+ throw new CliError('billing_policy_changed', 'The credit policy changed during review. No tools were requested. Review the current selection again.', 2);
280
+ quotes.push(quote);
281
+ selections.push({ ...chosen, usage_quote_id: quote.id });
282
+ }
283
+ const maximum = quotes.reduce((total, quote) => total + BigInt(quote.maximum_units), 0n);
284
+ if (maximum > BigInt(Number.MAX_SAFE_INTEGER))
285
+ throw new CliError('billing_budget_capacity', 'This combined budget needs a smaller selection. No tools were requested.', 2);
286
+ output('tool_setup_budget', { workspace_id: scope.workspace, request_key: key, selection_count: selections.length,
287
+ duration_seconds: value.duration_seconds, maximum_units: Number(maximum), credit_scale: quotes[0].credit_scale,
288
+ denomination: quotes[0].denomination, quotes: quotes.map(quote => ({ id: quote.id, maximum_units: quote.maximum_units, expires_at: quote.expires_at })) }, `Up to ${creditAmount(Number(maximum), quotes[0].credit_scale)} credits for ${selections.length} ${selections.length === 1 ? 'tool' : 'tools'} for ${value.duration_seconds} seconds, including startup and waiting for capacity. Unused credits are released after cleanup.`);
289
+ if (tty(options) && !await selectOne('Start the selected tools with this runtime budget?', [{ value: false, label: 'Cancel' }, { value: true, label: selections.length === 1 ? 'Start tool' : `Start ${selections.length} tools` }], options.signal))
290
+ throw new CliError('canceled', 'No tools were requested.', 130);
291
+ await beforeDispatch?.();
292
+ options.signal.throwIfAborted();
293
+ if (quotes.some(quote => Date.parse(quote.expires_at) <= Date.now()))
294
+ throw new CliError('billing_scope_mismatch', 'The runtime quotes expired before startup. No tools were requested. Review the current selection again.', 2);
295
+ const input = { ...value, selections, start_consent: true };
296
+ let recorded;
297
+ try {
298
+ const row = await api(`${prefix}/setups`, { ...auth, method: 'POST', idempotencyKey: key, body: input });
299
+ toolGroupRead(row, await toolGroupAccepted(row, owner, key, input));
300
+ recorded = await reads.original(key, { input, id: row.id, digest: row.request_digest });
301
+ }
302
+ catch {
303
+ try {
304
+ recorded = await reads.original(key, { input });
305
+ }
306
+ catch {
307
+ throw new CliError('tool_group_acceptance_unconfirmed', `Setup acceptance is unconfirmed. No replacement was submitted. Use tools setup-status ${key} --workspace ${scope.workspace}.`, 2);
308
+ }
309
+ }
310
+ output('tool_setup', recorded, text(recorded) + `\nUse tools setup-members ${recorded.id} for each original instance, then tools open <instance> for its portal view.`);
311
+ }
@@ -0,0 +1,186 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { api } from './auth.js';
3
+ import { CliError } from './http.js';
4
+ import { selectOne } from './prompts.js';
5
+ import { identifier } from './world-contracts.js';
6
+ import { selectedWorkspace } from './world-access.js';
7
+ import { choosePaged } from './configuration.js';
8
+ import { requestInput as argumentsFile } from './request-input.js';
9
+ import { packageReads } from './tool-package-reads.js';
10
+ import { toolLifecycleReads } from './tool-lifecycle-reads.js';
11
+ import { toolStateReads } from './tool-state-reads.js';
12
+ import { toolCommandRead, toolCommandMatches, toolCommandsMatch } from './tool-command-contracts.js';
13
+ import { toolCommandDownloadHints } from './tool-command-export.js';
14
+ const interactive = (options) => !options.json && Boolean(process.stdin.isTTY && process.stderr.isTTY);
15
+ function commandText(row) {
16
+ const schema = { matched: 'Matches declared schema', mismatch: 'Does not match declared schema',
17
+ not_json: 'Response is not JSON', missing_structured: 'No structured result',
18
+ not_applicable: row.response?.transport === 'http' && row.response.response_encoding === 'binary' ? 'Binary response' : 'Text response' }[row.response?.schema_status ?? ''] ?? 'Schema unavailable';
19
+ const downloads = toolCommandDownloadHints(row);
20
+ return `${row.service_key}.${row.operation_name}: ${row.state}\nCommand: ${row.id}\nRequest: ${row.request_key}`
21
+ + (row.response?.transport === 'surface' ? `\nBrowser receipt: ${row.response.surface_state}` : row.response ? `\n${row.response.transport === 'mcp' ? `MCP tool error: ${row.response.mcp_is_error ? 'yes' : 'no'}`
22
+ : `HTTP ${row.response.http_status}`}. ${schema}. ${row.response.body_size} bytes.` : '')
23
+ + (row.response_withheld ? '\nResponse content was withheld from history.' : '')
24
+ + (row.problem ? '\n' + row.problem : '') + (downloads ? '\n' + downloads : '');
25
+ }
26
+ export async function toolInstanceCommand(args, options, output) {
27
+ const action = args[0];
28
+ if (options.state !== undefined && (action !== 'instances' || !['all', 'active', 'closed', 'removed'].includes(options.state)))
29
+ throw new CliError('arguments_invalid', 'Use tools instances --state all|active|closed|removed.', 2);
30
+ if (args.length > (action === 'call' ? 4 : action === 'result' ? 3 : 2)
31
+ || !['instances', 'removed', 'activity', 'checkpoints'].includes(action) && (options.page !== undefined || options.search !== undefined)
32
+ || action === 'activity' && options.page !== undefined
33
+ || action !== 'activity' && [options.before, options.after, options.anchor, options.outcome].some(one => one !== undefined)
34
+ || ['instances', 'removed'].includes(action) && args.length > 1 || options.service !== undefined || options.upload
35
+ || options.seconds !== undefined || action !== 'call' && (options.file !== undefined || options.request !== undefined || options.change)) {
36
+ throw new CliError('arguments_invalid', 'Use tools instances, instance <id>, activity <id>, checkpoints [instance], result <instance> <request>, or call [instance] [service] [operation] [--file arguments.json].', 2);
37
+ }
38
+ if (options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
39
+ throw new CliError('arguments_invalid', 'Use a positive page and a search up to 200 characters.', 2);
40
+ }
41
+ const scope = await selectedWorkspace({ ...options, interactive: interactive(options) });
42
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal };
43
+ const prefix = `/api/workspaces/${scope.workspace}/tools`;
44
+ const lifecycle = toolLifecycleReads(scope, options.signal);
45
+ const query = `page=${options.page ?? '1'}&q=${encodeURIComponent(options.search ?? '')}`;
46
+ if (['instances', 'removed'].includes(action)) {
47
+ const state = action === 'removed' ? 'removed' : (options.state ?? 'all');
48
+ const page = await lifecycle.instances(Number(options.page ?? 1), options.search ?? '', state);
49
+ const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
50
+ output('tool_instances', page, (page.results.map(one => `${one.id} ${one.name} ${one.version} · ${one.removal ? one.removal.completed_at ? 'removed' : 'removing' : one.state}`).join('\n') || 'No matching tool instances in this workspace.')
51
+ + `\nPage ${page.page}; ${page.count} instances.${page.next ? `\nNext: firedrill tools ${action} --workspace ${scope.workspace} --page ${page.page + 1}${action === 'instances' ? ` --state ${state}` : ''}${options.search ? ` --search ${quote(options.search)}` : ''}` : ''}`);
52
+ return;
53
+ }
54
+ let instance = args[1] ? identifier(args[1]) : undefined;
55
+ if (!instance && interactive(options)) {
56
+ let page = 1;
57
+ while (!instance) {
58
+ const result = await lifecycle.instances(page, '', action === 'call' ? 'active' : 'all');
59
+ const selected = await selectOne('Tool instance', [
60
+ ...result.results.map(one => ({ value: one.id, label: one.name, detail: `${one.version} · ${one.state}` })),
61
+ ...(result.next ? [{ value: '__next__', label: 'Next page' }] : []), ...(page > 1 ? [{ value: '__previous__', label: 'Previous page' }] : []),
62
+ { value: '__cancel__', label: 'Cancel' },
63
+ ], options.signal);
64
+ if (selected === '__cancel__')
65
+ throw new CliError('canceled', 'No operation was submitted.', 130);
66
+ if (selected === '__next__')
67
+ page++;
68
+ else if (selected === '__previous__')
69
+ page--;
70
+ else
71
+ instance = identifier(selected);
72
+ }
73
+ }
74
+ if (!instance)
75
+ throw new CliError('tool_instance_required', 'Provide a tool instance ID from tools instances.', 2);
76
+ const path = `${prefix}/instances/${instance}`;
77
+ const commandScope = { workspace: scope.workspace, instance, actor: scope.owner };
78
+ if (action === 'instance') {
79
+ const row = await lifecycle.instance(instance);
80
+ output('tool_instance', row, `${row.name} ${row.version} · ${row.removal ? row.removal.completed_at ? 'removed' : 'removing' : row.state}${row.problem ? '\n' + row.problem : ''}`
81
+ + (row.removal ? '\nSaved activity and data follow workspace retention.' : ''));
82
+ return;
83
+ }
84
+ if (action === 'activity') {
85
+ const sequence = (value, zero = false) => value === undefined
86
+ || /^(0|[1-9][0-9]{0,18})$/.test(value) && BigInt(value) >= (zero ? 0n : 1n) && BigInt(value) < 2n ** 63n;
87
+ if (!sequence(options.before) || !sequence(options.after) || !sequence(options.anchor, true)
88
+ || options.before !== undefined && options.after !== undefined
89
+ || (options.before !== undefined || options.after !== undefined) && options.anchor === undefined
90
+ || options.outcome !== undefined && !['queued', 'publishing', 'accepted', 'observed', 'not_dispatched', 'unknown'].includes(options.outcome)) {
91
+ throw new CliError('arguments_invalid', 'Use an activity outcome and the saved --before or --after position with --anchor.', 2);
92
+ }
93
+ const params = new URLSearchParams({ q: options.search ?? '' });
94
+ for (const name of ['before', 'after', 'anchor', 'outcome'])
95
+ if (options[name] !== undefined)
96
+ params.set(name, options[name]);
97
+ const page = await api(`${path}/commands?${params}`, auth);
98
+ toolCommandRead(page, toolCommandsMatch(page, commandScope, { query: options.search, outcome: options.outcome, anchor: options.anchor }));
99
+ const quoted = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
100
+ const original = `firedrill tools activity ${instance} --workspace ${scope.workspace}`
101
+ + (options.search ? ` --search ${quoted(options.search)}` : '') + (options.outcome ? ` --outcome ${options.outcome}` : '');
102
+ const continuation = (page.next_before ? `\nOlder: ${original} --anchor ${page.anchor} --before ${page.next_before}` : '')
103
+ + (page.previous_after ? `\nNewer: ${original} --anchor ${page.anchor} --after ${page.previous_after}` : '');
104
+ output('tool_activity', page, (page.results.map(commandText).join('\n\n') || 'No matching operation history in this instance.') + continuation);
105
+ return;
106
+ }
107
+ if (action === 'checkpoints') {
108
+ const page = await toolStateReads(scope, instance, options.signal).checkpoints(Number(options.page ?? 1), options.search ?? '');
109
+ const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
110
+ output('tool_data_checkpoints', page, (page.results.map(one => `${one.id} ${one.scope === 'declared_stateless' ? one.kind === 'baseline' ? 'Startup record' : 'Closing record' : one.kind} · ${one.captured_at}\n${one.available ? one.scope === 'declared_stateless' ? 'No persistent data declared. Service memory is not included.' : 'Saved data available.' : one.problem ?? 'Saved data unavailable.'}`).join('\n\n') || 'No data checkpoints in this instance.')
111
+ + `\nPage ${page.page}; ${page.count} checkpoints.${page.next ? `\nNext: firedrill tools checkpoints ${instance} --workspace ${scope.workspace} --page ${page.page + 1}${options.search ? ' --search ' + quote(options.search) : ''}` : ''}`);
112
+ return;
113
+ }
114
+ if (action === 'result') {
115
+ const key = identifier(args[2] ?? '');
116
+ const row = await api(`${path}/requests/${key}`, auth);
117
+ toolCommandRead(row, toolCommandMatches(row, commandScope, { key }));
118
+ output('tool_operation_result', row, commandText(row));
119
+ return;
120
+ }
121
+ const row = await lifecycle.instance(instance);
122
+ if (!row.observation_current)
123
+ throw new CliError('tool_instance_not_ready', 'Wait for a current tool observation before submitting an operation.', 2);
124
+ const packagePath = `${prefix}/implementations/${identifier(row.implementation_id)}`;
125
+ const packages = packageReads(scope, options.signal);
126
+ const version = await packages.version(row.implementation_id);
127
+ if (version.state !== 'inspected')
128
+ throw new CliError('tool_version_unavailable', 'The original tool version is no longer available. No operation was submitted.', 2);
129
+ let service = args[2];
130
+ if (!service && interactive(options)) {
131
+ const chosen = await choosePaged('Service', `${packagePath}/services`, scope, options, one => ({ label: one.name, detail: `${one.operation_count} operations` }), false, (page, search) => packages.services(version, page, search));
132
+ service = chosen?.key;
133
+ }
134
+ if (!service || !/^[a-z][a-z0-9_-]{0,79}$/.test(service))
135
+ throw new CliError('tool_service_required', 'Choose a service from tools services for this instance version.', 2);
136
+ const selected = await packages.service(version, service);
137
+ if (selected.key !== service || selected.implementation_id !== row.implementation_id || selected.contract_digest !== version.contract_digest) {
138
+ throw new CliError('tool_version_unavailable', 'The original tool version changed. No operation was submitted.', 2);
139
+ }
140
+ let operation = args[3];
141
+ if (!operation && interactive(options)) {
142
+ let page = 1;
143
+ while (!operation) {
144
+ const result = await packages.operations(version, service, page);
145
+ const selected = await selectOne('Operation', [
146
+ ...result.results.filter(one => one.transport.kind === 'mcp' || one.transport.kind === 'http' && ['json', 'query', 'form', 'multipart', 'text', 'base64', 'none'].includes(one.transport.encoding ?? ''))
147
+ .map(one => ({ value: one.name, label: one.name, detail: one.effect })),
148
+ ...(result.next ? [{ value: '__next__', label: 'Next page' }] : []), ...(page > 1 ? [{ value: '__previous__', label: 'Previous page' }] : []),
149
+ { value: '__cancel__', label: 'Cancel' },
150
+ ], options.signal);
151
+ if (selected === '__cancel__')
152
+ throw new CliError('canceled', 'No operation was submitted.', 130);
153
+ if (selected === '__next__')
154
+ page++;
155
+ else if (selected === '__previous__')
156
+ page--;
157
+ else
158
+ operation = selected;
159
+ }
160
+ }
161
+ if (!operation || !/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(operation))
162
+ throw new CliError('tool_operation_required', 'Choose an operation name from tools operations.', 2);
163
+ await packages.operation(version, service, operation);
164
+ const approved = options.change || interactive(options) && await selectOne('Submit this operation to the managed synthetic tool?', [
165
+ { value: false, label: 'Cancel' }, { value: true, label: 'Submit operation', detail: 'Selected arguments leave this device. Do not include real credentials.' },
166
+ ], options.signal);
167
+ if (!approved)
168
+ throw new CliError('tool_data_consent_required', 'Nothing was submitted. Use --approve-tool-change for explicit automated submission.', 2);
169
+ const data = await argumentsFile(options.file, options.signal);
170
+ const key = identifier(options.request ?? randomUUID());
171
+ output('tool_operation_intent', { instance_id: instance, request_key: key }, `Request: ${key}\nIf interrupted, use tools result ${instance} ${key}. Do not create a replacement request.`);
172
+ let command;
173
+ try {
174
+ command = await api(`${path}/commands`, { ...auth, method: 'POST', idempotencyKey: key,
175
+ bodyJson: `{"service_key":${JSON.stringify(service)},"operation_name":${JSON.stringify(operation)},"arguments":${data},"data_transfer_consent":true}` });
176
+ toolCommandRead(command, toolCommandMatches(command, commandScope, { key, service, operation }));
177
+ }
178
+ catch (error) {
179
+ if (error instanceof CliError)
180
+ throw error;
181
+ throw new CliError('tool_operation_acceptance_unconfirmed', `Operation acceptance is unconfirmed. Nothing was repeated. Inspect tools result ${instance} ${key} --workspace ${scope.workspace}.`);
182
+ }
183
+ const original = await api(`${path}/requests/${key}`, auth);
184
+ toolCommandRead(original, toolCommandMatches(original, commandScope, { key, id: command.id, digest: command.request_digest, service, operation }));
185
+ output('tool_operation_request', original, commandText(original));
186
+ }
@@ -0,0 +1 @@
1
+ export * from '../../contracts/tool-lifecycle-contracts.js';
@@ -0,0 +1,30 @@
1
+ import { api } from './auth.js';
2
+ import { identifier } from './world-contracts.js';
3
+ import { toolLifecycleRead, toolInstancePageMatches, toolInstanceDetailMatches, toolStartReceiptMatches, toolRemovalMatches } from './tool-lifecycle-contracts.js';
4
+ export function toolLifecycleReads(scope, signal) {
5
+ const prefix = `/api/workspaces/${scope.workspace}/tools`, auth = { actor: scope.owner, workspace: scope.workspace, signal };
6
+ const owner = { actor: scope.owner, workspace: scope.workspace };
7
+ return {
8
+ async instances(page = 1, query = '', state = 'all') {
9
+ const value = await api(`${prefix}/instances?page=${page}&q=${encodeURIComponent(query)}&state=${encodeURIComponent(state)}`, auth);
10
+ return toolLifecycleRead(value, toolInstancePageMatches(value, scope.workspace, query, state, page));
11
+ },
12
+ async instance(id) {
13
+ const value = await api(`${prefix}/instances/${identifier(id)}`, auth);
14
+ return toolLifecycleRead(value, toolInstanceDetailMatches(value, scope.workspace, { id }));
15
+ },
16
+ async startRequest(key, selected = {}) {
17
+ const value = await api(`${prefix}/instance-requests/${identifier(key)}`, auth);
18
+ return toolLifecycleRead(value, toolInstanceDetailMatches(value, scope.workspace, { key, id: selected.id, actor: scope.owner })
19
+ && (!selected.input || await toolStartReceiptMatches(value, owner, key, selected.input)));
20
+ },
21
+ async removal(instance) {
22
+ const value = await api(`${prefix}/instances/${identifier(instance)}/removal`, auth);
23
+ return toolLifecycleRead(value, toolRemovalMatches(value, owner, { instance }));
24
+ },
25
+ async removalRequest(key, selected = {}) {
26
+ const value = await api(`${prefix}/removal-requests/${identifier(key)}`, auth);
27
+ return toolLifecycleRead(value, toolRemovalMatches(value, owner, { key, ...selected }));
28
+ },
29
+ };
30
+ }