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,99 @@
1
+ import { driverBasisMatches } from './agent-execution-contracts.js';
2
+ const object = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
3
+ const id = (value) => typeof value === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(value);
4
+ const hash = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
5
+ const integer = (value, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum && value <= maximum;
6
+ const text = (value, maximum) => typeof value === 'string' && value.length <= maximum && !/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/.test(value);
7
+ const nullableText = (value, maximum) => value === null || text(value, maximum);
8
+ const kinds = ['conversation', 'http_task', 'browser_workflow', 'adaptive_browser', 'desktop', 'process', 'event'];
9
+ const changes = ['current', 'added', 'removed', 'changed', 'unchanged'];
10
+ const exact = (value, keys) => Object.keys(value).sort().join(',') === keys.sort().join(',');
11
+ function locator(value) {
12
+ return object(value) && exact(value, ['kind', 'value', 'name', 'exact', 'frames', 'presentation_omitted'])
13
+ && ['role', 'label', 'test_id', 'text', 'css'].includes(String(value.kind)) && nullableText(value.value, 2048)
14
+ && nullableText(value.name, 2048) && typeof value.exact === 'boolean' && typeof value.presentation_omitted === 'boolean'
15
+ && Array.isArray(value.frames) && value.frames.length <= 8 && value.frames.every(one => text(one, 2048))
16
+ && (value.value !== null || value.presentation_omitted);
17
+ }
18
+ function source(value) {
19
+ return object(value) && exact(value, ['key', 'root', 'path', 'sha256', 'line', 'presentation_omitted'])
20
+ && hash(value.key) && nullableText(value.root, 128) && nullableText(value.path, 2048) && hash(value.sha256)
21
+ && (value.line === null || integer(value.line, 1, 2147483647)) && typeof value.presentation_omitted === 'boolean'
22
+ && (value.root !== null && value.path !== null || value.presentation_omitted);
23
+ }
24
+ function action(value) {
25
+ return object(value) && exact(value, ['id', 'position', 'action', 'effect', 'page', 'timeout_ms', 'locator', 'path',
26
+ 'input_path', 'output', 'point', 'has_literal_value', 'presentation_omitted', 'source'])
27
+ && text(value.id, 128) && /^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(value.id)
28
+ && integer(value.position, 1, 10000) && text(value.action, 32)
29
+ && ['navigate', 'click', 'fill', 'select', 'check', 'press', 'wait_visible', 'wait_hidden', 'wait_text', 'extract', 'switch_page', 'close_page', 'upload', 'download', 'dialog', 'point'].includes(value.action)
30
+ && ['read', 'write'].includes(String(value.effect)) && integer(value.page, 0, 31) && integer(value.timeout_ms, 100, 300000)
31
+ && (value.locator === null || locator(value.locator)) && nullableText(value.path, 2048)
32
+ && nullableText(value.input_path, 256) && nullableText(value.output, 64)
33
+ && (value.point === null || Array.isArray(value.point) && value.point.length === 2 && value.point.every(one => integer(one, Number.MIN_SAFE_INTEGER)))
34
+ && typeof value.has_literal_value === 'boolean' && typeof value.presentation_omitted === 'boolean' && source(value.source);
35
+ }
36
+ function completion(value) {
37
+ return object(value) && exact(value, ['kind', 'label', 'locator', 'result_locator', 'page', 'attribute', 'literal_value_omitted'])
38
+ && ['source_dom_marker', 'process_exit', 'result_file', 'source_status_endpoint', 'not_required'].includes(String(value.kind))
39
+ && text(value.label, 160) && (value.locator === null || locator(value.locator))
40
+ && (value.result_locator === null || locator(value.result_locator)) && (value.page === null || integer(value.page, 0, 31))
41
+ && nullableText(value.attribute, 128) && typeof value.literal_value_omitted === 'boolean';
42
+ }
43
+ export function driverReviewMatches(value, world, selection = {}) {
44
+ if (!object(value) || !exact(value, ['schema', 'id', 'world_id', 'validation_job_id', 'contract_sha256', 'review_sha256', 'revision',
45
+ 'kind', 'origin', 'current', 'available', 'created_at', 'source', 'configuration', 'basis', 'action_count', 'source_count',
46
+ 'omitted_count', 'scope', 'completion', 'previous']) || value.schema !== 'firedrill.driver-review.v1'
47
+ || !id(value.id) || value.world_id !== world || !id(world) || !id(value.validation_job_id)
48
+ || selection.id !== undefined && value.id !== selection.id || selection.contract !== undefined && value.contract_sha256 !== selection.contract
49
+ || !hash(value.contract_sha256) || !hash(value.review_sha256) || !text(value.revision, 128) || !kinds.includes(String(value.kind))
50
+ || !['guest_report', 'customer_device'].includes(String(value.origin)) || typeof value.current !== 'boolean' || typeof value.available !== 'boolean'
51
+ || typeof value.created_at !== 'string' || !Number.isFinite(Date.parse(value.created_at))
52
+ || !object(value.source) || !exact(value.source, ['id', 'revision']) || !id(value.source.id) || !text(value.source.revision, 256)
53
+ || !object(value.configuration) || !exact(value.configuration, ['id', 'revision']) || !id(value.configuration.id) || !integer(value.configuration.revision, 1)
54
+ || !driverBasisMatches(value.basis) || !integer(value.action_count, 0, 10000) || !integer(value.source_count, 0, 15000)
55
+ || !integer(value.omitted_count) || !Array.isArray(value.scope) || value.scope.length > 64
56
+ || !value.scope.every(one => object(one) && exact(one, ['name', 'label', 'value', 'omitted']) && text(one.name, 128)
57
+ && text(one.label, 80) && nullableText(one.value, 16384) && typeof one.omitted === 'boolean')
58
+ || new Set(value.scope.map(one => one.name)).size !== value.scope.length
59
+ || !(value.completion === null || completion(value.completion)))
60
+ return false;
61
+ if (!value.available && (value.scope.length !== 0 || value.completion !== null))
62
+ return false;
63
+ return value.previous === null || object(value.previous) && exact(value.previous, ['id', 'contract_sha256', 'revision', 'kind', 'available'])
64
+ && id(value.previous.id) && value.previous.id !== value.id && hash(value.previous.contract_sha256)
65
+ && text(value.previous.revision, 128) && kinds.includes(String(value.previous.kind)) && typeof value.previous.available === 'boolean';
66
+ }
67
+ function page(value, world, review, selection) {
68
+ if (!object(value) || !exact(value, ['results', 'count', 'page', 'page_size', 'next', 'previous', 'world_id', 'review_id',
69
+ 'review_sha256', 'baseline_id', 'baseline_review_sha256', 'query', 'change']) || value.world_id !== world || review.world_id !== world
70
+ || value.review_id !== review.id || value.review_sha256 !== review.review_sha256 || value.baseline_id !== (selection.baseline ?? null)
71
+ || value.baseline_id === null && value.baseline_review_sha256 !== null || value.baseline_id !== null && (!id(value.baseline_id) || !hash(value.baseline_review_sha256))
72
+ || !integer(value.count, 0, 30000) || !integer(value.page, 1) || !integer(value.page_size, 1, 100)
73
+ || selection.page !== undefined && value.page !== selection.page || !Array.isArray(value.results) || value.results.length > value.page_size
74
+ || value.query !== (selection.query ?? '').trim() || value.change !== (selection.change ?? 'all'))
75
+ return false;
76
+ const prefix = `/api/workspaces/`;
77
+ return [value.next, value.previous].every(one => one === null || typeof one === 'string'
78
+ && (one.startsWith(prefix) || /^https?:\/\//.test(one)) && !/[\x00-\x20]/.test(one));
79
+ }
80
+ function changed(value, baseline) {
81
+ return changes.includes(String(value.change)) && typeof value.omitted_change === 'boolean'
82
+ && (baseline === null ? value.change === 'current' && value.before === null && value.after !== null
83
+ : value.change !== 'current' && (value.change === 'added' ? value.before === null && value.after !== null
84
+ : value.change === 'removed' ? value.before !== null && value.after === null : value.before !== null && value.after !== null));
85
+ }
86
+ export function driverReviewActionsMatch(value, world, review, selection = {}) {
87
+ return page(value, world, review, selection) && new Set(value.results.map(one => object(one) ? one.key : null)).size === value.results.length
88
+ && value.results.every(one => object(one) && exact(one, ['key', 'change', 'before', 'after', 'omitted_change', 'order_changed'])
89
+ && text(one.key, 128) && changed(one, selection.baseline ?? null) && typeof one.order_changed === 'boolean'
90
+ && (one.before === null || action(one.before) && one.before.id === one.key)
91
+ && (one.after === null || action(one.after) && one.after.id === one.key));
92
+ }
93
+ export function driverReviewSourcesMatch(value, world, review, selection = {}) {
94
+ return page(value, world, review, selection) && new Set(value.results.map(one => object(one) ? one.key : null)).size === value.results.length
95
+ && value.results.every(one => object(one) && exact(one, ['key', 'change', 'before', 'after', 'omitted_change'])
96
+ && hash(one.key) && changed(one, selection.baseline ?? null)
97
+ && (one.before === null || source(one.before) && one.before.key === one.key)
98
+ && (one.after === null || source(one.after) && one.after.key === one.key));
99
+ }
@@ -0,0 +1,65 @@
1
+ /** Private evidence identities shared by the portal, native CLI and MCP. */
2
+ import { fileMatchesTrial } from './media-contracts.js';
3
+ const uuid = (value) => typeof value === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value);
4
+ const digest = (value) => typeof value === 'string' && /^[a-f0-9]{64}$/.test(value);
5
+ const date = (value) => typeof value === 'string' && value.length <= 64 && Number.isFinite(Date.parse(value));
6
+ const natural = (value) => Number.isSafeInteger(value) && value >= 0;
7
+ const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
8
+ const categories = new Set(['message', 'driver', 'action', 'service', 'state', 'file', 'assessment', 'event']);
9
+ const kinds = new Set(['transcript', 'tool_trace', 'trace', 'result', 'state_snapshot', 'dataset_application', 'browser_capture', 'desktop_capture', 'other',
10
+ 'recording_combined', 'recording_stereo', 'recording_customer', 'recording_assistant', 'recording_segment']);
11
+ function page(value) {
12
+ return Array.isArray(value.results) && value.results.length <= 100
13
+ && natural(value.count) && natural(value.page) && value.page > 0 && natural(value.page_size) && value.page_size > 0 && value.page_size <= 100
14
+ && (value.next === null || typeof value.next === 'string') && (value.previous === null || typeof value.previous === 'string');
15
+ }
16
+ export function evidenceFileMatches(value, prefix, execution) {
17
+ if (!record(value) || !uuid(value.id) || value.execution_id !== execution || !natural(value.attempt_number) || value.attempt_number < 1
18
+ || typeof value.kind !== 'string' || !kinds.has(value.kind) || !natural(value.size) || !date(value.created_at)
19
+ || !['guest_report', 'customer_device'].includes(String(value.origin)) || typeof value.content_type !== 'string' || value.content_type.length > 255
20
+ || value.filename !== `evidence-${value.id}.${value.kind === 'tool_trace' ? 'jsonl' : value.content_type === 'audio/wav' ? 'wav' : value.content_type === 'application/json' ? 'json' : 'bin'}`
21
+ || value.generation !== null && (typeof value.generation !== 'string' || !/^[0-9]{1,30}$/.test(value.generation))
22
+ || value.sha256 !== null && !digest(value.sha256) || value.expires_at !== null && !date(value.expires_at)
23
+ || !['available', 'expired', 'deleted', 'pending', 'unavailable'].includes(String(value.state))
24
+ || value.media !== null && (!record(value.media) || value.media.id !== value.id || value.state !== 'available'
25
+ || value.media.preview !== null && !record(value.media.preview) || value.media.audio !== null && !record(value.media.audio)
26
+ || !fileMatchesTrial(value.media, prefix)))
27
+ return false;
28
+ return value.state === 'available' ? digest(value.sha256) && value.generation !== null && value.download_path === `${prefix}/evidence/${value.id}/bytes`
29
+ : value.download_path === null;
30
+ }
31
+ export function evidenceManifestMatches(value, prefix, execution) {
32
+ return record(value) && page(value) && value.schema === 'firedrill.evidence-manifest.v1' && value.execution_id === execution && date(value.through_at)
33
+ && value.results.every(row => evidenceFileMatches(row, prefix, execution));
34
+ }
35
+ export function timelineMatches(value, prefix, execution) {
36
+ const selected = prefix.match(/^\/api\/workspaces\/[a-f0-9-]+\/campaigns\/([a-f0-9-]+)\/trials\/([a-f0-9-]+)$/);
37
+ if (!selected || selected[2] !== execution)
38
+ return false;
39
+ const portal = `/campaigns/${selected[1]}/trials/${execution}`;
40
+ return record(value) && page(value) && value.execution_id === execution && value.results.every(one => record(one)
41
+ && uuid(one.id) && one.execution_id === execution && categories.has(String(one.category)) && typeof one.label === 'string' && one.label.length <= 300
42
+ && (one.attempt_number === null || natural(one.attempt_number) && one.attempt_number > 0) && (one.position === null || natural(one.position))
43
+ && (one.observed_at === null || typeof one.observed_at === 'number' && Number.isFinite(one.observed_at) && one.observed_at > 0)
44
+ && date(one.recorded_at) && one.time_basis === (one.observed_at === null ? 'recorded' : 'observed')
45
+ && (one.evidence_id === null || uuid(one.evidence_id)) && (one.origin === null || ['guest_report', 'customer_device'].includes(String(one.origin)))
46
+ && (one.category === 'service' ? typeof one.receipt_available === 'boolean' && one.observed_at === null && one.evidence_id === null
47
+ && one.position === null && (one.receipt_available ? ['guest_report', 'customer_device'].includes(String(one.origin)) && natural(one.attempt_number) && one.attempt_number > 0
48
+ : one.origin === null && one.attempt_number === null && one.label === 'Service receipt unavailable') : one.receipt_available === null)
49
+ && one.detail_path === (one.category === 'message' ? `${prefix}/messages/${one.id}`
50
+ : one.category === 'action' ? `${prefix}/actions/${one.id}` : one.category === 'driver' ? `${prefix}/timeline/driver/${one.id}`
51
+ : one.category === 'service' ? `${prefix}/requests/${one.id}`
52
+ : one.category === 'assessment' ? `${prefix}/assessments/${one.id}` : one.category === 'state' ? `${prefix}/state-comparisons/checkpoints/${one.id}` : null)
53
+ && one.portal_path === (one.category === 'message' ? `${portal}?view=conversation&record=${one.id}`
54
+ : one.category === 'action' ? `${portal}?view=actions&record=${one.id}` : one.category === 'driver' ? `${portal}?view=timeline&step=${one.id}`
55
+ : one.category === 'service' ? `${portal}?view=requests&request=${one.id}`
56
+ : one.category === 'assessment' ? `${portal}?view=assessment&assessment=${one.id}` : one.category === 'state' ? `${portal}?view=state&checkpoint=${one.id}`
57
+ : one.category === 'file' ? `${portal}?view=files&artifact=${one.id}` : `${portal}?view=timeline`));
58
+ }
59
+ export function driverEventMatches(value, prefix, execution, id) {
60
+ return record(value) && value.id === id && value.execution_id === execution && uuid(value.evidence_id) && natural(value.attempt_number)
61
+ && ['guest_report', 'customer_device'].includes(String(value.origin))
62
+ && value.attempt_number > 0 && natural(value.position) && ['observation', 'action', 'completion'].includes(String(value.kind))
63
+ && typeof value.summary === 'string' && digest(value.contract_sha256) && typeof value.inline_available === 'boolean'
64
+ && value.inline_available === (value.content !== null) && value.file_path === `${prefix}/evidence/${value.evidence_id}/bytes`;
65
+ }
@@ -0,0 +1,10 @@
1
+ /** Coverage belongs to the selected attempt and evidence channel, not a grade. */
2
+ export function coveragePageMatches(page, kind, attempt) {
3
+ const selectedAttempt = attempt?.trim();
4
+ return (kind === 'messages' || kind === 'actions') && page.kind === kind && page.results.every(row => Number.isSafeInteger(row.attempt_number) && row.attempt_number > 0
5
+ && (!selectedAttempt || row.attempt_number === Number(selectedAttempt))
6
+ && [...row.task_message_streams, ...row.message_streams, ...row.voice_streams, ...row.speech_measurements, ...row.operation_streams]
7
+ .every(stream => stream.attempt_number === row.attempt_number)
8
+ && (kind === 'actions' ? row.task_message_streams.length + row.message_streams.length + row.voice_streams.length + row.speech_measurements.length === 0
9
+ : kind === 'messages' && row.operation_streams.length === 0));
10
+ }
@@ -0,0 +1,44 @@
1
+ const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
2
+ const executionStates = new Set(['planned', 'claimed', 'dispatching', 'running', 'reconciling', 'completed', 'failed', 'timed_out',
3
+ 'canceled', 'platform_failed', 'platform_failed_before_start', 'canceled_before_start']);
4
+ const exportStates = new Set(['queued', 'preparing', 'uploading', 'retained', 'failed', 'canceled', 'unavailable']);
5
+ export function normalizedExportFilters(value) {
6
+ const keys = ['q', 'status', 'checks', 'scenario', 'cohort', 'assessment_status', 'score_bucket'];
7
+ if (!value || typeof value !== 'object' || Array.isArray(value) || Object.entries(value).some(([key, one]) => !keys.includes(key) || typeof one !== 'string'))
8
+ return null;
9
+ const q = value.q ?? '', status = value.status ?? '', checks = value.checks ?? '', scenario = value.scenario?.toLowerCase() ?? '';
10
+ if ([...q].length > 200 || /[\u0000-\u001f\u007f]/.test(q.trim()) || status && !executionStates.has(status)
11
+ || checks && !['met', 'unmet', 'unassessed', 'unavailable', 'missing'].includes(checks) || scenario && !uuid.test(scenario))
12
+ return null;
13
+ const result = { q: q.trim(), status, checks, scenario };
14
+ if (!value.cohort)
15
+ return value.assessment_status || value.score_bucket ? null : result;
16
+ const cohort = value.cohort.toLowerCase(), assessment = value.assessment_status ?? '', bucket = value.score_bucket ?? '';
17
+ if (!uuid.test(cohort) || assessment && !['succeeded', 'not_assessable', 'not_applicable'].includes(assessment)
18
+ || bucket && (!/^[0-8]$/.test(bucket) || assessment && assessment !== 'succeeded'))
19
+ return null;
20
+ return { ...result, cohort, assessment_status: assessment, score_bucket: bucket };
21
+ }
22
+ function sameFilters(left, right) {
23
+ const first = normalizedExportFilters(left), second = normalizedExportFilters(right);
24
+ return first !== null && second !== null && Object.keys(first).length === Object.keys(second).length
25
+ && Object.entries(first).every(([key, value]) => second[key] === value);
26
+ }
27
+ export function reportExportMatches(value, scope, selected) {
28
+ return !!value && uuid.test(value.id) && uuid.test(value.actor_id) && uuid.test(value.request_key)
29
+ && value.workspace_id === scope.workspace && value.campaign_id === scope.campaign && (!scope.actor || value.actor_id === scope.actor)
30
+ && (!selected?.id || value.id === selected.id) && (!selected?.key || value.request_key === selected.key)
31
+ && exportStates.has(value.state) && normalizedExportFilters(value.filters) !== null
32
+ && (!selected?.filters || sameFilters(value.filters, selected.filters))
33
+ && Object.values(value.counts).every(one => Number.isSafeInteger(one) && one >= 0)
34
+ && (!value.file || value.state === 'retained' && Number.isSafeInteger(value.file.size) && value.file.size > 0
35
+ && /^[a-f0-9]{64}$/.test(value.file.sha256)
36
+ && value.file.download_path === `/api/workspaces/${scope.workspace}/campaigns/${scope.campaign}/report-exports/${value.id}/download`);
37
+ }
38
+ export function reportExportsPageMatches(value, scope) {
39
+ return 'results' in value && value.workspace_id === scope.workspace && value.campaign_id === scope.campaign
40
+ && value.results.every(row => reportExportMatches(row, scope) && row.file === null && !row.can_cancel);
41
+ }
42
+ export function reportExportRecoveryMatches(value, scope, key) {
43
+ return !('results' in value) && reportExportMatches(value, scope, { key });
44
+ }
@@ -0,0 +1,96 @@
1
+ const connectionFailures = {
2
+ github_connection_expired: 'GitHub authorization expired or your workspace access changed. Authorize GitHub again to continue.',
3
+ github_installation_denied: 'This GitHub account cannot access the selected installation. Choose an authorized account or update GitHub access.',
4
+ github_connection_retry: 'GitHub could not confirm this authorization. Check your connected accounts below, then authorize GitHub again if needed.',
5
+ github_connection_failed: 'The GitHub connection was not confirmed. Check your connected accounts below before authorizing again.',
6
+ };
7
+ export function gitHubConnectionFailure(reason) {
8
+ return reason && Object.prototype.hasOwnProperty.call(connectionFailures, reason) ? connectionFailures[reason] : null;
9
+ }
10
+ export const gitHubId = (value) => typeof value === 'string' && /^[1-9][0-9]{0,18}$/.test(value) && BigInt(value) < 2n ** 63n;
11
+ const uuid = (value) => typeof value === 'string' && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value);
12
+ const text = (value, maximum, empty = false) => typeof value === 'string' && (empty || value.length > 0)
13
+ && value.length <= maximum && !/[\x00-\x1f\x7f]/.test(value);
14
+ const date = (value) => typeof value === 'string' && value.length <= 64 && Number.isFinite(Date.parse(value));
15
+ export function gitHubConnectionMatches(value, workspace, installation) {
16
+ return !!value && uuid(value.id) && value.workspace_id === workspace && value.installation_id === installation && gitHubId(installation)
17
+ && text(value.account, 255) && text(value.state, 24) && date(value.verified_at) && (value.disconnected_at === null || date(value.disconnected_at));
18
+ }
19
+ export function gitHubConnectionChangeMatches(value, original) {
20
+ if (!value || value.workspace_id !== original.workspace || value.installation_id !== original.installation
21
+ || value.actor_id !== original.actor || value.request_key !== original.key || value.connection_id !== original.connection_id
22
+ || !text(value.current_connection_state, 24) || !date(value.current_verified_at))
23
+ return false;
24
+ if (value.state === 'not_received')
25
+ return value.connection_verified_at === null && value.disconnected_at === null
26
+ && value.cleanup_state === null && value.cleanup_completed_at === null;
27
+ return value.state === 'received' && value.connection_verified_at === original.verified_at && date(value.disconnected_at)
28
+ && (value.cleanup_state === 'pending' || value.cleanup_state === 'recovery_required') && value.cleanup_completed_at === null
29
+ || value.state === 'received' && value.connection_verified_at === original.verified_at && date(value.disconnected_at)
30
+ && value.cleanup_state === 'complete' && date(value.cleanup_completed_at);
31
+ }
32
+ export function gitHubConnectionsMatch(value, workspace, query, page) {
33
+ return !!value && value.workspace_id === workspace && value.query === query.trim() && pageMatches(value, page)
34
+ && value.results.every(row => gitHubConnectionMatches(row, workspace, row.installation_id));
35
+ }
36
+ export function gitHubConnectionChangesMatch(value, workspace, actor, page) {
37
+ return !!value && value.workspace_id === workspace && value.actor_id === actor && pageMatches(value, page)
38
+ && value.results.every(row => row.state === 'received' && uuid(row.request_key) && uuid(row.connection_id) && date(row.connection_verified_at)
39
+ && gitHubConnectionChangeMatches(row, { workspace, actor, installation: row.installation_id, key: row.request_key,
40
+ connection_id: row.connection_id, verified_at: row.connection_verified_at }));
41
+ }
42
+ export function gitHubRead(value, matches) {
43
+ if (!matches)
44
+ throw new Error('The GitHub response did not match the selected workspace, account or original request. Check the connection before continuing.');
45
+ return value;
46
+ }
47
+ function pageMatches(value, page) {
48
+ return value.page === page && Number.isSafeInteger(value.page) && value.page >= 1 && Number.isSafeInteger(value.page_size)
49
+ && value.page_size >= 1 && value.page_size <= 100 && Number.isSafeInteger(value.count) && value.count >= 0
50
+ && Array.isArray(value.results) && value.results.length <= value.page_size;
51
+ }
52
+ export function gitHubAccountMatches(value, workspace, installation) {
53
+ return !!value && value.workspace_id === workspace && gitHubId(value.id) && (!installation || installation === value.id)
54
+ && text(value.account, 255) && (value.avatar_url === null || typeof value.avatar_url === 'string' && value.avatar_url.length <= 2048);
55
+ }
56
+ export function gitHubAccountsMatch(value, workspace, query = '', page = 1) {
57
+ return !!value && value.workspace_id === workspace && value.query === query.trim() && pageMatches(value, page)
58
+ && value.results.every(row => gitHubAccountMatches(row, workspace));
59
+ }
60
+ export function gitHubSyncMatches(value, workspace, installation, original) {
61
+ return !!value && value.workspace_id === workspace && value.installation_id === installation && gitHubId(installation)
62
+ && [value.id, value.workspace_id, value.actor_id, value.request_key].every(uuid)
63
+ && (!original || value.actor_id === original.actor && value.request_key === original.key && (!original.id || original.id === value.id))
64
+ && ['pending', 'ready', 'failed', 'canceled'].includes(value.state) && typeof value.observed_count === 'string'
65
+ && /^(0|[1-9][0-9]*)$/.test(value.observed_count) && value.observed_count.length <= 19 && BigInt(value.observed_count) < 2n ** 63n
66
+ && (value.completed_at === null || Number.isFinite(Date.parse(value.completed_at)));
67
+ }
68
+ export function gitHubRepositoriesMatch(value, workspace, installation, query = '', page = 1) {
69
+ return !!value && value.workspace_id === workspace && value.installation_id === installation && value.query === query.trim()
70
+ && pageMatches(value, page) && typeof value.directory_ready === 'boolean' && gitHubSyncMatches(value.sync, workspace, installation)
71
+ && value.results.every(row => gitHubId(row.id) && text(row.full_name, 512) && row.full_name.split('/').length === 2
72
+ && row.full_name.split('/').every(Boolean) && typeof row.description === 'string' && row.description.length <= 2048
73
+ && (row.default_branch === null || text(row.default_branch, 512, true)) && typeof row.private === 'boolean');
74
+ }
75
+ export function gitHubRequestMatches(value, workspace, installation, original) {
76
+ return !!value && value.workspace_id === workspace && value.installation_id === installation && value.actor_id === original.actor
77
+ && value.request_key === original.key && (value.state === 'not_received' ? value.sync === null && !original.id
78
+ : value.state === 'received' && value.sync !== null && gitHubSyncMatches(value.sync, workspace, installation, original));
79
+ }
80
+ export function gitHubBranchesMatch(value, workspace, installation, repository, page) {
81
+ return !!value && value.workspace_id === workspace && value.installation_id === installation && value.repository_id === repository
82
+ && gitHubId(installation) && gitHubId(repository) && value.page === page && Number.isSafeInteger(page) && page >= 1 && page <= 99999999
83
+ && typeof value.has_next === 'boolean' && value.has_previous === (page > 1) && Array.isArray(value.results) && value.results.length <= 25
84
+ && value.results.every(row => text(row.name, 512));
85
+ }
86
+ export function gitHubHandoff(value, workspace) {
87
+ try {
88
+ const url = new URL(value.url);
89
+ if (value.workspace_id === workspace && url.protocol === 'https:' && url.hostname === 'github.com' && !url.port
90
+ && !url.username && !url.password && !url.hash && /^\/apps\/[a-zA-Z0-9_-]+\/installations\/new$/.test(url.pathname)
91
+ && [...url.searchParams.keys()].join(',') === 'state' && /^[a-zA-Z0-9_-]{32,256}$/.test(url.searchParams.get('state') ?? ''))
92
+ return url.href;
93
+ }
94
+ catch { /* Opaque setup URL stays out of errors and retained history. */ }
95
+ throw new Error('The GitHub installation page could not be confirmed. Start setup again.');
96
+ }
@@ -0,0 +1,11 @@
1
+ export function findingsMatch(value, selected) {
2
+ return value.campaign_id === selected.campaign && value.filters.q === selected.query.trim()
3
+ && value.filters.scenario === (selected.scenario ?? null)
4
+ && value.sample_selection === 'first_retained_trial_per_observed_outcome'
5
+ && value.results.every(row => !selected.scenario || row.id === selected.scenario.toLowerCase());
6
+ }
7
+ export function insightsMatch(value, campaign) {
8
+ return value.campaign_id === campaign && value.proposals.length <= value.proposal_page_size
9
+ && value.proposal_count >= value.proposals.length
10
+ && (value.state !== 'unavailable' || !value.content && !value.proposals.length && !value.can_apply);
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
2
+ const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
3
+ const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
4
+ const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
5
+ export function localAssignmentMatches(v, selected) {
6
+ if (!v || v.actor_id !== selected.actor || v.workspace_id !== selected.workspace || v.device_key !== selected.device
7
+ || !uuid(v.native_session_id) || selected.nativeSession && v.native_session_id !== selected.nativeSession
8
+ || !uuid(v.job_id) || selected.job && v.job_id !== selected.job || !uuid(v.world_id) || selected.world && v.world_id !== selected.world
9
+ || !uuid(v.grant_id) || typeof v.world_name !== 'string' || !hex(v.payload_digest) || v.provenance !== 'customer_device'
10
+ || !['validation', 'trial', 'world_session'].includes(v.purpose)
11
+ || !['received', 'queued', 'admitted', 'provisioning', 'running', 'finalizing', 'awaiting_validation', 'cleaning_up', 'retry_wait', 'completed', 'failed', 'canceled'].includes(v.job_state)
12
+ || ![v.deadline_at, v.created_at, v.server_time].every(date))
13
+ return false;
14
+ const session = v.world_session;
15
+ if (v.purpose === 'world_session' ? !session || !uuid(session.session_id) || !uuid(session.configuration_id) || !hex(session.input_sha256) || !date(session.expires_at)
16
+ : session !== undefined)
17
+ return false;
18
+ if (v.claim === null)
19
+ return v.input_preparation === undefined;
20
+ const claim = v.claim, input = v.input_preparation;
21
+ return !!claim && uuid(claim.id) && uuid(claim.request_key) && (!selected.request || claim.request_key === selected.request)
22
+ && ['claimed', 'draining', 'withdrawn', 'reconciling', 'expired'].includes(claim.state) && date(claim.expires_at)
23
+ && integer(claim.last_sequence) && (claim.last_pulse_digest === null || hex(claim.last_pulse_digest))
24
+ && (claim.request_digest === null ? claim.state === 'withdrawn' && claim.attempt_id === null : hex(claim.request_digest) && uuid(claim.attempt_id))
25
+ && typeof claim.cleanup_recorded === 'boolean' && claim.cleanup_provenance === (claim.cleanup_recorded ? 'customer_device' : null)
26
+ && !!input && ['pending', 'preparing', 'ready', 'failed'].includes(input.state)
27
+ && (input.problem === null || typeof input.problem === 'string') && (input.state !== 'failed' || !!input.problem);
28
+ }
29
+ export function localClaimLookupMatches(v, selected) {
30
+ return localAssignmentMatches(v, selected) && v.request_key === selected.request && v.claim_retained === (v.claim !== null);
31
+ }
32
+ export function localAssignmentsMatch(v, selected, filters = {}) {
33
+ return !!v && v.actor_id === selected.actor && v.workspace_id === selected.workspace && v.device_key === selected.device
34
+ && v.world_id === (filters.world ?? null) && v.campaign_id === (filters.campaign ?? null) && v.query === (filters.query ?? '').trim()
35
+ && v.available === (filters.available ?? false) && v.page === (filters.page ?? 1) && integer(v.count) && integer(v.page_size)
36
+ && v.page_size >= 1 && v.page_size <= 100 && Array.isArray(v.results) && v.results.length <= v.page_size
37
+ && new Set(v.results.map(one => one.job_id)).size === v.results.length
38
+ && v.results.every(one => localAssignmentMatches(one, { ...selected, world: filters.world ?? selected.world }));
39
+ }
@@ -0,0 +1,7 @@
1
+ export function localConfigurationIdentityMatches(value, selected) {
2
+ return !!value && value.schema === 'firedrill.local-runtime-configuration.v1'
3
+ && value.actor_id === selected.owner && value.workspace_id === selected.workspace && value.native_session_id === selected.nativeSession
4
+ && value.device_key === selected.device && value.job_id === selected.job && value.request_key === selected.request
5
+ && value.lease_id === selected.lease && value.operation_id === selected.lease && value.attempt_id === selected.attempt
6
+ && value.provenance === 'customer_device';
7
+ }
@@ -0,0 +1,32 @@
1
+ const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
2
+ const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
3
+ const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
4
+ export function localHistoryIdentityMatches(value, selected) {
5
+ return !!value && value.actor_id === selected.owner && value.workspace_id === selected.workspace && value.device_key === selected.device
6
+ && value.job_id === selected.job && value.request_key === selected.request && value.lease_id === selected.lease && value.attempt_id === selected.attempt
7
+ && value.manifest_sha256 === selected.manifestSha256 && value.provenance === 'customer_device' && date(value.server_time);
8
+ }
9
+ export function localEvidenceReceiptMatches(value, selected, key) {
10
+ if (!localHistoryIdentityMatches(value, selected) || value.transfer_key !== key || value.cloud_completion_recorded !== false
11
+ || !['not_retained', 'receiving', 'verifying', 'retained', 'refused', 'expired', 'unavailable'].includes(value.state)
12
+ || typeof value.upload_available !== 'boolean' || typeof value.authorization_current !== 'boolean' || !date(value.upload_until)
13
+ || !Array.isArray(value.chunks) || value.chunks.length > 512 || new Set(value.chunks.map(one => one.index)).size !== value.chunks.length
14
+ || value.chunks.some(one => !integer(one.index) || one.index > 511 || !integer(one.size) || one.size < 1 || one.size > 1024 ** 2
15
+ || !hex(one.sha256) || typeof one.retained !== 'boolean'))
16
+ return false;
17
+ if (value.state === 'not_retained')
18
+ return value.document === null && value.request_digest === null && value.artifact === null && value.chunks.length === 0;
19
+ if (!value.document || !hex(value.request_digest))
20
+ return false;
21
+ const artifact = value.artifact;
22
+ return value.state === 'retained' ? !!artifact && /^[a-f0-9-]{36}$/.test(artifact.id) && hex(artifact.sha256)
23
+ && integer(artifact.size) && /^[1-9][0-9]{0,30}$/.test(artifact.generation) : artifact === null;
24
+ }
25
+ export function localClosingReceiptMatches(value, selected) {
26
+ return localHistoryIdentityMatches(value, selected) && value.schema === 'firedrill.local-completion-receipt.v1'
27
+ && value.independently_attested === false && typeof value.received === 'boolean' && typeof value.settled === 'boolean'
28
+ && typeof value.cleanup_recorded === 'boolean' && (value.received ? value.document !== null && hex(value.digest) && value.cleanup_recorded
29
+ : value.document === null && value.digest === null && !value.cleanup_recorded && !value.settled)
30
+ && ['received', 'queued', 'admitted', 'provisioning', 'running', 'finalizing', 'awaiting_validation', 'cleaning_up', 'retry_wait', 'completed', 'failed', 'canceled'].includes(value.job_state)
31
+ && (!value.settled || ['completed', 'failed', 'canceled'].includes(value.job_state));
32
+ }
@@ -0,0 +1,83 @@
1
+ import { localRuntimeImageMatches } from './local-runner-contracts.js';
2
+ const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
3
+ const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
4
+ const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
5
+ const integer = (v, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => typeof v === 'number' && Number.isSafeInteger(v) && v >= minimum && v <= maximum;
6
+ const operations = (v) => Array.isArray(v) && v.length <= 3
7
+ && v.every(one => ['trial', 'validation', 'world_session'].includes(one)) && new Set(v).size === v.length;
8
+ export function localGrantRead(v, matches) {
9
+ if (!matches)
10
+ throw new Error('The saved local permission did not match this account, world, device or original approval. No local work was started.');
11
+ return v;
12
+ }
13
+ export function localGrantBasisMatches(v) {
14
+ return !!v && integer(v.world_revision, 1) && integer(v.config_revision, 1) && uuid(v.source_id) && uuid(v.source_snapshot_id)
15
+ && typeof v.source_revision === 'string' && v.source_revision.length > 0 && v.source_revision.length <= 128
16
+ && hex(v.source_sha256) && hex(v.source_content_digest) && hex(v.configuration_digest)
17
+ && (v.source_set === undefined || localGrantSourceSetMatches(v.source_set))
18
+ && (v.world_definition === null || !!v.world_definition && uuid(v.world_definition.artifact_id) && hex(v.world_definition.sha256)
19
+ && typeof v.world_definition.generation === 'string' && /^[1-9][0-9]*$/.test(v.world_definition.generation));
20
+ }
21
+ export function localGrantSourceSetMatches(v) {
22
+ return !!v && uuid(v.id) && [v.source_set_sha256, v.graph_sha256, v.roots_sha256].every(hex) && integer(v.root_count, 1, 128);
23
+ }
24
+ export function localGrantSourceRootsMatch(v, actor, workspace, world, device, session, basisDigest, sourceSet, page) {
25
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.device_key === device
26
+ && v.native_session_id === session && v.basis_sha256 === basisDigest && hex(basisDigest)
27
+ && localGrantSourceSetMatches(v.source_set) && ['id', 'source_set_sha256', 'graph_sha256', 'roots_sha256', 'root_count']
28
+ .every(key => v.source_set[key] === sourceSet[key])
29
+ && v.page === page && integer(v.page_size, 1, 100) && v.count === sourceSet.root_count && Array.isArray(v.results)
30
+ && v.results.length === Math.min(v.page_size, Math.max(0, v.count - (page - 1) * v.page_size))
31
+ && (v.next === null || typeof v.next === 'string') && (v.previous === null || typeof v.previous === 'string')
32
+ && (v.next !== null) === (page * v.page_size < v.count) && (v.previous !== null) === (page > 1)
33
+ && v.results.every(row => !!row && /^[a-z][a-z0-9_-]{0,63}$/.test(row.alias) && uuid(row.source_id) && uuid(row.snapshot_id)
34
+ && typeof row.revision === 'string' && row.revision.length > 0 && row.revision.length <= 128 && hex(row.snapshot_sha256)
35
+ && typeof row.generation === 'string' && /^[1-9][0-9]*$/.test(row.generation) && typeof row.label === 'string' && row.label.length <= 512
36
+ && row.directory === `components/${row.alias}` && (row.content_digest === null || hex(row.content_digest)))
37
+ && new Set(v.results.map(row => row.alias)).size === v.results.length && new Set(v.results.map(row => row.source_id)).size === v.results.length;
38
+ }
39
+ export function localGrantMatches(v, actor, workspace, selected = {}) {
40
+ if (!v || v.actor_id !== actor || v.workspace_id !== workspace || !uuid(v.id) || !uuid(v.request_key) || !uuid(v.device_key)
41
+ || !uuid(v.native_session_id) || !uuid(v.world_id) || selected.world && v.world_id !== selected.world || selected.device && v.device_key !== selected.device
42
+ || selected.key && v.request_key !== selected.key || selected.id && v.id !== selected.id
43
+ || typeof v.device_label !== 'string' || typeof v.world_name !== 'string' || typeof v.world_available !== 'boolean'
44
+ || !['active', 'revoked', 'expired', 'access_removed', 'superseded'].includes(v.state)
45
+ || ![v.server_time, v.expires_at, v.created_at].every(date) || !operations(v.operations) || !v.basis)
46
+ return false;
47
+ if (v.request_digest === null)
48
+ return v.state === 'revoked' && v.runtime === null && v.limits === null && v.consent === null
49
+ && v.operations.length === 0 && Object.keys(v.basis).length === 0;
50
+ return hex(v.request_digest) && localGrantBasisMatches(v.basis) && !!v.runtime && localRuntimeImageMatches(v.runtime)
51
+ && v.operations.length > 0 && !!v.limits && integer(v.limits.parallelism, 1, 10000) && integer(v.limits.cpu_units, 1, 65536)
52
+ && integer(v.limits.memory_mb, 1024, 16777216) && !!v.consent && v.consent.policy === 'firedrill.local-execution-consent.v1'
53
+ && v.consent.cloud_context_processing === true && v.consent.run_metadata === true
54
+ && [v.consent.evidence_upload, v.consent.vault_values_to_device, v.consent.real_account_access, v.consent.catalog_packages_to_device].every(one => typeof one === 'boolean');
55
+ }
56
+ export function localGrantLookupMatches(v, actor, workspace, world, key) {
57
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.request_key === key
58
+ && (v.state === 'not_retained' || localGrantMatches(v, actor, workspace, { world, key }));
59
+ }
60
+ export function localGrantsMatch(v, actor, workspace, selected) {
61
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === selected.world && v.device_key === (selected.device ?? null)
62
+ && v.grant_id === (selected.grant ?? null) && v.purpose === (selected.purpose ?? '') && v.available === (selected.available ?? false)
63
+ && v.query === (selected.query ?? '').trim() && v.page === (selected.page ?? 1) && integer(v.count) && integer(v.page_size, 1, 100)
64
+ && Array.isArray(v.results) && v.results.length <= v.page_size && new Set(v.results.map(one => one.id)).size === v.results.length
65
+ && v.results.every(one => localGrantMatches(one, actor, workspace, { world: selected.world ?? undefined, device: selected.device, id: selected.grant })
66
+ && (!selected.purpose || one.operations.includes(selected.purpose)));
67
+ }
68
+ export function localGrantOfferMatches(v, actor, workspace, world, device) {
69
+ const values = v?.disclosures;
70
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.world_id === world && v.device_key === device
71
+ && uuid(v.native_session_id) && v.schema === 'firedrill.local-grant-offer.v1' && typeof v.world_name === 'string' && date(v.server_time)
72
+ && localGrantBasisMatches(v.basis) && operations(v.operations) && v.operations.length === 3 && Array.isArray(v.runtimes)
73
+ && v.runtimes.length <= 1 && v.runtimes.every(localRuntimeImageMatches) && !!values
74
+ && [values.real_connections, values.target_model_connections, values.catalog_packages].every(one => integer(one))
75
+ && [values.vault_values, values.local_values].every(list => Array.isArray(list) && list.every(one => one && typeof one.name === 'string'
76
+ && ['build', 'runtime', 'both'].includes(one.phase)));
77
+ }
78
+ export function localGrantWorld(prefix, workspace) {
79
+ const world = prefix.split('/').at(-1);
80
+ if (!uuid(world) || prefix !== `/api/workspaces/${workspace}/worlds/${world}`)
81
+ throw new Error('Open this world in its selected workspace.');
82
+ return world;
83
+ }
@@ -0,0 +1,19 @@
1
+ const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
2
+ const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
3
+ export function localInputIdentityMatches(v, selected) {
4
+ return !!v && v.actor_id === selected.owner && v.workspace_id === selected.workspace && v.native_session_id === selected.nativeSession
5
+ && v.device_key === selected.device && v.job_id === selected.job && v.request_key === selected.request && v.lease_id === selected.lease && v.attempt_id === selected.attempt;
6
+ }
7
+ export function localInputPacketMatches(v, selected, requested = {}) {
8
+ return localInputIdentityMatches(v, selected) && v.schema === 'firedrill.local-input-response.v1'
9
+ && hex(v.manifest_sha256) && !!v.manifest && v.manifest.schema === 'firedrill.local-input-manifest.v1'
10
+ && v.manifest.job_id === selected.job && integer(v.offset) && (!requested.part || v.part === requested.part)
11
+ && (requested.offset === undefined || v.offset === requested.offset)
12
+ && (v.part === 'manifest' ? v.offset === 0 : ['source', 'candidate', 'compiled', 'scenarios', 'packages', 'dataset', 'checkpoint', 'application_checkpoint'].includes(v.part)
13
+ && integer(v.length) && v.length >= 1 && v.length <= 256 * 1024 && (requested.length === undefined || v.length === requested.length)
14
+ && typeof v.content_base64 === 'string' && v.content_base64.length === 4 * Math.ceil(v.length / 3) && hex(v.chunk_sha256));
15
+ }
16
+ export function localInputPendingMatches(v, selected) {
17
+ return localInputIdentityMatches(v, selected) && v.schema === 'firedrill.local-input-pending.v1'
18
+ && ['preparing_packages', 'preparing_starting_data'].includes(v.input_state) && v.retry_after_ms === 1000;
19
+ }
@@ -0,0 +1,42 @@
1
+ const uuid = (v) => typeof v === 'string' && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(v);
2
+ const date = (v) => typeof v === 'string' && Number.isFinite(Date.parse(v));
3
+ const hex = (v) => typeof v === 'string' && /^[a-f0-9]{64}$/.test(v);
4
+ const integer = (v) => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
5
+ export function localRunnerRead(v, matches) {
6
+ if (!matches)
7
+ throw new Error('The local runner response did not match this account, workspace or original device. No local work was started.');
8
+ return v;
9
+ }
10
+ export function localRunnerMatches(v, actor, workspace, device) {
11
+ if (!v || v.actor_id !== actor || v.workspace_id !== workspace || !uuid(v.device_key) || device && v.device_key !== device
12
+ || !uuid(v.native_session_id) || !date(v.server_time) || !date(v.created_at) || !date(v.expires_at)
13
+ || typeof v.label !== 'string' || v.label.length < 1 || v.label.length > 80 || !v.host || typeof v.host !== 'object'
14
+ || !['enrolled', 'idle', 'draining', 'stopped', 'offline', 'revoked', 'expired', 'access_removed'].includes(v.state)
15
+ || v.provenance !== 'customer_device' || !integer(v.last_sequence)
16
+ || ![v.last_seen_at, v.presence_until].every(one => one === null || date(one))
17
+ || ![v.enrollment_digest, v.last_pulse_digest].every(one => one === null || hex(one)))
18
+ return false;
19
+ const host = v.host;
20
+ return v.enrollment_digest === null ? v.state === 'revoked' && Object.keys(host).length === 0
21
+ : Object.keys(host).length === 2 && ['darwin', 'linux'].includes(host.os ?? '') && ['x64', 'arm64'].includes(host.arch ?? '');
22
+ }
23
+ export function localRunnerLookupMatches(v, actor, workspace, device) {
24
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.device_key === device && uuid(device) && date(v.server_time)
25
+ && (v.state === 'not_retained' || localRunnerMatches(v, actor, workspace, device));
26
+ }
27
+ export function localRunnersMatch(v, actor, workspace, query, page = 1) {
28
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.query === query.trim() && v.page === page
29
+ && integer(v.count) && integer(v.page_size) && v.page_size >= 1 && v.page_size <= 100 && Array.isArray(v.results)
30
+ && v.results.length <= v.page_size && new Set(v.results.map(one => one.device_key)).size === v.results.length
31
+ && v.results.every(one => localRunnerMatches(one, actor, workspace));
32
+ }
33
+ export function localRuntimeCatalogMatches(v, actor, workspace) {
34
+ return !!v && v.actor_id === actor && v.workspace_id === workspace && v.schema === 'firedrill.local-runtime-catalog.v1'
35
+ && Array.isArray(v.runtimes) && v.runtimes.length <= 2 && new Set(v.runtimes.map(one => one.arch)).size === v.runtimes.length
36
+ && v.runtimes.every(localRuntimeImageMatches);
37
+ }
38
+ export function localRuntimeImageMatches(one) {
39
+ return !!one && ['amd64', 'arm64'].includes(one.arch) && one.os === 'linux' && one.profile === 'firedrill.local-container.v1'
40
+ && typeof one.source_revision === 'string' && /^[a-f0-9]{40}$/.test(one.source_revision) && typeof one.image === 'string'
41
+ && /^[a-z][a-z0-9-]{0,62}-docker\.pkg\.dev\/[a-z][a-z0-9-]{4,62}\/[a-z][a-z0-9-]{0,62}\/[a-z][a-z0-9-]{0,62}@sha256:[a-f0-9]{64}$/.test(one.image);
42
+ }