feat(foundry): checkpoint actor and workspace refactor

This commit is contained in:
Nathan Flurry 2026-03-15 10:20:27 -07:00
parent 32f3c6c3bc
commit dbe57d45b9
81 changed files with 3441 additions and 2332 deletions

View file

@ -1,4 +1,4 @@
import type { WorkbenchModelId } from "./workbench.js";
import type { WorkspaceModelId } from "./workspace.js";
export type FoundryBillingPlanId = "free" | "team";
export type FoundryBillingStatus = "active" | "trialing" | "past_due" | "scheduled_cancel";
@ -14,6 +14,7 @@ export interface FoundryUser {
githubLogin: string;
roleLabel: string;
eligibleOrganizationIds: string[];
defaultModel: WorkspaceModelId;
}
export interface FoundryOrganizationMember {
@ -59,7 +60,6 @@ export interface FoundryOrganizationSettings {
slug: string;
primaryDomain: string;
seatAccrualMode: "first_prompt";
defaultModel: WorkbenchModelId;
autoImportRepos: boolean;
}

View file

@ -54,7 +54,6 @@ export const CreateTaskInputSchema = z.object({
explicitTitle: z.string().trim().min(1).optional(),
explicitBranchName: z.string().trim().min(1).optional(),
sandboxProviderId: SandboxProviderIdSchema.optional(),
agentType: AgentTypeSchema.optional(),
onBranch: z.string().trim().min(1).optional(),
});
export type CreateTaskInput = z.infer<typeof CreateTaskInputSchema>;
@ -69,9 +68,7 @@ export const TaskRecordSchema = z.object({
task: z.string().min(1),
sandboxProviderId: SandboxProviderIdSchema,
status: TaskStatusSchema,
statusMessage: z.string().nullable(),
activeSandboxId: z.string().nullable(),
activeSessionId: z.string().nullable(),
sandboxes: z.array(
z.object({
sandboxId: z.string().min(1),
@ -83,17 +80,12 @@ export const TaskRecordSchema = z.object({
updatedAt: z.number().int(),
}),
),
agentType: z.string().nullable(),
prSubmitted: z.boolean(),
diffStat: z.string().nullable(),
prUrl: z.string().nullable(),
prAuthor: z.string().nullable(),
ciStatus: z.string().nullable(),
reviewStatus: z.string().nullable(),
reviewer: z.string().nullable(),
conflictsWithMain: z.string().nullable(),
hasUnpushed: z.string().nullable(),
parentBranch: z.string().nullable(),
createdAt: z.number().int(),
updatedAt: z.number().int(),
});
@ -112,6 +104,7 @@ export type TaskSummary = z.infer<typeof TaskSummarySchema>;
export const TaskActionInputSchema = z.object({
organizationId: OrganizationIdSchema,
repoId: RepoIdSchema,
taskId: z.string().min(1),
});
export type TaskActionInput = z.infer<typeof TaskActionInputSchema>;
@ -180,7 +173,7 @@ export const HistoryQueryInputSchema = z.object({
});
export type HistoryQueryInput = z.infer<typeof HistoryQueryInputSchema>;
export const HistoryEventSchema = z.object({
export const AuditLogEventSchema = z.object({
id: z.number().int(),
organizationId: OrganizationIdSchema,
repoId: z.string().nullable(),
@ -190,7 +183,7 @@ export const HistoryEventSchema = z.object({
payloadJson: z.string().min(1),
createdAt: z.number().int(),
});
export type HistoryEvent = z.infer<typeof HistoryEventSchema>;
export type AuditLogEvent = z.infer<typeof AuditLogEventSchema>;
export const PruneInputSchema = z.object({
organizationId: OrganizationIdSchema,
@ -201,6 +194,7 @@ export type PruneInput = z.infer<typeof PruneInputSchema>;
export const KillInputSchema = z.object({
organizationId: OrganizationIdSchema,
repoId: RepoIdSchema,
taskId: z.string().min(1),
deleteBranch: z.boolean(),
abandon: z.boolean(),

View file

@ -3,5 +3,5 @@ export * from "./contracts.js";
export * from "./config.js";
export * from "./logging.js";
export * from "./realtime-events.js";
export * from "./workbench.js";
export * from "./workspace.js";
export * from "./organization.js";

View file

@ -1,5 +1,5 @@
import type { FoundryAppSnapshot } from "./app-shell.js";
import type { WorkbenchOpenPrSummary, WorkbenchRepositorySummary, WorkbenchSessionDetail, WorkbenchTaskDetail, WorkbenchTaskSummary } from "./workbench.js";
import type { OrganizationSummarySnapshot, WorkspaceSessionDetail, WorkspaceTaskDetail } from "./workspace.js";
export interface SandboxProcessSnapshot {
id: string;
@ -16,20 +16,13 @@ export interface SandboxProcessSnapshot {
}
/** Organization-level events broadcast by the organization actor. */
export type OrganizationEvent =
| { type: "taskSummaryUpdated"; taskSummary: WorkbenchTaskSummary }
| { type: "taskRemoved"; taskId: string }
| { type: "repoAdded"; repo: WorkbenchRepositorySummary }
| { type: "repoUpdated"; repo: WorkbenchRepositorySummary }
| { type: "repoRemoved"; repoId: string }
| { type: "pullRequestUpdated"; pullRequest: WorkbenchOpenPrSummary }
| { type: "pullRequestRemoved"; prId: string };
export type OrganizationEvent = { type: "organizationUpdated"; snapshot: OrganizationSummarySnapshot };
/** Task-level events broadcast by the task actor. */
export type TaskEvent = { type: "taskDetailUpdated"; detail: WorkbenchTaskDetail };
export type TaskEvent = { type: "taskUpdated"; detail: WorkspaceTaskDetail };
/** Session-level events broadcast by the task actor and filtered by sessionId on the client. */
export type SessionEvent = { type: "sessionUpdated"; session: WorkbenchSessionDetail };
export type SessionEvent = { type: "sessionUpdated"; session: WorkspaceSessionDetail };
/** App-level events broadcast by the app organization actor. */
export type AppEvent = { type: "appUpdated"; snapshot: FoundryAppSnapshot };

View file

@ -1,8 +1,8 @@
import type { AgentType, SandboxProviderId, TaskStatus } from "./contracts.js";
import type { SandboxProviderId, TaskStatus } from "./contracts.js";
export type WorkbenchTaskStatus = TaskStatus | "new";
export type WorkbenchAgentKind = "Claude" | "Codex" | "Cursor";
export type WorkbenchModelId =
export type WorkspaceTaskStatus = TaskStatus | "new";
export type WorkspaceAgentKind = "Claude" | "Codex" | "Cursor";
export type WorkspaceModelId =
| "claude-sonnet-4"
| "claude-opus-4"
| "gpt-5.3-codex"
@ -11,9 +11,9 @@ export type WorkbenchModelId =
| "gpt-5.1-codex-max"
| "gpt-5.2"
| "gpt-5.1-codex-mini";
export type WorkbenchSessionStatus = "pending_provision" | "pending_session_create" | "ready" | "running" | "idle" | "error";
export type WorkspaceSessionStatus = "pending_provision" | "pending_session_create" | "ready" | "running" | "idle" | "error";
export interface WorkbenchTranscriptEvent {
export interface WorkspaceTranscriptEvent {
id: string;
eventIndex: number;
sessionId: string;
@ -23,23 +23,23 @@ export interface WorkbenchTranscriptEvent {
payload: unknown;
}
export interface WorkbenchComposerDraft {
export interface WorkspaceComposerDraft {
text: string;
attachments: WorkbenchLineAttachment[];
attachments: WorkspaceLineAttachment[];
updatedAtMs: number | null;
}
/** Session metadata without transcript content. */
export interface WorkbenchSessionSummary {
export interface WorkspaceSessionSummary {
id: string;
/** Stable UI session id used for routing and task-local identity. */
sessionId: string;
/** Underlying sandbox session id when provisioning has completed. */
sandboxSessionId?: string | null;
sessionName: string;
agent: WorkbenchAgentKind;
model: WorkbenchModelId;
status: WorkbenchSessionStatus;
agent: WorkspaceAgentKind;
model: WorkspaceModelId;
status: WorkspaceSessionStatus;
thinkingSinceMs: number | null;
unread: boolean;
created: boolean;
@ -47,44 +47,44 @@ export interface WorkbenchSessionSummary {
}
/** Full session content — only fetched when viewing a specific session. */
export interface WorkbenchSessionDetail {
export interface WorkspaceSessionDetail {
/** Stable UI session id used for the session topic key and routing. */
sessionId: string;
sandboxSessionId: string | null;
sessionName: string;
agent: WorkbenchAgentKind;
model: WorkbenchModelId;
status: WorkbenchSessionStatus;
agent: WorkspaceAgentKind;
model: WorkspaceModelId;
status: WorkspaceSessionStatus;
thinkingSinceMs: number | null;
unread: boolean;
created: boolean;
errorMessage?: string | null;
draft: WorkbenchComposerDraft;
transcript: WorkbenchTranscriptEvent[];
draft: WorkspaceComposerDraft;
transcript: WorkspaceTranscriptEvent[];
}
export interface WorkbenchFileChange {
export interface WorkspaceFileChange {
path: string;
added: number;
removed: number;
type: "M" | "A" | "D";
}
export interface WorkbenchFileTreeNode {
export interface WorkspaceFileTreeNode {
name: string;
path: string;
isDir: boolean;
children?: WorkbenchFileTreeNode[];
children?: WorkspaceFileTreeNode[];
}
export interface WorkbenchLineAttachment {
export interface WorkspaceLineAttachment {
id: string;
filePath: string;
lineNumber: number;
lineContent: string;
}
export interface WorkbenchHistoryEvent {
export interface WorkspaceHistoryEvent {
id: string;
messageId: string;
preview: string;
@ -94,78 +94,67 @@ export interface WorkbenchHistoryEvent {
detail: string;
}
export type WorkbenchDiffLineKind = "context" | "add" | "remove" | "hunk";
export type WorkspaceDiffLineKind = "context" | "add" | "remove" | "hunk";
export interface WorkbenchParsedDiffLine {
kind: WorkbenchDiffLineKind;
export interface WorkspaceParsedDiffLine {
kind: WorkspaceDiffLineKind;
lineNumber: number;
text: string;
}
export interface WorkbenchPullRequestSummary {
number: number;
status: "draft" | "ready";
}
export interface WorkbenchOpenPrSummary {
prId: string;
repoId: string;
repoFullName: string;
export interface WorkspacePullRequestSummary {
number: number;
title: string;
state: string;
url: string;
headRefName: string;
baseRefName: string;
repoFullName: string;
authorLogin: string | null;
isDraft: boolean;
updatedAtMs: number;
}
export interface WorkbenchSandboxSummary {
export interface WorkspaceSandboxSummary {
sandboxProviderId: SandboxProviderId;
sandboxId: string;
cwd: string | null;
}
/** Sidebar-level task data. Materialized in the organization actor's SQLite. */
export interface WorkbenchTaskSummary {
export interface WorkspaceTaskSummary {
id: string;
repoId: string;
title: string;
status: WorkbenchTaskStatus;
status: WorkspaceTaskStatus;
repoName: string;
updatedAtMs: number;
branch: string | null;
pullRequest: WorkbenchPullRequestSummary | null;
pullRequest: WorkspacePullRequestSummary | null;
/** Summary of sessions — no transcript content. */
sessionsSummary: WorkbenchSessionSummary[];
sessionsSummary: WorkspaceSessionSummary[];
}
/** Full task detail — only fetched when viewing a specific task. */
export interface WorkbenchTaskDetail extends WorkbenchTaskSummary {
export interface WorkspaceTaskDetail extends WorkspaceTaskSummary {
/** Original task prompt/instructions shown in the detail view. */
task: string;
/** Agent choice used when creating new sandbox sessions for this task. */
agentType: AgentType | null;
/** Underlying task runtime status preserved for detail views and error handling. */
runtimeStatus: TaskStatus;
statusMessage: string | null;
activeSessionId: string | null;
diffStat: string | null;
prUrl: string | null;
reviewStatus: string | null;
fileChanges: WorkbenchFileChange[];
fileChanges: WorkspaceFileChange[];
diffs: Record<string, string>;
fileTree: WorkbenchFileTreeNode[];
fileTree: WorkspaceFileTreeNode[];
minutesUsed: number;
/** Sandbox info for this task. */
sandboxes: WorkbenchSandboxSummary[];
sandboxes: WorkspaceSandboxSummary[];
activeSandboxId: string | null;
}
/** Repo-level summary for organization sidebar. */
export interface WorkbenchRepositorySummary {
export interface WorkspaceRepositorySummary {
id: string;
label: string;
/** Aggregated branch/task overview state (replaces getRepoOverview polling). */
@ -176,121 +165,126 @@ export interface WorkbenchRepositorySummary {
/** Organization-level snapshot — initial fetch for the organization topic. */
export interface OrganizationSummarySnapshot {
organizationId: string;
repos: WorkbenchRepositorySummary[];
taskSummaries: WorkbenchTaskSummary[];
openPullRequests: WorkbenchOpenPrSummary[];
repos: WorkspaceRepositorySummary[];
taskSummaries: WorkspaceTaskSummary[];
}
export interface WorkbenchSession extends WorkbenchSessionSummary {
draft: WorkbenchComposerDraft;
transcript: WorkbenchTranscriptEvent[];
export interface WorkspaceSession extends WorkspaceSessionSummary {
draft: WorkspaceComposerDraft;
transcript: WorkspaceTranscriptEvent[];
}
export interface WorkbenchTask {
export interface WorkspaceTask {
id: string;
repoId: string;
title: string;
status: WorkbenchTaskStatus;
status: WorkspaceTaskStatus;
runtimeStatus?: TaskStatus;
statusMessage?: string | null;
repoName: string;
updatedAtMs: number;
branch: string | null;
pullRequest: WorkbenchPullRequestSummary | null;
sessions: WorkbenchSession[];
fileChanges: WorkbenchFileChange[];
pullRequest: WorkspacePullRequestSummary | null;
sessions: WorkspaceSession[];
fileChanges: WorkspaceFileChange[];
diffs: Record<string, string>;
fileTree: WorkbenchFileTreeNode[];
fileTree: WorkspaceFileTreeNode[];
minutesUsed: number;
activeSandboxId?: string | null;
}
export interface WorkbenchRepo {
export interface WorkspaceRepo {
id: string;
label: string;
}
export interface WorkbenchRepositorySection {
export interface WorkspaceRepositorySection {
id: string;
label: string;
updatedAtMs: number;
tasks: WorkbenchTask[];
tasks: WorkspaceTask[];
}
export interface TaskWorkbenchSnapshot {
export interface TaskWorkspaceSnapshot {
organizationId: string;
repos: WorkbenchRepo[];
repositories: WorkbenchRepositorySection[];
tasks: WorkbenchTask[];
repos: WorkspaceRepo[];
repositories: WorkspaceRepositorySection[];
tasks: WorkspaceTask[];
}
export interface WorkbenchModelOption {
id: WorkbenchModelId;
export interface WorkspaceModelOption {
id: WorkspaceModelId;
label: string;
}
export interface WorkbenchModelGroup {
export interface WorkspaceModelGroup {
provider: string;
models: WorkbenchModelOption[];
models: WorkspaceModelOption[];
}
export interface TaskWorkbenchSelectInput {
export interface TaskWorkspaceSelectInput {
repoId: string;
taskId: string;
authSessionId?: string;
}
export interface TaskWorkbenchCreateTaskInput {
export interface TaskWorkspaceCreateTaskInput {
repoId: string;
task: string;
title?: string;
branch?: string;
onBranch?: string;
model?: WorkbenchModelId;
model?: WorkspaceModelId;
authSessionId?: string;
}
export interface TaskWorkbenchRenameInput {
export interface TaskWorkspaceRenameInput {
repoId: string;
taskId: string;
value: string;
}
export interface TaskWorkbenchSendMessageInput {
export interface TaskWorkspaceSendMessageInput {
taskId: string;
sessionId: string;
text: string;
attachments: WorkbenchLineAttachment[];
attachments: WorkspaceLineAttachment[];
authSessionId?: string;
}
export interface TaskWorkbenchSessionInput {
export interface TaskWorkspaceSessionInput {
taskId: string;
sessionId: string;
authSessionId?: string;
}
export interface TaskWorkbenchRenameSessionInput extends TaskWorkbenchSessionInput {
export interface TaskWorkspaceRenameSessionInput extends TaskWorkspaceSessionInput {
title: string;
}
export interface TaskWorkbenchChangeModelInput extends TaskWorkbenchSessionInput {
model: WorkbenchModelId;
export interface TaskWorkspaceChangeModelInput extends TaskWorkspaceSessionInput {
model: WorkspaceModelId;
}
export interface TaskWorkbenchUpdateDraftInput extends TaskWorkbenchSessionInput {
export interface TaskWorkspaceUpdateDraftInput extends TaskWorkspaceSessionInput {
text: string;
attachments: WorkbenchLineAttachment[];
attachments: WorkspaceLineAttachment[];
}
export interface TaskWorkbenchSetSessionUnreadInput extends TaskWorkbenchSessionInput {
export interface TaskWorkspaceSetSessionUnreadInput extends TaskWorkspaceSessionInput {
unread: boolean;
}
export interface TaskWorkbenchDiffInput {
export interface TaskWorkspaceDiffInput {
repoId: string;
taskId: string;
path: string;
}
export interface TaskWorkbenchCreateTaskResponse {
export interface TaskWorkspaceCreateTaskResponse {
taskId: string;
sessionId?: string;
}
export interface TaskWorkbenchAddSessionResponse {
export interface TaskWorkspaceAddSessionResponse {
sessionId: string;
}