firedrill-run 2.2.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +178 -22
  2. package/bin/firedrill.mjs +29 -5
  3. package/dist/assembly.json +13 -0
  4. package/dist/cli/runtime/candidate_copy.py +395 -0
  5. package/dist/cli/runtime/source_snapshot.py +391 -0
  6. package/dist/cli/src/administration.js +295 -0
  7. package/dist/cli/src/agent-changes.js +496 -0
  8. package/dist/cli/src/assessment-requests.js +67 -0
  9. package/dist/cli/src/auth.js +465 -0
  10. package/dist/cli/src/billing-commerce-contracts.js +1 -0
  11. package/dist/cli/src/billing-usage.js +227 -0
  12. package/dist/cli/src/billing-wallet-contracts.js +1 -0
  13. package/dist/cli/src/billing.js +170 -0
  14. package/dist/cli/src/bootstrap-process-lock.js +136 -0
  15. package/dist/cli/src/browser-links.js +14 -0
  16. package/dist/cli/src/campaign-controls.js +122 -0
  17. package/dist/cli/src/campaign-diagnoses.js +118 -0
  18. package/dist/cli/src/campaign-reports.js +1215 -0
  19. package/dist/cli/src/campaign-world.js +156 -0
  20. package/dist/cli/src/candidate-copy.js +376 -0
  21. package/dist/cli/src/catalog-contracts.js +1 -0
  22. package/dist/cli/src/ci-access-contracts.js +1 -0
  23. package/dist/cli/src/ci-configuration-contracts.js +1 -0
  24. package/dist/cli/src/ci-coverage.js +64 -0
  25. package/dist/cli/src/ci-delivery-contracts.js +1 -0
  26. package/dist/cli/src/ci-result-contracts.js +1 -0
  27. package/dist/cli/src/ci-run-contracts.js +1 -0
  28. package/dist/cli/src/ci-setup-proposals.js +52 -0
  29. package/dist/cli/src/company-policies.js +229 -0
  30. package/dist/cli/src/completion.js +79 -0
  31. package/dist/cli/src/configuration-intents.js +277 -0
  32. package/dist/cli/src/configuration.js +369 -0
  33. package/dist/cli/src/continuous-simulation.js +277 -0
  34. package/dist/cli/src/coordinated-checkpoints.js +133 -0
  35. package/dist/cli/src/credential-mode.js +139 -0
  36. package/dist/cli/src/credentials.js +331 -0
  37. package/dist/cli/src/credit-format.js +8 -0
  38. package/dist/cli/src/dataset-contracts.js +1 -0
  39. package/dist/cli/src/dataset-export.js +154 -0
  40. package/dist/cli/src/dataset-reads.js +37 -0
  41. package/dist/cli/src/dependency-preparation.js +239 -0
  42. package/dist/cli/src/director-tool-setup.js +104 -0
  43. package/dist/cli/src/driver-review.js +225 -0
  44. package/dist/cli/src/evaluation-authoring.js +315 -0
  45. package/dist/cli/src/evidence-bundles.js +367 -0
  46. package/dist/cli/src/execution-proposals.js +36 -0
  47. package/dist/cli/src/http.js +302 -0
  48. package/dist/cli/src/local-container-boundary.js +38 -0
  49. package/dist/cli/src/local-container-policy.js +201 -0
  50. package/dist/cli/src/local-execution-setup.js +81 -0
  51. package/dist/cli/src/local-source-basis.js +73 -0
  52. package/dist/cli/src/local-source.js +66 -0
  53. package/dist/cli/src/local-value-store.js +91 -0
  54. package/dist/cli/src/local-values.js +163 -0
  55. package/dist/cli/src/login-organizations.js +70 -0
  56. package/dist/cli/src/main.js +1622 -0
  57. package/dist/cli/src/mcp-connection.js +109 -0
  58. package/dist/cli/src/native/bindings.js +137 -0
  59. package/dist/cli/src/native/canonical.js +370 -0
  60. package/dist/cli/src/native/cloud-resources.js +942 -0
  61. package/dist/cli/src/native/companion-channel.js +421 -0
  62. package/dist/cli/src/native/companion-ipc.js +473 -0
  63. package/dist/cli/src/native/contracts.js +218 -0
  64. package/dist/cli/src/native/control-plane.js +1354 -0
  65. package/dist/cli/src/native/credit-approval.js +15 -0
  66. package/dist/cli/src/native/dependency-proxy.js +1006 -0
  67. package/dist/cli/src/native/drill-cleanup.js +115 -0
  68. package/dist/cli/src/native/drill-runner.js +1562 -0
  69. package/dist/cli/src/native/drivers/action-trace.js +210 -0
  70. package/dist/cli/src/native/drivers/function-wrapper.js +152 -0
  71. package/dist/cli/src/native/drivers/http.js +442 -0
  72. package/dist/cli/src/native/drivers/index.js +47 -0
  73. package/dist/cli/src/native/drivers/jsonl-process.js +538 -0
  74. package/dist/cli/src/native/drivers/playwright.js +600 -0
  75. package/dist/cli/src/native/drivers/signed-event.js +244 -0
  76. package/dist/cli/src/native/drivers/types.js +578 -0
  77. package/dist/cli/src/native/evidence-filter.js +255 -0
  78. package/dist/cli/src/native/isolated-access.js +241 -0
  79. package/dist/cli/src/native/library-sharing.js +220 -0
  80. package/dist/cli/src/native/local-run.js +667 -0
  81. package/dist/cli/src/native/local-secrets.js +19 -0
  82. package/dist/cli/src/native/mcp-config.js +123 -0
  83. package/dist/cli/src/native/metadata-requests.js +120 -0
  84. package/dist/cli/src/native/observations.js +1159 -0
  85. package/dist/cli/src/native/owned-groups.js +46 -0
  86. package/dist/cli/src/native/plan.js +594 -0
  87. package/dist/cli/src/native/png.js +159 -0
  88. package/dist/cli/src/native/ports.js +43 -0
  89. package/dist/cli/src/native/readiness.js +139 -0
  90. package/dist/cli/src/native/reassessments.js +206 -0
  91. package/dist/cli/src/native/redaction.js +235 -0
  92. package/dist/cli/src/native/registration.js +155 -0
  93. package/dist/cli/src/native/roots.js +110 -0
  94. package/dist/cli/src/native/run-cleanup.js +199 -0
  95. package/dist/cli/src/native/scratch.js +117 -0
  96. package/dist/cli/src/native/screenshots.js +60 -0
  97. package/dist/cli/src/native/session.js +556 -0
  98. package/dist/cli/src/native/standalone-proxy.js +580 -0
  99. package/dist/cli/src/native/standalone-renewals.js +183 -0
  100. package/dist/cli/src/native/standalone-state.js +193 -0
  101. package/dist/cli/src/native/standalone-tools.js +187 -0
  102. package/dist/cli/src/native/supervisor.js +831 -0
  103. package/dist/cli/src/native/target-auth.js +161 -0
  104. package/dist/cli/src/native/target-state.js +330 -0
  105. package/dist/cli/src/native/tool-reuse.js +364 -0
  106. package/dist/cli/src/native/tool-setups.js +336 -0
  107. package/dist/cli/src/native/world-reuse.js +288 -0
  108. package/dist/cli/src/onboarding.js +23 -0
  109. package/dist/cli/src/priority-requests.js +70 -0
  110. package/dist/cli/src/priority.js +223 -0
  111. package/dist/cli/src/prompts.js +184 -0
  112. package/dist/cli/src/real-operations.js +221 -0
  113. package/dist/cli/src/recipe-intents.js +58 -0
  114. package/dist/cli/src/recorded-context-intents.js +59 -0
  115. package/dist/cli/src/recorded-context.js +248 -0
  116. package/dist/cli/src/recordings.js +69 -0
  117. package/dist/cli/src/registration-guide.js +39 -0
  118. package/dist/cli/src/report-exports.js +213 -0
  119. package/dist/cli/src/request-input.js +47 -0
  120. package/dist/cli/src/resource-lifecycle.js +141 -0
  121. package/dist/cli/src/revision-requests.js +55 -0
  122. package/dist/cli/src/runner-abandon.js +53 -0
  123. package/dist/cli/src/runner-campaign-controls.js +262 -0
  124. package/dist/cli/src/runner-claim-reads.js +36 -0
  125. package/dist/cli/src/runner-completion.js +65 -0
  126. package/dist/cli/src/runner-configuration.js +546 -0
  127. package/dist/cli/src/runner-container.js +794 -0
  128. package/dist/cli/src/runner-evidence-erasure.js +351 -0
  129. package/dist/cli/src/runner-evidence-privacy.js +37 -0
  130. package/dist/cli/src/runner-evidence-retention.js +161 -0
  131. package/dist/cli/src/runner-evidence.js +326 -0
  132. package/dist/cli/src/runner-execute.js +92 -0
  133. package/dist/cli/src/runner-execution-deadline.js +25 -0
  134. package/dist/cli/src/runner-grants.js +307 -0
  135. package/dist/cli/src/runner-identity.js +615 -0
  136. package/dist/cli/src/runner-inputs.js +308 -0
  137. package/dist/cli/src/runner-jobs.js +49 -0
  138. package/dist/cli/src/runner-model-transport.js +392 -0
  139. package/dist/cli/src/runner-ownership.js +332 -0
  140. package/dist/cli/src/runner-process-lock.js +175 -0
  141. package/dist/cli/src/runner-runtime.js +396 -0
  142. package/dist/cli/src/runner-session-channel.js +377 -0
  143. package/dist/cli/src/runner-trial-completion.js +34 -0
  144. package/dist/cli/src/runner-trial-journal.js +358 -0
  145. package/dist/cli/src/runner-trial-live.js +393 -0
  146. package/dist/cli/src/runner-trial-protocol.js +289 -0
  147. package/dist/cli/src/runner-trial-sync.js +110 -0
  148. package/dist/cli/src/runner-trial-transfer.js +107 -0
  149. package/dist/cli/src/runner-voice.js +40 -0
  150. package/dist/cli/src/runner-world-claim.js +21 -0
  151. package/dist/cli/src/runner-world-collector.js +159 -0
  152. package/dist/cli/src/runner-world-command-evidence.js +487 -0
  153. package/dist/cli/src/runner-world-commands.js +268 -0
  154. package/dist/cli/src/runner-world-completion.js +76 -0
  155. package/dist/cli/src/runner-world-erasure.js +95 -0
  156. package/dist/cli/src/runner-world-evidence-sync.js +91 -0
  157. package/dist/cli/src/runner-world-journal.js +510 -0
  158. package/dist/cli/src/runner-world-native-filters.js +134 -0
  159. package/dist/cli/src/runner-world-observations.js +50 -0
  160. package/dist/cli/src/runner-world-session-protocol.js +134 -0
  161. package/dist/cli/src/saved-records.js +40 -0
  162. package/dist/cli/src/scenario-authoring.js +212 -0
  163. package/dist/cli/src/scenario-discovery.js +216 -0
  164. package/dist/cli/src/scenario-paths.js +68 -0
  165. package/dist/cli/src/scenario-requests.js +4 -0
  166. package/dist/cli/src/service-receipts.js +105 -0
  167. package/dist/cli/src/simple/authoring.js +270 -0
  168. package/dist/cli/src/simple/credit-approval.js +16 -0
  169. package/dist/cli/src/simple/help.js +98 -0
  170. package/dist/cli/src/simple/init.js +61 -0
  171. package/dist/cli/src/simple/library-sharing.js +414 -0
  172. package/dist/cli/src/simple/metadata-requests.js +46 -0
  173. package/dist/cli/src/simple/native-report.js +841 -0
  174. package/dist/cli/src/simple/native-tools.js +780 -0
  175. package/dist/cli/src/simple/plan-apply.js +506 -0
  176. package/dist/cli/src/simple/reassessment.js +323 -0
  177. package/dist/cli/src/simple/register.js +123 -0
  178. package/dist/cli/src/simple/run.js +122 -0
  179. package/dist/cli/src/simple/status.js +49 -0
  180. package/dist/cli/src/simple/tool-renewal.js +308 -0
  181. package/dist/cli/src/simple/tool-reuse.js +526 -0
  182. package/dist/cli/src/simple/tool-start-catalog.js +100 -0
  183. package/dist/cli/src/simple/tool-state.js +426 -0
  184. package/dist/cli/src/simple/tools-author.js +222 -0
  185. package/dist/cli/src/simple/watch.js +28 -0
  186. package/dist/cli/src/simple/world-reuse.js +586 -0
  187. package/dist/cli/src/simulation-intents.js +406 -0
  188. package/dist/cli/src/simulation-run.js +1058 -0
  189. package/dist/cli/src/source-batch.js +322 -0
  190. package/dist/cli/src/source-connection.js +451 -0
  191. package/dist/cli/src/source-runtime.js +346 -0
  192. package/dist/cli/src/source-selection-intents.js +75 -0
  193. package/dist/cli/src/source-sets.js +214 -0
  194. package/dist/cli/src/target-models.js +109 -0
  195. package/dist/cli/src/tool-catalog.js +224 -0
  196. package/dist/cli/src/tool-command-contracts.js +1 -0
  197. package/dist/cli/src/tool-command-export.js +245 -0
  198. package/dist/cli/src/tool-connection-contracts.js +1 -0
  199. package/dist/cli/src/tool-connection-reads.js +23 -0
  200. package/dist/cli/src/tool-contributions.js +58 -0
  201. package/dist/cli/src/tool-data.js +116 -0
  202. package/dist/cli/src/tool-datasets.js +176 -0
  203. package/dist/cli/src/tool-group-contracts.js +1 -0
  204. package/dist/cli/src/tool-group-reads.js +26 -0
  205. package/dist/cli/src/tool-group-state.js +147 -0
  206. package/dist/cli/src/tool-groups.js +311 -0
  207. package/dist/cli/src/tool-instances.js +186 -0
  208. package/dist/cli/src/tool-lifecycle-contracts.js +1 -0
  209. package/dist/cli/src/tool-lifecycle-reads.js +30 -0
  210. package/dist/cli/src/tool-lifecycle.js +323 -0
  211. package/dist/cli/src/tool-package-contracts.js +1 -0
  212. package/dist/cli/src/tool-package-reads.js +41 -0
  213. package/dist/cli/src/tool-records.js +53 -0
  214. package/dist/cli/src/tool-runtime.js +96 -0
  215. package/dist/cli/src/tool-state-contracts.js +1 -0
  216. package/dist/cli/src/tool-state-reads.js +31 -0
  217. package/dist/cli/src/tools.js +306 -0
  218. package/dist/cli/src/trial-actions.js +93 -0
  219. package/dist/cli/src/trial-checkpoints.js +133 -0
  220. package/dist/cli/src/trial-checks.js +70 -0
  221. package/dist/cli/src/trial-state.js +197 -0
  222. package/dist/cli/src/vault.js +266 -0
  223. package/dist/cli/src/workspace-registration.js +383 -0
  224. package/dist/cli/src/workspace-team-contracts.js +1 -0
  225. package/dist/cli/src/world-access.js +231 -0
  226. package/dist/cli/src/world-agent.js +4 -0
  227. package/dist/cli/src/world-checkpoint-scopes.js +15 -0
  228. package/dist/cli/src/world-contracts.js +6 -0
  229. package/dist/cli/src/world-creation.js +201 -0
  230. package/dist/cli/src/world-datasets.js +207 -0
  231. package/dist/cli/src/world-inspection.js +257 -0
  232. package/dist/cli/src/world-recipes.js +103 -0
  233. package/dist/cli/src/world-requests.js +241 -0
  234. package/dist/cli/src/world-session-checkpoints.js +196 -0
  235. package/dist/cli/src/world-session-lifecycle.js +207 -0
  236. package/dist/cli/src/world-session-requests.js +215 -0
  237. package/dist/cli/src/world-session-state.js +145 -0
  238. package/dist/cli/src/world-session-voice.js +66 -0
  239. package/dist/cli/src/world-sessions.js +288 -0
  240. package/dist/cli/src/world-stream.js +99 -0
  241. package/dist/cli/src/world-time-report.js +58 -0
  242. package/dist/contracts/agent-change-contracts.js +75 -0
  243. package/dist/contracts/agent-execution-contracts.js +38 -0
  244. package/dist/contracts/application-state-contracts.js +26 -0
  245. package/dist/contracts/assessment-aggregate-contracts.js +15 -0
  246. package/dist/contracts/assessment-contracts.js +74 -0
  247. package/dist/contracts/billing-commerce-contracts.js +107 -0
  248. package/dist/contracts/billing-wallet-contracts.js +43 -0
  249. package/dist/contracts/campaign-lifecycle.js +1 -0
  250. package/dist/contracts/campaign-world-contracts.js +80 -0
  251. package/dist/contracts/catalog-contracts.js +73 -0
  252. package/dist/contracts/ci-access-contracts.js +68 -0
  253. package/dist/contracts/ci-configuration-contracts.js +88 -0
  254. package/dist/contracts/ci-coverage-contracts.js +65 -0
  255. package/dist/contracts/ci-coverage-labels.js +25 -0
  256. package/dist/contracts/ci-delivery-contracts.js +61 -0
  257. package/dist/contracts/ci-result-contracts.js +103 -0
  258. package/dist/contracts/ci-run-contracts.js +51 -0
  259. package/dist/contracts/ci-setup-contracts.js +88 -0
  260. package/dist/contracts/clip-contracts.js +32 -0
  261. package/dist/contracts/comparison-contracts.js +53 -0
  262. package/dist/contracts/composite-contracts.js +34 -0
  263. package/dist/contracts/context-contracts.js +76 -0
  264. package/dist/contracts/continuation-report-contracts.js +1 -0
  265. package/dist/contracts/continuity-requirements.js +8 -0
  266. package/dist/contracts/coordinated-checkpoint-contracts.js +34 -0
  267. package/dist/contracts/dataset-contracts.js +88 -0
  268. package/dist/contracts/dependency-decision-contracts.js +36 -0
  269. package/dist/contracts/dependency-protocol-contracts.js +43 -0
  270. package/dist/contracts/director-execution.js +51 -0
  271. package/dist/contracts/driver-review-contracts.js +99 -0
  272. package/dist/contracts/evidence-contracts.js +65 -0
  273. package/dist/contracts/evidence-coverage-contracts.js +10 -0
  274. package/dist/contracts/export-contracts.js +44 -0
  275. package/dist/contracts/github-directory-contracts.js +96 -0
  276. package/dist/contracts/insight-contracts.js +11 -0
  277. package/dist/contracts/inspection-contracts.js +1 -0
  278. package/dist/contracts/local-claim-contracts.js +39 -0
  279. package/dist/contracts/local-configuration-contracts.js +7 -0
  280. package/dist/contracts/local-evidence-contracts.js +32 -0
  281. package/dist/contracts/local-grant-contracts.js +83 -0
  282. package/dist/contracts/local-input-contracts.js +19 -0
  283. package/dist/contracts/local-runner-contracts.js +42 -0
  284. package/dist/contracts/local-trial-live-contracts.js +16 -0
  285. package/dist/contracts/local-value-source-contracts.js +92 -0
  286. package/dist/contracts/local-world-native-contracts.js +8 -0
  287. package/dist/contracts/mcp-contracts.js +42 -0
  288. package/dist/contracts/media-contracts.js +28 -0
  289. package/dist/contracts/native-check-contracts.js +31 -0
  290. package/dist/contracts/native-identity-contracts.js +52 -0
  291. package/dist/contracts/onboarding-contracts.js +32 -0
  292. package/dist/contracts/performance-contracts.js +85 -0
  293. package/dist/contracts/planning-contracts.js +81 -0
  294. package/dist/contracts/real-operation-contracts.js +30 -0
  295. package/dist/contracts/recommendation-contracts.js +54 -0
  296. package/dist/contracts/report-contracts.js +1 -0
  297. package/dist/contracts/resource-lifecycle-contracts.js +57 -0
  298. package/dist/contracts/revision-contracts.js +12 -0
  299. package/dist/contracts/revision-request-contracts.js +39 -0
  300. package/dist/contracts/saved-record-contracts.js +76 -0
  301. package/dist/contracts/scenario-draft-comparison.js +91 -0
  302. package/dist/contracts/scenario-world-contracts.js +14 -0
  303. package/dist/contracts/service-contracts.js +1 -0
  304. package/dist/contracts/session-command-contracts.js +56 -0
  305. package/dist/contracts/session-contracts.js +65 -0
  306. package/dist/contracts/session-state-contracts.js +67 -0
  307. package/dist/contracts/session-voice-contracts.js +45 -0
  308. package/dist/contracts/source-set-contracts.js +26 -0
  309. package/dist/contracts/source-upload-contracts.js +22 -0
  310. package/dist/contracts/tool-command-contracts.js +57 -0
  311. package/dist/contracts/tool-connection-contracts.js +87 -0
  312. package/dist/contracts/tool-contribution-contracts.js +35 -0
  313. package/dist/contracts/tool-group-contracts.js +53 -0
  314. package/dist/contracts/tool-group-state-contracts.js +63 -0
  315. package/dist/contracts/tool-lifecycle-contracts.js +94 -0
  316. package/dist/contracts/tool-package-contracts.js +91 -0
  317. package/dist/contracts/tool-protocol-contracts.js +71 -0
  318. package/dist/contracts/tool-runtime-billing-contracts.js +28 -0
  319. package/dist/contracts/tool-setup-proposal.js +59 -0
  320. package/dist/contracts/tool-state-contracts.js +79 -0
  321. package/dist/contracts/trial-state-contracts.js +30 -0
  322. package/dist/contracts/typescript/agent-change.js +1 -0
  323. package/dist/contracts/typescript/assessment-aggregate.js +1 -0
  324. package/dist/contracts/typescript/assessment.js +1 -0
  325. package/dist/contracts/typescript/billing-commerce.js +1 -0
  326. package/dist/contracts/typescript/campaigns.js +1 -0
  327. package/dist/contracts/typescript/catalog.js +1 -0
  328. package/dist/contracts/typescript/ci-access.js +1 -0
  329. package/dist/contracts/typescript/ci-configuration.js +1 -0
  330. package/dist/contracts/typescript/ci-delivery.js +1 -0
  331. package/dist/contracts/typescript/ci-result.js +1 -0
  332. package/dist/contracts/typescript/ci-runs.js +1 -0
  333. package/dist/contracts/typescript/clips.js +1 -0
  334. package/dist/contracts/typescript/comparison.js +1 -0
  335. package/dist/contracts/typescript/composite.js +1 -0
  336. package/dist/contracts/typescript/connections.js +1 -0
  337. package/dist/contracts/typescript/context.js +1 -0
  338. package/dist/contracts/typescript/datasets.js +1 -0
  339. package/dist/contracts/typescript/evidence-coverage.js +1 -0
  340. package/dist/contracts/typescript/export.js +1 -0
  341. package/dist/contracts/typescript/github-directory.js +1 -0
  342. package/dist/contracts/typescript/insight.js +1 -0
  343. package/dist/contracts/typescript/inspection.js +1 -0
  344. package/dist/contracts/typescript/intake.js +1 -0
  345. package/dist/contracts/typescript/local-claim.js +1 -0
  346. package/dist/contracts/typescript/local-configuration.js +1 -0
  347. package/dist/contracts/typescript/local-evidence.js +1 -0
  348. package/dist/contracts/typescript/local-grant.js +1 -0
  349. package/dist/contracts/typescript/local-input.js +1 -0
  350. package/dist/contracts/typescript/local-runner.js +1 -0
  351. package/dist/contracts/typescript/local-trial-live.js +1 -0
  352. package/dist/contracts/typescript/local-value-source.js +1 -0
  353. package/dist/contracts/typescript/local-world-native.js +1 -0
  354. package/dist/contracts/typescript/mcp.js +1 -0
  355. package/dist/contracts/typescript/media.js +1 -0
  356. package/dist/contracts/typescript/native-check.js +1 -0
  357. package/dist/contracts/typescript/native-identity.js +1 -0
  358. package/dist/contracts/typescript/onboarding.js +1 -0
  359. package/dist/contracts/typescript/performance.js +1 -0
  360. package/dist/contracts/typescript/planning.js +1 -0
  361. package/dist/contracts/typescript/preparation.js +1 -0
  362. package/dist/contracts/typescript/real-operation.js +1 -0
  363. package/dist/contracts/typescript/recommendation.js +1 -0
  364. package/dist/contracts/typescript/reports.js +1 -0
  365. package/dist/contracts/typescript/resource-lifecycle.js +1 -0
  366. package/dist/contracts/typescript/saved-record.js +1 -0
  367. package/dist/contracts/typescript/scenarios.js +1 -0
  368. package/dist/contracts/typescript/services.js +1 -0
  369. package/dist/contracts/typescript/session-command.js +1 -0
  370. package/dist/contracts/typescript/session-state.js +1 -0
  371. package/dist/contracts/typescript/session-voice.js +1 -0
  372. package/dist/contracts/typescript/session.js +1 -0
  373. package/dist/contracts/typescript/tool-command.js +1 -0
  374. package/dist/contracts/typescript/tool-connection.js +1 -0
  375. package/dist/contracts/typescript/tool-contribution.js +1 -0
  376. package/dist/contracts/typescript/tool-groups.js +1 -0
  377. package/dist/contracts/typescript/tool-lifecycle.js +1 -0
  378. package/dist/contracts/typescript/tool-package.js +1 -0
  379. package/dist/contracts/typescript/tool-state.js +1 -0
  380. package/dist/contracts/typescript/trial-state.js +1 -0
  381. package/dist/contracts/typescript/vault.js +1 -0
  382. package/dist/contracts/typescript/workspace-team.js +1 -0
  383. package/dist/contracts/typescript/world-stream.js +1 -0
  384. package/dist/contracts/workspace-team-contracts.js +55 -0
  385. package/dist/contracts/world-command.js +65 -0
  386. package/dist/contracts/world-lifetime-contracts.js +74 -0
  387. package/dist/contracts/world-recipe-contracts.js +27 -0
  388. package/dist/contracts/world-stream-contracts.js +27 -0
  389. package/dist/package.json +1 -0
  390. package/package.json +16 -16
  391. package/LICENSE.md +0 -23
  392. package/PACKAGING.json +0 -50
@@ -0,0 +1,255 @@
1
+ /**
2
+ * client-filtered-v1 evidence profile (Plan20 W09; root native assignment artifacts).
3
+ *
4
+ * Root inspects every committed JSON evidence record and refuses the whole
5
+ * record (`native_evidence_privacy_refused`) when it finds, anywhere in it:
6
+ * - depth above 8, more than 4096 values, a dict or list above 100 entries,
7
+ * a string above 16000 characters, a key above 128 characters;
8
+ * - a key that looks private (password/secret/token/authorization/cookie/
9
+ * api_key/private_key/credential as a `_`-separated word), or one of
10
+ * thinking/reasoning/system_prompt/provider_configuration;
11
+ * - a string (or key) that root's `public_text` would change: known token
12
+ * shapes, PEM private keys, or URLs with user info, a query, a fragment, an
13
+ * uppercase host or an unusual port; or an `export NAME_TOKEN=value`-style
14
+ * line whose name looks private.
15
+ *
16
+ * This module rewrites a value so that root's inspection finds none of those,
17
+ * removing (never guessing at) what it cannot keep, and counts every change.
18
+ * It is a client filter, not a guarantee: redaction of the plan's declared
19
+ * secrets still happens first, in the drivers. Regular expressions mirror root's
20
+ * Python patterns, including Python's Unicode whitespace, digits and case folding.
21
+ */
22
+ /** Characters Python's `str.isspace()` (and so `re` `\s` on str) treats as whitespace. */
23
+ const WS = '\\t\\n\\v\\f\\r \\x1c-\\x1f\\x85\\xa0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000';
24
+ /** Root `privacy.PRIVATE_KEYS` (re.I with Unicode case folding). */
25
+ const PRIVATE_KEYS = /(?:^|_)(?:password|passwd|secret|token|authorization|cookie|api_key|private_key|credential)(?:$|_)/iu;
26
+ const FORBIDDEN_KEYS = new Set(['thinking', 'reasoning', 'system_prompt', 'provider_configuration']);
27
+ /** Root `privacy.KNOWN_VALUES` (Python `\d` matches any Unicode decimal digit). */
28
+ const KNOWN_VALUES = /(?<![A-Za-z0-9_])(?:fd_tool_[a-f0-9-]{36}\.[A-Za-z0-9_-]{64}|(?:sk-ant-api\p{Nd}+-|sk-proj-|sk-svcacct-|gh[pousr]_|github_pat_)[A-Za-z0-9_-]{16,}|xox[baprs]-[A-Za-z0-9-]{16,}|AKIA[A-Z0-9]{16})(?![A-Za-z0-9_-])/gu;
29
+ /** Root `privacy.PRIVATE_PEM`. */
30
+ const PRIVATE_PEM = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/g;
31
+ /** Root `privacy.URL`. */
32
+ const URL_PATTERN = new RegExp(`https?://[^${WS}<>"']+`, 'gu');
33
+ /** Root `privacy.ASSIGNMENT` (Python `^` without MULTILINE is the string start). */
34
+ const ASSIGNMENT = new RegExp(`(?:^|\\n)[${WS}]*(?:export[${WS}]+)?([A-Za-z_][A-Za-z0-9_]*)[${WS}]*=[${WS}]*([^${WS}][^\\n]*)`, 'gu');
35
+ export const EVIDENCE_LIMITS = {
36
+ maxDepth: 8,
37
+ maxNodes: 4096,
38
+ maxEntries: 100,
39
+ maxStringChars: 16_000,
40
+ maxKeyChars: 128,
41
+ };
42
+ /**
43
+ * Default value budget the walk enforces. The headroom covers the markers a
44
+ * container appends after its loop (MARKER_HEADROOM) and a small envelope
45
+ * around the filtered content. A document with more values outside the content
46
+ * (an attachment reference) passes `outsideNodes` to shrink the budget.
47
+ */
48
+ const NODE_BUDGET = EVIDENCE_LIMITS.maxNodes - 32;
49
+ /** Markers added once the budget is spent: at most one per open container (content at depth >= 1, so at most 7). */
50
+ const MARKER_HEADROOM = 8;
51
+ const REDACTED = '[redacted]';
52
+ const PRIVATE_LINK = '[private link]';
53
+ const NESTED = '[nested content omitted]';
54
+ /**
55
+ * Reduce one URL match to `scheme://host[:port]/path`, which root's URL
56
+ * cleaning (urlsplit/urlunsplit with no query or fragment) leaves unchanged.
57
+ * Anything root would parse differently or reject becomes `[private link]`.
58
+ */
59
+ function reduceUrl(url) {
60
+ const separator = url.indexOf('://');
61
+ const scheme = url.slice(0, separator);
62
+ const rest = url.slice(separator + 3);
63
+ const end = rest.search(/[/?#]/);
64
+ const netloc = end === -1 ? rest : rest.slice(0, end);
65
+ const tail = end === -1 ? '' : rest.slice(end);
66
+ const pathEnd = tail.search(/[?#]/);
67
+ const path = pathEnd === -1 ? tail : tail.slice(0, pathEnd);
68
+ if (netloc.includes('[') || netloc.includes(']'))
69
+ return PRIVATE_LINK;
70
+ const hostinfo = netloc.slice(netloc.lastIndexOf('@') + 1);
71
+ const colon = hostinfo.indexOf(':');
72
+ const host = (colon === -1 ? hostinfo : hostinfo.slice(0, colon)).toLowerCase();
73
+ if (!host || !/^[\x21-\x7e]+$/.test(host))
74
+ return PRIVATE_LINK;
75
+ let port = '';
76
+ if (colon !== -1) {
77
+ const raw = hostinfo.slice(colon + 1);
78
+ if (!/^[0-9]{1,5}$/.test(raw) || Number(raw) > 65_535)
79
+ return PRIVATE_LINK;
80
+ // Root drops port 0 and normalises leading zeros.
81
+ if (Number(raw) !== 0)
82
+ port = `:${Number(raw)}`;
83
+ }
84
+ return `${scheme}://${host}${port}${path}`;
85
+ }
86
+ /**
87
+ * Root's `PRIVATE_KEYS.search(name)` under Python semantics: `$` also matches
88
+ * before one final newline, and `re.I` treats dotless ı and dotted İ as `i`
89
+ * (JS Unicode case folding does not).
90
+ */
91
+ function privateName(name) {
92
+ const folded = name.replace(/[İı]/g, 'i');
93
+ return PRIVATE_KEYS.test(folded) || (folded.endsWith('\n') && PRIVATE_KEYS.test(folded.slice(0, -1)));
94
+ }
95
+ /** One pass: redact known values and PEM keys, reduce links, redact private-looking assignment lines. */
96
+ function cleanOnce(text) {
97
+ let value = text.includes('\u0000') ? text.replaceAll('\u0000', '') : text;
98
+ value = value.replace(PRIVATE_PEM, REDACTED).replace(KNOWN_VALUES, REDACTED).replace(URL_PATTERN, reduceUrl);
99
+ return value.replace(ASSIGNMENT, (whole, name) => privateName(name) ? `${whole.startsWith('\n') ? '\n' : ''}${REDACTED}` : whole);
100
+ }
101
+ const CLEAN_PASSES = 4;
102
+ const OMITTED = '[content omitted]';
103
+ /**
104
+ * Clean until nothing changes: one rewrite can create a new match (a lowercased
105
+ * host forming a token, a redaction completing a PEM block). Text that does not
106
+ * settle within a few passes is omitted rather than sent for root to refuse.
107
+ */
108
+ function settle(text) {
109
+ let value = text;
110
+ for (let pass = 0; pass < CLEAN_PASSES; pass++) {
111
+ const next = cleanOnce(value);
112
+ if (next === value)
113
+ return value;
114
+ value = next;
115
+ }
116
+ return OMITTED;
117
+ }
118
+ /** Make `text` a fixed point of root's `public_text`, free of private-looking assignments and within `maxChars`. */
119
+ function cleanText(text, stats, maxChars) {
120
+ let value = settle(text);
121
+ if (value === OMITTED && value !== text)
122
+ stats.truncated = (stats.truncated ?? 0) + 1;
123
+ if (value.length > maxChars) {
124
+ stats.truncated = (stats.truncated ?? 0) + 1;
125
+ // UTF-16 length bounds the code-point count root measures; never split a
126
+ // surrogate pair. The marker is space-separated so it can never join a link,
127
+ // and the result is cleaned again in case the cut exposed a new match.
128
+ let cut = Math.max(0, maxChars - 64);
129
+ if (cut > 0 && /[\uD800-\uDBFF]/.test(value.charAt(cut - 1)))
130
+ cut -= 1;
131
+ value = settle(`${value.slice(0, cut)} [truncated]`);
132
+ if (value.length > maxChars)
133
+ value = OMITTED;
134
+ }
135
+ if (value !== text)
136
+ stats.rewritten++;
137
+ return value;
138
+ }
139
+ /** True when root keeps this key as-is; otherwise the entry is removed. */
140
+ function acceptableKey(key) {
141
+ if (key.length > EVIDENCE_LIMITS.maxKeyChars || privateName(key) || FORBIDDEN_KEYS.has(key))
142
+ return false;
143
+ return cleanOnce(key) === key;
144
+ }
145
+ /**
146
+ * Filter `value` (placed at `depth` inside the evidence document; the document
147
+ * root is depth 0) so root's inspection accepts it. `budget.nodes` is shared
148
+ * across one document and counts every value root will visit, up to
149
+ * `budget.limit`; `budget.maxString` caps each string (root's limit, or less
150
+ * when a record must shrink to fit).
151
+ */
152
+ export function filterEvidence(value, depth, budget, stats) {
153
+ budget.nodes += 1;
154
+ if (value === null || typeof value === 'boolean')
155
+ return value;
156
+ if (typeof value === 'number') {
157
+ if (Number.isFinite(value))
158
+ return value;
159
+ stats.truncated = (stats.truncated ?? 0) + 1;
160
+ stats.removed++;
161
+ return null;
162
+ }
163
+ if (typeof value === 'string')
164
+ return cleanText(value, stats, budget.maxString);
165
+ if (typeof value !== 'object') {
166
+ stats.truncated = (stats.truncated ?? 0) + 1;
167
+ stats.removed++;
168
+ return null;
169
+ }
170
+ const entries = Array.isArray(value) ? value.length : Object.keys(value).length;
171
+ // Children of a container at the maximum depth would exceed it, and so would
172
+ // any child once the document's value budget is spent.
173
+ if (entries > 0 && (depth >= EVIDENCE_LIMITS.maxDepth || budget.nodes >= budget.limit)) {
174
+ stats.truncated = (stats.truncated ?? 0) + 1;
175
+ stats.removed++;
176
+ return NESTED;
177
+ }
178
+ if (Array.isArray(value)) {
179
+ const out = [];
180
+ for (let index = 0; index < value.length; index++) {
181
+ const lastSlot = out.length === EVIDENCE_LIMITS.maxEntries - 1 && value.length - index > 1;
182
+ if (lastSlot || budget.nodes >= budget.limit) {
183
+ stats.truncated = (stats.truncated ?? 0) + 1;
184
+ budget.nodes += 1;
185
+ stats.removed++;
186
+ out.push(`[${value.length - index} more items omitted]`);
187
+ break;
188
+ }
189
+ out.push(filterEvidence(value[index], depth + 1, budget, stats));
190
+ }
191
+ return out;
192
+ }
193
+ const source = value;
194
+ const out = {};
195
+ const keys = Object.keys(source);
196
+ let kept = 0, dropped = 0;
197
+ for (let index = 0; index < keys.length; index++) {
198
+ const key = keys[index];
199
+ if (!acceptableKey(key) || key === 'omitted_entries') {
200
+ if (key.length > EVIDENCE_LIMITS.maxKeyChars || key === 'omitted_entries')
201
+ stats.truncated = (stats.truncated ?? 0) + 1;
202
+ dropped++;
203
+ continue;
204
+ }
205
+ if ((kept === EVIDENCE_LIMITS.maxEntries - 1 && keys.length - index > 1) || budget.nodes >= budget.limit) {
206
+ stats.truncated = (stats.truncated ?? 0) + 1;
207
+ dropped += keys.length - index;
208
+ break;
209
+ }
210
+ // defineProperty keeps a `__proto__` key an ordinary entry instead of replacing the prototype.
211
+ Object.defineProperty(out, key, { value: filterEvidence(source[key], depth + 1, budget, stats), enumerable: true, writable: true, configurable: true });
212
+ kept++;
213
+ }
214
+ // The 100th key is kept only when it is the last one; make room for the count.
215
+ if (dropped > 0 && kept === EVIDENCE_LIMITS.maxEntries) {
216
+ stats.truncated = (stats.truncated ?? 0) + 1;
217
+ delete out[keys[keys.length - 1]];
218
+ dropped++;
219
+ kept--;
220
+ }
221
+ if (dropped > 0) {
222
+ // Root sees how many entries were removed, never their names or values.
223
+ stats.removed += dropped;
224
+ budget.nodes += 1;
225
+ out.omitted_entries = dropped;
226
+ }
227
+ return out;
228
+ }
229
+ /**
230
+ * Filter one evidence value placed at `depth` in its document. `outsideNodes`
231
+ * counts the values the document holds outside this one (envelope fields, an
232
+ * attachment reference); the budget leaves room for them and for the markers,
233
+ * and never exceeds the default.
234
+ */
235
+ export function filterEvidenceValue(value, depth, maxStringChars = EVIDENCE_LIMITS.maxStringChars, outsideNodes = 0) {
236
+ const stats = { removed: 0, rewritten: 0 };
237
+ const maxString = Math.max(64, Math.min(EVIDENCE_LIMITS.maxStringChars, Math.floor(maxStringChars)));
238
+ const outside = Number.isSafeInteger(outsideNodes) && outsideNodes > 0 ? outsideNodes : 0;
239
+ const limit = Math.max(1, Math.min(NODE_BUDGET, EVIDENCE_LIMITS.maxNodes - MARKER_HEADROOM - outside));
240
+ return { value: filterEvidence(value, depth, { nodes: 0, limit, maxString }, stats), stats };
241
+ }
242
+ /** How many values root's inspection visits in `value`: one per value, containers included. */
243
+ export function evidenceNodes(value) {
244
+ let nodes = 0;
245
+ const stack = [value];
246
+ while (stack.length) {
247
+ const item = stack.pop();
248
+ nodes += 1;
249
+ if (Array.isArray(item))
250
+ stack.push(...item);
251
+ else if (item !== null && typeof item === 'object')
252
+ stack.push(...Object.values(item));
253
+ }
254
+ return nodes;
255
+ }
@@ -0,0 +1,241 @@
1
+ import { request as httpsRequest } from 'node:https';
2
+ import { api } from '../auth.js';
3
+ import { CliError } from '../http.js';
4
+ import { toolConnectionReads } from '../tool-connection-reads.js';
5
+ import { toolLifecycleReads } from '../tool-lifecycle-reads.js';
6
+ import { toolConnectionAccepted, toolConnectionMatches, toolConnectionTokenMatches, } from '../tool-connection-contracts.js';
7
+ import { SecretValue } from './contracts.js';
8
+ import { isTransient } from './control-plane.js';
9
+ import { header } from './dependency-proxy.js';
10
+ /** Connection-option pages read per instance (25 operations per page). */
11
+ const OPTION_PAGES = 40;
12
+ /** Root's operation argument limit plus its JSON envelope. */
13
+ export const ISOLATED_BODY_MAX_BYTES = 8 * 1_048_576 + 65_536;
14
+ /** A whole answer streamed back: root's 16 MiB response, base64-encoded, plus envelope. */
15
+ const ISOLATED_RESPONSE_MAX_BYTES = 24 * 1_048_576;
16
+ /** An MCP tools/call streams for up to 90 seconds with heartbeats before root answers. */
17
+ const STREAM_TIMEOUT_MS = 150_000;
18
+ /** Own time for finding and revoking a grant, also after the command was cancelled. */
19
+ export const CLEANUP_MS = 15_000;
20
+ /** Effects that never change the tool's state. */
21
+ const READ_EFFECTS = new Set(['read', 'none']);
22
+ /** Request headers an MCP client sends that root's gateway reads. Nothing else (no cookie, no Origin) is passed on. */
23
+ export const MCP_REQUEST_HEADERS = ['accept', 'content-type', 'mcp-session-id', 'mcp-protocol-version'];
24
+ /** Response headers passed back. Root's 401 challenge is not: the agent authenticates to this proxy, never to root. */
25
+ export const MCP_RESPONSE_HEADERS = ['content-type', 'mcp-session-id', 'allow', 'retry-after'];
26
+ export const OPERATION_RESPONSE_HEADERS = ['content-type', 'allow', 'retry-after'];
27
+ const connectionScope = (scope, instance, epoch) => ({ workspace: scope.workspace, actor: scope.owner, instance, epoch });
28
+ /**
29
+ * Issue one grant for an isolated child: the selected operations the human's
30
+ * mode allows (read-only unless writes were allowed), over HTTP and MCP, for the
31
+ * instance's remaining time. `requestKey` is the original Idempotency-Key, which
32
+ * the caller keeps (and reports) before this sends anything. Throws a CliError
33
+ * naming why it is not served.
34
+ */
35
+ export async function openIsolatedGrant(scope, input) {
36
+ const instance = await toolLifecycleReads(scope, input.signal).instance(input.instanceId);
37
+ if (!instance.observation_current)
38
+ throw new CliError('tool_instance_not_ready', 'This isolated tool has no current observation yet. Run tools access again shortly.', 2);
39
+ const connections = toolConnectionReads(scope, input.instanceId, input.signal);
40
+ const effects = new Map();
41
+ let duration = 0;
42
+ for (let page = 1; page <= OPTION_PAGES; page++) {
43
+ const choices = await connections.options(page, '', instance.state_epoch);
44
+ duration = choices.duration_seconds;
45
+ for (const one of choices.results)
46
+ if (one.service_key === input.serviceKey)
47
+ effects.set(one.operation_name, one.effect);
48
+ if (!choices.next)
49
+ break;
50
+ }
51
+ const allowed = input.operations.filter((name) => effects.has(name) && (input.mode === 'interactive' || READ_EFFECTS.has(effects.get(name))));
52
+ if (!allowed.length) {
53
+ throw new CliError('tool_operations_unavailable', input.mode === 'read'
54
+ ? 'None of its selected operations is read-only. Add --allow-writes only if the human asked for writes.'
55
+ : 'None of its selected operations can be connected.', 2);
56
+ }
57
+ if (duration < 60)
58
+ throw new CliError('tool_instance_ending', 'It has less than a minute left.', 2);
59
+ const permission = {
60
+ operations: allowed.map((operation_name) => ({ service_key: input.serviceKey, operation_name })),
61
+ protocols: ['http', 'mcp'], duration_seconds: duration,
62
+ };
63
+ const key = input.requestKey;
64
+ const path = `/api/workspaces/${scope.workspace}/tools/instances/${input.instanceId}/connections`;
65
+ const auth = { actor: scope.owner, workspace: scope.workspace, signal: input.signal };
66
+ let issued;
67
+ try {
68
+ issued = await api(path, { ...auth, method: 'POST', idempotencyKey: key, body: { ...permission, data_transfer_consent: true } });
69
+ }
70
+ catch (error) {
71
+ // An answered refusal created nothing. No answer, or a cancellation while it was being sent, may have
72
+ // created it: its token cannot be recovered, so the original is found and revoked on its own bounded
73
+ // time (the command's signal may already be aborted), and nothing is reissued.
74
+ if (error instanceof CliError && error.httpStatus !== null && !isTransient(error))
75
+ throw error;
76
+ const cleanup = AbortSignal.timeout(CLEANUP_MS);
77
+ const original = await toolConnectionReads(scope, input.instanceId, cleanup).original(key, { input: permission }).catch(() => null);
78
+ const revoked = original ? await revokeGrant(scope, input.instanceId, original.id, cleanup) : false;
79
+ throw new CliError('tool_connection_unconfirmed', `Its connection was not confirmed (request ${key}), and no replacement was requested. `
80
+ + (original ? (revoked ? `It had been created as ${original.id} and is now revoked.` : `It was created as ${original.id}; revoke it with firedrill tools disconnect ${input.instanceId} ${original.id} --approve-tool-change.`)
81
+ : `Whether it was created is unknown. If it was, find it with firedrill tools connections ${input.instanceId} and revoke it.`) + ' Run tools access again to connect it.');
82
+ }
83
+ const matches = await toolConnectionAccepted(issued, connectionScope(scope, input.instanceId, instance.state_epoch), key, permission).catch(() => false)
84
+ && toolConnectionTokenMatches(issued) && issued.token_available_once && typeof issued.token === 'string'
85
+ && issued.mcp_url !== null && issued.http_url !== null && !issued.revoked_at;
86
+ if (!matches) {
87
+ const id = typeof issued?.id === 'string' ? issued.id : null;
88
+ const revoked = id ? await revokeGrant(scope, input.instanceId, id, AbortSignal.timeout(CLEANUP_MS)) : false;
89
+ throw new CliError('native_response_unconfirmed', 'Its connection response could not be confirmed, so it is not served'
90
+ + (id ? (revoked ? ' and was revoked.' : `; revoke it with firedrill tools disconnect ${input.instanceId} ${id} --approve-tool-change.`) : '.'));
91
+ }
92
+ return {
93
+ instance_id: input.instanceId, service_key: input.serviceKey, grant_id: issued.id, request_key: key, expires_at: issued.expires_at,
94
+ state_epoch: issued.state_epoch,
95
+ operations: issued.operations.map((one) => ({ service_key: one.service_key, operation_name: one.operation_name })),
96
+ token: new SecretValue(issued.token, `tool-connection:${issued.id}`),
97
+ mcp_url: issued.mcp_url, operations_url: issued.http_url, requests_url: `${issued.audience}/requests`,
98
+ };
99
+ }
100
+ /** Revoke a grant this access issued; true only when root confirms the revocation. */
101
+ export async function revokeGrant(scope, instanceId, grantId, signal) {
102
+ try {
103
+ const row = await api(`/api/workspaces/${scope.workspace}/tools/instances/${instanceId}/connections/${grantId}`, { actor: scope.owner, workspace: scope.workspace, signal, method: 'DELETE' });
104
+ return toolConnectionMatches(row, connectionScope(scope, instanceId), { id: grantId, revoked: true });
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ /**
111
+ * Whether a served grant can still be used, after root refused a call with it.
112
+ * Root answers 401 for a revoked or expired grant, a reset or ended instance,
113
+ * and also for an operation or original request outside the grant, so a 401
114
+ * alone never ends an entry: this reads the grant and the instance. `unknown`
115
+ * when either read fails for another reason (the entry keeps being served and
116
+ * root keeps refusing what it must).
117
+ */
118
+ export async function isolatedGrantState(scope, grant, signal) {
119
+ try {
120
+ const row = await toolConnectionReads(scope, grant.instance_id, signal).original(grant.request_key, { id: grant.grant_id });
121
+ if (row.revoked_at || !(Date.parse(row.expires_at) > Date.now()))
122
+ return 'connection_ended';
123
+ }
124
+ catch (error) {
125
+ if (error instanceof CliError && (error.httpStatus === 404 || error.httpStatus === 410))
126
+ return 'connection_ended';
127
+ return 'unknown';
128
+ }
129
+ try {
130
+ const instance = await toolLifecycleReads(scope, signal).instance(grant.instance_id);
131
+ return instance.state === 'active' && !instance.terminal_at && instance.state_epoch === grant.state_epoch ? 'usable' : 'instance_ended';
132
+ }
133
+ catch (error) {
134
+ if (error instanceof CliError && (error.httpStatus === 404 || error.httpStatus === 410))
135
+ return 'instance_ended';
136
+ return 'unknown';
137
+ }
138
+ }
139
+ /** The request body, or null once it exceeds `limit` (the rest is drained, not buffered). */
140
+ export function readBounded(request, limit) {
141
+ return new Promise((resolve, reject) => {
142
+ const chunks = [];
143
+ let size = 0, over = false;
144
+ request.on('data', (chunk) => {
145
+ if (over)
146
+ return;
147
+ size += chunk.length;
148
+ if (size > limit) {
149
+ over = true;
150
+ chunks.length = 0;
151
+ resolve(null);
152
+ return;
153
+ }
154
+ chunks.push(chunk);
155
+ });
156
+ request.once('end', () => { if (!over)
157
+ resolve(Buffer.concat(chunks)); });
158
+ request.once('error', reject);
159
+ });
160
+ }
161
+ /** The named request headers that are present once, as given. */
162
+ export function pickHeaders(request, names) {
163
+ const picked = {};
164
+ for (const name of names) {
165
+ const value = request.headers[name];
166
+ if (typeof value === 'string')
167
+ picked[name] = value;
168
+ }
169
+ return picked;
170
+ }
171
+ /**
172
+ * One call to the grant's endpoint, its answer streamed back as it arrives
173
+ * (an MCP tools/call answers as an event stream). Only `passBack` response
174
+ * headers are returned. Never retried: root keys each operation by its original
175
+ * request, and a disconnect after acceptance neither cancels nor repeats it.
176
+ * Returns root's status once its answer started, or `unanswered`: nothing came
177
+ * back, so the call may or may not have taken effect.
178
+ */
179
+ export function forwardIsolated(input) {
180
+ return new Promise((resolve) => {
181
+ let target;
182
+ try {
183
+ target = new URL(input.url);
184
+ }
185
+ catch {
186
+ resolve('unanswered');
187
+ return;
188
+ }
189
+ if (target.protocol !== 'https:' || target.username || target.password || target.hash) {
190
+ resolve('unanswered');
191
+ return;
192
+ }
193
+ const headers = { ...input.headers, authorization: `Bearer ${input.token.reveal()}` };
194
+ if (input.body)
195
+ headers['content-length'] = String(input.body.length);
196
+ let done = false, status = 0;
197
+ const finish = (value) => { if (!done) {
198
+ done = true;
199
+ resolve(value === 'sent' ? status : value);
200
+ } };
201
+ const upstream = httpsRequest(target, { method: input.method, headers, signal: AbortSignal.any([input.signal, AbortSignal.timeout(STREAM_TIMEOUT_MS)]) }, (reply) => {
202
+ const passed = { 'cache-control': 'no-store', ...input.extra };
203
+ for (const name of input.passBack) {
204
+ const value = header(reply.headers, name);
205
+ if (value !== null)
206
+ passed[name] = value;
207
+ }
208
+ status = reply.statusCode ?? 502;
209
+ input.response.writeHead(status, passed);
210
+ let size = 0;
211
+ reply.on('data', (chunk) => {
212
+ size += chunk.length;
213
+ if (size > ISOLATED_RESPONSE_MAX_BYTES) {
214
+ reply.destroy();
215
+ input.response.destroy();
216
+ finish('sent');
217
+ return;
218
+ }
219
+ if (!input.response.write(chunk)) {
220
+ reply.pause();
221
+ input.response.once('drain', () => reply.resume());
222
+ }
223
+ });
224
+ reply.once('end', () => { input.response.end(); finish('sent'); });
225
+ reply.once('error', () => { input.response.destroy(); finish('sent'); });
226
+ reply.once('aborted', () => { input.response.destroy(); finish('sent'); });
227
+ });
228
+ // The agent hung up: stop reading. Root keeps an accepted operation; nothing is cancelled or repeated.
229
+ input.response.once('close', () => { if (!done)
230
+ upstream.destroy(); });
231
+ upstream.once('error', () => {
232
+ if (status) {
233
+ input.response.destroy();
234
+ finish('sent');
235
+ }
236
+ else
237
+ finish('unanswered');
238
+ });
239
+ upstream.end(input.body ?? undefined);
240
+ });
241
+ }