mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-18 15:03:06 +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 };
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
import {
|
||||
MODEL_GROUPS,
|
||||
buildInitialMockLayoutViewModel,
|
||||
groupWorkbenchRepositories,
|
||||
groupWorkspaceRepositories,
|
||||
nowMs,
|
||||
providerAgent,
|
||||
randomReply,
|
||||
removeFileTreePath,
|
||||
slugify,
|
||||
uid,
|
||||
} from "../workbench-model.js";
|
||||
} from "../workspace-model.js";
|
||||
import type {
|
||||
TaskWorkbenchAddSessionResponse,
|
||||
TaskWorkbenchChangeModelInput,
|
||||
TaskWorkbenchCreateTaskInput,
|
||||
TaskWorkbenchCreateTaskResponse,
|
||||
TaskWorkbenchDiffInput,
|
||||
TaskWorkbenchRenameInput,
|
||||
TaskWorkbenchRenameSessionInput,
|
||||
TaskWorkbenchSelectInput,
|
||||
TaskWorkbenchSetSessionUnreadInput,
|
||||
TaskWorkbenchSendMessageInput,
|
||||
TaskWorkbenchSnapshot,
|
||||
TaskWorkbenchSessionInput,
|
||||
TaskWorkbenchUpdateDraftInput,
|
||||
WorkbenchSession as AgentSession,
|
||||
WorkbenchTask as Task,
|
||||
WorkbenchTranscriptEvent as TranscriptEvent,
|
||||
TaskWorkspaceAddSessionResponse,
|
||||
TaskWorkspaceChangeModelInput,
|
||||
TaskWorkspaceCreateTaskInput,
|
||||
TaskWorkspaceCreateTaskResponse,
|
||||
TaskWorkspaceDiffInput,
|
||||
TaskWorkspaceRenameInput,
|
||||
TaskWorkspaceRenameSessionInput,
|
||||
TaskWorkspaceSelectInput,
|
||||
TaskWorkspaceSetSessionUnreadInput,
|
||||
TaskWorkspaceSendMessageInput,
|
||||
TaskWorkspaceSnapshot,
|
||||
TaskWorkspaceSessionInput,
|
||||
TaskWorkspaceUpdateDraftInput,
|
||||
WorkspaceSession as AgentSession,
|
||||
WorkspaceTask as Task,
|
||||
WorkspaceTranscriptEvent as TranscriptEvent,
|
||||
} from "@sandbox-agent/foundry-shared";
|
||||
import type { TaskWorkbenchClient } from "../workbench-client.js";
|
||||
import type { TaskWorkspaceClient } from "../workspace-client.js";
|
||||
|
||||
function buildTranscriptEvent(params: {
|
||||
sessionId: string;
|
||||
|
|
@ -47,12 +47,12 @@ function buildTranscriptEvent(params: {
|
|||
};
|
||||
}
|
||||
|
||||
class MockWorkbenchStore implements TaskWorkbenchClient {
|
||||
class MockWorkspaceStore implements TaskWorkspaceClient {
|
||||
private snapshot = buildInitialMockLayoutViewModel();
|
||||
private listeners = new Set<() => void>();
|
||||
private pendingTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
getSnapshot(): TaskWorkbenchSnapshot {
|
||||
getSnapshot(): TaskWorkspaceSnapshot {
|
||||
return this.snapshot;
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
};
|
||||
}
|
||||
|
||||
async createTask(input: TaskWorkbenchCreateTaskInput): Promise<TaskWorkbenchCreateTaskResponse> {
|
||||
async createTask(input: TaskWorkspaceCreateTaskInput): Promise<TaskWorkspaceCreateTaskResponse> {
|
||||
const id = uid();
|
||||
const sessionId = `session-${id}`;
|
||||
const repo = this.snapshot.repos.find((candidate) => candidate.id === input.repoId);
|
||||
|
|
@ -109,7 +109,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
return { taskId: id, sessionId };
|
||||
}
|
||||
|
||||
async markTaskUnread(input: TaskWorkbenchSelectInput): Promise<void> {
|
||||
async markTaskUnread(input: TaskWorkspaceSelectInput): Promise<void> {
|
||||
this.updateTask(input.taskId, (task) => {
|
||||
const targetSession = task.sessions[task.sessions.length - 1] ?? null;
|
||||
if (!targetSession) {
|
||||
|
|
@ -123,7 +123,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
});
|
||||
}
|
||||
|
||||
async renameTask(input: TaskWorkbenchRenameInput): Promise<void> {
|
||||
async renameTask(input: TaskWorkspaceRenameInput): Promise<void> {
|
||||
const value = input.value.trim();
|
||||
if (!value) {
|
||||
throw new Error(`Cannot rename task ${input.taskId} to an empty title`);
|
||||
|
|
@ -131,19 +131,11 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
this.updateTask(input.taskId, (task) => ({ ...task, title: value, updatedAtMs: nowMs() }));
|
||||
}
|
||||
|
||||
async renameBranch(input: TaskWorkbenchRenameInput): Promise<void> {
|
||||
const value = input.value.trim();
|
||||
if (!value) {
|
||||
throw new Error(`Cannot rename branch for task ${input.taskId} to an empty value`);
|
||||
}
|
||||
this.updateTask(input.taskId, (task) => ({ ...task, branch: value, updatedAtMs: nowMs() }));
|
||||
}
|
||||
|
||||
async archiveTask(input: TaskWorkbenchSelectInput): Promise<void> {
|
||||
async archiveTask(input: TaskWorkspaceSelectInput): Promise<void> {
|
||||
this.updateTask(input.taskId, (task) => ({ ...task, status: "archived", updatedAtMs: nowMs() }));
|
||||
}
|
||||
|
||||
async publishPr(input: TaskWorkbenchSelectInput): Promise<void> {
|
||||
async publishPr(input: TaskWorkspaceSelectInput): Promise<void> {
|
||||
const nextPrNumber = Math.max(0, ...this.snapshot.tasks.map((task) => task.pullRequest?.number ?? 0)) + 1;
|
||||
this.updateTask(input.taskId, (task) => ({
|
||||
...task,
|
||||
|
|
@ -152,7 +144,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
}));
|
||||
}
|
||||
|
||||
async revertFile(input: TaskWorkbenchDiffInput): Promise<void> {
|
||||
async revertFile(input: TaskWorkspaceDiffInput): Promise<void> {
|
||||
this.updateTask(input.taskId, (task) => {
|
||||
const file = task.fileChanges.find((entry) => entry.path === input.path);
|
||||
const nextDiffs = { ...task.diffs };
|
||||
|
|
@ -167,7 +159,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
});
|
||||
}
|
||||
|
||||
async updateDraft(input: TaskWorkbenchUpdateDraftInput): Promise<void> {
|
||||
async updateDraft(input: TaskWorkspaceUpdateDraftInput): Promise<void> {
|
||||
this.assertSession(input.taskId, input.sessionId);
|
||||
this.updateTask(input.taskId, (task) => ({
|
||||
...task,
|
||||
|
|
@ -187,7 +179,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
}));
|
||||
}
|
||||
|
||||
async sendMessage(input: TaskWorkbenchSendMessageInput): Promise<void> {
|
||||
async sendMessage(input: TaskWorkspaceSendMessageInput): Promise<void> {
|
||||
const text = input.text.trim();
|
||||
if (!text) {
|
||||
throw new Error(`Cannot send an empty mock prompt for task ${input.taskId}`);
|
||||
|
|
@ -288,7 +280,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
this.pendingTimers.set(input.sessionId, timer);
|
||||
}
|
||||
|
||||
async stopAgent(input: TaskWorkbenchSessionInput): Promise<void> {
|
||||
async stopAgent(input: TaskWorkspaceSessionInput): Promise<void> {
|
||||
this.assertSession(input.taskId, input.sessionId);
|
||||
const existing = this.pendingTimers.get(input.sessionId);
|
||||
if (existing) {
|
||||
|
|
@ -311,14 +303,14 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
});
|
||||
}
|
||||
|
||||
async setSessionUnread(input: TaskWorkbenchSetSessionUnreadInput): Promise<void> {
|
||||
async setSessionUnread(input: TaskWorkspaceSetSessionUnreadInput): Promise<void> {
|
||||
this.updateTask(input.taskId, (currentTask) => ({
|
||||
...currentTask,
|
||||
sessions: currentTask.sessions.map((candidate) => (candidate.id === input.sessionId ? { ...candidate, unread: input.unread } : candidate)),
|
||||
}));
|
||||
}
|
||||
|
||||
async renameSession(input: TaskWorkbenchRenameSessionInput): Promise<void> {
|
||||
async renameSession(input: TaskWorkspaceRenameSessionInput): Promise<void> {
|
||||
const title = input.title.trim();
|
||||
if (!title) {
|
||||
throw new Error(`Cannot rename session ${input.sessionId} to an empty title`);
|
||||
|
|
@ -329,7 +321,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
}));
|
||||
}
|
||||
|
||||
async closeSession(input: TaskWorkbenchSessionInput): Promise<void> {
|
||||
async closeSession(input: TaskWorkspaceSessionInput): Promise<void> {
|
||||
this.updateTask(input.taskId, (currentTask) => {
|
||||
if (currentTask.sessions.length <= 1) {
|
||||
return currentTask;
|
||||
|
|
@ -342,7 +334,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
});
|
||||
}
|
||||
|
||||
async addSession(input: TaskWorkbenchSelectInput): Promise<TaskWorkbenchAddSessionResponse> {
|
||||
async addSession(input: TaskWorkspaceSelectInput): Promise<TaskWorkspaceAddSessionResponse> {
|
||||
this.assertTask(input.taskId);
|
||||
const nextSessionId = uid();
|
||||
const nextSession: AgentSession = {
|
||||
|
|
@ -368,7 +360,7 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
return { sessionId: nextSession.id };
|
||||
}
|
||||
|
||||
async changeModel(input: TaskWorkbenchChangeModelInput): Promise<void> {
|
||||
async changeModel(input: TaskWorkspaceChangeModelInput): Promise<void> {
|
||||
const group = MODEL_GROUPS.find((candidate) => candidate.models.some((entry) => entry.id === input.model));
|
||||
if (!group) {
|
||||
throw new Error(`Unable to resolve model provider for ${input.model}`);
|
||||
|
|
@ -382,11 +374,11 @@ class MockWorkbenchStore implements TaskWorkbenchClient {
|
|||
}));
|
||||
}
|
||||
|
||||
private updateState(updater: (current: TaskWorkbenchSnapshot) => TaskWorkbenchSnapshot): void {
|
||||
private updateState(updater: (current: TaskWorkspaceSnapshot) => TaskWorkspaceSnapshot): void {
|
||||
const nextSnapshot = updater(this.snapshot);
|
||||
this.snapshot = {
|
||||
...nextSnapshot,
|
||||
repositories: groupWorkbenchRepositories(nextSnapshot.repos, nextSnapshot.tasks),
|
||||
repositories: groupWorkspaceRepositories(nextSnapshot.repos, nextSnapshot.tasks),
|
||||
};
|
||||
this.notify();
|
||||
}
|
||||
|
|
@ -436,11 +428,11 @@ function candidateEventIndex(task: Task, sessionId: string): number {
|
|||
return (session?.transcript.length ?? 0) + 1;
|
||||
}
|
||||
|
||||
let sharedMockWorkbenchClient: TaskWorkbenchClient | null = null;
|
||||
let sharedMockWorkspaceClient: TaskWorkspaceClient | null = null;
|
||||
|
||||
export function getSharedMockWorkbenchClient(): TaskWorkbenchClient {
|
||||
if (!sharedMockWorkbenchClient) {
|
||||
sharedMockWorkbenchClient = new MockWorkbenchStore();
|
||||
export function getSharedMockWorkspaceClient(): TaskWorkspaceClient {
|
||||
if (!sharedMockWorkspaceClient) {
|
||||
sharedMockWorkspaceClient = new MockWorkspaceStore();
|
||||
}
|
||||
return sharedMockWorkbenchClient;
|
||||
return sharedMockWorkspaceClient;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue