@cursor/july 0.1.107 → 0.1.108

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 (302) hide show
  1. package/README.md +10 -5
  2. package/dist/bin/agent-serve.js +30 -8
  3. package/dist/channels/github/github-channel.js +8 -5
  4. package/dist/channels/github/types.d.ts +5 -1
  5. package/dist/channels/github/types.d.ts.map +1 -1
  6. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  7. package/dist/channels/origin/origin-channel.js +27 -13
  8. package/dist/channels/origin/types.d.ts +5 -1
  9. package/dist/channels/origin/types.d.ts.map +1 -1
  10. package/dist/channels.d.ts +45 -0
  11. package/dist/channels.d.ts.map +1 -1
  12. package/dist/channels.js +106 -7
  13. package/dist/connections.d.ts +2 -1
  14. package/dist/connections.d.ts.map +1 -1
  15. package/dist/connections.js +2 -1
  16. package/dist/docs/404.html +2 -2
  17. package/dist/docs/ab.html +4 -4
  18. package/dist/docs/assets/{app.CtuckIiL.js → app.BASKM3M4.js} +1 -1
  19. package/dist/docs/assets/chunks/@localSearchIndexroot.BHZYsNVi.js +1 -0
  20. package/dist/docs/assets/chunks/{VPLocalSearchBox.Bkkhnu8K.js → VPLocalSearchBox.BOKxlYGP.js} +1 -1
  21. package/dist/docs/assets/chunks/{theme.DhpIe0Pa.js → theme.DbDZW-zb.js} +2 -2
  22. package/dist/docs/assets/{deployment.md.MFPKqsqI.js → deployment.md.JenwuCCB.js} +4 -4
  23. package/dist/docs/assets/{deployment.md.MFPKqsqI.lean.js → deployment.md.JenwuCCB.lean.js} +1 -1
  24. package/dist/docs/assets/{evals.md.CbMoebP1.js → evals.md.C1ekS3k2.js} +1 -1
  25. package/dist/docs/assets/guides_cloud-agents.md.BsloyHdY.js +9 -0
  26. package/dist/docs/assets/{guides_cloud-agents.md.DS8RIjwx.lean.js → guides_cloud-agents.md.BsloyHdY.lean.js} +1 -1
  27. package/dist/docs/assets/{guides_github.md.c0gdGJ-D.js → guides_github.md.BH33UEBJ.js} +1 -1
  28. package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.js → guides_grokbot-agents.md.DyV-WArv.js} +1 -1
  29. package/dist/docs/assets/guides_improve.md.BGVk32bK.js +14 -0
  30. package/dist/docs/assets/guides_improve.md.BGVk32bK.lean.js +1 -0
  31. package/dist/docs/assets/{guides_slack.md.9oHPye9o.js → guides_slack.md.D4RVMM4G.js} +3 -3
  32. package/dist/docs/assets/{guides_slack.md.9oHPye9o.lean.js → guides_slack.md.D4RVMM4G.lean.js} +1 -1
  33. package/dist/docs/assets/index.md.BjH1w2ZW.js +5 -0
  34. package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.js → reference_agent-config.md.CvoL6pof.js} +1 -1
  35. package/dist/docs/assets/{reference_cli.md.Dm67hd2D.js → reference_cli.md.Ca26u0Es.js} +6 -6
  36. package/dist/docs/assets/{reference_cli.md.Dm67hd2D.lean.js → reference_cli.md.Ca26u0Es.lean.js} +1 -1
  37. package/dist/docs/assets/{reference_connections.md.Di6jJAXF.js → reference_connections.md.BojkC6c5.js} +1 -1
  38. package/dist/docs/assets/{reference_extensions.md.CGmMLblt.js → reference_extensions.md.CPWt00ds.js} +1 -1
  39. package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.js → scaffolding-agents.md.Kctn3OVb.js} +1 -1
  40. package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.js → skills_create-agent.md.Q3h6Je-e.js} +1 -1
  41. package/dist/docs/assets/{skills_debug.md.CDbPhHfg.js → skills_debug.md.CVjCXMFF.js} +1 -1
  42. package/dist/docs/assets/{skills_debug.md.CDbPhHfg.lean.js → skills_debug.md.CVjCXMFF.lean.js} +1 -1
  43. package/dist/docs/assets/{skills_evals.md.723kpUmA.js → skills_evals.md.BhovOvrl.js} +1 -1
  44. package/dist/docs/assets/skills_framework-map.md.D-tFZhFS.js +1 -0
  45. package/dist/docs/assets/{skills_framework-map.md.haibFyoB.lean.js → skills_framework-map.md.D-tFZhFS.lean.js} +1 -1
  46. package/dist/docs/assets/{skills_github.md.D0JahM8c.js → skills_github.md.hgFX_oKY.js} +1 -1
  47. package/dist/docs/assets/troubleshooting.md.CYEAO9bM.js +1 -0
  48. package/dist/docs/building-with-agents.html +4 -4
  49. package/dist/docs/concepts.html +4 -4
  50. package/dist/docs/deployment.html +7 -7
  51. package/dist/docs/deployment.md +7 -2
  52. package/dist/docs/evals.html +6 -6
  53. package/dist/docs/evals.md +3 -2
  54. package/dist/docs/guides/agent-to-agent.html +4 -4
  55. package/dist/docs/guides/cloud-agents.html +6 -6
  56. package/dist/docs/guides/cloud-agents.md +4 -3
  57. package/dist/docs/guides/convert-automation.html +4 -4
  58. package/dist/docs/guides/github.html +5 -5
  59. package/dist/docs/guides/github.md +2 -1
  60. package/dist/docs/guides/grokbot-agents.html +6 -6
  61. package/dist/docs/guides/grokbot-agents.md +2 -2
  62. package/dist/docs/guides/human-in-the-loop.html +4 -4
  63. package/dist/docs/guides/improve.html +40 -0
  64. package/dist/docs/guides/improve.md +91 -0
  65. package/dist/docs/guides/mcp-oauth.html +4 -4
  66. package/dist/docs/guides/opentelemetry.html +5 -5
  67. package/dist/docs/guides/slack.html +6 -6
  68. package/dist/docs/guides/slack.md +2 -1
  69. package/dist/docs/guides/webhooks.html +4 -4
  70. package/dist/docs/hashmap.json +1 -1
  71. package/dist/docs/hillclimbing.html +4 -4
  72. package/dist/docs/index.html +6 -6
  73. package/dist/docs/index.md +4 -0
  74. package/dist/docs/llms-full.txt +158 -33
  75. package/dist/docs/llms.txt +1 -0
  76. package/dist/docs/quickstart.html +4 -4
  77. package/dist/docs/reference/agent-config.html +6 -6
  78. package/dist/docs/reference/agent-config.md +5 -2
  79. package/dist/docs/reference/artifacts.html +4 -4
  80. package/dist/docs/reference/channels.html +4 -4
  81. package/dist/docs/reference/cli.html +10 -10
  82. package/dist/docs/reference/cli.md +18 -11
  83. package/dist/docs/reference/connections.html +6 -6
  84. package/dist/docs/reference/connections.md +2 -1
  85. package/dist/docs/reference/extensions.html +6 -6
  86. package/dist/docs/reference/extensions.md +2 -0
  87. package/dist/docs/reference/hooks.html +4 -4
  88. package/dist/docs/reference/http-api.html +4 -4
  89. package/dist/docs/reference/instructions.html +4 -4
  90. package/dist/docs/reference/playground.html +4 -4
  91. package/dist/docs/reference/project-layout.html +4 -4
  92. package/dist/docs/reference/prompt.html +4 -4
  93. package/dist/docs/reference/result.html +4 -4
  94. package/dist/docs/reference/schedules.html +4 -4
  95. package/dist/docs/reference/sessions.html +4 -4
  96. package/dist/docs/reference/skills.html +4 -4
  97. package/dist/docs/reference/subagents.html +4 -4
  98. package/dist/docs/reference/tools.html +4 -4
  99. package/dist/docs/scaffolding-agents.html +5 -5
  100. package/dist/docs/scaffolding-agents.md +2 -1
  101. package/dist/docs/skills/ab.html +4 -4
  102. package/dist/docs/skills/create-agent.html +6 -6
  103. package/dist/docs/skills/create-agent.md +1 -1
  104. package/dist/docs/skills/debug.html +5 -5
  105. package/dist/docs/skills/debug.md +1 -1
  106. package/dist/docs/skills/evals.html +6 -6
  107. package/dist/docs/skills/evals.md +1 -1
  108. package/dist/docs/skills/framework-map.html +5 -5
  109. package/dist/docs/skills/framework-map.md +4 -2
  110. package/dist/docs/skills/github.html +6 -6
  111. package/dist/docs/skills/github.md +1 -1
  112. package/dist/docs/skills/hillclimb.html +4 -4
  113. package/dist/docs/skills/index.html +4 -4
  114. package/dist/docs/skills/mcp-auth.html +4 -4
  115. package/dist/docs/skills/otel.html +4 -4
  116. package/dist/docs/skills/setup-slack.html +4 -4
  117. package/dist/docs/storage.html +4 -4
  118. package/dist/docs/templates/agentic-owners.html +4 -4
  119. package/dist/docs/templates/agents-md.html +4 -4
  120. package/dist/docs/templates/code-wiki.html +4 -4
  121. package/dist/docs/templates/demo.html +4 -4
  122. package/dist/docs/templates/grokbot-agents.html +4 -4
  123. package/dist/docs/templates/pr-autofixer.html +4 -4
  124. package/dist/docs/templates/security-help.html +4 -4
  125. package/dist/docs/templates/security-reviewer.html +4 -4
  126. package/dist/docs/templates/triage.html +4 -4
  127. package/dist/docs/troubleshooting.html +5 -5
  128. package/dist/docs/troubleshooting.md +1 -1
  129. package/dist/extensions/improve/extension.d.ts +46 -0
  130. package/dist/extensions/improve/extension.d.ts.map +1 -0
  131. package/dist/extensions/improve/extension.js +41 -0
  132. package/dist/extensions/improve/skills/yourself.d.ts +4 -0
  133. package/dist/extensions/improve/skills/yourself.d.ts.map +1 -0
  134. package/dist/extensions/improve/skills/yourself.js +43 -0
  135. package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -1
  136. package/dist/internal/builtin-tools/artifacts.js +2 -3
  137. package/dist/internal/channel-route-dispatch.d.ts +13 -0
  138. package/dist/internal/channel-route-dispatch.d.ts.map +1 -0
  139. package/dist/internal/channel-route-dispatch.js +62 -0
  140. package/dist/internal/channel-state.d.ts +17 -0
  141. package/dist/internal/channel-state.d.ts.map +1 -0
  142. package/dist/internal/channel-state.js +78 -0
  143. package/dist/internal/cli-ax.d.ts +7 -0
  144. package/dist/internal/cli-ax.d.ts.map +1 -1
  145. package/dist/internal/cli-ax.js +99 -1
  146. package/dist/internal/cli-cursor.d.ts.map +1 -1
  147. package/dist/internal/cli-cursor.js +2 -0
  148. package/dist/internal/cli-deploy.d.ts.map +1 -1
  149. package/dist/internal/cli-deploy.js +38 -14
  150. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  151. package/dist/internal/cli-mcp-oauth.js +15 -0
  152. package/dist/internal/cli-mcp.d.ts.map +1 -1
  153. package/dist/internal/cli-mcp.js +12 -0
  154. package/dist/internal/cli-slack.d.ts.map +1 -1
  155. package/dist/internal/cli-slack.js +8 -2
  156. package/dist/internal/cursor/credentials.d.ts +8 -2
  157. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  158. package/dist/internal/cursor/credentials.js +27 -5
  159. package/dist/internal/deploy-client.d.ts +27 -0
  160. package/dist/internal/deploy-client.d.ts.map +1 -1
  161. package/dist/internal/deploy-client.js +32 -0
  162. package/dist/internal/deploy-manifest.d.ts +23 -0
  163. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  164. package/dist/internal/deploy-manifest.js +84 -1
  165. package/dist/internal/describe-error.d.ts +1 -0
  166. package/dist/internal/describe-error.d.ts.map +1 -1
  167. package/dist/internal/describe-error.js +25 -0
  168. package/dist/internal/guest-network.d.ts +4 -10
  169. package/dist/internal/guest-network.d.ts.map +1 -1
  170. package/dist/internal/guest-network.js +42 -26
  171. package/dist/internal/hosted-admission-adapter.d.ts +2 -0
  172. package/dist/internal/hosted-admission-adapter.d.ts.map +1 -1
  173. package/dist/internal/hosted-catch-protocol.d.ts +51 -0
  174. package/dist/internal/hosted-catch-protocol.d.ts.map +1 -0
  175. package/dist/internal/hosted-catch-protocol.js +103 -0
  176. package/dist/internal/hosted-catch.d.ts +40 -0
  177. package/dist/internal/hosted-catch.d.ts.map +1 -0
  178. package/dist/internal/hosted-catch.js +149 -0
  179. package/dist/internal/hosted-delivery-protocol.d.ts +41 -0
  180. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  181. package/dist/internal/hosted-delivery-protocol.js +249 -0
  182. package/dist/internal/hosted-delivery.d.ts +10 -1
  183. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  184. package/dist/internal/hosted-delivery.js +100 -22
  185. package/dist/internal/hosted-execution-diag.d.ts +4 -1
  186. package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
  187. package/dist/internal/hosted-execution-diag.js +13 -4
  188. package/dist/internal/hosted-execution-flush.d.ts +3 -0
  189. package/dist/internal/hosted-execution-flush.d.ts.map +1 -1
  190. package/dist/internal/hosted-execution-flush.js +2 -4
  191. package/dist/internal/http-control-plane-session.d.ts +30 -0
  192. package/dist/internal/http-control-plane-session.d.ts.map +1 -0
  193. package/dist/internal/http-control-plane-session.js +83 -0
  194. package/dist/internal/legacy-command-guard.d.ts +22 -0
  195. package/dist/internal/legacy-command-guard.d.ts.map +1 -0
  196. package/dist/internal/legacy-command-guard.js +51 -0
  197. package/dist/internal/platform-timers.d.ts +7 -0
  198. package/dist/internal/platform-timers.d.ts.map +1 -1
  199. package/dist/internal/platform-timers.js +139 -0
  200. package/dist/internal/reminder-control-plane-protocol.d.ts +2 -0
  201. package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -1
  202. package/dist/internal/reminder-control-plane-protocol.js +9 -2
  203. package/dist/internal/reminder-runner.d.ts +8 -0
  204. package/dist/internal/reminder-runner.d.ts.map +1 -1
  205. package/dist/internal/reminder-runner.js +36 -9
  206. package/dist/internal/resolve-prod-target.d.ts +2 -0
  207. package/dist/internal/resolve-prod-target.d.ts.map +1 -1
  208. package/dist/internal/resolve-prod-target.js +13 -0
  209. package/dist/internal/sdk-runner.d.ts.map +1 -1
  210. package/dist/internal/sdk-runner.js +4 -3
  211. package/dist/internal/server.d.ts.map +1 -1
  212. package/dist/internal/server.js +57 -56
  213. package/dist/internal/session-engine.d.ts +2 -6
  214. package/dist/internal/session-engine.d.ts.map +1 -1
  215. package/dist/internal/session-engine.js +95 -67
  216. package/dist/playground/assets/{index-Bq2HpEQB.js → index-BMDqAeXu.js} +28 -28
  217. package/dist/playground/assets/index-LUgJoWdL.css +1 -0
  218. package/dist/playground/index.html +2 -2
  219. package/dist/storage-backends/cursor-hosted.d.ts +6 -0
  220. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  221. package/dist/storage-backends/cursor-hosted.js +29 -1
  222. package/dist/types.d.ts +33 -5
  223. package/dist/types.d.ts.map +1 -1
  224. package/docs/README.md +4 -0
  225. package/docs/deployment.md +7 -2
  226. package/docs/evals.md +3 -2
  227. package/docs/guides/cloud-agents.md +4 -3
  228. package/docs/guides/github.md +2 -1
  229. package/docs/guides/grokbot-agents.md +2 -2
  230. package/docs/guides/improve.md +96 -0
  231. package/docs/guides/slack.md +2 -1
  232. package/docs/reference/agent-config.md +5 -2
  233. package/docs/reference/cli.md +18 -11
  234. package/docs/reference/connections.md +2 -1
  235. package/docs/reference/extensions.md +2 -0
  236. package/docs/scaffolding-agents.md +2 -1
  237. package/docs/troubleshooting.md +1 -1
  238. package/package.json +8 -1
  239. package/skills/create-agent/SKILL.md +1 -1
  240. package/skills/debug/SKILL.md +1 -1
  241. package/skills/evals/SKILL.md +1 -1
  242. package/skills/framework-map/SKILL.md +4 -2
  243. package/skills/github/SKILL.md +1 -1
  244. package/src/bin/agent-serve.ts +27 -2
  245. package/src/channels/github/github-channel.ts +8 -8
  246. package/src/channels/github/types.ts +5 -0
  247. package/src/channels/origin/origin-channel.ts +29 -13
  248. package/src/channels/origin/types.ts +5 -0
  249. package/src/channels.ts +157 -10
  250. package/src/connections.ts +2 -1
  251. package/src/extensions/improve/extension.ts +70 -0
  252. package/src/extensions/improve/skills/yourself.ts +50 -0
  253. package/src/internal/builtin-tools/artifacts.ts +2 -3
  254. package/src/internal/channel-route-dispatch.ts +66 -0
  255. package/src/internal/channel-state.ts +96 -0
  256. package/src/internal/cli-ax.ts +110 -1
  257. package/src/internal/cli-cursor.ts +4 -1
  258. package/src/internal/cli-deploy.ts +50 -10
  259. package/src/internal/cli-mcp-oauth.ts +18 -0
  260. package/src/internal/cli-mcp.ts +11 -0
  261. package/src/internal/cli-slack.ts +15 -2
  262. package/src/internal/cursor/credentials.ts +35 -7
  263. package/src/internal/deploy-client.ts +54 -0
  264. package/src/internal/deploy-manifest.ts +115 -1
  265. package/src/internal/describe-error.ts +28 -0
  266. package/src/internal/guest-network.ts +43 -29
  267. package/src/internal/hosted-admission-adapter.ts +2 -0
  268. package/src/internal/hosted-catch-protocol.ts +130 -0
  269. package/src/internal/hosted-catch.ts +192 -0
  270. package/src/internal/hosted-delivery-protocol.ts +387 -0
  271. package/src/internal/hosted-delivery.ts +155 -22
  272. package/src/internal/hosted-execution-diag.ts +21 -3
  273. package/src/internal/hosted-execution-flush.ts +6 -3
  274. package/src/internal/http-control-plane-session.ts +104 -0
  275. package/src/internal/legacy-command-guard.ts +59 -0
  276. package/src/internal/platform-timers.ts +191 -0
  277. package/src/internal/reminder-control-plane-protocol.ts +15 -2
  278. package/src/internal/reminder-runner.ts +60 -9
  279. package/src/internal/resolve-prod-target.ts +15 -0
  280. package/src/internal/sdk-runner.ts +2 -1
  281. package/src/internal/server.ts +89 -79
  282. package/src/internal/session-engine.ts +141 -83
  283. package/src/storage-backends/cursor-hosted.ts +40 -1
  284. package/src/types.ts +39 -6
  285. package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +0 -1
  286. package/dist/docs/assets/guides_cloud-agents.md.DS8RIjwx.js +0 -9
  287. package/dist/docs/assets/index.md.BW_6tOgR.js +0 -5
  288. package/dist/docs/assets/skills_framework-map.md.haibFyoB.js +0 -1
  289. package/dist/docs/assets/troubleshooting.md.Cus_YZga.js +0 -1
  290. package/dist/playground/assets/index-CZKKNlmb.css +0 -1
  291. /package/dist/docs/assets/{evals.md.CbMoebP1.lean.js → evals.md.C1ekS3k2.lean.js} +0 -0
  292. /package/dist/docs/assets/{guides_github.md.c0gdGJ-D.lean.js → guides_github.md.BH33UEBJ.lean.js} +0 -0
  293. /package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.lean.js → guides_grokbot-agents.md.DyV-WArv.lean.js} +0 -0
  294. /package/dist/docs/assets/{index.md.BW_6tOgR.lean.js → index.md.BjH1w2ZW.lean.js} +0 -0
  295. /package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.lean.js → reference_agent-config.md.CvoL6pof.lean.js} +0 -0
  296. /package/dist/docs/assets/{reference_connections.md.Di6jJAXF.lean.js → reference_connections.md.BojkC6c5.lean.js} +0 -0
  297. /package/dist/docs/assets/{reference_extensions.md.CGmMLblt.lean.js → reference_extensions.md.CPWt00ds.lean.js} +0 -0
  298. /package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.lean.js → scaffolding-agents.md.Kctn3OVb.lean.js} +0 -0
  299. /package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.lean.js → skills_create-agent.md.Q3h6Je-e.lean.js} +0 -0
  300. /package/dist/docs/assets/{skills_evals.md.723kpUmA.lean.js → skills_evals.md.BhovOvrl.lean.js} +0 -0
  301. /package/dist/docs/assets/{skills_github.md.D0JahM8c.lean.js → skills_github.md.hgFX_oKY.lean.js} +0 -0
  302. /package/dist/docs/assets/{troubleshooting.md.Cus_YZga.lean.js → troubleshooting.md.CYEAO9bM.lean.js} +0 -0
@@ -12,11 +12,9 @@
12
12
  * so leftover races still return 503 `guest_network_unavailable` instead of
13
13
  * a redacted 500.
14
14
  *
15
- * Quiet guest Connect (`TypeError: fetch failed` with no errno, Connect
16
- * `[unknown]`/`[canceled]`/`[unavailable]`, headers/body timeout) is a
17
- * stall, not a dead NIC. Those become `guest_connect_stalled` so TAS
18
- * retries the same pod — including when `waitForCompletion` resolves
19
- * with that message instead of throwing.
15
+ * Quiet guest Connect failures with a typed Connect/undici code are stalls,
16
+ * not dead NICs. Those become `guest_connect_stalled` so TAS retries the same
17
+ * pod. Untyped errors stay unknown rather than being classified from prose.
20
18
  */
21
19
 
22
20
  import { HOSTED_GUEST_RETRY_TRANSIENT } from "./hosted-delivery-protocol.js";
@@ -71,11 +69,16 @@ export class GuestNetworkUnavailableError extends Error {
71
69
  }
72
70
  }
73
71
 
72
+ class GuestNetworkProbeFailedError extends Error {
73
+ constructor(cause: unknown) {
74
+ super("Guest network probe failed", { cause });
75
+ this.name = "GuestNetworkProbeFailedError";
76
+ }
77
+ }
78
+
74
79
  /**
75
80
  * True when the error (or anything on its `cause` chain, or inside an
76
- * `AggregateError`) is a network-level fetch failure. Connect-web wraps
77
- * undici's `TypeError: fetch failed` as `ConnectError` and keeps the
78
- * original on `cause`.
81
+ * `AggregateError`) carries a known network-level code.
79
82
  */
80
83
  export function isTransientGuestNetworkError(error: unknown): boolean {
81
84
  return isTransientAtDepth(error, 0);
@@ -91,9 +94,8 @@ const QUIET_CONNECT_ERROR_CODES: ReadonlySet<string> = new Set([
91
94
  "UND_ERR_BODY_TIMEOUT",
92
95
  "UND_ERR_HEADERS_TIMEOUT",
93
96
  ]);
94
-
95
- const QUIET_CONNECT_MESSAGE =
96
- /^(?:\[(?:unknown|canceled|unavailable)\])|connection stalled|this operation was aborted/i;
97
+ // gRPC Canceled, Unknown, and Unavailable.
98
+ const QUIET_CONNECT_CODES: ReadonlySet<number> = new Set([1, 2, 14]);
97
99
 
98
100
  export function hasGuestNetworkErrno(error: unknown): boolean {
99
101
  return hasCodeAtDepth(error, 0, TRANSIENT_NETWORK_ERROR_CODES);
@@ -115,16 +117,16 @@ export function isQuietGuestConnectStall(error: unknown): boolean {
115
117
  return isQuietSignatureAtDepth(error, 0);
116
118
  }
117
119
 
118
- /** Message-only form for `waitForCompletion` outcomes that do not throw. */
119
- export function isQuietGuestConnectStallMessage(message: string): boolean {
120
- return QUIET_CONNECT_MESSAGE.test(message);
121
- }
122
-
123
120
  function errorCode(error: Error): string | undefined {
124
121
  const code = (error as NodeJS.ErrnoException).code;
125
122
  return typeof code === "string" && code.length > 0 ? code : undefined;
126
123
  }
127
124
 
125
+ function numericErrorCode(error: Error): number | undefined {
126
+ const code = (error as { code?: unknown }).code;
127
+ return typeof code === "number" ? code : undefined;
128
+ }
129
+
128
130
  function hasCodeAtDepth(
129
131
  error: unknown,
130
132
  depth: number,
@@ -150,14 +152,15 @@ function isQuietSignatureAtDepth(error: unknown, depth: number): boolean {
150
152
  if (depth >= MAX_CAUSE_DEPTH || !(error instanceof Error)) {
151
153
  return false;
152
154
  }
153
- if (error instanceof TypeError && error.message === "fetch failed") {
155
+ if (error.name === "AbortError") {
154
156
  return true;
155
157
  }
156
- const code = errorCode(error);
157
- if (code !== undefined && QUIET_CONNECT_ERROR_CODES.has(code)) {
158
+ const numericCode = numericErrorCode(error);
159
+ if (numericCode !== undefined && QUIET_CONNECT_CODES.has(numericCode)) {
158
160
  return true;
159
161
  }
160
- if (QUIET_CONNECT_MESSAGE.test(error.message)) {
162
+ const code = errorCode(error);
163
+ if (code !== undefined && QUIET_CONNECT_ERROR_CODES.has(code)) {
161
164
  return true;
162
165
  }
163
166
  if (
@@ -173,10 +176,17 @@ function isTransientAtDepth(error: unknown, depth: number): boolean {
173
176
  if (depth >= MAX_CAUSE_DEPTH || !(error instanceof Error)) {
174
177
  return false;
175
178
  }
176
- if (error instanceof TypeError && error.message === "fetch failed") {
179
+ if (error instanceof GuestNetworkUnavailableError) {
177
180
  return true;
178
181
  }
179
- if (error instanceof GuestNetworkUnavailableError) {
182
+ if (error instanceof GuestNetworkProbeFailedError) {
183
+ return true;
184
+ }
185
+ if (error.name === "AbortError") {
186
+ return true;
187
+ }
188
+ const numericCode = numericErrorCode(error);
189
+ if (numericCode !== undefined && QUIET_CONNECT_CODES.has(numericCode)) {
180
190
  return true;
181
191
  }
182
192
  const code = errorCode(error);
@@ -249,13 +259,17 @@ async function defaultProbe(
249
259
  probeUrl: string,
250
260
  probeTimeoutMs: number
251
261
  ): Promise<void> {
252
- // Any HTTP response means egress and DNS work. 401/404 are ready.
253
- const response = await fetch(probeUrl, {
254
- method: "GET",
255
- redirect: "manual",
256
- signal: AbortSignal.timeout(probeTimeoutMs),
257
- });
258
- void response.body?.cancel();
262
+ try {
263
+ // Any HTTP response means egress and DNS work. 401/404 are ready.
264
+ const response = await fetch(probeUrl, {
265
+ method: "GET",
266
+ redirect: "manual",
267
+ signal: AbortSignal.timeout(probeTimeoutMs),
268
+ });
269
+ void response.body?.cancel();
270
+ } catch (error) {
271
+ throw new GuestNetworkProbeFailedError(error);
272
+ }
259
273
  }
260
274
 
261
275
  export type GuestNetworkWaitOutcome = "ready" | "skipped" | "unavailable";
@@ -19,6 +19,8 @@ export type HostedAdmissionAdapter = {
19
19
  */
20
20
  prepareArgs?(args: ChannelHandlerArgs): ChannelHandlerArgs;
21
21
  dispatch(input: {
22
+ /** Mounted channel id the delivery addressed (always set by hosted delivery). */
23
+ channelId?: string;
22
24
  eventType: string;
23
25
  payload: unknown;
24
26
  deliveryId: string;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Catch ingress wire contract, July copy. The control plane keeps a
3
+ * byte-identical copy of these constants in
4
+ * `backend/packages/server-shared/src/agentServe/agentSdkCatch.ts`; a
5
+ * backend drift test compares the two as text. Neither package imports the
6
+ * other.
7
+ *
8
+ * A caught request is one hosted admission whose `eventType` is
9
+ * {@link AGENT_SDK_CATCH_EVENT_TYPE} and whose `payload` is a
10
+ * {@link CatchRequestEnvelope}: the HTTP request the control plane
11
+ * accepted on a manifest-declared route after verifying the deployment's
12
+ * secret. July replays it into the authored route handler; the control
13
+ * plane never runs channel code.
14
+ */
15
+
16
+ export const AGENT_SDK_CATCH_ENVELOPE_VERSION = 1;
17
+ export const AGENT_SDK_CATCH_EVENT_TYPE = "http.request";
18
+ export const AGENT_SDK_CUSTOM_CHANNEL_KIND = "custom";
19
+ export const AGENT_SDK_SESSION_EVENT_TYPE = "agentsdk.session";
20
+ export const AGENT_SDK_HTTP_CHANNEL_ID = "http";
21
+ export const AGENT_SDK_HTTP_CHANNEL_KIND = "http";
22
+ export const AGENT_SDK_SESSION_RESUME_KEY_PREFIX = "cont:session:";
23
+ export const AGENT_SDK_CATCH_MAX_BODY_BYTES: number = 256 * 1024;
24
+
25
+ export const AGENT_SDK_CATCH_ROUTE_METHODS = [
26
+ "GET",
27
+ "POST",
28
+ "PUT",
29
+ "PATCH",
30
+ "DELETE",
31
+ ] as const;
32
+ export type CatchRouteMethod = (typeof AGENT_SDK_CATCH_ROUTE_METHODS)[number];
33
+
34
+ export type CatchRequestEnvelope = {
35
+ v: 1;
36
+ method: CatchRouteMethod;
37
+ /** Channel-relative path, e.g. `/webhook`. */
38
+ path: string;
39
+ /** Raw query string without the leading `?`; empty when absent. */
40
+ query: string;
41
+ /** Lowercased header names. */
42
+ headers: Record<string, string>;
43
+ body: { encoding: "utf8" | "base64"; data: string } | null;
44
+ receivedAt: string;
45
+ };
46
+
47
+ /**
48
+ * Mailbox key the control plane stamps on an explicit `ses_…` resume. The
49
+ * suffix is the CP session id the hosted execution also names, so the
50
+ * engine can recognise the key as "this existing session" rather than a
51
+ * new continuation identity.
52
+ */
53
+ export function hostedSessionResumeKey(sessionPublicId: string): string {
54
+ return `${AGENT_SDK_SESSION_RESUME_KEY_PREFIX}${sessionPublicId}`;
55
+ }
56
+
57
+ const ENVELOPE_KEYS = new Set([
58
+ "v",
59
+ "method",
60
+ "path",
61
+ "query",
62
+ "headers",
63
+ "body",
64
+ "receivedAt",
65
+ ]);
66
+ const METHODS = new Set<string>(AGENT_SDK_CATCH_ROUTE_METHODS);
67
+ const MAX_PATH_CHARS = 1024;
68
+ const MAX_QUERY_CHARS = 4096;
69
+ const MAX_HEADER_COUNT = 64;
70
+ const MAX_HEADER_BYTES = 16 * 1024;
71
+
72
+ function isRecord(value: unknown): value is Record<string, unknown> {
73
+ return typeof value === "object" && value !== null && !Array.isArray(value);
74
+ }
75
+
76
+ /** Strict parse of a claim-checked catch envelope; anything else is undefined. */
77
+ export function parseCatchRequestEnvelope(
78
+ value: unknown
79
+ ): CatchRequestEnvelope | undefined {
80
+ if (!isRecord(value)) {
81
+ return undefined;
82
+ }
83
+ if (
84
+ Object.keys(value).some((key) => !ENVELOPE_KEYS.has(key)) ||
85
+ value.v !== AGENT_SDK_CATCH_ENVELOPE_VERSION ||
86
+ typeof value.method !== "string" ||
87
+ !METHODS.has(value.method) ||
88
+ typeof value.path !== "string" ||
89
+ !value.path.startsWith("/") ||
90
+ value.path.length > MAX_PATH_CHARS ||
91
+ typeof value.query !== "string" ||
92
+ value.query.length > MAX_QUERY_CHARS ||
93
+ typeof value.receivedAt !== "string" ||
94
+ !isRecord(value.headers)
95
+ ) {
96
+ return undefined;
97
+ }
98
+ const entries = Object.entries(value.headers);
99
+ if (entries.length > MAX_HEADER_COUNT) {
100
+ return undefined;
101
+ }
102
+ let headerBytes = 0;
103
+ for (const [name, headerValue] of entries) {
104
+ if (typeof headerValue !== "string" || name !== name.toLowerCase()) {
105
+ return undefined;
106
+ }
107
+ headerBytes += name.length + headerValue.length;
108
+ }
109
+ if (headerBytes > MAX_HEADER_BYTES) {
110
+ return undefined;
111
+ }
112
+ if (value.body !== null) {
113
+ if (
114
+ !isRecord(value.body) ||
115
+ (value.body.encoding !== "utf8" && value.body.encoding !== "base64") ||
116
+ typeof value.body.data !== "string"
117
+ ) {
118
+ return undefined;
119
+ }
120
+ }
121
+ return value as CatchRequestEnvelope;
122
+ }
123
+
124
+ /** Raw request bytes from an envelope body; empty for a null body. */
125
+ export function decodeCatchBody(body: CatchRequestEnvelope["body"]): Buffer {
126
+ if (body === null) {
127
+ return Buffer.alloc(0);
128
+ }
129
+ return Buffer.from(body.data, body.encoding);
130
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Hosted admission for caught HTTP requests.
3
+ *
4
+ * The control plane accepted a request on one of this channel's declared
5
+ * routes, verified the deployment's secret, and claim-checked the request
6
+ * as a {@link CatchRequestEnvelope}. Admission replays that request into
7
+ * the authored route handler exactly as the local router would have
8
+ * (same route table, same Zod schema gate), with the control plane as the
9
+ * authenticated principal. A captured `send()` becomes the pending send;
10
+ * a handler that answers without sending is a `skip`.
11
+ */
12
+
13
+ import type {
14
+ AuthContext,
15
+ ChannelDefinition,
16
+ ChannelHandlerArgs,
17
+ ChannelRoute,
18
+ } from "../types.js";
19
+ import {
20
+ acknowledgeTurn,
21
+ dispatchOutcome,
22
+ skipped,
23
+ tagOutcome,
24
+ } from "./channel-dispatch.js";
25
+ import { invokeChannelRoute } from "./channel-route-dispatch.js";
26
+ import { DeferredChannelSessionError } from "./deferred-channel-session.js";
27
+ import { attachHostedAdmissionAdapter } from "./hosted-admission-adapter.js";
28
+ import {
29
+ AGENT_SDK_CATCH_EVENT_TYPE,
30
+ AGENT_SDK_CUSTOM_CHANNEL_KIND,
31
+ type CatchRequestEnvelope,
32
+ decodeCatchBody,
33
+ parseCatchRequestEnvelope,
34
+ } from "./hosted-catch-protocol.js";
35
+ import { errorResponse } from "./http-channel.js";
36
+ import { Router } from "./router.js";
37
+
38
+ /** Header a replayed request carries so handlers can dedupe on delivery. */
39
+ export const CATCH_DELIVERY_ID_HEADER = "x-agent-sdk-delivery-id";
40
+ /** Placeholder origin for replayed requests; handlers see a well-formed URL. */
41
+ const CATCH_REQUEST_ORIGIN = "http://agent-sdk.hosted";
42
+
43
+ /**
44
+ * Principal for a replayed request. The deployment secret was checked by
45
+ * the control plane before compute existed; the channel's own auth chain
46
+ * does not run again in the VM.
47
+ */
48
+ export const CATCH_AUTH_CONTEXT: AuthContext = {
49
+ authenticator: "control-plane-catch",
50
+ principalId: "agent-sdk:catch",
51
+ principalType: "runtime",
52
+ };
53
+
54
+ const BODYLESS_METHODS = new Set(["GET", "HEAD"]);
55
+
56
+ export function catchRequestFromEnvelope(input: {
57
+ channelId: string;
58
+ deliveryId: string;
59
+ envelope: CatchRequestEnvelope;
60
+ }): Request {
61
+ const { envelope } = input;
62
+ const url = `${CATCH_REQUEST_ORIGIN}/v1/channels/${input.channelId}${envelope.path}${
63
+ envelope.query === "" ? "" : `?${envelope.query}`
64
+ }`;
65
+ const headers = new Headers(envelope.headers);
66
+ headers.set(CATCH_DELIVERY_ID_HEADER, input.deliveryId);
67
+ const body = decodeCatchBody(envelope.body);
68
+ return new Request(url, {
69
+ method: envelope.method,
70
+ headers,
71
+ body:
72
+ body.length === 0 || BODYLESS_METHODS.has(envelope.method)
73
+ ? undefined
74
+ : new Uint8Array(body),
75
+ });
76
+ }
77
+
78
+ export async function replayCatchRequest(input: {
79
+ channelId: string;
80
+ deliveryId: string;
81
+ routes: readonly ChannelRoute[];
82
+ payload: unknown;
83
+ args: ChannelHandlerArgs;
84
+ }): Promise<Response> {
85
+ const envelope = parseCatchRequestEnvelope(input.payload);
86
+ if (envelope === undefined) {
87
+ return errorResponse(
88
+ 400,
89
+ "invalid_catch_envelope",
90
+ "Expected a supported Agent SDK catch request envelope."
91
+ );
92
+ }
93
+ const router = new Router();
94
+ for (const route of input.routes) {
95
+ router.add({
96
+ method: route.method,
97
+ pattern: route.path,
98
+ auth: "none",
99
+ handler: () => errorResponse(500, "unreachable", "unreachable"),
100
+ });
101
+ }
102
+ const matched = router.match(envelope.method, envelope.path);
103
+ const route = input.routes.find(
104
+ (candidate) =>
105
+ candidate.method === matched?.route.method &&
106
+ candidate.path === matched.route.pattern
107
+ );
108
+ if (matched === undefined || route === undefined) {
109
+ // The control plane routes from the same pinned manifest this VM was
110
+ // frozen from; a miss here is drift, not a caller error.
111
+ return errorResponse(
112
+ 404,
113
+ "route_not_declared",
114
+ `No ${envelope.method} ${envelope.path} route is mounted on channel "${input.channelId}".`
115
+ );
116
+ }
117
+ const request = catchRequestFromEnvelope({
118
+ channelId: input.channelId,
119
+ deliveryId: input.deliveryId,
120
+ envelope,
121
+ });
122
+ let response: Response;
123
+ try {
124
+ response = await invokeChannelRoute({
125
+ route,
126
+ request,
127
+ handlerArgs: {
128
+ ...input.args,
129
+ params: matched.params,
130
+ auth: CATCH_AUTH_CONTEXT,
131
+ },
132
+ });
133
+ } catch (error) {
134
+ // `return Response.json({ sessionId: session.id })` after `send()` is
135
+ // the ordinary local webhook shape. Hosted, `send()` is captured and the
136
+ // handle is deferred, so reading `.id` throws — but nobody reads this
137
+ // handler's HTTP reply hosted; the captured send is the outcome.
138
+ if (error instanceof DeferredChannelSessionError) {
139
+ return acknowledgeTurn();
140
+ }
141
+ throw error;
142
+ }
143
+ // A 2xx the handler did not tag and in which nothing was captured means
144
+ // it handled the request itself (acknowledged, ignored, replied). There is
145
+ // nothing to execute: report skip instead of failing the delivery.
146
+ if (response.ok && dispatchOutcome(response) === undefined) {
147
+ return tagOutcome(response, "skip");
148
+ }
149
+ // A 4xx is the handler rejecting the sender (bad signature on a
150
+ // handler-verified `publicEndpoint()` route, schema mismatch, unknown
151
+ // event). The sender already has its 202; retrying the delivery cannot
152
+ // change the verdict, so report a cheap skip that lets the control plane
153
+ // discard the compute instead of failing and retrying the activity.
154
+ if (response.status >= 400 && response.status < 500) {
155
+ // Logged so a probed public route or a misconfigured provider secret is
156
+ // distinguishable from a benign "nothing to do" skip in the guest logs;
157
+ // the wire outcome to the control plane is still `skip`.
158
+ console.info(
159
+ `[agent-sdk] catch replay rejected by handler deliveryId=${input.deliveryId} channelId=${input.channelId} route=${envelope.method} ${route.path} status=${String(response.status)}`
160
+ );
161
+ return skipped(`handler rejected the request (${response.status})`, {
162
+ status: response.status,
163
+ });
164
+ }
165
+ return response;
166
+ }
167
+
168
+ /**
169
+ * Attach catch admission to an authored channel. Provider packs attach
170
+ * their own adapter afterwards and replace this one; the control plane
171
+ * never sends `http.request` to a pack kind in any case.
172
+ */
173
+ export function attachCatchAdmission(
174
+ definition: ChannelDefinition<unknown>,
175
+ routes: readonly ChannelRoute[]
176
+ ): void {
177
+ attachHostedAdmissionAdapter(definition, {
178
+ kind: definition.meta?.kind ?? AGENT_SDK_CUSTOM_CHANNEL_KIND,
179
+ dispatch: ({ channelId, eventType, payload, deliveryId, args }) => {
180
+ if (eventType !== AGENT_SDK_CATCH_EVENT_TYPE) {
181
+ return Promise.resolve(skipped("not a caught http request"));
182
+ }
183
+ return replayCatchRequest({
184
+ channelId: channelId ?? "channel",
185
+ deliveryId,
186
+ routes,
187
+ payload,
188
+ args,
189
+ });
190
+ },
191
+ });
192
+ }