mirror of
https://github.com/saltbo/agent-kanban.git
synced 2026-09-19 09:01:47 +08:00
69 lines
3.0 KiB
TypeScript
69 lines
3.0 KiB
TypeScript
import { realmrootClientCredentialsToken } from "@server/adapters/realmroot/clientCredentials";
|
|
import { akResource } from "@server/config/serviceUrls";
|
|
import type { Env } from "@server/env";
|
|
import {
|
|
type TaskLifecycleEvent,
|
|
TaskLifecycleNotificationFailure,
|
|
type TaskLifecycleNotifier,
|
|
} from "@server/usecases/tasks/taskLifecycleNotifications";
|
|
|
|
export function inboxTaskLifecycleNotifier(env: Env): TaskLifecycleNotifier {
|
|
return {
|
|
async notify(notification) {
|
|
try {
|
|
requireConfiguration(env);
|
|
const token = await realmrootClientCredentialsToken({
|
|
issuer: env.OIDC_ISSUER,
|
|
clientId: env.OIDC_SERVICE_CLIENT_ID,
|
|
clientSecret: env.OIDC_SERVICE_CLIENT_SECRET,
|
|
resource: env.INBOX_RESOURCE,
|
|
scope: "messages:create",
|
|
});
|
|
const response = await fetch(`${env.INBOX_RESOURCE}/messages`, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"API-Version": env.INBOX_API_VERSION,
|
|
"Content-Type": "application/json",
|
|
"Idempotency-Key": `ak:${notification.event}:${notification.taskId}:${notification.version}`,
|
|
},
|
|
body: JSON.stringify({
|
|
recipients: [`agent:${notification.assigneeActorId}`],
|
|
subject: subject(notification.event),
|
|
content: { text: content(env, notification) },
|
|
routingKey: `agent-kanban:task:${notification.taskId}`,
|
|
}),
|
|
signal: AbortSignal.timeout(10_000),
|
|
});
|
|
if (!response.ok) {
|
|
throw new TaskLifecycleNotificationFailure("Inbox rejected the task notification", {
|
|
cause: new Error(`Inbox responded with HTTP ${response.status}`),
|
|
});
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof TaskLifecycleNotificationFailure) throw error;
|
|
throw new TaskLifecycleNotificationFailure("Inbox task notification is unavailable", { cause: error });
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function requireConfiguration(env: Env): void {
|
|
if (!env.INBOX_RESOURCE || !env.INBOX_API_VERSION || !env.OIDC_SERVICE_CLIENT_ID || !env.OIDC_SERVICE_CLIENT_SECRET) {
|
|
throw new TaskLifecycleNotificationFailure("AK Inbox machine integration is not configured");
|
|
}
|
|
}
|
|
|
|
function subject(event: TaskLifecycleEvent): string {
|
|
if (event === "assigned") return "Agent Kanban task assigned";
|
|
if (event === "review_rejected") return "Agent Kanban review rejected";
|
|
if (event === "completed") return "Agent Kanban task accepted";
|
|
return "Agent Kanban task cancelled";
|
|
}
|
|
|
|
function content(env: Env, notification: { taskId: string; event: TaskLifecycleEvent; reason?: string | null }): string {
|
|
const taskUrl = `${akResource(env)}/tasks/${notification.taskId}`;
|
|
const reason = notification.reason ? `\nReview reason: ${notification.reason}` : "";
|
|
return `Task ${notification.taskId} changed: ${notification.event}.${reason}\nRead the current Task before acting: realmroot toolbox get ${taskUrl} --json\nCanonical resource: ${taskUrl}`;
|
|
}
|