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,235 @@
1
+ /** Replacement marker written in place of a redacted value. */
2
+ export const REDACTED = '[redacted]';
3
+ /** Largest input a single `redact` call processes; longer text is truncated. */
4
+ export const MAX_REDACT_CHARS = 4 * 1024 * 1024;
5
+ const TRUNCATED = '[truncated]';
6
+ const MAX_SECRET_CHARS = 65_536;
7
+ const MAX_SECRETS = 512;
8
+ /** Exact strings to redact for each secret: the value plus URL/base64 encodings. */
9
+ function secretVariants(secrets) {
10
+ if (secrets.length > MAX_SECRETS)
11
+ return null;
12
+ const variants = new Set();
13
+ let total = 0;
14
+ for (const secret of secrets.slice(0, MAX_SECRETS)) {
15
+ const value = secret.reveal();
16
+ if (typeof value !== 'string' || !value || value.length > MAX_SECRET_CHARS)
17
+ return null;
18
+ variants.add(value);
19
+ let encoded;
20
+ try {
21
+ encoded = encodeURIComponent(value);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ if (encoded !== value)
27
+ variants.add(encoded);
28
+ if (value.length >= 6) {
29
+ const bytes = Buffer.from(value, 'utf8');
30
+ variants.add(bytes.toString('base64'));
31
+ variants.add(bytes.toString('base64url'));
32
+ variants.add(bytes.toString('base64').replace(/=+$/, ''));
33
+ }
34
+ total += value.length * 12;
35
+ if (total > MAX_REDACT_CHARS)
36
+ return null;
37
+ }
38
+ // Longest first, so a value never leaves a partial copy of a longer variant.
39
+ return [...variants].sort((a, b) => b.length - a.length);
40
+ }
41
+ const SECRET_KEY = '(?:secret|token|passw(?:or)?d|pwd|passphrase|api[_-]?key|access[_-]?key|private[_-]?key|credentials?|authorization|session[_-]?id|cookie)';
42
+ const KEY_VALUE = new RegExp(`(?<![A-Za-z0-9_.-])([A-Za-z0-9_.-]{0,40}${SECRET_KEY}[A-Za-z0-9_.-]{0,40}["']?\\s{0,4}[:=]\\s{0,4}["']?)(?!\\[redacted\\])([^\\s"'&,;<>}\\]]{1,4096})`, 'gi');
43
+ const AUTH_SCHEME = /\b(Bearer|Basic|Token|Digest)(\s+)(?!\[redacted\])[A-Za-z0-9._~+/=-]{8,}/gi;
44
+ const JWT_LIKE = /\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]*/g;
45
+ const PRIVATE_KEY_BLOCK = /-----BEGIN [A-Z0-9 ]{0,40}PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z0-9 ]{0,40}PRIVATE KEY-----|$)/g;
46
+ const URL_CREDENTIAL = /(\b[a-z][a-z0-9+.-]{0,30}:\/\/[^\s:/@]{1,256}:)([^\s@/]{1,1024})(@)/gi;
47
+ const LONG_HEX = /\b[A-Fa-f0-9]{32,}\b/g;
48
+ const LONG_MIXED = /[A-Za-z0-9+/_-]{32,}={0,2}/g;
49
+ const count = (value, pattern) => value.match(pattern)?.length ?? 0;
50
+ /** A long run looks like key material: mixed case and digits, and not path-like. */
51
+ function looksLikeKey(value) {
52
+ if (value.startsWith('/') || count(value, /\//g) > 2)
53
+ return false;
54
+ return count(value, /[A-Z]/g) >= 2 && count(value, /[a-z]/g) >= 2 && count(value, /[0-9]/g) >= 2;
55
+ }
56
+ function keyValue(_match, prefix, value) {
57
+ // Keep small numeric settings and literals readable (for example a length limit).
58
+ if (/^(?:\d{1,9}|true|false|null|none)$/i.test(value))
59
+ return prefix + value;
60
+ return prefix + REDACTED;
61
+ }
62
+ /** Token-shape rules in application order. Exported for streaming cut checks. */
63
+ export const TOKEN_SHAPES = [
64
+ [PRIVATE_KEY_BLOCK, REDACTED],
65
+ [URL_CREDENTIAL, `$1${REDACTED}$3`],
66
+ [AUTH_SCHEME, `$1$2${REDACTED}`],
67
+ [JWT_LIKE, REDACTED],
68
+ [KEY_VALUE, keyValue],
69
+ [LONG_HEX, REDACTED],
70
+ [LONG_MIXED, (match) => looksLikeKey(match) ? REDACTED : match],
71
+ ];
72
+ function redactWith(text, variants) {
73
+ let out = text;
74
+ for (const value of variants)
75
+ if (out.includes(value))
76
+ out = out.split(value).join(REDACTED);
77
+ for (const [pattern, replacement] of TOKEN_SHAPES) {
78
+ pattern.lastIndex = 0;
79
+ out = typeof replacement === 'string' ? out.replace(pattern, replacement) : out.replace(pattern, replacement);
80
+ }
81
+ return out;
82
+ }
83
+ /**
84
+ * Replace exact secret values and common token shapes in `text`. Bounded:
85
+ * input beyond MAX_REDACT_CHARS is truncated (after redaction of the retained
86
+ * part, so a secret straddling the limit is still removed).
87
+ */
88
+ export function redact(text, secrets) {
89
+ const input = typeof text === 'string' ? text : String(text);
90
+ const variants = secretVariants(secrets);
91
+ if (variants === null)
92
+ return REDACTED;
93
+ const longest = variants.length ? variants[0].length : 0;
94
+ const truncated = input.length > MAX_REDACT_CHARS;
95
+ const out = redactWith(truncated ? input.slice(0, MAX_REDACT_CHARS + longest + 1024) : input, variants);
96
+ return truncated ? out.slice(0, MAX_REDACT_CHARS) + TRUNCATED : out;
97
+ }
98
+ /**
99
+ * Replace only exact secret values (and their encodings), never token shapes.
100
+ * For short values whose public shape is known, such as a sha256 digest field,
101
+ * which the long-hex shape would otherwise erase.
102
+ */
103
+ export function redactSecrets(text, secrets) {
104
+ let out = typeof text === 'string' ? text : String(text);
105
+ const variants = secretVariants(secrets);
106
+ if (variants === null)
107
+ return REDACTED;
108
+ for (const value of variants)
109
+ if (out.includes(value))
110
+ out = out.split(value).join(REDACTED);
111
+ return out;
112
+ }
113
+ /**
114
+ * Largest cut point <= `cut` such that no exact secret occurrence or token-shape
115
+ * match straddles it, and no secret that may still be arriving starts before it.
116
+ */
117
+ function prefixTable(value) {
118
+ const table = new Int32Array(value.length);
119
+ for (let index = 1, matched = 0; index < value.length; index++) {
120
+ while (matched && value[index] !== value[matched])
121
+ matched = table[matched - 1];
122
+ if (value[index] === value[matched])
123
+ matched++;
124
+ table[index] = matched;
125
+ }
126
+ return table;
127
+ }
128
+ function safeCut(text, cut, variants, prefixes) {
129
+ let point = cut;
130
+ for (let changed = true; changed && point > 0;) {
131
+ changed = false;
132
+ for (let variant = 0; variant < variants.length; variant++) {
133
+ const value = variants[variant], table = prefixes[variant];
134
+ const full = text.indexOf(value, Math.max(0, point - value.length + 1));
135
+ if (full >= 0 && full < point) {
136
+ point = full;
137
+ changed = true;
138
+ }
139
+ // Linear suffix/prefix matching avoids quadratic repeated-character
140
+ // scans for long values, including encoded secrets split across chunks.
141
+ let matched = 0;
142
+ for (let index = Math.max(0, text.length - value.length + 1); index < text.length; index++) {
143
+ while (matched && text[index] !== value[matched])
144
+ matched = table[matched - 1];
145
+ if (text[index] === value[matched])
146
+ matched++;
147
+ }
148
+ const partial = text.length - matched;
149
+ if (matched && partial < point) {
150
+ point = partial;
151
+ changed = true;
152
+ }
153
+ }
154
+ for (const [pattern] of TOKEN_SHAPES) {
155
+ pattern.lastIndex = 0;
156
+ for (const match of text.matchAll(pattern)) {
157
+ const start = match.index ?? 0, end = start + match[0].length;
158
+ if (start < point && (end > point || end === text.length)) {
159
+ point = start;
160
+ changed = true;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return point;
166
+ }
167
+ /**
168
+ * Streaming redaction for process output. Text is emitted to `sink` only after
169
+ * redaction, at line boundaries when possible; a line longer than `maxLine` is
170
+ * cut at a point no secret straddles. Incomplete matches are retained within a
171
+ * finite budget; overflow suppresses the remaining capture rather than leaking
172
+ * an incomplete credential. Call `flush()` only at end of the stream.
173
+ */
174
+ export class RedactingCapture {
175
+ #variants;
176
+ #prefixes;
177
+ #sink;
178
+ #maxLine;
179
+ #pending = '';
180
+ #suppressed = false;
181
+ #pendingLimit;
182
+ constructor(secrets, sink, maxLine = 16_384) {
183
+ const variants = secretVariants(secrets);
184
+ this.#variants = variants ?? [];
185
+ this.#prefixes = this.#variants.map(prefixTable);
186
+ this.#sink = sink;
187
+ this.#maxLine = Number.isFinite(maxLine) ? Math.max(1024, Math.min(Math.floor(maxLine), 1_048_576)) : 16_384;
188
+ this.#pendingLimit = Math.max(this.#maxLine * 4, (this.#variants[0]?.length ?? 0) * 2 + 1024);
189
+ if (variants === null) {
190
+ this.#suppressed = true;
191
+ this.#sink(REDACTED);
192
+ }
193
+ }
194
+ /** Add decoded text; complete, safe portions are redacted and emitted. */
195
+ push(chunk) {
196
+ if (!chunk || this.#suppressed)
197
+ return;
198
+ for (let offset = 0; offset < chunk.length && !this.#suppressed; offset += 32768) {
199
+ this.#pushBounded(chunk.slice(offset, offset + 32768));
200
+ }
201
+ }
202
+ #pushBounded(chunk) {
203
+ this.#pending += chunk;
204
+ let cut = this.#pending.lastIndexOf('\n') + 1;
205
+ if (!cut && this.#pending.length > this.#maxLine)
206
+ cut = Math.max(0, this.#pending.length - 256);
207
+ if (!cut)
208
+ return;
209
+ cut = safeCut(this.#pending, cut, this.#variants, this.#prefixes);
210
+ if (!cut) {
211
+ // An incomplete secret/token is not safe merely because the buffer is
212
+ // full. Suppress this capture rather than exposing its prefix or suffix.
213
+ if (this.#pending.length > this.#pendingLimit) {
214
+ this.#pending = '';
215
+ this.#suppressed = true;
216
+ this.#sink(REDACTED + TRUNCATED);
217
+ }
218
+ return;
219
+ }
220
+ const ready = this.#pending.slice(0, cut);
221
+ this.#pending = this.#pending.slice(cut);
222
+ this.#sink(redactWith(ready, this.#variants));
223
+ }
224
+ /** Redact and emit everything still pending. */
225
+ flush() {
226
+ if (!this.#pending)
227
+ return;
228
+ const ready = this.#pending;
229
+ this.#pending = '';
230
+ // EOF may cut off a credential mid-value. Do not expose the withheld
231
+ // prefix just because no more process bytes will arrive.
232
+ const cut = safeCut(ready, ready.length, this.#variants, this.#prefixes);
233
+ this.#sink(redactWith(ready.slice(0, cut), this.#variants) + (cut < ready.length ? REDACTED : ''));
234
+ }
235
+ }
@@ -0,0 +1,155 @@
1
+ // Consumer for the root-released native metadata registration producer
2
+ // (docs: platform docs/implementation/plan20-contracts.md, operation ids
3
+ // native_agent_register, native_descriptors_list, native_descriptor_register,
4
+ // native_descriptor_get). Registration sends declared metadata only: no
5
+ // source upload, no absolute paths, no secret values, and nothing executes.
6
+ // Producer source exists; it is not deployed or qualified, so these calls are
7
+ // wiring-ready rather than verified.
8
+ import { randomUUID } from 'node:crypto';
9
+ import { api } from '../auth.js';
10
+ import { CliError } from '../http.js';
11
+ import { normalizeDescriptor } from './canonical.js';
12
+ import { SHA256_HEX } from './contracts.js';
13
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14
+ const NAME_LIMIT = 120;
15
+ const RESPONSE_LIMIT = 1024 * 1024;
16
+ const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
17
+ const unconfirmed = () => new CliError('native_registration_unconfirmed', 'The registration response could not be confirmed. Nothing local changed; rerun with the same request key to read the saved receipt.');
18
+ /** Returns a new idempotency key, or validates a caller-supplied one for replay. */
19
+ export function registrationKey(supplied) {
20
+ if (supplied === undefined)
21
+ return randomUUID();
22
+ if (!UUID.test(supplied))
23
+ throw new CliError('native_request_key_invalid', 'Use the request key printed by the earlier attempt. Nothing was submitted.', 2);
24
+ return supplied.toLowerCase();
25
+ }
26
+ const DECLARED_NAME_LIMIT = 120;
27
+ /**
28
+ * The optional self-reported coding-agent name sent with a registration or
29
+ * revision, trimmed the same way every time so a replay keeps the same request.
30
+ * It is what the caller says it is, never inferred (from a user agent or
31
+ * anything else) and never verified.
32
+ */
33
+ export function declaredAgentName(value) {
34
+ if (value === undefined)
35
+ return undefined;
36
+ const trimmed = value.trim();
37
+ if (!trimmed || trimmed.length > DECLARED_NAME_LIMIT || /[\u0000-\u001f\u007f]/.test(value)) {
38
+ throw new CliError('native_descriptor_agent_name_invalid', `Use a coding-agent name of 1 to ${DECLARED_NAME_LIMIT} characters without control characters or credentials. Nothing was submitted.`, 2);
39
+ }
40
+ return trimmed;
41
+ }
42
+ function attributionMatches(value) {
43
+ if (value === undefined || value === null)
44
+ return true;
45
+ return object(value) && Object.keys(value).length === 4 && typeof value.actor_id === 'string' && UUID.test(value.actor_id)
46
+ && (value.channel === 'browser' || value.channel === 'terminal' || value.channel === 'management_mcp')
47
+ && value.verified_client_name === null
48
+ && (value.declared_agent_name === null || (typeof value.declared_agent_name === 'string' && value.declared_agent_name.length >= 1
49
+ && value.declared_agent_name.length <= DECLARED_NAME_LIMIT));
50
+ }
51
+ /** The name this request declared is the one root recorded (none when none was declared). */
52
+ export function declaredNameRecorded(row, declared) {
53
+ return (row.caller_attribution?.declared_agent_name ?? null) === (declared ?? null);
54
+ }
55
+ function agentName(name) {
56
+ const trimmed = name.trim();
57
+ if (!trimmed || name.length > NAME_LIMIT || /[\u0000-\u001f\u007f]/.test(name)) {
58
+ throw new CliError('native_registration_invalid', `Use an agent name of up to ${NAME_LIMIT} characters without control characters. Nothing was submitted.`, 2);
59
+ }
60
+ return trimmed;
61
+ }
62
+ export function descriptorRevisionRow(value, expectedDigest, withDocument) {
63
+ if (!object(value) || typeof value.id !== 'string' || !UUID.test(value.id) || typeof value.agent_id !== 'string' || !UUID.test(value.agent_id)
64
+ || typeof value.revision !== 'number' || !Number.isSafeInteger(value.revision) || value.revision < 1
65
+ || typeof value.descriptor_sha256 !== 'string' || !SHA256_HEX.test(value.descriptor_sha256)
66
+ || value.origin !== 'local_descriptor' || value.provenance !== 'customer_declared'
67
+ || typeof value.requires_serial_execution !== 'boolean' || typeof value.created_at !== 'string'
68
+ || !attributionMatches(value.caller_attribution))
69
+ throw unconfirmed();
70
+ if (expectedDigest !== null && value.descriptor_sha256 !== expectedDigest) {
71
+ throw new CliError('native_descriptor_digest_mismatch', 'The service recorded a different descriptor digest than this CLI computed. Do not run a plan against it; update the CLI or report the mismatch.');
72
+ }
73
+ if (withDocument && (!object(value.descriptor) || !Array.isArray(value.startup_order)
74
+ || value.startup_order.some(alias => typeof alias !== 'string')))
75
+ throw unconfirmed();
76
+ return value;
77
+ }
78
+ /**
79
+ * Registers a new local agent from a descriptor. The descriptor is normalized
80
+ * locally first so invalid input never leaves the machine and the returned
81
+ * digest can be checked against the local computation.
82
+ */
83
+ export async function registerNativeAgent(scope, input) {
84
+ const name = agentName(input.name);
85
+ const declared = declaredAgentName(input.declaredAgentName);
86
+ const local = normalizeDescriptor(input.descriptor);
87
+ const requestKey = registrationKey(input.requestKey);
88
+ const raw = await api(`/api/workspaces/${scope.workspace}/native-agents`, {
89
+ method: 'POST', actor: scope.owner, workspace: scope.workspace, signal: input.signal, maximumResponseBytes: RESPONSE_LIMIT,
90
+ body: { request_key: requestKey, name, descriptor: local.descriptor, ...(declared === undefined ? {} : { declared_agent_name: declared }) },
91
+ });
92
+ const receipt = registrationReceiptRow(raw, local.descriptor_sha256);
93
+ if (!declaredNameRecorded(receipt.descriptor, declared))
94
+ throw unconfirmed();
95
+ return { ...receipt, request_key: requestKey };
96
+ }
97
+ /** A registration receipt `{agent_id, descriptor}`: the new agent and its first descriptor revision, with its document. */
98
+ export function registrationReceiptRow(raw, expectedDigest) {
99
+ if (!object(raw) || typeof raw.agent_id !== 'string' || !UUID.test(raw.agent_id))
100
+ throw unconfirmed();
101
+ const descriptor = descriptorRevisionRow(raw.descriptor, expectedDigest, true);
102
+ if (descriptor.agent_id !== raw.agent_id || descriptor.revision !== 1)
103
+ throw unconfirmed();
104
+ return { agent_id: raw.agent_id, descriptor };
105
+ }
106
+ /**
107
+ * Records a new immutable descriptor revision for an existing agent. Fails with
108
+ * native_descriptor_revision_conflict (from the producer) if another revision
109
+ * landed after `expectedRevision`.
110
+ */
111
+ export async function reviseNativeDescriptor(scope, input) {
112
+ if (!UUID.test(input.agentId))
113
+ throw new CliError('native_agent_invalid', 'Use the agent id from `firedrill status`. Nothing was submitted.', 2);
114
+ if (!Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0) {
115
+ throw new CliError('native_revision_invalid', 'Use the latest descriptor revision number. Nothing was submitted.', 2);
116
+ }
117
+ const declared = declaredAgentName(input.declaredAgentName);
118
+ const local = normalizeDescriptor(input.descriptor);
119
+ const requestKey = registrationKey(input.requestKey);
120
+ const raw = await api(`/api/workspaces/${scope.workspace}/agents/${input.agentId}/native-descriptors`, {
121
+ method: 'POST', actor: scope.owner, workspace: scope.workspace, signal: input.signal, maximumResponseBytes: RESPONSE_LIMIT,
122
+ body: { request_key: requestKey, expected_revision: input.expectedRevision, descriptor: local.descriptor,
123
+ ...(declared === undefined ? {} : { declared_agent_name: declared }) },
124
+ });
125
+ const row = descriptorRevisionRow(raw, local.descriptor_sha256, true);
126
+ if (row.agent_id !== input.agentId.toLowerCase() || row.revision !== input.expectedRevision + 1 || !declaredNameRecorded(row, declared))
127
+ throw unconfirmed();
128
+ return { ...row, request_key: requestKey };
129
+ }
130
+ /** Lists descriptor history, newest first, without full documents. */
131
+ export async function listNativeDescriptors(scope, input) {
132
+ if (!UUID.test(input.agentId))
133
+ throw new CliError('native_agent_invalid', 'Use the agent id from `firedrill status`.', 2);
134
+ const page = input.page ?? 1;
135
+ if (!Number.isSafeInteger(page) || page < 1)
136
+ throw new CliError('native_page_invalid', 'Use a page number of 1 or more.', 2);
137
+ const raw = await api(`/api/workspaces/${scope.workspace}/agents/${input.agentId}/native-descriptors?page=${page}`, {
138
+ actor: scope.owner, workspace: scope.workspace, signal: input.signal, maximumResponseBytes: RESPONSE_LIMIT,
139
+ });
140
+ if (!object(raw) || typeof raw.count !== 'number' || !Array.isArray(raw.results))
141
+ throw unconfirmed();
142
+ return { count: raw.count, next: typeof raw.next === 'string' ? raw.next : null,
143
+ previous: typeof raw.previous === 'string' ? raw.previous : null,
144
+ results: raw.results.map(one => descriptorRevisionRow(one, null, false)) };
145
+ }
146
+ /** Reads one descriptor revision with its full document and startup order. */
147
+ export async function getNativeDescriptor(scope, input) {
148
+ if (!UUID.test(input.agentId) || !UUID.test(input.descriptorId)) {
149
+ throw new CliError('native_descriptor_invalid_id', 'Use the agent and descriptor ids from the descriptor history.', 2);
150
+ }
151
+ const raw = await api(`/api/workspaces/${scope.workspace}/agents/${input.agentId}/native-descriptors/${input.descriptorId}`, {
152
+ actor: scope.owner, workspace: scope.workspace, signal: input.signal, maximumResponseBytes: RESPONSE_LIMIT,
153
+ });
154
+ return descriptorRevisionRow(raw, null, true);
155
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Local source roots (Plan20 W09).
3
+ *
4
+ * A root is a directory on this machine that the user approved for the local
5
+ * plan. Its identity digest binds the session plan to the exact directory
6
+ * (real path plus device and inode) without sending the path to the cloud.
7
+ * Paths inside a root are resolved with realpath and refused if they escape it,
8
+ * including through symbolic links. This is path discipline, not an OS sandbox:
9
+ * approved processes still run with the user's full permissions.
10
+ */
11
+ import { createHash } from 'node:crypto';
12
+ import { realpath, stat } from 'node:fs/promises';
13
+ import { isAbsolute, join, posix, resolve, sep, win32 } from 'node:path';
14
+ import { CliError } from '../http.js';
15
+ import { nativeError } from './contracts.js';
16
+ const ROOT_IDENTITY_PREFIX = 'firedrill.root-identity.v1\0';
17
+ const MAX_PATH = 4096;
18
+ const rootInvalid = () => new CliError('native_root_invalid', 'The selected root is not an existing directory owned by you. No plan was prepared for it.', 2);
19
+ const outside = () => nativeError('root_outside', 'An approved path resolves outside its root, or is absolute or uses "..". Nothing was started from it.', 2);
20
+ /**
21
+ * True when `relative` is a syntactically safe path inside a root: not absolute
22
+ * on any platform, no drive prefix, no ".." segment, no NUL, bounded length.
23
+ * Empty and "." mean the root itself. Symlinks are checked later by `insideRoot`.
24
+ */
25
+ export function isSafeRelativePath(relative) {
26
+ if (typeof relative !== 'string' || relative.length > MAX_PATH || relative.includes('\0'))
27
+ return false;
28
+ if (posix.isAbsolute(relative) || win32.isAbsolute(relative) || /^[A-Za-z]:/.test(relative))
29
+ return false;
30
+ return !relative.split(/[\\/]+/).some(segment => segment === '..');
31
+ }
32
+ function samePath(a, b) {
33
+ return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
34
+ }
35
+ function contains(root, candidate) {
36
+ if (samePath(root, candidate))
37
+ return true;
38
+ const prefix = root.endsWith(sep) ? root : root + sep;
39
+ return process.platform === 'win32'
40
+ ? candidate.toLowerCase().startsWith(prefix.toLowerCase())
41
+ : candidate.startsWith(prefix);
42
+ }
43
+ /**
44
+ * Resolve a root directory: realpath, must be a directory, and on POSIX must be
45
+ * owned by the current user. Identity is sha256 over the v1 prefix, the real
46
+ * path, a NUL separator and `<device>:<inode>`. Windows has no comparable owner
47
+ * check here; ownership is not asserted there.
48
+ */
49
+ export async function resolveRoot(path) {
50
+ if (typeof path !== 'string' || !path || path.length > MAX_PATH || path.includes('\0'))
51
+ throw rootInvalid();
52
+ let real;
53
+ let details;
54
+ try {
55
+ real = await realpath(resolve(path));
56
+ details = await stat(real, { bigint: true });
57
+ }
58
+ catch {
59
+ throw rootInvalid();
60
+ }
61
+ if (!details.isDirectory())
62
+ throw rootInvalid();
63
+ if (typeof process.getuid === 'function' && details.uid !== BigInt(process.getuid()))
64
+ throw rootInvalid();
65
+ const identity = createHash('sha256').update(ROOT_IDENTITY_PREFIX).update(real).update('\0')
66
+ .update(`${details.dev}:${details.ino}`).digest('hex');
67
+ return { path: real, identity_sha256: identity };
68
+ }
69
+ /**
70
+ * Resolve `relative` inside `root` and return its real path. Refuses absolute
71
+ * input, ".." segments and any symbolic link that resolves outside the root
72
+ * (`native_root_outside`). The target must exist (`native_path_unavailable`).
73
+ */
74
+ export async function insideRoot(root, relative) {
75
+ if (!isSafeRelativePath(relative) || typeof root !== 'string' || !isAbsolute(root))
76
+ throw outside();
77
+ let realRoot;
78
+ try {
79
+ realRoot = await realpath(root);
80
+ }
81
+ catch {
82
+ throw rootInvalid();
83
+ }
84
+ let real;
85
+ try {
86
+ real = await realpath(join(realRoot, relative));
87
+ }
88
+ catch {
89
+ throw new CliError('native_path_unavailable', 'An approved path does not exist inside its root. Nothing was started from it.', 2);
90
+ }
91
+ if (!contains(realRoot, real))
92
+ throw outside();
93
+ return real;
94
+ }
95
+ /**
96
+ * Resolve every plan root and confirm it is still the same directory the plan
97
+ * was approved for. Returns alias -> real path. A changed identity is
98
+ * `native_plan_changed`: the plan must be reviewed and approved again.
99
+ */
100
+ export async function resolvePlanRoots(plan) {
101
+ const roots = new Map();
102
+ for (const entry of plan.roots) {
103
+ const resolved = await resolveRoot(entry.path);
104
+ if (resolved.identity_sha256 !== entry.identity_sha256) {
105
+ throw nativeError('plan_changed', 'A plan root no longer matches the directory it was approved for. Review and approve the plan again; nothing was started.', 2);
106
+ }
107
+ roots.set(entry.alias, resolved.path);
108
+ }
109
+ return roots;
110
+ }