@cursor/july 0.1.95 → 0.1.97

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 (424) hide show
  1. package/AGENTS.md +3 -4
  2. package/README.md +3 -2
  3. package/dist/bin/agent-serve.js +14 -3
  4. package/dist/channels/deployments/types.d.ts +1 -1
  5. package/dist/channels/deployments/types.d.ts.map +1 -1
  6. package/dist/channels/deployments/types.js +1 -0
  7. package/dist/channels/origin/api.d.ts +72 -15
  8. package/dist/channels/origin/api.d.ts.map +1 -1
  9. package/dist/channels/origin/api.js +185 -28
  10. package/dist/channels/origin/binding.d.ts +13 -2
  11. package/dist/channels/origin/binding.d.ts.map +1 -1
  12. package/dist/channels/origin/binding.js +41 -25
  13. package/dist/channels/origin/checks.d.ts +6 -6
  14. package/dist/channels/origin/checks.js +6 -6
  15. package/dist/channels/origin/cursor-account.d.ts +5 -3
  16. package/dist/channels/origin/cursor-account.d.ts.map +1 -1
  17. package/dist/channels/origin/cursor-account.js +5 -3
  18. package/dist/channels/origin/index.d.ts +1 -0
  19. package/dist/channels/origin/index.d.ts.map +1 -1
  20. package/dist/channels/origin/index.js +1 -0
  21. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  22. package/dist/channels/origin/origin-channel.js +3 -0
  23. package/dist/channels/origin/reactions.d.ts +56 -0
  24. package/dist/channels/origin/reactions.d.ts.map +1 -0
  25. package/dist/channels/origin/reactions.js +67 -0
  26. package/dist/channels/origin/types.d.ts +11 -0
  27. package/dist/channels/origin/types.d.ts.map +1 -1
  28. package/dist/channels/slack/api.d.ts +9 -0
  29. package/dist/channels/slack/api.d.ts.map +1 -1
  30. package/dist/channels/slack/api.js +36 -5
  31. package/dist/channels/slack/approvals.d.ts +16 -0
  32. package/dist/channels/slack/approvals.d.ts.map +1 -1
  33. package/dist/channels/slack/approvals.js +86 -0
  34. package/dist/channels/slack/cli-project.d.ts +19 -0
  35. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  36. package/dist/channels/slack/cli-project.js +234 -0
  37. package/dist/channels/slack/defaults.d.ts.map +1 -1
  38. package/dist/channels/slack/defaults.js +110 -81
  39. package/dist/channels/slack/dispatch.d.ts +7 -1
  40. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  41. package/dist/channels/slack/dispatch.js +47 -23
  42. package/dist/channels/slack/inbound.d.ts +8 -0
  43. package/dist/channels/slack/inbound.d.ts.map +1 -1
  44. package/dist/channels/slack/inbound.js +32 -0
  45. package/dist/channels/slack/init.d.ts +3 -2
  46. package/dist/channels/slack/init.d.ts.map +1 -1
  47. package/dist/channels/slack/init.js +5 -5
  48. package/dist/channels/slack/interactive.d.ts +2 -1
  49. package/dist/channels/slack/interactive.d.ts.map +1 -1
  50. package/dist/channels/slack/interactive.js +15 -4
  51. package/dist/channels/slack/live-delivery.d.ts +26 -29
  52. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  53. package/dist/channels/slack/live-delivery.js +172 -73
  54. package/dist/channels/slack/manifest.d.ts +1 -1
  55. package/dist/channels/slack/manifest.d.ts.map +1 -1
  56. package/dist/channels/slack/manifest.js +6 -2
  57. package/dist/channels/slack/nudge-carrier.d.ts +2 -0
  58. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
  59. package/dist/channels/slack/nudge-carrier.js +48 -1
  60. package/dist/channels/slack/setup.d.ts +1 -1
  61. package/dist/channels/slack/setup.d.ts.map +1 -1
  62. package/dist/channels/slack/setup.js +10 -6
  63. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  64. package/dist/channels/slack/slack-channel.js +3 -2
  65. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  66. package/dist/channels/slack/tool-approval-events.js +91 -1
  67. package/dist/docs/404.html +2 -2
  68. package/dist/docs/ab.html +4 -4
  69. package/dist/docs/assets/{app.BBj0klBO.js → app.B3rWNYE1.js} +1 -1
  70. package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
  71. package/dist/docs/assets/chunks/{VPLocalSearchBox.CUEXpL78.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
  72. package/dist/docs/assets/chunks/{theme.DabmQnia.js → theme.B40_SXuv.js} +2 -2
  73. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  74. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  75. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  76. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  77. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  78. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  79. package/dist/docs/assets/{index.md.BoHaMdeZ.lean.js → index.md.CVeRUOeZ.lean.js} +1 -1
  80. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  81. package/dist/docs/assets/{reference_cli.md.BsMOxDvh.js → reference_cli.md.RyZf5OTE.js} +10 -6
  82. package/dist/docs/assets/{reference_cli.md.BsMOxDvh.lean.js → reference_cli.md.RyZf5OTE.lean.js} +1 -1
  83. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  84. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.5zOAbV86.js} +1 -1
  85. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  86. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.js} +1 -1
  87. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  88. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  89. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
  90. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
  91. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  92. package/dist/docs/building-with-agents.html +4 -4
  93. package/dist/docs/concepts.html +4 -4
  94. package/dist/docs/deployment.html +7 -7
  95. package/dist/docs/deployment.md +5 -4
  96. package/dist/docs/evals.html +4 -4
  97. package/dist/docs/guides/agent-to-agent.html +4 -4
  98. package/dist/docs/guides/cloud-runtime.html +4 -4
  99. package/dist/docs/guides/convert-automation.html +4 -4
  100. package/dist/docs/guides/github.html +4 -4
  101. package/dist/docs/guides/human-in-the-loop.html +4 -4
  102. package/dist/docs/guides/mcp-oauth.html +8 -8
  103. package/dist/docs/guides/mcp-oauth.md +26 -8
  104. package/dist/docs/guides/opentelemetry.html +4 -4
  105. package/dist/docs/guides/slack.html +17 -33
  106. package/dist/docs/guides/slack.md +92 -201
  107. package/dist/docs/guides/webhooks.html +4 -4
  108. package/dist/docs/hashmap.json +1 -1
  109. package/dist/docs/hillclimbing.html +4 -4
  110. package/dist/docs/index.html +6 -6
  111. package/dist/docs/index.md +7 -3
  112. package/dist/docs/llms-full.txt +398 -653
  113. package/dist/docs/llms.txt +3 -2
  114. package/dist/docs/quickstart.html +4 -4
  115. package/dist/docs/reference/agent-config.html +6 -6
  116. package/dist/docs/reference/agent-config.md +2 -2
  117. package/dist/docs/reference/artifacts.html +4 -4
  118. package/dist/docs/reference/channels.html +4 -4
  119. package/dist/docs/reference/cli.html +14 -10
  120. package/dist/docs/reference/cli.md +39 -16
  121. package/dist/docs/reference/connections.html +9 -8
  122. package/dist/docs/reference/connections.md +6 -3
  123. package/dist/docs/reference/hooks.html +4 -4
  124. package/dist/docs/reference/http-api.html +6 -6
  125. package/dist/docs/reference/http-api.md +2 -1
  126. package/dist/docs/reference/instructions.html +4 -4
  127. package/dist/docs/reference/playground.html +4 -4
  128. package/dist/docs/reference/project-layout.html +4 -4
  129. package/dist/docs/reference/prompt.html +4 -4
  130. package/dist/docs/reference/schedules.html +4 -4
  131. package/dist/docs/reference/sessions.html +5 -5
  132. package/dist/docs/reference/sessions.md +4 -2
  133. package/dist/docs/reference/skills.html +4 -4
  134. package/dist/docs/reference/subagents.html +4 -4
  135. package/dist/docs/reference/tools.html +6 -6
  136. package/dist/docs/reference/tools.md +9 -2
  137. package/dist/docs/scaffolding-agents.html +4 -4
  138. package/dist/docs/storage.html +4 -4
  139. package/dist/docs/templates/agentic-owners.html +4 -4
  140. package/dist/docs/templates/agents-md.html +28 -0
  141. package/dist/docs/templates/agents-md.md +105 -0
  142. package/dist/docs/templates/code-wiki.html +28 -0
  143. package/dist/docs/templates/code-wiki.md +91 -0
  144. package/dist/docs/templates/demo.html +5 -5
  145. package/dist/docs/templates/pr-autofixer.html +4 -4
  146. package/dist/docs/templates/security-help.html +4 -4
  147. package/dist/docs/templates/security-reviewer.html +5 -5
  148. package/dist/docs/templates/triage.html +4 -4
  149. package/dist/docs/troubleshooting.html +5 -5
  150. package/dist/docs/troubleshooting.md +2 -2
  151. package/dist/evals/run-facts.d.ts.map +1 -1
  152. package/dist/evals/run-facts.js +27 -0
  153. package/dist/files-backends/cursor-hosted.d.ts +26 -4
  154. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  155. package/dist/files-backends/cursor-hosted.js +32 -4
  156. package/dist/internal/advertise-tools.d.ts +2 -0
  157. package/dist/internal/advertise-tools.d.ts.map +1 -1
  158. package/dist/internal/advertise-tools.js +30 -10
  159. package/dist/internal/approval-gate.d.ts +12 -13
  160. package/dist/internal/approval-gate.d.ts.map +1 -1
  161. package/dist/internal/approval-gate.js +16 -21
  162. package/dist/internal/bounded.d.ts +7 -0
  163. package/dist/internal/bounded.d.ts.map +1 -0
  164. package/dist/internal/bounded.js +32 -0
  165. package/dist/internal/chat-client.js +10 -0
  166. package/dist/internal/cli-deploy.d.ts +5 -0
  167. package/dist/internal/cli-deploy.d.ts.map +1 -1
  168. package/dist/internal/cli-deploy.js +57 -18
  169. package/dist/internal/cli-mcp-oauth.js +8 -18
  170. package/dist/internal/cli-slack-manual.d.ts +24 -2
  171. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  172. package/dist/internal/cli-slack-manual.js +251 -20
  173. package/dist/internal/cli-slack.d.ts +9 -2
  174. package/dist/internal/cli-slack.d.ts.map +1 -1
  175. package/dist/internal/cli-slack.js +29 -82
  176. package/dist/internal/conversation-mirror.d.ts +11 -8
  177. package/dist/internal/conversation-mirror.d.ts.map +1 -1
  178. package/dist/internal/conversation-mirror.js +116 -55
  179. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  180. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  181. package/dist/internal/cursor/account-mcp.js +17 -0
  182. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  183. package/dist/internal/cursor/backend-client.js +22 -48
  184. package/dist/internal/cursor/session-token.d.ts +45 -0
  185. package/dist/internal/cursor/session-token.d.ts.map +1 -0
  186. package/dist/internal/cursor/session-token.js +99 -0
  187. package/dist/internal/cursor-agent-template.d.ts +1 -1
  188. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  189. package/dist/internal/cursor-agent-template.js +2 -0
  190. package/dist/internal/deploy-client.d.ts +17 -0
  191. package/dist/internal/deploy-client.d.ts.map +1 -1
  192. package/dist/internal/deploy-client.js +31 -10
  193. package/dist/internal/env-file.d.ts +19 -0
  194. package/dist/internal/env-file.d.ts.map +1 -0
  195. package/dist/internal/env-file.js +90 -0
  196. package/dist/internal/framework-file-storage.d.ts +61 -0
  197. package/dist/internal/framework-file-storage.d.ts.map +1 -0
  198. package/dist/internal/framework-file-storage.js +210 -0
  199. package/dist/internal/framework-storage-selection.d.ts +11 -5
  200. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  201. package/dist/internal/framework-storage-selection.js +38 -11
  202. package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
  203. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  204. package/dist/internal/hosted-delivery-protocol.js +7 -0
  205. package/dist/internal/hosted-delivery.d.ts +13 -0
  206. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  207. package/dist/internal/hosted-delivery.js +64 -19
  208. package/dist/internal/hosted-execution-diag.d.ts +48 -0
  209. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  210. package/dist/internal/hosted-execution-diag.js +150 -0
  211. package/dist/internal/http-channel.d.ts.map +1 -1
  212. package/dist/internal/http-channel.js +81 -1
  213. package/dist/internal/init-project.d.ts.map +1 -1
  214. package/dist/internal/init-project.js +4 -0
  215. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  216. package/dist/internal/install-cursor-skills.js +1 -3
  217. package/dist/internal/logs-client.d.ts +5 -0
  218. package/dist/internal/logs-client.d.ts.map +1 -1
  219. package/dist/internal/logs-client.js +5 -0
  220. package/dist/internal/managed-delivery.d.ts +80 -0
  221. package/dist/internal/managed-delivery.d.ts.map +1 -0
  222. package/dist/internal/managed-delivery.js +86 -0
  223. package/dist/internal/mcp-endpoint.js +2 -3
  224. package/dist/internal/mcp-host.d.ts +10 -0
  225. package/dist/internal/mcp-host.d.ts.map +1 -1
  226. package/dist/internal/mcp-host.js +57 -8
  227. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  228. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  229. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  230. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  231. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  232. package/dist/internal/mcp-oauth-persist.js +92 -0
  233. package/dist/internal/mcp-oauth.d.ts +1 -0
  234. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  235. package/dist/internal/mcp-oauth.js +10 -2
  236. package/dist/internal/otel/collector.d.ts.map +1 -1
  237. package/dist/internal/otel/collector.js +22 -9
  238. package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
  239. package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
  240. package/dist/internal/runtime-dispatch-runner.js +7 -0
  241. package/dist/internal/sdk-runner.d.ts +11 -0
  242. package/dist/internal/sdk-runner.d.ts.map +1 -1
  243. package/dist/internal/sdk-runner.js +39 -8
  244. package/dist/internal/server.d.ts +3 -0
  245. package/dist/internal/server.d.ts.map +1 -1
  246. package/dist/internal/server.js +49 -21
  247. package/dist/internal/session-engine.d.ts +101 -3
  248. package/dist/internal/session-engine.d.ts.map +1 -1
  249. package/dist/internal/session-engine.js +577 -46
  250. package/dist/internal/sessions-client.d.ts +9 -7
  251. package/dist/internal/sessions-client.d.ts.map +1 -1
  252. package/dist/internal/sessions-client.js +3 -1
  253. package/dist/internal/steering.d.ts +15 -0
  254. package/dist/internal/steering.d.ts.map +1 -0
  255. package/dist/internal/steering.js +18 -0
  256. package/dist/internal/storage-coordinator.d.ts +7 -0
  257. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  258. package/dist/internal/storage-coordinator.js +28 -5
  259. package/dist/internal/storage-roles.d.ts +5 -0
  260. package/dist/internal/storage-roles.d.ts.map +1 -1
  261. package/dist/internal/stream-progress.d.ts.map +1 -1
  262. package/dist/internal/stream-progress.js +10 -0
  263. package/dist/internal/tool-policy.d.ts +9 -1
  264. package/dist/internal/tool-policy.d.ts.map +1 -1
  265. package/dist/internal/tool-policy.js +13 -0
  266. package/dist/internal/trajectory.d.ts.map +1 -1
  267. package/dist/internal/trajectory.js +10 -0
  268. package/dist/internal/turn-outbox.d.ts +24 -0
  269. package/dist/internal/turn-outbox.d.ts.map +1 -0
  270. package/dist/internal/turn-outbox.js +80 -0
  271. package/dist/playground/assets/{index-CZA1uEWA.js → index-DrkI6y5O.js} +51 -51
  272. package/dist/playground/assets/index-DzNGwm7q.css +1 -0
  273. package/dist/playground/index.html +2 -2
  274. package/dist/storage-backends/cursor-hosted.d.ts +9 -6
  275. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  276. package/dist/storage-backends/cursor-hosted.js +16 -3
  277. package/dist/storage.d.ts +8 -0
  278. package/dist/storage.d.ts.map +1 -1
  279. package/dist/types.d.ts +65 -8
  280. package/dist/types.d.ts.map +1 -1
  281. package/docs/README.md +7 -3
  282. package/docs/deployment.md +5 -4
  283. package/docs/guides/mcp-oauth.md +26 -8
  284. package/docs/guides/slack.md +93 -202
  285. package/docs/reference/agent-config.md +2 -2
  286. package/docs/reference/cli.md +39 -16
  287. package/docs/reference/connections.md +6 -3
  288. package/docs/reference/http-api.md +2 -1
  289. package/docs/reference/sessions.md +4 -2
  290. package/docs/reference/tools.md +9 -2
  291. package/docs/templates/agents-md.md +110 -0
  292. package/docs/templates/code-wiki.md +96 -0
  293. package/docs/troubleshooting.md +2 -2
  294. package/package.json +2 -2
  295. package/skills/ab/SKILL.md +13 -97
  296. package/skills/create-agent/SKILL.md +124 -223
  297. package/skills/debug/SKILL.md +34 -68
  298. package/skills/evals/SKILL.md +55 -180
  299. package/skills/framework-map/SKILL.md +83 -145
  300. package/skills/github/SKILL.md +53 -111
  301. package/skills/hillclimb/SKILL.md +40 -163
  302. package/skills/mcp-auth/SKILL.md +51 -107
  303. package/skills/otel/SKILL.md +22 -68
  304. package/skills/setup-slack/SKILL.md +91 -212
  305. package/src/bin/agent-serve.ts +13 -1
  306. package/src/channels/deployments/types.ts +1 -0
  307. package/src/channels/origin/api.ts +264 -33
  308. package/src/channels/origin/binding.ts +57 -28
  309. package/src/channels/origin/checks.ts +6 -6
  310. package/src/channels/origin/cursor-account.ts +5 -3
  311. package/src/channels/origin/index.ts +1 -0
  312. package/src/channels/origin/origin-channel.ts +4 -0
  313. package/src/channels/origin/reactions.ts +82 -0
  314. package/src/channels/origin/types.ts +11 -0
  315. package/src/channels/slack/api.ts +41 -5
  316. package/src/channels/slack/approvals.ts +105 -0
  317. package/src/channels/slack/cli-project.ts +267 -0
  318. package/src/channels/slack/defaults.ts +122 -93
  319. package/src/channels/slack/dispatch.ts +56 -22
  320. package/src/channels/slack/inbound.ts +43 -0
  321. package/src/channels/slack/init.ts +10 -5
  322. package/src/channels/slack/interactive.ts +35 -9
  323. package/src/channels/slack/live-delivery.ts +235 -114
  324. package/src/channels/slack/manifest.ts +6 -2
  325. package/src/channels/slack/nudge-carrier.ts +48 -1
  326. package/src/channels/slack/setup.ts +10 -6
  327. package/src/channels/slack/slack-channel.ts +3 -0
  328. package/src/channels/slack/tool-approval-events.ts +93 -0
  329. package/src/evals/run-facts.ts +32 -0
  330. package/src/files-backends/cursor-hosted.ts +40 -7
  331. package/src/internal/advertise-tools.ts +49 -12
  332. package/src/internal/approval-gate.ts +20 -29
  333. package/src/internal/bounded.ts +28 -0
  334. package/src/internal/chat-client.ts +19 -0
  335. package/src/internal/cli-deploy.ts +91 -9
  336. package/src/internal/cli-mcp-oauth.ts +7 -21
  337. package/src/internal/cli-slack-manual.ts +350 -18
  338. package/src/internal/cli-slack.ts +40 -84
  339. package/src/internal/conversation-mirror.ts +145 -69
  340. package/src/internal/cursor/account-mcp.ts +21 -0
  341. package/src/internal/cursor/backend-client.ts +34 -58
  342. package/src/internal/cursor/session-token.ts +115 -0
  343. package/src/internal/cursor-agent-template.ts +2 -0
  344. package/src/internal/deploy-client.ts +43 -0
  345. package/src/internal/env-file.ts +84 -0
  346. package/src/internal/framework-file-storage.ts +228 -0
  347. package/src/internal/framework-storage-selection.ts +51 -11
  348. package/src/internal/hosted-delivery-protocol.ts +7 -0
  349. package/src/internal/hosted-delivery.ts +107 -24
  350. package/src/internal/hosted-execution-diag.ts +207 -0
  351. package/src/internal/http-channel.ts +99 -0
  352. package/src/internal/init-project.ts +4 -0
  353. package/src/internal/install-cursor-skills.ts +1 -4
  354. package/src/internal/logs-client.ts +5 -0
  355. package/src/internal/managed-delivery.ts +125 -0
  356. package/src/internal/mcp-endpoint.ts +13 -4
  357. package/src/internal/mcp-host.ts +77 -11
  358. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  359. package/src/internal/mcp-oauth-persist.ts +127 -0
  360. package/src/internal/mcp-oauth.ts +17 -1
  361. package/src/internal/otel/collector.ts +30 -0
  362. package/src/internal/runtime-dispatch-runner.ts +10 -0
  363. package/src/internal/sdk-runner.ts +56 -8
  364. package/src/internal/server.ts +61 -9
  365. package/src/internal/session-engine.ts +723 -60
  366. package/src/internal/sessions-client.ts +17 -8
  367. package/src/internal/steering.ts +17 -0
  368. package/src/internal/storage-coordinator.ts +38 -8
  369. package/src/internal/storage-roles.ts +7 -0
  370. package/src/internal/stream-progress.ts +19 -0
  371. package/src/internal/tool-policy.ts +18 -0
  372. package/src/internal/trajectory.ts +10 -0
  373. package/src/internal/turn-outbox.ts +85 -0
  374. package/src/storage-backends/cursor-hosted.ts +22 -8
  375. package/src/storage.ts +12 -0
  376. package/src/types.ts +70 -9
  377. package/templates/agents-md/README.md +39 -0
  378. package/templates/agents-md/agent/agent.ts +8 -0
  379. package/templates/agents-md/agent/channels/github.ts +10 -0
  380. package/templates/agents-md/agent/channels/queue.ts +28 -0
  381. package/templates/agents-md/agent/channels/slack.ts +9 -0
  382. package/templates/agents-md/agent/instructions.md +39 -0
  383. package/templates/agents-md/agent/lib/collect.ts +439 -0
  384. package/templates/agents-md/agent/lib/config.ts +8 -0
  385. package/templates/agents-md/agent/lib/paths.ts +51 -0
  386. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  387. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  388. package/templates/agents-md/evals/agents.eval.ts +135 -0
  389. package/templates/agents-md/evals/evals.config.ts +5 -0
  390. package/templates/agents-md/init.json +38 -0
  391. package/templates/agents-md/package.json +18 -0
  392. package/templates/agents-md/tsconfig.json +12 -0
  393. package/templates/code-wiki/README.md +34 -0
  394. package/templates/code-wiki/agent/agent.ts +8 -0
  395. package/templates/code-wiki/agent/channels/github.ts +144 -0
  396. package/templates/code-wiki/agent/instructions.md +39 -0
  397. package/templates/code-wiki/agent/lib/repos.ts +5 -0
  398. package/templates/code-wiki/agent/lib/wiki.ts +22 -0
  399. package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
  400. package/templates/code-wiki/evals/evals.config.ts +5 -0
  401. package/templates/code-wiki/evals/wiki.eval.ts +132 -0
  402. package/templates/code-wiki/init.json +25 -0
  403. package/templates/code-wiki/package.json +18 -0
  404. package/templates/code-wiki/tsconfig.json +12 -0
  405. package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.js +0 -1
  406. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  407. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  408. package/dist/docs/assets/index.md.BoHaMdeZ.js +0 -5
  409. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  410. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  411. package/dist/docs/design/agsh.md +0 -406
  412. package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
  413. package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
  414. package/dist/internal/deployment-scm-event-stream.js +0 -474
  415. package/dist/playground/assets/index-CF7hUDnQ.css +0 -1
  416. package/docs/design/agsh.md +0 -406
  417. package/src/internal/deployment-scm-event-stream.ts +0 -593
  418. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  419. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  420. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  421. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.5zOAbV86.lean.js} +0 -0
  422. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  423. /package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.lean.js} +0 -0
  424. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -15,6 +15,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
15
15
  step((generator = generator.apply(thisArg, _arguments || [])).next());
16
16
  });
17
17
  };
18
+ import { AsyncLocalStorage } from "node:async_hooks";
18
19
  import { mkdir, rm, writeFile } from "node:fs/promises";
19
20
  import { dirname, join } from "node:path";
20
21
  import { mergeABInstructions, resolveABMaxPlaygroundSessions, resolveABSplit, } from "../ab.js";
@@ -24,11 +25,11 @@ import { SlackAskDedupeStore, SlackNudgeStore, } from "../channels/slack/nudge-s
24
25
  import { resolveDedicatedSlackHostBinding } from "../channels/slack/slack-channel.js";
25
26
  import { AGENT_SERVE_USER_STORE_FILES_SOURCE_ID, agentStoreKeys, } from "../files.js";
26
27
  import { cursorHostedFiles, isCursorHostedFilesAvailable, isCursorUserStoreFilesAvailable, } from "../files-backends/cursor-hosted.js";
27
- import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
28
+ import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isCursorAccountTransport, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
28
29
  import { ABCollector } from "./ab-collector.js";
29
30
  import { foldABStream } from "./ab-fold.js";
30
31
  import { buildABSnapshot } from "./ab-snapshot.js";
31
- import { resolveAdvertisedTools } from "./advertise-tools.js";
32
+ import { resolveAdvertisedTools, } from "./advertise-tools.js";
32
33
  import { ApprovalGate } from "./approval-gate.js";
33
34
  import { ArtifactsStore } from "./artifacts-store.js";
34
35
  import { SessionAuthError, samePrincipal } from "./auth.js";
@@ -47,19 +48,22 @@ import { createHostPlatforms, createHostSlackClient, } from "./host-platforms.js
47
48
  import { HostedAdmissionLocalSendError, isDuringHostedAdmission, } from "./hosted-admission-context.js";
48
49
  import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, parseContinuationToken, } from "./ids.js";
49
50
  import { validateToolInput } from "./json-schema.js";
50
- import { McpHost } from "./mcp-host.js";
51
+ import { isMcpNeedsAuthError, McpHost } from "./mcp-host.js";
52
+ import { McpOauthOnDemandHub } from "./mcp-oauth-on-demand.js";
51
53
  import { shouldStartProcessMemoryTelemetry, startProcessMemoryTelemetry, } from "./process-memory-telemetry.js";
52
54
  import { buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX, } from "./prompt-context.js";
53
55
  import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
54
56
  import { sessionExternalLinks } from "./session-links.js";
55
57
  import { SessionStore } from "./session-store.js";
56
58
  import { syncSkillsToStore } from "./skills-sync.js";
59
+ import { steeringEnabled } from "./steering.js";
57
60
  import { StorageCoordinator } from "./storage-coordinator.js";
58
- import { decideToolCall, resolveEffect } from "./tool-policy.js";
61
+ import { decideToolCall, directCallEffect } from "./tool-policy.js";
59
62
  import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
60
63
  import { estimateTurnCostFromApi, roundCostUsd, } from "./turn-cost.js";
61
64
  import { TurnGovernor } from "./turn-governor.js";
62
65
  import { TurnLeaseStore } from "./turn-lease-store.js";
66
+ import { TurnOutbox } from "./turn-outbox.js";
63
67
  import { buildAgentsMdContent, buildCloudMemorySection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, writeWorkspaceFiles, } from "./workspace.js";
64
68
  export const HTTP_CHANNEL_ID = "http";
65
69
  export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
@@ -133,6 +137,19 @@ export class ApprovalInterruptedError extends Error {
133
137
  this.name = "ApprovalInterruptedError";
134
138
  }
135
139
  }
140
+ export class AuthNotFoundError extends Error {
141
+ constructor(sessionId, callId) {
142
+ super(`No pending MCP auth for session ${sessionId} call ${callId}`);
143
+ this.name = "AuthNotFoundError";
144
+ }
145
+ }
146
+ export class AuthInterruptedError extends Error {
147
+ constructor(sessionId, callId) {
148
+ super(`MCP auth for session ${sessionId} call ${callId} is no longer live (host may have restarted while the tool was parked)`);
149
+ this.name = "AuthInterruptedError";
150
+ }
151
+ }
152
+ const mcpAuthAls = new AsyncLocalStorage();
136
153
  export class SessionEngine {
137
154
  constructor(options) {
138
155
  var _a, _b, _c, _d, _e;
@@ -142,6 +159,13 @@ export class SessionEngine {
142
159
  /** Sessions with a turn being admitted (before the run promise exists). */
143
160
  this.pendingTurns = new Set();
144
161
  this.activeTurns = new Map();
162
+ /**
163
+ * Sessions whose workspace a turn in this process has materialized. A
164
+ * session is busy from admit — before the governor slot and the turn's
165
+ * own materialize — so a read running alongside a turn consults this,
166
+ * not busy, to know whether the workspace is ready to be left alone.
167
+ */
168
+ this.materializedWorkspaces = new Set();
145
169
  /** Context used by the cloud turn's HTTP MCP callbacks. */
146
170
  this.activeToolContexts = new Map();
147
171
  /**
@@ -178,6 +202,8 @@ export class SessionEngine {
178
202
  this.dispatchChains = new Map();
179
203
  this.backgroundWork = new Set();
180
204
  this.approvals = new ApprovalGate();
205
+ this.auths = new ApprovalGate();
206
+ this.oauthHub = new McpOauthOnDemandHub();
181
207
  /** Serve-time resolved connections (see {@link setResolvedConnections}). */
182
208
  this.resolvedConnections = [];
183
209
  /** Sessions whose completed turns are fully priced (no further backfill). */
@@ -202,6 +228,7 @@ export class SessionEngine {
202
228
  this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
203
229
  this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
204
230
  this.turnLeases = new TurnLeaseStore(join(options.stateRoot, "turn-leases"));
231
+ this.outbox = new TurnOutbox(join(options.stateRoot, "outbox"));
205
232
  this.logs = new EventLogStore((sessionId) => this.sessions.eventFilePath(sessionId), (sessionId, error) => this.logger(`[agent-sdk] failed to persist events for ${sessionId}: ${describeError(error)}`));
206
233
  // Framework session state (records, event chunks, continuations) is
207
234
  // mirrored to whatever this selects; see framework-storage-selection.ts
@@ -283,10 +310,20 @@ export class SessionEngine {
283
310
  setCloudServerToolsConnection(connection) {
284
311
  this.cloudServerToolsConnection = connection;
285
312
  }
313
+ setMcpOauthCallbackUrl(url) {
314
+ this.oauthHub.callbackUrl = url;
315
+ this.mcpHost.setOAuthRedirectUrl(url);
316
+ }
317
+ setCursorAccountAuthProbe(probe) {
318
+ this.probeCursorAccount = probe;
319
+ }
320
+ get oauthCallbackHub() {
321
+ return this.oauthHub;
322
+ }
286
323
  /** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
287
324
  get host() {
288
325
  return {
289
- mcp: this.mcpHost,
326
+ mcp: this.wrapMcp(this.mcpHost),
290
327
  github: this.platforms.github,
291
328
  slack: this.platforms.slack,
292
329
  reminders: this.reminderApi,
@@ -396,6 +433,9 @@ export class SessionEngine {
396
433
  this.hasBorrowedWorkspace(record)) {
397
434
  return;
398
435
  }
436
+ if (steeringEnabled()) {
437
+ return;
438
+ }
399
439
  try {
400
440
  yield materializeSlackNudgeCarrier({
401
441
  harnessCwd,
@@ -580,12 +620,33 @@ export class SessionEngine {
580
620
  const now = new Date().toISOString();
581
621
  const channelState = this.initialChannelState(channelId, sendOptions.state);
582
622
  const purpose = sendOptions.purpose === "eval" ? "eval" : "live";
583
- let record = Object.assign(Object.assign({ sessionId: (_d = sendOptions.hostedSessionId) !== null && _d !== void 0 ? _d : newSessionId(), channelId, mode: (_e = sendOptions.mode) !== null && _e !== void 0 ? _e : "chat", purpose }, (sendOptions.dryRun === true ? { dryRun: true } : {})), { continuationKey: (_f = sendOptions.continuationToken) !== null && _f !== void 0 ? _f : newContinuationKey(), auth, title: sendOptions.title, channelState, workspaceOverride: sendOptions.workspaceDir, pendingWorkspaceFiles: sendOptions.workspaceFiles, pendingImages: sendOptions.images, cloudOverride: sendOptions.cloud, sdkAgentId: sendOptions.sdkAgentId, turnCount: 0, createdAt: now, updatedAt: now });
623
+ let record = {
624
+ sessionId: (_d = sendOptions.hostedSessionId) !== null && _d !== void 0 ? _d : newSessionId(),
625
+ channelId,
626
+ mode: (_e = sendOptions.mode) !== null && _e !== void 0 ? _e : "chat",
627
+ purpose,
628
+ dryRun: sendOptions.dryRun === true ? true : undefined,
629
+ continuationKey: (_f = sendOptions.continuationToken) !== null && _f !== void 0 ? _f : newContinuationKey(),
630
+ auth,
631
+ title: sendOptions.title,
632
+ channelState,
633
+ workspaceOverride: sendOptions.workspaceDir,
634
+ pendingWorkspaceFiles: sendOptions.workspaceFiles,
635
+ pendingImages: sendOptions.images,
636
+ cloudOverride: sendOptions.cloud,
637
+ sdkAgentId: sendOptions.sdkAgentId,
638
+ turnCount: 0,
639
+ createdAt: now,
640
+ updatedAt: now,
641
+ };
584
642
  yield this.sessions.save(record);
585
643
  (_a = this.storage) === null || _a === void 0 ? void 0 : _a.sessionRecord(record);
586
644
  yield this.appendEvent(record.sessionId, {
587
645
  type: "session.started",
588
- data: { channelId },
646
+ data: {
647
+ channelId,
648
+ dryRun: record.dryRun === true ? true : undefined,
649
+ },
589
650
  });
590
651
  record = yield this.enrollABs(record);
591
652
  const since = (yield this.logs.get(record.sessionId)).count;
@@ -787,10 +848,28 @@ export class SessionEngine {
787
848
  const prior = (_a = this.admissionChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
788
849
  const admitted = prior
789
850
  .catch(() => { })
790
- .then(() => this.admitTurn(sessionId, message, options));
851
+ .then(() => __awaiter(this, void 0, void 0, function* () {
852
+ // Another server holds the turn: hand the message to it and coalesce.
853
+ if (yield this.turnLeases.runningElsewhere(sessionId)) {
854
+ yield this.outbox.post(sessionId, { message, options });
855
+ return true;
856
+ }
857
+ return this.admitTurn(sessionId, message, options);
858
+ }));
791
859
  this.admissionChains.set(sessionId, admitted.catch(() => { }));
792
860
  return admitted;
793
861
  }
862
+ /** Admit what other servers handed to this turn's holder. Never awaited by the lifecycle. */
863
+ drainOutbox(sessionId) {
864
+ void this.outbox
865
+ .takeAll(sessionId)
866
+ .then((items) => {
867
+ for (const item of items) {
868
+ void this.startTurn(sessionId, item.message, item.options).catch(() => { });
869
+ }
870
+ })
871
+ .catch(() => { });
872
+ }
794
873
  enqueueCoalesced(sessionId, item) {
795
874
  var _a;
796
875
  const queue = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
@@ -901,14 +980,46 @@ export class SessionEngine {
901
980
  });
902
981
  });
903
982
  }
983
+ steerIntoRunningTurn(sessionId, message) {
984
+ return __awaiter(this, void 0, void 0, function* () {
985
+ if (this.runner.steer === undefined) {
986
+ return "revert_to_followup";
987
+ }
988
+ try {
989
+ return yield this.runner.steer({ sessionId, text: message });
990
+ }
991
+ catch (error) {
992
+ this.logger(`[session] steer failed for ${sessionId}: ${describeError(error)}`);
993
+ return "revert_to_followup";
994
+ }
995
+ });
996
+ }
904
997
  admitTurn(sessionId_1, message_1) {
905
998
  return __awaiter(this, arguments, void 0, function* (sessionId, message, options = {}) {
906
999
  var _a;
907
1000
  const admission = (_a = options.admission) !== null && _a !== void 0 ? _a : "preempt";
1001
+ if (admission === "coalesce" &&
1002
+ steeringEnabled() &&
1003
+ this.isTurnRunning(sessionId)) {
1004
+ const toolCtx = this.activeToolContexts.get(sessionId);
1005
+ const steerOutcome = yield this.steerIntoRunningTurn(sessionId, message);
1006
+ if (steerOutcome === "complete_delivered") {
1007
+ this.logger(`[session] steered into running turn ${sessionId}`);
1008
+ if (toolCtx !== undefined) {
1009
+ toolCtx.emit({ type: "message.received", data: { text: message } });
1010
+ toolCtx.emit({ type: "nudge.delivered", data: {} });
1011
+ }
1012
+ return true;
1013
+ }
1014
+ }
1015
+ // Re-checked after the steer: a turn that ended during it falls through
1016
+ // and this message starts the next turn itself. Enqueueing here instead
1017
+ // would orphan it — the completion drain already ran.
908
1018
  if (admission === "coalesce" && this.isTurnRunning(sessionId)) {
909
- this.enqueueCoalesced(sessionId, Object.assign({ message }, (options.sourceTs === undefined
910
- ? {}
911
- : { sourceTs: options.sourceTs })));
1019
+ this.enqueueCoalesced(sessionId, {
1020
+ message,
1021
+ sourceTs: options.sourceTs,
1022
+ });
912
1023
  this.logger(`[session] coalesce enqueue ${sessionId}`);
913
1024
  return true;
914
1025
  }
@@ -968,6 +1079,8 @@ export class SessionEngine {
968
1079
  let lifecycle;
969
1080
  lifecycle = (() => __awaiter(this, void 0, void 0, function* () {
970
1081
  let outcome;
1082
+ const poll = setInterval(() => this.drainOutbox(sessionId), 1000);
1083
+ poll.unref();
971
1084
  try {
972
1085
  outcome = yield this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
973
1086
  }
@@ -977,6 +1090,9 @@ export class SessionEngine {
977
1090
  errorMessage: describeError(error),
978
1091
  };
979
1092
  }
1093
+ finally {
1094
+ clearInterval(poll);
1095
+ }
980
1096
  this.lastOutcomes.set(sessionId, outcome);
981
1097
  const wasCurrent = this.activeTurns.get(sessionId) === lifecycle;
982
1098
  if (wasCurrent) {
@@ -1002,6 +1118,10 @@ export class SessionEngine {
1002
1118
  // The drain may have nested a successor turn, which took its own
1003
1119
  // reference; the lease outlives this release until that one ends too.
1004
1120
  yield lease.release();
1121
+ // Handed off between the last poll and the release: nobody else will.
1122
+ if (!abort.signal.aborted) {
1123
+ this.drainOutbox(sessionId);
1124
+ }
1005
1125
  return outcome;
1006
1126
  }))();
1007
1127
  this.activeTurns.set(sessionId, lifecycle);
@@ -1051,6 +1171,7 @@ export class SessionEngine {
1051
1171
  this.coalesceDrains.has(sessionId) ||
1052
1172
  ((_b = (_a = this.coalesceQueues.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
1053
1173
  this.clearCoalesced(sessionId);
1174
+ yield this.outbox.clear(sessionId);
1054
1175
  const record = yield this.sessions.get(sessionId);
1055
1176
  const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
1056
1177
  if (continuationKey !== undefined && continuationKey !== null) {
@@ -1239,6 +1360,7 @@ export class SessionEngine {
1239
1360
  seed: isFirstTurn,
1240
1361
  stateRoot: this.stateRoot,
1241
1362
  });
1363
+ this.materializedWorkspaces.add(sessionId);
1242
1364
  // Must precede the carrier: the harness resolves rules, skills,
1243
1365
  // `mcp.json` and hooks from its git root, so a workspace nested in an
1244
1366
  // outer repo would load that repo's hooks instead of the ones written
@@ -1301,12 +1423,15 @@ export class SessionEngine {
1301
1423
  }
1302
1424
  catch (error) {
1303
1425
  this.approvals.cancelSession(sessionId, "deny");
1426
+ this.auths.cancelSession(sessionId, "deny");
1304
1427
  outcome = { status: "error", errorMessage: describeError(error) };
1305
1428
  }
1306
1429
  finally {
1307
1430
  permit.release();
1308
1431
  this.approvals.cancelSession(sessionId, "deny");
1432
+ this.auths.cancelSession(sessionId, "deny");
1309
1433
  yield this.clearPendingApprovals(sessionId);
1434
+ yield this.clearPendingAuths(sessionId);
1310
1435
  // Anything still queued belongs to the next turn's boundary drain, not
1311
1436
  // to this turn's edge detection.
1312
1437
  this.deliveredNudges.delete(sessionId);
@@ -1797,11 +1922,17 @@ export class SessionEngine {
1797
1922
  // named passthroughs resolved fresh on every turn — reminder fires and
1798
1923
  // post-restart follow-ups included — and a listing failure throws,
1799
1924
  // failing the turn closed.
1800
- for (const tool of yield resolveAdvertisedTools({
1925
+ for (const tool of yield mcpAuthAls.run({
1926
+ sessionId: record.sessionId,
1927
+ turnId,
1928
+ callId: newToolCallId("mcp"),
1929
+ toolName: "mcp",
1930
+ emit,
1931
+ }, () => resolveAdvertisedTools({
1801
1932
  agent,
1802
1933
  session: this.sessionInfo(record),
1803
- hostMcp: this.mcpHost,
1804
- })) {
1934
+ hostMcp: this.advertiseMcp(),
1935
+ }))) {
1805
1936
  register(tool);
1806
1937
  }
1807
1938
  return tools;
@@ -1909,7 +2040,13 @@ export class SessionEngine {
1909
2040
  }
1910
2041
  const started = Date.now();
1911
2042
  this.logger(`[session] tool ${args.tool.name} start`);
1912
- const result = yield execute(validation.value, toolCtx);
2043
+ const result = yield mcpAuthAls.run({
2044
+ sessionId: args.record.sessionId,
2045
+ turnId: args.turnId,
2046
+ callId: toolCallId,
2047
+ toolName: args.tool.name,
2048
+ emit: args.emit,
2049
+ }, () => execute(validation.value, toolCtx));
1913
2050
  this.logger(`[session] tool ${args.tool.name} done (${Date.now() - started}ms)`);
1914
2051
  return normalizeToolResult(result);
1915
2052
  }
@@ -1934,7 +2071,7 @@ export class SessionEngine {
1934
2071
  args: args.args,
1935
2072
  requestedAt,
1936
2073
  };
1937
- const resolutionPromise = this.approvals.wait(Object.assign({ sessionId: args.sessionId }, pendingRecord));
2074
+ const resolutionPromise = this.approvals.wait(args.sessionId, pendingRecord);
1938
2075
  yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: [
1939
2076
  ...((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
1940
2077
  pendingRecord,
@@ -2020,6 +2157,245 @@ export class SessionEngine {
2020
2157
  yield this.updateRecord(sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: ((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== callId) })); });
2021
2158
  });
2022
2159
  }
2160
+ wrapMcp(inner) {
2161
+ return {
2162
+ names: () => inner.names(),
2163
+ listTools: (name) => this.withMcpAuth(name, () => inner.listTools(name)),
2164
+ callTool: (name, tool, args) => this.withMcpAuth(name, () => inner.callTool(name, tool, args)),
2165
+ };
2166
+ }
2167
+ advertiseMcp() {
2168
+ return {
2169
+ listTools: (name) => this.withMcpListAuth(name, () => this.mcpHost.listTools(name)),
2170
+ callTool: (name, tool, args) => this.withMcpAuth(name, () => this.mcpHost.callTool(name, tool, args)),
2171
+ oneOff: (connection) => {
2172
+ const inner = this.mcpHost.oneOff(connection);
2173
+ return {
2174
+ listTools: () => this.withMcpListAuth(connection.name, () => inner.listTools()),
2175
+ callTool: (tool, args) => this.withMcpAuth(connection.name, () => inner.callTool(tool, args)),
2176
+ };
2177
+ },
2178
+ };
2179
+ }
2180
+ withMcpListAuth(connection, run) {
2181
+ const ctx = mcpAuthAls.getStore();
2182
+ if (ctx === undefined) {
2183
+ return this.withMcpAuth(connection, run);
2184
+ }
2185
+ const toolName = `mcp:${connection}`;
2186
+ return mcpAuthAls.run(Object.assign(Object.assign({}, ctx), { callId: newToolCallId(toolName), toolName }), () => this.withMcpAuth(connection, run));
2187
+ }
2188
+ withMcpAuth(connection, run) {
2189
+ return __awaiter(this, void 0, void 0, function* () {
2190
+ try {
2191
+ return yield run();
2192
+ }
2193
+ catch (error) {
2194
+ if (!isMcpNeedsAuthError(error)) {
2195
+ throw error;
2196
+ }
2197
+ return yield this.parkAndRetryMcp(connection, run, error);
2198
+ }
2199
+ });
2200
+ }
2201
+ parkAndRetryMcp(connection, run, error) {
2202
+ return __awaiter(this, void 0, void 0, function* () {
2203
+ const ctx = mcpAuthAls.getStore();
2204
+ if (ctx === undefined) {
2205
+ throw error;
2206
+ }
2207
+ const minted = yield this.mintAuthUrl(connection, ctx.sessionId);
2208
+ if (minted === undefined) {
2209
+ throw error;
2210
+ }
2211
+ if ("authorized" in minted) {
2212
+ yield this.persistHostedMcpOauth(connection);
2213
+ yield this.mcpHost.dropClient(connection);
2214
+ return yield this.withMcpAuth(connection, run);
2215
+ }
2216
+ if (minted.state !== "") {
2217
+ const bind = this.oauthHub.bind(minted.state, ctx.sessionId, ctx.callId);
2218
+ if (bind === "already_done") {
2219
+ yield this.persistHostedMcpOauth(connection);
2220
+ yield this.mcpHost.dropClient(connection);
2221
+ return yield this.withMcpAuth(connection, run);
2222
+ }
2223
+ }
2224
+ const decision = yield this.parkForAuth({
2225
+ sessionId: ctx.sessionId,
2226
+ turnId: ctx.turnId,
2227
+ callId: ctx.callId,
2228
+ toolName: ctx.toolName,
2229
+ connection,
2230
+ authUrl: minted.authUrl,
2231
+ emit: ctx.emit,
2232
+ });
2233
+ if (decision === "deny") {
2234
+ throw new Error(`MCP connection "${connection}" authorization was denied.`);
2235
+ }
2236
+ yield this.persistHostedMcpOauth(connection);
2237
+ yield this.mcpHost.dropClient(connection);
2238
+ return yield this.withMcpAuth(connection, run);
2239
+ });
2240
+ }
2241
+ persistHostedMcpOauth(connection) {
2242
+ return __awaiter(this, void 0, void 0, function* () {
2243
+ const authored = this.authoredConnection(connection);
2244
+ if (authored === undefined ||
2245
+ !isHostOauthConnectionTransport(authored.transport)) {
2246
+ return;
2247
+ }
2248
+ yield this.oauthHub.persistFor(connection, authored.transport.url);
2249
+ });
2250
+ }
2251
+ authoredConnection(name) {
2252
+ var _a;
2253
+ var _b;
2254
+ return ((_b = this.project.agent.connections.find((entry) => entry.name === name)) !== null && _b !== void 0 ? _b : (_a = this.project.agent.hostConnections) === null || _a === void 0 ? void 0 : _a.find((entry) => entry.name === name));
2255
+ }
2256
+ mintAuthUrl(connection, sessionId) {
2257
+ return __awaiter(this, void 0, void 0, function* () {
2258
+ const authored = this.authoredConnection(connection);
2259
+ if (authored === undefined) {
2260
+ return undefined;
2261
+ }
2262
+ if (isCursorAccountTransport(authored.transport)) {
2263
+ const probe = this.probeCursorAccount;
2264
+ if (probe === undefined) {
2265
+ return undefined;
2266
+ }
2267
+ const authUrl = yield probe(connection);
2268
+ if (authUrl === undefined || authUrl === "") {
2269
+ return undefined;
2270
+ }
2271
+ return { authUrl, state: "" };
2272
+ }
2273
+ if (!isHostOauthConnectionTransport(authored.transport)) {
2274
+ return undefined;
2275
+ }
2276
+ return yield this.oauthHub.mint({
2277
+ connection,
2278
+ url: authored.transport.url,
2279
+ type: authored.transport.type === "sse" ? "sse" : "http",
2280
+ headers: authored.transport.headers,
2281
+ sessionId,
2282
+ });
2283
+ });
2284
+ }
2285
+ parkForAuth(args) {
2286
+ return __awaiter(this, void 0, void 0, function* () {
2287
+ const requestedAt = new Date().toISOString();
2288
+ const pendingRecord = {
2289
+ callId: args.callId,
2290
+ turnId: args.turnId,
2291
+ toolName: args.toolName,
2292
+ connection: args.connection,
2293
+ authUrl: args.authUrl,
2294
+ requestedAt,
2295
+ };
2296
+ const resolutionPromise = this.auths.wait(args.sessionId, pendingRecord);
2297
+ yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: [
2298
+ ...((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
2299
+ pendingRecord,
2300
+ ] })); });
2301
+ args.emit({
2302
+ type: "action.auth_requested",
2303
+ data: {
2304
+ callId: args.callId,
2305
+ toolName: args.toolName,
2306
+ connection: args.connection,
2307
+ authUrl: args.authUrl,
2308
+ },
2309
+ });
2310
+ this.logger(`[session] tool ${args.toolName} awaiting MCP auth ${args.connection} (${args.callId})`);
2311
+ const { decision, by } = yield resolutionPromise;
2312
+ yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId) })); });
2313
+ args.emit({
2314
+ type: "action.auth_resolved",
2315
+ data: {
2316
+ callId: args.callId,
2317
+ toolName: args.toolName,
2318
+ connection: args.connection,
2319
+ decision,
2320
+ by,
2321
+ },
2322
+ });
2323
+ this.logger(`[session] tool ${args.toolName} MCP auth ${decision} (${args.callId})`);
2324
+ return decision;
2325
+ });
2326
+ }
2327
+ clearPendingAuths(sessionId) {
2328
+ return __awaiter(this, void 0, void 0, function* () {
2329
+ yield this.oauthHub.abortSession(sessionId);
2330
+ const current = yield this.sessions.get(sessionId);
2331
+ if (current === undefined ||
2332
+ current.pendingAuths === undefined ||
2333
+ current.pendingAuths.length === 0) {
2334
+ return;
2335
+ }
2336
+ yield this.updateRecord(sessionId, (record) => (Object.assign(Object.assign({}, record), { pendingAuths: [] })));
2337
+ });
2338
+ }
2339
+ listPendingAuths(sessionId) {
2340
+ return __awaiter(this, void 0, void 0, function* () {
2341
+ var _a;
2342
+ const record = yield this.sessions.get(sessionId);
2343
+ if (record === undefined) {
2344
+ return [];
2345
+ }
2346
+ const live = this.auths.list(sessionId);
2347
+ if (live.length > 0) {
2348
+ return live;
2349
+ }
2350
+ return (_a = record.pendingAuths) !== null && _a !== void 0 ? _a : [];
2351
+ });
2352
+ }
2353
+ resolveAuth(sessionId, callId, decision, auth, options) {
2354
+ return __awaiter(this, void 0, void 0, function* () {
2355
+ var _a, _b;
2356
+ const record = yield this.sessions.get(sessionId);
2357
+ if (record === undefined) {
2358
+ throw new AuthNotFoundError(sessionId, callId);
2359
+ }
2360
+ const ownershipAuth = (options === null || options === void 0 ? void 0 : options.asSessionOwner) === true ? record.auth : auth;
2361
+ if (!samePrincipal(record.auth, ownershipAuth)) {
2362
+ throw new SessionAuthError(sessionId);
2363
+ }
2364
+ const pending = (_b = ((_a = record.pendingAuths) !== null && _a !== void 0 ? _a : []).find((item) => item.callId === callId)) !== null && _b !== void 0 ? _b : this.auths.list(sessionId).find((item) => item.callId === callId);
2365
+ if (pending === undefined) {
2366
+ throw new AuthNotFoundError(sessionId, callId);
2367
+ }
2368
+ const resolvedBy = approvalResolverLabel(auth);
2369
+ if (decision === "deny") {
2370
+ yield this.oauthHub.abortCall(sessionId, callId);
2371
+ }
2372
+ if (!this.auths.resolve(sessionId, callId, decision, resolvedBy)) {
2373
+ yield this.removePendingAuth(sessionId, callId);
2374
+ throw new AuthInterruptedError(sessionId, callId);
2375
+ }
2376
+ return pending;
2377
+ });
2378
+ }
2379
+ removePendingAuth(sessionId, callId) {
2380
+ return __awaiter(this, void 0, void 0, function* () {
2381
+ yield this.updateRecord(sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== callId) })); });
2382
+ });
2383
+ }
2384
+ runWithMcpPark(sessionId, toolName, fn) {
2385
+ return __awaiter(this, void 0, void 0, function* () {
2386
+ const active = this.activeToolContexts.get(sessionId);
2387
+ if (active === undefined) {
2388
+ return fn();
2389
+ }
2390
+ return mcpAuthAls.run({
2391
+ sessionId,
2392
+ turnId: active.turnId,
2393
+ callId: newToolCallId(toolName),
2394
+ toolName,
2395
+ emit: active.emit,
2396
+ }, fn);
2397
+ });
2398
+ }
2023
2399
  // ==========================================================================
2024
2400
  // Direct tool calls (deterministic invocation, no model turn)
2025
2401
  // ==========================================================================
@@ -2034,8 +2410,10 @@ export class SessionEngine {
2034
2410
  * and materialized workspace) and the call is recorded on the session's
2035
2411
  * event stream as `actions.requested` / `action.result` under a
2036
2412
  * per-call `turnId` (the call id), so it shows up in trajectories.
2037
- * Rejected with {@link SessionBusyError} while a model turn is running.
2038
- * Without a session, the tool runs against an **ephemeral** scratch
2413
+ * While a model turn is running, a read-effect call runs alongside it
2414
+ * (the turn's workspace is read as-is) and a write-effect call is
2415
+ * rejected with {@link SessionBusyError}. Without a session, the tool
2416
+ * runs against an **ephemeral** scratch
2039
2417
  * workspace under `<stateRoot>/tool-calls/<callId>` (materialized with
2040
2418
  * seed files, removed after the call returns).
2041
2419
  *
@@ -2075,26 +2453,22 @@ export class SessionEngine {
2075
2453
  throw new InvalidContinuationTokenError(`expected "<channelId>:<key>" (as /v1/sessions lists it), got "${options.continuationToken}"`);
2076
2454
  }
2077
2455
  // A token that maps to a live (or restorable) session takes the
2078
- // session path: same owner check, same turn serialization, same
2079
- // event recording as an explicit sessionId. Skipping those just
2080
- // because the caller addressed the session by token would fail open.
2456
+ // session path: same owner check, same busy semantics, same event
2457
+ // recording as an explicit sessionId. Skipping those just because
2458
+ // the caller addressed the session by token would fail open.
2081
2459
  record = (_b = (yield this.sessions.findByContinuation(continuation.channelId, continuation.key))) !== null && _b !== void 0 ? _b : (yield this.restoreSessionByContinuation(continuation.channelId, continuation.key));
2082
2460
  if (record !== undefined && !samePrincipal(record.auth, auth)) {
2083
2461
  throw new SessionAuthError(record.sessionId);
2084
2462
  }
2085
2463
  }
2086
2464
  if (record !== undefined) {
2087
- // Direct calls respect the same serialization as follow-up sends: a
2088
- // running model turn owns the session workspace and event stream.
2089
- if (this.activeTurns.has(record.sessionId) ||
2090
- this.pendingTurns.has(record.sessionId)) {
2091
- throw new SessionBusyError(record.sessionId);
2092
- }
2093
2465
  return {
2094
2466
  record,
2095
2467
  session: this.sessionInfo(record),
2096
2468
  workspaceDir: this.harnessCwd(record),
2097
2469
  scratchDir: undefined,
2470
+ busy: this.activeTurns.has(record.sessionId) ||
2471
+ this.pendingTurns.has(record.sessionId),
2098
2472
  };
2099
2473
  }
2100
2474
  // No session behind the token (or no token at all): a synthetic
@@ -2114,6 +2488,7 @@ export class SessionEngine {
2114
2488
  },
2115
2489
  workspaceDir: scratchDir,
2116
2490
  scratchDir,
2491
+ busy: false,
2117
2492
  };
2118
2493
  });
2119
2494
  }
@@ -2146,8 +2521,32 @@ export class SessionEngine {
2146
2521
  };
2147
2522
  });
2148
2523
  }
2524
+ /**
2525
+ * One catalog tool by name. Authored tools are local — their schema does
2526
+ * not depend on session identity or connection listings — so a detail
2527
+ * fetch for `ls` must not wait on every advertised MCP server.
2528
+ */
2529
+ describeCatalogTool(toolName_1) {
2530
+ return __awaiter(this, arguments, void 0, function* (toolName, options = {}) {
2531
+ const authored = this.project.agent.tools.find((tool) => tool.name === toolName);
2532
+ if (authored !== undefined) {
2533
+ return { tool: authored };
2534
+ }
2535
+ const catalog = yield this.listCatalogTools(options);
2536
+ const tool = catalog.tools.find((entry) => entry.name === toolName);
2537
+ if (tool === undefined) {
2538
+ return {
2539
+ tool: undefined,
2540
+ availableTools: catalog.tools.map((entry) => entry.name),
2541
+ connectionErrors: catalog.connectionErrors,
2542
+ };
2543
+ }
2544
+ return { tool };
2545
+ });
2546
+ }
2149
2547
  callTool(toolName_1) {
2150
2548
  return __awaiter(this, arguments, void 0, function* (toolName, input = {}, options = {}) {
2549
+ var _a;
2151
2550
  const callId = newToolCallId(toolName);
2152
2551
  // Identity resolves before the tool lookup: advertised connection tools
2153
2552
  // only exist relative to a session (per-session auth), so finding them
@@ -2167,7 +2566,7 @@ export class SessionEngine {
2167
2566
  const advertised = yield resolveAdvertisedTools({
2168
2567
  agent: this.project.agent,
2169
2568
  session,
2170
- hostMcp: this.mcpHost,
2569
+ hostMcp: this.advertiseMcp(),
2171
2570
  });
2172
2571
  tool = advertised.find((t) => t.name === toolName);
2173
2572
  }
@@ -2184,6 +2583,17 @@ export class SessionEngine {
2184
2583
  if (!validation.ok) {
2185
2584
  throw new ToolInputError({ toolName, detail: validation.message });
2186
2585
  }
2586
+ // A running model turn owns the session workspace: a direct write would
2587
+ // race the harness's own edits, so it waits (409). A read runs alongside
2588
+ // the turn — the operator inspecting a live session is the point — and
2589
+ // its events stay separable by their per-call turnId. The effect can
2590
+ // depend on the input, hence deciding after validation; an MCP tool
2591
+ // nothing classifies is a write here, fail-closed.
2592
+ if (identity.busy &&
2593
+ record !== undefined &&
2594
+ directCallEffect(tool, validation.value) !== "read") {
2595
+ throw new SessionBusyError(record.sessionId);
2596
+ }
2187
2597
  let workspaceUnavailable = false;
2188
2598
  try {
2189
2599
  yield mkdir(workspaceDir, { recursive: true });
@@ -2203,28 +2613,50 @@ export class SessionEngine {
2203
2613
  // to write) authored tool may rely on ctx.workspaceDir being the
2204
2614
  // real session workspace, so silently relocating it is not safe —
2205
2615
  // fail typed instead.
2616
+ //
2617
+ // A shared `local.workspaceDir` (change-monitor's phantom /repo) is
2618
+ // that path by design. Relocating it to scratch would flag a false
2619
+ // session-state warning; keep the borrowed cwd and skip the flag.
2206
2620
  const relocatable = tool.advertised === true ||
2207
- resolveEffect(tool.definition.effect, validation.value) === "read";
2621
+ directCallEffect(tool, validation.value) === "read";
2208
2622
  if (!relocatable) {
2209
2623
  throw new WorkspaceUnavailableError(toolName, error);
2210
2624
  }
2211
- this.logger(`[session] tool ${toolName} workspace unavailable (direct ${callId}); using scratch: ${error instanceof Error ? error.message : String(error)}`);
2212
- scratchDir = join(this.stateRoot, "tool-calls", callId);
2213
- workspaceDir = scratchDir;
2214
- workspaceUnavailable = true;
2215
- yield mkdir(workspaceDir, { recursive: true });
2625
+ if (((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined) {
2626
+ this.logger(`[session] tool ${toolName} harness cwd unavailable (direct ${callId}); keeping borrowed workspace`);
2627
+ }
2628
+ else {
2629
+ this.logger(`[session] tool ${toolName} workspace unavailable (direct ${callId}); using scratch: ${error instanceof Error ? error.message : String(error)}`);
2630
+ scratchDir = join(this.stateRoot, "tool-calls", callId);
2631
+ workspaceDir = scratchDir;
2632
+ workspaceUnavailable = true;
2633
+ yield mkdir(workspaceDir, { recursive: true });
2634
+ }
2216
2635
  }
2217
2636
  // Match what a model turn would see: session workspaces get the same
2218
2637
  // scaffold as executeTurn (seeds only before the first turn); scratch
2219
- // workspaces are materialized fresh.
2220
- yield materializeWorkspace({
2221
- agent: this.project.agent,
2222
- workspaceDir: record === undefined || workspaceUnavailable
2223
- ? workspaceDir
2224
- : this.scaffoldDir(record),
2225
- seed: record === undefined || workspaceUnavailable || record.turnCount === 0,
2226
- stateRoot: this.stateRoot,
2227
- });
2638
+ // workspaces are materialized fresh. A read running alongside a turn
2639
+ // that has materialized its workspace leaves it alone — re-seeding
2640
+ // would restore files the agent deleted mid-turn. A session is busy
2641
+ // from admit, though, so a read that lands while the first turn is
2642
+ // still queued or scaffolding materializes the workspace itself, seeds
2643
+ // included (turnCount is already 1 for that pending first turn).
2644
+ const turnMaterialized = record !== undefined &&
2645
+ identity.busy &&
2646
+ this.materializedWorkspaces.has(record.sessionId);
2647
+ if (record === undefined || workspaceUnavailable || !turnMaterialized) {
2648
+ yield materializeWorkspace({
2649
+ agent: this.project.agent,
2650
+ workspaceDir: record === undefined || workspaceUnavailable
2651
+ ? workspaceDir
2652
+ : this.scaffoldDir(record),
2653
+ seed: record === undefined ||
2654
+ workspaceUnavailable ||
2655
+ record.turnCount === 0 ||
2656
+ (identity.busy && record.turnCount === 1),
2657
+ stateRoot: this.stateRoot,
2658
+ });
2659
+ }
2228
2660
  // A per-call turnId keeps these events visible in trajectories (which
2229
2661
  // drop turn-less action events) without attributing them to a model turn.
2230
2662
  if (record !== undefined) {
@@ -2267,7 +2699,7 @@ export class SessionEngine {
2267
2699
  isError = false;
2268
2700
  }
2269
2701
  else {
2270
- result = normalizeToolResult(yield execute(validation.value, {
2702
+ const run = () => execute(validation.value, {
2271
2703
  toolCallId: callId,
2272
2704
  session,
2273
2705
  workspaceDir,
@@ -2285,7 +2717,16 @@ export class SessionEngine {
2285
2717
  : session.auth })),
2286
2718
  getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
2287
2719
  emit,
2288
- }));
2720
+ });
2721
+ result = normalizeToolResult(record !== undefined && emit !== undefined
2722
+ ? yield mcpAuthAls.run({
2723
+ sessionId: record.sessionId,
2724
+ turnId: callId,
2725
+ callId,
2726
+ toolName,
2727
+ emit,
2728
+ }, run)
2729
+ : yield run());
2289
2730
  isError = isErrorEnvelope(result);
2290
2731
  }
2291
2732
  }
@@ -2513,6 +2954,29 @@ export class SessionEngine {
2513
2954
  // ==========================================================================
2514
2955
  // Session handles
2515
2956
  // ==========================================================================
2957
+ /**
2958
+ * Wait until everything this session has already emitted is durably
2959
+ * mirrored: its dispatch chain first (the chain's tail runs the turn-end
2960
+ * storage flush, so the record carries the boundary event's handler
2961
+ * mutations), then the storage delivery queue. Hosted v2 awaits this
2962
+ * before the execution reply — the pod can be parked the moment the
2963
+ * reply lands, and an unflushed session record would strand a
2964
+ * conversation whose blobs persisted but whose record (which selects the
2965
+ * conversation to hydrate) never reached the sink. No-op without a
2966
+ * storage mirror.
2967
+ */
2968
+ drainSessionDurability(sessionId) {
2969
+ return __awaiter(this, void 0, void 0, function* () {
2970
+ if (this.storage === undefined) {
2971
+ return;
2972
+ }
2973
+ const chain = this.dispatchChains.get(sessionId);
2974
+ if (chain !== undefined) {
2975
+ yield chain.catch(() => { });
2976
+ }
2977
+ yield this.storage.whenIdle();
2978
+ });
2979
+ }
2516
2980
  waitForCompletion(sessionId) {
2517
2981
  return __awaiter(this, void 0, void 0, function* () {
2518
2982
  var _a;
@@ -2629,6 +3093,64 @@ export class SessionEngine {
2629
3093
  };
2630
3094
  });
2631
3095
  }
3096
+ /**
3097
+ * The single newest session visible to `caller` (`GET /v1/sessions?latest=1`).
3098
+ *
3099
+ * Short-circuits the full listing: the local store already holds every
3100
+ * session this host created plus the startup bulk restore, so the common
3101
+ * case answers from memory without a sink round trip. Only when the local
3102
+ * store has nothing for the caller (fresh host, `restore: "off"`) does it
3103
+ * ask the sink — for a bounded newest-written window when the backend
3104
+ * can order by write time, else the full list. A session written by
3105
+ * another replica moments ago can lose to a local one; callers that need
3106
+ * the exact newest row should use the full listing.
3107
+ */
3108
+ latestSessionWithSource(caller, options) {
3109
+ return __awaiter(this, void 0, void 0, function* () {
3110
+ var _a;
3111
+ var _b;
3112
+ const includeAll = (_b = options === null || options === void 0 ? void 0 : options.includeAll) !== null && _b !== void 0 ? _b : false;
3113
+ const newestVisible = (records) => {
3114
+ let newest;
3115
+ for (const record of records) {
3116
+ if (!(includeAll || samePrincipal(record.auth, caller))) {
3117
+ continue;
3118
+ }
3119
+ if (newest === undefined ||
3120
+ record.updatedAt.localeCompare(newest.updatedAt) > 0) {
3121
+ newest = record;
3122
+ }
3123
+ }
3124
+ return newest;
3125
+ };
3126
+ const local = newestVisible(yield this.sessions.list());
3127
+ if (local !== undefined) {
3128
+ return {
3129
+ sessions: this.summarizeSessionRecords([local], caller, includeAll),
3130
+ source: "local",
3131
+ };
3132
+ }
3133
+ if (((_a = this.storage) === null || _a === void 0 ? void 0 : _a.isCursorHosted) === true) {
3134
+ try {
3135
+ const window = yield this.storage.listNewestSessionsFromSink(LATEST_SESSION_SINK_WINDOW);
3136
+ if (window !== undefined) {
3137
+ const stored = newestVisible(window);
3138
+ if (stored !== undefined) {
3139
+ return {
3140
+ sessions: this.summarizeSessionRecords([stored], caller, includeAll),
3141
+ source: "cursor-hosted",
3142
+ };
3143
+ }
3144
+ }
3145
+ }
3146
+ catch (error) {
3147
+ this.logger(`[agent-sdk] cursor-hosted newest-session lookup failed; falling back to the full list: ${describeError(error)}`);
3148
+ }
3149
+ }
3150
+ const listed = yield this.listSessionsWithSource(caller, options);
3151
+ return { sessions: listed.sessions.slice(0, 1), source: listed.source };
3152
+ });
3153
+ }
2632
3154
  /**
2633
3155
  * Durable sink rows plus local records (local wins on the same id when
2634
3156
  * newer — covers debounce windows that have not flushed yet). Newest
@@ -2675,6 +3197,7 @@ export class SessionEngine {
2675
3197
  : undefined,
2676
3198
  createdAt: record.createdAt,
2677
3199
  updatedAt: record.updatedAt,
3200
+ dryRun: record.dryRun === true ? true : undefined,
2678
3201
  continuationToken: record.httpContinuationKey !== undefined
2679
3202
  ? formatContinuationToken(HTTP_CHANNEL_ID, record.httpContinuationKey)
2680
3203
  : record.continuationKey === null
@@ -2947,7 +3470,7 @@ export class SessionEngine {
2947
3470
  this.coalesceDrains.has(sessionId));
2948
3471
  }
2949
3472
  sessionInfo(record) {
2950
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ id: record.sessionId, channelId: record.channelId, mode: record.mode, purpose: record.purpose === "eval" ? "eval" : "live" }, (record.dryRun === true ? { dryRun: true } : {})), { auth: record.auth, continuationKey: record.continuationKey }), (record.abs === undefined ? {} : { abs: record.abs })), (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined
3473
+ return Object.assign(Object.assign(Object.assign({ id: record.sessionId, channelId: record.channelId, mode: record.mode, purpose: record.purpose === "eval" ? "eval" : "live", dryRun: record.dryRun === true ? true : undefined, auth: record.auth, continuationKey: record.continuationKey }, (record.abs === undefined ? {} : { abs: record.abs })), (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined
2951
3474
  ? {}
2952
3475
  : { sdkAgentId: record.sdkAgentId }));
2953
3476
  }
@@ -3232,6 +3755,8 @@ export class SessionEngine {
3232
3755
  var _a, _b, _c, _d, _e;
3233
3756
  (_a = this.stopMemoryTelemetry) === null || _a === void 0 ? void 0 : _a.call(this);
3234
3757
  this.approvals.cancelAll("deny");
3758
+ this.auths.cancelAll("deny");
3759
+ yield this.oauthHub.close();
3235
3760
  yield Promise.allSettled([...this.activeTurns.values()]);
3236
3761
  yield Promise.allSettled([...this.backgroundWork]);
3237
3762
  yield Promise.allSettled([...this.dispatchChains.values()]);
@@ -3248,6 +3773,12 @@ export class SessionEngine {
3248
3773
  }
3249
3774
  /** Parallel event-stream loads during the startup bulk restore. */
3250
3775
  const RESTORE_FETCH_CONCURRENCY = 8;
3776
+ /**
3777
+ * Newest-written sink rows fetched for `GET /v1/sessions?latest=1` when the
3778
+ * local store has nothing for the caller. Wide enough that the caller's own
3779
+ * session is normally inside it even when other principals are busier.
3780
+ */
3781
+ const LATEST_SESSION_SINK_WINDOW = 50;
3251
3782
  /** NDJSON payload for a restored event stream; undefined when empty. */
3252
3783
  function mergePendingImages(current, incoming) {
3253
3784
  if (incoming === undefined) {