@cursor/july 0.1.96 → 0.1.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +3 -2
  3. package/dist/bin/agent-serve.js +13 -3
  4. package/dist/channels/github/github-channel.d.ts.map +1 -1
  5. package/dist/channels/github/github-channel.js +16 -0
  6. package/dist/channels/origin/api.d.ts +15 -4
  7. package/dist/channels/origin/api.d.ts.map +1 -1
  8. package/dist/channels/origin/api.js +19 -3
  9. package/dist/channels/origin/binding.d.ts +11 -0
  10. package/dist/channels/origin/binding.d.ts.map +1 -1
  11. package/dist/channels/origin/binding.js +41 -25
  12. package/dist/channels/origin/index.d.ts +1 -0
  13. package/dist/channels/origin/index.d.ts.map +1 -1
  14. package/dist/channels/origin/index.js +1 -0
  15. package/dist/channels/origin/origin-channel.d.ts +2 -0
  16. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  17. package/dist/channels/origin/origin-channel.js +40 -4
  18. package/dist/channels/origin/reactions.d.ts +56 -0
  19. package/dist/channels/origin/reactions.d.ts.map +1 -0
  20. package/dist/channels/origin/reactions.js +67 -0
  21. package/dist/channels/origin/types.d.ts +11 -0
  22. package/dist/channels/origin/types.d.ts.map +1 -1
  23. package/dist/channels/slack/approvals.d.ts +16 -0
  24. package/dist/channels/slack/approvals.d.ts.map +1 -1
  25. package/dist/channels/slack/approvals.js +86 -0
  26. package/dist/channels/slack/cli-project.d.ts +19 -0
  27. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  28. package/dist/channels/slack/cli-project.js +234 -0
  29. package/dist/channels/slack/defaults.d.ts.map +1 -1
  30. package/dist/channels/slack/defaults.js +142 -63
  31. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  32. package/dist/channels/slack/dispatch.js +1 -1
  33. package/dist/channels/slack/init.d.ts +3 -2
  34. package/dist/channels/slack/init.d.ts.map +1 -1
  35. package/dist/channels/slack/init.js +5 -5
  36. package/dist/channels/slack/interactive.d.ts +2 -1
  37. package/dist/channels/slack/interactive.d.ts.map +1 -1
  38. package/dist/channels/slack/interactive.js +15 -4
  39. package/dist/channels/slack/live-delivery.d.ts +1 -1
  40. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  41. package/dist/channels/slack/live-delivery.js +4 -2
  42. package/dist/channels/slack/manifest.d.ts +1 -1
  43. package/dist/channels/slack/manifest.js +1 -1
  44. package/dist/channels/slack/progress-delivery.d.ts +28 -0
  45. package/dist/channels/slack/progress-delivery.d.ts.map +1 -0
  46. package/dist/channels/slack/progress-delivery.js +115 -0
  47. package/dist/channels/slack/setup.d.ts +1 -1
  48. package/dist/channels/slack/setup.d.ts.map +1 -1
  49. package/dist/channels/slack/setup.js +10 -6
  50. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  51. package/dist/channels/slack/slack-channel.js +1 -0
  52. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  53. package/dist/channels/slack/tool-approval-events.js +91 -1
  54. package/dist/channels/slack/types.d.ts +9 -1
  55. package/dist/channels/slack/types.d.ts.map +1 -1
  56. package/dist/docs/404.html +2 -2
  57. package/dist/docs/ab.html +4 -4
  58. package/dist/docs/assets/{app.jXleCmOc.js → app.BplEAZ3S.js} +1 -1
  59. package/dist/docs/assets/chunks/@localSearchIndexroot.DpuI4i7Z.js +1 -0
  60. package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.CHXyv4wP.js} +1 -1
  61. package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.BTcsZmjQ.js} +2 -2
  62. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  63. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  64. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  65. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  66. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  67. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  68. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  69. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.DfoeyvL0.js} +9 -6
  70. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
  71. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  72. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.BEJx9XVj.js} +2 -2
  73. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  74. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.CvAHsdSp.js} +10 -2
  75. package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
  76. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  77. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  78. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  79. package/dist/docs/building-with-agents.html +4 -4
  80. package/dist/docs/concepts.html +4 -4
  81. package/dist/docs/deployment.html +7 -7
  82. package/dist/docs/deployment.md +5 -4
  83. package/dist/docs/design/runtime-abstraction.md +1757 -0
  84. package/dist/docs/evals.html +4 -4
  85. package/dist/docs/guides/agent-to-agent.html +4 -4
  86. package/dist/docs/guides/cloud-runtime.html +4 -4
  87. package/dist/docs/guides/convert-automation.html +4 -4
  88. package/dist/docs/guides/github.html +4 -4
  89. package/dist/docs/guides/human-in-the-loop.html +4 -4
  90. package/dist/docs/guides/mcp-oauth.html +8 -8
  91. package/dist/docs/guides/mcp-oauth.md +26 -8
  92. package/dist/docs/guides/opentelemetry.html +4 -4
  93. package/dist/docs/guides/slack.html +17 -33
  94. package/dist/docs/guides/slack.md +92 -201
  95. package/dist/docs/guides/webhooks.html +4 -4
  96. package/dist/docs/hashmap.json +1 -1
  97. package/dist/docs/hillclimbing.html +4 -4
  98. package/dist/docs/index.html +6 -6
  99. package/dist/docs/index.md +5 -3
  100. package/dist/docs/llms-full.txt +2065 -637
  101. package/dist/docs/llms.txt +3 -2
  102. package/dist/docs/quickstart.html +4 -4
  103. package/dist/docs/reference/agent-config.html +6 -6
  104. package/dist/docs/reference/agent-config.md +2 -2
  105. package/dist/docs/reference/artifacts.html +4 -4
  106. package/dist/docs/reference/channels.html +4 -4
  107. package/dist/docs/reference/cli.html +13 -10
  108. package/dist/docs/reference/cli.md +32 -16
  109. package/dist/docs/reference/connections.html +9 -8
  110. package/dist/docs/reference/connections.md +6 -3
  111. package/dist/docs/reference/hooks.html +4 -4
  112. package/dist/docs/reference/http-api.html +7 -7
  113. package/dist/docs/reference/http-api.md +3 -1
  114. package/dist/docs/reference/instructions.html +4 -4
  115. package/dist/docs/reference/playground.html +4 -4
  116. package/dist/docs/reference/project-layout.html +4 -4
  117. package/dist/docs/reference/prompt.html +4 -4
  118. package/dist/docs/reference/schedules.html +4 -4
  119. package/dist/docs/reference/sessions.html +5 -5
  120. package/dist/docs/reference/sessions.md +4 -2
  121. package/dist/docs/reference/skills.html +4 -4
  122. package/dist/docs/reference/subagents.html +4 -4
  123. package/dist/docs/reference/tools.html +15 -7
  124. package/dist/docs/reference/tools.md +34 -2
  125. package/dist/docs/scaffolding-agents.html +4 -4
  126. package/dist/docs/storage.html +4 -4
  127. package/dist/docs/templates/agentic-owners.html +4 -4
  128. package/dist/docs/templates/agents-md.html +28 -0
  129. package/dist/docs/templates/agents-md.md +105 -0
  130. package/dist/docs/templates/code-wiki.html +5 -5
  131. package/dist/docs/templates/demo.html +4 -4
  132. package/dist/docs/templates/pr-autofixer.html +4 -4
  133. package/dist/docs/templates/security-help.html +4 -4
  134. package/dist/docs/templates/security-reviewer.html +5 -5
  135. package/dist/docs/templates/triage.html +4 -4
  136. package/dist/docs/troubleshooting.html +5 -5
  137. package/dist/docs/troubleshooting.md +2 -2
  138. package/dist/evals/run-facts.d.ts.map +1 -1
  139. package/dist/evals/run-facts.js +27 -0
  140. package/dist/files-backends/cursor-hosted.d.ts +3 -2
  141. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  142. package/dist/files-backends/cursor-hosted.js +3 -2
  143. package/dist/index.d.ts +1 -0
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js +1 -0
  146. package/dist/internal/advertise-tools.d.ts.map +1 -1
  147. package/dist/internal/advertise-tools.js +15 -9
  148. package/dist/internal/approval-gate.d.ts +12 -13
  149. package/dist/internal/approval-gate.d.ts.map +1 -1
  150. package/dist/internal/approval-gate.js +16 -21
  151. package/dist/internal/as-of.d.ts +19 -0
  152. package/dist/internal/as-of.d.ts.map +1 -0
  153. package/dist/internal/as-of.js +82 -0
  154. package/dist/internal/chat-client.js +10 -0
  155. package/dist/internal/cli-deploy.d.ts +6 -0
  156. package/dist/internal/cli-deploy.d.ts.map +1 -1
  157. package/dist/internal/cli-deploy.js +149 -21
  158. package/dist/internal/cli-mcp-oauth.js +8 -18
  159. package/dist/internal/cli-slack-manual.d.ts +24 -2
  160. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  161. package/dist/internal/cli-slack-manual.js +251 -20
  162. package/dist/internal/cli-slack.d.ts +9 -2
  163. package/dist/internal/cli-slack.d.ts.map +1 -1
  164. package/dist/internal/cli-slack.js +29 -82
  165. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  166. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  167. package/dist/internal/cursor/account-mcp.js +17 -0
  168. package/dist/internal/cursor-agent-template.d.ts +1 -1
  169. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  170. package/dist/internal/cursor-agent-template.js +1 -0
  171. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  172. package/dist/internal/cursor-event-relay.js +5 -6
  173. package/dist/internal/deploy-client.d.ts +17 -0
  174. package/dist/internal/deploy-client.d.ts.map +1 -1
  175. package/dist/internal/deploy-client.js +31 -10
  176. package/dist/internal/env-file.d.ts +19 -0
  177. package/dist/internal/env-file.d.ts.map +1 -0
  178. package/dist/internal/env-file.js +90 -0
  179. package/dist/internal/framework-file-storage.d.ts +11 -17
  180. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  181. package/dist/internal/framework-file-storage.js +17 -40
  182. package/dist/internal/framework-storage-selection.d.ts +13 -11
  183. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  184. package/dist/internal/framework-storage-selection.js +37 -26
  185. package/dist/internal/hosted-admission-adapter.d.ts +28 -0
  186. package/dist/internal/hosted-admission-adapter.d.ts.map +1 -0
  187. package/dist/internal/hosted-admission-adapter.js +7 -0
  188. package/dist/internal/hosted-delivery-protocol.d.ts +13 -1
  189. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  190. package/dist/internal/hosted-delivery-protocol.js +8 -1
  191. package/dist/internal/hosted-delivery.d.ts +21 -1
  192. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  193. package/dist/internal/hosted-delivery.js +101 -34
  194. package/dist/internal/hosted-execution-diag.d.ts +59 -0
  195. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  196. package/dist/internal/hosted-execution-diag.js +187 -0
  197. package/dist/internal/http-channel.d.ts.map +1 -1
  198. package/dist/internal/http-channel.js +85 -2
  199. package/dist/internal/init-project.d.ts.map +1 -1
  200. package/dist/internal/init-project.js +2 -0
  201. package/dist/internal/logs-client.d.ts +5 -0
  202. package/dist/internal/logs-client.d.ts.map +1 -1
  203. package/dist/internal/logs-client.js +5 -0
  204. package/dist/internal/mcp-endpoint.js +2 -3
  205. package/dist/internal/mcp-host.d.ts +10 -0
  206. package/dist/internal/mcp-host.d.ts.map +1 -1
  207. package/dist/internal/mcp-host.js +57 -8
  208. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  209. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  210. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  211. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  212. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  213. package/dist/internal/mcp-oauth-persist.js +92 -0
  214. package/dist/internal/mcp-oauth.d.ts +1 -0
  215. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  216. package/dist/internal/mcp-oauth.js +10 -2
  217. package/dist/internal/null-kv-storage.d.ts +48 -0
  218. package/dist/internal/null-kv-storage.d.ts.map +1 -0
  219. package/dist/internal/null-kv-storage.js +58 -0
  220. package/dist/internal/otel/collector.d.ts.map +1 -1
  221. package/dist/internal/otel/collector.js +22 -9
  222. package/dist/internal/server.d.ts +6 -0
  223. package/dist/internal/server.d.ts.map +1 -1
  224. package/dist/internal/server.js +123 -33
  225. package/dist/internal/session-engine.d.ts +88 -3
  226. package/dist/internal/session-engine.d.ts.map +1 -1
  227. package/dist/internal/session-engine.js +538 -46
  228. package/dist/internal/session-run-log.d.ts +120 -0
  229. package/dist/internal/session-run-log.d.ts.map +1 -0
  230. package/dist/internal/session-run-log.js +359 -0
  231. package/dist/internal/sessions-client.d.ts +9 -7
  232. package/dist/internal/sessions-client.d.ts.map +1 -1
  233. package/dist/internal/sessions-client.js +3 -1
  234. package/dist/internal/storage-coordinator.d.ts +13 -0
  235. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  236. package/dist/internal/storage-coordinator.js +37 -6
  237. package/dist/internal/storage-roles.d.ts +5 -0
  238. package/dist/internal/storage-roles.d.ts.map +1 -1
  239. package/dist/internal/stream-progress.d.ts.map +1 -1
  240. package/dist/internal/stream-progress.js +10 -0
  241. package/dist/internal/tool-policy.d.ts +29 -9
  242. package/dist/internal/tool-policy.d.ts.map +1 -1
  243. package/dist/internal/tool-policy.js +24 -0
  244. package/dist/internal/trajectory.d.ts.map +1 -1
  245. package/dist/internal/trajectory.js +10 -0
  246. package/dist/internal/turn-outbox.d.ts +24 -0
  247. package/dist/internal/turn-outbox.d.ts.map +1 -0
  248. package/dist/internal/turn-outbox.js +80 -0
  249. package/dist/playground/assets/index-D7rGZNjy.css +1 -0
  250. package/dist/playground/assets/index-jzNbJENa.js +69 -0
  251. package/dist/playground/index.html +2 -2
  252. package/dist/storage-backends/cursor-hosted.d.ts +1 -1
  253. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  254. package/dist/storage-backends/cursor-hosted.js +12 -0
  255. package/dist/storage-protocol.d.ts +8 -0
  256. package/dist/storage-protocol.d.ts.map +1 -1
  257. package/dist/storage-protocol.js +8 -0
  258. package/dist/storage.d.ts +8 -0
  259. package/dist/storage.d.ts.map +1 -1
  260. package/dist/tools.d.ts +12 -2
  261. package/dist/tools.d.ts.map +1 -1
  262. package/dist/types.d.ts +88 -7
  263. package/dist/types.d.ts.map +1 -1
  264. package/docs/README.md +5 -3
  265. package/docs/deployment.md +5 -4
  266. package/docs/design/runtime-abstraction.md +1757 -0
  267. package/docs/guides/mcp-oauth.md +26 -8
  268. package/docs/guides/slack.md +93 -202
  269. package/docs/reference/agent-config.md +2 -2
  270. package/docs/reference/cli.md +32 -16
  271. package/docs/reference/connections.md +6 -3
  272. package/docs/reference/http-api.md +3 -1
  273. package/docs/reference/sessions.md +4 -2
  274. package/docs/reference/tools.md +34 -2
  275. package/docs/templates/agents-md.md +110 -0
  276. package/docs/troubleshooting.md +2 -2
  277. package/package.json +1 -1
  278. package/skills/create-agent/SKILL.md +2 -1
  279. package/skills/mcp-auth/SKILL.md +6 -3
  280. package/skills/setup-slack/SKILL.md +23 -13
  281. package/src/bin/agent-serve.ts +12 -1
  282. package/src/channels/github/github-channel.ts +17 -0
  283. package/src/channels/origin/api.ts +24 -5
  284. package/src/channels/origin/binding.ts +55 -26
  285. package/src/channels/origin/index.ts +1 -0
  286. package/src/channels/origin/origin-channel.ts +78 -10
  287. package/src/channels/origin/reactions.ts +82 -0
  288. package/src/channels/origin/types.ts +11 -0
  289. package/src/channels/slack/approvals.ts +105 -0
  290. package/src/channels/slack/cli-project.ts +267 -0
  291. package/src/channels/slack/defaults.ts +165 -74
  292. package/src/channels/slack/dispatch.ts +2 -0
  293. package/src/channels/slack/init.ts +10 -5
  294. package/src/channels/slack/interactive.ts +35 -9
  295. package/src/channels/slack/live-delivery.ts +5 -3
  296. package/src/channels/slack/manifest.ts +1 -1
  297. package/src/channels/slack/progress-delivery.ts +133 -0
  298. package/src/channels/slack/setup.ts +10 -6
  299. package/src/channels/slack/slack-channel.ts +1 -0
  300. package/src/channels/slack/tool-approval-events.ts +93 -0
  301. package/src/channels/slack/types.ts +9 -1
  302. package/src/evals/run-facts.ts +32 -0
  303. package/src/files-backends/cursor-hosted.ts +3 -2
  304. package/src/index.ts +4 -0
  305. package/src/internal/advertise-tools.ts +31 -10
  306. package/src/internal/approval-gate.ts +20 -29
  307. package/src/internal/as-of.ts +107 -0
  308. package/src/internal/chat-client.ts +19 -0
  309. package/src/internal/cli-deploy.ts +203 -13
  310. package/src/internal/cli-mcp-oauth.ts +7 -21
  311. package/src/internal/cli-slack-manual.ts +350 -18
  312. package/src/internal/cli-slack.ts +40 -84
  313. package/src/internal/cursor/account-mcp.ts +21 -0
  314. package/src/internal/cursor-agent-template.ts +1 -0
  315. package/src/internal/cursor-event-relay.ts +5 -6
  316. package/src/internal/deploy-client.ts +43 -0
  317. package/src/internal/env-file.ts +84 -0
  318. package/src/internal/framework-file-storage.ts +18 -46
  319. package/src/internal/framework-storage-selection.ts +41 -25
  320. package/src/internal/hosted-admission-adapter.ts +42 -0
  321. package/src/internal/hosted-delivery-protocol.ts +14 -2
  322. package/src/internal/hosted-delivery.ts +169 -44
  323. package/src/internal/hosted-execution-diag.ts +252 -0
  324. package/src/internal/http-channel.ts +107 -0
  325. package/src/internal/init-project.ts +2 -0
  326. package/src/internal/logs-client.ts +5 -0
  327. package/src/internal/mcp-endpoint.ts +13 -4
  328. package/src/internal/mcp-host.ts +77 -11
  329. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  330. package/src/internal/mcp-oauth-persist.ts +127 -0
  331. package/src/internal/mcp-oauth.ts +17 -1
  332. package/src/internal/null-kv-storage.ts +74 -0
  333. package/src/internal/otel/collector.ts +30 -0
  334. package/src/internal/server.ts +159 -11
  335. package/src/internal/session-engine.ts +691 -57
  336. package/src/internal/session-run-log.ts +584 -0
  337. package/src/internal/sessions-client.ts +17 -8
  338. package/src/internal/storage-coordinator.ts +49 -9
  339. package/src/internal/storage-roles.ts +7 -0
  340. package/src/internal/stream-progress.ts +19 -0
  341. package/src/internal/tool-policy.ts +49 -9
  342. package/src/internal/trajectory.ts +10 -0
  343. package/src/internal/turn-outbox.ts +85 -0
  344. package/src/storage-backends/cursor-hosted.ts +13 -1
  345. package/src/storage-protocol.ts +10 -0
  346. package/src/storage.ts +12 -0
  347. package/src/tools.ts +12 -2
  348. package/src/types.ts +93 -8
  349. package/templates/agents-md/README.md +39 -0
  350. package/templates/agents-md/agent/agent.ts +8 -0
  351. package/templates/agents-md/agent/channels/github.ts +10 -0
  352. package/templates/agents-md/agent/channels/queue.ts +28 -0
  353. package/templates/agents-md/agent/channels/slack.ts +9 -0
  354. package/templates/agents-md/agent/instructions.md +39 -0
  355. package/templates/agents-md/agent/lib/collect.ts +439 -0
  356. package/templates/agents-md/agent/lib/config.ts +8 -0
  357. package/templates/agents-md/agent/lib/paths.ts +51 -0
  358. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  359. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  360. package/templates/agents-md/evals/agents.eval.ts +135 -0
  361. package/templates/agents-md/evals/evals.config.ts +5 -0
  362. package/templates/agents-md/init.json +38 -0
  363. package/templates/agents-md/package.json +18 -0
  364. package/templates/agents-md/tsconfig.json +12 -0
  365. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
  366. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  367. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  368. package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
  369. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  370. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  371. package/dist/docs/design/agsh.md +0 -406
  372. package/dist/playground/assets/index-B1DDvBeC.css +0 -1
  373. package/dist/playground/assets/index-Cx9CclK_.js +0 -88
  374. package/docs/design/agsh.md +0 -406
  375. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  376. /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
  377. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  378. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  379. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
  380. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  381. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -10,7 +10,9 @@ import {
10
10
  continuationSource,
11
11
  normalizeIdentity,
12
12
  } from "../../internal/continuation-identity.js";
13
+ import { isDeferredChannelSession } from "../../internal/deferred-channel-session.js";
13
14
  import { isDevMode } from "../../internal/dev-mode.js";
15
+ import { attachHostedAdmissionAdapter } from "../../internal/hosted-admission-adapter.js";
14
16
  import { bindLocalIngressSend } from "../../internal/local-ingress.js";
15
17
  import type {
16
18
  AgentCloudOptions,
@@ -306,6 +308,7 @@ export function parseOriginPullRequestEvent(input: {
306
308
  repository,
307
309
  pullRequestNumber: number,
308
310
  pullRequestUrl: url,
311
+ pullRequestAuthor: author,
309
312
  payload,
310
313
  sender: originSender(input.envelope, payload),
311
314
  },
@@ -432,16 +435,43 @@ function enrichEventContext(input: {
432
435
  origin: buildOriginBinding({
433
436
  repository: input.ctx.repository,
434
437
  installationId: input.ctx.installationId,
438
+ cursorAccount:
439
+ input.options.cursorAccount !== undefined &&
440
+ input.options.cursorAccount !== false,
435
441
  app: input.options.app,
436
442
  }),
437
443
  };
438
444
  }
439
445
 
446
+ export type OriginDispatchOutcome = "skip" | "send" | "task";
447
+
448
+ const dispatchOutcomeByResponse = new WeakMap<
449
+ Response,
450
+ OriginDispatchOutcome
451
+ >();
452
+
453
+ function withDispatchOutcome(
454
+ response: Response,
455
+ outcome: OriginDispatchOutcome
456
+ ): Response {
457
+ dispatchOutcomeByResponse.set(response, outcome);
458
+ return response;
459
+ }
460
+
461
+ export function getOriginDispatchOutcome(
462
+ response: Response
463
+ ): OriginDispatchOutcome | undefined {
464
+ return dispatchOutcomeByResponse.get(response);
465
+ }
466
+
440
467
  function skipped(
441
468
  reason: string,
442
469
  extra: Record<string, unknown> = {}
443
470
  ): Response {
444
- return Response.json({ ok: true, skipped: true, reason, ...extra });
471
+ return withDispatchOutcome(
472
+ Response.json({ ok: true, skipped: true, reason, ...extra }),
473
+ "skip"
474
+ );
445
475
  }
446
476
 
447
477
  function acknowledgeTask(
@@ -458,9 +488,12 @@ function acknowledgeTask(
458
488
  });
459
489
  })
460
490
  );
461
- return Response.json(
462
- { ok: true, dispatched: true, mode: "task", ...extra },
463
- { status: 202 }
491
+ return withDispatchOutcome(
492
+ Response.json(
493
+ { ok: true, dispatched: true, mode: "task", ...extra },
494
+ { status: 202 }
495
+ ),
496
+ "task"
464
497
  );
465
498
  }
466
499
 
@@ -481,12 +514,26 @@ async function startTurn(input: {
481
514
  title: input.dispatch.title ?? defaultOriginTitle(input.ctx),
482
515
  ...(input.cloud === undefined ? {} : { cloud: input.cloud }),
483
516
  });
484
- return Response.json({
485
- ok: true,
486
- sessionId: session.id,
487
- isNew: session.isNew,
488
- continuationToken: session.continuationToken,
489
- });
517
+ if (isDeferredChannelSession(session)) {
518
+ // Hosted admission captured the send; the control plane allocates the
519
+ // session after acknowledgement, so there is nothing to report yet.
520
+ return withDispatchOutcome(
521
+ Response.json(
522
+ { ok: true, dispatched: true, mode: "turn" },
523
+ { status: 202 }
524
+ ),
525
+ "send"
526
+ );
527
+ }
528
+ return withDispatchOutcome(
529
+ Response.json({
530
+ ok: true,
531
+ sessionId: session.id,
532
+ isNew: session.isNew,
533
+ continuationToken: session.continuationToken,
534
+ }),
535
+ "send"
536
+ );
490
537
  }
491
538
 
492
539
  async function resolveCloudOptions(
@@ -808,6 +855,27 @@ export function originChannel(
808
855
  ],
809
856
  });
810
857
  originChannelOptionsByDefinition.set(definition, channelOptions);
858
+ attachHostedAdmissionAdapter(definition, {
859
+ kind: ORIGIN_CHANNEL_KIND,
860
+ dispatch: async ({ payload, args }) => {
861
+ const envelope = parseOriginWebhookEnvelope(payload);
862
+ if (envelope === null) {
863
+ return Response.json(
864
+ { ok: false, error: "invalid_origin_payload" },
865
+ { status: 400 }
866
+ );
867
+ }
868
+ return dispatchOriginWebhook({
869
+ options: channelOptions,
870
+ envelope,
871
+ args,
872
+ });
873
+ },
874
+ getOutcome: (response) => {
875
+ const outcome = getOriginDispatchOutcome(response);
876
+ return outcome === "skip" || outcome === "task" ? outcome : undefined;
877
+ },
878
+ });
811
879
  if (usesCursorAccount) {
812
880
  registerOriginCursorAccountRuntime(definition, {
813
881
  options: channelOptions,
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Origin PR comment reactions — the Origin analogue of the GitHub channel's
3
+ * `thread.react("eyes")`.
4
+ *
5
+ * Unlike checks and comments, reactions have no `/v1/origin/...` REST
6
+ * binding. The only write is Connect unary JSON against Origin's first-party
7
+ * service (`origin.v1.OriginService/SetCommentReaction`), which lives on a
8
+ * different host than the public REST API — {@link originRpcBaseUrl}, not
9
+ * {@link originPublicApiBaseUrl}. `ctx.origin.reactToComment` already points
10
+ * at the right one; a hand-built client must too.
11
+ *
12
+ * Writing needs `repository:pull_requests:reviews:write`: an `oit_…`
13
+ * installation token for app channels, or a deployment service-account
14
+ * session (hosted v2 forks). Origin rejects reactions on comments that
15
+ * belong to an unsubmitted review. API failures throw `OriginApiError`.
16
+ */
17
+
18
+ import type { OriginApiMethod } from "./api.js";
19
+
20
+ /** Any client that can call Origin's ConnectRPC host. */
21
+ export interface OriginReactionClient {
22
+ request<T = unknown>(input: {
23
+ method: OriginApiMethod;
24
+ path: string;
25
+ body?: unknown;
26
+ }): Promise<T>;
27
+ }
28
+
29
+ /** Connect unary route backing {@link setOriginCommentReaction}. */
30
+ export const SET_ORIGIN_COMMENT_REACTION_PATH =
31
+ "/origin.v1.OriginService/SetCommentReaction";
32
+
33
+ /**
34
+ * The eight reactions Origin accepts. Anything else is rejected with
35
+ * `invalid_argument`, so channels get a compile error instead of a 400.
36
+ */
37
+ export const ORIGIN_COMMENT_REACTIONS = [
38
+ "👍",
39
+ "👎",
40
+ "😄",
41
+ "🎉",
42
+ "😕",
43
+ "❤️",
44
+ "🚀",
45
+ "👀",
46
+ ] as const;
47
+
48
+ export type OriginCommentReaction = (typeof ORIGIN_COMMENT_REACTIONS)[number];
49
+
50
+ export interface OriginCommentReactionInput {
51
+ /** Origin comment TypeID (`cmt_…`), i.e. `comment.id` on a comment hook. */
52
+ commentId: string;
53
+ emoji: OriginCommentReaction;
54
+ /** `false` removes the reaction. Removing one that is absent is a no-op. */
55
+ active?: boolean;
56
+ }
57
+
58
+ /**
59
+ * Add (or, with `active: false`, remove) one reaction on an Origin PR
60
+ * comment as the client's own principal.
61
+ *
62
+ * ```ts
63
+ * await setOriginCommentReaction({
64
+ * origin: createOriginClient({ apiBaseUrl: originRpcBaseUrl() }),
65
+ * commentId: comment.id,
66
+ * emoji: "👀",
67
+ * });
68
+ * ```
69
+ */
70
+ export async function setOriginCommentReaction(
71
+ input: OriginCommentReactionInput & { origin: OriginReactionClient }
72
+ ): Promise<void> {
73
+ await input.origin.request({
74
+ method: "POST",
75
+ path: SET_ORIGIN_COMMENT_REACTION_PATH,
76
+ body: {
77
+ commentId: input.commentId,
78
+ emoji: input.emoji,
79
+ active: input.active ?? true,
80
+ },
81
+ });
82
+ }
@@ -58,6 +58,11 @@ export interface OriginEventContext {
58
58
  repository: OriginRepositoryRef;
59
59
  pullRequestNumber: number;
60
60
  pullRequestUrl: string;
61
+ /**
62
+ * Who opened the pull request, when the payload carries it. Distinct from
63
+ * {@link sender} — on a comment event the sender is the commenter.
64
+ */
65
+ pullRequestAuthor?: OriginActor;
61
66
  /**
62
67
  * Raw parsed `event.payload` record (`{}` when absent), for consumers
63
68
  * whose normalization needs more than the typed event — the GitHub
@@ -152,6 +157,12 @@ export interface OriginAppOptions {
152
157
  token?: string | OriginTokenProvider;
153
158
  /** Public Origin REST base, e.g. `https://api.cursor.com`. */
154
159
  apiBaseUrl?: string;
160
+ /**
161
+ * Origin ConnectRPC base for operations without a REST binding (comment
162
+ * reactions), e.g. `https://api.origin.cursor.com`. Falls back to
163
+ * `ORIGIN_RPC_BASE_URL`.
164
+ */
165
+ rpcBaseUrl?: string;
155
166
  }
156
167
 
157
168
  export interface OriginChannelOptions {
@@ -6,6 +6,8 @@ import type { ApprovalDecision } from "../../types.js";
6
6
 
7
7
  export const SLACK_APPROVE_ACTION_ID = "agent_serve_approve";
8
8
  export const SLACK_DENY_ACTION_ID = "agent_serve_deny";
9
+ export const SLACK_AUTH_CONNECTED_ACTION_ID = "agent_serve_auth_connected";
10
+ export const SLACK_AUTH_CANCEL_ACTION_ID = "agent_serve_auth_cancel";
9
11
 
10
12
  export interface SlackApprovalActionValue {
11
13
  sessionId: string;
@@ -54,6 +56,29 @@ export function decisionFromActionId(
54
56
  return null;
55
57
  }
56
58
 
59
+ export function decisionFromAuthActionId(
60
+ actionId: string
61
+ ): ApprovalDecision | null {
62
+ if (actionId === SLACK_AUTH_CONNECTED_ACTION_ID) {
63
+ return "approve";
64
+ }
65
+ if (actionId === SLACK_AUTH_CANCEL_ACTION_ID) {
66
+ return "deny";
67
+ }
68
+ return null;
69
+ }
70
+
71
+ export function authThreadStatus(connection: string): string {
72
+ const prefix = "Waiting for ";
73
+ const suffix = " auth…";
74
+ const budget = 50 - prefix.length - suffix.length;
75
+ const name =
76
+ connection.length <= budget
77
+ ? connection
78
+ : `${connection.slice(0, Math.max(0, budget - 1))}…`;
79
+ return `${prefix}${name}${suffix}`;
80
+ }
81
+
57
82
  const REDACTED = "[REDACTED]";
58
83
  const SENSITIVE_KEY =
59
84
  /(?:pass(?:word|wd)?|secret|token|api[_-]?key|authorization|credential|private[_-]?key|access[_-]?key)/i;
@@ -176,6 +201,86 @@ export function buildApprovalBlocks(args: {
176
201
  ];
177
202
  }
178
203
 
204
+ export function buildAuthBlocks(args: {
205
+ sessionId: string;
206
+ callId: string;
207
+ toolName: string;
208
+ connection: string;
209
+ authUrl: string;
210
+ }): unknown[] {
211
+ const value = encodeApprovalActionValue({
212
+ sessionId: args.sessionId,
213
+ callId: args.callId,
214
+ });
215
+ const label =
216
+ args.connection.length > 20
217
+ ? `Authorize ${args.connection.slice(0, 17)}…`
218
+ : `Authorize ${args.connection}`;
219
+ return [
220
+ {
221
+ type: "header",
222
+ text: {
223
+ type: "plain_text",
224
+ text: "MCP authorization needed",
225
+ emoji: true,
226
+ },
227
+ },
228
+ {
229
+ type: "section",
230
+ text: {
231
+ type: "mrkdwn",
232
+ text: `Connect \`${args.connection}\` so \`${args.toolName}\` can continue.`,
233
+ },
234
+ },
235
+ {
236
+ type: "actions",
237
+ block_id: `auth_${args.callId}`.slice(0, 255),
238
+ elements: [
239
+ {
240
+ type: "button",
241
+ text: { type: "plain_text", text: label, emoji: true },
242
+ url: args.authUrl,
243
+ },
244
+ {
245
+ type: "button",
246
+ action_id: SLACK_AUTH_CONNECTED_ACTION_ID,
247
+ text: { type: "plain_text", text: "Connected", emoji: true },
248
+ style: "primary",
249
+ value,
250
+ },
251
+ {
252
+ type: "button",
253
+ action_id: SLACK_AUTH_CANCEL_ACTION_ID,
254
+ text: { type: "plain_text", text: "Cancel", emoji: true },
255
+ style: "danger",
256
+ value,
257
+ },
258
+ ],
259
+ },
260
+ ];
261
+ }
262
+
263
+ export function buildResolvedAuthBlocks(args: {
264
+ connection: string;
265
+ decision: ApprovalDecision;
266
+ byUserId?: string;
267
+ }): unknown[] {
268
+ const verb = args.decision === "approve" ? "Connected" : "Cancelled";
269
+ const by =
270
+ args.byUserId === undefined || args.byUserId === ""
271
+ ? ""
272
+ : ` by <@${args.byUserId}>`;
273
+ return [
274
+ {
275
+ type: "section",
276
+ text: {
277
+ type: "mrkdwn",
278
+ text: `*${verb}*${by} — \`${args.connection}\``,
279
+ },
280
+ },
281
+ ];
282
+ }
283
+
179
284
  export function buildResolvedApprovalBlocks(args: {
180
285
  toolName: string;
181
286
  decision: ApprovalDecision;
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Official Slack CLI project files for `slack init --manual`.
3
+ *
4
+ * `.slack/` is the committed Slack CLI project: hook, manifests, hooks.json.
5
+ * Agent SDK serve owns the socket; do not `slack deploy` / `slack run`.
6
+ */
7
+
8
+ import { randomUUID } from "node:crypto";
9
+ import { existsSync } from "node:fs";
10
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+
14
+ export const SLACK_CLI_HOME_BIN = ".slack/bin/slack";
15
+
16
+ export const SLACK_CLI_GET_MANIFEST = "node .slack/get-manifest.mjs";
17
+
18
+ export type SlackCliEnvironment = "local" | "deployed";
19
+
20
+ const TEAM_ID_RE = /\(Team ID: ([TE][A-Z0-9]+)\)/g;
21
+
22
+ const GET_MANIFEST_JS = `import { readFileSync } from "node:fs";
23
+ import { dirname, join } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+
26
+ const here = dirname(fileURLToPath(import.meta.url));
27
+
28
+ function target() {
29
+ const env = process.env.SLACK_ENV ?? process.env.SLACK_APP_ENVIRONMENT;
30
+ if (env === "deployed" || env === "prod") return "manifest.prod.json";
31
+ if (env === "local" || env === "dev") return "manifest.dev.json";
32
+ try {
33
+ const raw = readFileSync(join(here, "cli-environment"), "utf8").trim();
34
+ if (raw === "deployed" || raw === "prod") return "manifest.prod.json";
35
+ } catch {
36
+ // default local
37
+ }
38
+ return "manifest.dev.json";
39
+ }
40
+
41
+ process.stdout.write(readFileSync(join(here, target()), "utf8"));
42
+ `;
43
+
44
+ function isEnoent(error: unknown): boolean {
45
+ return (
46
+ error instanceof Error &&
47
+ "code" in error &&
48
+ (error as NodeJS.ErrnoException).code === "ENOENT"
49
+ );
50
+ }
51
+
52
+ /** Official Slack CLI, if present. `SLACK_CLI` overrides. */
53
+ export function detectSlackCli(
54
+ env: NodeJS.ProcessEnv = process.env
55
+ ): string | undefined {
56
+ const override = env.SLACK_CLI;
57
+ if (override !== undefined && override !== "") {
58
+ return override;
59
+ }
60
+ const home = env.HOME ?? env.USERPROFILE ?? homedir();
61
+ const homeBin = join(home, SLACK_CLI_HOME_BIN);
62
+ return existsSync(homeBin) ? homeBin : undefined;
63
+ }
64
+
65
+ /** Hook + manifests under `.slack/`. Merge hooks.json; keep existing config. */
66
+ export async function writeSlackCliProject(
67
+ rootDir: string,
68
+ manifests: ReadonlyArray<{ name: string; contents: string }>
69
+ ): Promise<string[]> {
70
+ const slackDir = join(rootDir, ".slack");
71
+ await mkdir(slackDir, { recursive: true });
72
+ const created: string[] = [];
73
+
74
+ await writeFile(join(slackDir, "get-manifest.mjs"), GET_MANIFEST_JS);
75
+ created.push(".slack/get-manifest.mjs");
76
+
77
+ for (const file of manifests) {
78
+ await writeFile(join(slackDir, file.name), file.contents);
79
+ created.push(`.slack/${file.name}`);
80
+ }
81
+
82
+ if (await upsertGetManifestHook(slackDir)) {
83
+ created.push(".slack/hooks.json");
84
+ }
85
+ if (
86
+ await writeIfMissing(
87
+ join(slackDir, "config.json"),
88
+ `${JSON.stringify(
89
+ {
90
+ manifest: { source: "local" },
91
+ project_id: randomUUID(),
92
+ },
93
+ null,
94
+ 2
95
+ )}\n`
96
+ )
97
+ ) {
98
+ created.push(".slack/config.json");
99
+ }
100
+ if (await mergeGitignore(join(slackDir, ".gitignore"))) {
101
+ created.push(".slack/.gitignore");
102
+ }
103
+ return created;
104
+ }
105
+
106
+ async function upsertGetManifestHook(slackDir: string): Promise<boolean> {
107
+ const path = join(slackDir, "hooks.json");
108
+ let existing: { hooks?: Record<string, string> };
109
+ try {
110
+ const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
111
+ if (
112
+ parsed === null ||
113
+ typeof parsed !== "object" ||
114
+ Array.isArray(parsed)
115
+ ) {
116
+ throw new Error(".slack/hooks.json is not a JSON object");
117
+ }
118
+ existing = parsed as { hooks?: Record<string, string> };
119
+ } catch (error) {
120
+ if (!isEnoent(error)) {
121
+ throw error;
122
+ }
123
+ await writeFile(
124
+ path,
125
+ `${JSON.stringify({ hooks: { "get-manifest": SLACK_CLI_GET_MANIFEST } }, null, 2)}\n`
126
+ );
127
+ return true;
128
+ }
129
+ const hooks = existing.hooks ?? {};
130
+ if (hooks["get-manifest"] === SLACK_CLI_GET_MANIFEST) {
131
+ return false;
132
+ }
133
+ hooks["get-manifest"] = SLACK_CLI_GET_MANIFEST;
134
+ existing.hooks = hooks;
135
+ await writeFile(path, `${JSON.stringify(existing, null, 2)}\n`);
136
+ return true;
137
+ }
138
+
139
+ async function writeIfMissing(
140
+ path: string,
141
+ contents: string
142
+ ): Promise<boolean> {
143
+ try {
144
+ await writeFile(path, contents, { flag: "wx" });
145
+ return true;
146
+ } catch (error) {
147
+ if (
148
+ error instanceof Error &&
149
+ "code" in error &&
150
+ (error as NodeJS.ErrnoException).code === "EEXIST"
151
+ ) {
152
+ return false;
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ async function mergeGitignore(path: string): Promise<boolean> {
159
+ const lines = ["apps.dev.json", "cache/", "cli-environment"];
160
+ let existing = "";
161
+ try {
162
+ existing = await readFile(path, "utf8");
163
+ } catch (error) {
164
+ if (!isEnoent(error)) {
165
+ throw error;
166
+ }
167
+ await writeFile(path, `${lines.join("\n")}\n`);
168
+ return true;
169
+ }
170
+ const have = new Set(
171
+ existing
172
+ .split(/\r?\n/)
173
+ .map((line) => line.trim())
174
+ .filter((line) => line !== "")
175
+ );
176
+ const missing = lines.filter((line) => !have.has(line));
177
+ if (missing.length === 0) {
178
+ return false;
179
+ }
180
+ const prefix =
181
+ existing === "" || existing.endsWith("\n") ? existing : `${existing}\n`;
182
+ await writeFile(path, `${prefix}${missing.join("\n")}\n`);
183
+ return true;
184
+ }
185
+
186
+ export function parseSlackAuthTeams(text: string): string[] {
187
+ const ids: string[] = [];
188
+ for (const match of text.matchAll(TEAM_ID_RE)) {
189
+ const id = match[1];
190
+ if (id !== undefined && !ids.includes(id)) {
191
+ ids.push(id);
192
+ }
193
+ }
194
+ return ids;
195
+ }
196
+
197
+ export async function readSlackCliAppId(
198
+ dir: string,
199
+ environment: SlackCliEnvironment,
200
+ team?: string
201
+ ): Promise<string | undefined> {
202
+ const apps = await loadAppMap(dir, environment);
203
+ if (apps === undefined) {
204
+ return undefined;
205
+ }
206
+ if (team !== undefined) {
207
+ return appIdOf(apps[team]);
208
+ }
209
+ return firstAppId(apps);
210
+ }
211
+
212
+ async function loadAppMap(
213
+ dir: string,
214
+ environment: SlackCliEnvironment
215
+ ): Promise<Record<string, unknown> | undefined> {
216
+ if (environment === "local") {
217
+ return readJson(join(dir, ".slack/apps.dev.json"));
218
+ }
219
+ const deployed = await readJson(join(dir, ".slack/apps.json"));
220
+ if (deployed === undefined) {
221
+ return undefined;
222
+ }
223
+ const apps = deployed.apps;
224
+ if (apps !== undefined && typeof apps === "object" && apps !== null) {
225
+ return apps as Record<string, unknown>;
226
+ }
227
+ return deployed;
228
+ }
229
+
230
+ function firstAppId(value: Record<string, unknown>): string | undefined {
231
+ for (const entry of Object.values(value)) {
232
+ const id = appIdOf(entry);
233
+ if (id !== undefined) {
234
+ return id;
235
+ }
236
+ }
237
+ return undefined;
238
+ }
239
+
240
+ function appIdOf(entry: unknown): string | undefined {
241
+ if (entry === undefined || entry === null || typeof entry !== "object") {
242
+ return undefined;
243
+ }
244
+ const appId = (entry as { app_id?: unknown }).app_id;
245
+ if (typeof appId === "string" && appId.startsWith("A")) {
246
+ return appId;
247
+ }
248
+ return undefined;
249
+ }
250
+
251
+ async function readJson(
252
+ path: string
253
+ ): Promise<Record<string, unknown> | undefined> {
254
+ try {
255
+ const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
256
+ if (
257
+ parsed === null ||
258
+ typeof parsed !== "object" ||
259
+ Array.isArray(parsed)
260
+ ) {
261
+ return undefined;
262
+ }
263
+ return parsed as Record<string, unknown>;
264
+ } catch {
265
+ return undefined;
266
+ }
267
+ }