Skip to content
← Public packages

@kentcdodds/sentry-triage

Sentry triage wakes Cole (Grok Bot) per issue: one active wake per repo (lease + queue), loop-safe Discord. Cole may spawn Cursor for isolated repo work.

src/cole-handoff.ts

161 lines · 4.4 KB · TypeScript
import { packageStorage } from 'kody:runtime'
import directWake from 'kody:@kentcdodds/grok-bot/wake'
import { WAKE_WEBHOOK_STORAGE_KEY } from './configure-wake-webhook.ts'

export const COLE_BOT_NAME = 'cole'
export const COLE_AGENT_ID = 'cole'
export const COLE_AGENT_URL = 'grok-bot:cole'
export const GROK_BOT_WAKE_WEBHOOK_SECRET = 'grokBotWakeWebhookUrl'

type WakeSlim = {
	ok?: boolean
	woke?: boolean
	timedOut?: boolean
	dryRun?: boolean
	error?: string
	status?: number
}

function asRecord(value: unknown): Record<string, unknown> | null {
	return value && typeof value === 'object' ? (value as Record<string, unknown>) : null
}

function unwrapWakeBody(body: unknown): WakeSlim {
	const root = asRecord(body)
	if (!root) return { ok: true, woke: true }
	for (const key of ['result', 'data', 'value', 'output'] as const) {
		const nested = asRecord(root[key])
		if (
			nested &&
			('woke' in nested || 'ok' in nested || 'timedOut' in nested || 'error' in nested)
		) {
			return nested as WakeSlim
		}
	}
	return root as WakeSlim
}

function wakeOk(input: { dryRun?: boolean }, woke: WakeSlim) {
	const timedOut = woke.timedOut === true
	if (input.dryRun === true) return { woke, timedOut, ok: Boolean(woke.ok) }
	const explicitFail = woke.ok === false && woke.woke === false && !timedOut
	const ok =
		Boolean(woke.woke) ||
		timedOut ||
		(!explicitFail && woke.ok === true && woke.woke !== false)
	return { woke, timedOut, ok }
}

async function resolveWakeWebhookTarget() {
	try {
		const stored = await packageStorage().get(WAKE_WEBHOOK_STORAGE_KEY)
		if (typeof stored === 'string' && stored.startsWith('https://')) return stored
	} catch {
		// Fall through to the user-secret placeholder.
	}
	return `{{secret:${GROK_BOT_WAKE_WEBHOOK_SECRET}}}`
}

async function wakeViaWebhook(params: Record<string, unknown>): Promise<WakeSlim> {
	const response = await fetch(await resolveWakeWebhookTarget(), {
		method: 'POST',
		headers: { 'content-type': 'application/json' },
		body: JSON.stringify(params),
	})
	const text = await response.text()
	let body: unknown = null
	try {
		body = text ? JSON.parse(text) : null
	} catch {
		body = { raw: text.slice(0, 500) }
	}
	if (!response.ok) {
		const slim = unwrapWakeBody(body)
		return {
			ok: false,
			woke: false,
			error: `wake webhook HTTP ${response.status}: ${slim.error || text.slice(0, 240)}`,
		}
	}
	const slim = unwrapWakeBody(body)
	if (slim.ok === false && slim.woke === false && slim.timedOut !== true) return slim
	if (slim.woke === undefined && slim.timedOut === undefined && slim.dryRun !== true) {
		return { ...slim, ok: true, woke: true }
	}
	return slim
}

async function wakeViaPackage(params: Record<string, unknown>): Promise<WakeSlim> {
	try {
		return (await directWake(
			params as {
				bot: string
				prompt: string
				dryRun?: boolean
				discordChannelId?: string
				discordMessageId?: string
			},
		)) as WakeSlim
	} catch (error) {
		return {
			ok: false,
			woke: false,
			error: error instanceof Error ? error.message : String(error),
		}
	}
}

/** Wake Cole without auto-spawning Cursor. Cole decides whether repo work needs Cursor. */
export async function wakeColeForTriage(input: {
	prompt: string
	discordChannelId?: string | null
	discordMessageId?: string | null
	dryRun?: boolean
}) {
	const params = {
		bot: COLE_BOT_NAME,
		prompt: input.prompt,
		dryRun: input.dryRun === true,
		...(input.discordChannelId ? { discordChannelId: input.discordChannelId } : {}),
		...(input.discordMessageId ? { discordMessageId: input.discordMessageId } : {}),
	}
	let via = 'webhook'
	let woke: WakeSlim
	try {
		woke = await wakeViaWebhook(params)
	} catch (error) {
		woke = {
			ok: false,
			woke: false,
			error: error instanceof Error ? error.message : String(error),
		}
	}
	if (!woke.ok && !woke.woke && !woke.timedOut) {
		via = 'package-fallback'
		woke = await wakeViaPackage(params)
	}
	return { ...wakeOk(input, woke), via }
}

/** Agent-shaped compatibility result for existing lease/state code. */
export async function wakeCole(input: {
	promptText: string
	discordMessageId?: string | null
	discordChannelId?: string | null
}) {
	const result = await wakeColeForTriage({
		prompt: input.promptText,
		discordMessageId: input.discordMessageId,
		discordChannelId: input.discordChannelId,
	})
	if (!result.ok) {
		throw new Error(`cole-wake-failed: ${result.woke.error ?? 'unknown wake failure'}`)
	}
	return {
		id: COLE_AGENT_ID,
		url: COLE_AGENT_URL,
		bot: COLE_BOT_NAME,
		timedOut: result.timedOut,
		via: result.via,
	}
}