firedrill-run 2.2.5 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +176 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +506 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1627 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +96 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,578 @@
1
+ /**
2
+ * Driver interfaces and shared driver primitives (Plan20 W10, spec section 3.3).
3
+ *
4
+ * A driver turns one cloud-originated AssignmentStep into a real interaction with
5
+ * the user's locally running target, through that target's real surface. Only
6
+ * `AssignmentStep.payload` is ever sent to the target: persona goals, judge
7
+ * criteria and other cloud-side context are not part of a step and never reach
8
+ * a driver.
9
+ *
10
+ * Observation channels (each observation is reported exactly once):
11
+ * - Observations tied to a step are returned in `StepResult.observations`.
12
+ * - Observations outside any step (session opened, process exited, late or
13
+ * unsolicited target output, browser-context reset) go to `ctx.observe`.
14
+ * Both carry bounded, redacted content built by `boundedObservation`.
15
+ *
16
+ * Unknown-outcome rule: once any byte of a step may have reached the target and
17
+ * the response did not complete (timeout, cancel, dropped connection, oversized
18
+ * or malformed stream, process exit), a non-idempotent step is reported
19
+ * `unknown` with `native_outcome_unknown` and is never retried by a driver.
20
+ */
21
+ import { constants as fsConstants } from 'node:fs';
22
+ import { open } from 'node:fs/promises';
23
+ import { isIP } from 'node:net';
24
+ import { lookup } from 'node:dns/promises';
25
+ import path from 'node:path';
26
+ import { CliError } from '../../http.js';
27
+ import { NATIVE_ERRORS, SHA256_HEX, SecretValue, nativeError } from '../contracts.js';
28
+ import { redact, redactSecrets } from '../redaction.js';
29
+ import { sha256Hex } from '../canonical.js';
30
+ export const DEFAULT_DRIVER_BOUNDS = Object.freeze({
31
+ maxResponseBytes: 1024 * 1024,
32
+ maxLineBytes: 1024 * 1024,
33
+ maxStreamEvents: 10_000,
34
+ defaultStepMs: 60_000,
35
+ maxStepMs: 10 * 60_000,
36
+ flowSetupMs: 60_000,
37
+ maxObservationBytes: 64 * 1024,
38
+ maxStringChars: 16 * 1024,
39
+ maxItems: 200,
40
+ maxDepth: 8,
41
+ maxStepObservations: 256,
42
+ maxAttachmentBytes: 2 * 1024 * 1024,
43
+ maxScreenshotsPerSession: 64,
44
+ stderrTailBytes: 64 * 1024,
45
+ maxEventRetries: 3,
46
+ maxWrapperRelaunches: 3,
47
+ stopTimeoutMs: 5_000,
48
+ });
49
+ /** Driver-specific stable error codes (supplementing NATIVE_ERRORS). */
50
+ export const DRIVER_ERRORS = {
51
+ not_loopback: 'native_target_not_loopback',
52
+ step_unsupported: 'native_step_unsupported',
53
+ step_timeout: 'native_step_timeout',
54
+ step_cancelled: 'native_step_cancelled',
55
+ intent_missing: 'native_intent_missing',
56
+ session_mismatch: 'native_session_mismatch',
57
+ session_closed: 'native_session_closed',
58
+ session_failed: 'native_session_failed',
59
+ target_status: 'native_target_status',
60
+ target_error: 'native_target_error',
61
+ target_unreachable: 'native_target_unreachable',
62
+ response_malformed: 'native_response_malformed',
63
+ response_oversized: 'native_response_oversized',
64
+ response_pointer_missing: 'native_response_pointer_missing',
65
+ event_invalid: 'native_event_invalid',
66
+ driver_plan_invalid: 'native_driver_plan_invalid',
67
+ readiness_unattested: 'native_readiness_unattested',
68
+ };
69
+ /** The driver cannot attest the entry service's readiness the way root requires; nothing is claimed instead. */
70
+ export function unattested(message) {
71
+ return new CliError(DRIVER_ERRORS.readiness_unattested, message);
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Context helpers.
75
+ export function driverBounds(ctx) {
76
+ const merged = { ...DEFAULT_DRIVER_BOUNDS };
77
+ for (const [key, value] of Object.entries(ctx.bounds ?? {})) {
78
+ if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0)
79
+ merged[key] = value;
80
+ }
81
+ return merged;
82
+ }
83
+ export function clockOf(ctx) {
84
+ return ctx.now ?? Date.now;
85
+ }
86
+ export function planError(message) {
87
+ return new CliError(DRIVER_ERRORS.driver_plan_invalid, message);
88
+ }
89
+ /** Narrow ctx.driver to the expected profile or refuse. */
90
+ export function driverPlanFor(ctx, profile) {
91
+ if (ctx.driver.profile !== profile)
92
+ throw planError(`The approved plan does not select the ${profile} driver, so it was not prepared.`);
93
+ return ctx.driver;
94
+ }
95
+ export function rootPath(ctx, alias) {
96
+ const value = ctx.roots.get(alias);
97
+ if (!value || !path.isAbsolute(value))
98
+ throw nativeError('root_outside', `The approved plan root "${alias}" is not resolved, so no file was read from it.`);
99
+ return value;
100
+ }
101
+ export function serviceHandle(ctx, alias) {
102
+ const handle = ctx.services.get(alias);
103
+ if (!handle)
104
+ throw nativeError('driver_unavailable', `The approved service "${alias}" is not running under this companion, so the driver was not prepared.`);
105
+ return handle;
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Redaction and bounding.
109
+ export function clipText(text, maxChars) {
110
+ if (text.length <= maxChars)
111
+ return text;
112
+ return `${text.slice(0, Math.max(0, maxChars))}[truncated:${text.length - maxChars} chars]`;
113
+ }
114
+ function clipUtf8(text, maxBytes) {
115
+ const bytes = Buffer.from(text, 'utf8');
116
+ if (bytes.byteLength <= maxBytes)
117
+ return text;
118
+ return `${bytes.subarray(0, Math.max(0, maxBytes)).toString('utf8')}[truncated:${bytes.byteLength - maxBytes} bytes]`;
119
+ }
120
+ const DIGEST_VALUE = /^[0-9a-f]{64}$/;
121
+ /**
122
+ * A sha256 digest this client computed itself from bytes it holds (a pinned
123
+ * adapter, the approved masking policy, a delivered event body). Only such a
124
+ * value keeps its hex through `sanitize`: target-controlled data is plain
125
+ * parsed JSON and can never hold one, so a target's own `*_sha256` string is
126
+ * redacted like any other long hex.
127
+ */
128
+ export class OwnDigest {
129
+ hex;
130
+ constructor(hex) { this.hex = hex; }
131
+ toJSON() { return DIGEST_VALUE.test(this.hex) ? this.hex : null; }
132
+ }
133
+ /**
134
+ * Convert any value to JSON-safe, redacted, bounded data. Strings (and object
135
+ * keys) are redacted before clipping so a clipped secret cannot survive.
136
+ * An `OwnDigest` keeps its hex (the long-hex token shape would erase the
137
+ * digest a reviewer compares), but an exact secret value is still removed.
138
+ */
139
+ export function sanitize(value, secrets, bounds, changes) {
140
+ const changed = (value) => { if (changes)
141
+ changes.changed = true; return value; };
142
+ const walk = (current, depth) => {
143
+ if (current === null || typeof current === 'boolean')
144
+ return current;
145
+ if (current === undefined)
146
+ return changed(null);
147
+ if (typeof current === 'number')
148
+ return Number.isFinite(current) ? current : changed(null);
149
+ if (typeof current === 'string') {
150
+ const clean = clipText(redact(current, secrets), bounds.maxStringChars);
151
+ return clean === current ? clean : changed(clean);
152
+ }
153
+ if (current instanceof OwnDigest) {
154
+ if (!DIGEST_VALUE.test(current.hex))
155
+ return changed(null);
156
+ const clean = redactSecrets(current.hex, secrets);
157
+ return clean === current.hex ? clean : changed(clean);
158
+ }
159
+ if (typeof current === 'bigint')
160
+ return changed(clipText(current.toString(), 64));
161
+ if (current instanceof SecretValue)
162
+ return changed(current.toJSON());
163
+ if (current instanceof Uint8Array)
164
+ return changed({ omitted_bytes: current.byteLength });
165
+ if (current instanceof Date)
166
+ return changed(Number.isFinite(current.getTime()) ? current.toISOString() : null);
167
+ if (typeof current !== 'object')
168
+ return changed(null);
169
+ if (depth >= bounds.maxDepth)
170
+ return changed('[truncated:depth]');
171
+ if (Array.isArray(current)) {
172
+ const kept = current.slice(0, bounds.maxItems).map((item) => walk(item, depth + 1));
173
+ if (current.length > bounds.maxItems) {
174
+ changed(null);
175
+ kept.push(`[truncated:${current.length - bounds.maxItems} items]`);
176
+ }
177
+ return kept;
178
+ }
179
+ const output = {};
180
+ const entries = Object.entries(current);
181
+ for (const [key, item] of entries.slice(0, bounds.maxItems)) {
182
+ const cleanKey = clipText(redact(key, secrets), 256);
183
+ if (cleanKey !== key)
184
+ changed(null);
185
+ Object.defineProperty(output, cleanKey, { value: walk(item, depth + 1), enumerable: true, configurable: true });
186
+ }
187
+ if (entries.length > bounds.maxItems) {
188
+ changed(null);
189
+ output['[truncated]'] = `${entries.length - bounds.maxItems} keys`;
190
+ }
191
+ return output;
192
+ };
193
+ return walk(value, 0);
194
+ }
195
+ /** Sanitize, then enforce the serialized byte bound of one observation/output. */
196
+ export function boundedValue(value, secrets, bounds, changes) {
197
+ const clean = sanitize(value, secrets, bounds, changes);
198
+ const json = JSON.stringify(clean) ?? 'null';
199
+ const size = Buffer.byteLength(json, 'utf8');
200
+ if (size <= bounds.maxObservationBytes)
201
+ return clean;
202
+ if (changes)
203
+ changes.changed = true;
204
+ return { truncated: true, original_bytes: size, preview: clipUtf8(json, Math.max(0, bounds.maxObservationBytes - 256)) };
205
+ }
206
+ export function boundedObservation(kind, atMs, content, secrets, bounds, attachment) {
207
+ const bounded = boundedValue(content, secrets, bounds);
208
+ const record = (bounded && typeof bounded === 'object' && !Array.isArray(bounded) ? bounded : { value: bounded });
209
+ const observation = { kind, at_ms: atMs, content: record };
210
+ if (attachment) {
211
+ if (attachment.bytes.byteLength <= bounds.maxAttachmentBytes && /^[a-z]+\/[a-z0-9.+-]+$/.test(attachment.media_type))
212
+ observation.attachment = { media_type: attachment.media_type, bytes: attachment.bytes };
213
+ else
214
+ record.attachment_omitted = { bytes: attachment.bytes.byteLength, reason: 'attachment exceeds the local bound' };
215
+ }
216
+ return observation;
217
+ }
218
+ /** Report an out-of-step observation through ctx.observe (bounded, redacted). */
219
+ export function emit(ctx, kind, content, extraSecrets = []) {
220
+ const bounds = driverBounds(ctx);
221
+ try {
222
+ ctx.observe(boundedObservation(kind, clockOf(ctx)(), content, [...ctx.secrets, ...extraSecrets], bounds));
223
+ }
224
+ catch { /* an observation sink failure never changes target interaction */ }
225
+ }
226
+ /** Report an out-of-step sanitized action trace segment through ctx.observe (its own `state.observed` record). */
227
+ export function emitTrace(ctx, segment) {
228
+ try {
229
+ ctx.observe({ kind: 'state.observed', at_ms: clockOf(ctx)(), content: {}, trace_segment: segment });
230
+ }
231
+ catch { /* an observation sink failure never changes target interaction */ }
232
+ }
233
+ /** Bounded, redacted text tail of a process stream. */
234
+ /** Extra bytes kept beyond the visible tail so a secret straddling the cut is still redacted whole. */
235
+ const TAIL_REDACTION_MARGIN = 8192;
236
+ export class OutputTail {
237
+ chunks = [];
238
+ size = 0;
239
+ dropped = 0;
240
+ limit;
241
+ visible;
242
+ constructor(limit) { this.visible = Math.max(0, limit); this.limit = this.visible + TAIL_REDACTION_MARGIN; }
243
+ push(chunk) {
244
+ this.chunks.push(chunk);
245
+ this.size += chunk.byteLength;
246
+ while (this.size > this.limit && this.chunks.length) {
247
+ const head = this.chunks[0];
248
+ const excess = this.size - this.limit;
249
+ if (head.byteLength <= excess) {
250
+ this.chunks.shift();
251
+ this.size -= head.byteLength;
252
+ this.dropped += head.byteLength;
253
+ }
254
+ else {
255
+ this.chunks[0] = head.subarray(excess);
256
+ this.size -= excess;
257
+ this.dropped += excess;
258
+ }
259
+ }
260
+ }
261
+ text(secrets) {
262
+ // Redact everything kept (tail plus margin), then show only the visible tail.
263
+ const whole = redact(Buffer.concat(this.chunks).toString('utf8'), secrets);
264
+ const body = whole.length > this.visible ? whole.slice(whole.length - this.visible) : whole;
265
+ const hidden = this.dropped + Math.max(0, this.size - this.visible);
266
+ return hidden ? `[earlier output dropped:${hidden} bytes]${body}` : body;
267
+ }
268
+ }
269
+ /** Collects step-scoped observations and builds the StepResult. */
270
+ export class StepRecorder {
271
+ step;
272
+ startedMs;
273
+ now;
274
+ secrets;
275
+ bounds;
276
+ items = [];
277
+ /** Trace segments cut during the step, held until its capture is recorded (see `recordTrace`). */
278
+ traces = [];
279
+ dropped = 0;
280
+ interruption = false;
281
+ constructor(ctx, step, extraSecrets = []) {
282
+ this.step = step;
283
+ this.now = clockOf(ctx);
284
+ this.startedMs = this.now();
285
+ this.secrets = [...ctx.secrets, ...extraSecrets];
286
+ this.bounds = driverBounds(ctx);
287
+ }
288
+ record(kind, content, attachment, capture) {
289
+ if (this.items.length + this.traces.length >= this.bounds.maxStepObservations) {
290
+ this.dropped += 1;
291
+ return;
292
+ }
293
+ const observation = boundedObservation(kind, this.now(), { step_id: this.step.step_id, ...content }, this.secrets, this.bounds, attachment);
294
+ // An approved capture's result travels beside the observation that captured it (never inside its content).
295
+ if (capture && kind === 'action.observed')
296
+ observation.capture = { ...capture, step_id: this.step.step_id };
297
+ this.items.push(observation);
298
+ if (observation.capture)
299
+ this.flushTraces();
300
+ }
301
+ /**
302
+ * A sanitized action trace segment cut during this step, as its own record.
303
+ * It is held and queued after the step's capture (or at the step's end): the
304
+ * capture's interval frames were taken earlier than a segment cut mid-step,
305
+ * and elapsed time never goes back, so recording the segment first would move
306
+ * those frames to the cut time. The segment's own times are unchanged; its
307
+ * record is simply made later. Returns false when the step's observation bound
308
+ * dropped it.
309
+ */
310
+ recordTrace(segment) {
311
+ if (this.items.length + this.traces.length >= this.bounds.maxStepObservations) {
312
+ this.dropped += 1;
313
+ return false;
314
+ }
315
+ this.traces.push(segment);
316
+ return true;
317
+ }
318
+ flushTraces() {
319
+ for (const segment of this.traces.splice(0))
320
+ this.items.push({ kind: 'state.observed', at_ms: this.now(), content: {}, trace_segment: segment });
321
+ }
322
+ /**
323
+ * Record, once, that the step was interrupted, with the approved capture taken
324
+ * before it stopped. A driver calls it ahead of its own cleanup records so a
325
+ * capture's frames keep their times; `interrupted()` then adds nothing more.
326
+ */
327
+ noteInterrupted(cause, written, capture) {
328
+ if (this.interruption)
329
+ return;
330
+ this.interruption = true;
331
+ this.record('action.observed', { type: 'step.interrupted', cause, request_written: written }, undefined, capture);
332
+ }
333
+ /**
334
+ * `delivered`: the input reached the target (a request was written or the target answered).
335
+ * By default a completed step or one with the target's own output did; a refusal or an
336
+ * undelivered input carries no output and did not.
337
+ */
338
+ finish(outcome, output, errorCode, delivered = outcome === 'completed' || (output !== null && output !== undefined)) {
339
+ const changes = { changed: false };
340
+ const retainedOutput = boundedValue(output, this.secrets, this.bounds, changes);
341
+ this.flushTraces();
342
+ if (this.dropped)
343
+ this.items.push(boundedObservation('action.observed', this.now(), { step_id: this.step.step_id, type: 'observations.truncated', dropped: this.dropped }, this.secrets, this.bounds));
344
+ const kind = outcome === 'completed' ? 'driver.completed' : outcome === 'cancelled' ? 'driver.cancelled' : 'driver.failed';
345
+ this.items.push(boundedObservation(kind, this.now(), { step_id: this.step.step_id, outcome, error_code: errorCode }, this.secrets, this.bounds));
346
+ return {
347
+ step_id: this.step.step_id, intent_id: this.step.intent_id, outcome,
348
+ started_ms: this.startedMs, finished_ms: this.now(),
349
+ output: retainedOutput, output_coverage: changes.changed ? 'redacted' : 'complete',
350
+ error_code: errorCode, observations: this.items, delivered,
351
+ };
352
+ }
353
+ /** Interrupted step: applies the unknown-outcome rule. */
354
+ interrupted(cause, written, options = {}) {
355
+ const { outcome, error_code } = interruptedOutcome(this.step, written, cause, options);
356
+ this.noteInterrupted(cause, written);
357
+ return this.finish(outcome, null, error_code, written);
358
+ }
359
+ }
360
+ const CAUSE_CODES = {
361
+ timeout: DRIVER_ERRORS.step_timeout,
362
+ cancelled: DRIVER_ERRORS.step_cancelled,
363
+ dropped: DRIVER_ERRORS.target_unreachable,
364
+ oversized: DRIVER_ERRORS.response_oversized,
365
+ malformed: DRIVER_ERRORS.response_malformed,
366
+ refused: DRIVER_ERRORS.target_unreachable,
367
+ unreachable: DRIVER_ERRORS.target_unreachable,
368
+ exited: NATIVE_ERRORS.process_exited,
369
+ thrown: DRIVER_ERRORS.target_error,
370
+ };
371
+ /**
372
+ * Unknown-outcome rule. Nothing written: cancelled or failed. Written and the
373
+ * response incomplete: non-idempotent (or `unknownAfterWrite`) => unknown, never
374
+ * retried; idempotent => cancelled/failed with the specific cause code.
375
+ */
376
+ export function interruptedOutcome(step, written, cause, options = {}) {
377
+ if (written && (!step.idempotent || options.unknownAfterWrite))
378
+ return { outcome: 'unknown', error_code: NATIVE_ERRORS.unknown_outcome };
379
+ if (cause === 'cancelled')
380
+ return { outcome: 'cancelled', error_code: CAUSE_CODES.cancelled };
381
+ return { outcome: 'failed', error_code: CAUSE_CODES[cause] };
382
+ }
383
+ /** Pre-invocation checks shared by all drivers. Returns an error code or null. */
384
+ export function precheck(step, sessionKey, kinds) {
385
+ if (!step.idempotent && !step.intent_id)
386
+ return DRIVER_ERRORS.intent_missing;
387
+ if (!kinds.includes(step.kind) || step.payload.kind !== step.kind)
388
+ return DRIVER_ERRORS.step_unsupported;
389
+ if (step.payload.kind !== 'event' && step.payload.session_key !== sessionKey)
390
+ return DRIVER_ERRORS.session_mismatch;
391
+ return null;
392
+ }
393
+ /**
394
+ * Step timeout in milliseconds. `deadline_ms` is treated as a relative duration
395
+ * from invocation (pending root contract), capped by `maxStepMs`.
396
+ */
397
+ export function stepTimeoutMs(step, bounds) {
398
+ const value = step.deadline_ms;
399
+ if (Number.isSafeInteger(value) && value > 0)
400
+ return Math.min(value, bounds.maxStepMs);
401
+ return Math.min(bounds.defaultStepMs, bounds.maxStepMs);
402
+ }
403
+ /** One abort signal for a step: step timeout, caller cancel and drill cancel. */
404
+ export function stepAbort(ctx, signal, timeoutMs) {
405
+ const controller = new AbortController();
406
+ const state = { cause: null };
407
+ const fire = (cause) => { if (state.cause)
408
+ return; state.cause = cause; controller.abort(); };
409
+ const timer = setTimeout(() => fire('timeout'), timeoutMs);
410
+ // The runner aborts with a TimeoutError reason when the step budget, lease or
411
+ // elapsed budget runs out; that is a timeout, not a cancellation.
412
+ const onCancel = () => {
413
+ const reasons = [signal.reason, ctx.signal.reason];
414
+ fire(reasons.some((reason) => reason instanceof Error && reason.name === 'TimeoutError') ? 'timeout' : 'cancelled');
415
+ };
416
+ const sources = [signal, ctx.signal];
417
+ for (const source of sources) {
418
+ if (source.aborted)
419
+ onCancel();
420
+ else
421
+ source.addEventListener('abort', onCancel, { once: true });
422
+ }
423
+ return {
424
+ signal: controller.signal,
425
+ cause: () => state.cause,
426
+ dispose() { clearTimeout(timer); for (const source of sources)
427
+ source.removeEventListener('abort', onCancel); },
428
+ };
429
+ }
430
+ /** Resolve when `ms` passes, or reject (with no value) when the signal aborts. */
431
+ export function abortableDelay(ms, signal) {
432
+ return new Promise((resolve, reject) => {
433
+ if (signal.aborted) {
434
+ reject(new Error('aborted'));
435
+ return;
436
+ }
437
+ const onAbort = () => { clearTimeout(timer); reject(new Error('aborted')); };
438
+ const timer = setTimeout(() => { signal.removeEventListener('abort', onAbort); resolve(); }, ms);
439
+ signal.addEventListener('abort', onAbort, { once: true });
440
+ });
441
+ }
442
+ // ---------------------------------------------------------------------------
443
+ // Loopback targets.
444
+ export function isLoopbackHost(hostname) {
445
+ const host = hostname.replace(/^\[|\]$/g, '').toLowerCase();
446
+ if (host === 'localhost')
447
+ return true;
448
+ if (isIP(host) === 4)
449
+ return host.startsWith('127.');
450
+ if (isIP(host) === 6)
451
+ return host === '::1' || host === '0:0:0:0:0:0:0:1';
452
+ return false;
453
+ }
454
+ /** Parse a service URL and require plain loopback http without credentials. */
455
+ export function loopbackBase(raw, alias) {
456
+ let url;
457
+ try {
458
+ url = new URL(raw ?? '');
459
+ }
460
+ catch {
461
+ throw new CliError(DRIVER_ERRORS.not_loopback, `The approved service "${alias}" has no usable local URL, so no request was sent.`);
462
+ }
463
+ if (url.protocol !== 'http:' || !isLoopbackHost(url.hostname) || url.username || url.password || url.hash)
464
+ throw new CliError(DRIVER_ERRORS.not_loopback, `The approved service "${alias}" is not a plain loopback HTTP address, so no request was sent.`);
465
+ return url;
466
+ }
467
+ /** Join an approved path onto a loopback base; the origin must not change. */
468
+ export function loopbackTarget(base, relative, alias) {
469
+ if (!relative.startsWith('/') || relative.startsWith('//') || /[\\\r\n#\0]/.test(relative))
470
+ throw planError(`The approved path for "${alias}" must be an absolute path on the same local service; no request was sent.`);
471
+ const target = new URL(relative, base);
472
+ if (target.origin !== base.origin)
473
+ throw new CliError(DRIVER_ERRORS.not_loopback, `The approved path for "${alias}" leaves the local service, so no request was sent.`);
474
+ return target;
475
+ }
476
+ /**
477
+ * Resolve the connect address for a loopback URL. Names resolve only to loopback
478
+ * addresses (a hostname mapped elsewhere is refused before connecting).
479
+ */
480
+ export async function resolveLoopback(url) {
481
+ const name = url.hostname.replace(/^\[|\]$/g, '');
482
+ const port = url.port ? Number(url.port) : 80;
483
+ let host = name;
484
+ if (!isIP(name)) {
485
+ let addresses = [];
486
+ try {
487
+ addresses = await lookup(name, { all: true });
488
+ }
489
+ catch {
490
+ addresses = [];
491
+ }
492
+ const loopback = addresses.find((entry) => isLoopbackHost(entry.address));
493
+ if (!loopback)
494
+ throw new CliError(DRIVER_ERRORS.not_loopback, 'The local service name does not resolve to a loopback address, so no request was sent.');
495
+ host = loopback.address;
496
+ }
497
+ if (!isLoopbackHost(host))
498
+ throw new CliError(DRIVER_ERRORS.not_loopback, 'The local service address is not loopback, so no request was sent.');
499
+ return { host, port, hostHeader: url.host };
500
+ }
501
+ // ---------------------------------------------------------------------------
502
+ // Headers.
503
+ const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/;
504
+ export function validHeaderName(name) { return HEADER_NAME.test(name); }
505
+ /** Exactly the bytes Node's HTTP client accepts in a header value, so validation fails before any byte is sent. */
506
+ export function validHeaderValue(value) { return typeof value === 'string' && value.length <= 8192 && /^[\t\x20-\x7e\x80-\xff]*$/.test(value); }
507
+ // ---------------------------------------------------------------------------
508
+ // Approved adapter files (resolve paths with roots.ts insideRoot first).
509
+ /**
510
+ * Read a regular file and require its sha256 to equal the approved digest.
511
+ * A mismatch voids approval (`native_plan_changed`); the file is not used.
512
+ */
513
+ export async function readPinned(file, expectedSha256, maxBytes = 8 * 1024 * 1024) {
514
+ if (!SHA256_HEX.test(expectedSha256))
515
+ throw nativeError('plan_changed', 'The approved adapter digest is missing or malformed, so the adapter was not loaded or run.');
516
+ const nofollow = fsConstants.O_NOFOLLOW ?? 0;
517
+ let handle = null;
518
+ try {
519
+ handle = await open(file, fsConstants.O_RDONLY | nofollow);
520
+ const stat = await handle.stat();
521
+ if (!stat.isFile() || stat.size > maxBytes)
522
+ throw new Error('unsupported');
523
+ const bytes = await handle.readFile();
524
+ const sha256 = sha256Hex(bytes);
525
+ if (sha256 !== expectedSha256)
526
+ throw nativeError('plan_changed', 'The approved adapter file changed since approval, so it was not loaded or run.');
527
+ return { bytes, sha256 };
528
+ }
529
+ catch (error) {
530
+ if (error instanceof CliError)
531
+ throw error;
532
+ throw nativeError('plan_changed', 'The approved adapter file could not be read as a regular file, so it was not loaded or run.');
533
+ }
534
+ finally {
535
+ await handle?.close().catch(() => undefined);
536
+ }
537
+ }
538
+ // ---------------------------------------------------------------------------
539
+ // JSON pointer (RFC 6901).
540
+ export function parsePointer(pointer) {
541
+ if (pointer === null)
542
+ return null;
543
+ if (pointer === '')
544
+ return [];
545
+ if (!pointer.startsWith('/') || /~[^01]|~$/.test(pointer))
546
+ throw planError('The approved response pointer is not a valid JSON pointer, so the driver was not prepared.');
547
+ return pointer.slice(1).split('/').map((token) => token.replace(/~1/g, '/').replace(/~0/g, '~'));
548
+ }
549
+ /** Returns undefined when the pointer does not resolve. */
550
+ export function readPointer(document, tokens) {
551
+ let current = document;
552
+ for (const token of tokens) {
553
+ if (Array.isArray(current)) {
554
+ if (!/^(0|[1-9][0-9]*)$/.test(token))
555
+ return undefined;
556
+ const index = Number(token);
557
+ if (index >= current.length)
558
+ return undefined;
559
+ current = current[index];
560
+ }
561
+ else if (current && typeof current === 'object') {
562
+ if (!Object.prototype.hasOwnProperty.call(current, token))
563
+ return undefined;
564
+ current = current[token];
565
+ }
566
+ else
567
+ return undefined;
568
+ }
569
+ return current;
570
+ }
571
+ /** Target-visible step input only (never anything beyond payload). */
572
+ export function stepInput(step) {
573
+ if (step.payload.kind === 'message')
574
+ return { kind: 'message', input: step.payload.text };
575
+ if (step.payload.kind === 'task')
576
+ return { kind: 'task', input: step.payload.input };
577
+ return null;
578
+ }