fix(sf): keep web autonomy on proven routes
This commit is contained in:
parent
8f097f8dca
commit
d8fd70e57f
11 changed files with 81 additions and 27 deletions
|
|
@ -822,6 +822,18 @@ export async function selectAndApplyModel(
|
|||
ctx.ui.notify(`Model ${modelId} not found, trying fallback.`, "info");
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
isAutoMode &&
|
||||
!allowsFreeTierAutoRoute(unitType) &&
|
||||
routingConfig?.allow_free_models !== true &&
|
||||
isFreeTierModelRoute(model.provider, model.id)
|
||||
) {
|
||||
ctx.ui.notify(
|
||||
`Skipping free-tier model ${model.provider}/${model.id} for main autonomous unit ${unitType}; trying next fallback.`,
|
||||
"warning",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
enabledModelsResolvedCount++;
|
||||
// ── Enforce operator enabledModels allowlist ──────────────────────
|
||||
// Applied as the first model-level filter so disallowed providers
|
||||
|
|
|
|||
|
|
@ -1493,7 +1493,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|||
const confirmed = await showConfirm(ctx, {
|
||||
title: "Switch to Build mode?",
|
||||
message:
|
||||
"You're in Ask mode. Build mode runs autonomously with broad permissions — SF may still pause at gates or risky operations. Use Ctrl+Y for YOLO (no stops at all).",
|
||||
"You're in Ask mode. Build mode runs autonomously with broad permissions — SF may still pause at gates or risky operations. Use Ctrl+Alt+Y for YOLO (no stops at all).",
|
||||
confirmLabel: "Switch to Build",
|
||||
declineLabel: "Stay in Ask",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -574,7 +574,7 @@ export class AutoSession {
|
|||
* (build + autonomous + deep + unrestricted). On deactivation, restores
|
||||
* the mode that was active before YOLO was turned on.
|
||||
*
|
||||
* Consumer: Ctrl+Y keybinding in steerable-autonomous-extension.js.
|
||||
* Consumer: Ctrl+Alt+Y keybinding in steerable-autonomous-extension.js.
|
||||
* Returns the new yolo state (true = on, false = off).
|
||||
*/
|
||||
toggleYolo() {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const BASE_RUNTIME_COMMANDS = new Set([
|
|||
"terminal",
|
||||
"exit",
|
||||
"quit",
|
||||
"stop",
|
||||
]);
|
||||
/**
|
||||
* Top-level SF subcommands with descriptions.
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export function registerSFCommands(pi) {
|
|||
getArgumentCompletions: (prefix) =>
|
||||
getSfTopLevelCommandCompletions(command.cmd, prefix),
|
||||
handler: async (args, ctx) => {
|
||||
// Cache this command ctx so shortcut handlers (Ctrl+Y) can fall back
|
||||
// Cache this command ctx so shortcut handlers (Ctrl+Alt+Y) can fall back
|
||||
// to a valid ExtensionCommandContext that has newSession().
|
||||
// Import lazily to avoid a circular dep at module load time.
|
||||
importExtensionModule(import.meta.url, "../auto/session.js")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Purpose: provide Shift+Tab for run-control cycling (manual → assisted →
|
||||
* autonomous) when the session is idle, and for steering/asking questions
|
||||
* during autonomous execution (Copilot Auto style). Also integrates Ctrl+Y
|
||||
* during autonomous execution (Copilot Auto style). Also integrates Ctrl+Alt+Y
|
||||
* for YOLO mode (bypass git prompts).
|
||||
*
|
||||
* Consumer: index.js → steerableAutonomousExtension(pi) on every startup.
|
||||
|
|
@ -32,7 +32,7 @@ export default function steerableAutonomousExtension(api) {
|
|||
}
|
||||
});
|
||||
|
||||
// Handle key events - Shift+Tab and Ctrl+Y
|
||||
// Handle key events - Shift+Tab and Ctrl+Alt+Y
|
||||
api.registerShortcut("shift+tab", {
|
||||
description:
|
||||
"Cycle mode (Ask ↔ Build) or open steerable panel during autonomous",
|
||||
|
|
@ -77,7 +77,7 @@ export default function steerableAutonomousExtension(api) {
|
|||
},
|
||||
});
|
||||
|
||||
api.registerShortcut("ctrl+y", {
|
||||
api.registerShortcut("ctrl+alt+y", {
|
||||
description:
|
||||
"Toggle YOLO mode (build + autonomous + deep + unrestricted; bypass git prompts). If not running, starts the autonomous loop immediately.",
|
||||
handler: async (ctx) => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Provides Shift+Tab interface for steering and asking questions
|
||||
* during autonomous execution, similar to Copilot Auto.
|
||||
* Also integrates Ctrl+Y for YOLO mode (bypass git prompts).
|
||||
* Also integrates Ctrl+Alt+Y for YOLO mode (bypass git prompts).
|
||||
*/
|
||||
|
||||
import { createInterface } from "node:readline";
|
||||
|
|
@ -56,7 +56,7 @@ const CONTROL_CATEGORIES = [
|
|||
items: [
|
||||
{ key: "p", label: "Pause autonomous", action: "pause" },
|
||||
{ key: "s", label: "Stop execution", action: "stop" },
|
||||
{ key: "y", label: "YOLO mode (Ctrl+Y)", action: "yolo" },
|
||||
{ key: "y", label: "YOLO mode (Ctrl+Alt+Y)", action: "yolo" },
|
||||
{ key: "h", label: "Help/commands", action: "help" },
|
||||
{ key: "esc", label: "Close panel", action: "close" },
|
||||
],
|
||||
|
|
@ -113,7 +113,7 @@ function renderPanel(currentStatus = "") {
|
|||
// Add footer
|
||||
panelContent.push("");
|
||||
panelContent.push(
|
||||
"\x1b[90mShift+Tab or / to open/close • Ctrl+Y for YOLO\x1b[0m",
|
||||
"\x1b[90mShift+Tab or / to open/close • Ctrl+Alt+Y for YOLO\x1b[0m",
|
||||
);
|
||||
|
||||
return renderBox(panelContent, "🎛️ Steerable Autonomous Mode");
|
||||
|
|
|
|||
|
|
@ -208,24 +208,20 @@ function isCodebaseSearchError(details) {
|
|||
*
|
||||
* Consumer: `codebase_search.execute`.
|
||||
*/
|
||||
function buildCodebaseSearchArgs(strategy, query, scope, projectRoot) {
|
||||
export function buildCodebaseSearchArgs(strategy, query, scope, projectRoot) {
|
||||
// Scope-aware retriever selection: repo-root scope uses bm25+phrase (fast,
|
||||
// avoids the long first-time vector index build on full workspace), while
|
||||
// scoped subdirs get vector+reranking for semantic signal. Timeouts are
|
||||
// sized to accommodate cold-cache embedding builds.
|
||||
const { retrievers, reranking } = chooseSiftRetrievers(scope, projectRoot);
|
||||
const args = [
|
||||
"search",
|
||||
"--strategy",
|
||||
strategy,
|
||||
"--retrievers",
|
||||
retrievers,
|
||||
"--reranking",
|
||||
reranking,
|
||||
"--agent",
|
||||
query,
|
||||
scope,
|
||||
];
|
||||
const args = ["search", "--strategy", strategy, "--agent", query, scope];
|
||||
// Sift's agent runtime intentionally supports a narrower option surface than
|
||||
// direct search; passing direct-search retriever/reranker flags makes agent
|
||||
// search fail before it can return any evidence. Keep the computed defaults
|
||||
// here as documentation for the direct `sift_search` tool, but do not pass
|
||||
// them while `--agent` is active.
|
||||
void retrievers;
|
||||
void reranking;
|
||||
// --verbose gives more progress info from the Rust binary when the
|
||||
// operator explicitly asked for debug-level sift logging.
|
||||
const siftLogLevel = (process.env.SF_SIFT_LOG_LEVEL ?? "info").toLowerCase();
|
||||
|
|
|
|||
|
|
@ -269,6 +269,31 @@ describe("free-tier autonomous routing policy", () => {
|
|||
["openrouter/qwen/qwen3-coder:free", "kimi-coding/kimi-k2.6"],
|
||||
);
|
||||
});
|
||||
|
||||
test("main_worker_final_fallback_loop_skips_explicit_free_primary", async () => {
|
||||
makeEnv({
|
||||
enabledModels: undefined,
|
||||
prefsYaml: [
|
||||
"version: 1",
|
||||
"models:",
|
||||
" execution:",
|
||||
" model: openrouter/qwen/qwen3-coder:free",
|
||||
" fallbacks:",
|
||||
" - kimi-coding/kimi-k2.6",
|
||||
"",
|
||||
].join("\n"),
|
||||
});
|
||||
const notified = [];
|
||||
const applied = await dispatch([FREE, PAID], notified);
|
||||
|
||||
assert.ok(applied !== null, "paid fallback should be applied");
|
||||
assert.equal(applied.provider, "kimi-coding");
|
||||
assert.equal(applied.id, "kimi-k2.6");
|
||||
assert.ok(
|
||||
notified.some((n) => n.msg.includes("Skipping free-tier model")),
|
||||
`expected free-tier skip notification; got ${JSON.stringify(notified)}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Part 2: fallback chain respects enabledModels ─────────────────────────────
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import assert from "node:assert/strict";
|
|||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, it, vi } from "vitest";
|
||||
import { describe, it } from "vitest";
|
||||
import {
|
||||
chooseSiftRetrievers,
|
||||
resolveSiftSearchScopes,
|
||||
} from "../code-intelligence.js";
|
||||
import { buildCodebaseSearchArgs } from "../subagent/index.js";
|
||||
|
||||
// ── chooseSiftRetrievers unit tests ────────────────────────────────────────
|
||||
|
||||
|
|
@ -99,6 +100,25 @@ describe("sift-search-tool buildSiftArgs via chooseSiftRetrievers", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("codebase_search agent args", () => {
|
||||
it("does_not_pass_direct_search_retriever_flags_to_sift_agent_mode", () => {
|
||||
const args = buildCodebaseSearchArgs(
|
||||
"page-index-hybrid",
|
||||
"crash loop classifier",
|
||||
"src/resources/extensions/sf",
|
||||
"/repo",
|
||||
);
|
||||
|
||||
assert.ok(args.includes("--agent"), "agent mode must remain enabled");
|
||||
assert.ok(!args.includes("--retrievers"));
|
||||
assert.ok(!args.includes("--reranking"));
|
||||
assert.deepEqual(args.slice(-2), [
|
||||
"crash loop classifier",
|
||||
"src/resources/extensions/sf",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// ── ensureSiftIndexWarmup regression guard ─────────────────────────────────
|
||||
//
|
||||
// Warmup always passes "." (repo root) as scope. After the refactor,
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ export default function sfTui(pi) {
|
|||
"Cycle permission profile (restricted→normal→trusted→unrestricted)",
|
||||
handler: () => cyclePermissionProfile(ctx),
|
||||
});
|
||||
// Ctrl+G — open current project in $EDITOR (or notify if none)
|
||||
pi.registerShortcut(Key.ctrl("g"), {
|
||||
// Ctrl+Alt+E — open current project in $EDITOR (or notify if none)
|
||||
pi.registerShortcut(Key.ctrlAlt("e"), {
|
||||
description: "Open project root in $EDITOR",
|
||||
handler: () => {
|
||||
const editor = process.env.EDITOR || process.env.VISUAL;
|
||||
|
|
@ -193,8 +193,8 @@ export default function sfTui(pi) {
|
|||
ctx.ui.notify(`Opened ${editor} ${projectRoot() ?? "."}`, "info");
|
||||
},
|
||||
});
|
||||
// Ctrl+T — toggle reasoning display
|
||||
pi.registerShortcut(Key.ctrl("t"), {
|
||||
// Ctrl+Alt+T — toggle reasoning display
|
||||
pi.registerShortcut(Key.ctrlAlt("t"), {
|
||||
description: "Toggle extended thinking / reasoning display",
|
||||
handler: () => {
|
||||
const current = getExperimentalFlag("show_reasoning");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue