refactor(session): drop dead task-scoped session columns (v41)

Sessions have been agent-scoped since v34, but the schema kept four
dead task-scoped columns that every session code path had to pretend
were meaningful:

  - tasks.active_session_id     (mirrored agents.session_id)
  - tasks.session_json          (superseded by task_sessions.session_params_json)
  - tasks.session_display_id    (superseded by task_sessions.session_display_id)
  - task_sessions.task_id       (always empty-string on new rows)

v41 drops all four via SQLite table rebuild — the only safe path when
multiple columns need to go in one step and indexes / FK directions
need to be preserved. `PRAGMA foreign_keys = OFF` spans the swap
window so cascade-delete does not fire mid-rebuild. The migration is
idempotent and also backfills the optional task columns
(task_type / requires_user_review / retry_count / last_failure_at /
started_at / completed_at / cancelled_at / checkout_run_id /
execution_locked_at) before rebuilding so it works whether or not
`ensureOptionalColumns` has run yet on this install.

Along with the schema change, every code path that read or wrote
those columns has been rewired to the agent-scoped equivalents:

- task-sessions-crud.ts: deleted updateTaskSession,
  getTaskSession, getActiveTaskSession, updateTasksActiveSessionForAgent;
  added updateSessionParams(sessionId, ...) for the worker's
  finalizer. ensureAgentSession and createNewSession no longer touch
  task_id.
- database-base.ts: facade drops the now-unused pass-throughs.
- snapshot.ts / task-queries.ts / tasks-status.ts / entity-crud.ts:
  SELECT projections no longer pull session_json / session_display_id
  / active_session_id from tasks.
- tasks-crud.ts (TaskRow) and shared/types/records-core.ts
  (TaskRecord): sessionJson / sessionDisplayId fields removed.
- TaskSessionRecord: taskId field removed.
- turn-context.ts::resolveSession: no more
  updateTasksActiveSessionForAgent mirror; sessionParams read directly
  from the current session via getSessionById.
- run-finished-helpers.ts::finalizeRun: stamps sessionParams via
  updateSessionParams on the agent's current session, resolved through
  agents.session_id.
- session-management-service.ts: rotateSession stops calling
  updateTasksActiveSessionForAgent.

Test suite updated to use the new session-id API. Existing tests used
`getActiveTaskSession(taskId)` and `createNewSession({ taskId, ...})`
as probes; they now use `getSessionById(sessionId)` and the taskId-less
createNewSession input.

Tests: 603/603 green. Typecheck clean. Migration tested on fresh DB
(runs v1→v41 cleanly) and idempotent re-apply (detects absent dead
columns, skips).
This commit is contained in:
ZacharyZhang-NY committed 2026-04-18 21:54:31 -04:00
1 parent 6abc1a823f
commit 2ace9183b0
17 files changed
+270 -243

No files matched your search

+10 -12
View File
@@ -154,9 +154,6 @@ function resolveSession(
companyId,
adapterType: connectorId,
});
// Mirror the agent's active session onto the task pointer so UI paths
// that read per-task active_session_id keep working.
db.updateTasksActiveSessionForAgent(agent.id, agentSessionId);
// Evaluate rotation against the agent-scoped session unconditionally.
// Heartbeat runs (no task) must rotate too, or their transcript grows
@@ -175,18 +172,19 @@ function resolveSession(
}
// `sessionParams` / `sessionDisplayId` are transient per-run state pi-ai
// stamps onto the task row after a turn (e.g., Gemini's resume id). They
// are only meaningful when a task's cached pointer still matches the
// current (unrotated) session; after rotation the new session has none.
// stamps onto the session row after a turn (e.g., Gemini's resume id).
// They live on the agent session; read directly by session id.
let sessionParams: Record<string, unknown> | null = null;
let sessionDisplayId: string | null = null;
if (task) {
const active = db.getActiveTaskSession(task.id);
if (active && active.id === currentSessionId && active.sessionParamsJson) {
sessionParams = JSON.parse(active.sessionParamsJson) as Record<string, unknown>;
sessionDisplayId = active.sessionDisplayId;
}
const currentSession = db.getSessionById(currentSessionId);
if (currentSession?.sessionParamsJson) {
sessionParams = JSON.parse(currentSession.sessionParamsJson) as Record<string, unknown>;
sessionDisplayId = currentSession.sessionDisplayId;
}
// Suppress the unused-parameter warning while keeping the signature
// stable — task identity is reported through other fields on the
// payload, not through the session row anymore.
void task;
return {
taskSession: { id: currentSessionId, sessionParams, sessionDisplayId },