firedrill-run 2.2.5 → 3.0.1

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 +176 -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 +506 -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 +1627 -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 +96 -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,159 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { setTimeout as sleep } from 'node:timers/promises';
3
+ import { CliError } from './http.js';
4
+ import { executeLocalWorldSession } from './runner-container.js';
5
+ import { withLocalJobInputs } from './runner-ownership.js';
6
+ import { LocalWorldCommandDelivery } from './runner-world-commands.js';
7
+ import { LocalWorldJournal } from './runner-world-journal.js';
8
+ const invalid = () => new CliError('local_world_collection_unconfirmed', 'The original local world journal is incomplete. No target was restarted or cloud completion claimed.');
9
+ async function retain(journal, label, body, signal) {
10
+ if (!body.length || body.length > 12 * 1024 ** 2)
11
+ throw invalid();
12
+ const parts = [];
13
+ for (let offset = 0, index = 0; offset < body.length; offset += 1024 ** 2, index++) {
14
+ signal.throwIfAborted();
15
+ const part = body.subarray(offset, offset + 1024 ** 2);
16
+ await journal.put(label + '-part-' + index, part);
17
+ parts.push({ size: part.length, sha256: createHash('sha256').update(part).digest('hex') });
18
+ }
19
+ const metadata = Buffer.from(JSON.stringify({ schema: 'firedrill.local-world-journal-file.v1',
20
+ size: body.length, sha256: createHash('sha256').update(body).digest('hex'), parts }));
21
+ try {
22
+ await journal.put(label + '-metadata', metadata);
23
+ }
24
+ finally {
25
+ metadata.fill(0);
26
+ }
27
+ }
28
+ function collector(configuration) {
29
+ let commandEvidencePending = false;
30
+ return {
31
+ async run(owner, actual, channel, signal) {
32
+ if (actual !== configuration || actual.control.schema !== 'firedrill.world-session.v1')
33
+ throw invalid();
34
+ const delivery = new LocalWorldCommandDelivery(owner, configuration, channel, signal);
35
+ try {
36
+ while (!channel.isClosing) {
37
+ try {
38
+ const command = await delivery.next();
39
+ if (command) {
40
+ commandEvidencePending = true;
41
+ const result = command.publication_key === null ? await delivery.deliver(command) : await delivery.recover(command);
42
+ commandEvidencePending = !['observed', 'not_dispatched', 'unknown', 'stop_requested'].includes(result.state);
43
+ }
44
+ }
45
+ catch (error) {
46
+ if (!channel.isClosing || !(error instanceof CliError) || !['local_world_claim_changed', 'local_world_command_unavailable',
47
+ 'local_world_evidence_unavailable', 'local_world_file_unavailable', 'local_session_observation_unavailable'].includes(error.code))
48
+ throw error;
49
+ // Closing can revoke the execution claim while an original file
50
+ // transfer is pending. Keep the actual closed journal, not an
51
+ // execution retry, extended lease or invented cloud receipt.
52
+ commandEvidencePending = true;
53
+ }
54
+ if (!channel.isClosing)
55
+ await Promise.race([sleep(500, undefined, { signal }), channel.waitClosing()]);
56
+ }
57
+ await channel.waitClosing();
58
+ }
59
+ finally {
60
+ await delivery.close();
61
+ }
62
+ },
63
+ async collect(owner, observation, final, signal) {
64
+ if (configuration.control.schema !== 'firedrill.world-session.v1' || observation.operation_id !== owner.lease
65
+ || final.observation.operation_id !== owner.lease || final.observation.session_id !== owner.session.session_id)
66
+ throw invalid();
67
+ const journal = await LocalWorldJournal.acquire(owner, configuration.manifest_sha256, { existingOnly: true });
68
+ signal = AbortSignal.any([signal, journal.signal]);
69
+ try {
70
+ const context = Buffer.from(configuration.control.evidence_context_sha256);
71
+ try {
72
+ if (context.length !== 64 || !/^[a-f0-9]{64}$/.test(context.toString('ascii')))
73
+ throw invalid();
74
+ await journal.put('evidence-context', context);
75
+ }
76
+ finally {
77
+ context.fill(0);
78
+ }
79
+ if (final.reference) {
80
+ if (!final.statusBytes || final.statusBytes.length !== final.reference.size
81
+ || createHash('sha256').update(final.statusBytes).digest('hex') !== final.reference.sha256)
82
+ throw invalid();
83
+ await retain(journal, 'final-native-status', final.statusBytes, signal);
84
+ }
85
+ else if (final.statusBytes !== null)
86
+ throw invalid();
87
+ const body = Buffer.from(JSON.stringify(observation));
88
+ try {
89
+ await retain(journal, 'final-container-observation', body, signal);
90
+ }
91
+ finally {
92
+ body.fill(0);
93
+ }
94
+ const result = { schema: 'firedrill.local-world-collection.v1', session_id: owner.session.session_id,
95
+ operation_id: owner.lease, manifest_sha256: configuration.manifest_sha256, stage: 'local_collected',
96
+ native_status_retained: final.reference !== null, command_evidence_pending: commandEvidencePending,
97
+ cloud_completion_recorded: false, provenance: 'customer_device' };
98
+ const receipt = Buffer.from(JSON.stringify(result));
99
+ try {
100
+ await journal.put('local-collection', receipt);
101
+ }
102
+ finally {
103
+ receipt.fill(0);
104
+ }
105
+ return result;
106
+ }
107
+ finally {
108
+ await journal.close();
109
+ }
110
+ },
111
+ };
112
+ }
113
+ /** Original current-claim manual-world consumer, shared by session opening and
114
+ * explicit execution of a web-admitted assignment. Finite closing/evidence
115
+ * recovery remains separate. This never claims a target trial or assessment.
116
+ */
117
+ export async function retainLocalWorldSession(context, device, job, request, signal) {
118
+ let operation, result, executionError;
119
+ try {
120
+ result = await withLocalJobInputs(context, device, job, request, 'world_session', signal, async (owner, inputs, configuration, active) => {
121
+ if (owner.purpose !== 'world_session' || !owner.evidenceUpload)
122
+ throw invalid();
123
+ operation = owner.lease;
124
+ return executeLocalWorldSession(owner, inputs, configuration, collector(configuration), active);
125
+ });
126
+ }
127
+ catch (error) {
128
+ executionError = error;
129
+ }
130
+ const { syncLocalWorldCompletion } = await import('./runner-world-completion.js');
131
+ let closing;
132
+ let evidenceRecovery;
133
+ if (operation) {
134
+ try {
135
+ closing = await syncLocalWorldCompletion(context, operation, AbortSignal.timeout(30000));
136
+ }
137
+ catch (error) {
138
+ if (!executionError)
139
+ throw error;
140
+ }
141
+ }
142
+ if (operation && closing?.received && !closing.settled) {
143
+ try {
144
+ const { syncLocalWorldEvidence } = await import('./runner-world-evidence-sync.js');
145
+ await syncLocalWorldEvidence(context, operation, AbortSignal.timeout(30000));
146
+ closing = await syncLocalWorldCompletion(context, operation, AbortSignal.timeout(30000));
147
+ }
148
+ catch (error) {
149
+ // The original cleanup receipt is already accepted. Keep it visible when
150
+ // optional private-file recovery is interrupted or no longer authorized.
151
+ evidenceRecovery = { state: 'unconfirmed', code: error instanceof CliError ? error.code : 'local_world_evidence_unconfirmed' };
152
+ }
153
+ }
154
+ if (executionError)
155
+ throw executionError;
156
+ if (!result || !closing)
157
+ throw invalid();
158
+ return { ...result, closing, ...(evidenceRecovery ? { evidence_recovery: evidenceRecovery } : {}) };
159
+ }
@@ -0,0 +1,487 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { setTimeout as sleep } from 'node:timers/promises';
3
+ import { api } from './auth.js';
4
+ import { CliError } from './http.js';
5
+ import { identifier } from './world-contracts.js';
6
+ import { historicalRunnerSigner, signRunnerRequest } from './runner-identity.js';
7
+ import { localWorldClaimPath, localWorldClaimTime } from './runner-world-claim.js';
8
+ import { LocalWorldJournal } from './runner-world-journal.js';
9
+ import { localClosingReceiptMatches } from '../../contracts/local-evidence-contracts.js';
10
+ import { localWorldIdentityMatches, LOCAL_WORLD_IDENTITY_KEYS } from '../../contracts/local-world-native-contracts.js';
11
+ const PART = 1024 ** 2, CHUNK = 16384, MAX = 544 * 1024 ** 2, HASH = /^[a-f0-9]{64}$/;
12
+ const FINISHED = ['observed', 'not_dispatched', 'unknown', 'stop_requested'];
13
+ const KINDS = ['receipt', 'response', 'state', 'checkpoint'];
14
+ const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
15
+ const sha = (body) => createHash('sha256').update(body).digest('hex');
16
+ const invalid = () => new CliError('local_world_evidence_changed', 'The original command evidence could not be confirmed. No agent action was repeated.');
17
+ const exact = (value, keys) => Object.keys(value).sort().join(',') === [...keys].sort().join(',');
18
+ function canonical(value) {
19
+ if (Array.isArray(value))
20
+ return '[' + value.map(canonical).join(',') + ']';
21
+ if (record(value))
22
+ return '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + canonical(value[key])).join(',') + '}';
23
+ if (typeof value === 'number' && !Number.isFinite(value))
24
+ throw invalid();
25
+ const encoded = JSON.stringify(value);
26
+ if (encoded === undefined)
27
+ throw invalid();
28
+ return encoded;
29
+ }
30
+ const digest = (value) => sha(Buffer.from(canonical(value)));
31
+ const name = (command, suffix) => 'command-' + identifier(command.id) + '-' + suffix;
32
+ /** Original native observations/files only. Neither recovery nor uploading can
33
+ * publish a command or recreate a target. Disk holds encrypted chunks; the
34
+ * server separately validates the whole file and both private matcher sets.
35
+ */
36
+ export class LocalWorldCommandEvidence {
37
+ owner;
38
+ manifest;
39
+ context;
40
+ channel;
41
+ journal;
42
+ signal;
43
+ closing;
44
+ constructor(owner, manifest, context, channel, journal, signal, closing = false) {
45
+ this.owner = owner;
46
+ this.manifest = manifest;
47
+ this.context = context;
48
+ this.channel = channel;
49
+ this.journal = journal;
50
+ this.signal = AbortSignal.any([signal, journal.signal]);
51
+ this.closing = closing;
52
+ }
53
+ static async open(owner, configuration, channel, signal) {
54
+ if (configuration.control.schema !== 'firedrill.world-session.v1')
55
+ throw invalid();
56
+ const journal = await LocalWorldJournal.acquire(owner, configuration.manifest_sha256, { existingOnly: true });
57
+ const context = configuration.control.evidence_context_sha256;
58
+ if (!HASH.test(context)) {
59
+ await journal.close();
60
+ throw invalid();
61
+ }
62
+ const raw = Buffer.from(context);
63
+ try {
64
+ await journal.put('evidence-context', raw);
65
+ }
66
+ catch (error) {
67
+ await journal.close();
68
+ throw error;
69
+ }
70
+ finally {
71
+ raw.fill(0);
72
+ }
73
+ return new LocalWorldCommandEvidence(owner, configuration.manifest_sha256, context, channel, journal, signal);
74
+ }
75
+ static async recover(owner, manifest, signal) {
76
+ const journal = await LocalWorldJournal.acquire(owner, manifest, { existingOnly: true });
77
+ let raw = null;
78
+ try {
79
+ raw = await journal.get('evidence-context');
80
+ const context = raw?.toString('ascii');
81
+ if (!raw || raw.length !== 64 || !context || !HASH.test(context))
82
+ throw invalid();
83
+ return new LocalWorldCommandEvidence(owner, manifest, context, null, journal, signal, true);
84
+ }
85
+ catch (error) {
86
+ await journal.close();
87
+ throw error;
88
+ }
89
+ finally {
90
+ raw?.fill(0);
91
+ }
92
+ }
93
+ async *commands() {
94
+ // Historical evidence is corroborated per original command, so publication
95
+ // need not sort or load the complete session before retaining its first file.
96
+ for await (const id of this.journal.commandIdentities(this.signal)) {
97
+ const raw = await this.journal.get('command-' + id + '-identity');
98
+ try {
99
+ if (!raw)
100
+ throw invalid();
101
+ const value = JSON.parse(raw.toString('utf8'));
102
+ if (!record(value) || !exact(value, ['id', 'sequence', 'kind', 'sha256', 'not_after']) || value.id !== id
103
+ || typeof value.sequence !== 'string' || !/^[1-9][0-9]{0,15}$/.test(value.sequence) || !Number.isSafeInteger(Number(value.sequence))
104
+ || typeof value.kind !== 'string' || !/^[a-z_]{1,32}$/.test(value.kind) || typeof value.sha256 !== 'string' || !HASH.test(value.sha256)
105
+ || typeof value.not_after !== 'string' || !Number.isFinite(Date.parse(value.not_after)))
106
+ throw invalid();
107
+ yield value;
108
+ }
109
+ finally {
110
+ raw?.fill(0);
111
+ }
112
+ }
113
+ }
114
+ async retainFinalNative(reference) {
115
+ if (!this.closing || !Number.isSafeInteger(reference.size) || reference.size < 1 || reference.size > 8 * PART
116
+ || !HASH.test(reference.sha256))
117
+ throw invalid();
118
+ const owner = this.owner, auth = { actor: owner.owner, workspace: owner.workspace, signal: this.signal };
119
+ const path = localWorldClaimPath(owner) + '/world-final-status';
120
+ const closing = await api(localWorldClaimPath(owner) + '/completion', auth);
121
+ if (!localClosingReceiptMatches(closing, { ...owner, manifestSha256: this.manifest })
122
+ || closing.schema !== 'firedrill.local-completion-receipt.v1' || closing.job_id !== owner.job || closing.attempt_id !== owner.attempt
123
+ || closing.lease_id !== owner.lease || closing.request_key !== owner.request || closing.manifest_sha256 !== this.manifest
124
+ || closing.received !== true || closing.cleanup_recorded !== true || !HASH.test(closing.digest ?? '')
125
+ || closing.document?.schema !== 'firedrill.local-world-container-receipt.v1'
126
+ || closing.document.world_session.session_id !== owner.session.session_id
127
+ || closing.document.world_session.input_sha256 !== owner.session.input_sha256
128
+ || canonical(closing.document.evidence.native_status) !== canonical(reference))
129
+ throw invalid();
130
+ const check = (raw) => {
131
+ if (!record(raw) || !localWorldIdentityMatches(raw, owner, this.manifest)
132
+ || !exact(raw, [...LOCAL_WORLD_IDENTITY_KEYS.filter(key => key !== 'manifest_sha256'), 'schema', 'session_id', 'lease_id', 'manifest_sha256', 'input_sha256', 'closing_sha256',
133
+ 'state', 'artifact', 'provenance', 'independently_attested', 'server_time']) || raw.schema !== 'firedrill.local-world-final-receipt.v1'
134
+ || raw.session_id !== owner.session.session_id || raw.lease_id !== owner.lease || raw.manifest_sha256 !== this.manifest
135
+ || raw.input_sha256 !== owner.session.input_sha256 || raw.closing_sha256 !== closing.digest
136
+ || !['pending', 'retained', 'withheld', 'invalid', 'unavailable'].includes(String(raw.state))
137
+ || raw.provenance !== 'customer_device' || raw.independently_attested !== false
138
+ || typeof raw.server_time !== 'string' || !Number.isFinite(Date.parse(raw.server_time)))
139
+ throw invalid();
140
+ if (raw.state === 'retained') {
141
+ const item = raw.artifact;
142
+ if (!record(item) || !exact(item, ['id', 'size', 'sha256', 'generation']) || typeof item.id !== 'string'
143
+ || identifier(item.id) !== item.id || item.size !== reference.size || item.sha256 !== reference.sha256
144
+ || typeof item.generation !== 'string' || !/^[1-9][0-9]*$/.test(item.generation))
145
+ throw invalid();
146
+ }
147
+ else if (raw.artifact !== null)
148
+ throw invalid();
149
+ return raw;
150
+ };
151
+ let receipt = check(await api(path, auth));
152
+ if (receipt.state !== 'pending')
153
+ return receipt;
154
+ if (closing.settled !== false)
155
+ throw invalid();
156
+ const metadata = await this.journal.get('final-native-status-metadata');
157
+ let body = null;
158
+ try {
159
+ if (!metadata)
160
+ throw new CliError('local_world_final_unavailable', 'The original final status was not saved locally. No execution was repeated.');
161
+ const value = JSON.parse(metadata.toString('utf8'));
162
+ if (!record(value) || !exact(value, ['schema', 'size', 'sha256', 'parts'])
163
+ || value.schema !== 'firedrill.local-world-journal-file.v1')
164
+ throw invalid();
165
+ const plan = { size: value.size, sha256: value.sha256, parts: value.parts };
166
+ this.checkMeta(plan, { ...reference, maximum: 8 * PART });
167
+ body = Buffer.alloc(reference.size);
168
+ let offset = 0;
169
+ for (let index = 0; index < plan.parts.length; index++) {
170
+ this.signal.throwIfAborted();
171
+ const part = await this.journal.get('final-native-status-part-' + index), expected = plan.parts[index];
172
+ try {
173
+ if (!part || part.length !== expected.size || sha(part) !== expected.sha256)
174
+ throw invalid();
175
+ part.copy(body, offset);
176
+ offset += part.length;
177
+ }
178
+ finally {
179
+ part?.fill(0);
180
+ }
181
+ }
182
+ if (offset !== body.length || sha(body) !== reference.sha256)
183
+ throw invalid();
184
+ const signer = await historicalRunnerSigner(owner, owner.device, this.signal);
185
+ const payload = { schema: 'firedrill.local-world-final-write.v1', job_id: owner.job, attempt_id: owner.attempt,
186
+ lease_id: owner.lease, request_key: owner.request, device_key: owner.device, native_session_id: signer.nativeSession,
187
+ manifest_sha256: this.manifest, session_id: owner.session.session_id, input_sha256: owner.session.input_sha256,
188
+ context_sha256: this.context, closing_sha256: closing.digest, write_key: randomUUID(), sent_at_ms: Date.parse(receipt.server_time),
189
+ size: body.length, sha256: reference.sha256, content_base64: body.toString('base64') };
190
+ const proof = await signer.sign(payload);
191
+ try {
192
+ receipt = check(await api(path, { ...auth, method: 'POST', body: proof }));
193
+ }
194
+ catch (error) {
195
+ const observed = check(await api(path, auth));
196
+ if (observed.state === 'pending')
197
+ throw error;
198
+ receipt = observed; // Read original write intent, never automatic resubmission.
199
+ }
200
+ return receipt;
201
+ }
202
+ finally {
203
+ metadata?.fill(0);
204
+ body?.fill(0);
205
+ }
206
+ }
207
+ async remember(command) {
208
+ const body = Buffer.from(canonical(Object.fromEntries(['id', 'sequence', 'kind', 'sha256', 'not_after'].map(key => [key, command[key]]))));
209
+ try {
210
+ await this.journal.put(name(command, 'identity'), body);
211
+ }
212
+ finally {
213
+ body.fill(0);
214
+ }
215
+ }
216
+ inspectObservation(command, value) {
217
+ if (!record(value) || value.schema !== 'firedrill.world-session-command-observation.v1' || value.session_id !== this.owner.session.session_id
218
+ || value.operation_id !== this.owner.lease || value.command_id !== command.id || value.command_sha256 !== command.sha256
219
+ || value.sequence !== Number(command.sequence) || !['queued', 'accepted', ...FINISHED].includes(String(value.state)))
220
+ throw invalid();
221
+ const finished = FINISHED.includes(String(value.state));
222
+ if (!exact(value, ['schema', 'session_id', 'operation_id', 'command_id', 'sequence', 'command_sha256', 'state', 'accepted_at', 'finished_at',
223
+ ...(finished ? ['code', 'response'] : [])])
224
+ || (value.state === 'queued' ? value.accepted_at !== null
225
+ : typeof value.accepted_at !== 'string' || !Number.isFinite(Date.parse(value.accepted_at)))
226
+ || (finished ? typeof value.finished_at !== 'string' || !Number.isFinite(Date.parse(value.finished_at)) : value.finished_at !== null)
227
+ || finished && (typeof value.code !== 'string' || !/^([a-z][a-z0-9_]{0,127})?$/.test(value.code)
228
+ || (value.state === 'observed' ? !record(value.response) : value.response !== null))
229
+ || finished && typeof value.accepted_at === 'string' && typeof value.finished_at === 'string'
230
+ && Date.parse(value.finished_at) < Date.parse(value.accepted_at))
231
+ throw invalid();
232
+ // Exact kind-specific native response validation remains server-owned. This
233
+ // declaration is not a successful outcome until that original API accepts it.
234
+ }
235
+ async saved(command) {
236
+ await this.remember(command);
237
+ const raw = await this.journal.get(name(command, 'terminal'));
238
+ if (!raw)
239
+ return null;
240
+ try {
241
+ if (raw.length > (['app_save', 'paired_save'].includes(command.kind) ? 2 * PART : 65536))
242
+ throw invalid();
243
+ const value = JSON.parse(raw.toString('utf8'));
244
+ this.inspectObservation(command, value);
245
+ if (!FINISHED.includes(value.state))
246
+ throw invalid();
247
+ return value;
248
+ }
249
+ finally {
250
+ raw.fill(0);
251
+ }
252
+ }
253
+ async request(command, input) {
254
+ this.signal.throwIfAborted();
255
+ const owner = this.owner;
256
+ let sent, nativeSession = owner.nativeSession, signer = null;
257
+ if (this.closing) {
258
+ const receipt = await api(localWorldClaimPath(owner) + '/completion', { actor: owner.owner, workspace: owner.workspace, signal: this.signal });
259
+ if (!localClosingReceiptMatches(receipt, { ...owner, manifestSha256: this.manifest })
260
+ || receipt.schema !== 'firedrill.local-completion-receipt.v1' || receipt.job_id !== owner.job || receipt.attempt_id !== owner.attempt
261
+ || receipt.lease_id !== owner.lease || receipt.request_key !== owner.request || receipt.manifest_sha256 !== this.manifest
262
+ || receipt.received !== true || receipt.cleanup_recorded !== true || receipt.settled !== false
263
+ || receipt.document?.schema !== 'firedrill.local-world-container-receipt.v1'
264
+ || receipt.document.world_session.session_id !== owner.session.session_id
265
+ || receipt.document.world_session.input_sha256 !== owner.session.input_sha256 || !Number.isFinite(Date.parse(receipt.server_time)))
266
+ throw invalid();
267
+ sent = Date.parse(receipt.server_time);
268
+ signer = await historicalRunnerSigner(owner, owner.device, this.signal);
269
+ nativeSession = signer.nativeSession;
270
+ }
271
+ else
272
+ sent = await localWorldClaimTime(owner, this.signal);
273
+ const base = { job_id: owner.job, attempt_id: owner.attempt, lease_id: owner.lease,
274
+ request_key: owner.request, device_key: owner.device, native_session_id: nativeSession, manifest_sha256: this.manifest,
275
+ session_id: owner.session.session_id, input_sha256: owner.session.input_sha256, command_id: command.id, command_sha256: command.sha256,
276
+ context_sha256: this.context, write_key: randomUUID(), sent_at_ms: sent };
277
+ let payload;
278
+ if (input.kind === 'observation') {
279
+ payload = this.closing ? { ...base, schema: 'firedrill.local-world-closing-command-result.v1', observation: input.observation }
280
+ : { ...base, schema: 'firedrill.local-world-command-result.v1', observation: input.observation };
281
+ }
282
+ else {
283
+ const file = { ...base, kind: input.fileKind, evidence_id: input.evidenceId, ...input.action };
284
+ payload = this.closing ? { ...file, schema: 'firedrill.local-world-closing-file-write.v1' }
285
+ : { ...file, schema: 'firedrill.local-world-file-write.v1' };
286
+ }
287
+ const suffix = input.kind === 'file' ? '/files/' + input.fileKind : '/evidence';
288
+ const proof = signer ? await signer.sign(payload) : await signRunnerRequest(owner, owner.device, payload, this.signal);
289
+ return api(localWorldClaimPath(owner) + (this.closing ? '/world-command-history/' : '/world-commands/') + identifier(command.id) + suffix, { actor: owner.owner, workspace: owner.workspace, signal: this.signal, method: 'POST', body: proof });
290
+ }
291
+ inspectReceipt(command, observation, raw) {
292
+ if (!record(raw) || !localWorldIdentityMatches(raw, this.owner, this.manifest)
293
+ || !exact(raw, [...LOCAL_WORLD_IDENTITY_KEYS, 'schema', 'evidence_id', 'command_id', 'session_id', 'lease_id', 'command_sha256', 'observation_sha256',
294
+ 'native_state', 'state', 'required_files', 'files', 'server_time', 'provenance'])
295
+ || raw.schema !== 'firedrill.local-world-command-evidence-receipt.v1' || typeof raw.evidence_id !== 'string'
296
+ || identifier(raw.evidence_id) !== raw.evidence_id || raw.command_id !== command.id || raw.session_id !== this.owner.session.session_id
297
+ || raw.lease_id !== this.owner.lease || raw.command_sha256 !== command.sha256 || raw.observation_sha256 !== digest(observation)
298
+ || raw.native_state !== observation.state || !['observing', 'awaiting_files', 'finalized', 'unavailable'].includes(String(raw.state))
299
+ || !record(raw.required_files) || !Array.isArray(raw.files) || raw.files.length > 4 || typeof raw.server_time !== 'string'
300
+ || !Number.isFinite(Date.parse(raw.server_time)) || raw.provenance !== 'customer_device')
301
+ throw invalid();
302
+ const required = Object.keys(raw.required_files);
303
+ if (FINISHED.includes(observation.state) ? !required.includes('receipt') : required.length !== 0)
304
+ throw invalid();
305
+ if (required.some(kind => !KINDS.includes(kind)))
306
+ throw invalid();
307
+ for (const [kind, plan] of Object.entries(raw.required_files)) {
308
+ if (!record(plan) || !exact(plan, ['size', 'sha256', 'maximum']) || !Number.isSafeInteger(plan.maximum)
309
+ || Number(plan.maximum) < 1 || Number(plan.maximum) > MAX || kind === 'receipt' && Number(plan.maximum) > 2 * PART
310
+ || plan.size !== null && (!Number.isSafeInteger(plan.size) || Number(plan.size) < 1 || Number(plan.size) > Number(plan.maximum))
311
+ || plan.sha256 !== null && (typeof plan.sha256 !== 'string' || !HASH.test(plan.sha256))
312
+ || (plan.size === null) !== (plan.sha256 === null) || plan.size === null && kind !== 'receipt')
313
+ throw invalid();
314
+ }
315
+ if (raw.state === 'unavailable')
316
+ throw new CliError('local_world_evidence_unavailable', 'The original action has unavailable evidence. Inspect its saved report; do not repeat the action.');
317
+ const seen = new Set();
318
+ for (const file of raw.files) {
319
+ if (!record(file) || !exact(file, ['id', 'kind', 'size', 'sha256', 'state', 'received_parts', 'expires_at'])
320
+ || typeof file.id !== 'string' || identifier(file.id) !== file.id || !KINDS.includes(file.kind) || seen.has(file.kind)
321
+ || !raw.required_files[file.kind] || !Number.isSafeInteger(file.size) || file.size < 1 || file.size > MAX
322
+ || !HASH.test(file.sha256) || !['receiving', 'verifying', 'retained', 'withheld', 'refused', 'expired'].includes(file.state)
323
+ || !Array.isArray(file.received_parts) || new Set(file.received_parts).size !== file.received_parts.length
324
+ || file.received_parts.some(index => !Number.isSafeInteger(index) || index < 0 || index >= Math.ceil(file.size / PART))
325
+ || typeof file.expires_at !== 'string' || !Number.isFinite(Date.parse(file.expires_at)))
326
+ throw invalid();
327
+ const plan = raw.required_files[file.kind];
328
+ if (file.size > plan.maximum || plan.size !== null && file.size !== plan.size
329
+ || plan.sha256 !== null && file.sha256 !== plan.sha256)
330
+ throw invalid();
331
+ seen.add(file.kind);
332
+ }
333
+ return raw;
334
+ }
335
+ inspectFile(command, evidence, kind, metadata, raw) {
336
+ const count = metadata.parts.length;
337
+ if (!record(raw) || !localWorldIdentityMatches(raw, this.owner, this.manifest)
338
+ || !exact(raw, [...LOCAL_WORLD_IDENTITY_KEYS, 'schema', 'file_id', 'evidence_id', 'command_id', 'lease_id', 'kind', 'size', 'sha256', 'state',
339
+ 'part_bytes', 'received_parts', 'expires_at', 'server_time', 'provenance']) || raw.schema !== 'firedrill.local-world-file-receipt.v1'
340
+ || typeof raw.file_id !== 'string' || identifier(raw.file_id) !== raw.file_id || raw.evidence_id !== evidence.evidence_id
341
+ || raw.command_id !== command.id || raw.lease_id !== this.owner.lease || raw.kind !== kind || raw.size !== metadata.size
342
+ || raw.sha256 !== metadata.sha256 || raw.part_bytes !== PART || !Array.isArray(raw.received_parts)
343
+ || new Set(raw.received_parts).size !== raw.received_parts.length || raw.received_parts.some(index => !Number.isSafeInteger(index) || index < 0 || index >= count)
344
+ || !['receiving', 'verifying', 'retained', 'withheld', 'refused', 'expired'].includes(String(raw.state)) || raw.provenance !== 'customer_device'
345
+ || typeof raw.server_time !== 'string' || !Number.isFinite(Date.parse(raw.server_time)) || typeof raw.expires_at !== 'string'
346
+ || !Number.isFinite(Date.parse(raw.expires_at)) || ['verifying', 'retained', 'withheld'].includes(String(raw.state)) && raw.received_parts.length !== count)
347
+ throw invalid();
348
+ if (['refused', 'expired'].includes(String(raw.state)))
349
+ throw new CliError('local_world_file_unavailable', 'The original command file is unavailable. No action was repeated.');
350
+ return raw;
351
+ }
352
+ checkMeta(metadata, plan) {
353
+ if (!record(metadata) || !exact(metadata, ['size', 'sha256', 'parts']) || !Number.isSafeInteger(metadata.size)
354
+ || Number(metadata.size) < 1 || Number(metadata.size) > plan.maximum || typeof metadata.sha256 !== 'string' || !HASH.test(metadata.sha256)
355
+ || plan.size !== null && metadata.size !== plan.size || plan.sha256 !== null && metadata.sha256 !== plan.sha256
356
+ || !Array.isArray(metadata.parts) || metadata.parts.length !== Math.ceil(Number(metadata.size) / PART))
357
+ throw invalid();
358
+ for (let index = 0; index < metadata.parts.length; index++) {
359
+ const part = metadata.parts[index];
360
+ if (!record(part) || !exact(part, ['size', 'sha256']) || part.size !== Math.min(PART, Number(metadata.size) - index * PART)
361
+ || typeof part.sha256 !== 'string' || !HASH.test(part.sha256))
362
+ throw invalid();
363
+ }
364
+ }
365
+ async collect(command, kind, plan) {
366
+ const prefix = name(command, kind), cached = await this.journal.get(prefix + '-metadata');
367
+ if (cached) {
368
+ try {
369
+ const metadata = JSON.parse(cached.toString('utf8'));
370
+ this.checkMeta(metadata, plan);
371
+ return metadata;
372
+ }
373
+ finally {
374
+ cached.fill(0);
375
+ }
376
+ }
377
+ if (this.closing || !this.channel)
378
+ throw new CliError('local_world_file_unavailable', 'These original command bytes are not in the saved local journal. No agent or native command was restarted.');
379
+ const file = await this.channel.openFile(command.id, kind), hash = createHash('sha256'), parts = [];
380
+ try {
381
+ if (file.size > plan.maximum || plan.size !== null && file.size !== plan.size || plan.sha256 !== null && file.sha256 !== plan.sha256)
382
+ throw invalid();
383
+ for (let offset = 0, index = 0; offset < file.size; offset += PART, index++) {
384
+ const body = Buffer.alloc(Math.min(PART, file.size - offset));
385
+ try {
386
+ for (let position = 0; position < body.length; position += CHUNK) {
387
+ this.signal.throwIfAborted();
388
+ const chunk = await file.read(offset + position, Math.min(CHUNK, body.length - position));
389
+ try {
390
+ chunk.copy(body, position);
391
+ }
392
+ finally {
393
+ chunk.fill(0);
394
+ }
395
+ }
396
+ hash.update(body);
397
+ parts.push({ size: body.length, sha256: sha(body) });
398
+ await this.journal.put(prefix + '-part-' + index, body);
399
+ }
400
+ finally {
401
+ body.fill(0);
402
+ }
403
+ }
404
+ if (hash.digest('hex') !== file.sha256)
405
+ throw invalid();
406
+ const metadata = { size: file.size, sha256: file.sha256, parts };
407
+ this.checkMeta(metadata, plan);
408
+ const raw = Buffer.from(JSON.stringify(metadata));
409
+ try {
410
+ await this.journal.put(prefix + '-metadata', raw);
411
+ }
412
+ finally {
413
+ raw.fill(0);
414
+ }
415
+ return metadata;
416
+ }
417
+ finally {
418
+ await file.close();
419
+ }
420
+ }
421
+ async transfer(command, evidence, kind, plan) {
422
+ const metadata = await this.collect(command, kind, plan);
423
+ const request = async (action) => this.inspectFile(command, evidence, kind, metadata, await this.request(command, { kind: 'file', fileKind: kind, evidenceId: evidence.evidence_id, action }));
424
+ let file = await request({ action: 'register', size: metadata.size, sha256: metadata.sha256 });
425
+ if (file.state === 'receiving') {
426
+ for (let index = 0; index < metadata.parts.length; index++) {
427
+ if (file.received_parts.includes(index))
428
+ continue;
429
+ const body = await this.journal.get(name(command, kind) + '-part-' + index), part = metadata.parts[index];
430
+ if (!body)
431
+ throw invalid();
432
+ try {
433
+ if (body.length !== part.size || sha(body) !== part.sha256)
434
+ throw invalid();
435
+ file = await request({ action: 'chunk', index, size: body.length, sha256: part.sha256, content_base64: body.toString('base64') });
436
+ if (!file.received_parts.includes(index))
437
+ throw invalid();
438
+ }
439
+ finally {
440
+ body.fill(0);
441
+ }
442
+ }
443
+ file = await request({ action: 'commit' });
444
+ }
445
+ while (file.state === 'verifying') {
446
+ await sleep(1000, undefined, { signal: this.signal }); // Real worker retention, never simulation progress.
447
+ file = await request({ action: 'status' });
448
+ }
449
+ if (!['retained', 'withheld'].includes(file.state))
450
+ throw invalid();
451
+ }
452
+ async retain(command, supplied) {
453
+ await this.remember(command);
454
+ this.inspectObservation(command, supplied);
455
+ const saved = await this.saved(command), observation = saved ?? supplied;
456
+ if (saved && FINISHED.includes(supplied.state) && digest(saved) !== digest(supplied))
457
+ throw invalid();
458
+ const raw = Buffer.from(JSON.stringify(observation));
459
+ try {
460
+ if (raw.length > (['app_save', 'paired_save'].includes(command.kind) ? 2 * PART : 65536))
461
+ throw invalid();
462
+ await this.journal.put(name(command, FINISHED.includes(observation.state) ? 'terminal' : 'observation'), raw, !FINISHED.includes(observation.state));
463
+ }
464
+ finally {
465
+ raw.fill(0);
466
+ }
467
+ const publish = async () => this.inspectReceipt(command, observation, await this.request(command, { kind: 'observation', observation }));
468
+ let evidence = await publish();
469
+ if (evidence.state === 'awaiting_files') {
470
+ for (const kind of KINDS)
471
+ if (evidence.required_files[kind])
472
+ await this.transfer(command, evidence, kind, evidence.required_files[kind]);
473
+ evidence = await publish();
474
+ if (evidence.state !== 'finalized')
475
+ throw invalid();
476
+ }
477
+ const receipt = Buffer.from(JSON.stringify(evidence));
478
+ try {
479
+ await this.journal.put(name(command, 'cloud-receipt'), receipt, true);
480
+ }
481
+ finally {
482
+ receipt.fill(0);
483
+ }
484
+ return observation;
485
+ }
486
+ close() { return this.journal.close(); }
487
+ }