import { randomUUID } from "node:crypto"; import { definePlugin, runWorker, type PluginApiRequestInput } from "@paperclipai/plugin-sdk"; type SmokeInput = { companyId: string; issueId: string; assigneeAgentId?: string | null; actorAgentId?: string | null; actorUserId?: string | null; actorRunId?: string | null; }; type SmokeSummary = { rootIssueId: string; childIssueId: string | null; blockerIssueId: string | null; billingCode: string; joinedRows: unknown[]; subtreeIssueIds: string[]; wakeupQueued: boolean; }; let readSmokeSummary: ((companyId: string, issueId: string) => Promise) | null = null; let initializeSmoke: ((input: SmokeInput) => Promise) | null = null; function tableName(namespace: string) { return `${namespace}.smoke_runs`; } function stringField(value: unknown): string | null { return typeof value === "string" && value.trim().length > 0 ? value : null; } const plugin = definePlugin({ async setup(ctx) { readSmokeSummary = async function readSummary(companyId: string, issueId: string): Promise { const rows = await ctx.db.query<{ root_issue_id: string; child_issue_id: string | null; blocker_issue_id: string | null; billing_code: string; issue_title: string; last_summary: unknown; }>( `SELECT s.root_issue_id, s.child_issue_id, s.blocker_issue_id, s.billing_code, i.title AS issue_title, s.last_summary FROM ${tableName(ctx.db.namespace)} s JOIN public.issues i ON i.id = s.root_issue_id WHERE s.root_issue_id = $1`, [issueId], ); const row = rows[0]; if (!row) return null; const orchestration = await ctx.issues.summaries.getOrchestration({ issueId, companyId, includeSubtree: true, billingCode: row.billing_code, }); return { rootIssueId: row.root_issue_id, childIssueId: row.child_issue_id, blockerIssueId: row.blocker_issue_id, billingCode: row.billing_code, joinedRows: rows, subtreeIssueIds: orchestration.subtreeIssueIds, wakeupQueued: Boolean((row.last_summary as { wakeupQueued?: unknown } | null)?.wakeupQueued), }; }; initializeSmoke = async function runSmoke(input: SmokeInput): Promise { const root = await ctx.issues.get(input.issueId, input.companyId); if (!root) throw new Error(`Issue not found: ${input.issueId}`); const billingCode = `plugin-smoke:${input.issueId}`; const actor = { actorAgentId: input.actorAgentId ?? null, actorUserId: input.actorUserId ?? null, actorRunId: input.actorRunId ?? null, }; const blocker = await ctx.issues.create({ companyId: input.companyId, parentId: input.issueId, inheritExecutionWorkspaceFromIssueId: input.issueId, title: "Orchestration smoke blocker", description: "Resolved blocker used to verify plugin relation writes without preventing the smoke wakeup.", status: "done", priority: "low", billingCode, originKind: `plugin:${ctx.manifest.id}:blocker`, originId: `${input.issueId}:blocker`, actor, }); const child = await ctx.issues.create({ companyId: input.companyId, parentId: input.issueId, inheritExecutionWorkspaceFromIssueId: input.issueId, title: "Orchestration smoke child", description: "Generated by the orchestration smoke plugin to verify issue, document, relation, wakeup, and summary APIs.", status: "todo", priority: "medium", assigneeAgentId: input.assigneeAgentId ?? root.assigneeAgentId ?? undefined, billingCode, originKind: `plugin:${ctx.manifest.id}:child`, originId: `${input.issueId}:child`, blockedByIssueIds: [blocker.id], actor, }); await ctx.issues.relations.setBlockedBy(child.id, [blocker.id], input.companyId, actor); await ctx.issues.documents.upsert({ issueId: child.id, companyId: input.companyId, key: "orchestration-smoke", title: "Orchestration Smoke", format: "markdown", body: [ "# Orchestration Smoke", "", `- Root issue: ${input.issueId}`, `- Child issue: ${child.id}`, `- Billing code: ${billingCode}`, ].join("\n"), changeSummary: "Recorded orchestration smoke output", }); const wakeup = await ctx.issues.requestWakeup(child.id, input.companyId, { reason: "plugin:orchestration_smoke", contextSource: "plugin-orchestration-smoke", idempotencyKey: `${input.issueId}:child`, ...actor, }); const orchestration = await ctx.issues.summaries.getOrchestration({ issueId: input.issueId, companyId: input.companyId, includeSubtree: true, billingCode, }); const summarySnapshot = { childIssueId: child.id, blockerIssueId: blocker.id, wakeupQueued: wakeup.queued, subtreeIssueIds: orchestration.subtreeIssueIds, }; await ctx.db.execute( `INSERT INTO ${tableName(ctx.db.namespace)} (id, root_issue_id, child_issue_id, blocker_issue_id, billing_code, last_summary) VALUES ($1, $2, $3, $4, $5, $6::jsonb) ON CONFLICT (id) DO UPDATE SET child_issue_id = EXCLUDED.child_issue_id, blocker_issue_id = EXCLUDED.blocker_issue_id, billing_code = EXCLUDED.billing_code, last_summary = EXCLUDED.last_summary, updated_at = now()`, [ randomUUID(), input.issueId, child.id, blocker.id, billingCode, JSON.stringify(summarySnapshot), ], ); return { rootIssueId: input.issueId, childIssueId: child.id, blockerIssueId: blocker.id, billingCode, joinedRows: await ctx.db.query( `SELECT s.id, s.billing_code, i.title AS root_title FROM ${tableName(ctx.db.namespace)} s JOIN public.issues i ON i.id = s.root_issue_id WHERE s.root_issue_id = $1`, [input.issueId], ), subtreeIssueIds: orchestration.subtreeIssueIds, wakeupQueued: wakeup.queued, }; }; ctx.data.register("surface-status", async (params) => { const companyId = stringField(params.companyId); const issueId = stringField(params.issueId); return { status: "ok", checkedAt: new Date().toISOString(), databaseNamespace: ctx.db.namespace, routeKeys: (ctx.manifest.apiRoutes ?? []).map((route) => route.routeKey), capabilities: ctx.manifest.capabilities, summary: companyId && issueId ? await readSmokeSummary?.(companyId, issueId) ?? null : null, }; }); ctx.actions.register("initialize-smoke", async (params) => { const companyId = stringField(params.companyId); const issueId = stringField(params.issueId); if (!companyId || !issueId) throw new Error("companyId and issueId are required"); if (!initializeSmoke) throw new Error("Smoke initializer is not ready"); return initializeSmoke({ companyId, issueId, assigneeAgentId: stringField(params.assigneeAgentId), actorAgentId: stringField(params.actorAgentId), actorUserId: stringField(params.actorUserId), actorRunId: stringField(params.actorRunId), }); }); }, async onApiRequest(input: PluginApiRequestInput) { if (input.routeKey === "summary") { const issueId = input.params.issueId; return { body: await readSmokeSummary?.(input.companyId, issueId) ?? null, }; } if (input.routeKey === "initialize") { if (!initializeSmoke) throw new Error("Smoke initializer is not ready"); const body = input.body as Record | null; return { status: 201, body: await initializeSmoke({ companyId: input.companyId, issueId: input.params.issueId, assigneeAgentId: stringField(body?.assigneeAgentId), actorAgentId: input.actor.agentId ?? null, actorUserId: input.actor.userId ?? null, actorRunId: input.actor.runId ?? null, }), }; } return { status: 404, body: { error: `Unknown orchestration smoke route: ${input.routeKey}` }, }; }, async onHealth() { return { status: "ok", message: "Orchestration smoke plugin worker is running", details: { surfaces: ["database", "scoped-api-route", "issue-panel", "orchestration-apis"], }, }; } }); export default plugin; runWorker(plugin, import.meta.url);