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,221 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { open, unlink } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { createInterface } from 'node:readline/promises';
5
+ import { api } from './auth.js';
6
+ import { privateDirectory } from './credentials.js';
7
+ import { readMetadata } from './configuration-intents.js';
8
+ import { CliError } from './http.js';
9
+ import { selectOne } from './prompts.js';
10
+ import { selectedWorkspace, selectedWorld } from './world-access.js';
11
+ import { identifier } from './world-contracts.js';
12
+ import { realOperationMatches, realOperationsMatch } from '../../contracts/real-operation-contracts.js';
13
+ const names = { admitted: 'Request recorded', dispatching: 'Dispatch started', response: 'Response received',
14
+ not_sent: 'Not dispatched', uncertain: 'Outcome not confirmed' };
15
+ const interactive = (options) => !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY;
16
+ const auth = (context, options) => ({ actor: context.owner, workspace: context.workspace, signal: options.signal });
17
+ const path = (context, operation) => `${context.prefix}/real-operations/${identifier(operation)}`;
18
+ const invalid = () => new CliError('real_review_intent_invalid', 'The exact review record is unavailable or differs. No operation was sent and no replacement review was created.', 2);
19
+ const selection = (context) => ({ viewer: context.owner, workspace: context.workspace, world: context.world });
20
+ async function readOperation(context, operation, options, key) {
21
+ const row = await api(path(context, identifier(operation)) + (key ? `?review_request_key=${identifier(key)}` : ''), auth(context, options));
22
+ if (!realOperationMatches(row, selection(context), operation, key))
23
+ throw invalid();
24
+ return row;
25
+ }
26
+ const location = async (scope, key) => join(await privateDirectory(), `real-review-${identifier(scope.owner)}-${identifier(scope.workspace)}-${identifier(key)}.json`);
27
+ function show(row, output) {
28
+ const activity = { simulation: 'Simulation', manual_session: 'Manual session', agent_startup: 'Agent startup', other: 'Other activity' }[row.activity];
29
+ output('real_operation', row, `${row.operation} · ${row.service_name}\n${activity ?? 'Other activity'}\n${names[row.outcome] ?? 'Outcome unavailable'} · ${row.effect === 'write' ? 'May change the real account' : 'Read'}\n`
30
+ + `Recorded: ${row.created_at}\nDispatch: ${row.dispatch_started_at ?? 'Not recorded'}\nHTTP: ${row.http_status ?? 'Not recorded'}\n`
31
+ + `Response delivered: ${row.response_delivery_confirmed_at ?? 'Not confirmed'}\nConfiguration: ${row.configuration_revision}\n`
32
+ + (row.reviewed_at ? `Reviewed: ${row.reviewed_at}\n` : row.requires_review ? `Review required.${row.review_available ? '' : ` Available after ${row.review_available_at}.`}\n` : '')
33
+ + 'Resetting or deleting this world does not undo real-account changes. HTTP is not a task-success score.');
34
+ }
35
+ async function saved(scope, key) {
36
+ let row;
37
+ try {
38
+ row = await readMetadata(await location(scope, key), true);
39
+ }
40
+ catch {
41
+ throw invalid();
42
+ }
43
+ if (Object.keys(row).sort().join(',') !== ['schema', 'owner', 'workspace', 'world', 'operation', 'key', 'acknowledge_external_effects', 'acknowledge_duplicate_risk'].sort().join(',')
44
+ || row.schema !== 'firedrill.local-real-review.v1' || row.owner !== scope.owner || row.workspace !== scope.workspace || row.key !== key
45
+ || typeof row.world !== 'string' || typeof row.operation !== 'string' || row.acknowledge_external_effects !== true || row.acknowledge_duplicate_risk !== true)
46
+ throw invalid();
47
+ identifier(row.world);
48
+ identifier(row.operation);
49
+ return row;
50
+ }
51
+ async function remember(context, row, key) {
52
+ const intent = { schema: 'firedrill.local-real-review.v1', owner: context.owner, workspace: context.workspace,
53
+ world: context.world, operation: identifier(row.id), key: identifier(key), acknowledge_external_effects: true, acknowledge_duplicate_risk: true };
54
+ try {
55
+ const file = await open(await location(context, key), 'wx', 0o600);
56
+ try {
57
+ await file.writeFile(JSON.stringify(intent));
58
+ await file.sync();
59
+ }
60
+ finally {
61
+ await file.close();
62
+ }
63
+ }
64
+ catch (error) {
65
+ if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST')
66
+ throw error;
67
+ const previous = await saved(context, key);
68
+ if (previous.world !== intent.world || previous.operation !== intent.operation)
69
+ throw invalid();
70
+ }
71
+ return intent;
72
+ }
73
+ function receipt(row, intent) {
74
+ const value = row.review_receipt;
75
+ if (!value || row.id !== intent.operation || value.operation_id !== intent.operation || value.request_key !== intent.key
76
+ || !['accepted', 'not_received', 'reviewed_by_another_request'].includes(value.state)
77
+ || value.state !== 'not_received' && !row.reviewed_at)
78
+ throw invalid();
79
+ return value.state;
80
+ }
81
+ async function review(context, row, options, output) {
82
+ if (!row.can_review)
83
+ throw new CliError('admin_required', 'An administrator must review this real-account request.', 3);
84
+ const key = options.request ? identifier(options.request) : randomUUID();
85
+ if (options.request) {
86
+ const original = await saved(context, key);
87
+ if (original.world !== context.world || original.operation !== row.id)
88
+ throw invalid();
89
+ const current = await readOperation(context, row.id, options, key);
90
+ const state = receipt(current, original);
91
+ if (state !== 'not_received') {
92
+ show(current, output);
93
+ output('review_receipt', current.review_receipt, state === 'accepted' ? 'The original review was accepted. No operation was repeated.' : 'Another review was already recorded. Nothing was changed.');
94
+ return;
95
+ }
96
+ row = current;
97
+ }
98
+ if (!row.review_available)
99
+ throw new CliError('real_effect_dispatch_pending', row.reviewed_at
100
+ ? 'This request is already reviewed. Inspect its recorded outcome.' : row.requires_review
101
+ ? `The original dispatch window is still open. Review becomes available after ${row.review_available_at}.`
102
+ : 'This request does not need outcome review.', 2);
103
+ show(row, output);
104
+ const effects = options.effects || interactive(options) && await selectOne('Have you checked the real account and reviewed this request\'s possible effects?', [{ value: false, label: 'Cancel' }, { value: true, label: 'I checked the real account' }], options.signal);
105
+ const risk = effects && (options.duplicateRisk || interactive(options) && await selectOne('Another write may duplicate an earlier effect. Record review?', [{ value: false, label: 'Cancel' }, { value: true, label: 'I understand the duplicate risk' }], options.signal));
106
+ if (!effects || !risk)
107
+ throw new CliError('real_effect_review_required', 'No review was sent. Automation needs both --acknowledge-real-effects and --acknowledge-duplicate-risk after actual account review.', 2);
108
+ const intent = await remember(context, row, key);
109
+ output('review_request', { request_key: key, operation_id: row.id }, `Review request: ${key}\nIf the reply is interrupted: firedrill real-operations recover --workspace ${context.workspace} --request-key ${key}`);
110
+ const body = { request_key: key, acknowledge_external_effects: true, acknowledge_duplicate_risk: true };
111
+ const result = await api(path(context, row.id), { ...auth(context, options), method: 'POST', body });
112
+ if (!realOperationMatches(result, selection(context), row.id, key))
113
+ throw invalid();
114
+ if (receipt(result, intent) !== 'accepted')
115
+ throw invalid();
116
+ output('review_receipt', result.review_receipt, 'Review recorded. The original operation was not repeated or marked successful.');
117
+ show(result, output);
118
+ }
119
+ function filters(options, page, search) {
120
+ const values = new URLSearchParams({ page: String(page), q: search });
121
+ if (options.service)
122
+ values.set('service_id', identifier(options.service));
123
+ if (options.outcome) {
124
+ if (!Object.hasOwn(names, options.outcome))
125
+ throw new CliError('arguments_invalid', 'Choose admitted, dispatching, response, not_sent or uncertain.', 2);
126
+ values.set('outcome', options.outcome);
127
+ }
128
+ if (options.needsReview)
129
+ values.set('review', 'required');
130
+ return values;
131
+ }
132
+ export async function realOperationsCommand(args, options, output) {
133
+ if (args.length > 2 || options.page !== undefined && !/^[1-9][0-9]{0,8}$/.test(options.page) || (options.search?.length ?? 0) > 200) {
134
+ throw new CliError('arguments_invalid', 'Use real-operations [list|show|review|recover|forget], scoped paging and exact request options.', 2);
135
+ }
136
+ const action = args[0] ?? 'list';
137
+ if (!['list', 'show', 'review', 'recover', 'forget'].includes(action)
138
+ || ['show', 'review'].includes(action) && !args[1] || ['list', 'recover', 'forget'].includes(action) && args[1]
139
+ || ['recover', 'forget'].includes(action) && !options.request
140
+ || action !== 'review' && (options.effects !== undefined || options.duplicateRisk !== undefined)
141
+ || !['review', 'recover', 'forget'].includes(action) && options.request !== undefined
142
+ || action !== 'list' && [options.page, options.search, options.service, options.outcome, options.needsReview].some(value => value !== undefined)) {
143
+ throw new CliError('arguments_invalid', 'Use list filters only for history, an operation ID for show/review, and the exact --request-key for recover/forget.', 2);
144
+ }
145
+ const selecting = interactive(options);
146
+ if (action === 'list')
147
+ filters(options, Number(options.page ?? 1), options.search ?? '');
148
+ if (action === 'recover' || action === 'forget') {
149
+ const scope = await selectedWorkspace({ ...options, interactive: selecting });
150
+ const intent = await saved(scope, identifier(options.request));
151
+ if (options.world && identifier(options.world) !== intent.world)
152
+ throw invalid();
153
+ if (action === 'forget') {
154
+ await unlink(await location(scope, intent.key));
155
+ output('review_receipt', { request_key: intent.key, local_copy_removed: true }, 'Only this terminal\'s recovery copy was removed. Any accepted or delayed review is unchanged.');
156
+ return;
157
+ }
158
+ const context = await selectedWorld({ ...options, expectedOwner: scope.owner, workspace: scope.workspace, world: intent.world, interactive: false });
159
+ const row = await readOperation(context, intent.operation, options, intent.key);
160
+ const state = receipt(row, intent);
161
+ show(row, output);
162
+ output('review_receipt', row.review_receipt, state === 'accepted' ? 'Original review accepted. No request was repeated.'
163
+ : state === 'reviewed_by_another_request' ? 'Another review was recorded. Nothing was changed.'
164
+ : `No accepted review receipt is visible. A delayed review may still arrive. No provider operation is resent. To explicitly retry only this same review: firedrill real-operations review ${intent.operation} --workspace ${scope.workspace} --world ${intent.world} --request-key ${intent.key}`);
165
+ return;
166
+ }
167
+ if (options.service)
168
+ identifier(options.service);
169
+ const context = await selectedWorld({ ...options, interactive: selecting });
170
+ if (action !== 'list') {
171
+ const row = await readOperation(context, identifier(args[1]), options);
172
+ if (action === 'show')
173
+ show(row, output);
174
+ else
175
+ await review(context, row, options, output);
176
+ return;
177
+ }
178
+ let page = Number(options.page ?? 1), search = options.search ?? '';
179
+ while (true) {
180
+ const result = await api(`${context.prefix}/real-operations?${filters(options, page, search)}`, auth(context, options));
181
+ if (!realOperationsMatch(result, selection(context), { page, query: search, outcome: options.outcome,
182
+ service: options.service, review: options.needsReview ? 'required' : '' }))
183
+ throw invalid();
184
+ output('real_operations', result, result.results.length ? result.results.map(row => `${row.id} ${row.operation} ${names[row.outcome] ?? row.outcome}${row.requires_review ? ' · Review required' : ''} ${row.created_at}`).join('\n')
185
+ + `\nPage ${result.page} · ${result.count} recorded requests` : 'No matching real-account requests.');
186
+ if (!selecting || args[0] === 'list')
187
+ return;
188
+ const chosen = await selectOne('Real-account history', [...result.results.map(row => ({ value: row.id, label: row.operation,
189
+ detail: `${row.service_name} · ${row.requires_review ? 'Review required' : names[row.outcome]} · ${row.created_at}` })),
190
+ ...(page > 1 ? [{ value: 'previous', label: 'Previous page' }] : []), ...(result.next ? [{ value: 'next', label: 'Next page' }] : []),
191
+ { value: 'search', label: 'Search operations' }, { value: 'done', label: 'Back' }], options.signal);
192
+ if (chosen === 'done')
193
+ return;
194
+ if (chosen === 'previous') {
195
+ page -= 1;
196
+ continue;
197
+ }
198
+ if (chosen === 'next') {
199
+ page += 1;
200
+ continue;
201
+ }
202
+ if (chosen === 'search') {
203
+ const input = createInterface({ input: process.stdin, output: process.stderr, terminal: false });
204
+ try {
205
+ search = (await input.question('Operation name (blank for all): ', { signal: options.signal })).trim();
206
+ }
207
+ finally {
208
+ input.close();
209
+ }
210
+ if (search.length > 200 || /[\u0000-\u001f\u007f]/.test(search))
211
+ throw new CliError('arguments_invalid', 'Use an operation search up to 200 characters.', 2);
212
+ page = 1;
213
+ continue;
214
+ }
215
+ const row = await readOperation(context, identifier(chosen), options);
216
+ show(row, output);
217
+ if (row.review_available && await selectOne('Request outcome', [{ value: false, label: 'Back to history' },
218
+ { value: true, label: 'Review uncertain outcome' }], options.signal))
219
+ await review(context, row, options, output);
220
+ }
221
+ }
@@ -0,0 +1,58 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { open } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { privateDirectory } from './credentials.js';
5
+ import { readMetadata } from './configuration-intents.js';
6
+ import { CliError } from './http.js';
7
+ import { identifier } from './world-contracts.js';
8
+ import { recipeNameMatches } from '../../contracts/world-recipe-contracts.js';
9
+ const invalid = () => new CliError('world_recipe_intent_invalid', 'Keep the exact original saved recipe metadata. A newer world version was not substituted.', 2);
10
+ const digest = (body) => createHash('sha256').update(JSON.stringify([body.name, body.world_revision, body.configuration_revision])).digest('hex');
11
+ const location = async (scope, key) => join(await privateDirectory(), `recipe-${identifier(scope.owner)}-${identifier(scope.workspace)}-${identifier(scope.world)}-${identifier(key)}.json`);
12
+ function bodyMatches(value) {
13
+ const row = value;
14
+ return !!row && typeof row === 'object' && Object.keys(row).sort().join(',') === 'configuration_revision,name,world_revision'
15
+ && recipeNameMatches(row.name) && Number.isSafeInteger(row.world_revision) && row.world_revision > 0
16
+ && Number.isSafeInteger(row.configuration_revision) && row.configuration_revision > 0;
17
+ }
18
+ export async function retainedRecipe(scope, key) {
19
+ let value;
20
+ try {
21
+ value = await readMetadata(await location(scope, key), true);
22
+ }
23
+ catch (error) {
24
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
25
+ return null;
26
+ throw invalid();
27
+ }
28
+ if (Object.keys(value).sort().join(',') !== 'body,digest,key,owner,schema,workspace,world'
29
+ || value.schema !== 'firedrill.recipe-save-intent.v1' || value.owner !== scope.owner || value.workspace !== scope.workspace
30
+ || value.world !== scope.world || value.key !== key || !bodyMatches(value.body) || value.digest !== digest(value.body))
31
+ throw invalid();
32
+ return value;
33
+ }
34
+ export async function rememberRecipe(scope, key, body) {
35
+ if (!bodyMatches(body))
36
+ throw invalid();
37
+ const value = { schema: 'firedrill.recipe-save-intent.v1', owner: scope.owner, workspace: scope.workspace,
38
+ world: scope.world, key: identifier(key), body, digest: digest(body) };
39
+ try {
40
+ const file = await open(await location(scope, key), 'wx', 0o600);
41
+ try {
42
+ await file.writeFile(JSON.stringify(value));
43
+ await file.sync();
44
+ }
45
+ finally {
46
+ await file.close();
47
+ }
48
+ }
49
+ catch (error) {
50
+ if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST')
51
+ throw error;
52
+ const prior = await retainedRecipe(scope, key);
53
+ if (!prior || prior.digest !== value.digest)
54
+ throw invalid();
55
+ return prior;
56
+ }
57
+ return value;
58
+ }
@@ -0,0 +1,59 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { open } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { privateDirectory } from './credentials.js';
5
+ import { readMetadata } from './configuration-intents.js';
6
+ import { CliError } from './http.js';
7
+ import { identifier } from './world-contracts.js';
8
+ const invalid = () => new CliError('context_original_metadata_required', 'Keep the exact original recording metadata and request ID. A different file was not substituted.', 2);
9
+ const hash = (value) => createHash('sha256').update(JSON.stringify([value.filename, value.size, value.sha256, value.format])).digest('hex');
10
+ const location = async (scope, key) => join(await privateDirectory(), `context-${identifier(scope.owner)}-${identifier(scope.workspace)}-${identifier(scope.world)}-${identifier(key)}.json`);
11
+ function valid(value) {
12
+ const row = value;
13
+ return !!row && typeof row === 'object' && Object.keys(row).sort().join(',') === 'content_upload_consent,filename,format,preparation_processing_consent,sha256,size'
14
+ && typeof row.filename === 'string' && row.filename.length <= 255 && row.filename.toLowerCase().endsWith('.json') && !/[\u0000-\u001f\u007f/\\]/.test(row.filename)
15
+ && Number.isSafeInteger(row.size) && row.size > 0 && row.size <= 64 * 1024 ** 2 && /^[a-f0-9]{64}$/.test(row.sha256)
16
+ && ['incident_json', 'otlp_json', 'langfuse_json', 'har_json'].includes(row.format)
17
+ && row.content_upload_consent === true && row.preparation_processing_consent === true;
18
+ }
19
+ export async function retainedRecording(scope, key) {
20
+ let value;
21
+ try {
22
+ value = await readMetadata(await location(scope, key), true);
23
+ }
24
+ catch (error) {
25
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
26
+ return null;
27
+ throw invalid();
28
+ }
29
+ if (Object.keys(value).sort().join(',') !== 'digest,key,owner,schema,upload,workspace,world'
30
+ || value.schema !== 'firedrill.recording-upload-intent.v1' || value.owner !== scope.owner || value.workspace !== scope.workspace
31
+ || value.world !== scope.world || value.key !== key || !valid(value.upload) || value.digest !== hash(value.upload))
32
+ throw invalid();
33
+ return value;
34
+ }
35
+ export async function rememberRecording(scope, key, upload) {
36
+ if (!valid(upload))
37
+ throw invalid();
38
+ const value = { schema: 'firedrill.recording-upload-intent.v1', owner: scope.owner, workspace: scope.workspace,
39
+ world: scope.world, key: identifier(key), upload, digest: hash(upload) };
40
+ try {
41
+ const file = await open(await location(scope, key), 'wx', 0o600);
42
+ try {
43
+ await file.writeFile(JSON.stringify(value));
44
+ await file.sync();
45
+ }
46
+ finally {
47
+ await file.close();
48
+ }
49
+ }
50
+ catch (error) {
51
+ if (!error || typeof error !== 'object' || !('code' in error) || error.code !== 'EEXIST')
52
+ throw error;
53
+ const prior = await retainedRecording(scope, key);
54
+ if (!prior || prior.digest !== value.digest)
55
+ throw invalid();
56
+ return prior;
57
+ }
58
+ return value;
59
+ }
@@ -0,0 +1,248 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { open } from 'node:fs/promises';
4
+ import { basename } from 'node:path';
5
+ import { Readable } from 'node:stream';
6
+ import { api } from './auth.js';
7
+ import { CliError } from './http.js';
8
+ import { selectedWorld } from './world-access.js';
9
+ import { identifier } from './world-contracts.js';
10
+ import { selectOne } from './prompts.js';
11
+ import { rememberRecording, retainedRecording } from './recorded-context-intents.js';
12
+ import { recordingUploadDestination, recordingMatches, recordingsMatch, recordingRequestMatches, recordedObservationsMatch } from '../../contracts/context-contracts.js';
13
+ const formats = ['incident_json', 'otlp_json', 'langfuse_json', 'har_json'];
14
+ const actions = new Set(['list', 'import', 'request', 'withdraw', 'show', 'records', 'resume', 'complete', 'accept', 'discover', 'remove']);
15
+ const invalid = () => new CliError('context_receipt_unconfirmed', 'The original import could not be confirmed. Inspect its request before changing or importing it again.');
16
+ const privateName = /(?:^|[._-])(?:authorization|cookie|set_cookie|password|passwd|api_key|apikey|access_token|refresh_token|private_key|secret)(?:$|[._-])/i;
17
+ const privateValue = /\bBearer\s+[A-Za-z0-9._~+/-]{12,}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\b(?:sk-ant-|sk-proj-|ghp_|github_pat_)[A-Za-z0-9_-]{12,}|https?:\/\/[^\s/]+:[^\s/]+@|[?&](?:token|api_key|signature)=[^\s&#]+/i;
18
+ function inspectLocal(bytes) {
19
+ let document;
20
+ try {
21
+ document = JSON.parse(new TextDecoder('utf8', { fatal: true }).decode(bytes));
22
+ }
23
+ catch {
24
+ throw new CliError('context_invalid_json', 'Choose a valid UTF-8 JSON export.', 2);
25
+ }
26
+ const stack = [{ value: document, depth: 0 }];
27
+ let count = 0;
28
+ while (stack.length) {
29
+ const { value, depth } = stack.pop();
30
+ if (++count > 2000000 || depth > 32)
31
+ throw new CliError('context_capacity_required', 'Import a smaller bounded recording.', 2);
32
+ if (typeof value === 'string' && privateValue.test(value))
33
+ throw new CliError('context_credentials_detected', 'Remove credentials from this recording before importing. No file was transferred.', 2);
34
+ if (value && typeof value === 'object') {
35
+ for (const [key, child] of Object.entries(value)) {
36
+ if (privateName.test(key) && child !== null && child !== '' && child !== '[redacted]' && child !== '[REDACTED]')
37
+ throw new CliError('context_credentials_detected', 'Remove credentials and authentication headers before importing. No file was transferred.', 2);
38
+ stack.push({ value: child, depth: depth + 1 });
39
+ }
40
+ }
41
+ }
42
+ }
43
+ export async function recordedContextCommand(args, options, output, selected) {
44
+ const action = args[0] ?? 'list';
45
+ if (!actions.has(action) || args.length > 2)
46
+ throw new CliError('arguments_invalid', 'Use context list|import|request|withdraw|show|records|resume|complete|accept|discover|remove.', 2);
47
+ if (['list', 'import'].includes(action) && args.length > 1
48
+ || !['list', 'import', 'request'].includes(action) && args.length !== 2
49
+ || options.file !== undefined && !['import', 'resume'].includes(action) || options.format !== undefined && action !== 'import'
50
+ || options.request !== undefined && !['import', 'request', 'discover'].includes(action)
51
+ || (options.page !== undefined || options.search !== undefined) && !['list', 'records'].includes(action)
52
+ || options.approve !== undefined && !['import', 'resume', 'accept', 'discover', 'remove', 'withdraw'].includes(action))
53
+ throw new CliError('arguments_invalid', 'Use context import with --file, or supply the exact recording ID for a saved-record action.', 2);
54
+ const interactive = !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY;
55
+ const scope = selected ?? await selectedWorld({ world: options.world, workspace: options.workspace, interactive,
56
+ signal: options.signal, openBrowser: options.openBrowser });
57
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: options.signal }, base = scope.prefix + '/contexts';
58
+ const identity = { workspace: scope.workspace, world: scope.world };
59
+ const confirm = async (message) => {
60
+ if (options.approve)
61
+ return;
62
+ if (!interactive || !await selectOne(message, [{ value: false, label: 'Not now' }, { value: true, label: 'Approve' }], options.signal))
63
+ throw new CliError('context_approval_required', 'Review this import or change, then use --approve-change. No agent is run by this action.', 2);
64
+ };
65
+ const show = (row) => {
66
+ if (!recordingMatches(row))
67
+ throw invalid();
68
+ identifier(row.id);
69
+ output('recorded_context', row, `${row.filename}: ${row.state}. No target execution was started.`);
70
+ };
71
+ const lookup = async (key) => {
72
+ const row = await api(`${base}/requests/${identifier(key)}`, auth);
73
+ if (!recordingRequestMatches(row, identity, key))
74
+ throw invalid();
75
+ return row;
76
+ };
77
+ if (action === 'list') {
78
+ const page = Number(options.page ?? '1');
79
+ if (!Number.isSafeInteger(page) || page < 1 || page > 999999999 || (options.search?.length ?? 0) > 200)
80
+ throw new CliError('query_invalid', 'Use a positive page and a search of up to 200 characters.', 2);
81
+ const rows = await api(`${base}?${new URLSearchParams({ page: String(page), q: options.search ?? '' })}`, auth);
82
+ if (!recordingsMatch(rows, identity, page, options.search ?? ''))
83
+ throw invalid();
84
+ output('recorded_contexts', rows, rows.results.map(row => `${row.id} ${row.filename} ${row.state}`).join('\n') || 'No imported recordings. Source alone is enough to prepare.');
85
+ return;
86
+ }
87
+ if (action === 'request') {
88
+ const key = identifier(args[1] ?? options.request ?? '');
89
+ const row = await lookup(key);
90
+ output('recorded_context_request', row, `Import ${key}: ${row.state}.`);
91
+ return;
92
+ }
93
+ if (action === 'withdraw') {
94
+ const key = identifier(args[1]), original = await retainedRecording(scope, key);
95
+ const before = await lookup(key);
96
+ if (before.recording && before.recording.state !== 'uploading') {
97
+ show(before.recording);
98
+ return;
99
+ }
100
+ if (!original)
101
+ throw new CliError('context_original_metadata_required', 'This device does not retain the original approved file metadata. Recover its upload in the portal or use the exact API request metadata. No replacement was created.', 2);
102
+ await confirm('Withdraw only this original unfinished recording upload? A delayed create must not revive it.');
103
+ const row = await api(`${base}/requests/${key}`, { ...auth, method: 'DELETE', body: { upload: original.upload } });
104
+ if (!recordingRequestMatches(row, identity, key))
105
+ throw invalid();
106
+ output('recorded_context_request', row, row.recording?.state === 'revoked' ? 'Original upload withdrawn. It cannot be resumed.' : 'The original recording was already received. Inspect its status; it was not removed.');
107
+ return;
108
+ }
109
+ if (action === 'show' || action === 'records') {
110
+ const id = identifier(args[1] ?? '');
111
+ if (action === 'show') {
112
+ const row = await api(`${base}/${id}`, auth);
113
+ if (!recordingMatches(row, id))
114
+ throw invalid();
115
+ show(row);
116
+ return;
117
+ }
118
+ const page = Number(options.page ?? '1');
119
+ if (!Number.isSafeInteger(page) || page < 1 || page > 999999999)
120
+ throw new CliError('query_invalid', 'Choose a positive record page.', 2);
121
+ if ((options.search?.length ?? 0) > 200)
122
+ throw new CliError('query_invalid', 'Use a search of up to 200 characters.', 2);
123
+ const rows = await api(`${base}/${id}/records?page=${page}&q=${encodeURIComponent(options.search ?? '')}`, auth);
124
+ if (!recordedObservationsMatch(rows, identity, id, page, options.search ?? ''))
125
+ throw invalid();
126
+ output('recorded_observations', rows, rows.results.map(row => JSON.stringify(row)).join('\n') || 'No retained observations on this page.');
127
+ return;
128
+ }
129
+ if (action === 'accept') {
130
+ const id = identifier(args[1] ?? '');
131
+ const row = await api(`${base}/${id}`, auth);
132
+ if (!recordingMatches(row, id))
133
+ throw invalid();
134
+ show(row);
135
+ const world = await api(scope.prefix + '/preparation', auth);
136
+ await confirm('Use these reviewed records as untrusted preparation context? They do not authorize real actions or establish a correct outcome.');
137
+ show(await api(`${base}/${id}/accept`, { ...auth, method: 'POST', body: {
138
+ world_revision: world.basis.world_revision, preparation_processing_consent: true
139
+ } }));
140
+ return;
141
+ }
142
+ if (action === 'complete') {
143
+ const id = identifier(args[1] ?? '');
144
+ show(await api(`${base}/${id}/complete`, { ...auth, method: 'POST', body: {} }));
145
+ return;
146
+ }
147
+ if (action === 'discover') {
148
+ const id = identifier(args[1] ?? ''), key = options.request ? identifier(options.request) : randomUUID();
149
+ const row = await api(`${base}/${id}`, auth);
150
+ show(row);
151
+ if (row.state !== 'accepted')
152
+ throw new CliError('context_review_required', 'Review and accept this recording before requesting scenario drafts.', 2);
153
+ await confirm('Request scenario drafts from this recording and the connected source? This does not run the target.');
154
+ output('context_discovery_request', { context_id: id, request_key: key }, `Scenario discovery request ${key}. Keep this key to recover the original request.`);
155
+ const discovery = await api(scope.prefix + '/scenario-discovery', {
156
+ ...auth, method: 'POST', idempotencyKey: key, body: { context_id: id }
157
+ });
158
+ if (!discovery || discovery.request_key !== key || typeof discovery.state !== 'string')
159
+ throw invalid();
160
+ identifier(discovery.id);
161
+ output('context_scenario_discovery', discovery, `Scenario discovery ${discovery.id}: ${discovery.state}. Review with scenarios drafts. No target run started.`);
162
+ return;
163
+ }
164
+ if (action === 'remove') {
165
+ const id = identifier(args[1] ?? '');
166
+ await confirm('Remove this recording and revoke its use in future preparation?');
167
+ await api(`${base}/${id}`, { ...auth, method: 'DELETE' });
168
+ output('recording_removed', { id }, 'Recording removed.');
169
+ return;
170
+ }
171
+ let selectedFile = options.file;
172
+ if (!selectedFile && interactive) {
173
+ const { createInterface } = await import('node:readline/promises');
174
+ const prompt = createInterface({ input: process.stdin, output: process.stderr, terminal: true, historySize: 0 });
175
+ try {
176
+ selectedFile = (await prompt.question('Path to the JSON recording: ', { signal: options.signal })).trim();
177
+ }
178
+ finally {
179
+ prompt.close();
180
+ }
181
+ }
182
+ if (!selectedFile)
183
+ throw new CliError('file_required', 'Choose the original JSON export with --file. File paths and file content are not sent before approval.', 2);
184
+ const file = await open(selectedFile, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
185
+ try {
186
+ const stat = await file.stat({ bigint: true });
187
+ if (!stat.isFile() || stat.size < 1n || stat.size > 64n * 1024n ** 2n)
188
+ throw new CliError('context_capacity_required', 'Choose a regular JSON file up to 64 MiB. Narrow larger exports before importing.', 2);
189
+ const bytes = Buffer.alloc(Number(stat.size) + 1);
190
+ let size = 0;
191
+ while (size < bytes.length) {
192
+ options.signal.throwIfAborted();
193
+ const chunk = await file.read(bytes, size, Math.min(1024 * 1024, bytes.length - size), size);
194
+ if (!chunk.bytesRead)
195
+ break;
196
+ size += chunk.bytesRead;
197
+ }
198
+ if (size !== Number(stat.size))
199
+ throw new CliError('context_file_changed', 'The recording changed. No file was transferred.', 2);
200
+ const read = { bytesRead: size }, selectedBytes = bytes.subarray(0, size);
201
+ inspectLocal(selectedBytes);
202
+ const sha256 = createHash('sha256').update(selectedBytes).digest('hex');
203
+ output('context_local_review', { filename: basename(selectedFile), bytes: read.bytesRead, sha256, uploaded: false }, `${basename(selectedFile)}: ${read.bytesRead.toLocaleString()} bytes. Check personal data before approving. Credential checks are not a complete privacy filter.`);
204
+ await confirm('Upload this selected recording privately for review and optional preparation use? It cannot replay real effects or run the agent.');
205
+ let ticket, expectedId;
206
+ if (action === 'resume') {
207
+ const id = identifier(args[1] ?? '');
208
+ expectedId = id;
209
+ const resumed = await api(`${base}/${id}/upload`, { ...auth, method: 'POST', body: {} });
210
+ if (!recordingMatches(resumed, id) || resumed.state !== 'uploading' || !resumed.upload_identity || resumed.upload_identity.sha256 !== sha256 || resumed.upload_identity.size !== read.bytesRead)
211
+ throw invalid();
212
+ ticket = resumed;
213
+ }
214
+ else {
215
+ const format = options.format ?? (interactive ? await selectOne('Recording format', formats.map(value => ({ value,
216
+ label: value === 'incident_json' ? 'Incident, task or policy JSON' : value === 'otlp_json' ? 'OTLP JSON' : value === 'har_json' ? 'Sanitized HAR structural facts' : 'Langfuse observations JSON' })), options.signal) : 'incident_json');
217
+ if (!formats.includes(format))
218
+ throw new CliError('context_format_invalid', 'Choose incident_json, otlp_json, langfuse_json or har_json.', 2);
219
+ const key = options.request ? identifier(options.request) : randomUUID();
220
+ const body = { filename: basename(selectedFile), size: read.bytesRead, sha256, format: format, content_upload_consent: true, preparation_processing_consent: true };
221
+ await rememberRecording(scope, key, body);
222
+ const prior = await lookup(key);
223
+ if (prior.state !== 'not_retained') {
224
+ if (prior.recording)
225
+ show(prior.recording);
226
+ return;
227
+ }
228
+ output('context_import_request', { request_key: key }, `Import request ${key}. Recover with context request ${key}.`);
229
+ ticket = await api(base, { ...auth, method: 'POST', idempotencyKey: key, body });
230
+ if (!recordingMatches(ticket) || ticket.state !== 'uploading' || ticket.filename !== body.filename || ticket.format !== body.format)
231
+ throw invalid();
232
+ }
233
+ const destination = recordingUploadDestination(ticket, scope.workspace, expectedId);
234
+ const current = await file.stat({ bigint: true });
235
+ if (current.ino !== stat.ino || current.dev !== stat.dev || current.size !== stat.size || current.mtimeNs !== stat.mtimeNs)
236
+ throw new CliError('context_file_changed', 'The recording changed. Inspect the original request; the file was not transferred.', 2);
237
+ // Send the reviewed bytes, not a reopened or possibly replaced filesystem path.
238
+ const response = await fetch(destination, { method: 'PUT', headers: ticket.headers, body: Readable.toWeb(Readable.from([selectedBytes])),
239
+ duplex: 'half', credentials: 'omit', redirect: 'error', signal: options.signal });
240
+ await response.body?.cancel();
241
+ if (![200, 201, 412].includes(response.status))
242
+ throw new CliError('context_upload_unknown', 'Upload was not confirmed. Inspect the original import before explicitly resuming the same file.');
243
+ show(await api(`${base}/${ticket.id}/complete`, { ...auth, method: 'POST', body: {} }));
244
+ }
245
+ finally {
246
+ await file.close();
247
+ }
248
+ }