sandbox-agent/foundry/packages/backend/src/actors/task/index.ts
Nathan Flurry 3895e34bdb feat(foundry): add foundry base sandbox image with sudo, chromium, and dev tooling
Add a custom Docker image (foundry-base.Dockerfile) that builds sandbox-agent
from source and layers sudo, git, neovim, gh, node, bun, chromium, and
agent-browser. Includes publish script for timestamped + latest tags to
rivetdev/sandbox-agent on Docker Hub.

Update local sandbox provider default to use foundry-base-latest and wire
HF_LOCAL_SANDBOX_IMAGE env var through compose.dev.yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 02:09:12 -07:00

99 lines
3.5 KiB
TypeScript

import { actor, queue } from "rivetkit";
import { workflow } from "rivetkit/workflow";
import type { TaskRecord } from "@sandbox-agent/foundry-shared";
import { taskDb } from "./db/db.js";
import { getCurrentRecord } from "./workflow/common.js";
import {
changeWorkspaceModel,
getSessionDetail,
getTaskDetail,
getTaskSummary,
markWorkspaceUnread,
refreshWorkspaceDerivedState,
refreshWorkspaceSessionTranscript,
renameWorkspaceSession,
renameWorkspaceTask,
selectWorkspaceSession,
setWorkspaceSessionUnread,
syncTaskPullRequest,
syncWorkspaceSessionStatus,
updateWorkspaceDraft,
} from "./workspace.js";
import { runTaskWorkflow } from "./workflow/index.js";
import { TASK_QUEUE_NAMES } from "./workflow/queue.js";
export interface TaskInput {
organizationId: string;
repoId: string;
taskId: string;
}
export const task = actor({
db: taskDb,
queues: Object.fromEntries(TASK_QUEUE_NAMES.map((name) => [name, queue()])),
options: {
name: "Task",
icon: "wrench",
actionTimeout: 10 * 60_000,
},
createState: (_c, input: TaskInput) => ({
organizationId: input.organizationId,
repoId: input.repoId,
taskId: input.taskId,
}),
actions: {
async get(c): Promise<TaskRecord> {
return await getCurrentRecord(c);
},
async getTaskSummary(c) {
return await getTaskSummary(c);
},
async getTaskDetail(c, input?: { authSessionId?: string }) {
return await getTaskDetail(c, input?.authSessionId);
},
async getSessionDetail(c, input: { sessionId: string; authSessionId?: string }) {
return await getSessionDetail(c, input.sessionId, input.authSessionId);
},
// Direct actions migrated from queue:
async markUnread(c, input: { authSessionId?: string }) {
await markWorkspaceUnread(c, input?.authSessionId);
},
async renameTask(c, input: { value: string }) {
await renameWorkspaceTask(c, input.value);
},
async renameSession(c, input: { sessionId: string; title: string }) {
await renameWorkspaceSession(c, input.sessionId, input.title);
},
async selectSession(c, input: { sessionId: string; authSessionId?: string }) {
await selectWorkspaceSession(c, input.sessionId, input?.authSessionId);
},
async setSessionUnread(c, input: { sessionId: string; unread: boolean; authSessionId?: string }) {
await setWorkspaceSessionUnread(c, input.sessionId, input.unread, input?.authSessionId);
},
async updateDraft(c, input: { sessionId: string; text: string; attachments: any[]; authSessionId?: string }) {
await updateWorkspaceDraft(c, input.sessionId, input.text, input.attachments, input?.authSessionId);
},
async changeModel(c, input: { sessionId: string; model: string; authSessionId?: string }) {
await changeWorkspaceModel(c, input.sessionId, input.model, input?.authSessionId);
},
async refreshSessionTranscript(c, input: { sessionId: string }) {
await refreshWorkspaceSessionTranscript(c, input.sessionId);
},
async refreshDerived(c) {
await refreshWorkspaceDerivedState(c);
},
async syncSessionStatus(c, input: { sessionId: string; status: "running" | "idle" | "error"; at: number }) {
await syncWorkspaceSessionStatus(c, input.sessionId, input.status, input.at);
},
async syncPullRequest(c, input: { pullRequest: any }) {
await syncTaskPullRequest(c, input?.pullRequest ?? null);
},
},
run: workflow(runTaskWorkflow),
});
export { taskWorkflowQueueName } from "./workflow/index.js";