import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { WorkspaceMigration } from "./types.js"; /** * Bump stale `timeouts.providerStreamTimeoutSec: 300` to 1800. * * The schema default was raised from 300s (5 min) to 1800s (30 min) in * PR #22702, but users whose workspace config was written before that change * still carry an explicit 300 that overrides the new default, causing streams * to abort after 5 minutes with "Anthropic stream timed out after 300s". */ export const bumpStaleProviderStreamTimeoutMigration: WorkspaceMigration = { id: "044-bump-stale-provider-stream-timeout", description: "Bump legacy timeouts.providerStreamTimeoutSec: 300 to 1800 to match the current schema default", run(workspaceDir: string): void { const configPath = join(workspaceDir, "config.json"); if (!existsSync(configPath)) { return; } let config: Record; try { const raw = JSON.parse(readFileSync(configPath, "utf-8")); if (!raw || typeof raw !== "object" || Array.isArray(raw)) { return; } config = raw as Record; } catch { return; } const timeouts = config.timeouts; if (!timeouts || typeof timeouts !== "object" || Array.isArray(timeouts)) { return; } const timeoutsObj = timeouts as Record; if (timeoutsObj.providerStreamTimeoutSec !== 300) { return; } timeoutsObj.providerStreamTimeoutSec = 1800; writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n"); }, down(_workspaceDir: string): void { // Forward-only: the runner marks migrations as applied even when `run()` // was a no-op (e.g. workspaces that already had 1800 from the new schema // default, or that never had the key at all). A `down()` that blindly // rewrote 1800 → 300 would silently downgrade those workspaces and // resurrect the "stream timed out after 300s" bug this migration fixes. // We can't distinguish "this migration set 1800" from "the value was // always 1800" without an extra state marker, so we treat the bump as // irreversible. Users who genuinely want 300s can set it in config. }, };