evOo-local/packages/adapters/src/background-job-handlers.ts
Claude 044669edfc
Add a media library: owner folders that bots can search and import from
The deployment owner adds folders of photos and videos in the media settings. A per-folder
background job indexes them (relative path, kind, size, capture date, dimensions, duration),
re-indexes every six hours, and drops files that are gone. The owner's own runs get two tools:
search_media (words in folder and file names, kind, capture date range, newest first) and
import_media (copies one file into the conversation as an artifact). Files are never changed;
hidden entries and symlinks are skipped, and an import must resolve inside its folder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs
2026-10-05 08:45:30 +00:00

138 lines
4.8 KiB
TypeScript

import type {
AgentHomeStore,
AgentRuntime,
BackgroundJobHandlers,
JobPublisher,
MessagingSurface,
SandboxProvider,
} from "@rakazo/adapter-kit";
import { messagingDeliverJob } from "@rakazo/adapter-kit";
import type { PrismaClient, ThreadEvents } from "@rakazo/db";
import { getLogger } from "@rakazo/logging";
import type { CloudAgentConnection } from "./cloud-agent-factory.js";
import { pollCloudAgent } from "./cloud-agent-poll.js";
import { expireComputerControl } from "./computer-control.js";
import { scheduleComputerSleep, sleepComputerIfIdle } from "./computer-idle.js";
import { performComputerUpdate } from "./computer-update.js";
import type { createRunExecutor } from "./executor.js";
import { compactHistory } from "./history-compaction.js";
import { syncMediaLibrarySource } from "./media-library.js";
import type { MediaRuntimeHost } from "./media-runtime.js";
import type { MemoryProviderResolver } from "./memory-provider-factory.js";
import { deliverMessagingOutbound, mirrorMessagingOutbound } from "./messaging-delivery.js";
import { consolidateProfile } from "./profile-consolidation.js";
import type { EncryptedSecretStore } from "./secrets.js";
import { expireTaughtSkillTeaching } from "./teaching-session.js";
export function createBackgroundJobHandlers(deps: {
executor: ReturnType<typeof createRunExecutor>;
prisma: PrismaClient;
sandbox: SandboxProvider;
home: AgentHomeStore;
jobs: JobPublisher;
events: ThreadEvents;
workerId: string;
runtime: AgentRuntime;
secretStore: EncryptedSecretStore;
memoryProviders: MemoryProviderResolver;
deploymentModelKey?: string;
messaging?: MessagingSurface;
cloudAgent?: CloudAgentConnection | null;
media?: MediaRuntimeHost;
}): BackgroundJobHandlers {
const deliverMessaging = async (runId?: string) => {
if (!deps.messaging) return;
await deliverMessagingOutbound(
{ prisma: deps.prisma, messaging: deps.messaging, events: deps.events, jobs: deps.jobs },
{ runId },
{
operationId: `messaging.deliver:${runId ?? "drain"}`,
traceId: `messaging.deliver:${runId ?? "drain"}`,
spaceId: "",
userId: "",
signal: new AbortController().signal,
},
);
};
return {
"run.continue": async (payload) => {
await deps.executor.continueRun(payload.runId, deps.workerId);
// Automatic messaging mirror: once the run's bot messages are durable,
// copy them into the outbox. Never let mirror failures fail the run.
if (deps.messaging) {
await mirrorMessagingOutbound(
{ prisma: deps.prisma, messaging: deps.messaging, events: deps.events, jobs: deps.jobs },
payload.runId,
);
await deps.jobs.enqueue(messagingDeliverJob()).catch(async (error) => {
getLogger().error("messaging.deliver enqueue error", error);
await deliverMessaging();
});
}
},
"messaging.deliver": async (payload) => {
await deliverMessaging(payload.runId);
},
"routine.wakeup": async (payload) => {
await deps.executor.wakeRoutine(payload.routineId, payload.scheduledFor);
},
"computer.update": async ({ updateId }) => {
await performComputerUpdate(deps, updateId);
},
"computer.sleep": async (payload) => {
await sleepComputerIfIdle(deps, payload.computerId);
},
"computer.control-expire": async (payload) => {
if (await expireComputerControl(deps, payload.computerId, payload.leaseId)) {
scheduleComputerSleep(deps.jobs, payload.computerId);
}
},
"skill.teaching-expire": async (payload) => {
await expireTaughtSkillTeaching(deps, payload.skillId);
},
"cloud_agent.poll": async (payload) => {
await pollCloudAgent(
{
prisma: deps.prisma,
jobs: deps.jobs,
events: deps.events,
cloudAgent: deps.cloudAgent,
},
payload,
);
},
"history.compact": async (payload) => {
await compactHistory(
{
prisma: deps.prisma,
runtime: deps.runtime,
jobs: deps.jobs,
memoryProviders: deps.memoryProviders,
deploymentModelKey: deps.deploymentModelKey,
...(deps.executor.resolveModel ? { resolveModel: deps.executor.resolveModel } : {}),
},
payload.threadId,
);
},
"media.run": async (payload) => {
await deps.media?.runJob(payload.jobId);
},
"media.library.sync": async (payload) => {
await syncMediaLibrarySource(deps, payload.sourceId);
},
"profile.consolidate": async (payload) => {
await consolidateProfile(
{
prisma: deps.prisma,
runtime: deps.runtime,
jobs: deps.jobs,
memoryProviders: deps.memoryProviders,
resolveModel: (scope) => deps.executor.resolveModel(scope),
},
payload.userId,
);
},
};
}