firedrill-run 2.2.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +178 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +465 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1622 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +61 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,109 @@
1
+ import { api, credentialOverview } from './auth.js';
2
+ import { selectedWorkspace } from './world-access.js';
3
+ import { APP_ORIGIN, CliError, endpoint, jsonRequest } from './http.js';
4
+ import { mcpConnectionRead } from '../../contracts/mcp-contracts.js';
5
+ export const MCP_CLIENTS = ['codex', 'claude', 'other'];
6
+ /**
7
+ * Management MCP addresses of the fixed platform profiles (root release
8
+ * configuration, confirmed 2026-11-10). Used before sign-in only when the
9
+ * platform's public terminal configuration predates its MCP fields; once signed
10
+ * in, the workspace's own connection read is authoritative.
11
+ */
12
+ const PROFILE_MCP = {
13
+ 'https://app.firedrill.run': 'https://mcp.firedrill.run',
14
+ 'https://app.sim-dev.firedrill.run': 'https://mcp.sim-dev.firedrill.run',
15
+ };
16
+ /** A bare https origin has no shell-special characters, so one printed command works in every shell. */
17
+ const SAFE_ORIGIN = /^https:\/\/[a-z0-9.-]+(?::443)?$/;
18
+ /**
19
+ * The platform's management MCP address from its public terminal configuration
20
+ * (root `cli/config`, 2026-11-10): a bare https origin with transport
21
+ * `streamable-http`, or both null when MCP is disabled or not configured.
22
+ * Returns undefined for a platform whose configuration predates these fields.
23
+ * The sign-in fields are not checked here, so MCP guidance never depends on them.
24
+ */
25
+ async function platformMcp(signal) {
26
+ const result = await jsonRequest(endpoint('/api/auth/cli/config'), { signal });
27
+ const body = result.body;
28
+ if (result.status !== 200 || !body || typeof body !== 'object' || Array.isArray(body)) {
29
+ throw new CliError('mcp_not_configured', 'The platform\'s MCP address could not be read. Your terminal commands remain available.', 2);
30
+ }
31
+ const { management_mcp_url: url, management_mcp_transport: transport } = body;
32
+ if (url === undefined && transport === undefined)
33
+ return undefined;
34
+ if (url === null && transport === null)
35
+ return null;
36
+ if (typeof url === 'string' && SAFE_ORIGIN.test(url) && transport === 'streamable-http')
37
+ return url;
38
+ throw new CliError('mcp_not_configured', 'The platform\'s MCP address could not be printed safely. Your terminal commands remain available.', 2);
39
+ }
40
+ function guidanceFor(url, agent) {
41
+ const guidance = [];
42
+ if (agent === undefined || agent === 'codex')
43
+ guidance.push({ client: 'codex', label: 'Codex',
44
+ commands: [`codex mcp add firedrill --url ${url}`, 'codex mcp login firedrill'],
45
+ authentication: 'codex mcp login opens the browser to authorize Firedrill for Codex.',
46
+ scope: 'This adds the server to your Codex user configuration; a trusted project can instead list it in .codex/config.toml.',
47
+ removal: 'codex mcp remove firedrill' });
48
+ if (agent === undefined || agent === 'claude')
49
+ guidance.push({ client: 'claude', label: 'Claude Code',
50
+ commands: [`claude mcp add --transport http firedrill ${url}`],
51
+ authentication: 'Open /mcp in Claude Code and choose firedrill to authorize it in the browser.',
52
+ scope: 'By default only you see it, in this project. Add --scope user for all your projects, or --scope project to share it through .mcp.json.',
53
+ removal: 'claude mcp remove firedrill (add the same --scope if you used one)' });
54
+ if (agent === undefined || agent === 'other')
55
+ guidance.push({ client: 'other', label: 'Other MCP clients',
56
+ commands: [],
57
+ authentication: `Add a remote (Streamable HTTP) MCP server named firedrill with this URL: ${url}\nYour client handles the OAuth browser sign-in. No token or header is needed.`,
58
+ scope: null, removal: null });
59
+ return guidance;
60
+ }
61
+ function guidanceText(url, guidance, source) {
62
+ return [
63
+ `MCP server: ${url}${source !== 'workspace' ? ' (this platform\'s address; sign in with firedrill init to confirm it is enabled for your workspace)' : ''}`,
64
+ ...guidance.map(item => [item.label, ...item.commands, item.authentication,
65
+ ...(item.scope ? [item.scope] : []), ...(item.removal ? [`To remove: ${item.removal}`] : [])].join('\n')),
66
+ 'These commands work in macOS and Linux shells, PowerShell and Command Prompt. Firedrill has not changed any client settings.',
67
+ 'Approve access in the browser opened by your MCP client. This is separate from firedrill init.',
68
+ 'If a firedrill connection already exists, inspect its URL in your client before adding or replacing it. Other servers in your client configuration are left as they are.',
69
+ 'Remote MCP cannot read project files or start processes on this computer. Local targets run only through firedrill run with a plan you approve.',
70
+ ].join('\n\n');
71
+ }
72
+ /**
73
+ * Prints connection guidance only. Firedrill never writes or edits an MCP
74
+ * client's configuration. Signed in, the server URL is the remote root the
75
+ * workspace's connection read returns (validated by mcpConnectionRead: https
76
+ * origin, no path suffix). Before sign-in it is the address the platform's
77
+ * public configuration publishes (or, for a platform that predates it, the fixed
78
+ * profile's address), so MCP setup does not depend on terminal device sign-in.
79
+ */
80
+ export async function mcpConnection(options, output) {
81
+ if (options.agent !== undefined && !MCP_CLIENTS.includes(options.agent))
82
+ throw new CliError('invalid_agent', 'Choose codex, claude or other for MCP connection guidance.', 2);
83
+ const signedIn = (await credentialOverview(options.signal)).signed_in;
84
+ if (!signedIn && options.workspace === undefined) {
85
+ const published = await platformMcp(options.signal);
86
+ if (published === null)
87
+ throw new CliError('mcp_not_configured', 'MCP access is not configured for this platform. Your terminal commands remain available.', 2);
88
+ const url = published ?? PROFILE_MCP[APP_ORIGIN];
89
+ if (!url)
90
+ throw new CliError('authentication_required', 'This platform has no published MCP address. Run firedrill init, then firedrill mcp.', 2);
91
+ const source = published ? 'platform_configuration' : 'platform_profile';
92
+ const guidance = guidanceFor(url, options.agent);
93
+ output({ enabled: null, url, source, authentication: 'oauth', guidance, client_configuration_changed: false }, guidanceText(url, guidance, source));
94
+ return;
95
+ }
96
+ const scope = await selectedWorkspace({ workspace: options.workspace,
97
+ interactive: !options.json && !!process.stdin.isTTY && !!process.stderr.isTTY,
98
+ signal: options.signal, openBrowser: options.openBrowser && !options.json });
99
+ const result = mcpConnectionRead(await api(`/api/workspaces/${scope.workspace}/mcp-connection`, {
100
+ actor: scope.owner, workspace: scope.workspace, signal: options.signal
101
+ }), scope.owner, scope.workspace, APP_ORIGIN);
102
+ if (!result.enabled || !result.url)
103
+ throw new CliError('mcp_not_configured', 'MCP access is not configured for this platform. Your terminal commands remain available.', 2);
104
+ const url = result.url;
105
+ if (!SAFE_ORIGIN.test(url))
106
+ throw new CliError('mcp_not_configured', 'The configured MCP address could not be printed safely. Your terminal commands remain available.', 2);
107
+ const guidance = guidanceFor(url, options.agent);
108
+ output({ ...result, source: 'workspace', guidance, client_configuration_changed: false }, guidanceText(url, guidance, 'workspace'));
109
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Simulated dependency routes (Plan20 FIR-490, root `native-dependency-access.v1`).
3
+ *
4
+ * Root does not hand the client endpoints or credentials for a target's
5
+ * dependencies. Each simulated (`synthetic`) dependency the descriptor declares
6
+ * is reached through the companion's per-drill loopback proxy
7
+ * (dependency-proxy.ts), which keeps root's short-lived token in memory. This
8
+ * module checks, before anything is waited for or started, that the approved plan
9
+ * routes every declared dependency into a process launched for this drill, and
10
+ * maps the proxy's loopback URLs onto the environment names the plan declares:
11
+ * - `dependency_env` entries on the dependency's service, `{alias, name, url}`,
12
+ * where `url` is `invoke` for an `http` dependency and `mcp` for an `mcp` one;
13
+ * - for `mcp` dependencies, the service's `mcp_config_env`, which receives the
14
+ * path of a private MCP client configuration (mcp-config.ts).
15
+ * A dependency the plan does not route is refused rather than left to reach a
16
+ * live service, and a `real-grant-required` dependency is refused because this
17
+ * version issues no real grants.
18
+ *
19
+ * Network posture: no root CA, proxy variable, TLS verification change or DNS
20
+ * change is made or accepted here. Proxy/CA/TLS environment names are refused as
21
+ * route targets, so a route can never redirect or weaken target traffic.
22
+ *
23
+ * Secrets: the loopback URLs carry the proxy's per-drill path capability, so they
24
+ * stay inside SecretValue until the injection point (child env value, private MCP
25
+ * config file). Error messages name aliases and env names only, never URLs.
26
+ */
27
+ import { nativeError } from './contracts.js';
28
+ // ---------------------------------------------------------------------------
29
+ // Shared validation helpers (also used by mcp-config.ts and target-auth.ts).
30
+ export const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
31
+ const HEADER_TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/;
32
+ /** Headers an identity grant may never set (transport-controlled or hop-by-hop). */
33
+ const FORBIDDEN_HEADERS = new Set(['host', 'content-length', 'transfer-encoding', 'connection', 'keep-alive', 'upgrade',
34
+ 'te', 'trailer', 'expect', 'proxy-authorization', 'proxy-authenticate', 'proxy-connection']);
35
+ /**
36
+ * Environment names a dependency route may never write. They control process
37
+ * loading, the base environment the supervisor builds, or network trust/routing.
38
+ * The comparison is case-insensitive because Windows env names are.
39
+ */
40
+ const RESERVED_ENV = new Set(['PATH', 'HOME', 'LANG', 'TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'SYSTEMROOT', 'COMSPEC',
41
+ 'PATHEXT', 'SHELL', 'USER', 'LOGNAME', 'NODE_OPTIONS', 'NODE_PATH', 'NODE_EXTRA_CA_CERTS', 'NODE_TLS_REJECT_UNAUTHORIZED',
42
+ 'SSL_CERT_FILE', 'SSL_CERT_DIR', 'REQUESTS_CA_BUNDLE', 'CURL_CA_BUNDLE', 'GIT_SSL_NO_VERIFY', 'PYTHONHTTPSVERIFY',
43
+ 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY', 'FTP_PROXY', 'PYTHONPATH', 'PYTHONSTARTUP', 'RUBYOPT', 'PERL5OPT',
44
+ 'BASH_ENV', 'ENV']);
45
+ const RESERVED_ENV_PREFIXES = ['LD_', 'DYLD_', 'FIREDRILL_'];
46
+ export function isReservedEnvName(name) {
47
+ const upper = name.toUpperCase();
48
+ return RESERVED_ENV.has(upper) || RESERVED_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix));
49
+ }
50
+ export function isValidHeaderName(name) {
51
+ return HEADER_TOKEN.test(name) && !FORBIDDEN_HEADERS.has(name.toLowerCase());
52
+ }
53
+ /**
54
+ * Every declared dependency's route, checked before anything starts. Throws
55
+ * `native_binding_invalid` (naming aliases and env names only) when:
56
+ * - a dependency requires a real grant (none is issued; no live service replaces it);
57
+ * - its service is not in the approved plan, or is shared or attached (it cannot
58
+ * receive per-drill URLs);
59
+ * - the plan gives it no env name and no MCP client configuration;
60
+ * - a plan route names an undeclared dependency, a dependency another service
61
+ * hosts, the wrong URL kind for the protocol, or an env name that is malformed,
62
+ * reserved, undeclared in the service configuration or used twice.
63
+ */
64
+ export function validateDependencyRoutes(descriptor, plan, options) {
65
+ const dependencies = new Map(descriptor.dependencies.map((dependency) => [dependency.alias, dependency]));
66
+ for (const service of plan.services) {
67
+ const configured = new Set((descriptor.services.find((entry) => entry.alias === service.alias)?.configuration ?? [])
68
+ .map((config) => config.name));
69
+ const taken = new Set([service.port_env, service.mcp_config_env]
70
+ .flatMap((name) => typeof name === 'string' && name ? [name.toUpperCase()] : []));
71
+ for (const entry of service.dependency_env ?? []) {
72
+ const dependency = dependencies.get(entry.alias);
73
+ if (!dependency || dependency.service_alias !== service.alias) {
74
+ throw nativeError('binding_invalid', `The approved plan routes dependency "${entry.alias}" into service "${service.alias}", which does not declare it. No drill was started.`);
75
+ }
76
+ if ((dependency.protocol === 'http' ? 'invoke' : 'mcp') !== entry.url) {
77
+ throw nativeError('binding_invalid', `The approved plan routes ${dependency.protocol} dependency "${entry.alias}" as ${entry.url}. No drill was started.`);
78
+ }
79
+ if (!ENV_NAME.test(entry.name) || isReservedEnvName(entry.name)) {
80
+ throw nativeError('binding_invalid', `Environment name ${entry.name} is reserved and cannot carry dependency "${entry.alias}". No drill was started.`);
81
+ }
82
+ if (!configured.has(entry.name)) {
83
+ throw nativeError('binding_invalid', `Environment name ${entry.name} for dependency "${entry.alias}" is not declared in the configuration of service "${service.alias}". No drill was started.`);
84
+ }
85
+ if (taken.has(entry.name.toUpperCase())) {
86
+ throw nativeError('binding_invalid', `Environment name ${entry.name} of service "${service.alias}" is already used. No drill was started.`);
87
+ }
88
+ taken.add(entry.name.toUpperCase());
89
+ }
90
+ }
91
+ const routes = [];
92
+ for (const dependency of descriptor.dependencies) {
93
+ if (dependency.mode === 'real-grant-required') {
94
+ throw nativeError('binding_invalid', `Dependency "${dependency.alias}" requires a real grant, which this version does not issue. No drill was started and no live service is used in its place.`);
95
+ }
96
+ const service = plan.services.find((entry) => entry.alias === dependency.service_alias);
97
+ if (!service) {
98
+ throw nativeError('binding_invalid', `Dependency "${dependency.alias}" was not routed: its service "${dependency.service_alias}" is not in the approved plan. No drill was started.`);
99
+ }
100
+ const env = (service.dependency_env ?? []).filter((entry) => entry.alias === dependency.alias)
101
+ .map((entry) => ({ name: entry.name, url: entry.url }));
102
+ const mcpConfig = dependency.protocol === 'mcp' && typeof service.mcp_config_env === 'string' && service.mcp_config_env.length > 0;
103
+ if (env.length === 0 && !mcpConfig) {
104
+ throw nativeError('binding_invalid', `Dependency "${dependency.alias}" was not routed: the approved plan gives service "${service.alias}" no environment name${dependency.protocol === 'mcp' ? ' or MCP client configuration' : ''} for it. No drill was started and no live service is used in its place.`);
105
+ }
106
+ if (!options.freshForDrill(service)) {
107
+ throw nativeError('binding_invalid', `Dependency "${dependency.alias}" was not routed: service "${service.alias}" must be launched for each drill to receive drill-scoped dependency URLs. No drill was started.`);
108
+ }
109
+ routes.push({ dependency, service, env, mcp_config: mcpConfig });
110
+ }
111
+ return routes;
112
+ }
113
+ /**
114
+ * One service's dependency env: each routed name receives its loopback URL. The
115
+ * supervisor applies these after `env_passthrough` and `env_static`, so a route
116
+ * always wins over a user-supplied value of the same name.
117
+ */
118
+ export function dependencyEnv(routes, serviceAlias, urls) {
119
+ const env = {};
120
+ const secrets = [];
121
+ for (const route of routes) {
122
+ if (route.service.alias !== serviceAlias)
123
+ continue;
124
+ for (const entry of route.env) {
125
+ const url = urls.url(route.dependency.alias, entry.url);
126
+ // Injection point: revealed only into the child env value.
127
+ env[entry.name] = url.reveal();
128
+ secrets.push(url);
129
+ }
130
+ }
131
+ return { env, secrets };
132
+ }
133
+ /** The MCP servers one service's private client configuration lists (server name = dependency alias). */
134
+ export function mcpServers(routes, serviceAlias, urls) {
135
+ return routes.filter((route) => route.service.alias === serviceAlias && route.mcp_config)
136
+ .map((route) => ({ name: route.dependency.alias, url: urls.url(route.dependency.alias, 'mcp') }));
137
+ }
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Canonical JSON, digests and the local descriptor pre-check (Plan20 W09).
3
+ *
4
+ * `canonicalJson` reproduces Python's
5
+ * `json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=True)`
6
+ * byte for byte for the JSON values the native client produces, so digests
7
+ * computed here equal the root service's digests over the same document.
8
+ *
9
+ * `normalizeDescriptor` mirrors root `native_descriptor.py` (same bounds, closed
10
+ * schema, uniqueness, startup order, domain rules, sorting and digest). It is a
11
+ * client-side pre-check only; the cloud result is authoritative.
12
+ */
13
+ import { createHash } from 'node:crypto';
14
+ import { CliError } from '../http.js';
15
+ import { ALIAS, DRIVER_PROFILES, LOCAL_DESCRIPTOR_SCHEMA, SERVICE_ROLES, SHA256_HEX, STATE_KINDS, STATE_LOCATIONS, STATE_RESETS, TARGET_AUTHENTICATIONS, } from './contracts.js';
16
+ // ---------------------------------------------------------------------------
17
+ // Canonical JSON.
18
+ const MAX_CANONICAL_DEPTH = 64;
19
+ /** Compare two strings by Unicode code point, which is Python's `str` ordering. */
20
+ export function compareCodePoints(a, b) {
21
+ if (!/[\ud800-\udfff]/.test(a) && !/[\ud800-\udfff]/.test(b))
22
+ return a < b ? -1 : a > b ? 1 : 0;
23
+ const left = Array.from(a, char => char.codePointAt(0));
24
+ const right = Array.from(b, char => char.codePointAt(0));
25
+ for (let index = 0; index < Math.min(left.length, right.length); index++) {
26
+ if (left[index] !== right[index])
27
+ return left[index] - right[index];
28
+ }
29
+ return left.length - right.length;
30
+ }
31
+ function canonicalString(value) {
32
+ let out = '"';
33
+ for (let index = 0; index < value.length; index++) {
34
+ const unit = value.charCodeAt(index);
35
+ if (unit === 0x22)
36
+ out += '\\"';
37
+ else if (unit === 0x5c)
38
+ out += '\\\\';
39
+ else if (unit === 0x0a)
40
+ out += '\\n';
41
+ else if (unit === 0x0d)
42
+ out += '\\r';
43
+ else if (unit === 0x09)
44
+ out += '\\t';
45
+ else if (unit === 0x08)
46
+ out += '\\b';
47
+ else if (unit === 0x0c)
48
+ out += '\\f';
49
+ // Python ensure_ascii escapes everything outside ' '..'~', per UTF-16 unit
50
+ // (non-BMP characters become surrogate pairs), with lowercase hex.
51
+ else if (unit < 0x20 || unit > 0x7e)
52
+ out += '\\u' + unit.toString(16).padStart(4, '0');
53
+ else
54
+ out += value[index];
55
+ }
56
+ return out + '"';
57
+ }
58
+ /** Python `repr(float)` for a finite, non-integral number. */
59
+ function pythonFloat(value) {
60
+ const sign = value < 0 ? '-' : '';
61
+ const [mantissa, exponentText] = Math.abs(value).toExponential().split('e');
62
+ const digits = mantissa.replace('.', '');
63
+ const exponent = Number(exponentText);
64
+ if (exponent < -4 || exponent >= 16) {
65
+ const body = digits.length > 1 ? `${digits[0]}.${digits.slice(1)}` : digits;
66
+ return `${sign}${body}e${exponent < 0 ? '-' : '+'}${String(Math.abs(exponent)).padStart(2, '0')}`;
67
+ }
68
+ const point = exponent + 1;
69
+ if (point <= 0)
70
+ return `${sign}0.${'0'.repeat(-point)}${digits}`;
71
+ if (point >= digits.length)
72
+ return `${sign}${digits}${'0'.repeat(point - digits.length)}.0`;
73
+ return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
74
+ }
75
+ function canonicalNumber(value) {
76
+ if (!Number.isFinite(value))
77
+ throw canonicalInvalid();
78
+ // Integral values are written as integers, as Python writes a parsed JSON integer.
79
+ if (Number.isInteger(value))
80
+ return Number.isSafeInteger(value) ? String(value === 0 ? 0 : value) : BigInt(value).toString();
81
+ return pythonFloat(value);
82
+ }
83
+ const canonicalInvalid = () => new CliError('native_canonical_invalid', 'A value could not be written in canonical form. Nothing was hashed or sent.');
84
+ function canonicalValue(value, depth) {
85
+ if (depth > MAX_CANONICAL_DEPTH)
86
+ throw canonicalInvalid();
87
+ if (value === null)
88
+ return 'null';
89
+ if (value !== undefined && typeof value.toJSON === 'function') {
90
+ return canonicalValue(value.toJSON(), depth + 1);
91
+ }
92
+ switch (typeof value) {
93
+ case 'string': return canonicalString(value);
94
+ case 'boolean': return value ? 'true' : 'false';
95
+ case 'number': return canonicalNumber(value);
96
+ case 'bigint': return value.toString();
97
+ case 'undefined': return undefined;
98
+ case 'object': break;
99
+ default: throw canonicalInvalid();
100
+ }
101
+ if (Array.isArray(value)) {
102
+ return '[' + value.map(item => {
103
+ const encoded = canonicalValue(item, depth + 1);
104
+ if (encoded === undefined)
105
+ throw canonicalInvalid();
106
+ return encoded;
107
+ }).join(',') + ']';
108
+ }
109
+ const prototype = Object.getPrototypeOf(value);
110
+ if (prototype !== Object.prototype && prototype !== null)
111
+ throw canonicalInvalid();
112
+ const parts = [];
113
+ for (const key of Object.keys(value).sort(compareCodePoints)) {
114
+ const encoded = canonicalValue(value[key], depth + 1);
115
+ if (encoded !== undefined)
116
+ parts.push(canonicalString(key) + ':' + encoded);
117
+ }
118
+ return '{' + parts.join(',') + '}';
119
+ }
120
+ /**
121
+ * Canonical JSON matching Python `json.dumps(sort_keys=True, separators=(',',':'),
122
+ * ensure_ascii=True)`. Accepts plain objects, arrays, strings, booleans, null,
123
+ * finite numbers and objects with `toJSON` (SecretValue serializes redacted).
124
+ * Undefined object properties are omitted. Non-finite numbers, cycles beyond the
125
+ * depth bound and non-plain objects are refused with `native_canonical_invalid`.
126
+ */
127
+ export function canonicalJson(value) {
128
+ const encoded = canonicalValue(value, 0);
129
+ if (encoded === undefined)
130
+ throw canonicalInvalid();
131
+ return encoded;
132
+ }
133
+ /** Lowercase hex SHA-256 of a UTF-8 string or raw bytes. */
134
+ export function sha256Hex(data) {
135
+ return createHash('sha256').update(data).digest('hex');
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // Local descriptor pre-check (mirror of root native_descriptor.py).
139
+ const MAX_BYTES = 262_144;
140
+ const MAX_NODES = 65_536;
141
+ const MAX_DEPTH = 16;
142
+ const CONFIG_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
143
+ const OPERATION = /^[A-Za-z0-9_.:/-]+$/;
144
+ function invalid(message = 'Check the local agent descriptor fields.') {
145
+ // Never reflect rejected values.
146
+ throw new CliError('local_descriptor_invalid', message, 2);
147
+ }
148
+ const codePointLength = (value) => {
149
+ let count = 0;
150
+ for (const _ of value)
151
+ count++;
152
+ return count;
153
+ };
154
+ function isPlainObject(value) {
155
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
156
+ return false;
157
+ const prototype = Object.getPrototypeOf(value);
158
+ return prototype === Object.prototype || prototype === null;
159
+ }
160
+ function boundedJson(value) {
161
+ const pending = [[value, 0]];
162
+ let count = 0;
163
+ while (pending.length) {
164
+ const [item, depth] = pending.pop();
165
+ count++;
166
+ if (count > MAX_NODES || depth > MAX_DEPTH)
167
+ invalid('The descriptor is too large or deeply nested.');
168
+ if (isPlainObject(item)) {
169
+ const keys = Object.keys(item);
170
+ if (keys.length > 256 || keys.some(key => codePointLength(key) > 128))
171
+ invalid();
172
+ for (const key of keys)
173
+ pending.push([item[key], depth + 1]);
174
+ }
175
+ else if (Array.isArray(item)) {
176
+ if (item.length > 256)
177
+ invalid();
178
+ for (const child of item)
179
+ pending.push([child, depth + 1]);
180
+ }
181
+ else if (typeof item === 'string') {
182
+ if (codePointLength(item) > 4096 || /[\u0000-\u001f]/.test(item))
183
+ invalid();
184
+ }
185
+ else if (item !== null && typeof item !== 'boolean' && !(typeof item === 'number' && Number.isInteger(item))) {
186
+ invalid();
187
+ }
188
+ }
189
+ let encoded;
190
+ try {
191
+ encoded = canonicalJson(value);
192
+ }
193
+ catch {
194
+ invalid();
195
+ }
196
+ if (encoded.length > MAX_BYTES)
197
+ invalid('The descriptor exceeds the accepted size.');
198
+ return encoded;
199
+ }
200
+ // Closed-schema checks equivalent to local-descriptor.v1.schema.json.
201
+ function exact(value, keys) {
202
+ if (!isPlainObject(value))
203
+ invalid();
204
+ const present = Object.keys(value);
205
+ if (present.length !== keys.length || keys.some(key => !Object.hasOwn(value, key)))
206
+ invalid();
207
+ return value;
208
+ }
209
+ function array(value, min, max, unique = false) {
210
+ if (!Array.isArray(value) || value.length < min || value.length > max)
211
+ invalid();
212
+ if (unique && new Set(value.map(item => canonicalJson(item))).size !== value.length)
213
+ invalid();
214
+ return value;
215
+ }
216
+ function alias(value) {
217
+ if (typeof value !== 'string' || value.length > 64 || !ALIAS.test(value))
218
+ invalid();
219
+ }
220
+ function sha(value) {
221
+ if (typeof value !== 'string' || !SHA256_HEX.test(value))
222
+ invalid();
223
+ }
224
+ function oneOf(value, options) {
225
+ if (typeof value !== 'string' || !options.includes(value))
226
+ invalid();
227
+ }
228
+ function bool(value) {
229
+ if (typeof value !== 'boolean')
230
+ invalid();
231
+ }
232
+ function checkSchema(value) {
233
+ const document = exact(value, ['schema', 'source_roots', 'services', 'entry_service', 'driver', 'state_domains', 'dependencies']);
234
+ if (document.schema !== LOCAL_DESCRIPTOR_SCHEMA)
235
+ invalid();
236
+ for (const item of array(document.source_roots, 1, 128)) {
237
+ const root = exact(item, ['alias', 'identity_sha256']);
238
+ alias(root.alias);
239
+ sha(root.identity_sha256);
240
+ }
241
+ for (const item of array(document.services, 1, 128)) {
242
+ const service = exact(item, ['alias', 'root_alias', 'role', 'depends_on', 'configuration']);
243
+ alias(service.alias);
244
+ alias(service.root_alias);
245
+ oneOf(service.role, SERVICE_ROLES);
246
+ array(service.depends_on, 0, 128, true).forEach(alias);
247
+ for (const entry of array(service.configuration, 0, 256)) {
248
+ const config = exact(entry, ['name', 'phase', 'secret', 'required']);
249
+ if (typeof config.name !== 'string' || config.name.length > 128 || !CONFIG_NAME.test(config.name))
250
+ invalid();
251
+ oneOf(config.phase, ['build', 'runtime', 'both']);
252
+ bool(config.secret);
253
+ bool(config.required);
254
+ }
255
+ }
256
+ alias(document.entry_service);
257
+ const driver = exact(document.driver, ['profile', 'adapter_sha256', 'authentication', 'state_domains']);
258
+ oneOf(driver.profile, DRIVER_PROFILES);
259
+ sha(driver.adapter_sha256);
260
+ oneOf(driver.authentication, TARGET_AUTHENTICATIONS);
261
+ array(driver.state_domains, 0, 256, true).forEach(alias);
262
+ for (const item of array(document.state_domains, 0, 256)) {
263
+ const domain = exact(item, ['alias', 'service_alias', 'kind', 'location', 'reset', 'concurrency', 'hook_sha256']);
264
+ alias(domain.alias);
265
+ alias(domain.service_alias);
266
+ oneOf(domain.kind, STATE_KINDS);
267
+ oneOf(domain.location, STATE_LOCATIONS);
268
+ oneOf(domain.reset, STATE_RESETS);
269
+ oneOf(domain.concurrency, ['independent', 'serial']);
270
+ if (domain.hook_sha256 !== null)
271
+ sha(domain.hook_sha256);
272
+ }
273
+ for (const item of array(document.dependencies, 0, 256)) {
274
+ const dependency = exact(item, ['alias', 'service_alias', 'protocol', 'mode', 'operations']);
275
+ alias(dependency.alias);
276
+ alias(dependency.service_alias);
277
+ oneOf(dependency.protocol, ['http', 'mcp']);
278
+ oneOf(dependency.mode, ['synthetic', 'real-grant-required']);
279
+ for (const operation of array(dependency.operations, 1, 256, true)) {
280
+ if (typeof operation !== 'string' || !operation.length || operation.length > 128 || !OPERATION.test(operation))
281
+ invalid();
282
+ }
283
+ }
284
+ }
285
+ function indexed(items, label) {
286
+ const map = new Map(items.map(item => [item.alias, item]));
287
+ if (map.size !== items.length)
288
+ invalid(`Use distinct ${label} aliases.`);
289
+ return map;
290
+ }
291
+ const byAlias = (a, b) => compareCodePoints(a.alias, b.alias);
292
+ /**
293
+ * Validate and normalize a local descriptor exactly as root `validate_local_descriptor`
294
+ * does. Throws `local_descriptor_invalid` (exit 2) without echoing rejected values.
295
+ */
296
+ export function normalizeDescriptor(value) {
297
+ const raw = boundedJson(value);
298
+ checkSchema(value);
299
+ const document = JSON.parse(raw);
300
+ const roots = indexed(document.source_roots, 'source root');
301
+ const services = indexed(document.services, 'service');
302
+ const domains = indexed(document.state_domains, 'state domain');
303
+ indexed(document.dependencies, 'dependency');
304
+ if (!services.has(document.entry_service))
305
+ invalid('Choose an entry service from the declared services.');
306
+ const remaining = new Map();
307
+ for (const [name, service] of services) {
308
+ const dependencies = new Set(service.depends_on);
309
+ if (!roots.has(service.root_alias) || [...dependencies].some(item => !services.has(item)) || dependencies.has(name)) {
310
+ invalid('Use declared roots and other services for startup dependencies.');
311
+ }
312
+ const config = service.configuration;
313
+ if (new Set(config.map(item => item.name)).size !== config.length)
314
+ invalid('Declare each configuration variable once per service.');
315
+ service.configuration = [...config].sort((a, b) => compareCodePoints(a.name, b.name));
316
+ service.depends_on = [...dependencies].sort(compareCodePoints);
317
+ remaining.set(name, dependencies);
318
+ }
319
+ const startupOrder = [];
320
+ while (remaining.size) {
321
+ const ready = [...remaining].filter(([, dependencies]) => !dependencies.size).map(([name]) => name).sort(compareCodePoints);
322
+ if (!ready.length)
323
+ invalid('Startup dependencies contain a cycle.');
324
+ startupOrder.push(...ready);
325
+ for (const name of ready)
326
+ remaining.delete(name);
327
+ for (const dependencies of remaining.values())
328
+ for (const name of ready)
329
+ dependencies.delete(name);
330
+ }
331
+ for (const domain of domains.values()) {
332
+ if (!services.has(domain.service_alias))
333
+ invalid('Assign each state domain to a declared service.');
334
+ const hooked = domain.reset === 'approved-hook';
335
+ if (hooked !== (domain.hook_sha256 !== null))
336
+ invalid('Provide a hook digest only for an approved reset hook.');
337
+ if ((domain.location === 'observation-only' || domain.reset === 'unavailable') && domain.concurrency !== 'serial') {
338
+ invalid('State without a reset boundary requires serial execution.');
339
+ }
340
+ if (domain.location === 'observation-only' && domain.reset !== 'unavailable')
341
+ invalid('Observation-only state cannot declare a reset capability.');
342
+ if (domain.reset === 'fresh-process' && domain.kind !== 'process')
343
+ invalid('Restarting a process does not reset its external state.');
344
+ if (domain.reset === 'fresh-session' && domain.kind !== 'browser' && domain.kind !== 'session') {
345
+ invalid('A new session does not reset external storage.');
346
+ }
347
+ }
348
+ const driver = document.driver;
349
+ const driverDomains = new Set(driver.state_domains);
350
+ if (driverDomains.size !== domains.size || [...driverDomains].some(name => !domains.has(name))) {
351
+ invalid('The driver must account for every declared state domain.');
352
+ }
353
+ driver.state_domains = [...driver.state_domains].sort(compareCodePoints);
354
+ for (const dependency of document.dependencies) {
355
+ if (!services.has(dependency.service_alias))
356
+ invalid('Assign each dependency to a declared service.');
357
+ dependency.operations = [...dependency.operations].sort(compareCodePoints);
358
+ }
359
+ document.source_roots = [...document.source_roots].sort(byAlias);
360
+ document.services = [...document.services].sort(byAlias);
361
+ document.state_domains = [...document.state_domains].sort(byAlias);
362
+ document.dependencies = [...document.dependencies].sort(byAlias);
363
+ const canonical = boundedJson(document);
364
+ return {
365
+ descriptor: document,
366
+ descriptor_sha256: sha256Hex(canonical),
367
+ startup_order: startupOrder,
368
+ requires_serial_execution: !domains.size || [...domains.values()].some(item => item.concurrency === 'serial'),
369
+ };
370
+ }