@cursor/july 0.1.96 → 0.1.99

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 (381) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +3 -2
  3. package/dist/bin/agent-serve.js +13 -3
  4. package/dist/channels/github/github-channel.d.ts.map +1 -1
  5. package/dist/channels/github/github-channel.js +16 -0
  6. package/dist/channels/origin/api.d.ts +15 -4
  7. package/dist/channels/origin/api.d.ts.map +1 -1
  8. package/dist/channels/origin/api.js +19 -3
  9. package/dist/channels/origin/binding.d.ts +11 -0
  10. package/dist/channels/origin/binding.d.ts.map +1 -1
  11. package/dist/channels/origin/binding.js +41 -25
  12. package/dist/channels/origin/index.d.ts +1 -0
  13. package/dist/channels/origin/index.d.ts.map +1 -1
  14. package/dist/channels/origin/index.js +1 -0
  15. package/dist/channels/origin/origin-channel.d.ts +2 -0
  16. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  17. package/dist/channels/origin/origin-channel.js +40 -4
  18. package/dist/channels/origin/reactions.d.ts +56 -0
  19. package/dist/channels/origin/reactions.d.ts.map +1 -0
  20. package/dist/channels/origin/reactions.js +67 -0
  21. package/dist/channels/origin/types.d.ts +11 -0
  22. package/dist/channels/origin/types.d.ts.map +1 -1
  23. package/dist/channels/slack/approvals.d.ts +16 -0
  24. package/dist/channels/slack/approvals.d.ts.map +1 -1
  25. package/dist/channels/slack/approvals.js +86 -0
  26. package/dist/channels/slack/cli-project.d.ts +19 -0
  27. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  28. package/dist/channels/slack/cli-project.js +234 -0
  29. package/dist/channels/slack/defaults.d.ts.map +1 -1
  30. package/dist/channels/slack/defaults.js +142 -63
  31. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  32. package/dist/channels/slack/dispatch.js +1 -1
  33. package/dist/channels/slack/init.d.ts +3 -2
  34. package/dist/channels/slack/init.d.ts.map +1 -1
  35. package/dist/channels/slack/init.js +5 -5
  36. package/dist/channels/slack/interactive.d.ts +2 -1
  37. package/dist/channels/slack/interactive.d.ts.map +1 -1
  38. package/dist/channels/slack/interactive.js +15 -4
  39. package/dist/channels/slack/live-delivery.d.ts +1 -1
  40. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  41. package/dist/channels/slack/live-delivery.js +4 -2
  42. package/dist/channels/slack/manifest.d.ts +1 -1
  43. package/dist/channels/slack/manifest.js +1 -1
  44. package/dist/channels/slack/progress-delivery.d.ts +28 -0
  45. package/dist/channels/slack/progress-delivery.d.ts.map +1 -0
  46. package/dist/channels/slack/progress-delivery.js +115 -0
  47. package/dist/channels/slack/setup.d.ts +1 -1
  48. package/dist/channels/slack/setup.d.ts.map +1 -1
  49. package/dist/channels/slack/setup.js +10 -6
  50. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  51. package/dist/channels/slack/slack-channel.js +1 -0
  52. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  53. package/dist/channels/slack/tool-approval-events.js +91 -1
  54. package/dist/channels/slack/types.d.ts +9 -1
  55. package/dist/channels/slack/types.d.ts.map +1 -1
  56. package/dist/docs/404.html +2 -2
  57. package/dist/docs/ab.html +4 -4
  58. package/dist/docs/assets/{app.jXleCmOc.js → app.BplEAZ3S.js} +1 -1
  59. package/dist/docs/assets/chunks/@localSearchIndexroot.DpuI4i7Z.js +1 -0
  60. package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.CHXyv4wP.js} +1 -1
  61. package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.BTcsZmjQ.js} +2 -2
  62. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  63. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  64. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  65. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  66. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  67. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  68. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  69. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.DfoeyvL0.js} +9 -6
  70. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
  71. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  72. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.BEJx9XVj.js} +2 -2
  73. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  74. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.CvAHsdSp.js} +10 -2
  75. package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
  76. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  77. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  78. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  79. package/dist/docs/building-with-agents.html +4 -4
  80. package/dist/docs/concepts.html +4 -4
  81. package/dist/docs/deployment.html +7 -7
  82. package/dist/docs/deployment.md +5 -4
  83. package/dist/docs/design/runtime-abstraction.md +1757 -0
  84. package/dist/docs/evals.html +4 -4
  85. package/dist/docs/guides/agent-to-agent.html +4 -4
  86. package/dist/docs/guides/cloud-runtime.html +4 -4
  87. package/dist/docs/guides/convert-automation.html +4 -4
  88. package/dist/docs/guides/github.html +4 -4
  89. package/dist/docs/guides/human-in-the-loop.html +4 -4
  90. package/dist/docs/guides/mcp-oauth.html +8 -8
  91. package/dist/docs/guides/mcp-oauth.md +26 -8
  92. package/dist/docs/guides/opentelemetry.html +4 -4
  93. package/dist/docs/guides/slack.html +17 -33
  94. package/dist/docs/guides/slack.md +92 -201
  95. package/dist/docs/guides/webhooks.html +4 -4
  96. package/dist/docs/hashmap.json +1 -1
  97. package/dist/docs/hillclimbing.html +4 -4
  98. package/dist/docs/index.html +6 -6
  99. package/dist/docs/index.md +5 -3
  100. package/dist/docs/llms-full.txt +2065 -637
  101. package/dist/docs/llms.txt +3 -2
  102. package/dist/docs/quickstart.html +4 -4
  103. package/dist/docs/reference/agent-config.html +6 -6
  104. package/dist/docs/reference/agent-config.md +2 -2
  105. package/dist/docs/reference/artifacts.html +4 -4
  106. package/dist/docs/reference/channels.html +4 -4
  107. package/dist/docs/reference/cli.html +13 -10
  108. package/dist/docs/reference/cli.md +32 -16
  109. package/dist/docs/reference/connections.html +9 -8
  110. package/dist/docs/reference/connections.md +6 -3
  111. package/dist/docs/reference/hooks.html +4 -4
  112. package/dist/docs/reference/http-api.html +7 -7
  113. package/dist/docs/reference/http-api.md +3 -1
  114. package/dist/docs/reference/instructions.html +4 -4
  115. package/dist/docs/reference/playground.html +4 -4
  116. package/dist/docs/reference/project-layout.html +4 -4
  117. package/dist/docs/reference/prompt.html +4 -4
  118. package/dist/docs/reference/schedules.html +4 -4
  119. package/dist/docs/reference/sessions.html +5 -5
  120. package/dist/docs/reference/sessions.md +4 -2
  121. package/dist/docs/reference/skills.html +4 -4
  122. package/dist/docs/reference/subagents.html +4 -4
  123. package/dist/docs/reference/tools.html +15 -7
  124. package/dist/docs/reference/tools.md +34 -2
  125. package/dist/docs/scaffolding-agents.html +4 -4
  126. package/dist/docs/storage.html +4 -4
  127. package/dist/docs/templates/agentic-owners.html +4 -4
  128. package/dist/docs/templates/agents-md.html +28 -0
  129. package/dist/docs/templates/agents-md.md +105 -0
  130. package/dist/docs/templates/code-wiki.html +5 -5
  131. package/dist/docs/templates/demo.html +4 -4
  132. package/dist/docs/templates/pr-autofixer.html +4 -4
  133. package/dist/docs/templates/security-help.html +4 -4
  134. package/dist/docs/templates/security-reviewer.html +5 -5
  135. package/dist/docs/templates/triage.html +4 -4
  136. package/dist/docs/troubleshooting.html +5 -5
  137. package/dist/docs/troubleshooting.md +2 -2
  138. package/dist/evals/run-facts.d.ts.map +1 -1
  139. package/dist/evals/run-facts.js +27 -0
  140. package/dist/files-backends/cursor-hosted.d.ts +3 -2
  141. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  142. package/dist/files-backends/cursor-hosted.js +3 -2
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +1 -0
  146. package/dist/internal/advertise-tools.d.ts.map +1 -1
  147. package/dist/internal/advertise-tools.js +15 -9
  148. package/dist/internal/approval-gate.d.ts +12 -13
  149. package/dist/internal/approval-gate.d.ts.map +1 -1
  150. package/dist/internal/approval-gate.js +16 -21
  151. package/dist/internal/as-of.d.ts +19 -0
  152. package/dist/internal/as-of.d.ts.map +1 -0
  153. package/dist/internal/as-of.js +82 -0
  154. package/dist/internal/chat-client.js +10 -0
  155. package/dist/internal/cli-deploy.d.ts +6 -0
  156. package/dist/internal/cli-deploy.d.ts.map +1 -1
  157. package/dist/internal/cli-deploy.js +149 -21
  158. package/dist/internal/cli-mcp-oauth.js +8 -18
  159. package/dist/internal/cli-slack-manual.d.ts +24 -2
  160. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  161. package/dist/internal/cli-slack-manual.js +251 -20
  162. package/dist/internal/cli-slack.d.ts +9 -2
  163. package/dist/internal/cli-slack.d.ts.map +1 -1
  164. package/dist/internal/cli-slack.js +29 -82
  165. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  166. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  167. package/dist/internal/cursor/account-mcp.js +17 -0
  168. package/dist/internal/cursor-agent-template.d.ts +1 -1
  169. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  170. package/dist/internal/cursor-agent-template.js +1 -0
  171. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  172. package/dist/internal/cursor-event-relay.js +5 -6
  173. package/dist/internal/deploy-client.d.ts +17 -0
  174. package/dist/internal/deploy-client.d.ts.map +1 -1
  175. package/dist/internal/deploy-client.js +31 -10
  176. package/dist/internal/env-file.d.ts +19 -0
  177. package/dist/internal/env-file.d.ts.map +1 -0
  178. package/dist/internal/env-file.js +90 -0
  179. package/dist/internal/framework-file-storage.d.ts +11 -17
  180. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  181. package/dist/internal/framework-file-storage.js +17 -40
  182. package/dist/internal/framework-storage-selection.d.ts +13 -11
  183. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  184. package/dist/internal/framework-storage-selection.js +37 -26
  185. package/dist/internal/hosted-admission-adapter.d.ts +28 -0
  186. package/dist/internal/hosted-admission-adapter.d.ts.map +1 -0
  187. package/dist/internal/hosted-admission-adapter.js +7 -0
  188. package/dist/internal/hosted-delivery-protocol.d.ts +13 -1
  189. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  190. package/dist/internal/hosted-delivery-protocol.js +8 -1
  191. package/dist/internal/hosted-delivery.d.ts +21 -1
  192. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  193. package/dist/internal/hosted-delivery.js +101 -34
  194. package/dist/internal/hosted-execution-diag.d.ts +59 -0
  195. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  196. package/dist/internal/hosted-execution-diag.js +187 -0
  197. package/dist/internal/http-channel.d.ts.map +1 -1
  198. package/dist/internal/http-channel.js +85 -2
  199. package/dist/internal/init-project.d.ts.map +1 -1
  200. package/dist/internal/init-project.js +2 -0
  201. package/dist/internal/logs-client.d.ts +5 -0
  202. package/dist/internal/logs-client.d.ts.map +1 -1
  203. package/dist/internal/logs-client.js +5 -0
  204. package/dist/internal/mcp-endpoint.js +2 -3
  205. package/dist/internal/mcp-host.d.ts +10 -0
  206. package/dist/internal/mcp-host.d.ts.map +1 -1
  207. package/dist/internal/mcp-host.js +57 -8
  208. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  209. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  210. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  211. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  212. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  213. package/dist/internal/mcp-oauth-persist.js +92 -0
  214. package/dist/internal/mcp-oauth.d.ts +1 -0
  215. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  216. package/dist/internal/mcp-oauth.js +10 -2
  217. package/dist/internal/null-kv-storage.d.ts +48 -0
  218. package/dist/internal/null-kv-storage.d.ts.map +1 -0
  219. package/dist/internal/null-kv-storage.js +58 -0
  220. package/dist/internal/otel/collector.d.ts.map +1 -1
  221. package/dist/internal/otel/collector.js +22 -9
  222. package/dist/internal/server.d.ts +6 -0
  223. package/dist/internal/server.d.ts.map +1 -1
  224. package/dist/internal/server.js +123 -33
  225. package/dist/internal/session-engine.d.ts +88 -3
  226. package/dist/internal/session-engine.d.ts.map +1 -1
  227. package/dist/internal/session-engine.js +538 -46
  228. package/dist/internal/session-run-log.d.ts +120 -0
  229. package/dist/internal/session-run-log.d.ts.map +1 -0
  230. package/dist/internal/session-run-log.js +359 -0
  231. package/dist/internal/sessions-client.d.ts +9 -7
  232. package/dist/internal/sessions-client.d.ts.map +1 -1
  233. package/dist/internal/sessions-client.js +3 -1
  234. package/dist/internal/storage-coordinator.d.ts +13 -0
  235. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  236. package/dist/internal/storage-coordinator.js +37 -6
  237. package/dist/internal/storage-roles.d.ts +5 -0
  238. package/dist/internal/storage-roles.d.ts.map +1 -1
  239. package/dist/internal/stream-progress.d.ts.map +1 -1
  240. package/dist/internal/stream-progress.js +10 -0
  241. package/dist/internal/tool-policy.d.ts +29 -9
  242. package/dist/internal/tool-policy.d.ts.map +1 -1
  243. package/dist/internal/tool-policy.js +24 -0
  244. package/dist/internal/trajectory.d.ts.map +1 -1
  245. package/dist/internal/trajectory.js +10 -0
  246. package/dist/internal/turn-outbox.d.ts +24 -0
  247. package/dist/internal/turn-outbox.d.ts.map +1 -0
  248. package/dist/internal/turn-outbox.js +80 -0
  249. package/dist/playground/assets/index-D7rGZNjy.css +1 -0
  250. package/dist/playground/assets/index-jzNbJENa.js +69 -0
  251. package/dist/playground/index.html +2 -2
  252. package/dist/storage-backends/cursor-hosted.d.ts +1 -1
  253. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  254. package/dist/storage-backends/cursor-hosted.js +12 -0
  255. package/dist/storage-protocol.d.ts +8 -0
  256. package/dist/storage-protocol.d.ts.map +1 -1
  257. package/dist/storage-protocol.js +8 -0
  258. package/dist/storage.d.ts +8 -0
  259. package/dist/storage.d.ts.map +1 -1
  260. package/dist/tools.d.ts +12 -2
  261. package/dist/tools.d.ts.map +1 -1
  262. package/dist/types.d.ts +88 -7
  263. package/dist/types.d.ts.map +1 -1
  264. package/docs/README.md +5 -3
  265. package/docs/deployment.md +5 -4
  266. package/docs/design/runtime-abstraction.md +1757 -0
  267. package/docs/guides/mcp-oauth.md +26 -8
  268. package/docs/guides/slack.md +93 -202
  269. package/docs/reference/agent-config.md +2 -2
  270. package/docs/reference/cli.md +32 -16
  271. package/docs/reference/connections.md +6 -3
  272. package/docs/reference/http-api.md +3 -1
  273. package/docs/reference/sessions.md +4 -2
  274. package/docs/reference/tools.md +34 -2
  275. package/docs/templates/agents-md.md +110 -0
  276. package/docs/troubleshooting.md +2 -2
  277. package/package.json +1 -1
  278. package/skills/create-agent/SKILL.md +2 -1
  279. package/skills/mcp-auth/SKILL.md +6 -3
  280. package/skills/setup-slack/SKILL.md +23 -13
  281. package/src/bin/agent-serve.ts +12 -1
  282. package/src/channels/github/github-channel.ts +17 -0
  283. package/src/channels/origin/api.ts +24 -5
  284. package/src/channels/origin/binding.ts +55 -26
  285. package/src/channels/origin/index.ts +1 -0
  286. package/src/channels/origin/origin-channel.ts +78 -10
  287. package/src/channels/origin/reactions.ts +82 -0
  288. package/src/channels/origin/types.ts +11 -0
  289. package/src/channels/slack/approvals.ts +105 -0
  290. package/src/channels/slack/cli-project.ts +267 -0
  291. package/src/channels/slack/defaults.ts +165 -74
  292. package/src/channels/slack/dispatch.ts +2 -0
  293. package/src/channels/slack/init.ts +10 -5
  294. package/src/channels/slack/interactive.ts +35 -9
  295. package/src/channels/slack/live-delivery.ts +5 -3
  296. package/src/channels/slack/manifest.ts +1 -1
  297. package/src/channels/slack/progress-delivery.ts +133 -0
  298. package/src/channels/slack/setup.ts +10 -6
  299. package/src/channels/slack/slack-channel.ts +1 -0
  300. package/src/channels/slack/tool-approval-events.ts +93 -0
  301. package/src/channels/slack/types.ts +9 -1
  302. package/src/evals/run-facts.ts +32 -0
  303. package/src/files-backends/cursor-hosted.ts +3 -2
  304. package/src/index.ts +4 -0
  305. package/src/internal/advertise-tools.ts +31 -10
  306. package/src/internal/approval-gate.ts +20 -29
  307. package/src/internal/as-of.ts +107 -0
  308. package/src/internal/chat-client.ts +19 -0
  309. package/src/internal/cli-deploy.ts +203 -13
  310. package/src/internal/cli-mcp-oauth.ts +7 -21
  311. package/src/internal/cli-slack-manual.ts +350 -18
  312. package/src/internal/cli-slack.ts +40 -84
  313. package/src/internal/cursor/account-mcp.ts +21 -0
  314. package/src/internal/cursor-agent-template.ts +1 -0
  315. package/src/internal/cursor-event-relay.ts +5 -6
  316. package/src/internal/deploy-client.ts +43 -0
  317. package/src/internal/env-file.ts +84 -0
  318. package/src/internal/framework-file-storage.ts +18 -46
  319. package/src/internal/framework-storage-selection.ts +41 -25
  320. package/src/internal/hosted-admission-adapter.ts +42 -0
  321. package/src/internal/hosted-delivery-protocol.ts +14 -2
  322. package/src/internal/hosted-delivery.ts +169 -44
  323. package/src/internal/hosted-execution-diag.ts +252 -0
  324. package/src/internal/http-channel.ts +107 -0
  325. package/src/internal/init-project.ts +2 -0
  326. package/src/internal/logs-client.ts +5 -0
  327. package/src/internal/mcp-endpoint.ts +13 -4
  328. package/src/internal/mcp-host.ts +77 -11
  329. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  330. package/src/internal/mcp-oauth-persist.ts +127 -0
  331. package/src/internal/mcp-oauth.ts +17 -1
  332. package/src/internal/null-kv-storage.ts +74 -0
  333. package/src/internal/otel/collector.ts +30 -0
  334. package/src/internal/server.ts +159 -11
  335. package/src/internal/session-engine.ts +691 -57
  336. package/src/internal/session-run-log.ts +584 -0
  337. package/src/internal/sessions-client.ts +17 -8
  338. package/src/internal/storage-coordinator.ts +49 -9
  339. package/src/internal/storage-roles.ts +7 -0
  340. package/src/internal/stream-progress.ts +19 -0
  341. package/src/internal/tool-policy.ts +49 -9
  342. package/src/internal/trajectory.ts +10 -0
  343. package/src/internal/turn-outbox.ts +85 -0
  344. package/src/storage-backends/cursor-hosted.ts +13 -1
  345. package/src/storage-protocol.ts +10 -0
  346. package/src/storage.ts +12 -0
  347. package/src/tools.ts +12 -2
  348. package/src/types.ts +93 -8
  349. package/templates/agents-md/README.md +39 -0
  350. package/templates/agents-md/agent/agent.ts +8 -0
  351. package/templates/agents-md/agent/channels/github.ts +10 -0
  352. package/templates/agents-md/agent/channels/queue.ts +28 -0
  353. package/templates/agents-md/agent/channels/slack.ts +9 -0
  354. package/templates/agents-md/agent/instructions.md +39 -0
  355. package/templates/agents-md/agent/lib/collect.ts +439 -0
  356. package/templates/agents-md/agent/lib/config.ts +8 -0
  357. package/templates/agents-md/agent/lib/paths.ts +51 -0
  358. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  359. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  360. package/templates/agents-md/evals/agents.eval.ts +135 -0
  361. package/templates/agents-md/evals/evals.config.ts +5 -0
  362. package/templates/agents-md/init.json +38 -0
  363. package/templates/agents-md/package.json +18 -0
  364. package/templates/agents-md/tsconfig.json +12 -0
  365. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
  366. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  367. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  368. package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
  369. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  370. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  371. package/dist/docs/design/agsh.md +0 -406
  372. package/dist/playground/assets/index-B1DDvBeC.css +0 -1
  373. package/dist/playground/assets/index-Cx9CclK_.js +0 -88
  374. package/docs/design/agsh.md +0 -406
  375. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  376. /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
  377. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  378. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  379. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
  380. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  381. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -1,10 +1,4 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import {
3
- dispatchGitHubWebhook,
4
- getGitHubChannelOptions,
5
- getGitHubDispatchOutcome,
6
- withPropagatingGitHubBackgroundErrors,
7
- } from "../channels/github/github-channel.js";
8
2
  import type {
9
3
  AuthPolicy,
10
4
  ChannelHandlerArgs,
@@ -14,6 +8,10 @@ import {
14
8
  createDeferredChannelSession,
15
9
  DeferredChannelSessionError,
16
10
  } from "./deferred-channel-session.js";
11
+ import {
12
+ getHostedAdmissionAdapter,
13
+ type HostedAdmissionAdapter,
14
+ } from "./hosted-admission-adapter.js";
17
15
  import {
18
16
  HostedAdmissionLocalSendError,
19
17
  runDuringHostedAdmission,
@@ -27,9 +25,22 @@ import {
27
25
  parseHostedAdmission,
28
26
  parseHostedExecution,
29
27
  } from "./hosted-delivery-protocol.js";
28
+ import {
29
+ abortReasonFromTurnStatus,
30
+ HOSTED_EXECUTION_DIAG_RUNNING_STATUS,
31
+ hostedErrorStackSnippet,
32
+ hostedExecutionDiagId,
33
+ writeHostedExecutionDiag,
34
+ } from "./hosted-execution-diag.js";
30
35
  import { recordHostedManagedAutomation } from "./hosted-managed-automation.js";
31
36
  import { errorResponse, readJsonObject } from "./http-channel.js";
37
+ import type { LogRingEntry } from "./log-ring.js";
32
38
  import type { Router } from "./router.js";
39
+ import {
40
+ type SessionRunExecutionWrite,
41
+ serializeSessionRunError,
42
+ snapshotSessionRunLogs,
43
+ } from "./session-run-log.js";
33
44
 
34
45
  type DispatchInput = {
35
46
  delivery: HostedAdmission;
@@ -109,17 +120,20 @@ function nonRetryableRejection(error: unknown): AdmissionDispatch | undefined {
109
120
  }
110
121
 
111
122
  /**
112
- * Run authored admission without creating a framework session. V1 GitHub
123
+ * Run authored admission without creating a framework session. Authored
113
124
  * `task` callbacks execute as sessionless work; the first `send` is captured
114
125
  * and executed only after the control plane allocates `ses_…`. Capturing
115
126
  * does not terminate the caller — `send` returns an inert deferred handle so
116
127
  * a task body runs to completion before the admission response is computed.
128
+ *
129
+ * Dispatch is the adapter the mounted channel attached. Hosted delivery
130
+ * does not switch on `channelKind`.
117
131
  */
118
132
  export async function dispatchHostedAdmission(
119
133
  input: DispatchInput
120
134
  ): Promise<AdmissionDispatch> {
121
- const github = getGitHubChannelOptions(input.definition);
122
- if (github === undefined) {
135
+ const adapter = getHostedAdmissionAdapter(input.definition);
136
+ if (adapter === undefined || adapter.kind !== input.delivery.channelKind) {
123
137
  return {
124
138
  response: Response.json(
125
139
  { ok: false, error: "channel_kind_mismatch" },
@@ -129,16 +143,16 @@ export async function dispatchHostedAdmission(
129
143
  }
130
144
  // The admission window marker follows the async graph into task bodies so
131
145
  // the engine can refuse local sends that would bypass the capture stub.
132
- return runDuringHostedAdmission(() => runAdmission(input, github));
146
+ return runDuringHostedAdmission(() => runAdmission(input, adapter));
133
147
  }
134
148
 
135
149
  async function runAdmission(
136
150
  input: DispatchInput,
137
- github: NonNullable<ReturnType<typeof getGitHubChannelOptions>>
151
+ adapter: HostedAdmissionAdapter
138
152
  ): Promise<AdmissionDispatch> {
139
153
  const background: Promise<unknown>[] = [];
140
154
  let captured: { message: string; options: SendMessageOptions } | undefined;
141
- const args = withPropagatingGitHubBackgroundErrors({
155
+ const capturingArgs: ChannelHandlerArgs = {
142
156
  ...input.args,
143
157
  send: async (message, options = {}) => {
144
158
  if (captured !== undefined) {
@@ -150,12 +164,12 @@ async function runAdmission(
150
164
  waitUntil: (promise) => {
151
165
  background.push(promise);
152
166
  },
153
- });
167
+ };
168
+ const args = adapter.prepareArgs?.(capturingArgs) ?? capturingArgs;
154
169
  let dispatched: Response;
155
170
  try {
156
- dispatched = await dispatchGitHubWebhook({
157
- options: github,
158
- eventName: input.delivery.eventType,
171
+ dispatched = await adapter.dispatch({
172
+ eventType: input.delivery.eventType,
159
173
  payload: input.delivery.payload,
160
174
  deliveryId: input.delivery.deliveryId,
161
175
  args,
@@ -206,7 +220,7 @@ async function runAdmission(
206
220
  },
207
221
  };
208
222
  }
209
- const outcome = getGitHubDispatchOutcome(dispatched);
223
+ const outcome = adapter.getOutcome(dispatched);
210
224
  if (outcome === undefined) {
211
225
  return {
212
226
  response: Response.json(
@@ -252,6 +266,21 @@ export function registerHostedDeliveryRoute(input: {
252
266
  * the conversation on the next wake.
253
267
  */
254
268
  drainSessionDurability?(sessionId: string): Promise<void>;
269
+ /**
270
+ * Serve `--state-root` (not the per-slug SessionEngine dir). Hosted
271
+ * freeze is `/workspace/.agent-serve`; the Temporal host reads
272
+ * `hosted-execution.json` from that path after the POST returns.
273
+ */
274
+ stateRoot?: string;
275
+ /**
276
+ * Upsert this execution into the v2 session-level framework-store log.
277
+ * Called for the pre-turn `running` claim and every terminal outcome so
278
+ * a dead Node process still leaves a leftover. Soft-fail inside the
279
+ * store; this path still awaits so a parked pod cannot shed the write.
280
+ */
281
+ persistSessionRun?(write: SessionRunExecutionWrite): Promise<void>;
282
+ /** Snapshot of the process log ring at persist time. */
283
+ snapshotLogs?(): LogRingEntry[];
255
284
  }): void {
256
285
  const pending = new Map<
257
286
  string,
@@ -383,39 +412,135 @@ export function registerHostedDeliveryRoute(input: {
383
412
  }
384
413
  admitted.sessionPublicId = execution.sessionPublicId;
385
414
  admitted.execution ??= (async (): Promise<ExecutionReply> => {
386
- const args = input.buildHandlerArgs(
387
- admitted.send.channelId,
388
- execution.sessionPublicId
389
- );
390
- const session = await args.send(admitted.send.message, {
391
- ...admitted.send.options,
392
- mailboxContinuation: {
393
- key: admitted.send.mailboxContinuationKey,
394
- },
415
+ const startedAt = performance.now();
416
+ const executionId = hostedExecutionDiagId(execution.admissionToken);
417
+ const writeDiag = async (fields: {
418
+ sessionId: string;
419
+ turnStatus: string;
420
+ message?: string;
421
+ abortReason?:
422
+ | ReturnType<typeof abortReasonFromTurnStatus>
423
+ | "execute_throw";
424
+ error?: unknown;
425
+ }): Promise<void> => {
426
+ const durationMs = Math.max(
427
+ 0,
428
+ Math.round(performance.now() - startedAt)
429
+ );
430
+ const message =
431
+ fields.message === undefined
432
+ ? undefined
433
+ : boundedMessage(fields.message);
434
+ const stack =
435
+ fields.error === undefined
436
+ ? undefined
437
+ : hostedErrorStackSnippet(fields.error);
438
+ if (input.stateRoot !== undefined) {
439
+ await writeHostedExecutionDiag(input.stateRoot, {
440
+ v: 1,
441
+ executionId,
442
+ writtenAt: new Date().toISOString(),
443
+ sessionPublicId: execution.sessionPublicId,
444
+ sessionId: fields.sessionId,
445
+ turnStatus: fields.turnStatus,
446
+ durationMs,
447
+ ...(message === undefined ? {} : { message }),
448
+ ...(stack === undefined || stack.length === 0 ? {} : { stack }),
449
+ ...(message !== undefined && /abort/i.test(message)
450
+ ? { signalAborted: true }
451
+ : {}),
452
+ ...(fields.abortReason === undefined
453
+ ? {}
454
+ : { abortReason: fields.abortReason }),
455
+ });
456
+ }
457
+ if (input.persistSessionRun === undefined) {
458
+ return;
459
+ }
460
+ const error =
461
+ fields.error === undefined
462
+ ? undefined
463
+ : serializeSessionRunError(fields.error);
464
+ try {
465
+ await input.persistSessionRun({
466
+ executionId,
467
+ writtenAt: new Date().toISOString(),
468
+ sessionPublicId: execution.sessionPublicId,
469
+ sessionId: fields.sessionId,
470
+ turnStatus: fields.turnStatus,
471
+ durationMs,
472
+ ...(fields.abortReason === undefined
473
+ ? {}
474
+ : { abortReason: fields.abortReason }),
475
+ ...(message === undefined ? {} : { message }),
476
+ ...(error === undefined ? {} : { error }),
477
+ logs: snapshotSessionRunLogs(input.snapshotLogs?.() ?? []),
478
+ });
479
+ } catch {
480
+ // Soft-fail: a store outage must not change the HTTP reply.
481
+ }
482
+ };
483
+ // Claim the file before the turn runs. The host reads it after its
484
+ // own POST returns, which for a 9-minute timeout is while this turn
485
+ // is still going; without this marker that read would surface the
486
+ // PREVIOUS delivery's terminal record as if it were this one's.
487
+ await writeDiag({
488
+ sessionId: "",
489
+ turnStatus: HOSTED_EXECUTION_DIAG_RUNNING_STATUS,
395
490
  });
396
- const outcome = await session.waitForCompletion();
397
- // Failed turns drain too: their record/continuation updates must
398
- // survive the park just like a finished turn's.
399
- await input.drainSessionDurability?.(session.id);
400
- if (outcome.status !== "finished") {
491
+ try {
492
+ const args = input.buildHandlerArgs(
493
+ admitted.send.channelId,
494
+ execution.sessionPublicId
495
+ );
496
+ const session = await args.send(admitted.send.message, {
497
+ ...admitted.send.options,
498
+ mailboxContinuation: {
499
+ key: admitted.send.mailboxContinuationKey,
500
+ },
501
+ });
502
+ const outcome = await session.waitForCompletion();
503
+ // Failed turns drain too: their record/continuation updates must
504
+ // survive the park just like a finished turn's.
505
+ await input.drainSessionDurability?.(session.id);
506
+ await writeDiag({
507
+ sessionId: session.id,
508
+ turnStatus: outcome.status,
509
+ message: outcome.errorMessage,
510
+ abortReason: abortReasonFromTurnStatus(
511
+ outcome.status,
512
+ outcome.errorMessage
513
+ ),
514
+ });
515
+ if (outcome.status !== "finished") {
516
+ return {
517
+ status: 503,
518
+ body: {
519
+ ok: false,
520
+ error: "turn_failed",
521
+ turnStatus: outcome.status,
522
+ message: outcome.errorMessage,
523
+ },
524
+ };
525
+ }
401
526
  return {
402
- status: 503,
527
+ status: 200,
403
528
  body: {
404
- ok: false,
405
- error: "turn_failed",
406
- turnStatus: outcome.status,
407
- message: outcome.errorMessage,
529
+ ok: true,
530
+ v: HOSTED_DELIVERY_PROTOCOL_VERSION,
531
+ outcome: "send",
408
532
  },
409
533
  };
534
+ } catch (error) {
535
+ await writeDiag({
536
+ sessionId: "",
537
+ turnStatus: "error",
538
+ message: error instanceof Error ? error.message : String(error),
539
+ abortReason: "execute_throw",
540
+ error,
541
+ });
542
+ throw error;
410
543
  }
411
- return {
412
- status: 200,
413
- body: {
414
- ok: true,
415
- v: HOSTED_DELIVERY_PROTOCOL_VERSION,
416
- outcome: "send",
417
- },
418
- };
419
544
  })();
420
545
  const reply = await admitted.execution;
421
546
  return finish(Response.json(reply.body, { status: reply.status }));
@@ -0,0 +1,252 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { mkdir, rename, unlink, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+
5
+ import { HOSTED_EXECUTION_DIAG_FILENAME } from "./hosted-delivery-protocol.js";
6
+
7
+ export const HOSTED_EXECUTION_DIAG_VERSION = 1;
8
+ /** Cap so a runaway message cannot fill the guest disk or the host log. */
9
+ export const HOSTED_EXECUTION_DIAG_MAX_BYTES = 4096;
10
+
11
+ /**
12
+ * Correlate the on-disk record with the execution that produced it. One pod
13
+ * serves many deliveries of a continuation session, and the file lives at a
14
+ * single fixed path, so without this the host can read a *previous*
15
+ * delivery's terminal record whenever July never reaches its own write —
16
+ * the 9-minute host POST timeout, a 409 on an expired admission token, or a
17
+ * replayed token that short-circuits on the memoized reply.
18
+ *
19
+ * Derived from the admission token (which both sides already hold) rather
20
+ * than carried on the wire. The digest is one-way, so the token itself never
21
+ * reaches the guest disk or the host logs.
22
+ */
23
+ export function hostedExecutionDiagId(admissionToken: string): string {
24
+ return createHash("sha256").update(admissionToken).digest("hex").slice(0, 16);
25
+ }
26
+
27
+ export type HostedExecutionDiagAbortReason =
28
+ | "signal_abort"
29
+ | "runner_cancelled"
30
+ | "runner_error"
31
+ | "execute_throw";
32
+
33
+ /**
34
+ * Bounded guest-side record of one hosted-execution attempt. No prompt,
35
+ * tool content, or API key — the host copies known keys onto the Temporal
36
+ * activity span for Datadog.
37
+ */
38
+ export type HostedExecutionDiag = {
39
+ v: typeof HOSTED_EXECUTION_DIAG_VERSION;
40
+ /** {@link hostedExecutionDiagId} of the execution that wrote this record. */
41
+ executionId: string;
42
+ writtenAt: string;
43
+ sessionPublicId: string;
44
+ sessionId: string;
45
+ /** Terminal {@link TurnOutcome} status, or `running` before the turn ends. */
46
+ turnStatus: string;
47
+ durationMs: number;
48
+ message?: string;
49
+ /** First frames of a thrown Error; omitted on soft turn failures. */
50
+ stack?: string;
51
+ signalAborted?: boolean;
52
+ requestId?: string;
53
+ abortReason?: HostedExecutionDiagAbortReason;
54
+ };
55
+
56
+ /** Written before the turn starts so a stalled execution still has a record. */
57
+ export const HOSTED_EXECUTION_DIAG_RUNNING_STATUS = "running";
58
+
59
+ const DIAG_KEYS = new Set([
60
+ "v",
61
+ "executionId",
62
+ "writtenAt",
63
+ "sessionPublicId",
64
+ "sessionId",
65
+ "turnStatus",
66
+ "durationMs",
67
+ "message",
68
+ "stack",
69
+ "signalAborted",
70
+ "requestId",
71
+ "abortReason",
72
+ ]);
73
+
74
+ const ABORT_REASONS = new Set<HostedExecutionDiagAbortReason>([
75
+ "signal_abort",
76
+ "runner_cancelled",
77
+ "runner_error",
78
+ "execute_throw",
79
+ ]);
80
+
81
+ function boundedOptionalString(
82
+ value: unknown,
83
+ max: number
84
+ ): string | undefined {
85
+ return typeof value === "string" && value.length > 0
86
+ ? value.length > max
87
+ ? `${value.slice(0, max)}…`
88
+ : value
89
+ : undefined;
90
+ }
91
+
92
+ /** First line + frames; factory `last_error` is 1024 chars with a TAS prefix. */
93
+ export const HOSTED_ERROR_STACK_LINE_COUNT = 5;
94
+ export const HOSTED_ERROR_SNIPPET_MAX_CHARS = 700;
95
+
96
+ /**
97
+ * Bounded throw text for the hosted 500 body and the on-disk diag `stack`.
98
+ * Prefers `Error.stack` (message line + `at` frames) so `last_error` is not
99
+ * the generic "Internal server error." when the throw has a stack.
100
+ */
101
+ export function hostedErrorStackSnippet(error: unknown): string {
102
+ if (error instanceof Error) {
103
+ const stack = error.stack?.trim();
104
+ if (stack !== undefined && stack.length > 0) {
105
+ const lines = stack
106
+ .split("\n")
107
+ .map((line) => line.trimEnd())
108
+ .filter((line) => line.trim().length > 0)
109
+ .slice(0, HOSTED_ERROR_STACK_LINE_COUNT);
110
+ const text = lines.join("\n");
111
+ return text.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
112
+ ? `${text.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
113
+ : text;
114
+ }
115
+ return error.message.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
116
+ ? `${error.message.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
117
+ : error.message;
118
+ }
119
+ const text = String(error);
120
+ if (text.length === 0) {
121
+ return "";
122
+ }
123
+ return text.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
124
+ ? `${text.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
125
+ : text;
126
+ }
127
+
128
+ export function hostedExecutionDiagPath(stateRoot: string): string {
129
+ return join(stateRoot, HOSTED_EXECUTION_DIAG_FILENAME);
130
+ }
131
+
132
+ export function abortReasonFromTurnStatus(
133
+ status: string,
134
+ message: string | undefined
135
+ ): HostedExecutionDiagAbortReason | undefined {
136
+ if (status === "finished") {
137
+ return undefined;
138
+ }
139
+ if (status === "cancelled") {
140
+ return message !== undefined && /abort/i.test(message)
141
+ ? "signal_abort"
142
+ : "runner_cancelled";
143
+ }
144
+ return "runner_error";
145
+ }
146
+
147
+ export function parseHostedExecutionDiag(
148
+ raw: string
149
+ ): HostedExecutionDiag | undefined {
150
+ if (raw.length === 0 || raw.length > HOSTED_EXECUTION_DIAG_MAX_BYTES) {
151
+ return undefined;
152
+ }
153
+ let parsed: unknown;
154
+ try {
155
+ parsed = JSON.parse(raw);
156
+ } catch {
157
+ return undefined;
158
+ }
159
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
160
+ return undefined;
161
+ }
162
+ const body = parsed as Record<string, unknown>;
163
+ if (Object.keys(body).some((key) => !DIAG_KEYS.has(key))) {
164
+ return undefined;
165
+ }
166
+ if (
167
+ body.v !== HOSTED_EXECUTION_DIAG_VERSION ||
168
+ typeof body.executionId !== "string" ||
169
+ body.executionId.length === 0 ||
170
+ body.executionId.length > 64 ||
171
+ typeof body.writtenAt !== "string" ||
172
+ body.writtenAt.length === 0 ||
173
+ body.writtenAt.length > 64 ||
174
+ typeof body.sessionPublicId !== "string" ||
175
+ body.sessionPublicId.length === 0 ||
176
+ body.sessionPublicId.length > 128 ||
177
+ typeof body.sessionId !== "string" ||
178
+ body.sessionId.length > 128 ||
179
+ typeof body.turnStatus !== "string" ||
180
+ body.turnStatus.length === 0 ||
181
+ body.turnStatus.length > 64 ||
182
+ typeof body.durationMs !== "number" ||
183
+ !Number.isFinite(body.durationMs) ||
184
+ body.durationMs < 0
185
+ ) {
186
+ return undefined;
187
+ }
188
+ const message = boundedOptionalString(body.message, 1024);
189
+ const stack = boundedOptionalString(
190
+ body.stack,
191
+ HOSTED_ERROR_SNIPPET_MAX_CHARS
192
+ );
193
+ const requestId = boundedOptionalString(body.requestId, 128);
194
+ const abortReason =
195
+ body.abortReason === undefined
196
+ ? undefined
197
+ : typeof body.abortReason === "string" &&
198
+ ABORT_REASONS.has(body.abortReason as HostedExecutionDiagAbortReason)
199
+ ? (body.abortReason as HostedExecutionDiagAbortReason)
200
+ : undefined;
201
+ if (body.abortReason !== undefined && abortReason === undefined) {
202
+ return undefined;
203
+ }
204
+ if (
205
+ body.signalAborted !== undefined &&
206
+ typeof body.signalAborted !== "boolean"
207
+ ) {
208
+ return undefined;
209
+ }
210
+ return {
211
+ v: HOSTED_EXECUTION_DIAG_VERSION,
212
+ executionId: body.executionId,
213
+ writtenAt: body.writtenAt,
214
+ sessionPublicId: body.sessionPublicId,
215
+ sessionId: body.sessionId,
216
+ turnStatus: body.turnStatus,
217
+ durationMs: body.durationMs,
218
+ ...(message === undefined ? {} : { message }),
219
+ ...(stack === undefined ? {} : { stack }),
220
+ ...(body.signalAborted === true ? { signalAborted: true } : {}),
221
+ ...(requestId === undefined ? {} : { requestId }),
222
+ ...(abortReason === undefined ? {} : { abortReason }),
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Overwrite the per-execution diag file. Soft-fail: a write error must not
228
+ * change the hosted-execution HTTP reply.
229
+ */
230
+ export async function writeHostedExecutionDiag(
231
+ stateRoot: string,
232
+ diag: HostedExecutionDiag
233
+ ): Promise<void> {
234
+ const path = hostedExecutionDiagPath(stateRoot);
235
+ const tmp = `${path}.${randomBytes(8).toString("hex")}.tmp`;
236
+ try {
237
+ await mkdir(stateRoot, { recursive: true });
238
+ const serialized = JSON.stringify(diag);
239
+ const body =
240
+ serialized.length > HOSTED_EXECUTION_DIAG_MAX_BYTES
241
+ ? JSON.stringify({
242
+ ...diag,
243
+ stack: undefined,
244
+ message: boundedOptionalString(diag.message, 256),
245
+ })
246
+ : serialized;
247
+ await writeFile(tmp, body, "utf8");
248
+ await rename(tmp, path);
249
+ } catch {
250
+ await unlink(tmp).catch(() => undefined);
251
+ }
252
+ }
@@ -25,6 +25,8 @@ import type { ServerRoute } from "./router.js";
25
25
  import {
26
26
  ApprovalInterruptedError,
27
27
  ApprovalNotFoundError,
28
+ AuthInterruptedError,
29
+ AuthNotFoundError,
28
30
  HTTP_CHANNEL_ID,
29
31
  SessionAuthError,
30
32
  type SessionEngine,
@@ -93,6 +95,13 @@ export function buildHttpChannelRoutes(args: {
93
95
  "Expected a JSON body with a string `message`."
94
96
  );
95
97
  }
98
+ if (body.asOf !== undefined && typeof body.asOf !== "string") {
99
+ return errorResponse(
100
+ 400,
101
+ "invalid_request",
102
+ "`asOf` must be an ISO-8601 instant with a timezone when provided."
103
+ );
104
+ }
96
105
  const message = await transformMessage(body.message, ctx.auth);
97
106
  const workspaceFiles = readWorkspaceFiles(body.workspaceFiles);
98
107
  // Per-session cloud attach (create only). Eval harness uses this to pin
@@ -103,6 +112,7 @@ export function buildHttpChannelRoutes(args: {
103
112
  ...(typeof body.title === "string" ? { title: body.title } : {}),
104
113
  ...(body.purpose === "eval" ? { purpose: "eval" as const } : {}),
105
114
  ...(body.dryRun === true ? { dryRun: true } : {}),
115
+ asOf: body.asOf,
106
116
  ...(workspaceFiles === undefined ? {} : { workspaceFiles }),
107
117
  ...(cloud === undefined ? {} : { cloud }),
108
118
  });
@@ -454,6 +464,101 @@ export function buildHttpChannelRoutes(args: {
454
464
  },
455
465
  };
456
466
 
467
+ const listAuths: ServerRoute = {
468
+ method: "GET",
469
+ pattern: `${base}/v1/session/:sessionId/auths`,
470
+ auth,
471
+ handler: async (_request, ctx) => {
472
+ const sessionId = ctx.params.sessionId ?? "";
473
+ const record = await engine.getOrRestoreSession(sessionId);
474
+ if (record === undefined) {
475
+ return errorResponse(
476
+ 404,
477
+ "session_not_found",
478
+ `Unknown session ${sessionId}.`
479
+ );
480
+ }
481
+ const ownership = requireSessionOwner(record, ctx.auth, {
482
+ relax: relaxSessionReadAuth,
483
+ beyondLoopback: relaxSessionReadAuthBeyondLoopback,
484
+ requestIp: ctx.requestIp,
485
+ });
486
+ if (ownership !== undefined) {
487
+ return ownership;
488
+ }
489
+ const pending = await engine.listPendingAuths(sessionId);
490
+ return jsonResponse({ ok: true, sessionId, pending });
491
+ },
492
+ };
493
+
494
+ const resolveAuth: ServerRoute = {
495
+ method: "POST",
496
+ pattern: `${base}/v1/session/:sessionId/auths/:callId`,
497
+ auth,
498
+ handler: async (request, ctx) => {
499
+ const sessionId = ctx.params.sessionId ?? "";
500
+ const callId = ctx.params.callId ?? "";
501
+ const record = await engine.getOrRestoreSession(sessionId);
502
+ if (record === undefined) {
503
+ return errorResponse(
504
+ 404,
505
+ "session_not_found",
506
+ `Unknown session ${sessionId}.`
507
+ );
508
+ }
509
+ const bridgeSlackAuth =
510
+ relaxSessionApprovalAuth === true &&
511
+ record.auth?.authenticator === "slack" &&
512
+ (relaxSessionReadAuthBeyondLoopback === true ||
513
+ (typeof ctx.requestIp === "string" &&
514
+ isLoopbackAddress(ctx.requestIp)));
515
+ const ownership = requireSessionOwner(record, ctx.auth, {
516
+ relax: bridgeSlackAuth,
517
+ beyondLoopback: relaxSessionReadAuthBeyondLoopback,
518
+ requestIp: ctx.requestIp,
519
+ });
520
+ if (ownership !== undefined) {
521
+ return ownership;
522
+ }
523
+ const body = await readJsonObject(request);
524
+ const decision = body?.decision;
525
+ if (decision !== "approve" && decision !== "deny") {
526
+ return errorResponse(
527
+ 400,
528
+ "invalid_request",
529
+ 'Expected a JSON body with `decision`: "approve" | "deny".'
530
+ );
531
+ }
532
+ try {
533
+ const pending = await engine.resolveAuth(
534
+ sessionId,
535
+ callId,
536
+ decision,
537
+ ctx.auth,
538
+ bridgeSlackAuth ? { asSessionOwner: true } : undefined
539
+ );
540
+ return jsonResponse({
541
+ ok: true,
542
+ sessionId,
543
+ callId,
544
+ decision,
545
+ connection: pending.connection,
546
+ });
547
+ } catch (error) {
548
+ if (error instanceof AuthNotFoundError) {
549
+ return errorResponse(404, "auth_not_found", error.message);
550
+ }
551
+ if (error instanceof AuthInterruptedError) {
552
+ return errorResponse(409, "auth_interrupted", error.message);
553
+ }
554
+ if (error instanceof SessionAuthError) {
555
+ return errorResponse(403, "forbidden", error.message);
556
+ }
557
+ throw error;
558
+ }
559
+ },
560
+ };
561
+
457
562
  return [
458
563
  createSession,
459
564
  followUp,
@@ -463,6 +568,8 @@ export function buildHttpChannelRoutes(args: {
463
568
  cost,
464
569
  listApprovals,
465
570
  resolveApproval,
571
+ listAuths,
572
+ resolveAuth,
466
573
  ];
467
574
  }
468
575