diff --git a/packages/pi-coding-agent/src/cli/args.test.ts b/packages/pi-coding-agent/src/cli/args.test.ts index 956327d80..23c10a142 100644 --- a/packages/pi-coding-agent/src/cli/args.test.ts +++ b/packages/pi-coding-agent/src/cli/args.test.ts @@ -6,22 +6,16 @@ describe("parseArgs", () => { it("parses optional-value extension flags with implicit and explicit values", () => { const extensionFlags = new Map([ ["genai-proxy", { type: "string" as const, allowNoValue: true }], - ["gemini-cli-proxy", { type: "string" as const, allowNoValue: true }], ]); const defaultFlagArgs = parseArgs(["--genai-proxy"], extensionFlags); const explicitFlagArgs = parseArgs(["--genai-proxy=8080"], extensionFlags); - const legacyFlagArgs = parseArgs( - ["--gemini-cli-proxy=3001"], - extensionFlags, - ); assert.deepEqual( [ defaultFlagArgs.unknownFlags.get("genai-proxy"), explicitFlagArgs.unknownFlags.get("genai-proxy"), - legacyFlagArgs.unknownFlags.get("gemini-cli-proxy"), ], - [true, "8080", "3001"], + [true, "8080"], ); }); }); diff --git a/src/cli.ts b/src/cli.ts index b3a2ce3e5..ba38fd9f7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -588,9 +588,7 @@ async function runHeadlessFromAutonomous( } // `sf autonomous [args...]` — shorthand for headless autonomous mode (#2732). -// The legacy `sf auto` spelling is still accepted for compatibility, but all -// generated prompts use `/sf autonomous`. -if (cliFlags.messages[0] === "auto" || cliFlags.messages[0] === "autonomous") { +if (cliFlags.messages[0] === "autonomous") { await runHeadlessFromAutonomous([ "autonomous", ...cliFlags.messages.slice(1), @@ -878,10 +876,7 @@ if (!cliFlags.worktree && !isPrintMode) { // the TUI cannot render and the process hangs. Redirect to headless mode // which handles non-interactive output gracefully. // --------------------------------------------------------------------------- -if ( - (cliFlags.messages[0] === "auto" || cliFlags.messages[0] === "autonomous") && - !process.stdout.isTTY -) { +if (cliFlags.messages[0] === "autonomous" && !process.stdout.isTTY) { process.stderr.write( "[forge] stdout is not a terminal — running autonomous mode in headless mode.\n", ); diff --git a/src/headless.ts b/src/headless.ts index 21a1436d7..418c3c2e8 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -382,7 +382,7 @@ export function parseHeadlessArgs(argv: string[]): HeadlessOptions { options.bare = true; } } else if (!commandSeen) { - if (arg === "autonomous" || arg === "auto") { + if (arg === "autonomous") { options.command = "autonomous"; options.auto = true; // autonomous subcommand implies --auto } else { diff --git a/src/resources/extensions/genai-proxy/proxy-command.js b/src/resources/extensions/genai-proxy/proxy-command.js index b399d8753..21698554c 100644 --- a/src/resources/extensions/genai-proxy/proxy-command.js +++ b/src/resources/extensions/genai-proxy/proxy-command.js @@ -2,7 +2,6 @@ import { createProxyServer } from "./proxy-server.js"; const PROXY_COMMAND_NAME = "genai-proxy"; const PROXY_FLAG_NAME = "genai-proxy"; -const LEGACY_PROXY_FLAG_NAME = "gemini-cli-proxy"; const DEFAULT_PROXY_PORT = 3000; export function installGenaiProxyExtension(api, dependencies) { let proxyServer = null; @@ -31,12 +30,6 @@ export function installGenaiProxyExtension(api, dependencies) { allowNoValue: true, onStartup: startProxyFromFlag, }); - api.registerFlag(LEGACY_PROXY_FLAG_NAME, { - description: "Legacy alias for --genai-proxy", - type: "string", - allowNoValue: true, - onStartup: startProxyFromFlag, - }); api.registerCommand(PROXY_COMMAND_NAME, { description: "Manage the GenAI proxy server", handler: async (args, context) => { diff --git a/src/resources/extensions/sf/auto-loop.js b/src/resources/extensions/sf/auto-loop.js index f8194b582..aac7aaa9a 100644 --- a/src/resources/extensions/sf/auto-loop.js +++ b/src/resources/extensions/sf/auto-loop.js @@ -11,7 +11,11 @@ export { INFRA_ERROR_CODES, isInfrastructureError, } from "./auto/infra-errors.js"; -export { autoLoop, runLegacyAutoLoop, runUokKernelLoop } from "./auto/loop.js"; +export { + autoLoop, + runStandardAutoLoop, + runUokKernelLoop, +} from "./auto/loop.js"; export { _hasPendingResolve, _resetPendingResolve, diff --git a/src/resources/extensions/sf/auto.js b/src/resources/extensions/sf/auto.js index f475ef87d..691b289ab 100644 --- a/src/resources/extensions/sf/auto.js +++ b/src/resources/extensions/sf/auto.js @@ -337,11 +337,7 @@ export { function registerSigtermHandler(currentBasePath) { const prefs = loadEffectiveSFPreferences()?.preferences; const flags = resolveUokFlags(prefs); - const pathLabel = flags.legacyFallback - ? "legacy-fallback" - : flags.enabled - ? "uok-kernel" - : "legacy-wrapper"; + const pathLabel = flags.enabled ? "uok-kernel" : "standard-loop"; const onSignal = () => { // Write UOK parity exit heartbeat before process.exit(0) bypasses // the finally block in runAutoLoopWithUok. Fixes the enter/exit @@ -1736,7 +1732,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) { s, deps: buildLoopDeps(), runKernelLoop: runUokKernelLoop, - runLegacyLoop: autoLoop, + runStandardLoop: autoLoop, }); cleanupAfterLoopExit(ctx); return; @@ -1785,7 +1781,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) { s, deps: buildLoopDeps(), runKernelLoop: runUokKernelLoop, - runLegacyLoop: autoLoop, + runStandardLoop: autoLoop, }); cleanupAfterLoopExit(ctx); } diff --git a/src/resources/extensions/sf/auto/loop.js b/src/resources/extensions/sf/auto/loop.js index d59db2727..abd0dabb8 100644 --- a/src/resources/extensions/sf/auto/loop.js +++ b/src/resources/extensions/sf/auto/loop.js @@ -255,7 +255,7 @@ async function runUnitPhaseViaContract( loopState, sidecarItem, ) { - if (dispatchContract === "legacy-direct") { + if (dispatchContract === "standard-direct") { return runUnitPhase(ic, iterData, loopState, sidecarItem); } const scheduler = new ExecutionGraphScheduler(); @@ -308,7 +308,7 @@ async function enforceMinRequestInterval(s, prefs) { * dispatchNextUnit → handleAgentEnd → dispatchNextUnit chain. */ export async function autoLoop(ctx, pi, s, deps, options) { - const dispatchContract = options?.dispatchContract ?? "legacy-direct"; + const dispatchContract = options?.dispatchContract ?? "standard-direct"; debugLog("autoLoop", { phase: "enter" }); let iteration = 0; // Load persisted stuck state so counters survive session restarts (#3704) @@ -1119,6 +1119,6 @@ export async function autoLoop(ctx, pi, s, deps, options) { export async function runUokKernelLoop(ctx, pi, s, deps) { return autoLoop(ctx, pi, s, deps, { dispatchContract: "uok-scheduler" }); } -export async function runLegacyAutoLoop(ctx, pi, s, deps) { - return autoLoop(ctx, pi, s, deps, { dispatchContract: "legacy-direct" }); +export async function runStandardAutoLoop(ctx, pi, s, deps) { + return autoLoop(ctx, pi, s, deps, { dispatchContract: "standard-direct" }); } diff --git a/src/resources/extensions/sf/commands/handlers/auto.js b/src/resources/extensions/sf/commands/handlers/auto.js index 46facf816..137f07cec 100644 --- a/src/resources/extensions/sf/commands/handlers/auto.js +++ b/src/resources/extensions/sf/commands/handlers/auto.js @@ -17,8 +17,7 @@ import { guardRemoteSession, projectRoot } from "../context.js"; /** * Parse --yolo flag and optional file path from the autonomous command string. * Supports: `/sf autonomous --yolo path/to/file.md` or - * `/sf autonomous -y path/to/file.md`. The legacy `/sf auto` spelling is still - * normalized for compatibility, but is not a distinct user-facing mode. + * `/sf autonomous -y path/to/file.md`. */ function parseYoloFlag(trimmed) { const yoloRe = /(?:--yolo|-y)\s+("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\S+)/; @@ -51,7 +50,7 @@ export function parseMilestoneTarget(input) { * Dispatch entry point for the autonomous command family. * * Handles `/sf autonomous`, `/sf next`, `/sf stop`, `/sf pause`, and their flag - * variants. Legacy `/sf auto` is accepted as a hidden compatibility spelling. + * variants. * Returns `true` when the command was recognised and routed (caller stops * searching), `false` when the command isn't autonomous-related. * @@ -69,8 +68,6 @@ export function parseMilestoneTarget(input) { export async function handleAutoCommand(trimmed, ctx, pi) { const isAutonomousVerb = trimmed === "autonomous" || trimmed.startsWith("autonomous "); - const isLegacyAutoVerb = trimmed === "auto" || trimmed.startsWith("auto "); - const isAutonomousFamily = isAutonomousVerb || isLegacyAutoVerb; /** * Route an autonomous launch through either the headless (in-process) or * detached (spawned subprocess) entry point depending on `SF_HEADLESS`. @@ -114,9 +111,8 @@ export async function handleAutoCommand(trimmed, ctx, pi) { }); return true; } - if (isAutonomousFamily) { - const normalized = trimmed.replace(/^(?:auto|autonomous)\b/, "autonomous"); - const { yoloSeedFile, rest: afterYolo } = parseYoloFlag(normalized); + if (isAutonomousVerb) { + const { yoloSeedFile, rest: afterYolo } = parseYoloFlag(trimmed); const { milestoneId, rest: afterMilestone } = parseMilestoneTarget(afterYolo); const verboseMode = afterMilestone.includes("--verbose"); diff --git a/src/resources/extensions/sf/docs/preferences-reference.md b/src/resources/extensions/sf/docs/preferences-reference.md index 425623357..0013a18f5 100644 --- a/src/resources/extensions/sf/docs/preferences-reference.md +++ b/src/resources/extensions/sf/docs/preferences-reference.md @@ -210,8 +210,6 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea - `uok`: orchestration kernel controls. Keys: - `enabled`: boolean — enable kernel wrappers and contract observers. Default: `true`. - - `legacy_fallback.enabled`: boolean — emergency release fallback that forces legacy orchestration behavior even when `uok.enabled` is `true`. Default: `false`. - - Runtime override: set `SF_UOK_FORCE_LEGACY=1` (or `SF_UOK_LEGACY_FALLBACK=1`) to force legacy behavior for the current process. - `gates.enabled`: boolean — route checks through the unified gate runner and persist `gate_runs`. Default: `true`. - `model_policy.enabled`: boolean — enforce policy filtering before model capability scoring. Default: `true`. - `execution_graph.enabled`: boolean — enable DAG scheduler facade/adapters for execution. Default: `true`. diff --git a/src/resources/extensions/sf/preferences-validation.js b/src/resources/extensions/sf/preferences-validation.js index c6c655ad7..470d75fe7 100644 --- a/src/resources/extensions/sf/preferences-validation.js +++ b/src/resources/extensions/sf/preferences-validation.js @@ -240,7 +240,6 @@ export function validatePreferences(preferences) { valid[normalizedTargetKey] = parsed; } }; - parseEnabledBlock("legacy_fallback"); parseEnabledBlock("gates"); parseEnabledBlock("model_policy"); parseEnabledBlock("execution_graph"); @@ -297,7 +296,6 @@ export function validatePreferences(preferences) { } const knownUokKeys = new Set([ "enabled", - "legacy_fallback", "gates", "model_policy", "execution_graph", diff --git a/src/resources/extensions/sf/preferences.js b/src/resources/extensions/sf/preferences.js index a1e521e48..aea1d932e 100644 --- a/src/resources/extensions/sf/preferences.js +++ b/src/resources/extensions/sf/preferences.js @@ -440,13 +440,6 @@ function mergePreferences(base, override) { base.uok || override.uok ? { enabled: override.uok?.enabled ?? base.uok?.enabled, - legacy_fallback: - base.uok?.legacy_fallback || override.uok?.legacy_fallback - ? { - ...(base.uok?.legacy_fallback ?? {}), - ...(override.uok?.legacy_fallback ?? {}), - } - : undefined, gates: base.uok?.gates || override.uok?.gates ? { ...(base.uok?.gates ?? {}), ...(override.uok?.gates ?? {}) } diff --git a/src/resources/extensions/sf/templates/PREFERENCES.md b/src/resources/extensions/sf/templates/PREFERENCES.md index 37e5e2344..d010f0424 100644 --- a/src/resources/extensions/sf/templates/PREFERENCES.md +++ b/src/resources/extensions/sf/templates/PREFERENCES.md @@ -63,8 +63,6 @@ dynamic_routing: hooks: uok: enabled: true - legacy_fallback: - enabled: false gates: enabled: true model_policy: diff --git a/src/resources/extensions/sf/uok-parity-summary.js b/src/resources/extensions/sf/uok-parity-summary.js index ff0c0b1c4..20415d89b 100644 --- a/src/resources/extensions/sf/uok-parity-summary.js +++ b/src/resources/extensions/sf/uok-parity-summary.js @@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; /** * Read the last UOK parity report from /.sf/runtime/uok-parity-report.json - * and surface any divergences/fallbacks via ctx.ui?.notify?.(). + * and surface any divergences or errors via ctx.ui?.notify?.(). * * Never throws — all errors are swallowed so session_start is never blocked. */ @@ -17,12 +17,10 @@ export async function summarizeParityReport(basePath, ctx, pi) { return; } const mismatches = report.criticalMismatches?.length ?? 0; - const fallbacks = report.fallbackInvocations ?? 0; const errors = report.statuses?.error ?? 0; - if (mismatches > 0 || fallbacks > 0 || errors > 0) { + if (mismatches > 0 || errors > 0) { const msg = `UOK parity report shows ${mismatches} critical mismatch${mismatches === 1 ? "" : "es"}, ` + - `${fallbacks} fallback invocation${fallbacks === 1 ? "" : "s"}, ` + `${errors} error${errors === 1 ? "" : "s"} since ${report.generatedAt}. ` + `Inspect .sf/runtime/uok-parity-report.json.`; ctx.ui?.notify?.(msg, "warning"); diff --git a/src/resources/extensions/sf/uok/flags.js b/src/resources/extensions/sf/uok/flags.js index 257835aab..a94b6c847 100644 --- a/src/resources/extensions/sf/uok/flags.js +++ b/src/resources/extensions/sf/uok/flags.js @@ -1,25 +1,10 @@ import { loadEffectiveSFPreferences } from "../preferences.js"; -function envForcesLegacyFallback() { - const raw = - process.env.SF_UOK_FORCE_LEGACY ?? process.env.SF_UOK_LEGACY_FALLBACK; - if (!raw) return false; - const normalized = raw.trim().toLowerCase(); - return ( - normalized === "1" || - normalized === "true" || - normalized === "yes" || - normalized === "on" - ); -} export function resolveUokFlags(prefs) { const uok = prefs?.uok; - const legacyFallback = - uok?.legacy_fallback?.enabled === true || envForcesLegacyFallback(); const enabledByPreference = uok?.enabled ?? true; return { - enabled: enabledByPreference && !legacyFallback, - legacyFallback, + enabled: enabledByPreference, gates: uok?.gates?.enabled ?? true, modelPolicy: uok?.model_policy?.enabled ?? true, executionGraph: uok?.execution_graph?.enabled ?? true, diff --git a/src/resources/extensions/sf/uok/kernel.js b/src/resources/extensions/sf/uok/kernel.js index 1cac2c72a..cffa768d8 100644 --- a/src/resources/extensions/sf/uok/kernel.js +++ b/src/resources/extensions/sf/uok/kernel.js @@ -21,11 +21,10 @@ function refreshParityReport(basePath) { } } function resolveKernelPathLabel(flags) { - if (flags.legacyFallback) return "legacy-fallback"; - return flags.enabled ? "uok-kernel" : "legacy-wrapper"; + return flags.enabled ? "uok-kernel" : "standard-loop"; } export async function runAutoLoopWithUok(args) { - const { ctx, pi, s, deps, runKernelLoop, runLegacyLoop } = args; + const { ctx, pi, s, deps, runKernelLoop, runStandardLoop } = args; const prefs = deps.loadEffectiveSFPreferences()?.preferences; const flags = resolveUokFlags(prefs); const previousReport = refreshParityReport(s.basePath); @@ -73,10 +72,10 @@ export async function runAutoLoopWithUok(args) { let status = "ok"; let error; try { - if (flags.enabled && !flags.legacyFallback) { + if (flags.enabled) { await runKernelLoop(ctx, pi, s, decoratedDeps); } else { - await runLegacyLoop(ctx, pi, s, deps); + await runStandardLoop(ctx, pi, s, deps); } } catch (err) { status = "error"; diff --git a/src/resources/extensions/sf/uok/parity-report.js b/src/resources/extensions/sf/uok/parity-report.js index 7ab171319..31dc3f9bf 100644 --- a/src/resources/extensions/sf/uok/parity-report.js +++ b/src/resources/extensions/sf/uok/parity-report.js @@ -48,7 +48,6 @@ export function buildParityReport(events, sourcePath) { const paths = {}; const statuses = {}; const criticalMismatches = []; - let fallbackInvocations = 0; let enterEvents = 0; let exitEvents = 0; let totalDiffs = 0; @@ -75,13 +74,12 @@ export function buildParityReport(events, sourcePath) { } continue; } - // Legacy heartbeat event + // Kernel heartbeat event const heartbeat = event; increment(paths, heartbeat.path); increment(statuses, heartbeat.status); if (heartbeat.phase === "enter") enterEvents += 1; if (heartbeat.phase === "exit") exitEvents += 1; - if (heartbeat.path === "legacy-fallback") fallbackInvocations += 1; if (heartbeat.status === "error") { criticalMismatches.push(heartbeat.error ?? "parity event reported error"); } @@ -99,7 +97,6 @@ export function buildParityReport(events, sourcePath) { paths, statuses, criticalMismatches, - fallbackInvocations, enterEvents, exitEvents, missingExitEvents, diff --git a/src/tests/auto-piped-io.test.ts b/src/tests/auto-piped-io.test.ts index cbda99391..bb37963c3 100644 --- a/src/tests/auto-piped-io.test.ts +++ b/src/tests/auto-piped-io.test.ts @@ -34,14 +34,14 @@ const EXPLICIT_SUBCOMMANDS = new Set([ * Detect whether the current subcommand should be auto-redirected * to headless mode when stdout is not a TTY. * - * Returns true when: the subcommand is "autonomous", or legacy "auto", AND stdout is piped. + * Returns true when the subcommand is "autonomous" and stdout is piped. */ function shouldRedirectAutoToHeadless( subcommand: string | undefined, stdoutIsTTY: boolean, ): boolean { if (stdoutIsTTY) return false; - return subcommand === "auto" || subcommand === "autonomous"; + return subcommand === "autonomous"; } /** @@ -65,19 +65,12 @@ function isExplicitSubcommand(subcommand: string | undefined): boolean { // ─── shouldRedirectAutoToHeadless ───────────────────────────────────────── -test("redirects legacy 'auto' to headless when stdout is piped", () => { - assert.ok(shouldRedirectAutoToHeadless("auto", false)); -}); - test("redirects 'autonomous' to headless when stdout is piped", () => { assert.ok(shouldRedirectAutoToHeadless("autonomous", false)); }); -test("does NOT redirect legacy 'auto' when stdout is a TTY", () => { - assert.ok(!shouldRedirectAutoToHeadless("auto", true)); -}); - test("does NOT redirect non-auto subcommands when stdout is piped", () => { + assert.ok(!shouldRedirectAutoToHeadless("auto", false)); assert.ok(!shouldRedirectAutoToHeadless("headless", false)); assert.ok(!shouldRedirectAutoToHeadless("config", false)); assert.ok(!shouldRedirectAutoToHeadless("update", false)); @@ -114,7 +107,7 @@ test("identifies explicitly handled subcommands", () => { assert.ok(isExplicitSubcommand("web")); }); -test("does NOT identify legacy 'auto' as explicit subcommand", () => { +test("does NOT identify 'auto' as explicit subcommand", () => { assert.ok(!isExplicitSubcommand("auto")); }); diff --git a/src/tests/headless-cli-surface.test.ts b/src/tests/headless-cli-surface.test.ts index edd9285c8..6174f3866 100644 --- a/src/tests/headless-cli-surface.test.ts +++ b/src/tests/headless-cli-surface.test.ts @@ -109,7 +109,7 @@ function parseHeadlessArgs(argv: string[]): HeadlessOptions { options.bare = true; } } else if (!commandSeen) { - if (arg === "autonomous" || arg === "auto") { + if (arg === "autonomous") { options.command = "autonomous"; options.auto = true; } else { @@ -190,12 +190,6 @@ test("autonomous command preserves command arguments", () => { assert.deepEqual(opts.commandArgs, ["M001", "extra-context"]); }); -test("legacy auto command normalizes to autonomous", () => { - const opts = parseHeadlessArgs(["node", "sf", "headless", "auto", "M001"]); - assert.equal(opts.command, "autonomous"); - assert.deepEqual(opts.commandArgs, ["M001"]); -}); - test("invalid --output-format value throws", () => { assert.throws( () =>