lastlight 0.16.0 → 0.16.2
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.
- package/README.md +71 -692
- package/dist/cli-config.js.map +1 -0
- package/dist/cli-format.js.map +1 -0
- package/dist/{cli/cli-server.d.ts → cli-server.d.ts} +39 -1
- package/dist/{cli/cli-server.js → cli-server.js} +105 -5
- package/dist/cli-server.js.map +1 -0
- package/dist/cli-timeline.js.map +1 -0
- package/dist/{cli/cli.js → cli.js} +9 -7
- package/dist/cli.js.map +1 -0
- package/dist/{cli/fork-cli.d.ts → fork-cli.d.ts} +3 -3
- package/dist/{cli/fork-cli.js → fork-cli.js} +28 -27
- package/dist/fork-cli.js.map +1 -0
- package/dist/{cli/oauth-cli.js → oauth-cli.js} +1 -1
- package/dist/oauth-cli.js.map +1 -0
- package/dist/{cli/setup.js → setup.js} +10 -4
- package/dist/setup.js.map +1 -0
- package/dist/{cli/skills-install.d.ts → skills-install.d.ts} +6 -0
- package/dist/{cli/skills-install.js → skills-install.js} +6 -2
- package/dist/skills-install.js.map +1 -0
- package/package.json +20 -65
- package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
- package/agent-context/rules.md +0 -80
- package/agent-context/security.md +0 -69
- package/agent-context/soul.md +0 -21
- package/config/default.yaml +0 -119
- package/deploy/.env.production.example +0 -100
- package/deploy/entrypoint.sh +0 -82
- package/deploy/native/README.md +0 -160
- package/deploy/native/install.sh +0 -110
- package/deploy/native/lastlight.env.example +0 -95
- package/deploy/native/lastlight.service +0 -50
- package/deploy/sandbox-entrypoint.sh +0 -93
- package/dist/admin/auth.d.ts +0 -51
- package/dist/admin/auth.js +0 -121
- package/dist/admin/auth.js.map +0 -1
- package/dist/admin/chat-session-reader.d.ts +0 -39
- package/dist/admin/chat-session-reader.js +0 -84
- package/dist/admin/chat-session-reader.js.map +0 -1
- package/dist/admin/docker.d.ts +0 -61
- package/dist/admin/docker.js +0 -187
- package/dist/admin/docker.js.map +0 -1
- package/dist/admin/index.d.ts +0 -9
- package/dist/admin/index.js +0 -59
- package/dist/admin/index.js.map +0 -1
- package/dist/admin/routes.d.ts +0 -81
- package/dist/admin/routes.js +0 -1381
- package/dist/admin/routes.js.map +0 -1
- package/dist/admin/sessions.d.ts +0 -89
- package/dist/admin/sessions.js +0 -204
- package/dist/admin/sessions.js.map +0 -1
- package/dist/admin/tail.d.ts +0 -12
- package/dist/admin/tail.js +0 -145
- package/dist/admin/tail.js.map +0 -1
- package/dist/admin/version.d.ts +0 -22
- package/dist/admin/version.js +0 -92
- package/dist/admin/version.js.map +0 -1
- package/dist/cli/cli-config.js.map +0 -1
- package/dist/cli/cli-format.js.map +0 -1
- package/dist/cli/cli-server.js.map +0 -1
- package/dist/cli/cli-timeline.js.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/fork-cli.js.map +0 -1
- package/dist/cli/oauth-cli.js.map +0 -1
- package/dist/cli/setup.js.map +0 -1
- package/dist/cli/skills-install.js.map +0 -1
- package/dist/config/config-resolve.d.ts +0 -28
- package/dist/config/config-resolve.js +0 -47
- package/dist/config/config-resolve.js.map +0 -1
- package/dist/config/config.d.ts +0 -157
- package/dist/config/config.js +0 -581
- package/dist/config/config.js.map +0 -1
- package/dist/config/core-pin.d.ts +0 -16
- package/dist/config/core-pin.js +0 -85
- package/dist/config/core-pin.js.map +0 -1
- package/dist/config/overlay-assets.d.ts +0 -20
- package/dist/config/overlay-assets.js +0 -114
- package/dist/config/overlay-assets.js.map +0 -1
- package/dist/config/overlay-bootstrap.d.ts +0 -44
- package/dist/config/overlay-bootstrap.js +0 -304
- package/dist/config/overlay-bootstrap.js.map +0 -1
- package/dist/connectors/github-webhook.d.ts +0 -45
- package/dist/connectors/github-webhook.js +0 -321
- package/dist/connectors/github-webhook.js.map +0 -1
- package/dist/connectors/index.d.ts +0 -18
- package/dist/connectors/index.js +0 -41
- package/dist/connectors/index.js.map +0 -1
- package/dist/connectors/messaging/base.d.ts +0 -42
- package/dist/connectors/messaging/base.js +0 -139
- package/dist/connectors/messaging/base.js.map +0 -1
- package/dist/connectors/messaging/delivery.d.ts +0 -14
- package/dist/connectors/messaging/delivery.js +0 -38
- package/dist/connectors/messaging/delivery.js.map +0 -1
- package/dist/connectors/messaging/index.d.ts +0 -4
- package/dist/connectors/messaging/index.js +0 -4
- package/dist/connectors/messaging/index.js.map +0 -1
- package/dist/connectors/messaging/session-manager.d.ts +0 -53
- package/dist/connectors/messaging/session-manager.js +0 -255
- package/dist/connectors/messaging/session-manager.js.map +0 -1
- package/dist/connectors/messaging/types.d.ts +0 -58
- package/dist/connectors/messaging/types.js +0 -7
- package/dist/connectors/messaging/types.js.map +0 -1
- package/dist/connectors/slack/connector.d.ts +0 -142
- package/dist/connectors/slack/connector.js +0 -494
- package/dist/connectors/slack/connector.js.map +0 -1
- package/dist/connectors/slack/index.d.ts +0 -2
- package/dist/connectors/slack/index.js +0 -3
- package/dist/connectors/slack/index.js.map +0 -1
- package/dist/connectors/slack/mrkdwn.d.ts +0 -19
- package/dist/connectors/slack/mrkdwn.js +0 -240
- package/dist/connectors/slack/mrkdwn.js.map +0 -1
- package/dist/connectors/types.d.ts +0 -61
- package/dist/connectors/types.js +0 -2
- package/dist/connectors/types.js.map +0 -1
- package/dist/cron/fanout.d.ts +0 -33
- package/dist/cron/fanout.js +0 -55
- package/dist/cron/fanout.js.map +0 -1
- package/dist/cron/jobs.d.ts +0 -18
- package/dist/cron/jobs.js +0 -37
- package/dist/cron/jobs.js.map +0 -1
- package/dist/cron/scheduler.d.ts +0 -45
- package/dist/cron/scheduler.js +0 -105
- package/dist/cron/scheduler.js.map +0 -1
- package/dist/engine/agent-executor.d.ts +0 -34
- package/dist/engine/agent-executor.js +0 -246
- package/dist/engine/agent-executor.js.map +0 -1
- package/dist/engine/chat/chat-runner.d.ts +0 -132
- package/dist/engine/chat/chat-runner.js +0 -424
- package/dist/engine/chat/chat-runner.js.map +0 -1
- package/dist/engine/chat/chat-skills.d.ts +0 -45
- package/dist/engine/chat/chat-skills.js +0 -184
- package/dist/engine/chat/chat-skills.js.map +0 -1
- package/dist/engine/chat/chat.d.ts +0 -54
- package/dist/engine/chat/chat.js +0 -316
- package/dist/engine/chat/chat.js.map +0 -1
- package/dist/engine/chat/message-batcher.d.ts +0 -32
- package/dist/engine/chat/message-batcher.js +0 -109
- package/dist/engine/chat/message-batcher.js.map +0 -1
- package/dist/engine/dispatcher.d.ts +0 -73
- package/dist/engine/dispatcher.js +0 -663
- package/dist/engine/dispatcher.js.map +0 -1
- package/dist/engine/event-shim.d.ts +0 -117
- package/dist/engine/event-shim.js +0 -435
- package/dist/engine/event-shim.js.map +0 -1
- package/dist/engine/executors/orchestrator.d.ts +0 -81
- package/dist/engine/executors/orchestrator.js +0 -365
- package/dist/engine/executors/orchestrator.js.map +0 -1
- package/dist/engine/executors/shared.d.ts +0 -189
- package/dist/engine/executors/shared.js +0 -616
- package/dist/engine/executors/shared.js.map +0 -1
- package/dist/engine/github/git-auth.d.ts +0 -62
- package/dist/engine/github/git-auth.js +0 -161
- package/dist/engine/github/git-auth.js.map +0 -1
- package/dist/engine/github/github-app-client.d.ts +0 -23
- package/dist/engine/github/github-app-client.js +0 -33
- package/dist/engine/github/github-app-client.js.map +0 -1
- package/dist/engine/github/github-tools.d.ts +0 -18
- package/dist/engine/github/github-tools.js +0 -263
- package/dist/engine/github/github-tools.js.map +0 -1
- package/dist/engine/github/github.d.ts +0 -1193
- package/dist/engine/github/github.js +0 -357
- package/dist/engine/github/github.js.map +0 -1
- package/dist/engine/github/profiles.d.ts +0 -279
- package/dist/engine/github/profiles.js +0 -42
- package/dist/engine/github/profiles.js.map +0 -1
- package/dist/engine/github/review-poster.d.ts +0 -98
- package/dist/engine/github/review-poster.js +0 -177
- package/dist/engine/github/review-poster.js.map +0 -1
- package/dist/engine/llm.d.ts +0 -79
- package/dist/engine/llm.js +0 -269
- package/dist/engine/llm.js.map +0 -1
- package/dist/engine/oauth.d.ts +0 -43
- package/dist/engine/oauth.js +0 -103
- package/dist/engine/oauth.js.map +0 -1
- package/dist/engine/router.d.ts +0 -29
- package/dist/engine/router.js +0 -680
- package/dist/engine/router.js.map +0 -1
- package/dist/engine/screen/classifier.d.ts +0 -93
- package/dist/engine/screen/classifier.js +0 -215
- package/dist/engine/screen/classifier.js.map +0 -1
- package/dist/engine/screen/screen.d.ts +0 -41
- package/dist/engine/screen/screen.js +0 -93
- package/dist/engine/screen/screen.js.map +0 -1
- package/dist/evals-api.d.ts +0 -24
- package/dist/evals-api.js +0 -22
- package/dist/evals-api.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -940
- package/dist/index.js.map +0 -1
- package/dist/managed-repos.d.ts +0 -30
- package/dist/managed-repos.js +0 -72
- package/dist/managed-repos.js.map +0 -1
- package/dist/notify/blocks.d.ts +0 -23
- package/dist/notify/blocks.js +0 -118
- package/dist/notify/blocks.js.map +0 -1
- package/dist/notify/index.d.ts +0 -17
- package/dist/notify/index.js +0 -7
- package/dist/notify/index.js.map +0 -1
- package/dist/notify/model.d.ts +0 -57
- package/dist/notify/model.js +0 -89
- package/dist/notify/model.js.map +0 -1
- package/dist/notify/notifier.d.ts +0 -18
- package/dist/notify/notifier.js +0 -100
- package/dist/notify/notifier.js.map +0 -1
- package/dist/notify/render.d.ts +0 -21
- package/dist/notify/render.js +0 -59
- package/dist/notify/render.js.map +0 -1
- package/dist/notify/transports/github.d.ts +0 -26
- package/dist/notify/transports/github.js +0 -27
- package/dist/notify/transports/github.js.map +0 -1
- package/dist/notify/transports/slack.d.ts +0 -27
- package/dist/notify/transports/slack.js +0 -37
- package/dist/notify/transports/slack.js.map +0 -1
- package/dist/notify/types.d.ts +0 -119
- package/dist/notify/types.js +0 -16
- package/dist/notify/types.js.map +0 -1
- package/dist/providers.d.ts +0 -125
- package/dist/providers.js +0 -279
- package/dist/providers.js.map +0 -1
- package/dist/sandbox/docker.d.ts +0 -180
- package/dist/sandbox/docker.js +0 -517
- package/dist/sandbox/docker.js.map +0 -1
- package/dist/sandbox/egress-allowlist.d.ts +0 -74
- package/dist/sandbox/egress-allowlist.js +0 -164
- package/dist/sandbox/egress-allowlist.js.map +0 -1
- package/dist/sandbox/egress-firewall-config.d.ts +0 -127
- package/dist/sandbox/egress-firewall-config.js +0 -434
- package/dist/sandbox/egress-firewall-config.js.map +0 -1
- package/dist/sandbox/images.d.ts +0 -23
- package/dist/sandbox/images.js +0 -55
- package/dist/sandbox/images.js.map +0 -1
- package/dist/sandbox/index.d.ts +0 -112
- package/dist/sandbox/index.js +0 -332
- package/dist/sandbox/index.js.map +0 -1
- package/dist/sandbox/sandbox.d.ts +0 -214
- package/dist/sandbox/sandbox.js +0 -453
- package/dist/sandbox/sandbox.js.map +0 -1
- package/dist/sandbox/smol.d.ts +0 -162
- package/dist/sandbox/smol.js +0 -485
- package/dist/sandbox/smol.js.map +0 -1
- package/dist/session-log.d.ts +0 -63
- package/dist/session-log.js +0 -290
- package/dist/session-log.js.map +0 -1
- package/dist/state/approval-store.d.ts +0 -88
- package/dist/state/approval-store.js +0 -149
- package/dist/state/approval-store.js.map +0 -1
- package/dist/state/build-assets.d.ts +0 -50
- package/dist/state/build-assets.js +0 -154
- package/dist/state/build-assets.js.map +0 -1
- package/dist/state/db.d.ts +0 -81
- package/dist/state/db.js +0 -155
- package/dist/state/db.js.map +0 -1
- package/dist/state/execution-store.d.ts +0 -257
- package/dist/state/execution-store.js +0 -527
- package/dist/state/execution-store.js.map +0 -1
- package/dist/state/migrate.d.ts +0 -15
- package/dist/state/migrate.js +0 -175
- package/dist/state/migrate.js.map +0 -1
- package/dist/state/workflow-run-store.d.ts +0 -216
- package/dist/state/workflow-run-store.js +0 -403
- package/dist/state/workflow-run-store.js.map +0 -1
- package/dist/telemetry/index.d.ts +0 -38
- package/dist/telemetry/index.js +0 -253
- package/dist/telemetry/index.js.map +0 -1
- package/dist/telemetry/pi-events.d.ts +0 -12
- package/dist/telemetry/pi-events.js +0 -134
- package/dist/telemetry/pi-events.js.map +0 -1
- package/dist/workflows/dag.d.ts +0 -37
- package/dist/workflows/dag.js +0 -118
- package/dist/workflows/dag.js.map +0 -1
- package/dist/workflows/loader.d.ts +0 -53
- package/dist/workflows/loader.js +0 -421
- package/dist/workflows/loader.js.map +0 -1
- package/dist/workflows/loop-eval.d.ts +0 -28
- package/dist/workflows/loop-eval.js +0 -74
- package/dist/workflows/loop-eval.js.map +0 -1
- package/dist/workflows/phase-executor.d.ts +0 -222
- package/dist/workflows/phase-executor.js +0 -1153
- package/dist/workflows/phase-executor.js.map +0 -1
- package/dist/workflows/phase-ref.d.ts +0 -50
- package/dist/workflows/phase-ref.js +0 -91
- package/dist/workflows/phase-ref.js.map +0 -1
- package/dist/workflows/resume.d.ts +0 -58
- package/dist/workflows/resume.js +0 -313
- package/dist/workflows/resume.js.map +0 -1
- package/dist/workflows/runner.d.ts +0 -63
- package/dist/workflows/runner.js +0 -381
- package/dist/workflows/runner.js.map +0 -1
- package/dist/workflows/schema.d.ts +0 -344
- package/dist/workflows/schema.js +0 -460
- package/dist/workflows/schema.js.map +0 -1
- package/dist/workflows/simple.d.ts +0 -90
- package/dist/workflows/simple.js +0 -393
- package/dist/workflows/simple.js.map +0 -1
- package/dist/workflows/target-policy.d.ts +0 -74
- package/dist/workflows/target-policy.js +0 -80
- package/dist/workflows/target-policy.js.map +0 -1
- package/dist/workflows/templates.d.ts +0 -88
- package/dist/workflows/templates.js +0 -127
- package/dist/workflows/templates.js.map +0 -1
- package/dist/workflows/triggers.d.ts +0 -21
- package/dist/workflows/triggers.js +0 -45
- package/dist/workflows/triggers.js.map +0 -1
- package/dist/workflows/verdict.d.ts +0 -19
- package/dist/workflows/verdict.js +0 -30
- package/dist/workflows/verdict.js.map +0 -1
- package/dist/worktree/manager.d.ts +0 -43
- package/dist/worktree/manager.js +0 -127
- package/dist/worktree/manager.js.map +0 -1
- package/docker-compose.yml +0 -274
- package/sandbox.Dockerfile +0 -68
- package/skills/README.md +0 -42
- package/skills/browser-qa/SKILL.md +0 -208
- package/skills/browser-qa/scripts/agent-browser.mjs +0 -531
- package/skills/building/SKILL.md +0 -95
- package/skills/chat/SKILL.md +0 -36
- package/skills/code-review/SKILL.md +0 -82
- package/skills/debug-production/SKILL.md +0 -108
- package/skills/demo/SKILL.md +0 -228
- package/skills/demo/scripts/compose-demo.sh +0 -243
- package/skills/issue-answer/SKILL.md +0 -91
- package/skills/issue-comment/SKILL.md +0 -49
- package/skills/issue-triage/SKILL.md +0 -149
- package/skills/issue-triage/references/AGENT-BRIEF.md +0 -65
- package/skills/pr-comment/SKILL.md +0 -64
- package/skills/pr-review/SKILL.md +0 -148
- package/skills/pr-review/references/findings-schema.md +0 -89
- package/skills/qa-test/SKILL.md +0 -97
- package/skills/repo-health/SKILL.md +0 -67
- package/skills/security-feedback/SKILL.md +0 -173
- package/skills/security-feedback/references/templates.md +0 -77
- package/skills/security-review/SKILL.md +0 -215
- package/skills/security-review/references/issue-format.md +0 -302
- package/skills/verify/SKILL.md +0 -118
- package/workflows/answer.yaml +0 -44
- package/workflows/build.yaml +0 -129
- package/workflows/cron-health.yaml +0 -6
- package/workflows/cron-review.yaml +0 -8
- package/workflows/cron-security.yaml +0 -6
- package/workflows/cron-triage.yaml +0 -8
- package/workflows/demo.yaml +0 -62
- package/workflows/examples/bash-smoke.yaml +0 -41
- package/workflows/examples/parallel-review.yaml +0 -36
- package/workflows/examples/tdd-loop.yaml +0 -17
- package/workflows/explore.yaml +0 -80
- package/workflows/issue-comment.yaml +0 -15
- package/workflows/issue-triage.yaml +0 -22
- package/workflows/pr-comment.yaml +0 -16
- package/workflows/pr-fix.yaml +0 -17
- package/workflows/pr-review.yaml +0 -50
- package/workflows/prompts/answer.md +0 -68
- package/workflows/prompts/architect.md +0 -44
- package/workflows/prompts/classifier.md +0 -60
- package/workflows/prompts/classify-adds-info.md +0 -17
- package/workflows/prompts/demo.md +0 -125
- package/workflows/prompts/executor.md +0 -42
- package/workflows/prompts/explore-ask.md +0 -99
- package/workflows/prompts/explore-publish.md +0 -31
- package/workflows/prompts/explore-read.md +0 -120
- package/workflows/prompts/explore-synthesize.md +0 -88
- package/workflows/prompts/fix.md +0 -18
- package/workflows/prompts/guardrails.md +0 -96
- package/workflows/prompts/pr-fix.md +0 -26
- package/workflows/prompts/pr.md +0 -39
- package/workflows/prompts/qa-browser.md +0 -95
- package/workflows/prompts/qa-synth.md +0 -44
- package/workflows/prompts/qa-test.md +0 -60
- package/workflows/prompts/re-reviewer.md +0 -33
- package/workflows/prompts/reviewer.md +0 -60
- package/workflows/prompts/verify-browser.md +0 -93
- package/workflows/prompts/verify-synth.md +0 -44
- package/workflows/prompts/verify.md +0 -63
- package/workflows/qa-test.yaml +0 -94
- package/workflows/repo-health.yaml +0 -15
- package/workflows/security-feedback.yaml +0 -25
- package/workflows/security-review.yaml +0 -37
- package/workflows/verify.yaml +0 -93
- /package/dist/{cli/cli-config.d.ts → cli-config.d.ts} +0 -0
- /package/dist/{cli/cli-config.js → cli-config.js} +0 -0
- /package/dist/{cli/cli-format.d.ts → cli-format.d.ts} +0 -0
- /package/dist/{cli/cli-format.js → cli-format.js} +0 -0
- /package/dist/{cli/cli-timeline.d.ts → cli-timeline.d.ts} +0 -0
- /package/dist/{cli/cli-timeline.js → cli-timeline.js} +0 -0
- /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
- /package/dist/{cli/oauth-cli.d.ts → oauth-cli.d.ts} +0 -0
- /package/dist/{cli/setup.d.ts → setup.d.ts} +0 -0
package/dist/engine/router.js
DELETED
|
@@ -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
|