mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-19 02:01:37 +00:00
feat(foundry): checkpoint actor and workspace refactor
This commit is contained in:
parent
32f3c6c3bc
commit
dbe57d45b9
81 changed files with 3441 additions and 2332 deletions
|
|
@ -6,25 +6,25 @@ import type {
|
|||
SessionEvent,
|
||||
TaskRecord,
|
||||
TaskSummary,
|
||||
TaskWorkbenchChangeModelInput,
|
||||
TaskWorkbenchCreateTaskInput,
|
||||
TaskWorkbenchCreateTaskResponse,
|
||||
TaskWorkbenchDiffInput,
|
||||
TaskWorkbenchRenameInput,
|
||||
TaskWorkbenchRenameSessionInput,
|
||||
TaskWorkbenchSelectInput,
|
||||
TaskWorkbenchSetSessionUnreadInput,
|
||||
TaskWorkbenchSendMessageInput,
|
||||
TaskWorkbenchSnapshot,
|
||||
TaskWorkbenchSessionInput,
|
||||
TaskWorkbenchUpdateDraftInput,
|
||||
TaskWorkspaceChangeModelInput,
|
||||
TaskWorkspaceCreateTaskInput,
|
||||
TaskWorkspaceCreateTaskResponse,
|
||||
TaskWorkspaceDiffInput,
|
||||
TaskWorkspaceRenameInput,
|
||||
TaskWorkspaceRenameSessionInput,
|
||||
TaskWorkspaceSelectInput,
|
||||
TaskWorkspaceSetSessionUnreadInput,
|
||||
TaskWorkspaceSendMessageInput,
|
||||
TaskWorkspaceSnapshot,
|
||||
TaskWorkspaceSessionInput,
|
||||
TaskWorkspaceUpdateDraftInput,
|
||||
TaskEvent,
|
||||
WorkbenchSessionDetail,
|
||||
WorkbenchTaskDetail,
|
||||
WorkbenchTaskSummary,
|
||||
WorkspaceSessionDetail,
|
||||
WorkspaceTaskDetail,
|
||||
WorkspaceTaskSummary,
|
||||
OrganizationEvent,
|
||||
OrganizationSummarySnapshot,
|
||||
HistoryEvent,
|
||||
AuditLogEvent as HistoryEvent,
|
||||
HistoryQueryInput,
|
||||
SandboxProviderId,
|
||||
RepoOverview,
|
||||
|
|
@ -34,7 +34,7 @@ import type {
|
|||
} from "@sandbox-agent/foundry-shared";
|
||||
import type { ProcessCreateRequest, ProcessLogFollowQuery, ProcessLogsResponse, ProcessSignalQuery } from "sandbox-agent";
|
||||
import type { ActorConn, BackendClient, SandboxProcessRecord, SandboxSessionEventRecord, SandboxSessionRecord } from "../backend-client.js";
|
||||
import { getSharedMockWorkbenchClient } from "./workbench-client.js";
|
||||
import { getSharedMockWorkspaceClient } from "./workspace-client.js";
|
||||
|
||||
interface MockProcessRecord extends SandboxProcessRecord {
|
||||
logText: string;
|
||||
|
|
@ -89,7 +89,7 @@ function toTaskStatus(status: TaskRecord["status"], archived: boolean): TaskReco
|
|||
}
|
||||
|
||||
export function createMockBackendClient(defaultOrganizationId = "default"): BackendClient {
|
||||
const workbench = getSharedMockWorkbenchClient();
|
||||
const workspace = getSharedMockWorkspaceClient();
|
||||
const listenersBySandboxId = new Map<string, Set<() => void>>();
|
||||
const processesBySandboxId = new Map<string, MockProcessRecord[]>();
|
||||
const connectionListeners = new Map<string, Set<(payload: any) => void>>();
|
||||
|
|
@ -97,7 +97,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
let nextProcessId = 1;
|
||||
|
||||
const requireTask = (taskId: string) => {
|
||||
const task = workbench.getSnapshot().tasks.find((candidate) => candidate.id === taskId);
|
||||
const task = workspace.getSnapshot().tasks.find((candidate) => candidate.id === taskId);
|
||||
if (!task) {
|
||||
throw new Error(`Unknown mock task ${taskId}`);
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
async dispose(): Promise<void> {},
|
||||
});
|
||||
|
||||
const buildTaskSummary = (task: TaskWorkbenchSnapshot["tasks"][number]): WorkbenchTaskSummary => ({
|
||||
const buildTaskSummary = (task: TaskWorkspaceSnapshot["tasks"][number]): WorkspaceTaskSummary => ({
|
||||
id: task.id,
|
||||
repoId: task.repoId,
|
||||
title: task.title,
|
||||
|
|
@ -187,7 +187,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
})),
|
||||
});
|
||||
|
||||
const buildTaskDetail = (task: TaskWorkbenchSnapshot["tasks"][number]): WorkbenchTaskDetail => ({
|
||||
const buildTaskDetail = (task: TaskWorkspaceSnapshot["tasks"][number]): WorkspaceTaskDetail => ({
|
||||
...buildTaskSummary(task),
|
||||
task: task.title,
|
||||
agentType: task.sessions[0]?.agent === "Codex" ? "codex" : "claude",
|
||||
|
|
@ -211,7 +211,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
activeSandboxId: task.id,
|
||||
});
|
||||
|
||||
const buildSessionDetail = (task: TaskWorkbenchSnapshot["tasks"][number], sessionId: string): WorkbenchSessionDetail => {
|
||||
const buildSessionDetail = (task: TaskWorkspaceSnapshot["tasks"][number], sessionId: string): WorkspaceSessionDetail => {
|
||||
const tab = task.sessions.find((candidate) => candidate.id === sessionId);
|
||||
if (!tab) {
|
||||
throw new Error(`Unknown mock session ${sessionId} for task ${task.id}`);
|
||||
|
|
@ -232,7 +232,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
};
|
||||
|
||||
const buildOrganizationSummary = (): OrganizationSummarySnapshot => {
|
||||
const snapshot = workbench.getSnapshot();
|
||||
const snapshot = workspace.getSnapshot();
|
||||
const taskSummaries = snapshot.tasks.map(buildTaskSummary);
|
||||
return {
|
||||
organizationId: defaultOrganizationId,
|
||||
|
|
@ -256,20 +256,16 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
`sandbox:${organizationId}:${sandboxProviderId}:${sandboxId}`;
|
||||
|
||||
const emitOrganizationSnapshot = (): void => {
|
||||
const summary = buildOrganizationSummary();
|
||||
const latestTask = [...summary.taskSummaries].sort((left, right) => right.updatedAtMs - left.updatedAtMs)[0] ?? null;
|
||||
if (latestTask) {
|
||||
emitConnectionEvent(organizationScope(defaultOrganizationId), "organizationUpdated", {
|
||||
type: "taskSummaryUpdated",
|
||||
taskSummary: latestTask,
|
||||
} satisfies OrganizationEvent);
|
||||
}
|
||||
emitConnectionEvent(organizationScope(defaultOrganizationId), "organizationUpdated", {
|
||||
type: "organizationUpdated",
|
||||
snapshot: buildOrganizationSummary(),
|
||||
} satisfies OrganizationEvent);
|
||||
};
|
||||
|
||||
const emitTaskUpdate = (taskId: string): void => {
|
||||
const task = requireTask(taskId);
|
||||
emitConnectionEvent(taskScope(defaultOrganizationId, task.repoId, task.id), "taskUpdated", {
|
||||
type: "taskDetailUpdated",
|
||||
type: "taskUpdated",
|
||||
detail: buildTaskDetail(task),
|
||||
} satisfies TaskEvent);
|
||||
};
|
||||
|
|
@ -400,6 +396,10 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
return unsupportedAppSnapshot();
|
||||
},
|
||||
|
||||
async setAppDefaultModel(): Promise<FoundryAppSnapshot> {
|
||||
return unsupportedAppSnapshot();
|
||||
},
|
||||
|
||||
async updateAppOrganizationProfile(): Promise<FoundryAppSnapshot> {
|
||||
return unsupportedAppSnapshot();
|
||||
},
|
||||
|
|
@ -433,7 +433,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
},
|
||||
|
||||
async listRepos(_organizationId: string): Promise<RepoRecord[]> {
|
||||
return workbench.getSnapshot().repos.map((repo) => ({
|
||||
return workspace.getSnapshot().repos.map((repo) => ({
|
||||
organizationId: defaultOrganizationId,
|
||||
repoId: repo.id,
|
||||
remoteUrl: mockRepoRemote(repo.label),
|
||||
|
|
@ -447,7 +447,7 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
},
|
||||
|
||||
async listTasks(_organizationId: string, repoId?: string): Promise<TaskSummary[]> {
|
||||
return workbench
|
||||
return workspace
|
||||
.getSnapshot()
|
||||
.tasks.filter((task) => !repoId || task.repoId === repoId)
|
||||
.map((task) => ({
|
||||
|
|
@ -641,24 +641,24 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
return buildOrganizationSummary();
|
||||
},
|
||||
|
||||
async getTaskDetail(_organizationId: string, _repoId: string, taskId: string): Promise<WorkbenchTaskDetail> {
|
||||
async getTaskDetail(_organizationId: string, _repoId: string, taskId: string): Promise<WorkspaceTaskDetail> {
|
||||
return buildTaskDetail(requireTask(taskId));
|
||||
},
|
||||
|
||||
async getSessionDetail(_organizationId: string, _repoId: string, taskId: string, sessionId: string): Promise<WorkbenchSessionDetail> {
|
||||
async getSessionDetail(_organizationId: string, _repoId: string, taskId: string, sessionId: string): Promise<WorkspaceSessionDetail> {
|
||||
return buildSessionDetail(requireTask(taskId), sessionId);
|
||||
},
|
||||
|
||||
async getWorkbench(): Promise<TaskWorkbenchSnapshot> {
|
||||
return workbench.getSnapshot();
|
||||
async getWorkspace(): Promise<TaskWorkspaceSnapshot> {
|
||||
return workspace.getSnapshot();
|
||||
},
|
||||
|
||||
subscribeWorkbench(_organizationId: string, listener: () => void): () => void {
|
||||
return workbench.subscribe(listener);
|
||||
subscribeWorkspace(_organizationId: string, listener: () => void): () => void {
|
||||
return workspace.subscribe(listener);
|
||||
},
|
||||
|
||||
async createWorkbenchTask(_organizationId: string, input: TaskWorkbenchCreateTaskInput): Promise<TaskWorkbenchCreateTaskResponse> {
|
||||
const created = await workbench.createTask(input);
|
||||
async createWorkspaceTask(_organizationId: string, input: TaskWorkspaceCreateTaskInput): Promise<TaskWorkspaceCreateTaskResponse> {
|
||||
const created = await workspace.createTask(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(created.taskId);
|
||||
if (created.sessionId) {
|
||||
|
|
@ -667,99 +667,93 @@ export function createMockBackendClient(defaultOrganizationId = "default"): Back
|
|||
return created;
|
||||
},
|
||||
|
||||
async markWorkbenchUnread(_organizationId: string, input: TaskWorkbenchSelectInput): Promise<void> {
|
||||
await workbench.markTaskUnread(input);
|
||||
async markWorkspaceUnread(_organizationId: string, input: TaskWorkspaceSelectInput): Promise<void> {
|
||||
await workspace.markTaskUnread(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async renameWorkbenchTask(_organizationId: string, input: TaskWorkbenchRenameInput): Promise<void> {
|
||||
await workbench.renameTask(input);
|
||||
async renameWorkspaceTask(_organizationId: string, input: TaskWorkspaceRenameInput): Promise<void> {
|
||||
await workspace.renameTask(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async renameWorkbenchBranch(_organizationId: string, input: TaskWorkbenchRenameInput): Promise<void> {
|
||||
await workbench.renameBranch(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async createWorkbenchSession(_organizationId: string, input: TaskWorkbenchSelectInput & { model?: string }): Promise<{ sessionId: string }> {
|
||||
const created = await workbench.addSession(input);
|
||||
async createWorkspaceSession(_organizationId: string, input: TaskWorkspaceSelectInput & { model?: string }): Promise<{ sessionId: string }> {
|
||||
const created = await workspace.addSession(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, created.sessionId);
|
||||
return created;
|
||||
},
|
||||
|
||||
async renameWorkbenchSession(_organizationId: string, input: TaskWorkbenchRenameSessionInput): Promise<void> {
|
||||
await workbench.renameSession(input);
|
||||
async renameWorkspaceSession(_organizationId: string, input: TaskWorkspaceRenameSessionInput): Promise<void> {
|
||||
await workspace.renameSession(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async setWorkbenchSessionUnread(_organizationId: string, input: TaskWorkbenchSetSessionUnreadInput): Promise<void> {
|
||||
await workbench.setSessionUnread(input);
|
||||
async setWorkspaceSessionUnread(_organizationId: string, input: TaskWorkspaceSetSessionUnreadInput): Promise<void> {
|
||||
await workspace.setSessionUnread(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async updateWorkbenchDraft(_organizationId: string, input: TaskWorkbenchUpdateDraftInput): Promise<void> {
|
||||
await workbench.updateDraft(input);
|
||||
async updateWorkspaceDraft(_organizationId: string, input: TaskWorkspaceUpdateDraftInput): Promise<void> {
|
||||
await workspace.updateDraft(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async changeWorkbenchModel(_organizationId: string, input: TaskWorkbenchChangeModelInput): Promise<void> {
|
||||
await workbench.changeModel(input);
|
||||
async changeWorkspaceModel(_organizationId: string, input: TaskWorkspaceChangeModelInput): Promise<void> {
|
||||
await workspace.changeModel(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async sendWorkbenchMessage(_organizationId: string, input: TaskWorkbenchSendMessageInput): Promise<void> {
|
||||
await workbench.sendMessage(input);
|
||||
async sendWorkspaceMessage(_organizationId: string, input: TaskWorkspaceSendMessageInput): Promise<void> {
|
||||
await workspace.sendMessage(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async stopWorkbenchSession(_organizationId: string, input: TaskWorkbenchSessionInput): Promise<void> {
|
||||
await workbench.stopAgent(input);
|
||||
async stopWorkspaceSession(_organizationId: string, input: TaskWorkspaceSessionInput): Promise<void> {
|
||||
await workspace.stopAgent(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
emitSessionUpdate(input.taskId, input.sessionId);
|
||||
},
|
||||
|
||||
async closeWorkbenchSession(_organizationId: string, input: TaskWorkbenchSessionInput): Promise<void> {
|
||||
await workbench.closeSession(input);
|
||||
async closeWorkspaceSession(_organizationId: string, input: TaskWorkspaceSessionInput): Promise<void> {
|
||||
await workspace.closeSession(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async publishWorkbenchPr(_organizationId: string, input: TaskWorkbenchSelectInput): Promise<void> {
|
||||
await workbench.publishPr(input);
|
||||
async publishWorkspacePr(_organizationId: string, input: TaskWorkspaceSelectInput): Promise<void> {
|
||||
await workspace.publishPr(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async revertWorkbenchFile(_organizationId: string, input: TaskWorkbenchDiffInput): Promise<void> {
|
||||
await workbench.revertFile(input);
|
||||
async revertWorkspaceFile(_organizationId: string, input: TaskWorkspaceDiffInput): Promise<void> {
|
||||
await workspace.revertFile(input);
|
||||
emitOrganizationSnapshot();
|
||||
emitTaskUpdate(input.taskId);
|
||||
},
|
||||
|
||||
async reloadGithubOrganization(): Promise<void> {},
|
||||
async adminReloadGithubOrganization(): Promise<void> {},
|
||||
|
||||
async reloadGithubPullRequests(): Promise<void> {},
|
||||
async adminReloadGithubPullRequests(): Promise<void> {},
|
||||
|
||||
async reloadGithubRepository(): Promise<void> {},
|
||||
async adminReloadGithubRepository(): Promise<void> {},
|
||||
|
||||
async reloadGithubPullRequest(): Promise<void> {},
|
||||
async adminReloadGithubPullRequest(): Promise<void> {},
|
||||
|
||||
async health(): Promise<{ ok: true }> {
|
||||
return { ok: true };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue