Adapt heartbeat/task-runner/turn-context to lockMode=none first-class path

- heartbeat.ts and task-runner.ts now share a single ResolveWorkspaceFn
  signature exported from run-dispatch.ts. Callers handle
  WorkspaceConfigurationError explicitly:
    * heartbeat: release the task lease and skip with new
      workspace_unresolved reason (task re-enters pool next heartbeat)
    * task-runner: release the lease and return WORKSPACE_UNRESOLVED
      to the caller
  No silent fallback to a random shared workspace.
- crash-recovery.ts imports WorkspaceConfigurationError for clean
  catch-branch attribution; the outer try already rolls back the
  queued run on any resolve failure.
- run-dispatch.ts exposes ResolveWorkspaceFn so downstream modules
  type their injected resolver against the same shape (single source
  of truth).
- HeartbeatSkipReason gains workspace_unresolved to keep the closed
  enum honest about all heartbeat exit paths.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-20 15:32:55 -04:00
1 parent 28cdf7c56f
commit 2dd62e5338
4 files changed
+71 -15

No files matched your search

+18 -8
View File
@@ -1,10 +1,25 @@
import { getConnectorExecutionReadinessIssue } from "@shared/connector-policy";
import type { ConnectorRecord } from "@shared/types";
import type { AgentRecord, ConnectorRecord, DepartmentId, TaskRecord } from "@shared/types";
import type { OrchestrationDependencies } from "./types";
import { buildRunPayload, type RunDispatchParams } from "./turn-context";
import { validateConnectorModelSelection } from "../connector-model-policy";
import { resolveWorkspaceWithLockMode } from "./resolve-workspace";
type ResolveWorkspaceTaskInput =
| (Pick<TaskRecord, "workspaceId" | "projectId"> & { requiresFilesystem?: boolean })
| null;
type ResolveWorkspaceAgentInput = Pick<AgentRecord, "workspaceId"> & {
role?: string | null;
department?: DepartmentId | null;
};
export type ResolveWorkspaceFn = (
task: ResolveWorkspaceTaskInput,
agent: ResolveWorkspaceAgentInput,
companyId: string,
) => ReturnType<typeof resolveWorkspaceWithLockMode>;
export function createRunDispatch(deps: OrchestrationDependencies) {
const {
db,
@@ -20,13 +35,8 @@ export function createRunDispatch(deps: OrchestrationDependencies) {
evaluatePreRunSessionRotation,
} = deps;
function resolveWorkspace(
task: { workspaceId: string | null; projectId?: string | null } | null,
agent: { workspaceId: string | null },
companyId: string,
) {
return resolveWorkspaceWithLockMode(db, task, agent, companyId);
}
const resolveWorkspace: ResolveWorkspaceFn = (task, agent, companyId) =>
resolveWorkspaceWithLockMode(db, task, agent, companyId);
function getRunEligibilityFailure(
agent: { status: string; budgetMonthlyUsd: number; spentMonthlyUsd: number; name: string; connectorId: string; id: string; companyId: string; model?: string | null },