Files
kandev/apps/web/lib/api/domains/slack-api.ts
T
João Salavisa 7e0b46c227 feat(slack): cookie-mode integration that triages threads via a utility agent (#775)
* feat(slack): add cookie-mode integration that triages messages via utility agent

Adds an install-wide Slack integration that polls the connected user's
own messages for `!kandev <instruction>`, hands the thread context to a
configured utility agent with the Kandev MCP server attached, and
creates the resulting task. Auth uses the user's xoxc- token + d cookie
so it works on locked-down Enterprise Slack tenants where bot installs
aren't possible.

Shared infra:
- ACP host-utility executor accepts McpServers on session/new
- New MCP tool list_repositories_kandev so the triage agent can pick the
  right repo
- Template engine exposes Slack-specific variables for prompt injection
- secretadapter + healthpoll reused from jira/linear

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(slack): address PR review feedback round 9

- Drop dead `var _ = time.Second` no-op and its misleading comment in
  trigger_test.go.
- `RecordAuthHealth` now invalidates the cached client when the probe
  fails, so the poller self-recovers from credential rotation without
  requiring a re-save.
- Stable secondary sort key (`workspace_id DESC`) on the legacy →
  singleton migration so promoted-row selection is deterministic on
  `updated_at` ties.
- Extract `categorySlack` constant in `engine.go` and update the
  `VariableInfo.Category` doc-comment to list the full set of values.
- Realign `slack-integration/spec.md` with what shipped: cookie-mode +
  `!kandev` triage as v1, with bot install / User OAuth / reaction &
  slash & shortcut triggers moved under Future scope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 09:03:42 +01:00

37 lines
1.2 KiB
TypeScript

import { fetchJson, type ApiRequestOptions } from "../client";
import type {
SetSlackConfigRequest,
SlackConfig,
TestSlackConnectionResult,
} from "@/lib/types/slack";
// getSlackConfig returns null when the backend responds 204 (no config yet).
export async function getSlackConfig(options?: ApiRequestOptions): Promise<SlackConfig | null> {
const res = await fetchJson<SlackConfig | undefined>(`/api/v1/slack/config`, options);
return res ?? null;
}
export async function setSlackConfig(payload: SetSlackConfigRequest, options?: ApiRequestOptions) {
return fetchJson<SlackConfig>(`/api/v1/slack/config`, {
...options,
init: { ...(options?.init ?? {}), method: "POST", body: JSON.stringify(payload) },
});
}
export async function deleteSlackConfig(options?: ApiRequestOptions) {
return fetchJson<{ deleted: boolean }>(`/api/v1/slack/config`, {
...options,
init: { ...(options?.init ?? {}), method: "DELETE" },
});
}
export async function testSlackConnection(
payload: SetSlackConfigRequest,
options?: ApiRequestOptions,
) {
return fetchJson<TestSlackConnectionResult>(`/api/v1/slack/config/test`, {
...options,
init: { ...(options?.init ?? {}), method: "POST", body: JSON.stringify(payload) },
});
}