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
138 lines
4.8 KiB
TypeScript
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,
|
|
);
|
|
},
|
|
};
|
|
}
|