feat(openclaw): add adapter hire-approved hooks

This commit is contained in:
Dotta
2026-03-06 08:17:42 -06:00
parent 67bc601258
commit 3369a9e685
12 changed files with 512 additions and 3 deletions

View File

@@ -0,0 +1,113 @@
import { and, eq } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import { agents } from "@paperclipai/db";
import type { HireApprovedPayload } from "@paperclipai/adapter-utils";
import { findServerAdapter } from "../adapters/registry.js";
import { logger } from "../middleware/logger.js";
import { logActivity } from "./activity-log.js";
const HIRE_APPROVED_MESSAGE =
"Tell your user that your hire was approved, now they should assign you a task in Paperclip or ask you to create issues.";
export interface NotifyHireApprovedInput {
companyId: string;
agentId: string;
source: "join_request" | "approval";
sourceId: string;
approvedAt?: Date;
}
/**
* Invokes the adapter's onHireApproved hook when an agent is approved (join-request or hire_agent approval).
* Failures are non-fatal: we log and write to activity, never throw.
*/
export async function notifyHireApproved(
db: Db,
input: NotifyHireApprovedInput,
): Promise<void> {
const { companyId, agentId, source, sourceId } = input;
const approvedAt = input.approvedAt ?? new Date();
const row = await db
.select()
.from(agents)
.where(and(eq(agents.id, agentId), eq(agents.companyId, companyId)))
.then((rows) => rows[0] ?? null);
if (!row) {
logger.warn({ companyId, agentId, source, sourceId }, "hire hook: agent not found in company, skipping");
return;
}
const adapterType = row.adapterType ?? "process";
const adapter = findServerAdapter(adapterType);
const onHireApproved = adapter?.onHireApproved;
if (!onHireApproved) {
return;
}
const payload: HireApprovedPayload = {
companyId,
agentId,
agentName: row.name,
adapterType,
source,
sourceId,
approvedAt: approvedAt.toISOString(),
message: HIRE_APPROVED_MESSAGE,
};
const adapterConfig =
typeof row.adapterConfig === "object" && row.adapterConfig !== null && !Array.isArray(row.adapterConfig)
? (row.adapterConfig as Record<string, unknown>)
: {};
try {
const result = await onHireApproved(payload, adapterConfig);
if (result.ok) {
await logActivity(db, {
companyId,
actorType: "system",
actorId: "hire_hook",
action: "hire_hook.succeeded",
entityType: "agent",
entityId: agentId,
details: { source, sourceId, adapterType },
});
return;
}
logger.warn(
{ companyId, agentId, adapterType, source, sourceId, error: result.error, detail: result.detail },
"hire hook: adapter returned failure",
);
await logActivity(db, {
companyId,
actorType: "system",
actorId: "hire_hook",
action: "hire_hook.failed",
entityType: "agent",
entityId: agentId,
details: { source, sourceId, adapterType, error: result.error, detail: result.detail },
});
} catch (err) {
logger.error(
{ err, companyId, agentId, adapterType, source, sourceId },
"hire hook: adapter threw",
);
await logActivity(db, {
companyId,
actorType: "system",
actorId: "hire_hook",
action: "hire_hook.error",
entityType: "agent",
entityId: agentId,
details: {
source,
sourceId,
adapterType,
error: err instanceof Error ? err.message : String(err),
},
});
}
}