@pensar/apex 2.5.0 → 2.6.1-canary.0cc77dcc

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 (1507) hide show
  1. package/README.md +38 -0
  2. package/bin/pensar.js +12 -4
  3. package/build/agent-6mzhkzxj.js +13 -0
  4. package/build/agent-9ymr36jt.js +13 -0
  5. package/build/{agent-m30a62kh.js → agent-fb1d2x2a.js} +48 -50
  6. package/build/agent-h8z7zb26.js +15 -0
  7. package/build/agent-runs-awgj4sat.js +313 -0
  8. package/build/{apps-x4ef2gpc.js → apps-d50kkp3g.js} +50 -58
  9. package/build/{auth-7ay8qgxm.js → auth-396rc66q.js} +61 -68
  10. package/build/blackboxAgent-j3ncq55f.js +13 -0
  11. package/build/blackboxPentest-aw69ect4.js +59 -0
  12. package/build/cli-0ym9qz7r.js +175 -0
  13. package/build/{cli-zav1yx5k.js → cli-2dqvgkh8.js} +990 -632
  14. package/build/cli-38j2jc0m.js +62 -0
  15. package/build/{cli-ee3axgts.js → cli-3gt761d2.js} +7 -1
  16. package/build/{cli-sx3jkv7p.js → cli-3p4gyc9m.js} +28872 -25951
  17. package/build/cli-3qznjsnr.js +14 -0
  18. package/build/cli-5ppxe6qz.js +355 -0
  19. package/build/cli-691cfgdy.js +186 -0
  20. package/build/{cli-x92fkt2f.js → cli-6y2mzdz2.js} +55 -97
  21. package/build/{cli-73k43t1r.js → cli-8s4ew1hv.js} +53 -78
  22. package/build/{cli-53rm0rrz.js → cli-8w8k4dph.js} +1 -1
  23. package/build/{cli-68260ckj.js → cli-91gs9whc.js} +1658 -1606
  24. package/build/{cli-7vg06q5r.js → cli-9s7snz6x.js} +135 -125
  25. package/build/{cli-bw2ddttk.js → cli-a1m3pmke.js} +4730 -267
  26. package/build/{cli-knd276ed.js → cli-bb02pjt0.js} +31 -31
  27. package/build/{cli-p4r7q012.js → cli-c0ha5wh1.js} +16072 -12743
  28. package/build/{cli-j54qa6tm.js → cli-c2k4xxaw.js} +42 -42
  29. package/build/{cli-k71t434r.js → cli-ec2hwkv3.js} +34 -76
  30. package/build/{cli-vkbzcnw1.js → cli-fapvkkbh.js} +43 -40
  31. package/build/cli-g06s7edq.js +11 -0
  32. package/build/cli-gsaf4jk3.js +280 -0
  33. package/build/{cli-bc05qbss.js → cli-h6y3bt60.js} +13 -21
  34. package/build/cli-jdrx44ge.js +14 -0
  35. package/build/{cli-z5c0ypg1.js → cli-jp6wytx1.js} +4 -4
  36. package/build/cli-mz5pk30x.js +5508 -0
  37. package/build/{cli-kff2bbq7.js → cli-nmg32cfd.js} +43 -43
  38. package/build/cli-pckpvwbd.js +22 -0
  39. package/build/cli-pwm29ks7.js +6337 -0
  40. package/build/cli-retw3rw5.js +12 -0
  41. package/build/cli-se8ae8z3.js +2250 -0
  42. package/build/{cli-dm0fc0bm.js → cli-v1sarr4y.js} +5 -9
  43. package/build/cli-v9rrvh12.js +429 -0
  44. package/build/{cli-3py0a0wg.js → cli-vfta27tv.js} +206 -136
  45. package/build/cli-xcy2vxj2.js +1732 -0
  46. package/build/{cli-y3dpm18e.js → cli-xn12g67s.js} +75 -92
  47. package/build/cli.js +769 -195
  48. package/build/{config-4kzh88xz.js → config-hddxxr27.js} +16 -19
  49. package/build/{doctor-rz7sx6ev.js → doctor-z94afr10.js} +26 -15
  50. package/build/export-trajectory-rt4tf07k.js +112 -0
  51. package/build/{fastStrike-a54atjc8.js → fastStrike-mgws2nav.js} +56 -31
  52. package/build/{fixes-fv66yn1z.js → fixes-9qtsvwxx.js} +22 -30
  53. package/build/{getMachineId-bsd-xj3bn2fv.js → getMachineId-bsd-zr67nd9p.js} +5 -5
  54. package/build/{getMachineId-darwin-07jzjcx6.js → getMachineId-darwin-fd1d562d.js} +5 -5
  55. package/build/{getMachineId-linux-ahngynxd.js → getMachineId-linux-kjmg243j.js} +4 -4
  56. package/build/{getMachineId-unsupported-vy43b44z.js → getMachineId-unsupported-n4r58vke.js} +4 -4
  57. package/build/{getMachineId-win-8x66cynz.js → getMachineId-win-yzgpfsrb.js} +5 -5
  58. package/build/index-0d05xcz4.js +25 -0
  59. package/build/index-0ef0j7yp.js +12 -0
  60. package/build/index-557xary4.js +121 -0
  61. package/build/index-a0bxw5zq.js +17477 -0
  62. package/build/{index-cbwn03kz.js → index-a2xycbjs.js} +12 -31
  63. package/build/{index-psqjpw5m.js → index-c1h0g35p.js} +11 -11
  64. package/build/index-cfbd4zej.js +17 -0
  65. package/build/index-mqaw1ac6.js +9 -0
  66. package/build/{index-kyd1jm89.js → index-nfhetcq8.js} +159 -169
  67. package/build/index-qnyevb6q.js +1563 -0
  68. package/build/{index-fpwr10qm.js → index-wyn4g2c1.js} +5048 -3786
  69. package/build/index-zbq3rvwx.js +36 -0
  70. package/build/index-zhr65sg3.js +102 -0
  71. package/build/{issues-019678t0.js → issues-9zdszfq8.js} +51 -47
  72. package/build/knowledge-r6rmer3d.js +139 -0
  73. package/build/lib/chunk-02awpt3c.js +431 -0
  74. package/build/lib/chunk-0me4mrrd.js +7150 -0
  75. package/build/lib/chunk-2j49k090.js +922 -0
  76. package/build/lib/chunk-2njv7wz3.js +178 -0
  77. package/build/lib/chunk-2t65f749.js +1412 -0
  78. package/build/lib/chunk-3502z798.js +9232 -0
  79. package/build/lib/chunk-3q89v4xv.js +21 -0
  80. package/build/lib/chunk-3w05jmk6.js +685 -0
  81. package/build/lib/chunk-48zms2q1.js +572 -0
  82. package/build/lib/chunk-4ydrmwd9.js +980 -0
  83. package/build/lib/chunk-517rf135.js +143 -0
  84. package/build/lib/chunk-5cp1fp0x.js +186 -0
  85. package/build/lib/chunk-7gjdygcx.js +171 -0
  86. package/build/lib/chunk-8vhr65w3.js +272 -0
  87. package/build/lib/chunk-9nsrd4xg.js +12 -0
  88. package/build/lib/chunk-asbemhm5.js +56 -0
  89. package/build/lib/chunk-b8eq85d2.js +143 -0
  90. package/build/lib/chunk-bx94rwt5.js +24 -0
  91. package/build/lib/chunk-eb4h4k1f.js +213 -0
  92. package/build/lib/chunk-ebkq3bcq.js +102 -0
  93. package/build/lib/chunk-esymacwg.js +14 -0
  94. package/build/lib/chunk-f04pr76h.js +317 -0
  95. package/build/lib/chunk-fy3bbbqy.js +7896 -0
  96. package/build/lib/chunk-g8d2x00m.js +2429 -0
  97. package/build/lib/chunk-ja7j565c.js +743 -0
  98. package/build/lib/chunk-jah67hkt.js +120 -0
  99. package/build/lib/chunk-jdjagvgr.js +85 -0
  100. package/build/lib/chunk-k5m5rasy.js +439 -0
  101. package/build/lib/chunk-mp1czx2y.js +510 -0
  102. package/build/lib/chunk-mrjbsaf4.js +31 -0
  103. package/build/lib/chunk-mv74mq5n.js +59 -0
  104. package/build/lib/chunk-n0ba7khr.js +64 -0
  105. package/build/lib/chunk-ngp6p527.js +317 -0
  106. package/build/lib/chunk-q5e23acz.js +52 -0
  107. package/build/lib/chunk-sk8xnywf.js +212 -0
  108. package/build/lib/chunk-t5bt5vn1.js +146 -0
  109. package/build/lib/chunk-tsnk2k9x.js +345 -0
  110. package/build/lib/chunk-vt9spgd1.js +62 -0
  111. package/build/lib/chunk-vx228eeq.js +82 -0
  112. package/build/lib/chunk-wr6ke041.js +11157 -0
  113. package/build/lib/chunk-xcdtrzk5.js +27 -0
  114. package/build/lib/chunk-xpfg2fbk.js +90 -0
  115. package/build/lib/chunk-zrbqn62x.js +697 -0
  116. package/build/lib/core/agents/agentRuntime.d.ts +47 -0
  117. package/build/lib/core/agents/defineAgent.d.ts +82 -0
  118. package/build/lib/core/agents/offSecAgent/index.d.ts +9 -0
  119. package/build/lib/core/agents/offSecAgent/index.js +225 -0
  120. package/build/lib/core/agents/offSecAgent/interruptedStepFinalization.d.ts +23 -0
  121. package/build/lib/core/agents/offSecAgent/interruptedStepMessages.d.ts +42 -0
  122. package/build/lib/core/agents/offSecAgent/messagePersistence.d.ts +39 -0
  123. package/build/lib/core/agents/offSecAgent/offensiveSecurityAgent.d.ts +233 -0
  124. package/build/lib/core/agents/offSecAgent/prompt.d.ts +35 -0
  125. package/build/lib/core/agents/offSecAgent/recordedTools.d.ts +11 -0
  126. package/build/lib/core/agents/offSecAgent/runtimeContext.d.ts +43 -0
  127. package/build/lib/core/agents/offSecAgent/streamDiagnostics.d.ts +67 -0
  128. package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +223 -0
  129. package/build/lib/core/agents/offSecAgent/subagentSpawner.js +19 -0
  130. package/build/lib/core/agents/offSecAgent/toolLifecycle.d.ts +54 -0
  131. package/build/lib/core/agents/offSecAgent/tools/__tests__/sandboxScript.d.ts +1 -0
  132. package/build/lib/core/agents/offSecAgent/tools/addMemory.d.ts +16 -0
  133. package/build/lib/core/agents/offSecAgent/tools/agentScratch.d.ts +17 -0
  134. package/build/lib/core/agents/offSecAgent/tools/applyPatch.d.ts +8 -0
  135. package/build/lib/core/agents/offSecAgent/tools/applyPatchImpl.d.ts +24 -0
  136. package/build/lib/core/agents/offSecAgent/tools/askUserQuestions.d.ts +42 -0
  137. package/build/lib/core/agents/offSecAgent/tools/browserHeaderRouting.d.ts +9 -0
  138. package/build/lib/core/agents/offSecAgent/tools/browserToolFactories.d.ts +40 -0
  139. package/build/lib/core/agents/offSecAgent/tools/browserTools.d.ts +96 -0
  140. package/build/lib/core/agents/offSecAgent/tools/callbackListener.d.ts +212 -0
  141. package/build/lib/core/agents/offSecAgent/tools/camoufox.d.ts +106 -0
  142. package/build/lib/core/agents/offSecAgent/tools/checkpointState.d.ts +17 -0
  143. package/build/lib/core/agents/offSecAgent/tools/completeAuthentication.d.ts +39 -0
  144. package/build/lib/core/agents/offSecAgent/tools/crawlAuthenticated.d.ts +36 -0
  145. package/build/lib/core/agents/offSecAgent/tools/createAttackSurfaceReport.d.ts +31 -0
  146. package/build/lib/core/agents/offSecAgent/tools/createFile.d.ts +12 -0
  147. package/build/lib/core/agents/offSecAgent/tools/createTask.d.ts +20 -0
  148. package/build/lib/core/agents/offSecAgent/tools/delegateAuth.d.ts +72 -0
  149. package/build/lib/core/agents/offSecAgent/tools/deleteFile.d.ts +10 -0
  150. package/build/lib/core/agents/offSecAgent/tools/destructiveGuard.d.ts +94 -0
  151. package/build/lib/core/agents/offSecAgent/tools/detectAuthScheme.d.ts +72 -0
  152. package/build/lib/core/agents/offSecAgent/tools/documentApp.d.ts +38 -0
  153. package/build/lib/core/agents/offSecAgent/tools/documentEndpoint.d.ts +111 -0
  154. package/build/lib/core/agents/offSecAgent/tools/documentFinding.d.ts +285 -0
  155. package/build/lib/core/agents/offSecAgent/tools/email/adapters.d.ts +45 -0
  156. package/build/lib/core/agents/offSecAgent/tools/email/adapters.js +8 -0
  157. package/build/lib/core/agents/offSecAgent/tools/email/getAttachments.d.ts +33 -0
  158. package/build/lib/core/agents/offSecAgent/tools/email/getMessage.d.ts +20 -0
  159. package/build/lib/core/agents/offSecAgent/tools/email/index.d.ts +145 -0
  160. package/build/lib/core/agents/offSecAgent/tools/email/listInboxes.d.ts +23 -0
  161. package/build/lib/core/agents/offSecAgent/tools/email/listMessages.d.ts +22 -0
  162. package/build/lib/core/agents/offSecAgent/tools/email/markRead.d.ts +20 -0
  163. package/build/lib/core/agents/offSecAgent/tools/email/searchMessages.d.ts +24 -0
  164. package/build/lib/core/agents/offSecAgent/tools/email/sendEmail.d.ts +28 -0
  165. package/build/lib/core/agents/offSecAgent/tools/email/types.d.ts +40 -0
  166. package/build/lib/core/agents/offSecAgent/tools/email/types.js +0 -0
  167. package/build/lib/core/agents/offSecAgent/tools/executeCommand.d.ts +84 -0
  168. package/build/lib/core/agents/offSecAgent/tools/extractJsEndpoints.d.ts +13 -0
  169. package/build/lib/core/agents/offSecAgent/tools/fileWorkspace.d.ts +21 -0
  170. package/build/lib/core/agents/offSecAgent/tools/fileWorkspaceRemote.d.ts +13 -0
  171. package/build/lib/core/agents/offSecAgent/tools/getMemory.d.ts +12 -0
  172. package/build/lib/core/agents/offSecAgent/tools/getPage.d.ts +17 -0
  173. package/build/lib/core/agents/offSecAgent/tools/gitDiff.d.ts +12 -0
  174. package/build/lib/core/agents/offSecAgent/tools/gitStatus.d.ts +10 -0
  175. package/build/lib/core/agents/offSecAgent/tools/glob.d.ts +8 -0
  176. package/build/lib/core/agents/offSecAgent/tools/globImpl.d.ts +13 -0
  177. package/build/lib/core/agents/offSecAgent/tools/globPattern.d.ts +7 -0
  178. package/build/lib/core/agents/offSecAgent/tools/grep.d.ts +10 -0
  179. package/build/lib/core/agents/offSecAgent/tools/grepImpl.d.ts +34 -0
  180. package/build/lib/core/agents/offSecAgent/tools/httpRequest.d.ts +70 -0
  181. package/build/lib/core/agents/offSecAgent/tools/index.d.ts +466 -0
  182. package/build/lib/core/agents/offSecAgent/tools/index.js +262 -0
  183. package/build/lib/core/agents/offSecAgent/tools/listFiles.d.ts +9 -0
  184. package/build/lib/core/agents/offSecAgent/tools/listFilesImpl.d.ts +27 -0
  185. package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +13 -0
  186. package/build/lib/core/agents/offSecAgent/tools/listPromptInjections.d.ts +18 -0
  187. package/build/lib/core/agents/offSecAgent/tools/listTasks.d.ts +24 -0
  188. package/build/lib/core/agents/offSecAgent/tools/patchApply.d.ts +24 -0
  189. package/build/lib/core/agents/offSecAgent/tools/patchParse.d.ts +24 -0
  190. package/build/lib/core/agents/offSecAgent/tools/perCommandShell.d.ts +82 -0
  191. package/build/lib/core/agents/offSecAgent/tools/playwrightMcp.d.ts +369 -0
  192. package/build/lib/core/agents/offSecAgent/tools/probeAuthEndpoints.d.ts +28 -0
  193. package/build/lib/core/agents/offSecAgent/tools/profileCodebase.d.ts +33 -0
  194. package/build/lib/core/agents/offSecAgent/tools/provideComparisonResults.d.ts +31 -0
  195. package/build/lib/core/agents/offSecAgent/tools/queryWhiteboxCatalog.d.ts +17 -0
  196. package/build/lib/core/agents/offSecAgent/tools/readFile.d.ts +11 -0
  197. package/build/lib/core/agents/offSecAgent/tools/readFileImpl.d.ts +31 -0
  198. package/build/lib/core/agents/offSecAgent/tools/readSkill.d.ts +15 -0
  199. package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +31 -0
  200. package/build/lib/core/agents/offSecAgent/tools/response.d.ts +15 -0
  201. package/build/lib/core/agents/offSecAgent/tools/runAttackSurface.d.ts +81 -0
  202. package/build/lib/core/agents/offSecAgent/tools/runCodeQuery.d.ts +44 -0
  203. package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +32 -0
  204. package/build/lib/core/agents/offSecAgent/tools/runWhiteboxScan.d.ts +52 -0
  205. package/build/lib/core/agents/offSecAgent/tools/sandbox.d.ts +30 -0
  206. package/build/lib/core/agents/offSecAgent/tools/sandboxPlaywright.d.ts +137 -0
  207. package/build/lib/core/agents/offSecAgent/tools/sandboxScript.d.ts +6 -0
  208. package/build/lib/core/agents/offSecAgent/tools/scopeGuard.d.ts +91 -0
  209. package/build/lib/core/agents/offSecAgent/tools/scriptSyntaxCheck.d.ts +45 -0
  210. package/build/lib/core/agents/offSecAgent/tools/smsInbox.d.ts +67 -0
  211. package/build/lib/core/agents/offSecAgent/tools/smsInbox.js +6 -0
  212. package/build/lib/core/agents/offSecAgent/tools/smsListMessages.d.ts +29 -0
  213. package/build/lib/core/agents/offSecAgent/tools/spawnCodingAgent.d.ts +43 -0
  214. package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +51 -0
  215. package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +52 -0
  216. package/build/lib/core/agents/offSecAgent/tools/submitPlan.d.ts +12 -0
  217. package/build/lib/core/agents/offSecAgent/tools/testEndpointVariations.d.ts +34 -0
  218. package/build/lib/core/agents/offSecAgent/tools/threatModelGenerator.d.ts +32 -0
  219. package/build/lib/core/agents/offSecAgent/tools/toolOutput.d.ts +23 -0
  220. package/build/lib/core/agents/offSecAgent/tools/types.d.ts +215 -0
  221. package/build/lib/core/agents/offSecAgent/tools/updateFile.d.ts +14 -0
  222. package/build/lib/core/agents/offSecAgent/tools/updateTask.d.ts +20 -0
  223. package/build/lib/core/agents/offSecAgent/tools/validateDiscovery.d.ts +24 -0
  224. package/build/lib/core/agents/offSecAgent/tools/webSearch.d.ts +16 -0
  225. package/build/lib/core/agents/offSecAgent/tools/whiteboxCandidates.d.ts +108 -0
  226. package/build/lib/core/agents/offSecAgent/tools/whiteboxJobs.d.ts +157 -0
  227. package/build/lib/core/agents/offSecAgent/tools/windowsCurl.d.ts +20 -0
  228. package/build/lib/core/agents/offSecAgent/tools/workspaceApps.d.ts +120 -0
  229. package/build/lib/core/agents/offSecAgent/tools/workspaceDomains.d.ts +22 -0
  230. package/build/lib/core/agents/offSecAgent/tools/workspaceFailure.d.ts +6 -0
  231. package/build/lib/core/agents/offSecAgent/tools/writePlan.d.ts +13 -0
  232. package/build/lib/core/agents/offSecAgent/trace.d.ts +268 -0
  233. package/build/lib/core/agents/offSecAgent/types.d.ts +543 -0
  234. package/build/lib/core/agents/offSecAgent/types.js +7 -0
  235. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.d.ts +66 -0
  236. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +33 -0
  237. package/build/lib/core/agents/specialized/attackSurface/blackboxRiskScoring.d.ts +30 -0
  238. package/build/lib/core/agents/specialized/attackSurface/grpcSchema.d.ts +93 -0
  239. package/build/lib/core/agents/specialized/attackSurface/jsExtraction.d.ts +42 -0
  240. package/build/lib/core/agents/specialized/attackSurface/prompts.d.ts +1 -0
  241. package/build/lib/core/agents/specialized/attackSurface/schemas.d.ts +284 -0
  242. package/build/lib/core/agents/specialized/attackSurface/schemas.js +144 -0
  243. package/build/lib/core/agents/specialized/attackSurface/types.d.ts +32 -0
  244. package/build/lib/core/agents/specialized/authenticationAgent/agent.d.ts +73 -0
  245. package/build/lib/core/agents/specialized/authenticationAgent/index.d.ts +4 -0
  246. package/build/lib/core/agents/specialized/authenticationAgent/index.js +71 -0
  247. package/build/lib/core/agents/specialized/authenticationAgent/prompts.d.ts +10 -0
  248. package/build/lib/core/agents/specialized/authenticationAgent/types.d.ts +620 -0
  249. package/build/lib/core/agents/specialized/benchmark/comparisonAgent.d.ts +10 -0
  250. package/build/lib/core/agents/specialized/benchmark/docker-utils.d.ts +17 -0
  251. package/build/lib/core/agents/specialized/benchmark/flag-detector.d.ts +60 -0
  252. package/build/lib/core/agents/specialized/benchmark/index.d.ts +8 -0
  253. package/build/lib/core/agents/specialized/benchmark/prompts.d.ts +1 -0
  254. package/build/lib/core/agents/specialized/benchmark/remote/circuit-breaker.d.ts +22 -0
  255. package/build/lib/core/agents/specialized/benchmark/remote/daytona-wrapper.d.ts +33 -0
  256. package/build/lib/core/agents/specialized/benchmark/remote/index.d.ts +2 -0
  257. package/build/lib/core/agents/specialized/benchmark/types.d.ts +92 -0
  258. package/build/lib/core/agents/specialized/benchmarkComparisonAgent.d.ts +27 -0
  259. package/build/lib/core/agents/specialized/codeAgent/agent.d.ts +50 -0
  260. package/build/lib/core/agents/specialized/codeAgent/agent.js +31 -0
  261. package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
  262. package/build/lib/core/agents/specialized/codeAgent/schemas.d.ts +11 -0
  263. package/build/lib/core/agents/specialized/codeAgent/schemas.js +10 -0
  264. package/build/lib/core/agents/specialized/cvssScorer/index.d.ts +56 -0
  265. package/build/lib/core/agents/specialized/environment/agent.d.ts +43 -0
  266. package/build/lib/core/agents/specialized/environment/index.d.ts +4 -0
  267. package/build/lib/core/agents/specialized/environment/prompts.d.ts +10 -0
  268. package/build/lib/core/agents/specialized/environment/types.d.ts +37 -0
  269. package/build/lib/core/agents/specialized/findingJudge/agent.d.ts +22 -0
  270. package/build/lib/core/agents/specialized/findingJudge/agent.js +224 -0
  271. package/build/lib/core/agents/specialized/findingJudge/index.d.ts +31 -0
  272. package/build/lib/core/agents/specialized/findingJudge/index.js +13 -0
  273. package/build/lib/core/agents/specialized/findingJudge/prompts.d.ts +3 -0
  274. package/build/lib/core/agents/specialized/findingJudge/types.d.ts +77 -0
  275. package/build/lib/core/agents/specialized/googleSignInPrompt.d.ts +4 -0
  276. package/build/lib/core/agents/specialized/mobileOtpPrompt.d.ts +1 -0
  277. package/build/lib/core/agents/specialized/patching/agent.d.ts +64 -0
  278. package/build/lib/core/agents/specialized/patching/index.d.ts +4 -0
  279. package/build/lib/core/agents/specialized/patching/index.js +44 -0
  280. package/build/lib/core/agents/specialized/patching/prompts.d.ts +17 -0
  281. package/build/lib/core/agents/specialized/patching/types.d.ts +40 -0
  282. package/build/lib/core/agents/specialized/pentest/agent.d.ts +172 -0
  283. package/build/lib/core/agents/specialized/pentest/agent.js +43 -0
  284. package/build/lib/core/agents/specialized/pentest/helperWorkspace.d.ts +16 -0
  285. package/build/lib/core/agents/specialized/pentest/planPrompt.d.ts +13 -0
  286. package/build/lib/core/agents/specialized/utils.d.ts +7 -0
  287. package/build/lib/core/agents/specialized/vulnerabilityReproduction/agent.d.ts +94 -0
  288. package/build/lib/core/agents/specialized/whiteboxAttackSurface/agent.d.ts +148 -0
  289. package/build/lib/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.d.ts +61 -0
  290. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.d.ts +5 -0
  291. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +47 -0
  292. package/build/lib/core/agents/specialized/whiteboxAttackSurface/prompts.d.ts +34 -0
  293. package/build/lib/core/agents/specialized/whiteboxAttackSurface/types.d.ts +382 -0
  294. package/build/lib/core/agents/specialized/whiteboxAttackSurface/types.js +21 -0
  295. package/build/lib/core/ai/ai.d.ts +332 -0
  296. package/build/lib/core/ai/ai.js +68 -0
  297. package/build/lib/core/ai/caching.d.ts +26 -0
  298. package/build/lib/core/ai/compactionEvidence.d.ts +15 -0
  299. package/build/lib/core/ai/compactionTelemetry.d.ts +55 -0
  300. package/build/lib/core/ai/contextManagement.d.ts +85 -0
  301. package/build/lib/core/ai/index.d.ts +10 -0
  302. package/build/lib/core/ai/index.js +72 -0
  303. package/build/lib/core/ai/inference-attempt/adapters.d.ts +15 -0
  304. package/build/lib/core/ai/inference-attempt/builder.d.ts +31 -0
  305. package/build/lib/core/ai/inference-attempt/envelope.d.ts +120 -0
  306. package/build/lib/core/ai/inference-attempt/errors.d.ts +6 -0
  307. package/build/lib/core/ai/inference-attempt/identity.d.ts +13 -0
  308. package/build/lib/core/ai/inference-attempt/index.d.ts +36 -0
  309. package/build/lib/core/ai/inference-attempt/project.d.ts +21 -0
  310. package/build/lib/core/ai/inference-attempt/recording.d.ts +23 -0
  311. package/build/lib/core/ai/inference-attempt/tokens.d.ts +13 -0
  312. package/build/lib/core/ai/inference-attempt/validate.d.ts +2 -0
  313. package/build/lib/core/ai/mantle.d.ts +28 -0
  314. package/build/lib/core/ai/model-history.d.ts +4 -0
  315. package/build/lib/core/ai/models/anthropic.d.ts +2 -0
  316. package/build/lib/core/ai/models/bedrock.d.ts +2 -0
  317. package/build/lib/core/ai/models/claude-capabilities.d.ts +6 -0
  318. package/build/lib/core/ai/models/concentrate.d.ts +2 -0
  319. package/build/lib/core/ai/models/google.d.ts +2 -0
  320. package/build/lib/core/ai/models/inception.d.ts +2 -0
  321. package/build/lib/core/ai/models/index.d.ts +29 -0
  322. package/build/lib/core/ai/models/mantle.d.ts +2 -0
  323. package/build/lib/core/ai/models/openai.d.ts +2 -0
  324. package/build/lib/core/ai/models/openrouter.d.ts +2 -0
  325. package/build/lib/core/ai/models/pensar.d.ts +2 -0
  326. package/build/lib/core/ai/native-rollout-evidence/capture.d.ts +31 -0
  327. package/build/lib/core/ai/native-rollout-evidence/content.d.ts +14 -0
  328. package/build/lib/core/ai/native-rollout-evidence/index.d.ts +9 -0
  329. package/build/lib/core/ai/native-rollout-evidence/provider-metadata.d.ts +15 -0
  330. package/build/lib/core/ai/native-rollout-evidence/schema.d.ts +554 -0
  331. package/build/lib/core/ai/native-rollout-evidence/validate.d.ts +8 -0
  332. package/build/lib/core/ai/providers/anthropic.d.ts +2 -0
  333. package/build/lib/core/ai/providers/concentrate.d.ts +9 -0
  334. package/build/lib/core/ai/providers/custom.d.ts +3 -0
  335. package/build/lib/core/ai/providers/hoonify.d.ts +6 -0
  336. package/build/lib/core/ai/providers/openai-pro-fetch.d.ts +2 -0
  337. package/build/lib/core/ai/providers/pensar.d.ts +34 -0
  338. package/build/lib/core/ai/providers/pensarFormatters.d.ts +2 -0
  339. package/build/lib/core/ai/providers/pensarResponsesStream.d.ts +15 -0
  340. package/build/lib/core/ai/providers/pensarSSE.d.ts +23 -0
  341. package/build/lib/core/ai/providers/pensarSigning.d.ts +1 -0
  342. package/build/lib/core/ai/providers/pensarStreamUsage.d.ts +19 -0
  343. package/build/lib/core/ai/streamTelemetry.d.ts +22 -0
  344. package/build/lib/core/ai/utils.d.ts +90 -0
  345. package/build/lib/core/ai/utils.js +22 -0
  346. package/build/lib/core/api/apiClient.d.ts +7 -0
  347. package/build/lib/core/api/apps.d.ts +213 -0
  348. package/build/lib/core/api/attackSurface.d.ts +14 -0
  349. package/build/lib/core/api/authentication.d.ts +3 -0
  350. package/build/lib/core/api/benchmark.d.ts +5 -0
  351. package/build/lib/core/api/blackboxPentest.d.ts +11 -0
  352. package/build/lib/core/api/clientIdentity.d.ts +10 -0
  353. package/build/lib/core/api/constants.d.ts +5 -0
  354. package/build/lib/core/api/domains.d.ts +19 -0
  355. package/build/lib/core/api/environment.d.ts +16 -0
  356. package/build/lib/core/api/index.d.ts +41 -0
  357. package/build/lib/core/api/index.js +187 -0
  358. package/build/lib/core/api/issues.d.ts +273 -0
  359. package/build/lib/core/api/knowledge.d.ts +33 -0
  360. package/build/lib/core/api/localWorker.d.ts +4 -0
  361. package/build/lib/core/api/offesecAgent.d.ts +34 -0
  362. package/build/lib/core/api/patching.d.ts +15 -0
  363. package/build/lib/core/api/recordedRun.d.ts +36 -0
  364. package/build/lib/core/api/targetedPentest.d.ts +15 -0
  365. package/build/lib/core/api/targetedPentest.js +32 -0
  366. package/build/lib/core/api/threatModel.d.ts +1 -0
  367. package/build/lib/core/api/trajectoryExport.d.ts +38 -0
  368. package/build/lib/core/api/vulnerabilityReproduction.d.ts +10 -0
  369. package/build/lib/core/assets/wordlists.d.ts +31 -0
  370. package/build/lib/core/atif/convert.d.ts +27 -0
  371. package/build/lib/core/atif/index.d.ts +7 -0
  372. package/build/lib/core/atif/schema.d.ts +459 -0
  373. package/build/lib/core/atif/serialize.d.ts +2 -0
  374. package/build/lib/core/atif/test-fixtures.d.ts +28 -0
  375. package/build/lib/core/atif/validate.d.ts +12 -0
  376. package/build/lib/core/auth/connection.d.ts +12 -0
  377. package/build/lib/core/auth/device-flow.d.ts +38 -0
  378. package/build/lib/core/auth/gateway.d.ts +25 -0
  379. package/build/lib/core/auth/index.d.ts +8 -0
  380. package/build/lib/core/auth/signing.d.ts +14 -0
  381. package/build/lib/core/auth/token.d.ts +17 -0
  382. package/build/lib/core/auth/types.d.ts +85 -0
  383. package/build/lib/core/auth/workspaces.d.ts +34 -0
  384. package/build/lib/core/benchmark/index.d.ts +3 -0
  385. package/build/lib/core/benchmark/report.d.ts +3 -0
  386. package/build/lib/core/benchmark/runner.d.ts +3 -0
  387. package/build/lib/core/benchmark/types.d.ts +80 -0
  388. package/build/lib/core/cli/logLevelArgs.d.ts +18 -0
  389. package/build/lib/core/cli/model.d.ts +9 -0
  390. package/build/lib/core/cli/pentestMode.d.ts +5 -0
  391. package/build/lib/core/cli/tuiArgs.d.ts +5 -0
  392. package/build/lib/core/config/config.d.ts +86 -0
  393. package/build/lib/core/config/customProviders.d.ts +62 -0
  394. package/build/lib/core/config/index.d.ts +6 -0
  395. package/build/lib/core/config/index.js +6 -0
  396. package/build/lib/core/credentials/index.d.ts +2 -0
  397. package/build/lib/core/credentials/manager.d.ts +85 -0
  398. package/build/lib/core/credentials/types.d.ts +79 -0
  399. package/build/lib/core/doctor.d.ts +1 -0
  400. package/build/lib/core/eventBus.d.ts +195 -0
  401. package/build/lib/core/eventBus.js +6 -0
  402. package/build/lib/core/findings/attackSurfaceRegistry.d.ts +88 -0
  403. package/build/lib/core/findings/attackSurfaceRegistry.js +6 -0
  404. package/build/lib/core/findings/registry.d.ts +177 -0
  405. package/build/lib/core/findings/registry.js +21 -0
  406. package/build/lib/core/history.d.ts +23 -0
  407. package/build/lib/core/hoonify.d.ts +8 -0
  408. package/build/lib/core/http/parse.d.ts +50 -0
  409. package/build/lib/core/http/redirects.d.ts +23 -0
  410. package/build/lib/core/http/targetHeaders.d.ts +30 -0
  411. package/build/lib/core/http/targetScope.d.ts +12 -0
  412. package/build/lib/core/http/types.d.ts +11 -0
  413. package/build/lib/core/id/id.d.ts +60 -0
  414. package/build/lib/core/id/id.js +28 -0
  415. package/build/lib/core/installation/index.d.ts +25 -0
  416. package/build/lib/core/integrations/herdr.d.ts +13 -0
  417. package/build/lib/core/integrations/surface/index.d.ts +37 -0
  418. package/build/lib/core/integrations/surface/types.d.ts +22 -0
  419. package/build/lib/core/integrations/wandb/client.d.ts +23 -0
  420. package/build/lib/core/integrations/wandb/upload.d.ts +36 -0
  421. package/build/lib/core/logger/index.d.ts +62 -0
  422. package/build/lib/core/logger/structured.d.ts +30 -0
  423. package/build/lib/core/memory/index.d.ts +92 -0
  424. package/build/lib/core/memory/index.js +21 -0
  425. package/build/lib/core/messages/types.d.ts +96 -0
  426. package/build/lib/core/obfuscation/engine.d.ts +51 -0
  427. package/build/lib/core/obfuscation/index.d.ts +20 -0
  428. package/build/lib/core/observability/active-root-spans.d.ts +5 -0
  429. package/build/lib/core/observability/active-spans.d.ts +22 -0
  430. package/build/lib/core/observability/index.d.ts +36 -0
  431. package/build/lib/core/observability/index.js +27 -0
  432. package/build/lib/core/observability/runtime.d.ts +63 -0
  433. package/build/lib/core/observability/telemetry.d.ts +52 -0
  434. package/build/lib/core/observability/testkit.d.ts +24 -0
  435. package/build/lib/core/operator/approvalGate.d.ts +61 -0
  436. package/build/lib/core/operator/index.d.ts +9 -0
  437. package/build/lib/core/operator/types.d.ts +167 -0
  438. package/build/lib/core/plan/index.d.ts +16 -0
  439. package/build/lib/core/prompt-injections/index.d.ts +111 -0
  440. package/build/lib/core/prompt-injections/index.js +24 -0
  441. package/build/lib/core/providers/index.d.ts +5 -0
  442. package/build/lib/core/providers/types.d.ts +13 -0
  443. package/build/lib/core/providers/utils.d.ts +15 -0
  444. package/build/lib/core/providers/verify.d.ts +11 -0
  445. package/build/lib/core/report/builder.d.ts +9 -0
  446. package/build/lib/core/report/index.d.ts +7 -0
  447. package/build/lib/core/report/renderers/json.d.ts +2 -0
  448. package/build/lib/core/report/renderers/markdown.d.ts +2 -0
  449. package/build/lib/core/report/schemas.d.ts +115 -0
  450. package/build/lib/core/runtime/agentExecution.d.ts +15 -0
  451. package/build/lib/core/runtime/launchLocalWorker.d.ts +33 -0
  452. package/build/lib/core/runtime/localRunLock.d.ts +9 -0
  453. package/build/lib/core/runtime/localRunWorker.d.ts +18 -0
  454. package/build/lib/core/runtime/localWorkerEndpoint.d.ts +14 -0
  455. package/build/lib/core/runtime/localWorkerProtocol.d.ts +294 -0
  456. package/build/lib/core/runtime/localWorkerTransport.d.ts +30 -0
  457. package/build/lib/core/runtime/persistenceError.d.ts +9 -0
  458. package/build/lib/core/runtime/recordedExecution.d.ts +6 -0
  459. package/build/lib/core/runtime/recordedRunClient.d.ts +34 -0
  460. package/build/lib/core/runtime/runCheckpointStore.d.ts +11 -0
  461. package/build/lib/core/runtime/runContext.d.ts +48 -0
  462. package/build/lib/core/runtime/runControl.d.ts +44 -0
  463. package/build/lib/core/runtime/runControlStore.d.ts +83 -0
  464. package/build/lib/core/runtime/runDeadline.d.ts +4 -0
  465. package/build/lib/core/runtime/runEvidence.d.ts +37 -0
  466. package/build/lib/core/runtime/runInference.d.ts +9 -0
  467. package/build/lib/core/runtime/runModelStore.d.ts +27 -0
  468. package/build/lib/core/runtime/runObservation.d.ts +19 -0
  469. package/build/lib/core/runtime/runRecoveryContext.d.ts +20 -0
  470. package/build/lib/core/runtime/runRecoveryPlan.d.ts +38 -0
  471. package/build/lib/core/runtime/runRecoveryStore.d.ts +77 -0
  472. package/build/lib/core/runtime/runStore.d.ts +133 -0
  473. package/build/lib/core/runtime/runStorePath.d.ts +1 -0
  474. package/build/lib/core/runtime/runToolStore.d.ts +50 -0
  475. package/build/lib/core/runtime/runTools.d.ts +16 -0
  476. package/build/lib/core/runtime/sqliteControlStore.d.ts +41 -0
  477. package/build/lib/core/runtime/sqliteModelStore.d.ts +20 -0
  478. package/build/lib/core/runtime/sqliteRecoveryStore.d.ts +35 -0
  479. package/build/lib/core/runtime/sqliteRunStore.d.ts +8 -0
  480. package/build/lib/core/runtime/sqliteToolStore.d.ts +30 -0
  481. package/build/lib/core/runtime/testfixtures/localWorkerChild.d.ts +1 -0
  482. package/build/lib/core/runtime/testfixtures/localWorkerLauncher.d.ts +1 -0
  483. package/build/lib/core/runtime/testfixtures/recoveryChild.d.ts +1 -0
  484. package/build/lib/core/services/rateLimiter/index.d.ts +36 -0
  485. package/build/lib/core/services/rateLimiter/types.d.ts +10 -0
  486. package/build/lib/core/session/execution-metrics.d.ts +37 -0
  487. package/build/lib/core/session/index.d.ts +586 -0
  488. package/build/lib/core/session/index.js +36 -0
  489. package/build/lib/core/session/loader.d.ts +30 -0
  490. package/build/lib/core/session/persistence.d.ts +215 -0
  491. package/build/lib/core/session/persistence.js +35 -0
  492. package/build/lib/core/session/types.d.ts +59 -0
  493. package/build/lib/core/session/usage.d.ts +37 -0
  494. package/build/lib/core/skills/builtins/index.d.ts +13 -0
  495. package/build/lib/core/skills/builtins/pentest.d.ts +19 -0
  496. package/build/lib/core/skills/builtins/promptInjection.d.ts +2 -0
  497. package/build/lib/core/skills/builtins/threatModel.d.ts +11 -0
  498. package/build/lib/core/skills/index.d.ts +5 -0
  499. package/build/lib/core/skills/parser.d.ts +11 -0
  500. package/build/lib/core/skills/registry.d.ts +30 -0
  501. package/build/lib/core/skills/scanner.d.ts +12 -0
  502. package/build/lib/core/skills/types.d.ts +44 -0
  503. package/build/lib/core/skills/utils.d.ts +6 -0
  504. package/build/lib/core/storage/index.d.ts +248 -0
  505. package/build/lib/core/tasks/index.d.ts +78 -0
  506. package/build/lib/core/tools/backends/capture.d.ts +10 -0
  507. package/build/lib/core/tools/backends/collectCommand.d.ts +9 -0
  508. package/build/lib/core/tools/backends/helpers.d.ts +43 -0
  509. package/build/lib/core/tools/backends/httpTransport.d.ts +18 -0
  510. package/build/lib/core/tools/backends/index.d.ts +8 -0
  511. package/build/lib/core/tools/backends/index.js +31 -0
  512. package/build/lib/core/tools/backends/local.d.ts +11 -0
  513. package/build/lib/core/tools/backends/policy.d.ts +39 -0
  514. package/build/lib/core/tools/backends/resolve.d.ts +25 -0
  515. package/build/lib/core/tools/backends/types.d.ts +240 -0
  516. package/build/lib/core/tools/backends/whitebox.d.ts +6 -0
  517. package/build/lib/core/tools/backends/whiteboxJobs.d.ts +18 -0
  518. package/build/lib/core/tools/backends/windowsProgram.d.ts +4 -0
  519. package/build/lib/core/tools/daytonaSandbox.d.ts +14 -0
  520. package/build/lib/core/toolset/index.d.ts +28 -0
  521. package/build/lib/core/util/lazyLogger.d.ts +16 -0
  522. package/build/lib/core/utils/concurrency.d.ts +16 -0
  523. package/build/lib/core/utils/prompt.d.ts +12 -0
  524. package/build/lib/core/whitebox/adapters.d.ts +29 -0
  525. package/build/lib/core/whitebox/artifacts.d.ts +32 -0
  526. package/build/lib/core/whitebox/boundedProcess.d.ts +35 -0
  527. package/build/lib/core/whitebox/candidates.d.ts +29 -0
  528. package/build/lib/core/whitebox/catalog.d.ts +10 -0
  529. package/build/lib/core/whitebox/index.d.ts +9 -0
  530. package/build/lib/core/whitebox/jobKernel.d.ts +33 -0
  531. package/build/lib/core/whitebox/jobSupervisor.d.ts +14 -0
  532. package/build/lib/core/whitebox/jobs.d.ts +17 -0
  533. package/build/lib/core/whitebox/paths.d.ts +22 -0
  534. package/build/lib/core/whitebox/profileTransport.d.ts +10 -0
  535. package/build/lib/core/whitebox/profiles.d.ts +3 -0
  536. package/build/lib/core/whitebox/repoProfile.d.ts +4 -0
  537. package/build/lib/core/whitebox/types.d.ts +108 -0
  538. package/build/lib/core/workflows/fastStrike.d.ts +4 -0
  539. package/build/lib/core/workflows/pentest.d.ts +167 -0
  540. package/build/lib/core/workflows/pentest.js +42 -0
  541. package/build/lib/core/workflows/seams.d.ts +102 -0
  542. package/build/lib/core/workflows/seams.js +37 -0
  543. package/build/lib/core/workflows/threatModel.d.ts +26 -0
  544. package/build/lib/core/workflows/whiteboxAttackSurface.d.ts +142 -0
  545. package/build/lib/core/workflows/whiteboxAttackSurface.js +36 -0
  546. package/build/lib/index.d.ts +26 -0
  547. package/build/lib/index.js +96 -0
  548. package/build/lib/lib/attack-path/types.d.ts +9 -0
  549. package/build/lib/lib/cvss/calculator.d.ts +31 -0
  550. package/build/lib/lib/cvss/index.d.ts +24 -0
  551. package/build/lib/lib/cvss/index.js +24 -0
  552. package/build/lib/lib/cvss/macrovector-scores.d.ts +59 -0
  553. package/build/lib/lib/cvss/types.d.ts +114 -0
  554. package/build/lib/lib/cvss/types.js +8 -0
  555. package/build/lib/lib/cwe/cwe-catalog.d.ts +13 -0
  556. package/build/lib/lib/cwe/types.d.ts +28 -0
  557. package/build/lib/lib/cwe/validate.d.ts +15 -0
  558. package/build/lib/lib/evidence/types.d.ts +12 -0
  559. package/build/lib/util/errors.d.ts +186 -0
  560. package/build/lib/util/lock.d.ts +2 -0
  561. package/build/lib/util/name.d.ts +14 -0
  562. package/build/lib/util/url.d.ts +40 -0
  563. package/build/{logs-wzr5176k.js → logs-1r7nkzhg.js} +22 -30
  564. package/build/{multipart-parser-g2xxkezz.js → multipart-parser-vjjdbpd1.js} +10 -10
  565. package/build/native-rollout-evidence-y0vdtjje.js +135 -0
  566. package/build/offesecAgent-dfj42jkj.js +22 -0
  567. package/build/{parse-rtccymzn.js → parse-cqecqrc5.js} +0 -4
  568. package/build/pentest-n8k0dd5q.js +22 -0
  569. package/build/pentests-jh9az51h.js +143 -0
  570. package/build/recordedRunClient-mvendk9n.js +11 -0
  571. package/build/{targetedPentest-tdwtgmxm.js → targetedPentest-rgffxsgd.js} +22 -22
  572. package/build/{targets-763n91t7.js → targets-wn0gmdhe.js} +24 -32
  573. package/build/threatModel-6efzjpbv.js +21 -0
  574. package/build/{token-3bbp59y6.js → token-mk7t8y0y.js} +2 -2
  575. package/build/{token-util-6xjme11b.js → token-util-vd776hrv.js} +1 -2
  576. package/build/{uninstall-d33159cz.js → uninstall-cmv057jn.js} +3 -3
  577. package/build/{upload-9qj1kpt9.js → upload-vkkj6h54.js} +8 -14
  578. package/build/{utils-t7gc2903.js → utils-b1kae4f3.js} +29 -29
  579. package/package.json +230 -5
  580. package/src/cli/agent-runs.attach.test.ts +359 -0
  581. package/src/cli/agent-runs.test.ts +1172 -0
  582. package/src/cli/agent-runs.ts +430 -0
  583. package/src/cli/apps.test.ts +63 -0
  584. package/src/cli/apps.ts +526 -0
  585. package/src/cli/auth.ts +375 -0
  586. package/src/cli/command-exit.test.ts +15 -0
  587. package/src/cli/command-exit.ts +3 -0
  588. package/src/cli/config.ts +210 -0
  589. package/src/cli/export-trajectory.test.ts +224 -0
  590. package/src/cli/export-trajectory.ts +119 -0
  591. package/src/cli/fixes.ts +56 -0
  592. package/src/cli/issues.test.ts +214 -0
  593. package/src/cli/issues.ts +242 -0
  594. package/src/cli/json-output.test.ts +62 -0
  595. package/src/cli/knowledge.test.ts +140 -0
  596. package/src/cli/knowledge.ts +146 -0
  597. package/src/cli/launcher.test.ts +81 -0
  598. package/src/cli/logs.ts +111 -0
  599. package/src/cli/native-rollout-evidence.test.ts +322 -0
  600. package/src/cli/native-rollout-evidence.ts +206 -0
  601. package/src/cli/pentests.test.ts +194 -0
  602. package/src/cli/pentests.ts +159 -0
  603. package/src/cli/resume.test.ts +107 -0
  604. package/src/cli/targets.ts +125 -0
  605. package/src/cli/uninstall.ts +302 -0
  606. package/src/cli.ts +882 -0
  607. package/src/core/agents/agentRuntime.test.ts +242 -0
  608. package/src/core/agents/agentRuntime.ts +120 -0
  609. package/src/core/agents/defineAgent.ts +134 -0
  610. package/src/core/agents/offSecAgent/index.ts +162 -0
  611. package/src/core/agents/offSecAgent/interruptedStepFinalization.test.ts +295 -0
  612. package/src/core/agents/offSecAgent/interruptedStepFinalization.ts +162 -0
  613. package/src/core/agents/offSecAgent/interruptedStepMessages.test.ts +296 -0
  614. package/src/core/agents/offSecAgent/interruptedStepMessages.ts +137 -0
  615. package/src/core/agents/offSecAgent/messagePersistence.test.ts +281 -0
  616. package/src/core/agents/offSecAgent/messagePersistence.ts +111 -0
  617. package/src/core/agents/offSecAgent/offensiveSecurityAgent.runtimeContext.test.ts +689 -0
  618. package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +2899 -0
  619. package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +1610 -0
  620. package/src/core/agents/offSecAgent/prompt.test.ts +72 -0
  621. package/src/core/agents/offSecAgent/prompt.ts +368 -0
  622. package/src/core/agents/offSecAgent/recordedTools.test.ts +580 -0
  623. package/src/core/agents/offSecAgent/recordedTools.ts +162 -0
  624. package/src/core/agents/offSecAgent/runtimeContext.test.ts +531 -0
  625. package/src/core/agents/offSecAgent/runtimeContext.ts +387 -0
  626. package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +295 -0
  627. package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +498 -0
  628. package/src/core/agents/offSecAgent/streamDiagnostics.test.ts +310 -0
  629. package/src/core/agents/offSecAgent/streamDiagnostics.ts +237 -0
  630. package/src/core/agents/offSecAgent/subagentSpawner.hooks.test.ts +102 -0
  631. package/src/core/agents/offSecAgent/subagentSpawner.test.ts +384 -0
  632. package/src/core/agents/offSecAgent/subagentSpawner.ts +664 -0
  633. package/src/core/agents/offSecAgent/toolLifecycle.test.ts +199 -0
  634. package/src/core/agents/offSecAgent/toolLifecycle.ts +171 -0
  635. package/src/core/agents/offSecAgent/tools/__tests__/sandboxScript.ts +14 -0
  636. package/src/core/agents/offSecAgent/tools/addMemory.ts +87 -0
  637. package/src/core/agents/offSecAgent/tools/agentScratch.test.ts +36 -0
  638. package/src/core/agents/offSecAgent/tools/agentScratch.ts +23 -0
  639. package/src/core/agents/offSecAgent/tools/applyPatch.test.ts +546 -0
  640. package/src/core/agents/offSecAgent/tools/applyPatch.ts +58 -0
  641. package/src/core/agents/offSecAgent/tools/applyPatchImpl.ts +300 -0
  642. package/src/core/agents/offSecAgent/tools/askUserQuestions.ts +81 -0
  643. package/src/core/agents/offSecAgent/tools/authBackendStatus.test.ts +112 -0
  644. package/src/core/agents/offSecAgent/tools/browserHeaderRouting.test.ts +136 -0
  645. package/src/core/agents/offSecAgent/tools/browserHeaderRouting.ts +81 -0
  646. package/src/core/agents/offSecAgent/tools/browserToolFactories.ts +372 -0
  647. package/src/core/agents/offSecAgent/tools/browserTools.test.ts +243 -0
  648. package/src/core/agents/offSecAgent/tools/browserTools.ts +285 -0
  649. package/src/core/agents/offSecAgent/tools/callbackListener.test.ts +1011 -0
  650. package/src/core/agents/offSecAgent/tools/callbackListener.ts +1038 -0
  651. package/src/core/agents/offSecAgent/tools/camoufox.test.ts +119 -0
  652. package/src/core/agents/offSecAgent/tools/camoufox.ts +199 -0
  653. package/src/core/agents/offSecAgent/tools/checkpointState.ts +79 -0
  654. package/src/core/agents/offSecAgent/tools/codingTools.windows.test.ts +625 -0
  655. package/src/core/agents/offSecAgent/tools/completeAuthentication.ts +136 -0
  656. package/src/core/agents/offSecAgent/tools/crawlAuthenticated.test.ts +267 -0
  657. package/src/core/agents/offSecAgent/tools/crawlAuthenticated.ts +170 -0
  658. package/src/core/agents/offSecAgent/tools/createAttackSurfaceReport.ts +75 -0
  659. package/src/core/agents/offSecAgent/tools/createFile.test.ts +111 -0
  660. package/src/core/agents/offSecAgent/tools/createFile.ts +44 -0
  661. package/src/core/agents/offSecAgent/tools/createTask.ts +118 -0
  662. package/src/core/agents/offSecAgent/tools/delegateAuth.ts +391 -0
  663. package/src/core/agents/offSecAgent/tools/deleteFile.test.ts +102 -0
  664. package/src/core/agents/offSecAgent/tools/deleteFile.ts +39 -0
  665. package/src/core/agents/offSecAgent/tools/destructiveGuard.test.ts +827 -0
  666. package/src/core/agents/offSecAgent/tools/destructiveGuard.ts +747 -0
  667. package/src/core/agents/offSecAgent/tools/detectAuthScheme.ts +191 -0
  668. package/src/core/agents/offSecAgent/tools/documentApp.ts +242 -0
  669. package/src/core/agents/offSecAgent/tools/documentEndpoint.ts +291 -0
  670. package/src/core/agents/offSecAgent/tools/documentFinding.test.ts +2091 -0
  671. package/src/core/agents/offSecAgent/tools/documentFinding.ts +1240 -0
  672. package/src/core/agents/offSecAgent/tools/email/adapters.test.ts +279 -0
  673. package/src/core/agents/offSecAgent/tools/email/adapters.ts +1121 -0
  674. package/src/core/agents/offSecAgent/tools/email/getAttachments.ts +73 -0
  675. package/src/core/agents/offSecAgent/tools/email/getMessage.ts +56 -0
  676. package/src/core/agents/offSecAgent/tools/email/inboxBackend.test.ts +137 -0
  677. package/src/core/agents/offSecAgent/tools/email/index.ts +55 -0
  678. package/src/core/agents/offSecAgent/tools/email/listInboxes.ts +47 -0
  679. package/src/core/agents/offSecAgent/tools/email/listMessages.ts +67 -0
  680. package/src/core/agents/offSecAgent/tools/email/markRead.ts +57 -0
  681. package/src/core/agents/offSecAgent/tools/email/searchMessages.ts +76 -0
  682. package/src/core/agents/offSecAgent/tools/email/sendEmail.ts +165 -0
  683. package/src/core/agents/offSecAgent/tools/email/types.ts +45 -0
  684. package/src/core/agents/offSecAgent/tools/executeCommand.backends.test.ts +171 -0
  685. package/src/core/agents/offSecAgent/tools/executeCommand.test.ts +1153 -0
  686. package/src/core/agents/offSecAgent/tools/executeCommand.ts +604 -0
  687. package/src/core/agents/offSecAgent/tools/extractJsEndpoints.ts +44 -0
  688. package/src/core/agents/offSecAgent/tools/fileBackends.test.ts +66 -0
  689. package/src/core/agents/offSecAgent/tools/fileMutations.test.ts +159 -0
  690. package/src/core/agents/offSecAgent/tools/fileToolBackendPurity.test.ts +72 -0
  691. package/src/core/agents/offSecAgent/tools/fileWorkspace.test.ts +357 -0
  692. package/src/core/agents/offSecAgent/tools/fileWorkspace.ts +434 -0
  693. package/src/core/agents/offSecAgent/tools/fileWorkspaceRemote.ts +263 -0
  694. package/src/core/agents/offSecAgent/tools/getMemory.ts +69 -0
  695. package/src/core/agents/offSecAgent/tools/getPage.test.ts +1127 -0
  696. package/src/core/agents/offSecAgent/tools/getPage.ts +70 -0
  697. package/src/core/agents/offSecAgent/tools/gitDiff.ts +67 -0
  698. package/src/core/agents/offSecAgent/tools/gitStatus.ts +61 -0
  699. package/src/core/agents/offSecAgent/tools/gitTools.test.ts +237 -0
  700. package/src/core/agents/offSecAgent/tools/glob.test.ts +695 -0
  701. package/src/core/agents/offSecAgent/tools/glob.ts +51 -0
  702. package/src/core/agents/offSecAgent/tools/globImpl.ts +313 -0
  703. package/src/core/agents/offSecAgent/tools/globPattern.ts +203 -0
  704. package/src/core/agents/offSecAgent/tools/grep.test.ts +922 -0
  705. package/src/core/agents/offSecAgent/tools/grep.ts +73 -0
  706. package/src/core/agents/offSecAgent/tools/grepImpl.ts +718 -0
  707. package/src/core/agents/offSecAgent/tools/httpBackends.test.ts +534 -0
  708. package/src/core/agents/offSecAgent/tools/httpRequest.test.ts +2409 -0
  709. package/src/core/agents/offSecAgent/tools/httpRequest.ts +425 -0
  710. package/src/core/agents/offSecAgent/tools/index.ts +787 -0
  711. package/src/core/agents/offSecAgent/tools/listFiles.bounded.test.ts +514 -0
  712. package/src/core/agents/offSecAgent/tools/listFiles.public-counters.test.ts +163 -0
  713. package/src/core/agents/offSecAgent/tools/listFiles.test.ts +521 -0
  714. package/src/core/agents/offSecAgent/tools/listFiles.ts +49 -0
  715. package/src/core/agents/offSecAgent/tools/listFilesImpl.ts +351 -0
  716. package/src/core/agents/offSecAgent/tools/listMemories.ts +71 -0
  717. package/src/core/agents/offSecAgent/tools/listPromptInjections.test.ts +201 -0
  718. package/src/core/agents/offSecAgent/tools/listPromptInjections.ts +78 -0
  719. package/src/core/agents/offSecAgent/tools/listTasks.ts +100 -0
  720. package/src/core/agents/offSecAgent/tools/patchApply.test.ts +573 -0
  721. package/src/core/agents/offSecAgent/tools/patchApply.ts +430 -0
  722. package/src/core/agents/offSecAgent/tools/patchParse.test.ts +383 -0
  723. package/src/core/agents/offSecAgent/tools/patchParse.ts +354 -0
  724. package/src/core/agents/offSecAgent/tools/perCommandShell.drain.test.ts +109 -0
  725. package/src/core/agents/offSecAgent/tools/perCommandShell.test.ts +565 -0
  726. package/src/core/agents/offSecAgent/tools/perCommandShell.ts +516 -0
  727. package/src/core/agents/offSecAgent/tools/perCommandShell.windows.test.ts +142 -0
  728. package/src/core/agents/offSecAgent/tools/playwrightMcp.test.ts +778 -0
  729. package/src/core/agents/offSecAgent/tools/playwrightMcp.ts +1426 -0
  730. package/src/core/agents/offSecAgent/tools/probeAuthEndpoints.ts +262 -0
  731. package/src/core/agents/offSecAgent/tools/profileCodebase.ts +110 -0
  732. package/src/core/agents/offSecAgent/tools/provideComparisonResults.ts +106 -0
  733. package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.coalescing-cost.test.ts +108 -0
  734. package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.test.ts +183 -0
  735. package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.ts +143 -0
  736. package/src/core/agents/offSecAgent/tools/readFile.test.ts +1242 -0
  737. package/src/core/agents/offSecAgent/tools/readFile.ts +89 -0
  738. package/src/core/agents/offSecAgent/tools/readFileImpl.ts +956 -0
  739. package/src/core/agents/offSecAgent/tools/readSkill.ts +38 -0
  740. package/src/core/agents/offSecAgent/tools/reportError.test.ts +48 -0
  741. package/src/core/agents/offSecAgent/tools/reportError.ts +65 -0
  742. package/src/core/agents/offSecAgent/tools/response.ts +27 -0
  743. package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +198 -0
  744. package/src/core/agents/offSecAgent/tools/runCodeQuery.test.ts +82 -0
  745. package/src/core/agents/offSecAgent/tools/runCodeQuery.ts +237 -0
  746. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +122 -0
  747. package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +189 -0
  748. package/src/core/agents/offSecAgent/tools/runtimeBackendConsumers.test.ts +143 -0
  749. package/src/core/agents/offSecAgent/tools/sandbox.ts +37 -0
  750. package/src/core/agents/offSecAgent/tools/sandboxPlaywright.test.ts +235 -0
  751. package/src/core/agents/offSecAgent/tools/sandboxPlaywright.ts +1029 -0
  752. package/src/core/agents/offSecAgent/tools/sandboxScript.test.ts +31 -0
  753. package/src/core/agents/offSecAgent/tools/sandboxScript.ts +60 -0
  754. package/src/core/agents/offSecAgent/tools/scopeGuard.test.ts +574 -0
  755. package/src/core/agents/offSecAgent/tools/scopeGuard.ts +317 -0
  756. package/src/core/agents/offSecAgent/tools/scriptSyntaxCheck.test.ts +566 -0
  757. package/src/core/agents/offSecAgent/tools/scriptSyntaxCheck.ts +240 -0
  758. package/src/core/agents/offSecAgent/tools/selectiveFactories.test.ts +476 -0
  759. package/src/core/agents/offSecAgent/tools/smsInbox.ts +130 -0
  760. package/src/core/agents/offSecAgent/tools/smsListMessages.test.ts +443 -0
  761. package/src/core/agents/offSecAgent/tools/smsListMessages.ts +123 -0
  762. package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +229 -0
  763. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +413 -0
  764. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +266 -0
  765. package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +146 -0
  766. package/src/core/agents/offSecAgent/tools/submitPlan.ts +51 -0
  767. package/src/core/agents/offSecAgent/tools/testEndpointVariations.ts +127 -0
  768. package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +664 -0
  769. package/src/core/agents/offSecAgent/tools/toolOutput.test.ts +480 -0
  770. package/src/core/agents/offSecAgent/tools/toolOutput.ts +160 -0
  771. package/src/core/agents/offSecAgent/tools/types.ts +267 -0
  772. package/src/core/agents/offSecAgent/tools/updateFile.ts +121 -0
  773. package/src/core/agents/offSecAgent/tools/updateTask.ts +107 -0
  774. package/src/core/agents/offSecAgent/tools/validateDiscovery.ts +123 -0
  775. package/src/core/agents/offSecAgent/tools/webSearch.ts +265 -0
  776. package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +197 -0
  777. package/src/core/agents/offSecAgent/tools/whiteboxJobs.ts +312 -0
  778. package/src/core/agents/offSecAgent/tools/windowsCurl.test.ts +801 -0
  779. package/src/core/agents/offSecAgent/tools/windowsCurl.ts +202 -0
  780. package/src/core/agents/offSecAgent/tools/workspaceApps.test.ts +257 -0
  781. package/src/core/agents/offSecAgent/tools/workspaceApps.ts +334 -0
  782. package/src/core/agents/offSecAgent/tools/workspaceDomains.test.ts +113 -0
  783. package/src/core/agents/offSecAgent/tools/workspaceDomains.ts +57 -0
  784. package/src/core/agents/offSecAgent/tools/workspaceFailure.ts +11 -0
  785. package/src/core/agents/offSecAgent/tools/writePlan.ts +65 -0
  786. package/src/core/agents/offSecAgent/trace.test.ts +796 -0
  787. package/src/core/agents/offSecAgent/trace.ts +633 -0
  788. package/src/core/agents/offSecAgent/types.test.ts +224 -0
  789. package/src/core/agents/offSecAgent/types.ts +676 -0
  790. package/src/core/agents/specialized/attackSurface/blackboxAgent.test.ts +31 -0
  791. package/src/core/agents/specialized/attackSurface/blackboxAgent.ts +272 -0
  792. package/src/core/agents/specialized/attackSurface/blackboxRiskScoring.ts +276 -0
  793. package/src/core/agents/specialized/attackSurface/grpcSchema.ts +142 -0
  794. package/src/core/agents/specialized/attackSurface/jsExtraction.test.ts +237 -0
  795. package/src/core/agents/specialized/attackSurface/jsExtraction.ts +174 -0
  796. package/src/core/agents/specialized/attackSurface/prompts.ts +349 -0
  797. package/src/core/agents/specialized/attackSurface/schemas.ts +300 -0
  798. package/src/core/agents/specialized/attackSurface/types.ts +100 -0
  799. package/src/core/agents/specialized/authenticationAgent/agent.test.ts +111 -0
  800. package/src/core/agents/specialized/authenticationAgent/agent.ts +300 -0
  801. package/src/core/agents/specialized/authenticationAgent/index.ts +74 -0
  802. package/src/core/agents/specialized/authenticationAgent/prompts.test.ts +36 -0
  803. package/src/core/agents/specialized/authenticationAgent/prompts.ts +645 -0
  804. package/src/core/agents/specialized/authenticationAgent/types.ts +833 -0
  805. package/src/core/agents/specialized/benchmark/comparisonAgent.ts +344 -0
  806. package/src/core/agents/specialized/benchmark/docker-utils.ts +257 -0
  807. package/src/core/agents/specialized/benchmark/flag-detector.ts +686 -0
  808. package/src/core/agents/specialized/benchmark/index.ts +28 -0
  809. package/src/core/agents/specialized/benchmark/prompts.ts +257 -0
  810. package/src/core/agents/specialized/benchmark/remote/circuit-breaker.ts +108 -0
  811. package/src/core/agents/specialized/benchmark/remote/daytona-wrapper.ts +1005 -0
  812. package/src/core/agents/specialized/benchmark/remote/index.ts +2 -0
  813. package/src/core/agents/specialized/benchmark/types.ts +98 -0
  814. package/src/core/agents/specialized/benchmarkComparisonAgent.test.ts +111 -0
  815. package/src/core/agents/specialized/benchmarkComparisonAgent.ts +203 -0
  816. package/src/core/agents/specialized/codeAgent/agent.test.ts +89 -0
  817. package/src/core/agents/specialized/codeAgent/agent.ts +134 -0
  818. package/src/core/agents/specialized/codeAgent/prompts.ts +48 -0
  819. package/src/core/agents/specialized/codeAgent/schemas.ts +33 -0
  820. package/src/core/agents/specialized/cvssScorer/index.test.ts +87 -0
  821. package/src/core/agents/specialized/cvssScorer/index.ts +546 -0
  822. package/src/core/agents/specialized/environment/agent.ts +75 -0
  823. package/src/core/agents/specialized/environment/index.ts +16 -0
  824. package/src/core/agents/specialized/environment/prompts.ts +194 -0
  825. package/src/core/agents/specialized/environment/types.ts +48 -0
  826. package/src/core/agents/specialized/findingJudge/agent.test.ts +114 -0
  827. package/src/core/agents/specialized/findingJudge/agent.ts +59 -0
  828. package/src/core/agents/specialized/findingJudge/index.test.ts +155 -0
  829. package/src/core/agents/specialized/findingJudge/index.ts +166 -0
  830. package/src/core/agents/specialized/findingJudge/prompts.ts +140 -0
  831. package/src/core/agents/specialized/findingJudge/types.ts +115 -0
  832. package/src/core/agents/specialized/googleSignInPrompt.test.ts +29 -0
  833. package/src/core/agents/specialized/googleSignInPrompt.ts +18 -0
  834. package/src/core/agents/specialized/mobileOtpPrompt.ts +9 -0
  835. package/src/core/agents/specialized/patching/agent.test.ts +220 -0
  836. package/src/core/agents/specialized/patching/agent.ts +146 -0
  837. package/src/core/agents/specialized/patching/index.ts +20 -0
  838. package/src/core/agents/specialized/patching/prompts.test.ts +172 -0
  839. package/src/core/agents/specialized/patching/prompts.ts +396 -0
  840. package/src/core/agents/specialized/patching/types.ts +49 -0
  841. package/src/core/agents/specialized/pentest/agent.test.ts +1046 -0
  842. package/src/core/agents/specialized/pentest/agent.ts +1482 -0
  843. package/src/core/agents/specialized/pentest/helperLifecycle.test.ts +425 -0
  844. package/src/core/agents/specialized/pentest/helperWorkspace.test.ts +84 -0
  845. package/src/core/agents/specialized/pentest/helperWorkspace.ts +33 -0
  846. package/src/core/agents/specialized/pentest/planPrompt.ts +68 -0
  847. package/src/core/agents/specialized/pentest/results.test.ts +198 -0
  848. package/src/core/agents/specialized/utils.test.ts +16 -0
  849. package/src/core/agents/specialized/utils.ts +28 -0
  850. package/src/core/agents/specialized/vulnerabilityReproduction/agent.test.ts +195 -0
  851. package/src/core/agents/specialized/vulnerabilityReproduction/agent.ts +159 -0
  852. package/src/core/agents/specialized/whiteboxAttackSurface/agent.test.ts +39 -0
  853. package/src/core/agents/specialized/whiteboxAttackSurface/agent.ts +227 -0
  854. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.concurrency.test.ts +138 -0
  855. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.test.ts +199 -0
  856. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +330 -0
  857. package/src/core/agents/specialized/whiteboxAttackSurface/index.ts +24 -0
  858. package/src/core/agents/specialized/whiteboxAttackSurface/prompts.ts +345 -0
  859. package/src/core/agents/specialized/whiteboxAttackSurface/types.test.ts +67 -0
  860. package/src/core/agents/specialized/whiteboxAttackSurface/types.ts +234 -0
  861. package/src/core/ai/ai.contract.test.ts +43 -0
  862. package/src/core/ai/ai.durability-hooks.test.ts +527 -0
  863. package/src/core/ai/ai.effective-tools.test.ts +739 -0
  864. package/src/core/ai/ai.native-rollout-evidence.test.ts +373 -0
  865. package/src/core/ai/ai.outer-catch-recovery.test.ts +82 -0
  866. package/src/core/ai/ai.proactive-recovery.test.ts +69 -0
  867. package/src/core/ai/ai.recorded-attempts.test.ts +621 -0
  868. package/src/core/ai/ai.recorded-context.test.ts +448 -0
  869. package/src/core/ai/ai.recorded-tools.test.ts +850 -0
  870. package/src/core/ai/ai.request-retention.test.ts +296 -0
  871. package/src/core/ai/ai.test.ts +636 -0
  872. package/src/core/ai/ai.ts +2349 -0
  873. package/src/core/ai/caching.test.ts +179 -0
  874. package/src/core/ai/caching.ts +119 -0
  875. package/src/core/ai/compactionEvidence.test.ts +52 -0
  876. package/src/core/ai/compactionEvidence.ts +101 -0
  877. package/src/core/ai/compactionTelemetry.test.ts +621 -0
  878. package/src/core/ai/compactionTelemetry.ts +287 -0
  879. package/src/core/ai/concentrate.test.ts +28 -0
  880. package/src/core/ai/contextManagement.test.ts +350 -0
  881. package/src/core/ai/contextManagement.ts +594 -0
  882. package/src/core/ai/index.ts +73 -0
  883. package/src/core/ai/inference-attempt/adapters.test.ts +84 -0
  884. package/src/core/ai/inference-attempt/adapters.ts +391 -0
  885. package/src/core/ai/inference-attempt/attempt.test.ts +198 -0
  886. package/src/core/ai/inference-attempt/builder.ts +171 -0
  887. package/src/core/ai/inference-attempt/envelope.ts +120 -0
  888. package/src/core/ai/inference-attempt/errors.ts +15 -0
  889. package/src/core/ai/inference-attempt/fixtures/anthropic-messages-cache-create.json +18 -0
  890. package/src/core/ai/inference-attempt/fixtures/anthropic-messages-cache-read.json +18 -0
  891. package/src/core/ai/inference-attempt/fixtures/anthropic-messages-incomplete.json +16 -0
  892. package/src/core/ai/inference-attempt/fixtures/anthropic-messages-mixed.json +22 -0
  893. package/src/core/ai/inference-attempt/fixtures/anthropic-messages-non-cache.json +26 -0
  894. package/src/core/ai/inference-attempt/fixtures/bedrock-converse-cache-create.json +18 -0
  895. package/src/core/ai/inference-attempt/fixtures/bedrock-converse-cache-read.json +26 -0
  896. package/src/core/ai/inference-attempt/fixtures/bedrock-converse-incomplete.json +19 -0
  897. package/src/core/ai/inference-attempt/fixtures/bedrock-converse-mixed.json +20 -0
  898. package/src/core/ai/inference-attempt/fixtures/bedrock-converse-non-cache.json +24 -0
  899. package/src/core/ai/inference-attempt/fixtures/openai-responses-cache-read.json +17 -0
  900. package/src/core/ai/inference-attempt/fixtures/openai-responses-incomplete.json +16 -0
  901. package/src/core/ai/inference-attempt/fixtures/openai-responses-non-cache.json +23 -0
  902. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-rest-incomplete.json +21 -0
  903. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-rest-native.json +16 -0
  904. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-cache-create.json +20 -0
  905. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-cache-read.json +20 -0
  906. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-incomplete.json +16 -0
  907. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-mixed.json +21 -0
  908. package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-non-cache.json +19 -0
  909. package/src/core/ai/inference-attempt/fixtures/reject-cache-read-exceeds-inclusive.json +11 -0
  910. package/src/core/ai/inference-attempt/fixtures/reject-components-exceed-inclusive.json +12 -0
  911. package/src/core/ai/inference-attempt/fixtures/reject-identity-mismatch.json +13 -0
  912. package/src/core/ai/inference-attempt/fixtures/reject-negative-token.json +9 -0
  913. package/src/core/ai/inference-attempt/fixtures/reject-non-integer-token.json +8 -0
  914. package/src/core/ai/inference-attempt/fixtures/sdk-normalized-cache-read.json +19 -0
  915. package/src/core/ai/inference-attempt/fixtures/sdk-normalized-incomplete.json +15 -0
  916. package/src/core/ai/inference-attempt/fixtures/sdk-normalized-mixed.json +20 -0
  917. package/src/core/ai/inference-attempt/fixtures/sdk-normalized-non-cache.json +16 -0
  918. package/src/core/ai/inference-attempt/identity.ts +25 -0
  919. package/src/core/ai/inference-attempt/index.ts +70 -0
  920. package/src/core/ai/inference-attempt/project.test.ts +118 -0
  921. package/src/core/ai/inference-attempt/project.ts +60 -0
  922. package/src/core/ai/inference-attempt/recording.ts +46 -0
  923. package/src/core/ai/inference-attempt/tokens.ts +165 -0
  924. package/src/core/ai/inference-attempt/validate.ts +20 -0
  925. package/src/core/ai/mantle.test.ts +50 -0
  926. package/src/core/ai/mantle.ts +45 -0
  927. package/src/core/ai/model-history.test.ts +53 -0
  928. package/src/core/ai/model-history.ts +38 -0
  929. package/src/core/ai/models/anthropic.ts +145 -0
  930. package/src/core/ai/models/bedrock.ts +600 -0
  931. package/src/core/ai/models/claude-capabilities.ts +22 -0
  932. package/src/core/ai/models/concentrate.ts +192 -0
  933. package/src/core/ai/models/google.ts +154 -0
  934. package/src/core/ai/models/inception.ts +10 -0
  935. package/src/core/ai/models/index.ts +250 -0
  936. package/src/core/ai/models/mantle.ts +14 -0
  937. package/src/core/ai/models/models.test.ts +290 -0
  938. package/src/core/ai/models/openai.ts +409 -0
  939. package/src/core/ai/models/openrouter.ts +336 -0
  940. package/src/core/ai/models/pensar.ts +66 -0
  941. package/src/core/ai/native-rollout-evidence/capture.test.ts +1836 -0
  942. package/src/core/ai/native-rollout-evidence/capture.ts +1572 -0
  943. package/src/core/ai/native-rollout-evidence/content.test.ts +55 -0
  944. package/src/core/ai/native-rollout-evidence/content.ts +129 -0
  945. package/src/core/ai/native-rollout-evidence/index.ts +68 -0
  946. package/src/core/ai/native-rollout-evidence/provider-metadata.test.ts +85 -0
  947. package/src/core/ai/native-rollout-evidence/provider-metadata.ts +129 -0
  948. package/src/core/ai/native-rollout-evidence/recording.test.ts +398 -0
  949. package/src/core/ai/native-rollout-evidence/schema.test.ts +210 -0
  950. package/src/core/ai/native-rollout-evidence/schema.ts +353 -0
  951. package/src/core/ai/native-rollout-evidence/validate.ts +141 -0
  952. package/src/core/ai/providers/anthropic-models.test.ts +289 -0
  953. package/src/core/ai/providers/anthropic.ts +73 -0
  954. package/src/core/ai/providers/concentrate.live.test.ts +71 -0
  955. package/src/core/ai/providers/concentrate.test.ts +160 -0
  956. package/src/core/ai/providers/concentrate.ts +145 -0
  957. package/src/core/ai/providers/custom.live.test.ts +42 -0
  958. package/src/core/ai/providers/custom.test.ts +474 -0
  959. package/src/core/ai/providers/custom.ts +70 -0
  960. package/src/core/ai/providers/gateway-models.test.ts +638 -0
  961. package/src/core/ai/providers/hoonify.live.test.ts +72 -0
  962. package/src/core/ai/providers/hoonify.test.ts +284 -0
  963. package/src/core/ai/providers/hoonify.ts +32 -0
  964. package/src/core/ai/providers/openai-models.test.ts +356 -0
  965. package/src/core/ai/providers/openai-pro-fetch.test.ts +64 -0
  966. package/src/core/ai/providers/openai-pro-fetch.ts +32 -0
  967. package/src/core/ai/providers/pensar.ts +645 -0
  968. package/src/core/ai/providers/pensarFormatters.test.ts +272 -0
  969. package/src/core/ai/providers/pensarFormatters.ts +533 -0
  970. package/src/core/ai/providers/pensarResponsesStream.test.ts +191 -0
  971. package/src/core/ai/providers/pensarResponsesStream.ts +294 -0
  972. package/src/core/ai/providers/pensarSSE.test.ts +83 -0
  973. package/src/core/ai/providers/pensarSSE.ts +128 -0
  974. package/src/core/ai/providers/pensarSigning.ts +2 -0
  975. package/src/core/ai/providers/pensarStreamUsage.test.ts +79 -0
  976. package/src/core/ai/providers/pensarStreamUsage.ts +62 -0
  977. package/src/core/ai/stepContext.test.ts +61 -0
  978. package/src/core/ai/streamIdleTimeout.test.ts +390 -0
  979. package/src/core/ai/streamTelemetry.ts +90 -0
  980. package/src/core/ai/usage-callback.test.ts +511 -0
  981. package/src/core/ai/utils.native-rollout-evidence.test.ts +135 -0
  982. package/src/core/ai/utils.pensar.test.ts +59 -0
  983. package/src/core/ai/utils.ts +796 -0
  984. package/src/core/api/apiClient.test.ts +58 -0
  985. package/src/core/api/apiClient.ts +73 -0
  986. package/src/core/api/apps.test.ts +78 -0
  987. package/src/core/api/apps.ts +388 -0
  988. package/src/core/api/attackSurface.test.ts +24 -0
  989. package/src/core/api/attackSurface.ts +89 -0
  990. package/src/core/api/authentication.ts +8 -0
  991. package/src/core/api/benchmark.ts +35 -0
  992. package/src/core/api/blackboxPentest.test.ts +89 -0
  993. package/src/core/api/blackboxPentest.ts +64 -0
  994. package/src/core/api/clientIdentity.test.ts +61 -0
  995. package/src/core/api/clientIdentity.ts +57 -0
  996. package/src/core/api/comments.test.ts +69 -0
  997. package/src/core/api/constants.ts +15 -0
  998. package/src/core/api/domains.test.ts +35 -0
  999. package/src/core/api/domains.ts +32 -0
  1000. package/src/core/api/environment.ts +47 -0
  1001. package/src/core/api/index.ts +219 -0
  1002. package/src/core/api/issues.test.ts +82 -0
  1003. package/src/core/api/issues.ts +439 -0
  1004. package/src/core/api/knowledge.ts +70 -0
  1005. package/src/core/api/localWorker.ts +29 -0
  1006. package/src/core/api/offesecAgent.test.ts +289 -0
  1007. package/src/core/api/offesecAgent.ts +77 -0
  1008. package/src/core/api/patching.ts +44 -0
  1009. package/src/core/api/recordedRun.control.test.ts +830 -0
  1010. package/src/core/api/recordedRun.inference.test.ts +529 -0
  1011. package/src/core/api/recordedRun.loop.test.ts +290 -0
  1012. package/src/core/api/recordedRun.recovery.test.ts +651 -0
  1013. package/src/core/api/recordedRun.test.ts +646 -0
  1014. package/src/core/api/recordedRun.ts +542 -0
  1015. package/src/core/api/targetedPentest.test.ts +177 -0
  1016. package/src/core/api/targetedPentest.ts +83 -0
  1017. package/src/core/api/threatModel.ts +5 -0
  1018. package/src/core/api/trajectoryExport.test.ts +299 -0
  1019. package/src/core/api/trajectoryExport.ts +362 -0
  1020. package/src/core/api/vulnerabilityReproduction.test.ts +56 -0
  1021. package/src/core/api/vulnerabilityReproduction.ts +54 -0
  1022. package/src/core/assets/wordlists.test.ts +85 -0
  1023. package/src/core/assets/wordlists.ts +111 -0
  1024. package/src/core/atif/compatibility.test.ts +107 -0
  1025. package/src/core/atif/convert.test.ts +815 -0
  1026. package/src/core/atif/convert.ts +1658 -0
  1027. package/src/core/atif/fixtures/portable-bundle-v1.json +813 -0
  1028. package/src/core/atif/index.ts +34 -0
  1029. package/src/core/atif/schema.ts +345 -0
  1030. package/src/core/atif/serialize.test.ts +244 -0
  1031. package/src/core/atif/serialize.ts +419 -0
  1032. package/src/core/atif/test-fixtures.ts +184 -0
  1033. package/src/core/atif/validate.test.ts +188 -0
  1034. package/src/core/atif/validate.ts +289 -0
  1035. package/src/core/auth/connection.ts +26 -0
  1036. package/src/core/auth/device-flow.ts +203 -0
  1037. package/src/core/auth/gateway.ts +66 -0
  1038. package/src/core/auth/index.ts +29 -0
  1039. package/src/core/auth/signing.ts +25 -0
  1040. package/src/core/auth/token.ts +140 -0
  1041. package/src/core/auth/types.ts +106 -0
  1042. package/src/core/auth/workspaces.ts +192 -0
  1043. package/src/core/benchmark/index.ts +11 -0
  1044. package/src/core/benchmark/report.ts +257 -0
  1045. package/src/core/benchmark/runner.ts +698 -0
  1046. package/src/core/benchmark/types.ts +80 -0
  1047. package/src/core/cli/logLevelArgs.test.ts +83 -0
  1048. package/src/core/cli/logLevelArgs.ts +62 -0
  1049. package/src/core/cli/model.ts +35 -0
  1050. package/src/core/cli/pentestMode.test.ts +24 -0
  1051. package/src/core/cli/pentestMode.ts +23 -0
  1052. package/src/core/cli/tuiArgs.test.ts +58 -0
  1053. package/src/core/cli/tuiArgs.ts +28 -0
  1054. package/src/core/config/config.test.ts +183 -0
  1055. package/src/core/config/config.ts +201 -0
  1056. package/src/core/config/customProviders.test.ts +183 -0
  1057. package/src/core/config/customProviders.ts +190 -0
  1058. package/src/core/config/index.ts +7 -0
  1059. package/src/core/credentials/index.ts +6 -0
  1060. package/src/core/credentials/manager.ts +313 -0
  1061. package/src/core/credentials/types.ts +112 -0
  1062. package/src/core/doctor.ts +138 -0
  1063. package/src/core/eventBus.test.ts +374 -0
  1064. package/src/core/eventBus.ts +410 -0
  1065. package/src/core/findings/attackSurfaceRegistry.ts +294 -0
  1066. package/src/core/findings/registry.test.ts +1680 -0
  1067. package/src/core/findings/registry.ts +800 -0
  1068. package/src/core/history.ts +82 -0
  1069. package/src/core/hoonify.test.ts +250 -0
  1070. package/src/core/hoonify.ts +163 -0
  1071. package/src/core/http/headers.contract.test.ts +1161 -0
  1072. package/src/core/http/parse.ts +257 -0
  1073. package/src/core/http/redirects.test.ts +207 -0
  1074. package/src/core/http/redirects.ts +145 -0
  1075. package/src/core/http/targetHeaders.ts +604 -0
  1076. package/src/core/http/targetScope.ts +52 -0
  1077. package/src/core/http/types.ts +39 -0
  1078. package/src/core/id/id.test.ts +88 -0
  1079. package/src/core/id/id.ts +138 -0
  1080. package/src/core/installation/index.ts +222 -0
  1081. package/src/core/installation/installation.test.ts +415 -0
  1082. package/src/core/integrations/herdr.test.ts +335 -0
  1083. package/src/core/integrations/herdr.ts +163 -0
  1084. package/src/core/integrations/surface/index.test.ts +534 -0
  1085. package/src/core/integrations/surface/index.ts +224 -0
  1086. package/src/core/integrations/surface/types.ts +29 -0
  1087. package/src/core/integrations/wandb/client.ts +167 -0
  1088. package/src/core/integrations/wandb/upload.test.ts +415 -0
  1089. package/src/core/integrations/wandb/upload.ts +76 -0
  1090. package/src/core/logger/errorLog.test.ts +98 -0
  1091. package/src/core/logger/index.ts +214 -0
  1092. package/src/core/logger/structured.test.ts +212 -0
  1093. package/src/core/logger/structured.ts +207 -0
  1094. package/src/core/memory/backend.test.ts +186 -0
  1095. package/src/core/memory/index.ts +279 -0
  1096. package/src/core/memory/memory.test.ts +362 -0
  1097. package/src/core/messages/types.ts +141 -0
  1098. package/src/core/obfuscation/engine.test.ts +314 -0
  1099. package/src/core/obfuscation/engine.ts +647 -0
  1100. package/src/core/obfuscation/index.ts +27 -0
  1101. package/src/core/observability/active-root-spans.ts +30 -0
  1102. package/src/core/observability/active-spans.ts +80 -0
  1103. package/src/core/observability/index.ts +91 -0
  1104. package/src/core/observability/log-correlation.test.ts +276 -0
  1105. package/src/core/observability/payload-capture.test.ts +59 -0
  1106. package/src/core/observability/runtime.test.ts +743 -0
  1107. package/src/core/observability/runtime.ts +371 -0
  1108. package/src/core/observability/shutdown.test.ts +551 -0
  1109. package/src/core/observability/telemetry.test.ts +895 -0
  1110. package/src/core/observability/telemetry.ts +220 -0
  1111. package/src/core/observability/testkit.test.ts +66 -0
  1112. package/src/core/observability/testkit.ts +97 -0
  1113. package/src/core/observability/trace-contract.test.ts +1031 -0
  1114. package/src/core/observability.test.ts +135 -0
  1115. package/src/core/operator/approvalGate.test.ts +300 -0
  1116. package/src/core/operator/approvalGate.ts +287 -0
  1117. package/src/core/operator/index.ts +30 -0
  1118. package/src/core/operator/types.ts +355 -0
  1119. package/src/core/plan/index.ts +44 -0
  1120. package/src/core/prompt-injections/index.test.ts +248 -0
  1121. package/src/core/prompt-injections/index.ts +350 -0
  1122. package/src/core/providers/index.ts +5 -0
  1123. package/src/core/providers/types.ts +87 -0
  1124. package/src/core/providers/utils.test.ts +189 -0
  1125. package/src/core/providers/utils.ts +183 -0
  1126. package/src/core/providers/verify.test.ts +49 -0
  1127. package/src/core/providers/verify.ts +198 -0
  1128. package/src/core/report/builder.test.ts +229 -0
  1129. package/src/core/report/builder.ts +58 -0
  1130. package/src/core/report/index.ts +13 -0
  1131. package/src/core/report/renderers/json.test.ts +76 -0
  1132. package/src/core/report/renderers/json.ts +5 -0
  1133. package/src/core/report/renderers/markdown.test.ts +366 -0
  1134. package/src/core/report/renderers/markdown.ts +133 -0
  1135. package/src/core/report/schemas.ts +48 -0
  1136. package/src/core/runtime/agentExecution.test.ts +94 -0
  1137. package/src/core/runtime/agentExecution.ts +27 -0
  1138. package/src/core/runtime/launchLocalWorker.test.ts +707 -0
  1139. package/src/core/runtime/launchLocalWorker.ts +273 -0
  1140. package/src/core/runtime/localRunLock.ts +118 -0
  1141. package/src/core/runtime/localRunWorker.test.ts +381 -0
  1142. package/src/core/runtime/localRunWorker.ts +287 -0
  1143. package/src/core/runtime/localWorker.acceptance.test.ts +548 -0
  1144. package/src/core/runtime/localWorkerEndpoint.test.ts +145 -0
  1145. package/src/core/runtime/localWorkerEndpoint.ts +82 -0
  1146. package/src/core/runtime/localWorkerProtocol.ts +176 -0
  1147. package/src/core/runtime/localWorkerTransport.test.ts +841 -0
  1148. package/src/core/runtime/localWorkerTransport.ts +434 -0
  1149. package/src/core/runtime/persistenceError.ts +14 -0
  1150. package/src/core/runtime/recordedExecution.ts +43 -0
  1151. package/src/core/runtime/recordedRunClient.integration.test.ts +106 -0
  1152. package/src/core/runtime/recordedRunClient.test.ts +677 -0
  1153. package/src/core/runtime/recordedRunClient.ts +303 -0
  1154. package/src/core/runtime/runCheckpointStore.ts +23 -0
  1155. package/src/core/runtime/runContext.test.ts +445 -0
  1156. package/src/core/runtime/runContext.ts +184 -0
  1157. package/src/core/runtime/runControl.test.ts +923 -0
  1158. package/src/core/runtime/runControl.ts +419 -0
  1159. package/src/core/runtime/runControlStore.ts +106 -0
  1160. package/src/core/runtime/runDeadline.test.ts +44 -0
  1161. package/src/core/runtime/runDeadline.ts +28 -0
  1162. package/src/core/runtime/runEvidence.test.ts +384 -0
  1163. package/src/core/runtime/runEvidence.ts +267 -0
  1164. package/src/core/runtime/runInference.test.ts +408 -0
  1165. package/src/core/runtime/runInference.ts +125 -0
  1166. package/src/core/runtime/runModelStore.ts +55 -0
  1167. package/src/core/runtime/runObservation.ts +24 -0
  1168. package/src/core/runtime/runRecoveryContext.test.ts +602 -0
  1169. package/src/core/runtime/runRecoveryContext.ts +195 -0
  1170. package/src/core/runtime/runRecoveryPlan.test.ts +851 -0
  1171. package/src/core/runtime/runRecoveryPlan.ts +540 -0
  1172. package/src/core/runtime/runRecoveryStore.ts +78 -0
  1173. package/src/core/runtime/runStore.ts +148 -0
  1174. package/src/core/runtime/runStorePath.ts +13 -0
  1175. package/src/core/runtime/runToolStore.ts +66 -0
  1176. package/src/core/runtime/runTools.crash.test.ts +414 -0
  1177. package/src/core/runtime/runTools.test.ts +689 -0
  1178. package/src/core/runtime/runTools.ts +208 -0
  1179. package/src/core/runtime/sqliteControlStore.test.ts +917 -0
  1180. package/src/core/runtime/sqliteControlStore.ts +555 -0
  1181. package/src/core/runtime/sqliteModelStore.test.ts +619 -0
  1182. package/src/core/runtime/sqliteModelStore.ts +286 -0
  1183. package/src/core/runtime/sqliteRecoveryStore.public.test.ts +374 -0
  1184. package/src/core/runtime/sqliteRecoveryStore.test.ts +836 -0
  1185. package/src/core/runtime/sqliteRecoveryStore.ts +435 -0
  1186. package/src/core/runtime/sqliteRunContext.test.ts +1000 -0
  1187. package/src/core/runtime/sqliteRunObservation.test.ts +432 -0
  1188. package/src/core/runtime/sqliteRunStore.test.ts +698 -0
  1189. package/src/core/runtime/sqliteRunStore.ts +593 -0
  1190. package/src/core/runtime/sqliteToolStore.test.ts +1231 -0
  1191. package/src/core/runtime/sqliteToolStore.ts +419 -0
  1192. package/src/core/runtime/testfixtures/localWorkerChild.ts +254 -0
  1193. package/src/core/runtime/testfixtures/localWorkerLauncher.ts +15 -0
  1194. package/src/core/runtime/testfixtures/recoveryChild.ts +159 -0
  1195. package/src/core/services/rateLimiter/index.test.ts +106 -0
  1196. package/src/core/services/rateLimiter/index.ts +157 -0
  1197. package/src/core/services/rateLimiter/types.ts +10 -0
  1198. package/src/core/session/admitted-config.test.ts +50 -0
  1199. package/src/core/session/deprecated-headers-shim.test.ts +111 -0
  1200. package/src/core/session/execution-metrics.test.ts +297 -0
  1201. package/src/core/session/execution-metrics.ts +176 -0
  1202. package/src/core/session/index.ts +1085 -0
  1203. package/src/core/session/loader.ts +212 -0
  1204. package/src/core/session/persistence.test.ts +1210 -0
  1205. package/src/core/session/persistence.ts +1076 -0
  1206. package/src/core/session/rehydrate.test.ts +393 -0
  1207. package/src/core/session/types.ts +139 -0
  1208. package/src/core/session/usage.test.ts +99 -0
  1209. package/src/core/session/usage.ts +84 -0
  1210. package/src/core/skills/builtins/index.ts +22 -0
  1211. package/src/core/skills/builtins/pentest.ts +135 -0
  1212. package/src/core/skills/builtins/promptInjection.ts +44 -0
  1213. package/src/core/skills/builtins/threatModel.ts +336 -0
  1214. package/src/core/skills/index.ts +15 -0
  1215. package/src/core/skills/parser.test.ts +120 -0
  1216. package/src/core/skills/parser.ts +106 -0
  1217. package/src/core/skills/registry.test.ts +202 -0
  1218. package/src/core/skills/registry.ts +109 -0
  1219. package/src/core/skills/scanner.test.ts +157 -0
  1220. package/src/core/skills/scanner.ts +115 -0
  1221. package/src/core/skills/types.ts +45 -0
  1222. package/src/core/skills/utils.ts +16 -0
  1223. package/src/core/storage/index.ts +150 -0
  1224. package/src/core/tasks/index.ts +242 -0
  1225. package/src/core/tools/backends/capture.ts +38 -0
  1226. package/src/core/tools/backends/collectCommand.test.ts +33 -0
  1227. package/src/core/tools/backends/collectCommand.ts +28 -0
  1228. package/src/core/tools/backends/helpers.ts +140 -0
  1229. package/src/core/tools/backends/httpTransport.ts +534 -0
  1230. package/src/core/tools/backends/index.ts +61 -0
  1231. package/src/core/tools/backends/local.test.ts +307 -0
  1232. package/src/core/tools/backends/local.ts +820 -0
  1233. package/src/core/tools/backends/policy.test.ts +137 -0
  1234. package/src/core/tools/backends/policy.ts +129 -0
  1235. package/src/core/tools/backends/resolve.test.ts +309 -0
  1236. package/src/core/tools/backends/resolve.ts +152 -0
  1237. package/src/core/tools/backends/types.ts +279 -0
  1238. package/src/core/tools/backends/whitebox.test.ts +275 -0
  1239. package/src/core/tools/backends/whitebox.ts +14 -0
  1240. package/src/core/tools/backends/whiteboxJobs.test.ts +375 -0
  1241. package/src/core/tools/backends/whiteboxJobs.ts +157 -0
  1242. package/src/core/tools/backends/windowsProgram.ts +36 -0
  1243. package/src/core/tools/daytonaSandbox.test.ts +491 -0
  1244. package/src/core/tools/daytonaSandbox.ts +232 -0
  1245. package/src/core/toolset/index.ts +583 -0
  1246. package/src/core/util/lazyLogger.ts +32 -0
  1247. package/src/core/utils/concurrency.test.ts +204 -0
  1248. package/src/core/utils/concurrency.ts +75 -0
  1249. package/src/core/utils/prompt.ts +28 -0
  1250. package/src/core/whitebox/adapters.ts +277 -0
  1251. package/src/core/whitebox/artifacts.ts +128 -0
  1252. package/src/core/whitebox/boundedProcess.ts +265 -0
  1253. package/src/core/whitebox/candidates.ts +268 -0
  1254. package/src/core/whitebox/catalog.ts +82 -0
  1255. package/src/core/whitebox/index.ts +53 -0
  1256. package/src/core/whitebox/jobKernel.test.ts +91 -0
  1257. package/src/core/whitebox/jobKernel.ts +347 -0
  1258. package/src/core/whitebox/jobSupervisor.ts +264 -0
  1259. package/src/core/whitebox/jobs.ts +21 -0
  1260. package/src/core/whitebox/paths.ts +94 -0
  1261. package/src/core/whitebox/profileTransport.ts +225 -0
  1262. package/src/core/whitebox/profiles.ts +294 -0
  1263. package/src/core/whitebox/repoProfile.test.ts +217 -0
  1264. package/src/core/whitebox/repoProfile.ts +256 -0
  1265. package/src/core/whitebox/types.ts +189 -0
  1266. package/src/core/whitebox/whitebox.test.ts +789 -0
  1267. package/src/core/workflows/fastStrike.seams.test.ts +217 -0
  1268. package/src/core/workflows/fastStrike.test.ts +29 -0
  1269. package/src/core/workflows/fastStrike.ts +239 -0
  1270. package/src/core/workflows/pentest.test.ts +575 -0
  1271. package/src/core/workflows/pentest.ts +1022 -0
  1272. package/src/core/workflows/seams.test.ts +386 -0
  1273. package/src/core/workflows/seams.ts +276 -0
  1274. package/src/core/workflows/threatModel.ts +108 -0
  1275. package/src/core/workflows/usage-forwarding.test.ts +203 -0
  1276. package/src/core/workflows/whiteboxAttackSurface.test.ts +695 -0
  1277. package/src/core/workflows/whiteboxAttackSurface.ts +1740 -0
  1278. package/src/index.ts +49 -0
  1279. package/src/lib/attack-path/types.ts +13 -0
  1280. package/src/lib/cvss/calculator.test.ts +2090 -0
  1281. package/src/lib/cvss/calculator.ts +643 -0
  1282. package/src/lib/cvss/index.ts +65 -0
  1283. package/src/lib/cvss/macrovector-scores.ts +402 -0
  1284. package/src/lib/cvss/types.ts +241 -0
  1285. package/src/lib/cwe/cwe-catalog.ts +444 -0
  1286. package/src/lib/cwe/types.test.ts +142 -0
  1287. package/src/lib/cwe/types.ts +37 -0
  1288. package/src/lib/cwe/validate.test.ts +110 -0
  1289. package/src/lib/cwe/validate.ts +53 -0
  1290. package/src/lib/evidence/types.ts +9 -0
  1291. package/src/tests/agentWorkingDirectory.test.ts +175 -0
  1292. package/src/tests/attackSurface.test.ts +35 -0
  1293. package/src/tests/auth.test.ts +196 -0
  1294. package/src/tests/authCookieExport.test.ts +143 -0
  1295. package/src/tests/authPentestHandoff.test.ts +231 -0
  1296. package/src/tests/browserCookies.test.ts +128 -0
  1297. package/src/tests/credentialManager.test.ts +657 -0
  1298. package/src/tests/data/console.openapi.json +1037 -0
  1299. package/src/tests/data/console.sitemap.xml +348 -0
  1300. package/src/tests/delegateAuthMerge.test.ts +48 -0
  1301. package/src/tui/auto-copy.ts +36 -0
  1302. package/src/tui/clipboard.ts +65 -0
  1303. package/src/tui/command-registry.test.ts +124 -0
  1304. package/src/tui/command-registry.ts +645 -0
  1305. package/src/tui/command-router.ts +81 -0
  1306. package/src/tui/components/agent-display.tsx +480 -0
  1307. package/src/tui/components/ascii-art.tsx +225 -0
  1308. package/src/tui/components/chat/approval-inline.tsx +51 -0
  1309. package/src/tui/components/chat/config-view.tsx +279 -0
  1310. package/src/tui/components/chat/home-view.tsx +288 -0
  1311. package/src/tui/components/chat/index.tsx +161 -0
  1312. package/src/tui/components/chat/input-area.tsx +529 -0
  1313. package/src/tui/components/chat/lib/play-core/num.ts +102 -0
  1314. package/src/tui/components/chat/lib/wave-simulation.ts +124 -0
  1315. package/src/tui/components/chat/loading-indicator.test.ts +33 -0
  1316. package/src/tui/components/chat/loading-indicator.tsx +115 -0
  1317. package/src/tui/components/chat/message-list.tsx +264 -0
  1318. package/src/tui/components/chat/petri-animation.tsx +175 -0
  1319. package/src/tui/components/chat/plan-review-message.tsx +49 -0
  1320. package/src/tui/components/chat/questions-form.tsx +756 -0
  1321. package/src/tui/components/commands/__tests__/recorded-runs-lifecycle.tsx +907 -0
  1322. package/src/tui/components/commands/advanced-settings.tsx +94 -0
  1323. package/src/tui/components/commands/api-key-input.tsx +122 -0
  1324. package/src/tui/components/commands/auth-flow-logic.test.ts +58 -0
  1325. package/src/tui/components/commands/auth-flow-logic.ts +30 -0
  1326. package/src/tui/components/commands/auth-flow.tsx +702 -0
  1327. package/src/tui/components/commands/credits-flow.tsx +210 -0
  1328. package/src/tui/components/commands/help-dialog.tsx +238 -0
  1329. package/src/tui/components/commands/models-display.tsx +111 -0
  1330. package/src/tui/components/commands/operator-wizard.tsx +328 -0
  1331. package/src/tui/components/commands/provider-manager.tsx +241 -0
  1332. package/src/tui/components/commands/provider-selection.tsx +129 -0
  1333. package/src/tui/components/commands/recorded-runs.test.ts +40 -0
  1334. package/src/tui/components/commands/recorded-runs.tsx +793 -0
  1335. package/src/tui/components/commands/sessions-display.tsx +365 -0
  1336. package/src/tui/components/commands/shortcuts-dialog.tsx +52 -0
  1337. package/src/tui/components/commands/skills-dialog.tsx +308 -0
  1338. package/src/tui/components/commands/theme-picker.tsx +177 -0
  1339. package/src/tui/components/commands/web-wizard.tsx +965 -0
  1340. package/src/tui/components/dialog-layout.tsx +73 -0
  1341. package/src/tui/components/error-boundary.tsx +82 -0
  1342. package/src/tui/components/footer.tsx +101 -0
  1343. package/src/tui/components/input.tsx +43 -0
  1344. package/src/tui/components/loaders.tsx +784 -0
  1345. package/src/tui/components/model-picker/ModelPicker.tsx +907 -0
  1346. package/src/tui/components/model-picker/ModelPickerDialog.tsx +75 -0
  1347. package/src/tui/components/model-picker/index.ts +2 -0
  1348. package/src/tui/components/model-picker/model-navigation.test.ts +102 -0
  1349. package/src/tui/components/model-picker/model-navigation.ts +54 -0
  1350. package/src/tui/components/model-picker/model-search.test.ts +62 -0
  1351. package/src/tui/components/model-picker/model-search.ts +42 -0
  1352. package/src/tui/components/model-picker/model-visibility.test.ts +65 -0
  1353. package/src/tui/components/model-picker/model-visibility.ts +59 -0
  1354. package/src/tui/components/operator-dashboard/conversation.test.ts +431 -0
  1355. package/src/tui/components/operator-dashboard/conversation.ts +179 -0
  1356. package/src/tui/components/operator-dashboard/display-state.test.ts +369 -0
  1357. package/src/tui/components/operator-dashboard/display-state.ts +163 -0
  1358. package/src/tui/components/operator-dashboard/herdr-state.test.ts +162 -0
  1359. package/src/tui/components/operator-dashboard/herdr-state.ts +81 -0
  1360. package/src/tui/components/operator-dashboard/index.tsx +2044 -0
  1361. package/src/tui/components/operator-dashboard/logic.test.ts +753 -0
  1362. package/src/tui/components/operator-dashboard/logic.ts +465 -0
  1363. package/src/tui/components/operator-dashboard/queue.test.ts +147 -0
  1364. package/src/tui/components/operator-dashboard/queue.ts +55 -0
  1365. package/src/tui/components/operator-dashboard/queued-messages.tsx +50 -0
  1366. package/src/tui/components/operator-dashboard/run-events.test.ts +913 -0
  1367. package/src/tui/components/operator-dashboard/run-events.ts +507 -0
  1368. package/src/tui/components/operator-dashboard/run-tracing.test.ts +206 -0
  1369. package/src/tui/components/operator-dashboard/run-tracing.ts +105 -0
  1370. package/src/tui/components/operator-dashboard/subagent-detail-view.tsx +51 -0
  1371. package/src/tui/components/operator-dashboard/subagent-dialog.tsx +142 -0
  1372. package/src/tui/components/operator-dashboard/subagent-hub.test.ts +162 -0
  1373. package/src/tui/components/operator-dashboard/subagent-hub.tsx +266 -0
  1374. package/src/tui/components/operator-dashboard/subagent-state.test.ts +431 -0
  1375. package/src/tui/components/operator-dashboard/subagent-state.ts +404 -0
  1376. package/src/tui/components/operator-dashboard/subagent-status-bar.tsx +80 -0
  1377. package/src/tui/components/operator-dashboard/tool-argument-buffer.test.ts +317 -0
  1378. package/src/tui/components/operator-dashboard/tool-arguments.perf.test.ts +121 -0
  1379. package/src/tui/components/operator-dashboard/welcome-logo.tsx +79 -0
  1380. package/src/tui/components/operator-dashboard/workflow-data.test.ts +291 -0
  1381. package/src/tui/components/operator-dashboard/workflow-data.ts +130 -0
  1382. package/src/tui/components/report-viewer-dialog.tsx +98 -0
  1383. package/src/tui/components/responsible-use-disclosure.tsx +55 -0
  1384. package/src/tui/components/screenshot-modal.tsx +204 -0
  1385. package/src/tui/components/shared/__tests__/prompt-input-lifecycle.tsx +64 -0
  1386. package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +69 -0
  1387. package/src/tui/components/shared/action-label.test.ts +133 -0
  1388. package/src/tui/components/shared/action-label.ts +18 -0
  1389. package/src/tui/components/shared/ascii-spinner.tsx +40 -0
  1390. package/src/tui/components/shared/dialog-controls.tsx +76 -0
  1391. package/src/tui/components/shared/index.ts +37 -0
  1392. package/src/tui/components/shared/markdown-viewer.tsx +188 -0
  1393. package/src/tui/components/shared/markdown.ts +239 -0
  1394. package/src/tui/components/shared/message-renderer.tsx +178 -0
  1395. package/src/tui/components/shared/message-utils.ts +175 -0
  1396. package/src/tui/components/shared/pentest-workflow-display.tsx +330 -0
  1397. package/src/tui/components/shared/prompt-input-lifecycle.test.ts +22 -0
  1398. package/src/tui/components/shared/prompt-input-logic.test.ts +782 -0
  1399. package/src/tui/components/shared/prompt-input-logic.ts +423 -0
  1400. package/src/tui/components/shared/prompt-input.tsx +782 -0
  1401. package/src/tui/components/shared/result-registry.test.ts +90 -0
  1402. package/src/tui/components/shared/result-registry.ts +900 -0
  1403. package/src/tui/components/shared/syntax-highlight.ts +242 -0
  1404. package/src/tui/components/shared/tool-registry.test.ts +59 -0
  1405. package/src/tui/components/shared/tool-registry.ts +279 -0
  1406. package/src/tui/components/shared/tool-renderer.test.ts +13 -0
  1407. package/src/tui/components/shared/tool-renderer.tsx +223 -0
  1408. package/src/tui/components/shared/type-guards.ts +64 -0
  1409. package/src/tui/components/shared/use-paste-extmarks.ts +109 -0
  1410. package/src/tui/components/switch.tsx +81 -0
  1411. package/src/tui/components/terminal-focus-handler.tsx +34 -0
  1412. package/src/tui/components/toast.tsx +82 -0
  1413. package/src/tui/components/usage-labels.test.ts +191 -0
  1414. package/src/tui/components/usage-labels.ts +78 -0
  1415. package/src/tui/console-theme.ts +59 -0
  1416. package/src/tui/context/agent.tsx +251 -0
  1417. package/src/tui/context/command.tsx +291 -0
  1418. package/src/tui/context/config.tsx +59 -0
  1419. package/src/tui/context/dialog.tsx +206 -0
  1420. package/src/tui/context/dimensions.tsx +31 -0
  1421. package/src/tui/context/focus-ownership.test.ts +26 -0
  1422. package/src/tui/context/focus-ownership.ts +12 -0
  1423. package/src/tui/context/focus.tsx +86 -0
  1424. package/src/tui/context/herdr.tsx +28 -0
  1425. package/src/tui/context/input.tsx +45 -0
  1426. package/src/tui/context/keybinding.tsx +92 -0
  1427. package/src/tui/context/obfuscation.tsx +114 -0
  1428. package/src/tui/context/route.tsx +127 -0
  1429. package/src/tui/context/session-usage.test.ts +295 -0
  1430. package/src/tui/context/session-usage.ts +204 -0
  1431. package/src/tui/context/session.tsx +62 -0
  1432. package/src/tui/context/toast.tsx +66 -0
  1433. package/src/tui/generated-ascii-art.json +1 -0
  1434. package/src/tui/generated-ascii-art.json.d.ts +2 -0
  1435. package/src/tui/herdr-startup.test.ts +157 -0
  1436. package/src/tui/hooks/use-sessions-list.ts +165 -0
  1437. package/src/tui/index.tsx +838 -0
  1438. package/src/tui/keybindings/index.ts +126 -0
  1439. package/src/tui/keybindings/registry.ts +146 -0
  1440. package/src/tui/keybindings-registry.ts +43 -0
  1441. package/src/tui/obfuscation/patch.ts +139 -0
  1442. package/src/tui/terminal-focus.ts +218 -0
  1443. package/src/tui/theme/colors.ts +43 -0
  1444. package/src/tui/theme/context.tsx +137 -0
  1445. package/src/tui/theme/detect-mode.ts +56 -0
  1446. package/src/tui/theme/index.ts +75 -0
  1447. package/src/tui/theme/registry.ts +29 -0
  1448. package/src/tui/theme/themes/apex.ts +190 -0
  1449. package/src/tui/theme/themes/ayu.ts +189 -0
  1450. package/src/tui/theme/themes/catppuccin.ts +189 -0
  1451. package/src/tui/theme/themes/dracula.ts +190 -0
  1452. package/src/tui/theme/themes/everforest.ts +189 -0
  1453. package/src/tui/theme/themes/flexoki.ts +190 -0
  1454. package/src/tui/theme/themes/github.ts +189 -0
  1455. package/src/tui/theme/themes/gruvbox.ts +189 -0
  1456. package/src/tui/theme/themes/index.ts +54 -0
  1457. package/src/tui/theme/themes/kanagawa.ts +189 -0
  1458. package/src/tui/theme/themes/material.ts +190 -0
  1459. package/src/tui/theme/themes/monokai.ts +190 -0
  1460. package/src/tui/theme/themes/nightfox.ts +189 -0
  1461. package/src/tui/theme/themes/nord.ts +190 -0
  1462. package/src/tui/theme/themes/onedark.ts +190 -0
  1463. package/src/tui/theme/themes/rose-pine.ts +189 -0
  1464. package/src/tui/theme/themes/solarized.ts +189 -0
  1465. package/src/tui/theme/themes/tokyonight.ts +189 -0
  1466. package/src/tui/theme/themes/vesper.ts +191 -0
  1467. package/src/tui/theme/types.ts +143 -0
  1468. package/src/tui/utils/command-flags.test.ts +269 -0
  1469. package/src/tui/utils/command-flags.ts +377 -0
  1470. package/src/tui/utils/kitty-graphics.ts +76 -0
  1471. package/src/tui/utils/open-file.ts +37 -0
  1472. package/src/tui/utils/open-report.ts +34 -0
  1473. package/src/tui/utils/open-url.ts +39 -0
  1474. package/src/tui/utils/paste.ts +8 -0
  1475. package/src/tui/utils/scroll.ts +81 -0
  1476. package/src/util/errors.ts +58 -0
  1477. package/src/util/lock.ts +100 -0
  1478. package/src/util/name.ts +148 -0
  1479. package/src/util/url.test.ts +162 -0
  1480. package/src/util/url.ts +107 -0
  1481. package/build/agent-13rrhp8d.js +0 -20
  1482. package/build/agent-zbj2331s.js +0 -30
  1483. package/build/authentication-f4j3w5vs.js +0 -20
  1484. package/build/blackboxAgent-v2vx9ws4.js +0 -20
  1485. package/build/blackboxPentest-w5nwzpvk.js +0 -70
  1486. package/build/cli-5wemv37a.js +0 -194
  1487. package/build/cli-7s062hr6.js +0 -22
  1488. package/build/cli-9qbxkhye.js +0 -55
  1489. package/build/cli-avmhkjtv.js +0 -22961
  1490. package/build/cli-g3czc3jz.js +0 -204
  1491. package/build/cli-ww7hbazg.js +0 -88
  1492. package/build/index-18vyy6mj.js +0 -19
  1493. package/build/index-5wh7gq37.js +0 -12858
  1494. package/build/index-e23zc8g4.js +0 -34
  1495. package/build/index-hnwhswqj.js +0 -213
  1496. package/build/index-wkmnr4nm.js +0 -39
  1497. package/build/index-ywmfnd30.js +0 -12
  1498. package/build/offesecAgent-c97pqzcn.js +0 -27
  1499. package/build/pentest-4p5jt7gj.js +0 -30
  1500. package/build/pentests-v98eg8n3.js +0 -92
  1501. package/build/threatModel-81d4rcbk.js +0 -29
  1502. /package/build/{cli-c8131c4q.js → cli-0wsh8j2t.js} +0 -0
  1503. /package/build/{cli-9pbk8m32.js → cli-ag6jtndp.js} +0 -0
  1504. /package/build/{cli-e08r86zk.js → cli-b3dvxjgk.js} +0 -0
  1505. /package/build/{cli-fw5r7pfj.js → cli-q126f6ef.js} +0 -0
  1506. /package/build/{cli-9fsre5pt.js → cli-qba2x69h.js} +0 -0
  1507. /package/build/{cli-7q3ae3ft.js → cli-z82n3qk1.js} +0 -0
@@ -0,0 +1,2409 @@
1
+ import { exec } from "node:child_process";
2
+ import {
3
+ mkdirSync,
4
+ mkdtempSync,
5
+ readFileSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import http from "node:http";
10
+ import { createServer as createNetServer } from "node:net";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { asSchema } from "ai";
14
+ import { afterEach, describe, expect, it, vi } from "vitest";
15
+ import {
16
+ promptInjectionRef,
17
+ StaticPromptInjectionLibrary,
18
+ } from "../../../prompt-injections";
19
+ import { RateLimiter } from "../../../services/rateLimiter";
20
+ import type { SessionInfo } from "../../../session";
21
+ import { LocalBackends } from "../../../tools/backends/local";
22
+ import { inProcessSubagentSpawner } from "../subagentSpawner";
23
+ import {
24
+ DEFAULT_HTTP_TIMEOUT_MS,
25
+ type HttpRequestResult,
26
+ httpRequest,
27
+ resolveHttpRequestTimeoutInput,
28
+ } from "./httpRequest";
29
+ import type { UnifiedSandbox } from "./sandbox";
30
+ import type { ToolContext } from "./types";
31
+
32
+ const TEST_LIBRARY = new StaticPromptInjectionLibrary([
33
+ {
34
+ id: "pi.direct.override",
35
+ name: "Direct Override",
36
+ category: "instruction-hijack",
37
+ description: "Safe metadata for a direct override test.",
38
+ tags: ["baseline"],
39
+ deliveryHints: ["json-body"],
40
+ expectedObservation: "The system should preserve hierarchy.",
41
+ payload: "TEST PAYLOAD: direct override",
42
+ },
43
+ ]);
44
+
45
+ function makeCtx(overrides: Partial<ToolContext> = {}): ToolContext {
46
+ return {
47
+ subagentSpawner: inProcessSubagentSpawner,
48
+ session: {
49
+ id: "ses_test",
50
+ version: "1.0.0",
51
+ targets: ["https://example.com"],
52
+ time: { created: Date.now(), updated: Date.now() },
53
+ rootPath: "/tmp/test",
54
+ logsPath: "/tmp/test/logs",
55
+ findingsPath: "/tmp/test/findings",
56
+ scratchpadPath: "/tmp/test/scratchpad",
57
+ pocsPath: "/tmp/test/pocs",
58
+ } as SessionInfo,
59
+ agentCwd: "/tmp/test",
60
+ target: "https://example.com",
61
+ ...overrides,
62
+ };
63
+ }
64
+
65
+ describe("httpRequest prompt injection refs", () => {
66
+ afterEach(() => {
67
+ vi.unstubAllGlobals();
68
+ });
69
+
70
+ it("resolves body refs only at execution time and redacts echoed payloads", async () => {
71
+ const id = "pi.direct.override";
72
+ const payload = TEST_LIBRARY.getPayload(id)!;
73
+ let capturedBody: BodyInit | null | undefined;
74
+
75
+ vi.stubGlobal(
76
+ "fetch",
77
+ vi.fn(async (_url: string, init?: RequestInit) => {
78
+ capturedBody = init?.body;
79
+ return new Response(`server echoed ${String(init?.body)}`, {
80
+ status: 200,
81
+ headers: { "x-echo": String(init?.body) },
82
+ });
83
+ }),
84
+ );
85
+
86
+ const tool = httpRequest(makeCtx({ promptInjectionLibrary: TEST_LIBRARY }));
87
+ const result = (await tool.execute?.(
88
+ {
89
+ url: "https://example.com/chat",
90
+ method: "POST",
91
+ body: promptInjectionRef(id),
92
+ followRedirects: false,
93
+ timeout: 1000,
94
+ toolCallDescription: "Send hidden prompt-injection reference",
95
+ },
96
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
97
+ )) as HttpRequestResult;
98
+
99
+ expect(capturedBody).toBe(payload);
100
+ expect(result.success).toBe(true);
101
+ expect(result.body).toContain(`[PROMPT_INJECTION:${id}]`);
102
+ expect(result.body).not.toContain(payload);
103
+ expect(result.headers["x-echo"]).toBe(`[PROMPT_INJECTION:${id}]`);
104
+ });
105
+
106
+ it("does not resolve inline placeholder strings in request bodies", async () => {
107
+ let capturedBody: BodyInit | null | undefined;
108
+
109
+ vi.stubGlobal(
110
+ "fetch",
111
+ vi.fn(async (_url: string, init?: RequestInit) => {
112
+ capturedBody = init?.body;
113
+ return new Response("ok", { status: 200 });
114
+ }),
115
+ );
116
+
117
+ const tool = httpRequest(makeCtx({ promptInjectionLibrary: TEST_LIBRARY }));
118
+ await tool.execute?.(
119
+ {
120
+ url: "https://example.com/chat",
121
+ method: "POST",
122
+ body: "payload={{prompt_injection:pi.encoded.override}}",
123
+ followRedirects: false,
124
+ timeout: 1000,
125
+ toolCallDescription: "Send literal placeholder text",
126
+ },
127
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
128
+ );
129
+
130
+ expect(capturedBody).toBe(
131
+ "payload={{prompt_injection:pi.encoded.override}}",
132
+ );
133
+ });
134
+ });
135
+
136
+ describe("httpRequest rate limiting", () => {
137
+ afterEach(() => {
138
+ vi.unstubAllGlobals();
139
+ vi.restoreAllMocks();
140
+ });
141
+
142
+ function ctxWithLimiter() {
143
+ const limiter = new RateLimiter({ requestsPerSecond: 5 });
144
+ const acquireSlot = vi
145
+ .spyOn(limiter, "acquireSlot")
146
+ .mockResolvedValue(true);
147
+ const ctx = makeCtx();
148
+ ctx.session._rateLimiter = limiter;
149
+ return { ctx, acquireSlot };
150
+ }
151
+
152
+ it("acquires exactly one rate-limit slot per dispatched request", async () => {
153
+ vi.stubGlobal(
154
+ "fetch",
155
+ vi.fn(async () => new Response("ok", { status: 200 })),
156
+ );
157
+ const { ctx, acquireSlot } = ctxWithLimiter();
158
+
159
+ await httpRequest(ctx).execute?.(
160
+ {
161
+ url: "https://example.com/api",
162
+ method: "GET",
163
+ followRedirects: false,
164
+ timeout: 1000,
165
+ toolCallDescription: "Rate-limited GET",
166
+ },
167
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
168
+ );
169
+
170
+ expect(acquireSlot).toHaveBeenCalledTimes(1);
171
+ });
172
+
173
+ it("does not consume a slot when the request is out of scope", async () => {
174
+ const fetchSpy = vi.fn(async () => new Response("ok", { status: 200 }));
175
+ vi.stubGlobal("fetch", fetchSpy);
176
+ const { ctx, acquireSlot } = ctxWithLimiter();
177
+
178
+ const result = (await httpRequest(ctx).execute?.(
179
+ {
180
+ url: "https://evil.com/",
181
+ method: "GET",
182
+ followRedirects: false,
183
+ timeout: 1000,
184
+ toolCallDescription: "Out-of-scope GET",
185
+ },
186
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
187
+ )) as HttpRequestResult;
188
+
189
+ expect(result.success).toBe(false);
190
+ expect(acquireSlot).not.toHaveBeenCalled();
191
+ expect(fetchSpy).not.toHaveBeenCalled();
192
+ });
193
+
194
+ it("acquires one slot before the sandbox curl dispatch", async () => {
195
+ const { ctx, acquireSlot } = ctxWithLimiter();
196
+ const execute = vi.fn(async () => ({
197
+ success: true,
198
+ exitCode: 0,
199
+ stdout: Buffer.from("HTTP/1.1 200 OK\n\n").toString("base64"),
200
+ stderr: "",
201
+ }));
202
+ ctx.sandbox = { execute } as unknown as ToolContext["sandbox"];
203
+
204
+ const result = (await httpRequest(ctx).execute?.(
205
+ {
206
+ url: "https://example.com/api",
207
+ method: "GET",
208
+ followRedirects: false,
209
+ timeout: 1000,
210
+ toolCallDescription: "Sandbox GET",
211
+ },
212
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
213
+ )) as HttpRequestResult;
214
+
215
+ expect(acquireSlot).toHaveBeenCalledTimes(1);
216
+ expect(execute).toHaveBeenCalledTimes(1);
217
+ expect(result.status).toBe(200);
218
+ });
219
+
220
+ it("deletes the request-body temp file after a sandbox POST", async () => {
221
+ const { ctx } = ctxWithLimiter();
222
+ const commands: string[] = [];
223
+ const execute = vi.fn(async (command: string) => {
224
+ commands.push(command);
225
+ return {
226
+ success: true,
227
+ exitCode: 0,
228
+ stdout: Buffer.from("HTTP/1.1 200 OK\n\n").toString("base64"),
229
+ stderr: "",
230
+ };
231
+ });
232
+ ctx.sandbox = { execute } as unknown as ToolContext["sandbox"];
233
+
234
+ await httpRequest(ctx).execute?.(
235
+ {
236
+ url: "https://example.com/api",
237
+ method: "POST",
238
+ body: "hello=world",
239
+ followRedirects: false,
240
+ timeout: 1000,
241
+ toolCallDescription: "Sandbox POST",
242
+ },
243
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
244
+ );
245
+
246
+ const bodyFile = commands
247
+ .join("\n")
248
+ .match(/\/tmp\/apex_http_body_[^\s"']+\.txt/)?.[0];
249
+ expect(bodyFile).toBeDefined();
250
+ // The temp file is both written (curl --data-binary) and removed.
251
+ expect(commands.some((c) => c.includes(`rm -f ${bodyFile}`))).toBe(true);
252
+ });
253
+
254
+ it("returns an aborted result without dispatching when already aborted", async () => {
255
+ const fetchSpy = vi.fn(async () => new Response("ok", { status: 200 }));
256
+ vi.stubGlobal("fetch", fetchSpy);
257
+ const { ctx } = ctxWithLimiter();
258
+ const controller = new AbortController();
259
+ controller.abort();
260
+ ctx.abortSignal = controller.signal;
261
+
262
+ const result = (await httpRequest(ctx).execute?.(
263
+ {
264
+ url: "https://example.com/api",
265
+ method: "GET",
266
+ followRedirects: false,
267
+ timeout: 1000,
268
+ toolCallDescription: "Aborted GET",
269
+ },
270
+ { toolCallId: "tc_test", messages: [], abortSignal: controller.signal },
271
+ )) as HttpRequestResult;
272
+
273
+ expect(result.success).toBe(false);
274
+ expect(result.error).toContain("aborted");
275
+ expect(fetchSpy).not.toHaveBeenCalled();
276
+ });
277
+ });
278
+
279
+ // Full-lifetime liveness: the request deadline must cover the body read, not
280
+ // just the headers, and the body must be byte-bounded while streaming. All
281
+ // fixtures are synthetic streams — no network.
282
+ describe("httpRequest body liveness", () => {
283
+ const enc = new TextEncoder();
284
+ const scratchDirs: string[] = [];
285
+
286
+ afterEach(() => {
287
+ vi.unstubAllGlobals();
288
+ for (const dir of scratchDirs.splice(0)) {
289
+ rmSync(dir, { recursive: true, force: true });
290
+ }
291
+ });
292
+
293
+ function ctxWithScratchLogs(overrides: Partial<ToolContext> = {}) {
294
+ const scratchDir = mkdtempSync(join(tmpdir(), "apex-httpreq-test-"));
295
+ scratchDirs.push(scratchDir);
296
+ const base = makeCtx();
297
+ return makeCtx({
298
+ ...overrides,
299
+ session: {
300
+ ...base.session,
301
+ rootPath: scratchDir,
302
+ logsPath: join(scratchDir, "logs"),
303
+ } as SessionInfo,
304
+ });
305
+ }
306
+
307
+ function savedPathFrom(body: string): string {
308
+ const m = body.match(/(?:partial|full) response saved to (.+?)\)\./);
309
+ if (!m) {
310
+ throw new Error(`no saved-file pointer in body: ${body.slice(0, 200)}`);
311
+ }
312
+ return m[1];
313
+ }
314
+
315
+ function stalledBodyStream(first: string): ReadableStream<Uint8Array> {
316
+ return new ReadableStream({
317
+ start(c) {
318
+ c.enqueue(enc.encode(first));
319
+ // Never enqueues again and never closes — a stalling body.
320
+ },
321
+ });
322
+ }
323
+
324
+ it("fails with a bounded timeout when the body stalls after headers arrive", async () => {
325
+ vi.stubGlobal(
326
+ "fetch",
327
+ vi.fn(
328
+ async () =>
329
+ new Response(stalledBodyStream("headers-arrived-body-stalls"), {
330
+ status: 200,
331
+ headers: { "content-type": "text/html" },
332
+ }),
333
+ ),
334
+ );
335
+
336
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
337
+ {
338
+ url: "https://example.com/stall",
339
+ method: "GET",
340
+ followRedirects: false,
341
+ timeout: 100,
342
+ toolCallDescription: "GET with a stalling body",
343
+ },
344
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
345
+ )) as HttpRequestResult;
346
+
347
+ expect(result.success).toBe(false);
348
+ expect(result.error).toContain("Request timeout after 100ms");
349
+ // Timeout after partial data: status/headers and the bounded partial body
350
+ // survive, marked incomplete — never discarded, never a clean success.
351
+ expect(result.status).toBe(200);
352
+ expect(result.headers["content-type"]).toBe("text/html");
353
+ expect(result.body).toContain("headers-arrived-body-stalls");
354
+ expect(result.body).toContain("INCOMPLETE");
355
+ expect(result.capture).toMatchObject({
356
+ complete: false,
357
+ stopReason: "timeout",
358
+ capturedBytes: "headers-arrived-body-stalls".length,
359
+ capturedBytesBasis: "raw",
360
+ });
361
+ }, 5_000);
362
+
363
+ it("bounds an endless body at the byte cap, cancels the stream, and still saves the capture", async () => {
364
+ const CAP = 5 * 1024 * 1024;
365
+ const chunk = new Uint8Array(64 * 1024).fill(65); // "A" * 64KiB
366
+ let delivered = 0;
367
+ let cancelled = false;
368
+ const body = new ReadableStream<Uint8Array>({
369
+ pull(c) {
370
+ delivered += chunk.byteLength;
371
+ c.enqueue(chunk);
372
+ },
373
+ cancel() {
374
+ cancelled = true;
375
+ },
376
+ });
377
+ vi.stubGlobal(
378
+ "fetch",
379
+ vi.fn(async () => new Response(body, { status: 200 })),
380
+ );
381
+
382
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
383
+ {
384
+ url: "https://example.com/endless",
385
+ method: "GET",
386
+ followRedirects: false,
387
+ timeout: 30_000,
388
+ toolCallDescription: "GET with an endless body",
389
+ },
390
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
391
+ )) as HttpRequestResult;
392
+
393
+ expect(result.success).toBe(false);
394
+ // A capped capture is not an ordinary success even though the status is
395
+ // 200 — the structured capture says exactly why.
396
+ expect(result.error).toContain("capped");
397
+ expect(result.capture).toMatchObject({
398
+ complete: false,
399
+ stopReason: "byte-cap",
400
+ capturedBytes: CAP,
401
+ capturedBytesBasis: "raw",
402
+ });
403
+ // Streaming cap: the tool stopped reading well before the fixture could
404
+ // deliver an unbounded body (response.text() would never finish here).
405
+ // Slack covers the stream's one-chunk readahead plus the cap-boundary peek.
406
+ expect(delivered).toBeLessThanOrEqual(CAP + 4 * chunk.byteLength);
407
+ // The underlying stream was actually cancelled, not just abandoned.
408
+ expect(cancelled).toBe(true);
409
+ // Incomplete download is explicit to the caller — never "full response saved".
410
+ expect(result.body).toContain("INCOMPLETE");
411
+ expect(result.body).not.toContain("full response saved");
412
+ const saved = readFileSync(savedPathFrom(result.body), "utf-8");
413
+ expect(saved.length).toBe(CAP);
414
+ expect(/^A+$/.test(saved)).toBe(true);
415
+ }, 10_000);
416
+
417
+ it("caps a body delivered as many tiny chunks plus one huge chunk, byte-exactly", async () => {
418
+ const CAP = 5 * 1024 * 1024;
419
+ const tinyCount = 5_000;
420
+ const tiny = new Uint8Array([65]); // "A"
421
+ const huge = new Uint8Array(6 * 1024 * 1024).fill(66); // "B" * 6 MiB
422
+ let cancelled = false;
423
+ let tinyDelivered = 0;
424
+ let hugeDelivered = false;
425
+ const body = new ReadableStream<Uint8Array>({
426
+ pull(c) {
427
+ if (tinyDelivered < tinyCount) {
428
+ tinyDelivered++;
429
+ c.enqueue(tiny);
430
+ } else if (!hugeDelivered) {
431
+ hugeDelivered = true;
432
+ c.enqueue(huge);
433
+ }
434
+ // else: stall — no further chunks, no close.
435
+ },
436
+ cancel() {
437
+ cancelled = true;
438
+ },
439
+ });
440
+ vi.stubGlobal(
441
+ "fetch",
442
+ vi.fn(async () => new Response(body, { status: 200 })),
443
+ );
444
+
445
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
446
+ {
447
+ url: "https://example.com/adversarial",
448
+ method: "GET",
449
+ followRedirects: false,
450
+ timeout: 10_000,
451
+ toolCallDescription: "GET with adversarial chunking",
452
+ },
453
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
454
+ )) as HttpRequestResult;
455
+
456
+ expect(result.success).toBe(false);
457
+ expect(result.capture).toMatchObject({
458
+ complete: false,
459
+ stopReason: "byte-cap",
460
+ capturedBytes: CAP,
461
+ capturedBytesBasis: "raw",
462
+ });
463
+ expect(cancelled).toBe(true);
464
+ // The capture is byte-exact: every tiny chunk landed, then the huge chunk
465
+ // filled the remaining room to the cap — one owned buffer, no loss.
466
+ const saved = readFileSync(savedPathFrom(result.body), "utf-8");
467
+ expect(saved.length).toBe(CAP);
468
+ expect(saved.startsWith("A".repeat(tinyCount))).toBe(true);
469
+ expect(saved.substring(tinyCount)).toBe("B".repeat(CAP - tinyCount));
470
+ }, 10_000);
471
+
472
+ it("completes despite a body stream whose cancel never settles", async () => {
473
+ const chunk = new Uint8Array(64 * 1024).fill(65); // "A" * 64KiB
474
+ let cancelCalled = false;
475
+ const body = new ReadableStream<Uint8Array>({
476
+ pull(c) {
477
+ c.enqueue(chunk);
478
+ },
479
+ cancel() {
480
+ cancelCalled = true;
481
+ return new Promise(() => {}); // never settles
482
+ },
483
+ });
484
+ vi.stubGlobal(
485
+ "fetch",
486
+ vi.fn(async () => new Response(body, { status: 200 })),
487
+ );
488
+
489
+ const started = Date.now();
490
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
491
+ {
492
+ url: "https://example.com/hanging-cancel",
493
+ method: "GET",
494
+ followRedirects: false,
495
+ timeout: 10_000,
496
+ toolCallDescription: "GET whose stream cancel never settles",
497
+ },
498
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
499
+ )) as HttpRequestResult;
500
+
501
+ // The cap path invoked the real cancel but never awaited it — completion
502
+ // must not depend on the stream's cancel promise.
503
+ expect(result.success).toBe(false);
504
+ expect(result.capture).toMatchObject({
505
+ stopReason: "byte-cap",
506
+ capturedBytesBasis: "raw",
507
+ });
508
+ expect(cancelCalled).toBe(true);
509
+ expect(Date.now() - started).toBeLessThan(2_000);
510
+ }, 5_000);
511
+
512
+ it("aborts promptly when the host aborts between headers and body registration", async () => {
513
+ const ac = new AbortController();
514
+ let cancelled = false;
515
+ const body = new ReadableStream<Uint8Array>({
516
+ start(c) {
517
+ c.enqueue(enc.encode("partial"));
518
+ // Never completes on its own.
519
+ },
520
+ cancel() {
521
+ cancelled = true;
522
+ },
523
+ });
524
+ vi.stubGlobal(
525
+ "fetch",
526
+ vi.fn(async () => {
527
+ const res = new Response(body, { status: 200 });
528
+ ac.abort(); // after headers exist, before the body reader registers
529
+ return res;
530
+ }),
531
+ );
532
+
533
+ const result = (await httpRequest(
534
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
535
+ ).execute?.(
536
+ {
537
+ url: "https://example.com/pre-aborted",
538
+ method: "GET",
539
+ followRedirects: false,
540
+ timeout: 10_000,
541
+ toolCallDescription: "GET pre-aborted between headers and body",
542
+ },
543
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
544
+ )) as HttpRequestResult;
545
+
546
+ expect(result.success).toBe(false);
547
+ expect(result.error).toContain("Request aborted by user");
548
+ // Nothing was read before the abort — zero-byte partial, abort cause.
549
+ expect(result.capture).toMatchObject({
550
+ complete: false,
551
+ stopReason: "aborted",
552
+ capturedBytes: 0,
553
+ });
554
+ expect(cancelled).toBe(true);
555
+ }, 5_000);
556
+
557
+ it("does not report success for a curl --max-time cutoff (exit 28) with partial 200 output", async () => {
558
+ // Sandbox adapter contract for the bounded pipeline: the sandbox reports
559
+ // head's exit (0/success); curl's real exit rides the nonce marker.
560
+ const execute = vi.fn(async (command: string) => {
561
+ const nonce = command.match(/__APEX_([0-9a-f]+)_CURL_EXIT_/)?.[1];
562
+ return {
563
+ success: true,
564
+ exitCode: 0,
565
+ stdout: Buffer.from(
566
+ "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\npartial" +
567
+ `\n__APEX_${nonce}_CURL_EXIT_28\n`,
568
+ ).toString("base64"),
569
+ stderr: "",
570
+ };
571
+ });
572
+ const ctx = ctxWithScratchLogs({
573
+ sandbox: { execute } as unknown as ToolContext["sandbox"],
574
+ });
575
+
576
+ const result = (await httpRequest(ctx).execute?.(
577
+ {
578
+ url: "https://example.com/slow-download",
579
+ method: "GET",
580
+ followRedirects: false,
581
+ timeout: 1_000,
582
+ toolCallDescription: "GET cut off by curl --max-time",
583
+ },
584
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
585
+ )) as HttpRequestResult;
586
+
587
+ expect(result.success).toBe(false);
588
+ expect(result.error).toContain("curl exited 28");
589
+ // Partial metadata is retained so the caller sees what actually arrived.
590
+ expect(result.status).toBe(200);
591
+ expect(result.headers["content-type"]).toBe("text/plain");
592
+ expect(result.body).toContain("partial");
593
+ expect(result.body).toContain("INCOMPLETE");
594
+ expect(result.capture).toMatchObject({
595
+ complete: false,
596
+ stopReason: "curl-exit",
597
+ capturedBytes: "partial".length,
598
+ capturedBytesBasis: "decoded",
599
+ });
600
+ });
601
+
602
+ it.each([
603
+ { statusLine: "HTTP/2 200", status: 200, success: true },
604
+ { statusLine: "HTTP/3 404", status: 404, success: false },
605
+ { statusLine: "HTTP/1.1 200", status: 200, success: true },
606
+ ])("parses $statusLine without a reason phrase", async ({
607
+ statusLine,
608
+ status,
609
+ success,
610
+ }) => {
611
+ const execute = vi.fn(async (command: string) => {
612
+ const nonce = command.match(/__APEX_([0-9a-f]+)_CURL_EXIT_/)?.[1];
613
+ return {
614
+ success: true,
615
+ exitCode: 0,
616
+ stdout: Buffer.from(
617
+ `${statusLine}\r\nContent-Type: application/json\r\n\r\n{"status":"ok"}\n__APEX_${nonce}_CURL_EXIT_0\n`,
618
+ ).toString("base64"),
619
+ stderr: "",
620
+ };
621
+ });
622
+ const ctx = ctxWithScratchLogs({
623
+ sandbox: { execute } as unknown as ToolContext["sandbox"],
624
+ });
625
+ const result = (await httpRequest(ctx).execute?.(
626
+ {
627
+ url: "https://example.com/health",
628
+ method: "GET",
629
+ followRedirects: false,
630
+ timeout: 1_000,
631
+ toolCallDescription: "Read a response without a reason phrase",
632
+ },
633
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
634
+ )) as HttpRequestResult;
635
+
636
+ expect(result.success).toBe(success);
637
+ expect(result.status).toBe(status);
638
+ expect(result.statusText).toBe("");
639
+ expect(result.headers["content-type"]).toBe("application/json");
640
+ expect(result.body).toBe('{"status":"ok"}');
641
+ expect(result.capture).toMatchObject({ complete: true, stopReason: "end" });
642
+ });
643
+
644
+ it("preserves raw CRLF bytes in a sandbox response body", async () => {
645
+ const execute = vi.fn(async (command: string) => {
646
+ const nonce = command.match(/__APEX_([0-9a-f]+)_CURL_EXIT_/)?.[1];
647
+ return {
648
+ success: true,
649
+ exitCode: 0,
650
+ stdout: Buffer.from(
651
+ "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nline1\r\nline2\r\n" +
652
+ `\n__APEX_${nonce}_CURL_EXIT_0\n`,
653
+ ).toString("base64"),
654
+ stderr: "",
655
+ };
656
+ });
657
+ const ctx = ctxWithScratchLogs({
658
+ sandbox: { execute } as unknown as ToolContext["sandbox"],
659
+ });
660
+
661
+ const result = (await httpRequest(ctx).execute?.(
662
+ {
663
+ url: "https://example.com/crlf",
664
+ method: "GET",
665
+ followRedirects: false,
666
+ timeout: 1_000,
667
+ toolCallDescription: "Sandbox GET with CRLF body",
668
+ },
669
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
670
+ )) as HttpRequestResult;
671
+
672
+ expect(result.success).toBe(true);
673
+ expect(result.headers["content-type"]).toBe("text/plain");
674
+ // The body keeps its raw bytes — no line-ending normalization.
675
+ expect(result.body).toBe("line1\r\nline2\r\n");
676
+ // Sandbox byte counts are honestly labeled: the decoded output's UTF-8
677
+ // length, including every CRLF byte.
678
+ expect(result.capture).toMatchObject({
679
+ complete: true,
680
+ stopReason: "end",
681
+ capturedBytes: Buffer.byteLength("line1\r\nline2\r\n", "utf-8"),
682
+ capturedBytesBasis: "decoded",
683
+ });
684
+ });
685
+
686
+ it("reports success for a complete sandbox transfer with a clean curl exit marker", async () => {
687
+ const execute = vi.fn(async (command: string) => {
688
+ const nonce = command.match(/__APEX_([0-9a-f]+)_CURL_EXIT_/)?.[1];
689
+ return {
690
+ success: true,
691
+ exitCode: 0,
692
+ stdout: Buffer.from(
693
+ `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nok-body\n__APEX_${nonce}_CURL_EXIT_0\n`,
694
+ ).toString("base64"),
695
+ stderr: "",
696
+ };
697
+ });
698
+ const ctx = ctxWithScratchLogs({
699
+ sandbox: { execute } as unknown as ToolContext["sandbox"],
700
+ });
701
+
702
+ const result = (await httpRequest(ctx).execute?.(
703
+ {
704
+ url: "https://example.com/ok",
705
+ method: "GET",
706
+ followRedirects: false,
707
+ timeout: 1_000,
708
+ toolCallDescription: "Complete sandbox GET",
709
+ },
710
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
711
+ )) as HttpRequestResult;
712
+
713
+ expect(result.success).toBe(true);
714
+ expect(result.error).toBeUndefined();
715
+ expect(result.status).toBe(200);
716
+ expect(result.body).toBe("ok-body");
717
+ });
718
+
719
+ it("treats marker-less sandbox output as capped and incomplete", async () => {
720
+ // head cut the stream at the cap: curl SIGPIPE'd before writing its exit
721
+ // marker, so the output carries no trustworthy exit status.
722
+ const CAP = 5 * 1024 * 1024;
723
+ const headers = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
724
+ const execute = vi.fn(async () => ({
725
+ success: true,
726
+ exitCode: 0,
727
+ stdout: Buffer.from(`${headers}${"A".repeat(CAP)}`).toString("base64"),
728
+ stderr: "",
729
+ }));
730
+ const ctx = ctxWithScratchLogs({
731
+ sandbox: { execute } as unknown as ToolContext["sandbox"],
732
+ });
733
+
734
+ const result = (await httpRequest(ctx).execute?.(
735
+ {
736
+ url: "https://example.com/endless-download",
737
+ method: "GET",
738
+ followRedirects: false,
739
+ timeout: 30_000,
740
+ toolCallDescription: "Sandbox GET capped by head",
741
+ },
742
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
743
+ )) as HttpRequestResult;
744
+
745
+ expect(result.success).toBe(false);
746
+ expect(result.error).toContain("capped");
747
+ expect(result.body).toContain("INCOMPLETE");
748
+ expect(readFileSync(savedPathFrom(result.body), "utf-8").length).toBe(
749
+ CAP - headers.length,
750
+ );
751
+ }, 10_000);
752
+
753
+ it.each([
754
+ { delta: -1, byte: 0x78 },
755
+ { delta: 0, byte: 0x78 },
756
+ { delta: 1, byte: 0x78 },
757
+ { delta: -3 * 1024 * 1024, byte: 0xff },
758
+ { delta: 0, byte: 0xff },
759
+ { delta: 1, byte: 0xff },
760
+ ])("preserves POSIX completion at the capture boundary ($delta bytes, byte=$byte)", async ({
761
+ delta,
762
+ byte,
763
+ }) => {
764
+ const cap = 5 * 1024 * 1024;
765
+ const targetSize = cap + delta;
766
+ const headersFor = (bytes: number) =>
767
+ `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: ${bytes}\r\nConnection: close\r\n\r\n`;
768
+ const bodySize = targetSize - headersFor(targetSize).length;
769
+ const headers = headersFor(bodySize);
770
+ const wire = Buffer.concat([
771
+ Buffer.from(headers),
772
+ Buffer.alloc(bodySize, byte),
773
+ ]);
774
+ expect(wire.length).toBe(targetSize);
775
+ const server = createNetServer((socket) => {
776
+ socket.once("data", () => socket.end(wire));
777
+ });
778
+ await new Promise<void>((resolve) =>
779
+ server.listen(0, "127.0.0.1", resolve),
780
+ );
781
+ const url = `http://127.0.0.1:${(server.address() as { port: number }).port}/boundary`;
782
+ const sandbox = {
783
+ type: "linux",
784
+ execute: (command: string) =>
785
+ new Promise((resolve) => {
786
+ exec(
787
+ command,
788
+ { maxBuffer: 8 * 1024 * 1024, timeout: 15_000 },
789
+ (error, stdout, stderr) => {
790
+ resolve({
791
+ success: !error,
792
+ exitCode: error ? 1 : 0,
793
+ stdout,
794
+ stderr,
795
+ });
796
+ },
797
+ );
798
+ }),
799
+ } as UnifiedSandbox;
800
+ try {
801
+ const base = makeCtx();
802
+ const ctx = ctxWithScratchLogs({
803
+ sandbox,
804
+ target: url,
805
+ session: { ...base.session, targets: [url] },
806
+ });
807
+ const result = (await httpRequest(ctx).execute?.(
808
+ {
809
+ url,
810
+ method: "GET",
811
+ followRedirects: false,
812
+ timeout: 10_000,
813
+ toolCallDescription: "Read a POSIX response at the byte cap",
814
+ },
815
+ { toolCallId: "boundary", messages: [], abortSignal: undefined },
816
+ )) as HttpRequestResult;
817
+ expect(result.success).toBe(delta <= 0);
818
+ expect(result.capture.complete).toBe(delta <= 0);
819
+ expect(result.capture.stopReason).toBe(delta <= 0 ? "end" : "byte-cap");
820
+ const saved = readFileSync(savedPathFrom(result.body), "utf8");
821
+ expect(saved).toBe(
822
+ Buffer.alloc(Math.min(bodySize, cap - headers.length), byte).toString(
823
+ "utf8",
824
+ ),
825
+ );
826
+ } finally {
827
+ await new Promise<void>((resolve) => server.close(() => resolve()));
828
+ }
829
+ }, 20_000);
830
+
831
+ it("stops an oversized producer in the sandbox and returns bounded output (real loopback)", async () => {
832
+ const CAP = 5 * 1024 * 1024;
833
+ let writes = 0;
834
+ let connectionClosed = false;
835
+ const server = http.createServer((_req, res) => {
836
+ res.writeHead(200, { "content-type": "text/plain" }); // chunked
837
+ const timer = setInterval(() => {
838
+ writes++;
839
+ res.write("x".repeat(64 * 1024));
840
+ }, 5);
841
+ res.on("close", () => {
842
+ connectionClosed = true;
843
+ clearInterval(timer);
844
+ });
845
+ });
846
+ await new Promise<void>((resolve) =>
847
+ server.listen(0, "127.0.0.1", resolve),
848
+ );
849
+ const port = (server.address() as { port: number }).port;
850
+ const url = `http://127.0.0.1:${port}/endless`;
851
+
852
+ // Real child-process shell adapter: mirrors the sandbox contract
853
+ // (command → stdout/stderr/exitCode) using the local toolchain.
854
+ const sandbox: UnifiedSandbox = {
855
+ type: "linux",
856
+ execute: (command, opts) =>
857
+ new Promise((resolve) => {
858
+ exec(
859
+ command,
860
+ {
861
+ timeout: (opts?.timeout ?? 30) * 1_000,
862
+ maxBuffer: 16 * 1024 * 1024,
863
+ encoding: "utf8",
864
+ },
865
+ (error, stdout, stderr) => {
866
+ resolve({
867
+ stdout: stdout ?? "",
868
+ stderr: stderr ?? "",
869
+ exitCode:
870
+ typeof error?.code === "number" ? error.code : error ? 1 : 0,
871
+ success: !error,
872
+ });
873
+ },
874
+ );
875
+ }),
876
+ };
877
+
878
+ try {
879
+ const base = makeCtx();
880
+ const ctx = ctxWithScratchLogs({
881
+ sandbox,
882
+ target: url,
883
+ session: {
884
+ ...base.session,
885
+ targets: [url],
886
+ } as SessionInfo,
887
+ });
888
+
889
+ const started = Date.now();
890
+ const result = (await httpRequest(ctx).execute?.(
891
+ {
892
+ url,
893
+ method: "GET",
894
+ followRedirects: false,
895
+ timeout: 20_000,
896
+ toolCallDescription: "Endless loopback download through sandbox curl",
897
+ },
898
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
899
+ )) as HttpRequestResult;
900
+ const elapsed = Date.now() - started;
901
+
902
+ // The call must not hang: the pipeline finishes on its own once head
903
+ // hits the cap and curl SIGPIPEs.
904
+ expect(elapsed).toBeLessThan(10_000);
905
+ // The producer was actually stopped: the connection close event may
906
+ // lag the tool result on the event loop, so wait for it (bounded).
907
+ const closeDeadline = Date.now() + 3_000;
908
+ while (!connectionClosed && Date.now() < closeDeadline) {
909
+ await new Promise((r) => setTimeout(r, 25));
910
+ }
911
+ expect(connectionClosed).toBe(true);
912
+ const writesAtReturn = writes;
913
+ await new Promise((r) => setTimeout(r, 300));
914
+ expect(writes).toBe(writesAtReturn);
915
+ // …and the total produced stayed bounded (cap + socket-buffer slack) —
916
+ // an endless producer with a live consumer would grow without bound.
917
+ expect(writes).toBeLessThan(500);
918
+ // SDK-visible output is bounded by the pipeline, not by post-hoc
919
+ // truncation: the saved capture is exactly at the cap and incomplete.
920
+ expect(result.success).toBe(false);
921
+ expect(result.error).toContain("capped");
922
+ const saved = readFileSync(savedPathFrom(result.body), "utf-8");
923
+ expect(saved.length).toBeLessThanOrEqual(CAP);
924
+ expect(saved.length).toBeGreaterThan(CAP - 64 * 1024);
925
+ } finally {
926
+ server.close();
927
+ }
928
+ }, 30_000);
929
+
930
+ it("reports failure when the body stream errors mid-read", async () => {
931
+ const body = new ReadableStream<Uint8Array>({
932
+ start(c) {
933
+ c.enqueue(enc.encode("partial;"));
934
+ // Error after the first chunk is consumed, so the partial capture
935
+ // is real (error() drops unread queued chunks).
936
+ setTimeout(() => c.error(new Error("synthetic connection reset")), 10);
937
+ },
938
+ });
939
+ vi.stubGlobal(
940
+ "fetch",
941
+ vi.fn(async () => new Response(body, { status: 200 })),
942
+ );
943
+
944
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
945
+ {
946
+ url: "https://example.com/broken",
947
+ method: "GET",
948
+ followRedirects: false,
949
+ timeout: 2_000,
950
+ toolCallDescription: "GET with a broken body stream",
951
+ },
952
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
953
+ )) as HttpRequestResult;
954
+
955
+ expect(result.success).toBe(false);
956
+ expect(result.error).toContain("synthetic connection reset");
957
+ // An ordinary stream failure is error, not a deadline timeout — the
958
+ // bounded partial capture is preserved with its cause.
959
+ expect(result.body).toContain("partial;");
960
+ expect(result.body).toContain("INCOMPLETE");
961
+ expect(result.capture).toMatchObject({
962
+ complete: false,
963
+ stopReason: "error",
964
+ capturedBytes: "partial;".length,
965
+ capturedBytesBasis: "raw",
966
+ });
967
+ });
968
+
969
+ it("fails and cancels the stream when the host aborts mid-body", async () => {
970
+ const ac = new AbortController();
971
+ let cancelled = false;
972
+ const body = new ReadableStream<Uint8Array>({
973
+ start(c) {
974
+ c.enqueue(enc.encode("<html>partial"));
975
+ setTimeout(() => ac.abort(), 50);
976
+ // Body never completes on its own — only the abort can end this read.
977
+ },
978
+ cancel() {
979
+ cancelled = true;
980
+ },
981
+ });
982
+ vi.stubGlobal(
983
+ "fetch",
984
+ vi.fn(async () => new Response(body, { status: 200 })),
985
+ );
986
+
987
+ const result = (await httpRequest(
988
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
989
+ ).execute?.(
990
+ {
991
+ url: "https://example.com/slow",
992
+ method: "GET",
993
+ followRedirects: false,
994
+ timeout: 30_000,
995
+ toolCallDescription: "GET aborted by the host mid-body",
996
+ },
997
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
998
+ )) as HttpRequestResult;
999
+
1000
+ expect(result.success).toBe(false);
1001
+ expect(result.error).toContain("Request aborted by user");
1002
+ // Abort mid-body: the bounded partial capture is preserved.
1003
+ expect(result.body).toContain("<html>partial");
1004
+ expect(result.capture).toMatchObject({
1005
+ complete: false,
1006
+ stopReason: "aborted",
1007
+ capturedBytes: "<html>partial".length,
1008
+ capturedBytesBasis: "raw",
1009
+ });
1010
+ expect(cancelled).toBe(true);
1011
+ }, 5_000);
1012
+
1013
+ it.each([
1014
+ "end",
1015
+ "byte-cap",
1016
+ "timeout",
1017
+ ])("preserves %s capture completeness when the response spill write fails", async (stopReason) => {
1018
+ const ctx = ctxWithScratchLogs();
1019
+ mkdirSync(ctx.session.logsPath, { recursive: true });
1020
+ writeFileSync(join(ctx.session.logsPath, "http-responses"), "occupied");
1021
+ const body = new ReadableStream<Uint8Array>({
1022
+ start(controller) {
1023
+ controller.enqueue(
1024
+ enc.encode(
1025
+ "x".repeat(stopReason === "byte-cap" ? 5 * 1024 * 1024 + 1 : 6_000),
1026
+ ),
1027
+ );
1028
+ if (stopReason !== "timeout") controller.close();
1029
+ },
1030
+ });
1031
+ vi.stubGlobal(
1032
+ "fetch",
1033
+ vi.fn(async () => new Response(body, { status: 200 })),
1034
+ );
1035
+ const result = (await httpRequest(ctx).execute?.(
1036
+ {
1037
+ url: "https://example.com/spill-failure",
1038
+ method: "GET",
1039
+ followRedirects: false,
1040
+ timeout: stopReason === "timeout" ? 20 : 2_000,
1041
+ toolCallDescription: "Read a response whose spill write fails",
1042
+ },
1043
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1044
+ )) as HttpRequestResult;
1045
+
1046
+ expect(result.capture).toMatchObject({
1047
+ complete: stopReason === "end",
1048
+ stopReason,
1049
+ });
1050
+ expect(result.success).toBe(stopReason === "end");
1051
+ expect(result.body.startsWith("x".repeat(5_000))).toBe(true);
1052
+ expect(result.body).toContain("failed to save");
1053
+ expect(result.body).not.toContain("response saved to");
1054
+ if (stopReason === "end") {
1055
+ expect(result.body).toContain("failed to save full response");
1056
+ expect(result.body).not.toContain("INCOMPLETE");
1057
+ } else {
1058
+ expect(result.body).toContain("INCOMPLETE");
1059
+ expect(result.body).toContain(
1060
+ stopReason === "byte-cap"
1061
+ ? "download capped at"
1062
+ : "Request timeout after",
1063
+ );
1064
+ expect(result.body).not.toContain("full response");
1065
+ }
1066
+ });
1067
+
1068
+ it("returns a successful body and preserves inline/save-file truncation", async () => {
1069
+ vi.stubGlobal(
1070
+ "fetch",
1071
+ vi.fn(async () => new Response("ok-body", { status: 200 })),
1072
+ );
1073
+
1074
+ const small = (await httpRequest(ctxWithScratchLogs()).execute?.(
1075
+ {
1076
+ url: "https://example.com/ok",
1077
+ method: "GET",
1078
+ followRedirects: false,
1079
+ timeout: 2_000,
1080
+ toolCallDescription: "Small GET",
1081
+ },
1082
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1083
+ )) as HttpRequestResult;
1084
+
1085
+ expect(small.success).toBe(true);
1086
+ expect(small.body).toBe("ok-body");
1087
+
1088
+ vi.stubGlobal(
1089
+ "fetch",
1090
+ vi.fn(async () => new Response("x".repeat(6_000), { status: 200 })),
1091
+ );
1092
+
1093
+ const large = (await httpRequest(ctxWithScratchLogs()).execute?.(
1094
+ {
1095
+ url: "https://example.com/large",
1096
+ method: "GET",
1097
+ followRedirects: false,
1098
+ timeout: 2_000,
1099
+ toolCallDescription: "GET with a save-file-sized body",
1100
+ },
1101
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1102
+ )) as HttpRequestResult;
1103
+
1104
+ expect(large.success).toBe(true);
1105
+ expect(large.body.startsWith("xxxx")).toBe(true);
1106
+ expect(large.body).toContain("full response saved to");
1107
+ expect(readFileSync(savedPathFrom(large.body), "utf-8")).toBe(
1108
+ "x".repeat(6_000),
1109
+ );
1110
+ // Complete transfer: an ordinary success with complete capture metadata.
1111
+ expect(large.capture).toMatchObject({
1112
+ complete: true,
1113
+ stopReason: "end",
1114
+ capturedBytes: 6_000,
1115
+ capturedBytesBasis: "raw",
1116
+ });
1117
+ });
1118
+
1119
+ it("counts raw bytes exactly for CRLF and multibyte bodies", async () => {
1120
+ const body = "héllo\r\nwörld\r\n€\r\n";
1121
+ vi.stubGlobal(
1122
+ "fetch",
1123
+ vi.fn(
1124
+ async () =>
1125
+ new Response(body, {
1126
+ status: 200,
1127
+ headers: {
1128
+ "content-type": "text/plain; charset=utf-8",
1129
+ "content-length": String(Buffer.byteLength(body, "utf-8")),
1130
+ },
1131
+ }),
1132
+ ),
1133
+ );
1134
+
1135
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1136
+ {
1137
+ url: "https://example.com/counts",
1138
+ method: "GET",
1139
+ followRedirects: false,
1140
+ timeout: 2_000,
1141
+ toolCallDescription: "CRLF + multibyte byte counts",
1142
+ },
1143
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1144
+ )) as HttpRequestResult;
1145
+
1146
+ expect(result.success).toBe(true);
1147
+ // capturedBytes is the raw byte count: CRLF bytes and multibyte UTF-8
1148
+ // sequences counted exactly, not the character count.
1149
+ expect(result.capture).toMatchObject({
1150
+ complete: true,
1151
+ stopReason: "end",
1152
+ capturedBytes: Buffer.byteLength(body, "utf-8"),
1153
+ capturedBytesBasis: "raw",
1154
+ declaredBytes: Buffer.byteLength(body, "utf-8"),
1155
+ });
1156
+ expect(result.capture.capturedBytes).not.toBe(body.length);
1157
+ });
1158
+
1159
+ it("keeps a complete 4xx as success (pre-existing local-path semantics)", async () => {
1160
+ vi.stubGlobal(
1161
+ "fetch",
1162
+ vi.fn(async () => new Response("not found body", { status: 404 })),
1163
+ );
1164
+
1165
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1166
+ {
1167
+ url: "https://example.com/missing",
1168
+ method: "GET",
1169
+ followRedirects: false,
1170
+ timeout: 2_000,
1171
+ toolCallDescription: "complete 404",
1172
+ },
1173
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1174
+ )) as HttpRequestResult;
1175
+
1176
+ // Complete capture stays success:true — only incomplete transfers flip
1177
+ // it. The status itself is already a first-class field.
1178
+ expect(result.success).toBe(true);
1179
+ expect(result.status).toBe(404);
1180
+ expect(result.capture).toMatchObject({
1181
+ complete: true,
1182
+ stopReason: "end",
1183
+ });
1184
+ });
1185
+
1186
+ it("labels an incomplete capture with advertised Content-Length, never as a denominator", async () => {
1187
+ // Endless body: hits the byte cap; note must say advertised, not "of".
1188
+ const chunk = new Uint8Array(64 * 1024).fill(65);
1189
+ const body = new ReadableStream<Uint8Array>({
1190
+ pull(c) {
1191
+ c.enqueue(chunk);
1192
+ },
1193
+ });
1194
+ vi.stubGlobal(
1195
+ "fetch",
1196
+ vi.fn(
1197
+ async () =>
1198
+ new Response(body, {
1199
+ status: 200,
1200
+ headers: { "content-length": "999999999" },
1201
+ }),
1202
+ ),
1203
+ );
1204
+
1205
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1206
+ {
1207
+ url: "https://example.com/advertised",
1208
+ method: "GET",
1209
+ followRedirects: false,
1210
+ timeout: 30_000,
1211
+ toolCallDescription: "cap with advertised length",
1212
+ },
1213
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1214
+ )) as HttpRequestResult;
1215
+
1216
+ expect(result.success).toBe(false);
1217
+ expect(result.body).toContain("advertised Content-Length: 999999999");
1218
+ expect(result.body).not.toMatch(/captured \d+ of \d+/);
1219
+ expect(result.capture).toMatchObject({
1220
+ stopReason: "byte-cap",
1221
+ declaredBytes: 999_999_999,
1222
+ });
1223
+ }, 10_000);
1224
+
1225
+ it("treats a zero-length chunk at the exact cap followed by EOF as complete", async () => {
1226
+ const CAP = 5 * 1024 * 1024;
1227
+ // [cap bytes, empty chunk, EOF]: an empty non-done chunk is not evidence
1228
+ // of more bytes — the body is complete.
1229
+ const full = new Uint8Array(CAP).fill(66); // "B" * cap
1230
+ const body = new ReadableStream<Uint8Array>({
1231
+ start(c) {
1232
+ c.enqueue(full);
1233
+ c.enqueue(new Uint8Array(0));
1234
+ c.close();
1235
+ },
1236
+ });
1237
+ vi.stubGlobal(
1238
+ "fetch",
1239
+ vi.fn(async () => new Response(body, { status: 200 })),
1240
+ );
1241
+
1242
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1243
+ {
1244
+ url: "https://example.com/exact-cap-empty-eof",
1245
+ method: "GET",
1246
+ followRedirects: false,
1247
+ timeout: 5_000,
1248
+ toolCallDescription:
1249
+ "GET whose cap lands exactly at EOF via an empty chunk",
1250
+ },
1251
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1252
+ )) as HttpRequestResult;
1253
+
1254
+ expect(result.success).toBe(true);
1255
+ expect(result.capture).toMatchObject({
1256
+ complete: true,
1257
+ stopReason: "end",
1258
+ capturedBytes: CAP,
1259
+ });
1260
+ expect(result.body.startsWith("BBBB")).toBe(true);
1261
+ }, 10_000);
1262
+
1263
+ it("treats multiple zero-length chunks at the exact cap as still live until EOF", async () => {
1264
+ const CAP = 5 * 1024 * 1024;
1265
+ // [cap, empty, empty, EOF]: every empty is skipped, completion decided
1266
+ // by the eventual EOF only.
1267
+ const full = new Uint8Array(CAP).fill(67); // "C" * cap
1268
+ const body = new ReadableStream<Uint8Array>({
1269
+ start(c) {
1270
+ c.enqueue(full);
1271
+ c.enqueue(new Uint8Array(0));
1272
+ c.enqueue(new Uint8Array(0));
1273
+ c.close();
1274
+ },
1275
+ });
1276
+ vi.stubGlobal(
1277
+ "fetch",
1278
+ vi.fn(async () => new Response(body, { status: 200 })),
1279
+ );
1280
+
1281
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1282
+ {
1283
+ url: "https://example.com/exact-cap-empties-eof",
1284
+ method: "GET",
1285
+ followRedirects: false,
1286
+ timeout: 5_000,
1287
+ toolCallDescription: "GET with empties before EOF at the cap",
1288
+ },
1289
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1290
+ )) as HttpRequestResult;
1291
+
1292
+ expect(result.success).toBe(true);
1293
+ expect(result.capture).toMatchObject({
1294
+ complete: true,
1295
+ stopReason: "end",
1296
+ });
1297
+ }, 10_000);
1298
+
1299
+ it("a nonempty chunk after empties at the exact cap is a byte-cap overflow", async () => {
1300
+ const CAP = 5 * 1024 * 1024;
1301
+ // [cap, empty, nonempty]: the empty is skipped; the nonempty chunk proves
1302
+ // the body exceeded the cap.
1303
+ const full = new Uint8Array(CAP).fill(68); // "D" * cap
1304
+ const body = new ReadableStream<Uint8Array>({
1305
+ start(c) {
1306
+ c.enqueue(full);
1307
+ c.enqueue(new Uint8Array(0));
1308
+ c.enqueue(new Uint8Array([88])); // "X"
1309
+ c.close();
1310
+ },
1311
+ });
1312
+ vi.stubGlobal(
1313
+ "fetch",
1314
+ vi.fn(async () => new Response(body, { status: 200 })),
1315
+ );
1316
+
1317
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1318
+ {
1319
+ url: "https://example.com/exact-cap-empty-overflow",
1320
+ method: "GET",
1321
+ followRedirects: false,
1322
+ timeout: 5_000,
1323
+ toolCallDescription: "GET whose empties precede real overflow",
1324
+ },
1325
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1326
+ )) as HttpRequestResult;
1327
+
1328
+ expect(result.success).toBe(false);
1329
+ expect(result.capture).toMatchObject({
1330
+ complete: false,
1331
+ stopReason: "byte-cap",
1332
+ capturedBytes: CAP,
1333
+ });
1334
+ expect(result.body).toContain("INCOMPLETE");
1335
+ }, 10_000);
1336
+
1337
+ it("a stalled peek at the exact cap dies by the deadline, not as overflow", async () => {
1338
+ const CAP = 5 * 1024 * 1024;
1339
+ // [cap, stall forever]: the next chunk never arrives and never EOfs —
1340
+ // the deadline must classify the outcome, never a hang.
1341
+ const full = new Uint8Array(CAP).fill(69); // "E" * cap
1342
+ const body = new ReadableStream<Uint8Array>({
1343
+ start(c) {
1344
+ c.enqueue(full);
1345
+ // then stall — no further enqueue, no close
1346
+ },
1347
+ });
1348
+ vi.stubGlobal(
1349
+ "fetch",
1350
+ vi.fn(async () => new Response(body, { status: 200 })),
1351
+ );
1352
+
1353
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1354
+ {
1355
+ url: "https://example.com/exact-cap-stall",
1356
+ method: "GET",
1357
+ followRedirects: false,
1358
+ timeout: 100,
1359
+ toolCallDescription: "GET stalling at the exact cap",
1360
+ },
1361
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1362
+ )) as HttpRequestResult;
1363
+
1364
+ expect(result.success).toBe(false);
1365
+ expect(result.error).toContain("Request timeout after 100ms");
1366
+ expect(result.capture.stopReason).toBe("timeout");
1367
+ }, 5_000);
1368
+
1369
+ it("a user abort during the stalled peek reports aborted, not overflow", async () => {
1370
+ const CAP = 5 * 1024 * 1024;
1371
+ const full = new Uint8Array(CAP).fill(70); // "F" * cap
1372
+ const ac = new AbortController();
1373
+ const body = new ReadableStream<Uint8Array>({
1374
+ start(c) {
1375
+ c.enqueue(full);
1376
+ setTimeout(() => ac.abort(), 50);
1377
+ // then stall
1378
+ },
1379
+ });
1380
+ vi.stubGlobal(
1381
+ "fetch",
1382
+ vi.fn(async () => new Response(body, { status: 200 })),
1383
+ );
1384
+
1385
+ const result = (await httpRequest(
1386
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1387
+ ).execute?.(
1388
+ {
1389
+ url: "https://example.com/exact-cap-abort",
1390
+ method: "GET",
1391
+ followRedirects: false,
1392
+ timeout: 30_000,
1393
+ toolCallDescription: "GET aborted at the stalled cap boundary",
1394
+ },
1395
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1396
+ )) as HttpRequestResult;
1397
+
1398
+ expect(result.success).toBe(false);
1399
+ expect(result.error).toContain("Request aborted by user");
1400
+ expect(result.capture.stopReason).toBe("aborted");
1401
+ }, 5_000);
1402
+
1403
+ // --- First terminal outcome vs a same-turn abort (settlement ordering) ---
1404
+ // HWM-0 pull fixtures: each pull serves a PENDING read, so the second pull
1405
+ // fires the terminal event and the host abort in one turn — whichever
1406
+ // settles first is the truthful outcome.
1407
+
1408
+ it("an EOF that settles before a same-turn abort stays complete", async () => {
1409
+ const enc = new TextEncoder();
1410
+ const ac = new AbortController();
1411
+ let reads = 0;
1412
+ const body = new ReadableStream<Uint8Array>(
1413
+ {
1414
+ pull(c) {
1415
+ if (reads++ === 0) {
1416
+ c.enqueue(enc.encode("part-1;"));
1417
+ return;
1418
+ }
1419
+ c.close(); // settles FIRST
1420
+ ac.abort(); // same turn, second — must not overwrite the EOF
1421
+ },
1422
+ },
1423
+ { highWaterMark: 0 },
1424
+ );
1425
+ vi.stubGlobal(
1426
+ "fetch",
1427
+ vi.fn(async () => new Response(body, { status: 200 })),
1428
+ );
1429
+
1430
+ const result = (await httpRequest(
1431
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1432
+ ).execute?.(
1433
+ {
1434
+ url: "https://example.com/eof-then-abort",
1435
+ method: "GET",
1436
+ followRedirects: false,
1437
+ timeout: 5_000,
1438
+ toolCallDescription: "GET whose EOF beats a same-turn abort",
1439
+ },
1440
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1441
+ )) as HttpRequestResult;
1442
+
1443
+ expect(result.success).toBe(true);
1444
+ expect(result.body).toBe("part-1;");
1445
+ expect(result.capture).toMatchObject({
1446
+ complete: true,
1447
+ stopReason: "end",
1448
+ });
1449
+ }, 5_000);
1450
+
1451
+ it("an abort that settles before the stream ends stays incomplete", async () => {
1452
+ const enc = new TextEncoder();
1453
+ const ac = new AbortController();
1454
+ let reads = 0;
1455
+ const body = new ReadableStream<Uint8Array>(
1456
+ {
1457
+ pull(c) {
1458
+ if (reads++ === 0) {
1459
+ c.enqueue(enc.encode("part-1;"));
1460
+ return;
1461
+ }
1462
+ // No close: the abort's reader.cancel() provides the stream end,
1463
+ // and the abort settled first — the capture is incomplete.
1464
+ ac.abort();
1465
+ },
1466
+ },
1467
+ { highWaterMark: 0 },
1468
+ );
1469
+ vi.stubGlobal(
1470
+ "fetch",
1471
+ vi.fn(async () => new Response(body, { status: 200 })),
1472
+ );
1473
+
1474
+ const result = (await httpRequest(
1475
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1476
+ ).execute?.(
1477
+ {
1478
+ url: "https://example.com/abort-first",
1479
+ method: "GET",
1480
+ followRedirects: false,
1481
+ timeout: 5_000,
1482
+ toolCallDescription: "GET whose same-turn abort beats the end",
1483
+ },
1484
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1485
+ )) as HttpRequestResult;
1486
+
1487
+ expect(result.success).toBe(false);
1488
+ expect(result.error).toContain("Request aborted by user");
1489
+ expect(result.capture).toMatchObject({
1490
+ complete: false,
1491
+ stopReason: "aborted",
1492
+ });
1493
+ // Bytes that settled before the abort remain captured evidence.
1494
+ expect(result.body).toContain("part-1;");
1495
+ }, 5_000);
1496
+
1497
+ it("a byte-cap outcome survives an abort racing the cap cancellation", async () => {
1498
+ const CAP = 5 * 1024 * 1024;
1499
+ const chunk = new Uint8Array(64 * 1024).fill(65); // "A" * 64KiB
1500
+ const ac = new AbortController();
1501
+ const body = new ReadableStream<Uint8Array>(
1502
+ {
1503
+ pull(c) {
1504
+ c.enqueue(chunk);
1505
+ },
1506
+ cancel() {
1507
+ // truncatedAtCap cancels the reader AFTER assigning byte-cap; the
1508
+ // abort firing here must not overwrite the cap outcome.
1509
+ ac.abort();
1510
+ },
1511
+ },
1512
+ { highWaterMark: 0 },
1513
+ );
1514
+ vi.stubGlobal(
1515
+ "fetch",
1516
+ vi.fn(async () => new Response(body, { status: 200 })),
1517
+ );
1518
+
1519
+ const result = (await httpRequest(
1520
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1521
+ ).execute?.(
1522
+ {
1523
+ url: "https://example.com/cap-then-abort",
1524
+ method: "GET",
1525
+ followRedirects: false,
1526
+ timeout: 30_000,
1527
+ toolCallDescription: "GET capped before a racing abort",
1528
+ },
1529
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1530
+ )) as HttpRequestResult;
1531
+
1532
+ expect(result.success).toBe(false);
1533
+ expect(result.error).toContain("capped");
1534
+ expect(result.capture).toMatchObject({
1535
+ complete: false,
1536
+ stopReason: "byte-cap",
1537
+ capturedBytes: CAP,
1538
+ });
1539
+ }, 10_000);
1540
+
1541
+ it("a stream error retains its outcome when the signal aborts in the same turn", async () => {
1542
+ const enc = new TextEncoder();
1543
+ const ac = new AbortController();
1544
+ let reads = 0;
1545
+ const body = new ReadableStream<Uint8Array>(
1546
+ {
1547
+ pull(c) {
1548
+ if (reads++ === 0) {
1549
+ c.enqueue(enc.encode("part-1;"));
1550
+ return;
1551
+ }
1552
+ c.error(new TypeError("synthetic connection reset")); // settles FIRST
1553
+ ac.abort(); // must not overwrite the error
1554
+ },
1555
+ },
1556
+ { highWaterMark: 0 },
1557
+ );
1558
+ vi.stubGlobal(
1559
+ "fetch",
1560
+ vi.fn(async () => new Response(body, { status: 200 })),
1561
+ );
1562
+
1563
+ const result = (await httpRequest(
1564
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1565
+ ).execute?.(
1566
+ {
1567
+ url: "https://example.com/error-then-abort",
1568
+ method: "GET",
1569
+ followRedirects: false,
1570
+ timeout: 5_000,
1571
+ toolCallDescription: "GET whose stream error beats the abort",
1572
+ },
1573
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1574
+ )) as HttpRequestResult;
1575
+
1576
+ expect(result.success).toBe(false);
1577
+ expect(result.error).toContain("synthetic connection reset");
1578
+ expect(result.capture).toMatchObject({
1579
+ complete: false,
1580
+ stopReason: "error",
1581
+ });
1582
+ }, 5_000);
1583
+
1584
+ it("an abort settling before a same-turn stream error stays aborted", async () => {
1585
+ const enc = new TextEncoder();
1586
+ const ac = new AbortController();
1587
+ let reads = 0;
1588
+ const body = new ReadableStream<Uint8Array>(
1589
+ {
1590
+ pull(c) {
1591
+ if (reads++ === 0) {
1592
+ c.enqueue(enc.encode("part-1;"));
1593
+ return;
1594
+ }
1595
+ ac.abort(); // settles FIRST
1596
+ c.error(new TypeError("synthetic connection reset"));
1597
+ },
1598
+ },
1599
+ { highWaterMark: 0 },
1600
+ );
1601
+ vi.stubGlobal(
1602
+ "fetch",
1603
+ vi.fn(async () => new Response(body, { status: 200 })),
1604
+ );
1605
+
1606
+ const result = (await httpRequest(
1607
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1608
+ ).execute?.(
1609
+ {
1610
+ url: "https://example.com/abort-then-error",
1611
+ method: "GET",
1612
+ followRedirects: false,
1613
+ timeout: 5_000,
1614
+ toolCallDescription: "GET whose abort beats a same-turn error",
1615
+ },
1616
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1617
+ )) as HttpRequestResult;
1618
+
1619
+ expect(result.success).toBe(false);
1620
+ expect(result.error).toContain("Request aborted by user");
1621
+ expect(result.capture).toMatchObject({
1622
+ complete: false,
1623
+ stopReason: "aborted",
1624
+ });
1625
+ }, 5_000);
1626
+
1627
+ // --- Abort classification is identity-based, not name-based ---
1628
+
1629
+ it("an unrelated AbortError without a host abort stays an error", async () => {
1630
+ const enc = new TextEncoder();
1631
+ let reads = 0;
1632
+ // An AbortError-shaped error that is NOT this signal's reason — e.g. an
1633
+ // adapter-internal cancellation — must not be relabeled as our abort.
1634
+ const unrelated = new Error("adapter-internal cancellation");
1635
+ unrelated.name = "AbortError";
1636
+ const body = new ReadableStream<Uint8Array>(
1637
+ {
1638
+ pull(c) {
1639
+ if (reads++ === 0) {
1640
+ c.enqueue(enc.encode("part-1;"));
1641
+ return;
1642
+ }
1643
+ c.error(unrelated);
1644
+ },
1645
+ },
1646
+ { highWaterMark: 0 },
1647
+ );
1648
+ vi.stubGlobal(
1649
+ "fetch",
1650
+ vi.fn(async () => new Response(body, { status: 200 })),
1651
+ );
1652
+
1653
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1654
+ {
1655
+ url: "https://example.com/unrelated-abort-error",
1656
+ method: "GET",
1657
+ followRedirects: false,
1658
+ timeout: 5_000,
1659
+ toolCallDescription: "GET with an unrelated AbortError",
1660
+ },
1661
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1662
+ )) as HttpRequestResult;
1663
+
1664
+ expect(result.success).toBe(false);
1665
+ expect(result.error).toContain("adapter-internal cancellation");
1666
+ expect(result.capture).toMatchObject({
1667
+ complete: false,
1668
+ stopReason: "error",
1669
+ });
1670
+ }, 5_000);
1671
+
1672
+ it("an unrelated AbortError stays an error even when the host aborts later", async () => {
1673
+ const enc = new TextEncoder();
1674
+ const ac = new AbortController();
1675
+ let reads = 0;
1676
+ const unrelated = new Error("adapter-internal cancellation");
1677
+ unrelated.name = "AbortError";
1678
+ const body = new ReadableStream<Uint8Array>(
1679
+ {
1680
+ pull(c) {
1681
+ if (reads++ === 0) {
1682
+ c.enqueue(enc.encode("part-1;"));
1683
+ return;
1684
+ }
1685
+ c.error(unrelated); // settles FIRST
1686
+ ac.abort(); // a later host abort must not relabel the cause
1687
+ },
1688
+ },
1689
+ { highWaterMark: 0 },
1690
+ );
1691
+ vi.stubGlobal(
1692
+ "fetch",
1693
+ vi.fn(async () => new Response(body, { status: 200 })),
1694
+ );
1695
+
1696
+ const result = (await httpRequest(
1697
+ ctxWithScratchLogs({ abortSignal: ac.signal }),
1698
+ ).execute?.(
1699
+ {
1700
+ url: "https://example.com/unrelated-abort-then-host-abort",
1701
+ method: "GET",
1702
+ followRedirects: false,
1703
+ timeout: 5_000,
1704
+ toolCallDescription:
1705
+ "GET with an unrelated AbortError before a host abort",
1706
+ },
1707
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1708
+ )) as HttpRequestResult;
1709
+
1710
+ expect(result.success).toBe(false);
1711
+ expect(result.error).toContain("adapter-internal cancellation");
1712
+ expect(result.capture).toMatchObject({
1713
+ complete: false,
1714
+ stopReason: "error",
1715
+ });
1716
+ }, 5_000);
1717
+
1718
+ // Real fetch against a local loopback server: the native abort machinery
1719
+ // rejects the body with the signal's own reason object — classification
1720
+ // must match by identity. The barrier is a spy on the NATIVE body reader's
1721
+ // read(): the first read captures the 19-byte prefix, the second read
1722
+ // invocation fires the abort mid-consumption and returns the original
1723
+ // pending native read. The Response, its body, and reader.closed stay
1724
+ // untouched — no transform substitute.
1725
+ const NATIVE_PREFIX = "streaming-body-start"; // 20 bytes
1726
+
1727
+ async function runNativeAbortCase(reason: undefined | Error) {
1728
+ const server = http.createServer((_req, res) => {
1729
+ res.writeHead(200, { "content-type": "text/plain" });
1730
+ res.write(NATIVE_PREFIX);
1731
+ // Keep the connection open — only the abort ends this read.
1732
+ });
1733
+ await new Promise<void>((resolve) =>
1734
+ server.listen(0, "127.0.0.1", resolve),
1735
+ );
1736
+ const port = (server.address() as { port: number }).port;
1737
+ const url = `http://127.0.0.1:${port}/endless`;
1738
+ const ac = new AbortController();
1739
+
1740
+ try {
1741
+ // Truly-native fetch: clear any prior stubs BEFORE capturing.
1742
+ vi.unstubAllGlobals();
1743
+ const realFetch = globalThis.fetch;
1744
+ let readCalls = 0;
1745
+ vi.stubGlobal(
1746
+ "fetch",
1747
+ vi.fn(async (...args: Parameters<typeof realFetch>) => {
1748
+ const res = await realFetch(...args);
1749
+ if (res.body === null) throw new Error("native body missing");
1750
+ const body = res.body;
1751
+ const origGetReader = body.getReader.bind(body);
1752
+ Object.defineProperty(body, "getReader", {
1753
+ value: () => {
1754
+ const reader = origGetReader();
1755
+ const origRead = reader.read.bind(reader);
1756
+ Object.defineProperty(reader, "read", {
1757
+ value: () => {
1758
+ readCalls++;
1759
+ const pending = origRead();
1760
+ if (readCalls === 2) {
1761
+ // The prefix is already captured by the first read —
1762
+ // abort mid-consumption, handing back the native pending
1763
+ // read.
1764
+ ac.abort(reason);
1765
+ }
1766
+ return pending;
1767
+ },
1768
+ });
1769
+ return reader;
1770
+ },
1771
+ });
1772
+ return res;
1773
+ }),
1774
+ );
1775
+
1776
+ const base = makeCtx();
1777
+ const ctx = ctxWithScratchLogs({
1778
+ abortSignal: ac.signal,
1779
+ target: url,
1780
+ session: {
1781
+ ...base.session,
1782
+ targets: [url],
1783
+ } as SessionInfo,
1784
+ });
1785
+ const result = (await httpRequest(ctx).execute?.(
1786
+ {
1787
+ url,
1788
+ method: "GET",
1789
+ followRedirects: false,
1790
+ timeout: 30_000,
1791
+ toolCallDescription: "GET cancelled natively mid-body",
1792
+ },
1793
+ { toolCallId: "tc_test", messages: [], abortSignal: ac.signal },
1794
+ )) as HttpRequestResult;
1795
+
1796
+ return { result, readCalls };
1797
+ } finally {
1798
+ // Bounded teardown: force the abort (a regression may exit before the
1799
+ // read barrier fires), destroy any still-open connection, then await
1800
+ // the server's close before unstubbing.
1801
+ ac.abort();
1802
+ server.closeAllConnections();
1803
+ await new Promise<void>((resolve) => server.close(() => resolve()));
1804
+ vi.unstubAllGlobals();
1805
+ }
1806
+ }
1807
+
1808
+ it("a native fetch cancellation with the default reason is classified aborted mid-body", async () => {
1809
+ const { result, readCalls } = await runNativeAbortCase(undefined);
1810
+
1811
+ // The abort fired on the second native read — after the prefix was
1812
+ // captured, during body consumption (never before headers).
1813
+ expect(readCalls).toBeGreaterThanOrEqual(2);
1814
+ expect(result.success).toBe(false);
1815
+ expect(result.status).toBe(200);
1816
+ expect(result.error).toContain("Request aborted by user");
1817
+ expect(result.capture).toMatchObject({
1818
+ complete: false,
1819
+ stopReason: "aborted",
1820
+ });
1821
+ expect(result.body).toContain(NATIVE_PREFIX);
1822
+ expect(result.capture.capturedBytes).toBe(NATIVE_PREFIX.length);
1823
+ }, 15_000);
1824
+
1825
+ it("a native fetch cancellation with a custom reason object is classified aborted by identity", async () => {
1826
+ const { result, readCalls } = await runNativeAbortCase(
1827
+ new Error("host cancelled"),
1828
+ );
1829
+
1830
+ expect(readCalls).toBeGreaterThanOrEqual(2);
1831
+ expect(result.success).toBe(false);
1832
+ expect(result.status).toBe(200);
1833
+ expect(result.capture).toMatchObject({
1834
+ complete: false,
1835
+ stopReason: "aborted",
1836
+ });
1837
+ expect(result.body).toContain(NATIVE_PREFIX);
1838
+ expect(result.capture.capturedBytes).toBe(NATIVE_PREFIX.length);
1839
+ }, 15_000);
1840
+
1841
+ it("an unexpected processing failure propagates instead of hanging or faking EOF", async () => {
1842
+ const enc = new TextEncoder();
1843
+ let reads = 0;
1844
+ let cancelled = false;
1845
+ const body = new ReadableStream<Uint8Array>(
1846
+ {
1847
+ pull(c) {
1848
+ if (reads++ === 0) {
1849
+ c.enqueue(enc.encode("part-1;"));
1850
+ return;
1851
+ }
1852
+ c.enqueue({
1853
+ byteLength: 4,
1854
+ subarray: undefined,
1855
+ } as unknown as Uint8Array);
1856
+ },
1857
+ cancel() {
1858
+ cancelled = true;
1859
+ },
1860
+ },
1861
+ { highWaterMark: 0 },
1862
+ );
1863
+ vi.stubGlobal(
1864
+ "fetch",
1865
+ vi.fn(async () => new Response(body, { status: 200 })),
1866
+ );
1867
+
1868
+ const result = (await httpRequest(ctxWithScratchLogs()).execute?.(
1869
+ {
1870
+ url: "https://example.com/processing-failure",
1871
+ method: "GET",
1872
+ followRedirects: false,
1873
+ timeout: 5_000,
1874
+ toolCallDescription: "GET whose chunk processing fails",
1875
+ },
1876
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1877
+ )) as HttpRequestResult;
1878
+
1879
+ expect(result.success).toBe(false);
1880
+ expect(result.error).toContain("subarray");
1881
+ expect(result.capture.stopReason).toBe("error");
1882
+ // The open stream was actually cancelled, not merely lock-released.
1883
+ expect(cancelled).toBe(true);
1884
+ }, 5_000);
1885
+
1886
+ // --- Windows sandbox boundary (mocked helper contract; runs on all OS) ---
1887
+
1888
+ type SandboxExecuteCall = {
1889
+ command: string;
1890
+ opts?: { timeout?: number; envVars?: Record<string, string>; cwd?: string };
1891
+ };
1892
+
1893
+ function windowsSandboxMock(
1894
+ respond: (call: SandboxExecuteCall) => {
1895
+ stdout: string;
1896
+ stderr?: string;
1897
+ exitCode: number;
1898
+ success: boolean;
1899
+ },
1900
+ ): { execute: ReturnType<typeof vi.fn>; calls: SandboxExecuteCall[] } {
1901
+ const calls: SandboxExecuteCall[] = [];
1902
+ const execute = vi.fn(async (command: string, opts?: unknown) => {
1903
+ const call = { command, opts: opts as SandboxExecuteCall["opts"] };
1904
+ calls.push(call);
1905
+ const r = respond(call);
1906
+ return {
1907
+ success: r.success,
1908
+ exitCode: r.exitCode,
1909
+ stdout: r.stdout,
1910
+ stderr: r.stderr ?? "",
1911
+ };
1912
+ });
1913
+ return { execute, calls };
1914
+ }
1915
+
1916
+ const windowsCtx = (execute: ReturnType<typeof vi.fn>): ToolContext =>
1917
+ ctxWithScratchLogs({
1918
+ sandbox: {
1919
+ type: "windows",
1920
+ execute,
1921
+ } as unknown as ToolContext["sandbox"],
1922
+ });
1923
+
1924
+ const callWindowsTool = async (
1925
+ ctx: ToolContext,
1926
+ input: {
1927
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
1928
+ body?: string;
1929
+ },
1930
+ ): Promise<HttpRequestResult> =>
1931
+ (await httpRequest(ctx).execute?.(
1932
+ {
1933
+ url: "https://example.com/api",
1934
+ followRedirects: false,
1935
+ timeout: 1_000,
1936
+ toolCallDescription: "Windows sandbox boundary",
1937
+ ...input,
1938
+ },
1939
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
1940
+ )) as HttpRequestResult;
1941
+
1942
+ it("windows sandbox: complete GET runs the helper command with env, one execute call", async () => {
1943
+ const { execute, calls } = windowsSandboxMock((call) => {
1944
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
1945
+ return {
1946
+ success: true,
1947
+ exitCode: 0,
1948
+ stdout: `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nok-body\n${marker}0\n`,
1949
+ };
1950
+ });
1951
+ const result = await callWindowsTool(windowsCtx(execute), {
1952
+ method: "GET",
1953
+ });
1954
+
1955
+ expect(result.success).toBe(true);
1956
+ expect(result.body).toBe("ok-body");
1957
+ expect(result.capture).toMatchObject({
1958
+ complete: true,
1959
+ stopReason: "end",
1960
+ });
1961
+ // Exactly one dispatch: no POSIX printf staging, no rm cleanup.
1962
+ expect(calls).toHaveLength(1);
1963
+ expect(calls[0]?.command).toContain("powershell.exe");
1964
+ // Request data travels in envVars, including resolved headers in argv.
1965
+ const env = calls[0]?.opts?.envVars ?? {};
1966
+ expect(env.APEX_HTTP_CURL_ARGS).toContain("-X");
1967
+ expect(env.APEX_HTTP_CURL_ARGS).toContain("GET");
1968
+ expect(env.APEX_HTTP_CURL_ARGS).toContain("https://example.com/api");
1969
+ expect(Number(env.APEX_HTTP_MAX_BYTES)).toBe(5 * 1024 * 1024);
1970
+ expect(env.APEX_HTTP_MARKER).toMatch(/^__APEX_[0-9a-f]+_CURL_EXIT_$/);
1971
+ // Chunked body contract: count/length always set, both 0 with no body.
1972
+ expect(env.APEX_HTTP_BODY_COUNT).toBe("0");
1973
+ expect(env.APEX_HTTP_BODY_LENGTH).toBe("0");
1974
+ expect(env.APEX_HTTP_BODY_0).toBeUndefined();
1975
+ expect(env.APEX_HTTP_BODY).toBeUndefined();
1976
+ });
1977
+
1978
+ it("windows sandbox: POST forwards the body as base64 env with no POSIX setup or cleanup", async () => {
1979
+ const { execute, calls } = windowsSandboxMock((call) => {
1980
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
1981
+ return {
1982
+ success: true,
1983
+ exitCode: 0,
1984
+ stdout: `HTTP/1.1 201 Created\r\nContent-Type: text/plain\r\n\r\ncreated\n${marker}0\n`,
1985
+ };
1986
+ });
1987
+ const result = await callWindowsTool(windowsCtx(execute), {
1988
+ method: "POST",
1989
+ body: "hello=windows",
1990
+ });
1991
+
1992
+ expect(result.success).toBe(true);
1993
+ expect(result.status).toBe(201);
1994
+ expect(calls).toHaveLength(1);
1995
+ // No /tmp staging write and no rm cleanup were dispatched.
1996
+ expect(calls[0]?.command).not.toContain("/tmp");
1997
+ expect(calls.join(" ")).not.toContain("rm -f");
1998
+ // The body rides as chunked base64 env (small body: one chunk) for the
1999
+ // helper's own temp-file staging; the legacy single var is gone.
2000
+ const env = calls[0]?.opts?.envVars ?? {};
2001
+ const encoded = Buffer.from("hello=windows", "utf8").toString("base64");
2002
+ expect(env.APEX_HTTP_BODY_COUNT).toBe("1");
2003
+ expect(env.APEX_HTTP_BODY_LENGTH).toBe(String(encoded.length));
2004
+ expect(env.APEX_HTTP_BODY_0).toBe(encoded);
2005
+ expect(env.APEX_HTTP_BODY_1).toBeUndefined();
2006
+ expect(env.APEX_HTTP_BODY).toBeUndefined();
2007
+ });
2008
+
2009
+ it("windows sandbox: marker-less output is classified as a cap", async () => {
2010
+ const { execute } = windowsSandboxMock(() => ({
2011
+ success: true,
2012
+ exitCode: 0,
2013
+ stdout:
2014
+ "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\npartial-until-cap",
2015
+ }));
2016
+ const result = await callWindowsTool(windowsCtx(execute), {
2017
+ method: "GET",
2018
+ });
2019
+
2020
+ expect(result.success).toBe(false);
2021
+ expect(result.capture).toMatchObject({
2022
+ complete: false,
2023
+ stopReason: "byte-cap",
2024
+ });
2025
+ expect(result.body).toContain("INCOMPLETE");
2026
+ });
2027
+
2028
+ it("windows sandbox: nonzero helper exit surfaces redacted stderr alongside the partial response", async () => {
2029
+ const { execute } = windowsSandboxMock(() => ({
2030
+ success: false,
2031
+ exitCode: 2,
2032
+ stdout:
2033
+ "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\npartial-before-failure",
2034
+ stderr: "powershell wrapper failed mid-transfer",
2035
+ }));
2036
+ const result = await callWindowsTool(windowsCtx(execute), {
2037
+ method: "GET",
2038
+ });
2039
+
2040
+ expect(result.success).toBe(false);
2041
+ expect(result.capture).toMatchObject({
2042
+ complete: false,
2043
+ stopReason: "sandbox-exec",
2044
+ });
2045
+ // Native helper stderr is surfaced in the failure diagnostics, not suppressed.
2046
+ expect(result.error).toContain("powershell wrapper failed mid-transfer");
2047
+ expect(result.body).toContain("partial-before-failure");
2048
+ });
2049
+
2050
+ it("windows sandbox: curl nonzero with a partial 200 stays incomplete", async () => {
2051
+ const { execute } = windowsSandboxMock((call) => {
2052
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
2053
+ return {
2054
+ success: true,
2055
+ exitCode: 0,
2056
+ stdout: `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\npartial-download\n${marker}28\n`,
2057
+ };
2058
+ });
2059
+ const result = await callWindowsTool(windowsCtx(execute), {
2060
+ method: "GET",
2061
+ });
2062
+
2063
+ expect(result.success).toBe(false);
2064
+ expect(result.status).toBe(200);
2065
+ expect(result.capture).toMatchObject({
2066
+ complete: false,
2067
+ stopReason: "curl-exit",
2068
+ });
2069
+ expect(result.body).toContain("partial-download");
2070
+ expect(result.body).toContain("INCOMPLETE");
2071
+ });
2072
+
2073
+ it("windows sandbox: execute timeout includes PowerShell startup and cleanup headroom", async () => {
2074
+ const { execute, calls } = windowsSandboxMock((call) => {
2075
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
2076
+ return {
2077
+ success: true,
2078
+ exitCode: 0,
2079
+ stdout: `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nok\n${marker}0\n`,
2080
+ };
2081
+ });
2082
+ // 40s deadline: +10s Windows headroom must show past the 30s floor.
2083
+ await httpRequest(windowsCtx(execute)).execute?.(
2084
+ {
2085
+ url: "https://example.com/api",
2086
+ method: "GET",
2087
+ followRedirects: false,
2088
+ timeout: 40_000,
2089
+ toolCallDescription: "Windows timeout headroom",
2090
+ },
2091
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
2092
+ );
2093
+
2094
+ expect(calls.at(-1)?.opts?.timeout).toBe(55);
2095
+ });
2096
+
2097
+ it("windows sandbox: a negative curl exit marker is curl-exit, not byte-cap", async () => {
2098
+ const { execute } = windowsSandboxMock((call) => {
2099
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
2100
+ return {
2101
+ success: true,
2102
+ exitCode: 0,
2103
+ stdout: `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nterminated-output\n${marker}-1073741519\n`,
2104
+ };
2105
+ });
2106
+ const result = await callWindowsTool(windowsCtx(execute), {
2107
+ method: "GET",
2108
+ });
2109
+
2110
+ expect(result.success).toBe(false);
2111
+ expect(result.capture).toMatchObject({
2112
+ complete: false,
2113
+ stopReason: "curl-exit",
2114
+ });
2115
+ expect(result.error).toContain("-1073741519");
2116
+ });
2117
+
2118
+ it("windows sandbox: curl-exit failures surface native curl stderr diagnostics", async () => {
2119
+ const { execute } = windowsSandboxMock((call) => {
2120
+ const marker = call.opts?.envVars?.APEX_HTTP_MARKER ?? "";
2121
+ return {
2122
+ success: true,
2123
+ exitCode: 0,
2124
+ stdout: `HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\npartial\n${marker}28\n`,
2125
+ stderr: "curl: (28) Operation timed out",
2126
+ };
2127
+ });
2128
+ const result = await callWindowsTool(windowsCtx(execute), {
2129
+ method: "GET",
2130
+ });
2131
+
2132
+ expect(result.success).toBe(false);
2133
+ expect(result.capture).toMatchObject({ stopReason: "curl-exit" });
2134
+ // --show-error diagnostics ride stderr — surfaced, not suppressed.
2135
+ expect(result.error).toContain("curl: (28) Operation timed out");
2136
+ });
2137
+
2138
+ it("linux sandbox: execute timeout keeps the existing floor (no Windows headroom)", async () => {
2139
+ const calls: { opts?: { timeout?: number } }[] = [];
2140
+ const execute = vi.fn(async (_command: string, opts?: unknown) => {
2141
+ calls.push({ opts: opts as { timeout?: number } });
2142
+ return {
2143
+ success: true,
2144
+ exitCode: 0,
2145
+ stdout: "HTTP/1.1 200 OK\n\n",
2146
+ stderr: "",
2147
+ };
2148
+ });
2149
+ const ctx = ctxWithScratchLogs({
2150
+ sandbox: { type: "linux", execute } as unknown as ToolContext["sandbox"],
2151
+ });
2152
+ await httpRequest(ctx).execute?.(
2153
+ {
2154
+ url: "https://example.com/api",
2155
+ method: "GET",
2156
+ followRedirects: false,
2157
+ timeout: 40_000,
2158
+ toolCallDescription: "Linux timeout floor",
2159
+ },
2160
+ { toolCallId: "tc_test", messages: [], abortSignal: undefined },
2161
+ );
2162
+
2163
+ expect(calls.at(-1)?.opts?.timeout).toBe(40);
2164
+ });
2165
+ });
2166
+
2167
+ describe("resolveHttpRequestTimeoutInput", () => {
2168
+ it("resolves canonical, legacy, and null-inactive inputs before the default", () => {
2169
+ expect(resolveHttpRequestTimeoutInput({ timeoutMs: 500 })).toEqual({
2170
+ ok: true,
2171
+ ms: 500,
2172
+ });
2173
+ expect(resolveHttpRequestTimeoutInput({ timeout: 600 })).toEqual({
2174
+ ok: true,
2175
+ ms: 600,
2176
+ });
2177
+ // Null is an inactive spelling, not a value that fights the other alias.
2178
+ expect(
2179
+ resolveHttpRequestTimeoutInput({ timeoutMs: null, timeout: 700 }),
2180
+ ).toEqual({ ok: true, ms: 700 });
2181
+ expect(
2182
+ resolveHttpRequestTimeoutInput({ timeoutMs: 800, timeout: null }),
2183
+ ).toEqual({ ok: true, ms: 800 });
2184
+ // Equal aliases agree on one deadline — accepted, not treated as a conflict.
2185
+ expect(
2186
+ resolveHttpRequestTimeoutInput({ timeoutMs: 900, timeout: 900 }),
2187
+ ).toEqual({ ok: true, ms: 900 });
2188
+ expect(resolveHttpRequestTimeoutInput({})).toEqual({
2189
+ ok: true,
2190
+ ms: undefined,
2191
+ });
2192
+ expect(
2193
+ resolveHttpRequestTimeoutInput({ timeoutMs: null, timeout: null }),
2194
+ ).toEqual({ ok: true, ms: undefined });
2195
+ });
2196
+
2197
+ it("rejects disagreeing aliases instead of guessing which one was meant", () => {
2198
+ const r = resolveHttpRequestTimeoutInput({
2199
+ timeoutMs: 500,
2200
+ timeout: 900,
2201
+ });
2202
+ expect(r.ok).toBe(false);
2203
+ if (!r.ok) {
2204
+ expect(r.error).toContain("timeoutMs=500");
2205
+ expect(r.error).toContain("timeout=900");
2206
+ expect(r.error).toContain("milliseconds");
2207
+ }
2208
+ });
2209
+ });
2210
+
2211
+ // Canonical-field contract: the wire schema the model sees, and the deadline
2212
+ // the backend enforces, must stay in lockstep across every alias spelling.
2213
+ describe("http_request timeout fields (canonical timeoutMs)", () => {
2214
+ function wireSchema() {
2215
+ return asSchema(httpRequest(makeCtx()).inputSchema);
2216
+ }
2217
+
2218
+ it("serializes both spellings as nullable optional milliseconds in the wire schema", () => {
2219
+ const json = wireSchema().jsonSchema as {
2220
+ properties: Record<
2221
+ string,
2222
+ { anyOf?: { type: string }[]; description?: string } | undefined
2223
+ >;
2224
+ required?: string[];
2225
+ additionalProperties?: boolean;
2226
+ };
2227
+ for (const field of ["timeoutMs", "timeout"]) {
2228
+ expect(json.properties[field]?.anyOf?.map((t) => t.type)).toEqual([
2229
+ "number",
2230
+ "null",
2231
+ ]);
2232
+ }
2233
+ expect(json.required ?? []).not.toContain("timeoutMs");
2234
+ expect(json.required ?? []).not.toContain("timeout");
2235
+ // Strict wire schema: no third spelling can sneak in.
2236
+ expect(json.additionalProperties).toBe(false);
2237
+ expect(json.properties.timeoutMs?.description).toContain("milliseconds");
2238
+ expect(json.properties.timeout?.description).toContain("alias");
2239
+ });
2240
+
2241
+ it("keeps null as an accepted inactive value and schema defaults intact on the SDK validation path", async () => {
2242
+ const schema = wireSchema();
2243
+ const nulled = await schema.validate?.({
2244
+ url: "https://example.com",
2245
+ toolCallDescription: "Probe null handling",
2246
+ timeoutMs: null,
2247
+ timeout: null,
2248
+ });
2249
+ expect(nulled?.success).toBe(true);
2250
+ if (nulled?.success) {
2251
+ expect(nulled.value.timeoutMs).toBeNull();
2252
+ expect(nulled.value.timeout).toBeNull();
2253
+ // Non-alias defaults are untouched by timeout alias resolution.
2254
+ expect(nulled.value.method).toBe("GET");
2255
+ expect(nulled.value.followRedirects).toBe(false);
2256
+ // Unknown spellings are stripped by the strict schema — never a third alias.
2257
+ expect(
2258
+ (nulled.value as Record<string, unknown>).timeoutMS,
2259
+ ).toBeUndefined();
2260
+ }
2261
+ const wrongType = await schema.validate?.({
2262
+ url: "https://example.com",
2263
+ toolCallDescription: "Probe type handling",
2264
+ timeoutMs: "500",
2265
+ });
2266
+ expect(wrongType?.success).toBe(false);
2267
+ });
2268
+
2269
+ function ctxWithHttpTimeoutSpy(overrides: Partial<ToolContext> = {}) {
2270
+ const captured: Array<number | undefined> = [];
2271
+ const ctx = makeCtx(overrides);
2272
+ const local = LocalBackends(ctx);
2273
+ ctx.backends = {
2274
+ ...local,
2275
+ http: {
2276
+ request: async (req, opts) => {
2277
+ captured.push(opts?.timeoutMs);
2278
+ return {
2279
+ success: true,
2280
+ status: 200,
2281
+ statusText: "OK",
2282
+ headers: {},
2283
+ body: "ok",
2284
+ url: req.url,
2285
+ redirected: false,
2286
+ };
2287
+ },
2288
+ },
2289
+ };
2290
+ return { ctx, captured };
2291
+ }
2292
+
2293
+ type TimeoutAliasInput = {
2294
+ url: string;
2295
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
2296
+ headers?: string;
2297
+ body?: string;
2298
+ followRedirects?: boolean;
2299
+ timeoutMs?: number | null;
2300
+ timeout?: number | null;
2301
+ toolCallDescription?: string;
2302
+ };
2303
+
2304
+ async function callTool(ctx: ToolContext, input: TimeoutAliasInput) {
2305
+ return (await httpRequest(ctx).execute?.(
2306
+ {
2307
+ toolCallDescription: "Timeout alias dispatch test",
2308
+ method: "GET",
2309
+ followRedirects: false,
2310
+ ...input,
2311
+ },
2312
+ { toolCallId: "tc_alias", messages: [], abortSignal: undefined },
2313
+ )) as HttpRequestResult;
2314
+ }
2315
+
2316
+ it("dispatches the resolved milliseconds for every accepted alias shape", async () => {
2317
+ const { ctx, captured } = ctxWithHttpTimeoutSpy();
2318
+
2319
+ await callTool(ctx, { url: "https://example.com/a", timeoutMs: 500 });
2320
+ await callTool(ctx, { url: "https://example.com/b", timeout: 600 });
2321
+ await callTool(ctx, {
2322
+ url: "https://example.com/c",
2323
+ timeoutMs: null,
2324
+ timeout: 700,
2325
+ });
2326
+ await callTool(ctx, {
2327
+ url: "https://example.com/d",
2328
+ timeoutMs: 800,
2329
+ timeout: 800,
2330
+ });
2331
+ await callTool(ctx, {
2332
+ url: "https://example.com/e",
2333
+ timeoutMs: null,
2334
+ timeout: null,
2335
+ });
2336
+ await callTool(ctx, { url: "https://example.com/f" });
2337
+
2338
+ expect(captured).toEqual([
2339
+ 500,
2340
+ 600,
2341
+ 700,
2342
+ 800,
2343
+ DEFAULT_HTTP_TIMEOUT_MS,
2344
+ DEFAULT_HTTP_TIMEOUT_MS,
2345
+ ]);
2346
+ });
2347
+
2348
+ it("rejects conflicting aliases before any request is dispatched", async () => {
2349
+ const { ctx, captured } = ctxWithHttpTimeoutSpy();
2350
+
2351
+ const result = await callTool(ctx, {
2352
+ url: "https://example.com/conflict",
2353
+ timeoutMs: 500,
2354
+ timeout: 900,
2355
+ });
2356
+
2357
+ expect(result.success).toBe(false);
2358
+ expect(result.error).toContain("Conflicting timeout values");
2359
+ expect(result.error).toContain("timeoutMs=500");
2360
+ expect(result.error).toContain("timeout=900");
2361
+ expect(result.capture.stopReason).toBe("error");
2362
+ expect(captured).toEqual([]);
2363
+ });
2364
+
2365
+ it("an already-aborted caller wins over any timeout alias", async () => {
2366
+ const { ctx, captured } = ctxWithHttpTimeoutSpy();
2367
+ const ac = new AbortController();
2368
+ ac.abort();
2369
+ ctx.abortSignal = ac.signal;
2370
+
2371
+ const result = await callTool(ctx, {
2372
+ url: "https://example.com/aborted",
2373
+ timeoutMs: 500,
2374
+ });
2375
+
2376
+ expect(result.success).toBe(false);
2377
+ expect(result.error).toContain("aborted");
2378
+ expect(result.capture.stopReason).toBe("aborted");
2379
+ expect(captured).toEqual([]);
2380
+ });
2381
+
2382
+ it("enforces the canonical timeoutMs deadline on a stalled body (real timer)", async () => {
2383
+ const enc = new TextEncoder();
2384
+ const body = new ReadableStream<Uint8Array>({
2385
+ start(c) {
2386
+ c.enqueue(enc.encode("headers-arrived-body-stalls"));
2387
+ // Never enqueues again and never closes — a stalling body.
2388
+ },
2389
+ });
2390
+ vi.stubGlobal(
2391
+ "fetch",
2392
+ vi.fn(async () => new Response(body, { status: 200 })),
2393
+ );
2394
+ const result = (await httpRequest(makeCtx()).execute?.(
2395
+ {
2396
+ url: "https://example.com/stall-canonical",
2397
+ method: "GET",
2398
+ followRedirects: false,
2399
+ timeoutMs: 100,
2400
+ toolCallDescription: "Canonical-field GET with a stalling body",
2401
+ },
2402
+ { toolCallId: "tc_canonical", messages: [], abortSignal: undefined },
2403
+ )) as HttpRequestResult;
2404
+
2405
+ expect(result.success).toBe(false);
2406
+ expect(result.error).toContain("Request timeout after 100ms");
2407
+ expect(result.capture.stopReason).toBe("timeout");
2408
+ }, 5_000);
2409
+ });