Replace the raw per-agent mode dropdown in the BooCoder composer with a curated three-option permission ladder mapped generically onto each provider's native modes: `plan` id -> Plan, default -> Ask, isUnattended -> Bypass (claude bypassPermissions, qwen yolo, opencode full-access). modeId stays the single wire field; the active unified mode is derived from it (no contracts change). Native BooCode gains its own mode set: Ask stages to the pending-changes queue (today's behavior), Bypass auto-applies the queue to disk after the turn (interactive messages path + task dispatcher path), Plan falls back to Ask. The shared apps/server inference engine is left untouched. Also preserve isUnattended on live-probed ACP modes so opencode's bypass mode stays detectable from the wire. Coder 373 tests green; coder + web typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
92 lines
3.6 KiB
TypeScript
92 lines
3.6 KiB
TypeScript
/**
|
|
* Static provider mode metadata — lifted from Paseo provider-manifest.ts patterns.
|
|
*/
|
|
import type { ProviderMode } from './provider-types.js';
|
|
|
|
export interface ProviderManifestEntry {
|
|
defaultModeId: string | null;
|
|
modes: ProviderMode[];
|
|
/** Claude effort levels exposed as thinking options on models. */
|
|
thinkingOptions?: Array<{ id: string; label: string }>;
|
|
}
|
|
|
|
const CLAUDE_MODES: ProviderMode[] = [
|
|
{ id: 'default', label: 'Always Ask', description: 'Prompts for permission the first time a tool is used' },
|
|
{ id: 'auto', label: 'Auto mode', description: 'Model classifier reviews permission prompts automatically' },
|
|
{ id: 'acceptEdits', label: 'Accept File Edits', description: 'Automatically approves edit-focused tools' },
|
|
{ id: 'plan', label: 'Plan Mode', description: 'Analyze without executing tools or edits' },
|
|
{ id: 'bypassPermissions', label: 'Bypass', description: 'Skip all permission prompts', isUnattended: true },
|
|
];
|
|
|
|
const OPENCODE_MODES: ProviderMode[] = [
|
|
{ id: 'build', label: 'Build', description: 'Allows edits and tool execution' },
|
|
{ id: 'plan', label: 'Plan', description: 'Read-only planning mode' },
|
|
{ id: 'full-access', label: 'Full Access', description: 'Auto-approves all tool prompts', isUnattended: true },
|
|
];
|
|
|
|
const QWEN_PTY_MODES: ProviderMode[] = [
|
|
{ id: 'default', label: 'Default', description: 'Prompt for approval' },
|
|
{ id: 'plan', label: 'Plan', description: 'Plan only — no edits' },
|
|
{ id: 'auto-edit', label: 'Auto Edit', description: 'Auto-approve edit tools' },
|
|
{ id: 'auto', label: 'Auto', description: 'LLM classifier auto-approves safe actions' },
|
|
{ id: 'yolo', label: 'YOLO', description: 'Auto-approve all tools', isUnattended: true },
|
|
];
|
|
|
|
// Native BooCode (llama-swap) has no agent-native mode vocabulary, so we define
|
|
// one that matches the unified permission ladder. `bypass` is the only mode that
|
|
// changes behavior (auto-apply staged edits after the turn — dispatcher.ts);
|
|
// `plan` falls back to `ask` semantics for native (writes still stage to the
|
|
// pending-changes queue). External agents map the same three unified modes onto
|
|
// THEIR native ids via the `plan`-id / default / `isUnattended` shape.
|
|
const BOOCODE_MODES: ProviderMode[] = [
|
|
{ id: 'plan', label: 'Plan', description: 'Read-only analysis (native BooCode falls back to Ask)' },
|
|
{ id: 'ask', label: 'Ask Permission', description: 'Stage edits to the pending-changes queue for review' },
|
|
{ id: 'bypass', label: 'Bypass', description: 'Auto-apply edits to disk after the turn', isUnattended: true },
|
|
];
|
|
|
|
const CLAUDE_THINKING = [
|
|
{ id: 'low', label: 'Low' },
|
|
{ id: 'medium', label: 'Medium' },
|
|
{ id: 'high', label: 'High' },
|
|
{ id: 'xhigh', label: 'Extra High' },
|
|
{ id: 'max', label: 'Max' },
|
|
];
|
|
|
|
export const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry> = {
|
|
boocode: {
|
|
defaultModeId: 'ask',
|
|
modes: BOOCODE_MODES,
|
|
},
|
|
claude: {
|
|
defaultModeId: 'default',
|
|
modes: CLAUDE_MODES,
|
|
thinkingOptions: CLAUDE_THINKING,
|
|
},
|
|
opencode: {
|
|
defaultModeId: 'build',
|
|
modes: OPENCODE_MODES,
|
|
},
|
|
goose: {
|
|
defaultModeId: null,
|
|
modes: [],
|
|
},
|
|
qwen: {
|
|
defaultModeId: 'default',
|
|
modes: QWEN_PTY_MODES,
|
|
},
|
|
};
|
|
|
|
export function getManifestModes(provider: string): ProviderMode[] {
|
|
return PROVIDER_MANIFEST[provider]?.modes ?? [];
|
|
}
|
|
|
|
export function getManifestDefaultModeId(provider: string): string | null {
|
|
return PROVIDER_MANIFEST[provider]?.defaultModeId ?? null;
|
|
}
|
|
|
|
export function isUnattendedMode(provider: string, modeId: string | undefined): boolean {
|
|
if (!modeId) return false;
|
|
const modes = getManifestModes(provider);
|
|
return modes.some((m) => m.id === modeId && m.isUnattended);
|
|
}
|