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,268 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { performance } from 'node:perf_hooks';
3
+ import { api } from './auth.js';
4
+ import { CliError } from './http.js';
5
+ import { identifier } from './world-contracts.js';
6
+ import { signRunnerRequest } from './runner-identity.js';
7
+ import { localWorldClaimPath, localWorldClaimTime } from './runner-world-claim.js';
8
+ import { retainLocalWorldObservation } from './runner-world-observations.js';
9
+ import { LocalWorldCommandEvidence } from './runner-world-command-evidence.js';
10
+ import { localWorldIdentityMatches, LOCAL_WORLD_IDENTITY_KEYS } from '../../contracts/local-world-native-contracts.js';
11
+ const HASH = /^[a-f0-9]{64}$/, CHUNK = 65536, MAX_BODY = 8 * 1024 ** 2;
12
+ const KINDS = ['http', 'mcp', 'stop', 'surface_open', 'surface_action', 'state_capture', 'world_reset', 'checkpoint_save', 'checkpoint_load',
13
+ 'voice_start', 'voice_audio', 'voice_listen', 'voice_stop', 'app_save', 'paired_save'];
14
+ const FINISHED = ['observed', 'not_dispatched', 'unknown', 'stop_requested'];
15
+ const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
16
+ const exact = (value, fields) => Object.keys(value).sort().join(',') === [...fields].sort().join(',');
17
+ const sha = (body) => createHash('sha256').update(body).digest('hex');
18
+ const invalid = () => new CliError('local_world_command_changed', 'The saved command does not match this exact local world. No action was repeated.');
19
+ function command(value) {
20
+ if (value === null)
21
+ return;
22
+ if (!record(value) || !exact(value, ['id', 'sequence', 'kind', 'state', 'sha256', 'not_after', 'publication_key', 'publication_started_at'])
23
+ || typeof value.id !== 'string' || identifier(value.id) !== value.id
24
+ || typeof value.sequence !== 'string' || !/^[1-9][0-9]{0,15}$/.test(value.sequence)
25
+ || !Number.isSafeInteger(Number(value.sequence)) || typeof value.kind !== 'string' || !KINDS.includes(value.kind)
26
+ || typeof value.state !== 'string' || !['queued', 'publishing', 'accepted', ...FINISHED].includes(value.state)
27
+ || typeof value.sha256 !== 'string' || !HASH.test(value.sha256) || typeof value.not_after !== 'string'
28
+ || !Number.isFinite(Date.parse(value.not_after))
29
+ || (value.publication_key === null) !== (value.publication_started_at === null)
30
+ || value.publication_key !== null && (typeof value.publication_key !== 'string' || identifier(value.publication_key) !== value.publication_key
31
+ || typeof value.publication_started_at !== 'string' || !Number.isFinite(Date.parse(value.publication_started_at))))
32
+ throw invalid();
33
+ }
34
+ function same(left, right) {
35
+ return ['id', 'sequence', 'kind', 'sha256', 'not_after'].every(key => left[key] === right[key]);
36
+ }
37
+ function bytes(value, size, maximum, digest) {
38
+ if (typeof value !== 'string' || !Number.isSafeInteger(size) || size < 0 || size > maximum
39
+ || value.length !== 4 * Math.ceil(size / 3) || typeof digest !== 'string' || !HASH.test(digest))
40
+ throw invalid();
41
+ const body = Buffer.from(value, 'base64');
42
+ if (body.length !== size || body.toString('base64') !== value || sha(body) !== digest) {
43
+ body.fill(0);
44
+ throw invalid();
45
+ }
46
+ return body;
47
+ }
48
+ /** Private outbound command transport, not local-session admission. The cloud
49
+ * publication intent is committed before the sole native write. An existing or
50
+ * uncertain publication is read back by ORIGINAL id/hash, never sent again.
51
+ * Evidence collection, observation retention and lifecycle settlement are owned
52
+ * by the enclosing collector, not fabricated by returning from this transport. */
53
+ export class LocalWorldCommandDelivery {
54
+ busy = false;
55
+ retainedObservation = null;
56
+ retainedObservationShape = null;
57
+ lastObservationRetention = -Infinity;
58
+ evidence = null;
59
+ owner;
60
+ configuration;
61
+ channel;
62
+ signal;
63
+ constructor(owner, configuration, channel, signal) {
64
+ this.owner = owner;
65
+ this.configuration = configuration;
66
+ this.channel = channel;
67
+ this.signal = signal;
68
+ if (configuration.control.schema !== 'firedrill.world-session.v1' || !owner.evidenceUpload
69
+ || !HASH.test(configuration.manifest_sha256))
70
+ throw invalid();
71
+ }
72
+ evidenceStore() {
73
+ return this.evidence ??= LocalWorldCommandEvidence.open(this.owner, this.configuration, this.channel, this.signal);
74
+ }
75
+ async close() {
76
+ if (this.busy)
77
+ throw invalid();
78
+ if (this.evidence)
79
+ await (await this.evidence).close();
80
+ }
81
+ async request(input) {
82
+ const owner = this.owner, sent = await localWorldClaimTime(owner, this.signal);
83
+ const action = input.action, selected = input.action === 'next' ? undefined : input.selected;
84
+ const selection = selected ? { command_id: selected.id, command_sha256: selected.sha256 } : null;
85
+ const fields = input.action === 'next' ? { action: 'next' }
86
+ : input.action === 'packet' ? { action: 'packet', command_id: selection.command_id, command_sha256: selection.command_sha256 }
87
+ : input.action === 'publish' ? { action: 'publish', command_id: selection.command_id, command_sha256: selection.command_sha256, publication_key: input.publication_key }
88
+ : input.action === 'body' ? { action: 'body', command_id: selection.command_id, command_sha256: selection.command_sha256, offset: input.offset, length: input.length }
89
+ : { action: 'checkpoint', command_id: selection.command_id, command_sha256: selection.command_sha256, offset: input.offset, length: input.length };
90
+ const payload = { schema: 'firedrill.local-world-command-read.v1',
91
+ job_id: owner.job, attempt_id: owner.attempt, lease_id: owner.lease, request_key: owner.request,
92
+ device_key: owner.device, native_session_id: owner.nativeSession, manifest_sha256: this.configuration.manifest_sha256,
93
+ session_id: owner.session.session_id, input_sha256: owner.session.input_sha256, read_key: randomUUID(), sent_at_ms: sent, ...fields };
94
+ const proof = await signRunnerRequest(owner, owner.device, payload, this.signal);
95
+ // Neither this API client nor the private native channel retries a write.
96
+ const result = await api(localWorldClaimPath(owner) + '/world-commands', { actor: owner.owner,
97
+ workspace: owner.workspace, signal: this.signal, method: 'POST', body: proof });
98
+ const extra = action === 'packet' ? ['packet_base64', 'body'] : ['body', 'checkpoint'].includes(action) ? ['offset', 'length', 'sha256', 'content_base64'] : [];
99
+ if (!record(result) || !localWorldIdentityMatches(result, owner, this.configuration.manifest_sha256)
100
+ || !exact(result, [...LOCAL_WORLD_IDENTITY_KEYS.filter(key => key !== 'manifest_sha256'), 'schema', 'session_id', 'lease_id', 'manifest_sha256', 'input_sha256',
101
+ 'action', 'command', 'server_time', 'provenance', ...extra])
102
+ || result.schema !== 'firedrill.local-world-command-delivery.v1' || result.session_id !== owner.session.session_id
103
+ || result.lease_id !== owner.lease || result.manifest_sha256 !== this.configuration.manifest_sha256
104
+ || result.input_sha256 !== owner.session.input_sha256 || result.action !== action || result.provenance !== 'customer_device'
105
+ || typeof result.server_time !== 'string' || !Number.isFinite(Date.parse(result.server_time)))
106
+ throw invalid();
107
+ command(result.command);
108
+ if (selected && (!result.command || !same(selected, result.command)))
109
+ throw invalid();
110
+ if (result.command && Date.parse(result.command.not_after) > Date.parse(owner.session.expires_at))
111
+ throw invalid();
112
+ return result;
113
+ }
114
+ async next() {
115
+ if (this.busy)
116
+ throw invalid();
117
+ this.busy = true;
118
+ try {
119
+ await this.readObservation();
120
+ const result = await this.request({ action: 'next' });
121
+ if (result.command && FINISHED.includes(result.command.state))
122
+ throw invalid();
123
+ return result.command;
124
+ }
125
+ finally {
126
+ this.busy = false;
127
+ }
128
+ }
129
+ async observe() {
130
+ if (this.busy)
131
+ throw invalid();
132
+ this.busy = true;
133
+ try {
134
+ return await this.readObservation();
135
+ }
136
+ finally {
137
+ this.busy = false;
138
+ }
139
+ }
140
+ async readObservation() {
141
+ const observation = await this.channel.observe(), body = Buffer.from(JSON.stringify(observation));
142
+ // This is upload cadence only, not a simulation clock, invented delay or
143
+ // altered evidence. Keep changes promptly; coalesce unchanged heartbeats.
144
+ const comparable = Buffer.from(JSON.stringify({ ...observation, status: observation.status === null ? null
145
+ : Object.fromEntries(Object.entries(observation.status).filter(([key]) => !['observed_at', 'elapsed_ms'].includes(key))) }));
146
+ let digest, shape;
147
+ try {
148
+ digest = sha(body);
149
+ shape = sha(comparable);
150
+ }
151
+ finally {
152
+ body.fill(0);
153
+ comparable.fill(0);
154
+ }
155
+ if (this.retainedObservation !== digest && (this.retainedObservationShape !== shape || performance.now() - this.lastObservationRetention >= 15000)) {
156
+ await retainLocalWorldObservation(this.owner, this.configuration, observation, this.signal);
157
+ this.retainedObservation = digest;
158
+ this.retainedObservationShape = shape;
159
+ this.lastObservationRetention = performance.now();
160
+ }
161
+ return observation;
162
+ }
163
+ inspectNative(value, selected) {
164
+ if (value.session_id !== this.owner.session.session_id || value.operation_id !== this.owner.lease
165
+ || value.command_id !== selected.id || value.command_sha256 !== selected.sha256 || value.sequence !== Number(selected.sequence))
166
+ throw invalid();
167
+ return value;
168
+ }
169
+ /** Recovery is observation only. A missing native receipt stays unknown. */
170
+ async recover(selected) {
171
+ command(selected);
172
+ if (this.busy)
173
+ throw invalid();
174
+ this.busy = true;
175
+ try {
176
+ return await this.readResult(selected);
177
+ }
178
+ finally {
179
+ this.busy = false;
180
+ }
181
+ }
182
+ async readResult(selected) {
183
+ const evidence = await this.evidenceStore();
184
+ const saved = await evidence.saved(selected);
185
+ const value = this.inspectNative(saved ?? await this.channel.result(selected.id), selected);
186
+ return evidence.retain(selected, value);
187
+ }
188
+ async deliver(selected) {
189
+ command(selected);
190
+ if (this.busy || FINISHED.includes(selected.state))
191
+ throw invalid();
192
+ this.busy = true;
193
+ let packet = null, body = null;
194
+ try {
195
+ const evidence = await this.evidenceStore();
196
+ await evidence.remember(selected); // Durable encrypted identity before publication authority.
197
+ const saved = await this.request({ action: 'packet', selected }), current = saved.command;
198
+ if (current.publication_key !== null)
199
+ return await this.readResult(current);
200
+ if (current.state !== 'queued' || !record(saved.body) || !exact(saved.body, ['size', 'sha256'])
201
+ || !Number.isSafeInteger(saved.body.size) || Number(saved.body.size) < 0 || Number(saved.body.size) > MAX_BODY
202
+ || typeof saved.body.sha256 !== 'string' || !HASH.test(saved.body.sha256) || typeof saved.packet_base64 !== 'string')
203
+ throw invalid();
204
+ // Base64 length alone cannot determine the decoded size; bounded decoding
205
+ // is checked against the original retained command hash before use.
206
+ if (saved.packet_base64.length > 4 * Math.ceil(65536 / 3))
207
+ throw invalid();
208
+ packet = Buffer.from(saved.packet_base64, 'base64');
209
+ if (!packet.length || packet.length > 65536 || packet.toString('base64') !== saved.packet_base64 || sha(packet) !== selected.sha256)
210
+ throw invalid();
211
+ let document;
212
+ try {
213
+ document = JSON.parse(packet.toString('utf8'));
214
+ }
215
+ catch {
216
+ throw invalid();
217
+ }
218
+ if (!record(document) || !exact(document, ['schema', 'session_id', 'operation_id', 'command_id', 'sequence', 'kind', 'not_after', 'request'])
219
+ || document.schema !== 'firedrill.world-session-command.v1' || document.session_id !== this.owner.session.session_id
220
+ || document.operation_id !== this.owner.lease || document.command_id !== selected.id || document.sequence !== Number(selected.sequence)
221
+ || document.kind !== selected.kind || document.not_after !== selected.not_after || !record(document.request))
222
+ throw invalid();
223
+ body = Buffer.alloc(Number(saved.body.size));
224
+ for (let offset = 0; offset < body.length; offset += CHUNK) {
225
+ const length = Math.min(CHUNK, body.length - offset), part = await this.request({ action: 'body', selected, offset, length });
226
+ if (part.command.publication_key !== null)
227
+ return await this.readResult(part.command);
228
+ if (part.offset !== offset || part.length !== length)
229
+ throw invalid();
230
+ const chunk = bytes(part.content_base64, length, CHUNK, part.sha256);
231
+ try {
232
+ chunk.copy(body, offset);
233
+ }
234
+ finally {
235
+ chunk.fill(0);
236
+ }
237
+ }
238
+ if (sha(body) !== saved.body.sha256 || ['http', 'mcp'].includes(selected.kind)
239
+ && (document.request.body_size !== body.length || document.request.body_sha256 !== saved.body.sha256))
240
+ throw invalid();
241
+ if (selected.kind === 'checkpoint_load') {
242
+ if (body.length)
243
+ throw invalid();
244
+ await this.channel.checkpointInput(packet, async (offset, length) => {
245
+ const part = await this.request({ action: 'checkpoint', selected, offset, length });
246
+ if (part.command.publication_key !== null || part.command.state !== 'queued'
247
+ || part.offset !== offset || part.length !== length)
248
+ throw invalid();
249
+ return bytes(part.content_base64, length, CHUNK, part.sha256);
250
+ });
251
+ }
252
+ const publication = randomUUID(), admitted = await this.request({ action: 'publish', selected, publication_key: publication });
253
+ if (admitted.command?.publication_key !== publication || admitted.command.state !== 'publishing'
254
+ || !admitted.command.publication_started_at)
255
+ throw invalid();
256
+ // The cloud has already committed a durable intent. Loss from here can
257
+ // only trigger original result() readback, not another native publish().
258
+ this.signal.throwIfAborted();
259
+ const value = this.inspectNative(await this.channel.publish(packet, body), selected);
260
+ return await evidence.retain(selected, value);
261
+ }
262
+ finally {
263
+ packet?.fill(0);
264
+ body?.fill(0);
265
+ this.busy = false;
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,76 @@
1
+ import { CliError } from './http.js';
2
+ import { localContainerCompletion } from './runner-container.js';
3
+ import { syncClosingReceipt } from './runner-completion.js';
4
+ const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
5
+ const fail = () => new CliError('local_world_closing_changed', 'The original local world cleanup record could not be confirmed. No world or command was restarted.');
6
+ function status(value, session, operation) {
7
+ if (value === null)
8
+ return null;
9
+ if (!record(value) || value.schema !== 'firedrill.world-session-status.v7' || value.session_id !== session || value.operation_id !== operation
10
+ || value.target_invocation_observed !== false || value.behavior_assessed !== false || typeof value.state !== 'string'
11
+ || !['starting', 'active', 'resetting', 'checkpointing', 'stopping', 'stopped', 'failed'].includes(value.state)
12
+ || typeof value.code !== 'string' || value.code !== '' && !/^[a-z_]{1,64}$/.test(value.code))
13
+ throw fail();
14
+ const failed = value.state === 'failed';
15
+ // Cleanup carries lifecycle facts only. The original native status may also
16
+ // contain an applied dataset, fork, topology or private-filter reference;
17
+ // those stay in the separately gated evidence journal. Do not require their
18
+ // absence or open that journal to report this exact container's cleanup.
19
+ if (!failed && (typeof value.observed_at !== 'string' || !Number.isFinite(Date.parse(value.observed_at))
20
+ || ['elapsed_ms', 'completed_command_sequence', 'reset_count'].some(key => !Number.isSafeInteger(value[key]) || Number(value[key]) < 0)
21
+ || typeof value.native_cleanup_finished !== 'boolean' || value.native_cleanup_finished !== (value.state === 'stopped')))
22
+ throw fail();
23
+ return { schema: 'firedrill.local-world-closing-observation.v1', session_id: session, operation_id: operation,
24
+ target_invocation_observed: false, behavior_assessed: false, state: value.state, code: value.code,
25
+ observed_at: failed ? null : value.observed_at, elapsed_ms: failed ? null : value.elapsed_ms,
26
+ completed_command_sequence: failed ? null : value.completed_command_sequence,
27
+ reset_count: failed ? null : value.reset_count, native_cleanup_finished: failed ? null : value.native_cleanup_finished };
28
+ }
29
+ /** Reads existing private journals only. No Docker/provider lookup, execution
30
+ * claim/pulse, target/model/Vault activity, native publication or replacement key.
31
+ * The current authenticated member signs cleanup facts with the original device.
32
+ */
33
+ export async function syncLocalWorldCompletion(scope, operation, signal, options = {}) {
34
+ const saved = await localContainerCompletion(scope, operation, 'world_session');
35
+ const identity = saved.worldEvidenceOwner, session = saved.document.world_session;
36
+ if (!identity || !session || identity.session.session_id !== session.session_id)
37
+ throw fail();
38
+ // This reference was journaled from the actual channel's validated final
39
+ // bytes before native release. It is NOT proof the bytes were saved locally
40
+ // or retained in GCS. Reporting cleanup must not depend on decrypting files.
41
+ const reference = saved.worldFinalStatus;
42
+ const native = reference ? { size: reference.size, sha256: reference.sha256 } : null;
43
+ const document = { ...saved.document, status: status(saved.document.status, session.session_id, saved.lease),
44
+ evidence: { schema: 'firedrill.local-world-closing-evidence.v1', native_status: native } };
45
+ return syncClosingReceipt(scope, { ...saved, document }, signal, options);
46
+ }
47
+ /** Report cleanup before saved-file transfer, then wait for settlement only
48
+ * after retention actually completed. This avoids waiting for the evidence
49
+ * worker while the same command has not yet supplied its original bytes.
50
+ */
51
+ export async function syncLocalWorldClosing(scope, operation, signal, options = {}) {
52
+ let receipt = await syncLocalWorldCompletion(scope, operation, signal);
53
+ if (!receipt.received || receipt.settled)
54
+ return receipt;
55
+ const { syncLocalWorldEvidence } = await import('./runner-world-evidence-sync.js');
56
+ try {
57
+ const evidence = await syncLocalWorldEvidence(scope, operation, signal, options);
58
+ try {
59
+ receipt = await syncLocalWorldCompletion(scope, operation, signal, { wait: ['synced', 'access_removed', 'closed'].includes(evidence.state) });
60
+ return { ...receipt, evidence };
61
+ }
62
+ catch (error) {
63
+ signal.throwIfAborted();
64
+ return { ...receipt, evidence, settlement_observation: 'unconfirmed',
65
+ settlement_code: error instanceof CliError ? error.code : 'local_world_closing_unconfirmed' };
66
+ }
67
+ }
68
+ catch (error) {
69
+ signal.throwIfAborted();
70
+ // Cleanup acceptance is an already observed fact. Failure to open/decrypt
71
+ // an optional evidence journal must not erase that separate original receipt.
72
+ return { ...receipt, evidence: { schema: 'firedrill.local-world-evidence-sync.v2', state: 'unconfirmed',
73
+ operation_id: operation, cleanup_receipt_received: receipt.received, cloud_settlement_observed: receipt.settled,
74
+ command_scan_complete: false, code: error instanceof CliError ? error.code : 'local_world_evidence_unconfirmed' } };
75
+ }
76
+ }
@@ -0,0 +1,95 @@
1
+ import { CliError } from './http.js';
2
+ import { identifier } from './world-contracts.js';
3
+ import { selectOne } from './prompts.js';
4
+ import { localContainerCompletion, localWorldEvidenceContexts } from './runner-container.js';
5
+ import { LocalWorldJournal } from './runner-world-journal.js';
6
+ /** Offline removal of evidence only. The ORIGINAL owned container must already
7
+ * have an exact no-dispatch/cleanup receipt. No cloud login, model, Docker exec,
8
+ * credential rotation or deletion of source folders/cloud reports occurs.
9
+ */
10
+ export async function forgetWorldEvidence(operation, options, output) {
11
+ const id = identifier(operation);
12
+ let context;
13
+ for await (const one of localWorldEvidenceContexts(id, options.workspace, undefined, options.signal)) {
14
+ if (context)
15
+ throw new CliError('local_world_erasure_identity', 'Choose one original saved world operation. No files or keys were removed.', 2);
16
+ context = one;
17
+ }
18
+ if (!context)
19
+ throw new CliError('local_world_erasure_identity', 'Choose one original saved world operation. No files or keys were removed.', 2);
20
+ const saved = await localContainerCompletion(context, id, 'world_session');
21
+ if (!saved.worldEvidenceOwner)
22
+ throw new CliError('local_world_erasure_identity', 'The original local world identity is unavailable.');
23
+ if (!options.approve) {
24
+ if (options.json || !process.stdin.isTTY || !process.stderr.isTTY)
25
+ throw new CliError('local_world_erasure_consent', 'Pass --discard-evidence to remove this operation’s private local evidence key and files. Cloud reports are unchanged.', 2);
26
+ const approved = await selectOne('Remove private local evidence for ' + id + '?', [
27
+ { value: false, label: 'Keep evidence' },
28
+ { value: true, label: 'Remove key and files', detail: 'Unsynchronized evidence cannot be recovered. Cloud reports and the cleanup receipt remain.' },
29
+ ], options.signal);
30
+ if (!approved)
31
+ return output({ state: 'kept', operation_id: id }, 'Local evidence kept.');
32
+ }
33
+ options.signal.throwIfAborted();
34
+ const journal = await LocalWorldJournal.acquire(saved.worldEvidenceOwner, saved.manifestSha256, { existingOnly: true, erasureOnly: true });
35
+ try {
36
+ const receipt = await journal.erase(options.signal);
37
+ output({ ...receipt, operation_id: id, workspace_id: context.workspace }, receipt.already_removed
38
+ ? 'This operation’s private local evidence was already removed. Cloud reports are unchanged.'
39
+ : receipt.continuation_required ? 'The private evidence key is removed. A bounded file-removal pass finished; repeat this exact forget command to continue. Cloud reports are unchanged.'
40
+ : 'Private local evidence files and their OS-held key removed. Cloud reports are unchanged. Filesystem backups are not covered.');
41
+ }
42
+ finally {
43
+ await journal.close();
44
+ }
45
+ }
46
+ export async function pruneWorldEvidence(options, output) {
47
+ let removed = 0, alreadyRemoved = 0, kept = 0, deferredCount = 0, inspected = 0;
48
+ const deferred = [];
49
+ const problem = (operation, code) => {
50
+ deferredCount++;
51
+ const item = { operation_id: operation, code };
52
+ if (deferred.length < 25)
53
+ deferred.push(item);
54
+ output({ schema: 'firedrill.local-world-prune-item.v1', ...item, state: 'deferred' }, `${operation}: ${code}. Evidence was not removed.`);
55
+ };
56
+ for await (const context of localWorldEvidenceContexts(undefined, options.workspace, problem, options.signal)) {
57
+ options.signal.throwIfAborted();
58
+ let journal;
59
+ try {
60
+ const saved = await localContainerCompletion(context, context.operation, 'world_session');
61
+ if (!saved.worldEvidenceOwner)
62
+ throw new CliError('local_world_erasure_identity', 'Original local world identity unavailable.');
63
+ journal = await LocalWorldJournal.acquire(saved.worldEvidenceOwner, saved.manifestSha256, { existingOnly: true, erasureOnly: true });
64
+ if (!journal.removalStarted && !Number.isFinite(journal.expiresAt))
65
+ throw new CliError('local_world_retention_unavailable', 'Use the exact forget command to remove a partially initialized private journal. No expiry was invented.');
66
+ if (!journal.removalStarted && journal.expiresAt > Date.now()) {
67
+ kept++;
68
+ continue;
69
+ }
70
+ const receipt = await journal.erase(options.signal);
71
+ if (receipt.continuation_required)
72
+ problem(context.operation, 'local_world_erasure_pending');
73
+ else if (receipt.already_removed)
74
+ alreadyRemoved++;
75
+ else
76
+ removed++;
77
+ }
78
+ catch (error) {
79
+ if (!(error instanceof CliError))
80
+ throw error;
81
+ problem(context.operation, error.code);
82
+ }
83
+ finally {
84
+ if (journal)
85
+ await journal.close();
86
+ inspected++;
87
+ if (inspected % 250 === 0)
88
+ output({ schema: 'firedrill.local-world-prune-progress.v1', inspected, removed,
89
+ already_removed: alreadyRemoved, kept, deferred_count: deferredCount }, `Checked ${inspected} local world receipts.`);
90
+ }
91
+ }
92
+ output({ schema: 'firedrill.local-world-prune.v1', removed, already_removed: alreadyRemoved, kept, deferred,
93
+ inspected, deferred_count: deferredCount, deferred_truncated: deferredCount > deferred.length,
94
+ cloud_data_changed: false, filesystem_backup_erasure_attested: false }, `Local evidence: ${removed} removed, ${alreadyRemoved} previously removed, ${kept} within retention, ${deferredCount} need attention. Cloud reports are unchanged.`);
95
+ }
@@ -0,0 +1,91 @@
1
+ import { api } from './auth.js';
2
+ import { CliError } from './http.js';
3
+ import { localContainerCompletion } from './runner-container.js';
4
+ import { localWorldClaimPath } from './runner-world-claim.js';
5
+ import { LocalWorldCommandEvidence } from './runner-world-command-evidence.js';
6
+ import { historicalRunnerSigner } from './runner-identity.js';
7
+ import { localClosingReceiptMatches } from '../../contracts/local-evidence-contracts.js';
8
+ /** Existing encrypted bytes only, with a finite distinct historical server
9
+ * authority. No container/native channel, lease pulse, runtime configuration,
10
+ * model request or target replay exists in this recovery path.
11
+ */
12
+ export async function syncLocalWorldEvidence(scope, operation, signal, options = {}) {
13
+ const saved = await localContainerCompletion(scope, operation, 'world_session'), owner = saved.worldEvidenceOwner;
14
+ if (!owner)
15
+ throw new CliError('local_world_evidence_changed', 'The original local world identity is unavailable. No world was replaced.');
16
+ const receipt = await api(localWorldClaimPath(owner) + '/completion', { actor: scope.owner, workspace: scope.workspace, signal });
17
+ if (!localClosingReceiptMatches(receipt, { ...owner, manifestSha256: saved.manifestSha256 })
18
+ || receipt.schema !== 'firedrill.local-completion-receipt.v1' || receipt.job_id !== owner.job || receipt.attempt_id !== owner.attempt
19
+ || receipt.lease_id !== owner.lease || receipt.request_key !== owner.request || receipt.manifest_sha256 !== saved.manifestSha256
20
+ || receipt.received !== true || receipt.cleanup_recorded !== true
21
+ || receipt.document?.schema !== 'firedrill.local-world-container-receipt.v1'
22
+ || receipt.document.world_session.session_id !== owner.session.session_id) {
23
+ throw new CliError('local_world_closing_required', 'Synchronize the original cleanup receipt with firedrill runner completion sync before transferring saved world files.');
24
+ }
25
+ const common = { schema: 'firedrill.local-world-evidence-sync.v2', operation_id: owner.lease, session_id: owner.session.session_id,
26
+ cleanup_receipt_received: receipt.received, cloud_settlement_observed: receipt.settled,
27
+ provenance: 'customer_device', command_details: options.onCommand ? 'streamed' : 'summary_only' };
28
+ if (receipt.settled === true)
29
+ return { ...common, state: 'closed', synchronized_commands: null, unavailable_commands: null, command_scan_complete: null };
30
+ if (receipt.settled !== false)
31
+ throw new CliError('local_world_evidence_changed', 'The closing status could not be confirmed. No files were discarded.');
32
+ const signer = await historicalRunnerSigner(scope, saved.device, signal);
33
+ // Cleanup survives revocation, but private evidence does not. Do not even
34
+ // decrypt the retained evidence journal on this cleanup-only recovery path.
35
+ if (signer.cleanupOnly)
36
+ return { ...common, state: 'access_removed', synchronized_commands: null,
37
+ unavailable_commands: null, command_scan_complete: false, native_status: { state: 'unavailable', code: 'runner_cleanup_only' } };
38
+ const evidence = await LocalWorldCommandEvidence.recover(owner, saved.manifestSha256, signal);
39
+ let synchronized = 0, unavailable = 0, commandScanComplete = true;
40
+ let nativeStatus = null;
41
+ const report = async (command, code) => {
42
+ if (code === null)
43
+ synchronized++;
44
+ else
45
+ unavailable++;
46
+ await options.onCommand?.({ schema: 'firedrill.local-world-evidence-command.v1', operation_id: owner.lease,
47
+ session_id: owner.session.session_id, command_id: command.id, sequence: command.sequence,
48
+ state: code === null ? 'synchronized' : 'unavailable', code });
49
+ };
50
+ try {
51
+ for await (const command of evidence.commands()) {
52
+ signal.throwIfAborted();
53
+ const original = await evidence.saved(command);
54
+ if (!original) {
55
+ await report(command, 'native_result_not_retained');
56
+ continue;
57
+ }
58
+ try {
59
+ await evidence.retain(command, original);
60
+ await report(command, null);
61
+ }
62
+ catch (error) {
63
+ if (!(error instanceof CliError) || !['local_world_file_unavailable', 'local_world_evidence_unavailable',
64
+ 'local_world_closing_access_removed', 'runner_cleanup_only'].includes(error.code))
65
+ throw error;
66
+ await report(command, error.code);
67
+ if (['local_world_closing_access_removed', 'runner_cleanup_only'].includes(error.code)) {
68
+ commandScanComplete = false;
69
+ break;
70
+ }
71
+ }
72
+ }
73
+ if (saved.worldFinalStatus && commandScanComplete) {
74
+ try {
75
+ nativeStatus = await evidence.retainFinalNative({ size: saved.worldFinalStatus.size, sha256: saved.worldFinalStatus.sha256 });
76
+ }
77
+ catch (error) {
78
+ if (!(error instanceof CliError) || !['local_world_final_unavailable', 'local_world_closing_access_removed', 'runner_cleanup_only'].includes(error.code))
79
+ throw error;
80
+ nativeStatus = { state: 'unavailable', code: error.code };
81
+ }
82
+ }
83
+ const partial = !commandScanComplete || unavailable > 0 || !!nativeStatus && typeof nativeStatus === 'object' && 'state' in nativeStatus
84
+ && nativeStatus.state !== 'retained';
85
+ return { ...common, state: partial ? 'partial' : 'synced', synchronized_commands: synchronized,
86
+ unavailable_commands: unavailable, command_scan_complete: commandScanComplete, native_status: nativeStatus };
87
+ }
88
+ finally {
89
+ await evidence.close();
90
+ }
91
+ }