lastlight 0.16.0 → 0.16.1

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 (385) hide show
  1. package/README.md +71 -692
  2. package/dist/cli-config.js.map +1 -0
  3. package/dist/cli-format.js.map +1 -0
  4. package/dist/{cli/cli-server.d.ts → cli-server.d.ts} +26 -1
  5. package/dist/{cli/cli-server.js → cli-server.js} +40 -5
  6. package/dist/cli-server.js.map +1 -0
  7. package/dist/cli-timeline.js.map +1 -0
  8. package/dist/{cli/cli.js → cli.js} +4 -3
  9. package/dist/cli.js.map +1 -0
  10. package/dist/{cli/fork-cli.d.ts → fork-cli.d.ts} +3 -3
  11. package/dist/{cli/fork-cli.js → fork-cli.js} +28 -27
  12. package/dist/fork-cli.js.map +1 -0
  13. package/dist/{cli/oauth-cli.js → oauth-cli.js} +1 -1
  14. package/dist/oauth-cli.js.map +1 -0
  15. package/dist/{cli/setup.js → setup.js} +10 -4
  16. package/dist/setup.js.map +1 -0
  17. package/dist/{cli/skills-install.d.ts → skills-install.d.ts} +6 -0
  18. package/dist/{cli/skills-install.js → skills-install.js} +6 -2
  19. package/dist/skills-install.js.map +1 -0
  20. package/package.json +20 -65
  21. package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
  22. package/agent-context/rules.md +0 -80
  23. package/agent-context/security.md +0 -69
  24. package/agent-context/soul.md +0 -21
  25. package/config/default.yaml +0 -119
  26. package/deploy/.env.production.example +0 -100
  27. package/deploy/entrypoint.sh +0 -82
  28. package/deploy/native/README.md +0 -160
  29. package/deploy/native/install.sh +0 -110
  30. package/deploy/native/lastlight.env.example +0 -95
  31. package/deploy/native/lastlight.service +0 -50
  32. package/deploy/sandbox-entrypoint.sh +0 -93
  33. package/dist/admin/auth.d.ts +0 -51
  34. package/dist/admin/auth.js +0 -121
  35. package/dist/admin/auth.js.map +0 -1
  36. package/dist/admin/chat-session-reader.d.ts +0 -39
  37. package/dist/admin/chat-session-reader.js +0 -84
  38. package/dist/admin/chat-session-reader.js.map +0 -1
  39. package/dist/admin/docker.d.ts +0 -61
  40. package/dist/admin/docker.js +0 -187
  41. package/dist/admin/docker.js.map +0 -1
  42. package/dist/admin/index.d.ts +0 -9
  43. package/dist/admin/index.js +0 -59
  44. package/dist/admin/index.js.map +0 -1
  45. package/dist/admin/routes.d.ts +0 -81
  46. package/dist/admin/routes.js +0 -1381
  47. package/dist/admin/routes.js.map +0 -1
  48. package/dist/admin/sessions.d.ts +0 -89
  49. package/dist/admin/sessions.js +0 -204
  50. package/dist/admin/sessions.js.map +0 -1
  51. package/dist/admin/tail.d.ts +0 -12
  52. package/dist/admin/tail.js +0 -145
  53. package/dist/admin/tail.js.map +0 -1
  54. package/dist/admin/version.d.ts +0 -22
  55. package/dist/admin/version.js +0 -92
  56. package/dist/admin/version.js.map +0 -1
  57. package/dist/cli/cli-config.js.map +0 -1
  58. package/dist/cli/cli-format.js.map +0 -1
  59. package/dist/cli/cli-server.js.map +0 -1
  60. package/dist/cli/cli-timeline.js.map +0 -1
  61. package/dist/cli/cli.js.map +0 -1
  62. package/dist/cli/fork-cli.js.map +0 -1
  63. package/dist/cli/oauth-cli.js.map +0 -1
  64. package/dist/cli/setup.js.map +0 -1
  65. package/dist/cli/skills-install.js.map +0 -1
  66. package/dist/config/config-resolve.d.ts +0 -28
  67. package/dist/config/config-resolve.js +0 -47
  68. package/dist/config/config-resolve.js.map +0 -1
  69. package/dist/config/config.d.ts +0 -157
  70. package/dist/config/config.js +0 -581
  71. package/dist/config/config.js.map +0 -1
  72. package/dist/config/core-pin.d.ts +0 -16
  73. package/dist/config/core-pin.js +0 -85
  74. package/dist/config/core-pin.js.map +0 -1
  75. package/dist/config/overlay-assets.d.ts +0 -20
  76. package/dist/config/overlay-assets.js +0 -114
  77. package/dist/config/overlay-assets.js.map +0 -1
  78. package/dist/config/overlay-bootstrap.d.ts +0 -44
  79. package/dist/config/overlay-bootstrap.js +0 -304
  80. package/dist/config/overlay-bootstrap.js.map +0 -1
  81. package/dist/connectors/github-webhook.d.ts +0 -45
  82. package/dist/connectors/github-webhook.js +0 -321
  83. package/dist/connectors/github-webhook.js.map +0 -1
  84. package/dist/connectors/index.d.ts +0 -18
  85. package/dist/connectors/index.js +0 -41
  86. package/dist/connectors/index.js.map +0 -1
  87. package/dist/connectors/messaging/base.d.ts +0 -42
  88. package/dist/connectors/messaging/base.js +0 -139
  89. package/dist/connectors/messaging/base.js.map +0 -1
  90. package/dist/connectors/messaging/delivery.d.ts +0 -14
  91. package/dist/connectors/messaging/delivery.js +0 -38
  92. package/dist/connectors/messaging/delivery.js.map +0 -1
  93. package/dist/connectors/messaging/index.d.ts +0 -4
  94. package/dist/connectors/messaging/index.js +0 -4
  95. package/dist/connectors/messaging/index.js.map +0 -1
  96. package/dist/connectors/messaging/session-manager.d.ts +0 -53
  97. package/dist/connectors/messaging/session-manager.js +0 -255
  98. package/dist/connectors/messaging/session-manager.js.map +0 -1
  99. package/dist/connectors/messaging/types.d.ts +0 -58
  100. package/dist/connectors/messaging/types.js +0 -7
  101. package/dist/connectors/messaging/types.js.map +0 -1
  102. package/dist/connectors/slack/connector.d.ts +0 -142
  103. package/dist/connectors/slack/connector.js +0 -494
  104. package/dist/connectors/slack/connector.js.map +0 -1
  105. package/dist/connectors/slack/index.d.ts +0 -2
  106. package/dist/connectors/slack/index.js +0 -3
  107. package/dist/connectors/slack/index.js.map +0 -1
  108. package/dist/connectors/slack/mrkdwn.d.ts +0 -19
  109. package/dist/connectors/slack/mrkdwn.js +0 -240
  110. package/dist/connectors/slack/mrkdwn.js.map +0 -1
  111. package/dist/connectors/types.d.ts +0 -61
  112. package/dist/connectors/types.js +0 -2
  113. package/dist/connectors/types.js.map +0 -1
  114. package/dist/cron/fanout.d.ts +0 -33
  115. package/dist/cron/fanout.js +0 -55
  116. package/dist/cron/fanout.js.map +0 -1
  117. package/dist/cron/jobs.d.ts +0 -18
  118. package/dist/cron/jobs.js +0 -37
  119. package/dist/cron/jobs.js.map +0 -1
  120. package/dist/cron/scheduler.d.ts +0 -45
  121. package/dist/cron/scheduler.js +0 -105
  122. package/dist/cron/scheduler.js.map +0 -1
  123. package/dist/engine/agent-executor.d.ts +0 -34
  124. package/dist/engine/agent-executor.js +0 -246
  125. package/dist/engine/agent-executor.js.map +0 -1
  126. package/dist/engine/chat/chat-runner.d.ts +0 -132
  127. package/dist/engine/chat/chat-runner.js +0 -424
  128. package/dist/engine/chat/chat-runner.js.map +0 -1
  129. package/dist/engine/chat/chat-skills.d.ts +0 -45
  130. package/dist/engine/chat/chat-skills.js +0 -184
  131. package/dist/engine/chat/chat-skills.js.map +0 -1
  132. package/dist/engine/chat/chat.d.ts +0 -54
  133. package/dist/engine/chat/chat.js +0 -316
  134. package/dist/engine/chat/chat.js.map +0 -1
  135. package/dist/engine/chat/message-batcher.d.ts +0 -32
  136. package/dist/engine/chat/message-batcher.js +0 -109
  137. package/dist/engine/chat/message-batcher.js.map +0 -1
  138. package/dist/engine/dispatcher.d.ts +0 -73
  139. package/dist/engine/dispatcher.js +0 -663
  140. package/dist/engine/dispatcher.js.map +0 -1
  141. package/dist/engine/event-shim.d.ts +0 -117
  142. package/dist/engine/event-shim.js +0 -435
  143. package/dist/engine/event-shim.js.map +0 -1
  144. package/dist/engine/executors/orchestrator.d.ts +0 -81
  145. package/dist/engine/executors/orchestrator.js +0 -365
  146. package/dist/engine/executors/orchestrator.js.map +0 -1
  147. package/dist/engine/executors/shared.d.ts +0 -189
  148. package/dist/engine/executors/shared.js +0 -616
  149. package/dist/engine/executors/shared.js.map +0 -1
  150. package/dist/engine/github/git-auth.d.ts +0 -62
  151. package/dist/engine/github/git-auth.js +0 -161
  152. package/dist/engine/github/git-auth.js.map +0 -1
  153. package/dist/engine/github/github-app-client.d.ts +0 -23
  154. package/dist/engine/github/github-app-client.js +0 -33
  155. package/dist/engine/github/github-app-client.js.map +0 -1
  156. package/dist/engine/github/github-tools.d.ts +0 -18
  157. package/dist/engine/github/github-tools.js +0 -263
  158. package/dist/engine/github/github-tools.js.map +0 -1
  159. package/dist/engine/github/github.d.ts +0 -1193
  160. package/dist/engine/github/github.js +0 -357
  161. package/dist/engine/github/github.js.map +0 -1
  162. package/dist/engine/github/profiles.d.ts +0 -279
  163. package/dist/engine/github/profiles.js +0 -42
  164. package/dist/engine/github/profiles.js.map +0 -1
  165. package/dist/engine/github/review-poster.d.ts +0 -98
  166. package/dist/engine/github/review-poster.js +0 -177
  167. package/dist/engine/github/review-poster.js.map +0 -1
  168. package/dist/engine/llm.d.ts +0 -79
  169. package/dist/engine/llm.js +0 -269
  170. package/dist/engine/llm.js.map +0 -1
  171. package/dist/engine/oauth.d.ts +0 -43
  172. package/dist/engine/oauth.js +0 -103
  173. package/dist/engine/oauth.js.map +0 -1
  174. package/dist/engine/router.d.ts +0 -29
  175. package/dist/engine/router.js +0 -680
  176. package/dist/engine/router.js.map +0 -1
  177. package/dist/engine/screen/classifier.d.ts +0 -93
  178. package/dist/engine/screen/classifier.js +0 -215
  179. package/dist/engine/screen/classifier.js.map +0 -1
  180. package/dist/engine/screen/screen.d.ts +0 -41
  181. package/dist/engine/screen/screen.js +0 -93
  182. package/dist/engine/screen/screen.js.map +0 -1
  183. package/dist/evals-api.d.ts +0 -24
  184. package/dist/evals-api.js +0 -22
  185. package/dist/evals-api.js.map +0 -1
  186. package/dist/index.d.ts +0 -1
  187. package/dist/index.js +0 -940
  188. package/dist/index.js.map +0 -1
  189. package/dist/managed-repos.d.ts +0 -30
  190. package/dist/managed-repos.js +0 -72
  191. package/dist/managed-repos.js.map +0 -1
  192. package/dist/notify/blocks.d.ts +0 -23
  193. package/dist/notify/blocks.js +0 -118
  194. package/dist/notify/blocks.js.map +0 -1
  195. package/dist/notify/index.d.ts +0 -17
  196. package/dist/notify/index.js +0 -7
  197. package/dist/notify/index.js.map +0 -1
  198. package/dist/notify/model.d.ts +0 -57
  199. package/dist/notify/model.js +0 -89
  200. package/dist/notify/model.js.map +0 -1
  201. package/dist/notify/notifier.d.ts +0 -18
  202. package/dist/notify/notifier.js +0 -100
  203. package/dist/notify/notifier.js.map +0 -1
  204. package/dist/notify/render.d.ts +0 -21
  205. package/dist/notify/render.js +0 -59
  206. package/dist/notify/render.js.map +0 -1
  207. package/dist/notify/transports/github.d.ts +0 -26
  208. package/dist/notify/transports/github.js +0 -27
  209. package/dist/notify/transports/github.js.map +0 -1
  210. package/dist/notify/transports/slack.d.ts +0 -27
  211. package/dist/notify/transports/slack.js +0 -37
  212. package/dist/notify/transports/slack.js.map +0 -1
  213. package/dist/notify/types.d.ts +0 -119
  214. package/dist/notify/types.js +0 -16
  215. package/dist/notify/types.js.map +0 -1
  216. package/dist/providers.d.ts +0 -125
  217. package/dist/providers.js +0 -279
  218. package/dist/providers.js.map +0 -1
  219. package/dist/sandbox/docker.d.ts +0 -180
  220. package/dist/sandbox/docker.js +0 -517
  221. package/dist/sandbox/docker.js.map +0 -1
  222. package/dist/sandbox/egress-allowlist.d.ts +0 -74
  223. package/dist/sandbox/egress-allowlist.js +0 -164
  224. package/dist/sandbox/egress-allowlist.js.map +0 -1
  225. package/dist/sandbox/egress-firewall-config.d.ts +0 -127
  226. package/dist/sandbox/egress-firewall-config.js +0 -434
  227. package/dist/sandbox/egress-firewall-config.js.map +0 -1
  228. package/dist/sandbox/images.d.ts +0 -23
  229. package/dist/sandbox/images.js +0 -55
  230. package/dist/sandbox/images.js.map +0 -1
  231. package/dist/sandbox/index.d.ts +0 -112
  232. package/dist/sandbox/index.js +0 -332
  233. package/dist/sandbox/index.js.map +0 -1
  234. package/dist/sandbox/sandbox.d.ts +0 -214
  235. package/dist/sandbox/sandbox.js +0 -453
  236. package/dist/sandbox/sandbox.js.map +0 -1
  237. package/dist/sandbox/smol.d.ts +0 -162
  238. package/dist/sandbox/smol.js +0 -485
  239. package/dist/sandbox/smol.js.map +0 -1
  240. package/dist/session-log.d.ts +0 -63
  241. package/dist/session-log.js +0 -290
  242. package/dist/session-log.js.map +0 -1
  243. package/dist/state/approval-store.d.ts +0 -88
  244. package/dist/state/approval-store.js +0 -149
  245. package/dist/state/approval-store.js.map +0 -1
  246. package/dist/state/build-assets.d.ts +0 -50
  247. package/dist/state/build-assets.js +0 -154
  248. package/dist/state/build-assets.js.map +0 -1
  249. package/dist/state/db.d.ts +0 -81
  250. package/dist/state/db.js +0 -155
  251. package/dist/state/db.js.map +0 -1
  252. package/dist/state/execution-store.d.ts +0 -257
  253. package/dist/state/execution-store.js +0 -527
  254. package/dist/state/execution-store.js.map +0 -1
  255. package/dist/state/migrate.d.ts +0 -15
  256. package/dist/state/migrate.js +0 -175
  257. package/dist/state/migrate.js.map +0 -1
  258. package/dist/state/workflow-run-store.d.ts +0 -216
  259. package/dist/state/workflow-run-store.js +0 -403
  260. package/dist/state/workflow-run-store.js.map +0 -1
  261. package/dist/telemetry/index.d.ts +0 -38
  262. package/dist/telemetry/index.js +0 -253
  263. package/dist/telemetry/index.js.map +0 -1
  264. package/dist/telemetry/pi-events.d.ts +0 -12
  265. package/dist/telemetry/pi-events.js +0 -134
  266. package/dist/telemetry/pi-events.js.map +0 -1
  267. package/dist/workflows/dag.d.ts +0 -37
  268. package/dist/workflows/dag.js +0 -118
  269. package/dist/workflows/dag.js.map +0 -1
  270. package/dist/workflows/loader.d.ts +0 -53
  271. package/dist/workflows/loader.js +0 -421
  272. package/dist/workflows/loader.js.map +0 -1
  273. package/dist/workflows/loop-eval.d.ts +0 -28
  274. package/dist/workflows/loop-eval.js +0 -74
  275. package/dist/workflows/loop-eval.js.map +0 -1
  276. package/dist/workflows/phase-executor.d.ts +0 -222
  277. package/dist/workflows/phase-executor.js +0 -1153
  278. package/dist/workflows/phase-executor.js.map +0 -1
  279. package/dist/workflows/phase-ref.d.ts +0 -50
  280. package/dist/workflows/phase-ref.js +0 -91
  281. package/dist/workflows/phase-ref.js.map +0 -1
  282. package/dist/workflows/resume.d.ts +0 -58
  283. package/dist/workflows/resume.js +0 -313
  284. package/dist/workflows/resume.js.map +0 -1
  285. package/dist/workflows/runner.d.ts +0 -63
  286. package/dist/workflows/runner.js +0 -381
  287. package/dist/workflows/runner.js.map +0 -1
  288. package/dist/workflows/schema.d.ts +0 -344
  289. package/dist/workflows/schema.js +0 -460
  290. package/dist/workflows/schema.js.map +0 -1
  291. package/dist/workflows/simple.d.ts +0 -90
  292. package/dist/workflows/simple.js +0 -393
  293. package/dist/workflows/simple.js.map +0 -1
  294. package/dist/workflows/target-policy.d.ts +0 -74
  295. package/dist/workflows/target-policy.js +0 -80
  296. package/dist/workflows/target-policy.js.map +0 -1
  297. package/dist/workflows/templates.d.ts +0 -88
  298. package/dist/workflows/templates.js +0 -127
  299. package/dist/workflows/templates.js.map +0 -1
  300. package/dist/workflows/triggers.d.ts +0 -21
  301. package/dist/workflows/triggers.js +0 -45
  302. package/dist/workflows/triggers.js.map +0 -1
  303. package/dist/workflows/verdict.d.ts +0 -19
  304. package/dist/workflows/verdict.js +0 -30
  305. package/dist/workflows/verdict.js.map +0 -1
  306. package/dist/worktree/manager.d.ts +0 -43
  307. package/dist/worktree/manager.js +0 -127
  308. package/dist/worktree/manager.js.map +0 -1
  309. package/docker-compose.yml +0 -274
  310. package/sandbox.Dockerfile +0 -68
  311. package/skills/README.md +0 -42
  312. package/skills/browser-qa/SKILL.md +0 -208
  313. package/skills/browser-qa/scripts/agent-browser.mjs +0 -531
  314. package/skills/building/SKILL.md +0 -95
  315. package/skills/chat/SKILL.md +0 -36
  316. package/skills/code-review/SKILL.md +0 -82
  317. package/skills/debug-production/SKILL.md +0 -108
  318. package/skills/demo/SKILL.md +0 -228
  319. package/skills/demo/scripts/compose-demo.sh +0 -243
  320. package/skills/issue-answer/SKILL.md +0 -91
  321. package/skills/issue-comment/SKILL.md +0 -49
  322. package/skills/issue-triage/SKILL.md +0 -149
  323. package/skills/issue-triage/references/AGENT-BRIEF.md +0 -65
  324. package/skills/pr-comment/SKILL.md +0 -64
  325. package/skills/pr-review/SKILL.md +0 -148
  326. package/skills/pr-review/references/findings-schema.md +0 -89
  327. package/skills/qa-test/SKILL.md +0 -97
  328. package/skills/repo-health/SKILL.md +0 -67
  329. package/skills/security-feedback/SKILL.md +0 -173
  330. package/skills/security-feedback/references/templates.md +0 -77
  331. package/skills/security-review/SKILL.md +0 -215
  332. package/skills/security-review/references/issue-format.md +0 -302
  333. package/skills/verify/SKILL.md +0 -118
  334. package/workflows/answer.yaml +0 -44
  335. package/workflows/build.yaml +0 -129
  336. package/workflows/cron-health.yaml +0 -6
  337. package/workflows/cron-review.yaml +0 -8
  338. package/workflows/cron-security.yaml +0 -6
  339. package/workflows/cron-triage.yaml +0 -8
  340. package/workflows/demo.yaml +0 -62
  341. package/workflows/examples/bash-smoke.yaml +0 -41
  342. package/workflows/examples/parallel-review.yaml +0 -36
  343. package/workflows/examples/tdd-loop.yaml +0 -17
  344. package/workflows/explore.yaml +0 -80
  345. package/workflows/issue-comment.yaml +0 -15
  346. package/workflows/issue-triage.yaml +0 -22
  347. package/workflows/pr-comment.yaml +0 -16
  348. package/workflows/pr-fix.yaml +0 -17
  349. package/workflows/pr-review.yaml +0 -50
  350. package/workflows/prompts/answer.md +0 -68
  351. package/workflows/prompts/architect.md +0 -44
  352. package/workflows/prompts/classifier.md +0 -60
  353. package/workflows/prompts/classify-adds-info.md +0 -17
  354. package/workflows/prompts/demo.md +0 -125
  355. package/workflows/prompts/executor.md +0 -42
  356. package/workflows/prompts/explore-ask.md +0 -99
  357. package/workflows/prompts/explore-publish.md +0 -31
  358. package/workflows/prompts/explore-read.md +0 -120
  359. package/workflows/prompts/explore-synthesize.md +0 -88
  360. package/workflows/prompts/fix.md +0 -18
  361. package/workflows/prompts/guardrails.md +0 -96
  362. package/workflows/prompts/pr-fix.md +0 -26
  363. package/workflows/prompts/pr.md +0 -39
  364. package/workflows/prompts/qa-browser.md +0 -95
  365. package/workflows/prompts/qa-synth.md +0 -44
  366. package/workflows/prompts/qa-test.md +0 -60
  367. package/workflows/prompts/re-reviewer.md +0 -33
  368. package/workflows/prompts/reviewer.md +0 -60
  369. package/workflows/prompts/verify-browser.md +0 -93
  370. package/workflows/prompts/verify-synth.md +0 -44
  371. package/workflows/prompts/verify.md +0 -63
  372. package/workflows/qa-test.yaml +0 -94
  373. package/workflows/repo-health.yaml +0 -15
  374. package/workflows/security-feedback.yaml +0 -25
  375. package/workflows/security-review.yaml +0 -37
  376. package/workflows/verify.yaml +0 -93
  377. /package/dist/{cli/cli-config.d.ts → cli-config.d.ts} +0 -0
  378. /package/dist/{cli/cli-config.js → cli-config.js} +0 -0
  379. /package/dist/{cli/cli-format.d.ts → cli-format.d.ts} +0 -0
  380. /package/dist/{cli/cli-format.js → cli-format.js} +0 -0
  381. /package/dist/{cli/cli-timeline.d.ts → cli-timeline.d.ts} +0 -0
  382. /package/dist/{cli/cli-timeline.js → cli-timeline.js} +0 -0
  383. /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
  384. /package/dist/{cli/oauth-cli.d.ts → oauth-cli.d.ts} +0 -0
  385. /package/dist/{cli/setup.d.ts → setup.d.ts} +0 -0
@@ -1,680 +0,0 @@
1
- import { classifyComment, classifyCommentAddsInfo, classifyIssueIntent, WELL_KNOWN_INTENTS } from "./screen/classifier.js";
2
- import { screenForInjection, flagPrefix } from "./screen/screen.js";
3
- import { getManagedRepos, isManagedRepo } from "../managed-repos.js";
4
- import { getWorkflowByIntent } from "../workflows/loader.js";
5
- import { getRoutes, getBotName } from "../config/config.js";
6
- /**
7
- * Resolve a classifier intent the router has no bespoke branch for to the
8
- * workflow that claims it via its `classification.intent` (issue #164). Returns
9
- * undefined for well-known intents (they keep their explicit, context-dependent
10
- * routing) and for unclaimed tokens — so this fires only for a genuinely new
11
- * intent an overlay workflow introduced, routing it to that workflow.
12
- */
13
- function fallbackWorkflowForIntent(intent) {
14
- if (WELL_KNOWN_INTENTS.has(intent))
15
- return undefined;
16
- return getWorkflowByIntent(intent)?.name;
17
- }
18
- /** Friendly reply when a Slack/CLI command targets an unmanaged repo. */
19
- function unmanagedRepoReply(repo) {
20
- return (`❌ I'm not configured to work on \`${repo}\`.\n` +
21
- `Managed repos: ${getManagedRepos().map((r) => `\`${r}\``).join(", ")}.\n` +
22
- `Ask cliftonc to add it.`);
23
- }
24
- /**
25
- * Managed-repo gate shared by every Slack command that targets a repo.
26
- * Returns `{ ok: true, repo }` when the repo is present and managed, or
27
- * `{ ok: false, route }` carrying the reply Route to short-circuit with —
28
- * a missing-repo prompt or the unmanaged-repo reply. Collapses the guard
29
- * that was copy-pasted across triage/review/security/explore.
30
- */
31
- function requireManagedRepo(repo, missingReply) {
32
- if (!repo)
33
- return { ok: false, route: { action: "reply", message: missingReply } };
34
- if (!isManagedRepo(repo)) {
35
- return { ok: false, route: { action: "reply", message: unmanagedRepoReply(repo) } };
36
- }
37
- return { ok: true, repo };
38
- }
39
- /** Author associations that can trigger builds via @mention */
40
- const MAINTAINER_ROLES = new Set(["OWNER", "MEMBER", "COLLABORATOR"]);
41
- /** Escape a string for safe interpolation into a RegExp. */
42
- function escapeRegExp(s) {
43
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44
- }
45
- /**
46
- * Bot-mention matchers derived from the configured bot handle (`getBotName()`,
47
- * e.g. `last-light` / `nearform-lastlight`). Only the configured handle
48
- * matches — there is no legacy `@last-light` fallback (the default handle is
49
- * already `last-light`, so existing deployments are unaffected).
50
- * `command` builds `@<handle> <cmd>...` matchers for the structured commands.
51
- */
52
- function botMatchers(handle) {
53
- const h = escapeRegExp(handle);
54
- return {
55
- /** Bare mention gate — case-insensitive. */
56
- mention: new RegExp(`@${h}\\b`, "i"),
57
- command: (pattern) => new RegExp(`@${h}\\s+${pattern}`, "i"),
58
- };
59
- }
60
- /**
61
- * Event routing — deterministic for most events, LLM-classified for comments.
62
- * Maps normalized events to the handler that should process them. Returns a
63
- * decision only; the dispatcher performs the side effects.
64
- */
65
- export async function routeEvent(envelope, deps = {}) {
66
- const routes = getRoutes();
67
- const gh = routes.github;
68
- const slack = routes.slack;
69
- const bot = botMatchers(getBotName());
70
- switch (envelope.type) {
71
- case "issue.opened": {
72
- // Pure question issues ("how does X work?", "X vs Y?") want an ANSWER,
73
- // not a code change — route them to the dedicated answer workflow (web
74
- // search + its own model) instead of triage, which would otherwise file
75
- // a question as an enhancement and write an agent brief. Classified with
76
- // the same cheap model as comments; WORK is the safe default.
77
- const isQuestion = await classifyIssueIntent(envelope.title || "", envelope.body || "");
78
- console.log(`[router] New issue ${envelope.repo}#${envelope.issueNumber} classified as: ${isQuestion ? "question" : "work"}`);
79
- return {
80
- action: "handler",
81
- handler: isQuestion
82
- ? gh.issue_answer || "answer"
83
- : gh.issue_opened || "issue-triage",
84
- context: {
85
- repo: envelope.repo,
86
- issueNumber: envelope.issueNumber,
87
- title: envelope.title,
88
- body: envelope.body,
89
- sender: envelope.sender,
90
- labels: envelope.labels,
91
- },
92
- };
93
- }
94
- case "issue.reopened":
95
- return {
96
- action: "handler",
97
- handler: gh.issue_reopened || "issue-triage",
98
- context: {
99
- repo: envelope.repo,
100
- issueNumber: envelope.issueNumber,
101
- title: envelope.title,
102
- body: envelope.body,
103
- sender: envelope.sender,
104
- reopened: true,
105
- },
106
- };
107
- case "pr.opened":
108
- case "pr.synchronize":
109
- case "pr.reopened":
110
- // All three deserve a fresh review on the current head SHA. The
111
- // pr-review skill's "skip if already reviewed this SHA" guard covers
112
- // the no-op case (e.g. synchronize triggered by a non-code change
113
- // when we already reviewed the resulting SHA), so a stable handler
114
- // for every PR-attention event is correct.
115
- return {
116
- action: "handler",
117
- handler: gh[`pr_${envelope.type.split(".")[1]}`] || "pr-review",
118
- context: {
119
- _routeKey: `github.pr_${envelope.type.split(".")[1]}`,
120
- repo: envelope.repo,
121
- prNumber: envelope.prNumber,
122
- title: envelope.title,
123
- body: envelope.body,
124
- sender: envelope.sender,
125
- labels: envelope.labels,
126
- },
127
- };
128
- case "comment.created": {
129
- // Reply-gate short-circuit: if a paused socratic explore run is
130
- // waiting for any free-form message on this issue, feed the comment
131
- // body through without requiring an @mention or maintainer check.
132
- // Must sit ABOVE both the mention and role checks so plain replies
133
- // resume the conversation naturally.
134
- if (deps.db && envelope.issueNumber) {
135
- const triggerId = `${envelope.repo}#${envelope.issueNumber}`;
136
- const pendingReply = deps.db.approvals.getPendingReplyGateByTrigger(triggerId);
137
- if (pendingReply) {
138
- return {
139
- action: "handler",
140
- handler: gh.explore_reply || "explore-reply",
141
- context: {
142
- repo: envelope.repo,
143
- issueNumber: envelope.issueNumber,
144
- sender: envelope.sender,
145
- reply: envelope.body,
146
- workflowRunId: pendingReply.workflowRunId,
147
- },
148
- };
149
- }
150
- }
151
- // Reporter-driven re-triage (pre-build only). A plain (non-@mention)
152
- // comment on an ISSUE can re-open triage so new information re-classifies
153
- // it — but only before the issue has entered a build, and only from
154
- // people whose comment is meaningful triage input. Sits above the
155
- // mention/maintainer gates because reporters answering won't @-mention
156
- // and usually aren't maintainers. Bot comments are filtered at the
157
- // connector, so this can't self-loop.
158
- if (deps.db &&
159
- envelope.issueNumber &&
160
- !envelope.prNumber &&
161
- !bot.mention.test(envelope.body)) {
162
- const triggerId = `${envelope.repo}#${envelope.issueNumber}`;
163
- const buildStarted = deps.db.runs.hasRunForTrigger(triggerId, "build");
164
- if (!buildStarted) {
165
- const isAuthor = !!envelope.issueAuthor && envelope.sender === envelope.issueAuthor;
166
- const isMaintainer = MAINTAINER_ROLES.has(envelope.authorAssociation || "");
167
- const hasNeedsInfo = (envelope.labels || []).includes("needs-info");
168
- let retriage = false;
169
- if (hasNeedsInfo && (isAuthor || isMaintainer)) {
170
- // Answering a needs-info request — any OP/maintainer reply re-triages.
171
- retriage = true;
172
- }
173
- else if (isAuthor) {
174
- // Any other pre-build state: re-triage only when the reporter adds
175
- // substantive information (not "thanks"/acknowledgement).
176
- retriage = await classifyCommentAddsInfo(envelope.body, {
177
- issueTitle: envelope.title,
178
- });
179
- }
180
- if (retriage) {
181
- console.log(`[router] Re-triaging ${triggerId} from reporter/maintainer comment`);
182
- return {
183
- action: "handler",
184
- handler: gh.issue_opened || "issue-triage",
185
- context: {
186
- repo: envelope.repo,
187
- issueNumber: envelope.issueNumber,
188
- title: envelope.title,
189
- sender: envelope.sender,
190
- commentBody: envelope.body,
191
- mode: "retriage",
192
- },
193
- };
194
- }
195
- }
196
- }
197
- // Only act on mentions of the configured bot handle
198
- if (!bot.mention.test(envelope.body)) {
199
- return { action: "ignore", reason: "no bot mention in comment" };
200
- }
201
- // Only maintainers (OWNER, MEMBER, COLLABORATOR) can trigger builds.
202
- // For non-maintainers we reply directly via the connector — no agent
203
- // invocation needed.
204
- if (!MAINTAINER_ROLES.has(envelope.authorAssociation || "")) {
205
- return {
206
- action: "reply",
207
- message: `Thanks for the report, @${envelope.sender}! ` +
208
- `I only act on requests from repository maintainers — a maintainer ` +
209
- `(owner / member / collaborator) needs to mention me to trigger a build.`,
210
- };
211
- }
212
- // Check for approval commands before LLM classification
213
- const approveMatch = envelope.body.match(bot.command("approve\\b"));
214
- const rejectMatch = envelope.body.match(bot.command("reject\\b(.*)"));
215
- if (approveMatch || rejectMatch) {
216
- return {
217
- action: "handler",
218
- handler: gh.approval_response || "approval-response",
219
- context: {
220
- repo: envelope.repo,
221
- issueNumber: envelope.issueNumber,
222
- sender: envelope.sender,
223
- decision: approveMatch ? "approved" : "rejected",
224
- reason: rejectMatch ? rejectMatch[1].trim() || undefined : undefined,
225
- },
226
- };
227
- }
228
- // Structured match for security-review before LLM classification
229
- const securityMatch = envelope.body.match(bot.command("security-review\\b"));
230
- if (securityMatch) {
231
- return {
232
- action: "handler",
233
- handler: gh.security_review || "security-review",
234
- context: { repo: envelope.repo, sender: envelope.sender, source: envelope.source },
235
- };
236
- }
237
- // Structured matches for verify / qa-test before LLM classification.
238
- // Everything after the command word is the claim (verify) or the
239
- // target/steps (qa-test); it flows through as `commentBody`. Both work on
240
- // issues and PRs. Maintainer-gated above, like security-review.
241
- const verifyMatch = envelope.body.match(bot.command("verify\\b([\\s\\S]*)"));
242
- if (verifyMatch) {
243
- return {
244
- action: "handler",
245
- handler: gh.verify || "verify",
246
- context: {
247
- repo: envelope.repo,
248
- issueNumber: envelope.issueNumber,
249
- ...(envelope.prNumber ? { prNumber: envelope.prNumber } : {}),
250
- title: envelope.title,
251
- sender: envelope.sender,
252
- commentBody: verifyMatch[1].trim() || envelope.body,
253
- },
254
- };
255
- }
256
- const qaTestMatch = envelope.body.match(bot.command("qa-test\\b([\\s\\S]*)"));
257
- if (qaTestMatch) {
258
- return {
259
- action: "handler",
260
- handler: gh.qa_test || "qa-test",
261
- context: {
262
- repo: envelope.repo,
263
- issueNumber: envelope.issueNumber,
264
- ...(envelope.prNumber ? { prNumber: envelope.prNumber } : {}),
265
- title: envelope.title,
266
- sender: envelope.sender,
267
- commentBody: qaTestMatch[1].trim() || envelope.body,
268
- },
269
- };
270
- }
271
- // `@<bot> demo [notes]` — record a demo video of the PR/feature.
272
- // Anything after the command word flows through as `commentBody` (demo
273
- // scope/notes). Gated to the docker QA image at the workflow level; on a
274
- // host without it the demo phase silently skips.
275
- const demoMatch = envelope.body.match(bot.command("demo\\b([\\s\\S]*)"));
276
- if (demoMatch) {
277
- return {
278
- action: "handler",
279
- handler: gh.demo || "demo",
280
- context: {
281
- repo: envelope.repo,
282
- issueNumber: envelope.issueNumber,
283
- ...(envelope.prNumber ? { prNumber: envelope.prNumber } : {}),
284
- title: envelope.title,
285
- sender: envelope.sender,
286
- commentBody: demoMatch[1].trim() || envelope.body,
287
- },
288
- };
289
- }
290
- // Classify intent + screen for injection in parallel. Both run on the
291
- // same comment text and have similar latency (single haiku call); doing
292
- // them in parallel keeps overall router latency at max(classifier, screener)
293
- // rather than their sum.
294
- const [{ intent }, screen] = await Promise.all([
295
- classifyComment(envelope.body, {
296
- issueTitle: envelope.title,
297
- isPullRequest: !!envelope.prNumber,
298
- }),
299
- screenForInjection(envelope.body),
300
- ]);
301
- console.log(`[router] Comment classified as: ${intent}` +
302
- (screen.flagged ? ` [screener flagged: ${screen.reason || "no reason"}]` : ""));
303
- // When the screener flags, prefix the commentBody with a one-line
304
- // warning. Downstream agents anchored by agent-context/security.md
305
- // treat flagged content skeptically. Never refuse — false positives
306
- // shouldn't break legitimate comments.
307
- const commentBody = screen.flagged
308
- ? `${flagPrefix(screen.reason)}${envelope.body}`
309
- : envelope.body;
310
- if (envelope.prNumber) {
311
- // PR comments:
312
- // build → pr-fix (full Architect→Executor→Reviewer fix loop)
313
- // review → pr-review (a real formal review with inline comments —
314
- // "can you review this?" should trigger an actual review,
315
- // not a one-off Q&A answer)
316
- // verify → verify (test a behavioural claim against the PR)
317
- // qa-test → qa-test (drive a flow against the PR, step pass/fail)
318
- // else → pr-comment (diff-aware Q&A; the issue-comment skill
319
- // caps at 2 file reads which isn't enough to answer
320
- // "does this PR consider X?" with code-cited evidence)
321
- // Explore isn't meaningful on PRs since the code already exists.
322
- const prNovelWf = fallbackWorkflowForIntent(intent);
323
- const { handler: prHandler, routeKey: prRouteKey } = intent === "build" ? { handler: gh.pr_fix || "pr-fix", routeKey: "github.pr_fix" }
324
- : intent === "review" ? { handler: gh.pr_review || "pr-review", routeKey: "github.pr_review" }
325
- : intent === "verify" ? { handler: gh.verify || "verify", routeKey: "github.verify" }
326
- : intent === "qa-test" ? { handler: gh.qa_test || "qa-test", routeKey: "github.qa_test" }
327
- : intent === "demo" ? { handler: gh.demo || "demo", routeKey: "github.demo" }
328
- : prNovelWf ? { handler: prNovelWf, routeKey: `intent.${intent}` }
329
- : { handler: gh.pr_comment || "pr-comment", routeKey: "github.pr_comment" };
330
- return {
331
- action: "handler",
332
- handler: prHandler,
333
- context: {
334
- _routeKey: prRouteKey,
335
- repo: envelope.repo,
336
- prNumber: envelope.prNumber,
337
- issueNumber: envelope.issueNumber,
338
- title: envelope.title,
339
- body: envelope.body,
340
- sender: envelope.sender,
341
- commentBody,
342
- },
343
- };
344
- }
345
- // Issue comments: build → full build cycle, explore → socratic
346
- // explore workflow, security scan summary issues → security-feedback,
347
- // otherwise → issue-comment.
348
- //
349
- // Key on `security-scan` (not just `security`) so we only divert to
350
- // security-feedback on the per-run SUMMARY issue. Broken-out sub-issues
351
- // carry `["security", severity]` (no `security-scan`) and must stay on
352
- // the normal build/issue-comment path — "@<bot> build this fix"
353
- // on a sub-issue needs the real build cycle, not security-feedback.
354
- //
355
- // ALL comment intents on a summary issue funnel to security-feedback
356
- // — including BUILD ("create issues for the highs" looks like build to
357
- // the classifier but is really a break-out request). Approve/reject
358
- // regex matches already returned above, so they don't reach here.
359
- const hasScanSummaryLabel = (envelope.labels || []).includes("security-scan");
360
- if (hasScanSummaryLabel) {
361
- return {
362
- action: "handler",
363
- handler: gh.security_feedback || "security-feedback",
364
- context: {
365
- repo: envelope.repo,
366
- issueNumber: envelope.issueNumber,
367
- title: envelope.title,
368
- body: envelope.body,
369
- sender: envelope.sender,
370
- commentBody,
371
- },
372
- };
373
- }
374
- const issueNovelWf = fallbackWorkflowForIntent(intent);
375
- const { handler: issueSkill, routeKey: issueRouteKey } = intent === "build" ? { handler: gh.issue_build || "github-orchestrator", routeKey: "github.issue_build" }
376
- : intent === "explore" ? { handler: gh.issue_explore || "explore", routeKey: "github.issue_explore" }
377
- : intent === "verify" ? { handler: gh.verify || "verify", routeKey: "github.verify" }
378
- : intent === "qa-test" ? { handler: gh.qa_test || "qa-test", routeKey: "github.qa_test" }
379
- : intent === "demo" ? { handler: gh.demo || "demo", routeKey: "github.demo" }
380
- : issueNovelWf ? { handler: issueNovelWf, routeKey: `intent.${intent}` }
381
- : { handler: gh.issue_comment || "issue-comment", routeKey: "github.issue_comment" };
382
- return {
383
- action: "handler",
384
- handler: issueSkill,
385
- context: {
386
- _routeKey: issueRouteKey,
387
- repo: envelope.repo,
388
- issueNumber: envelope.issueNumber,
389
- title: envelope.title,
390
- body: envelope.body,
391
- sender: envelope.sender,
392
- commentBody,
393
- },
394
- };
395
- }
396
- case "pr_review.submitted":
397
- case "pr_review_comment.created":
398
- return { action: "ignore", reason: "PR review events not yet handled" };
399
- case "message": {
400
- const text = envelope.body.trim();
401
- const raw = envelope.raw;
402
- const channelId = raw?.channelId;
403
- const threadId = raw?.threadId;
404
- const teamId = raw?.team || raw?.team_id || "slack";
405
- const slackTriggerId = channelId && threadId
406
- ? `slack:${teamId}:${channelId}:${threadId}`
407
- : undefined;
408
- // Reply-gate short-circuit: if a paused socratic explore run is
409
- // waiting on this Slack thread, feed the message body through as
410
- // the next reply — this must sit above all slash-command handling
411
- // so replies don't get mis-parsed as commands.
412
- if (deps.db && slackTriggerId) {
413
- const pendingReply = deps.db.approvals.getPendingReplyGateByTrigger(slackTriggerId);
414
- if (pendingReply) {
415
- return {
416
- action: "handler",
417
- handler: slack.explore_reply || "explore-reply",
418
- context: {
419
- sender: envelope.sender,
420
- reply: text,
421
- workflowRunId: pendingReply.workflowRunId,
422
- source: envelope.source,
423
- triggerId: slackTriggerId,
424
- channelId,
425
- threadId,
426
- },
427
- };
428
- }
429
- }
430
- // Classify all Slack messages via the LLM classifier — no regex
431
- // commands. The classifier extracts intent, repo, issue number, and
432
- // reject reason from natural language. Screen for injection in parallel
433
- // (Slack messages are user-supplied text and reach the chat skill or a
434
- // workflow, both of which need the flag annotation).
435
- const [classification, screen] = await Promise.all([
436
- classifyComment(text),
437
- screenForInjection(text),
438
- ]);
439
- const { intent, repo: classifiedRepo, issueNumber: classifiedIssue, reason: classifiedReason, } = classification;
440
- console.log(`[router] Slack message classified as: ${intent}` +
441
- `${classifiedRepo ? ` (repo: ${classifiedRepo})` : ""}` +
442
- `${classifiedIssue ? ` (#${classifiedIssue})` : ""}` +
443
- (screen.flagged ? ` [screener flagged: ${screen.reason || "no reason"}]` : ""));
444
- const slackText = screen.flagged ? `${flagPrefix(screen.reason)}${text}` : text;
445
- switch (intent) {
446
- case "reset":
447
- return {
448
- action: "handler",
449
- handler: slack.reset || "chat-reset",
450
- context: { sessionId: raw?.sessionId, sender: envelope.sender, source: envelope.source },
451
- };
452
- case "status":
453
- return {
454
- action: "handler",
455
- handler: slack.status || "status-report",
456
- context: { sender: envelope.sender, source: envelope.source },
457
- };
458
- case "approve":
459
- return {
460
- action: "handler",
461
- handler: slack.approve || "approval-response",
462
- context: { sender: envelope.sender, decision: "approved", source: envelope.source },
463
- };
464
- case "reject":
465
- return {
466
- action: "handler",
467
- handler: slack.reject || "approval-response",
468
- context: {
469
- sender: envelope.sender,
470
- decision: "rejected",
471
- reason: classifiedReason,
472
- source: envelope.source,
473
- },
474
- };
475
- case "build": {
476
- // No repo + no issue context → classifier likely over-fired on
477
- // an imperative verb ("delete files in X", "clean up my docs").
478
- // Fall through to chat rather than nag the user for a repo.
479
- if (!classifiedRepo) {
480
- return {
481
- action: "handler",
482
- handler: slack.chat || "chat",
483
- context: {
484
- sessionId: raw?.sessionId,
485
- message: slackText,
486
- sender: envelope.sender,
487
- source: envelope.source,
488
- },
489
- };
490
- }
491
- if (!isManagedRepo(classifiedRepo)) {
492
- return { action: "reply", message: unmanagedRepoReply(classifiedRepo) };
493
- }
494
- return {
495
- action: "handler",
496
- handler: slack.build || "github-orchestrator",
497
- context: {
498
- _routeKey: "slack.build",
499
- repo: classifiedRepo,
500
- issueNumber: classifiedIssue,
501
- sender: envelope.sender,
502
- commentBody: slackText,
503
- source: envelope.source,
504
- },
505
- };
506
- }
507
- case "triage": {
508
- const gate = requireManagedRepo(classifiedRepo, "Which repo should I triage? e.g. `triage cliftonc/repo`");
509
- if (!gate.ok)
510
- return gate.route;
511
- return {
512
- action: "handler",
513
- handler: slack.triage || "issue-triage",
514
- context: { repo: gate.repo, sender: envelope.sender, source: envelope.source },
515
- };
516
- }
517
- case "review": {
518
- const gate = requireManagedRepo(classifiedRepo, "Which repo should I review PRs for? e.g. `review cliftonc/repo`");
519
- if (!gate.ok)
520
- return gate.route;
521
- return {
522
- action: "handler",
523
- handler: slack.review || "pr-review",
524
- context: {
525
- repo: gate.repo,
526
- prNumber: classifiedIssue,
527
- issueNumber: classifiedIssue,
528
- sender: envelope.sender,
529
- source: envelope.source,
530
- },
531
- };
532
- }
533
- case "security": {
534
- const gate = requireManagedRepo(classifiedRepo, "Which repo should I scan? e.g. `security review cliftonc/repo`");
535
- if (!gate.ok)
536
- return gate.route;
537
- return {
538
- action: "handler",
539
- handler: slack.security || "security-review",
540
- context: { repo: gate.repo, sender: envelope.sender, source: envelope.source },
541
- };
542
- }
543
- case "verify": {
544
- const gate = requireManagedRepo(classifiedRepo, "Which repo should I verify? e.g. `verify cliftonc/repo#42 — the fork flag creates a new session`");
545
- if (!gate.ok)
546
- return gate.route;
547
- return {
548
- action: "handler",
549
- handler: slack.verify || "verify",
550
- context: {
551
- repo: gate.repo,
552
- issueNumber: classifiedIssue,
553
- sender: envelope.sender,
554
- commentBody: slackText,
555
- source: envelope.source,
556
- triggerId: slackTriggerId,
557
- channelId,
558
- threadId,
559
- },
560
- };
561
- }
562
- case "qa-test": {
563
- const gate = requireManagedRepo(classifiedRepo, "Which repo should I QA-test? e.g. `qa-test cliftonc/repo#42 -- login, create a project`");
564
- if (!gate.ok)
565
- return gate.route;
566
- return {
567
- action: "handler",
568
- handler: slack.qa_test || "qa-test",
569
- context: {
570
- repo: gate.repo,
571
- issueNumber: classifiedIssue,
572
- sender: envelope.sender,
573
- commentBody: slackText,
574
- source: envelope.source,
575
- triggerId: slackTriggerId,
576
- channelId,
577
- threadId,
578
- },
579
- };
580
- }
581
- case "question": {
582
- // A substantive question targeting a managed repo → run the sandboxed
583
- // answer workflow (web search + repo docs), delivered back to this
584
- // thread. A repo-less question can't seed a sandbox workspace, so it
585
- // falls through to in-process chat for a quick answer (mirrors build).
586
- if (!classifiedRepo) {
587
- return {
588
- action: "handler",
589
- handler: slack.chat || "chat",
590
- context: {
591
- sessionId: raw?.sessionId,
592
- message: slackText,
593
- sender: envelope.sender,
594
- source: envelope.source,
595
- },
596
- };
597
- }
598
- if (!isManagedRepo(classifiedRepo)) {
599
- return { action: "reply", message: unmanagedRepoReply(classifiedRepo) };
600
- }
601
- return {
602
- action: "handler",
603
- handler: slack.answer || "answer",
604
- context: {
605
- repo: classifiedRepo,
606
- issueNumber: classifiedIssue,
607
- sender: envelope.sender,
608
- commentBody: slackText,
609
- source: envelope.source,
610
- triggerId: slackTriggerId,
611
- channelId,
612
- threadId,
613
- },
614
- };
615
- }
616
- case "explore": {
617
- const gate = requireManagedRepo(classifiedRepo, "I'd love to help explore that idea, but I need to know which repo to work against. " +
618
- "Could you restate your request and include the repo? For example: " +
619
- '"let\'s explore adding webhooks to cliftonc/lastlight"');
620
- if (!gate.ok)
621
- return gate.route;
622
- return {
623
- action: "handler",
624
- handler: slack.explore || "explore",
625
- context: {
626
- repo: gate.repo,
627
- issueNumber: classifiedIssue,
628
- sender: envelope.sender,
629
- commentBody: slackText,
630
- source: envelope.source,
631
- triggerId: slackTriggerId,
632
- channelId,
633
- threadId,
634
- },
635
- };
636
- }
637
- default: {
638
- // A novel intent an overlay workflow introduced (issue #164) → route
639
- // to that workflow. If it named an unmanaged repo, reject on the same
640
- // security boundary the built-in repo-scoped intents use.
641
- const novelWf = fallbackWorkflowForIntent(intent);
642
- if (novelWf) {
643
- if (classifiedRepo && !isManagedRepo(classifiedRepo)) {
644
- return { action: "reply", message: unmanagedRepoReply(classifiedRepo) };
645
- }
646
- return {
647
- action: "handler",
648
- handler: novelWf,
649
- context: {
650
- _routeKey: `intent.${intent}`,
651
- repo: classifiedRepo,
652
- issueNumber: classifiedIssue,
653
- sender: envelope.sender,
654
- commentBody: slackText,
655
- source: envelope.source,
656
- triggerId: slackTriggerId,
657
- channelId,
658
- threadId,
659
- },
660
- };
661
- }
662
- // chat — conversational reply
663
- return {
664
- action: "handler",
665
- handler: slack.chat || "chat",
666
- context: {
667
- sessionId: raw?.sessionId,
668
- message: slackText,
669
- sender: envelope.sender,
670
- source: envelope.source,
671
- },
672
- };
673
- }
674
- }
675
- }
676
- default:
677
- return { action: "ignore", reason: `unhandled event type: ${envelope.type}` };
678
- }
679
- }
680
- //# sourceMappingURL=router.js.map