Bypass mode (owner opt-in, default off): storebound settings bypass on|off|status and a one-time question in init; MCP owner_step (owner-only commands, dry run first), owner-only api operations with --yes --confirm, console plans may press buttons.final; every action logged to .storebound/owner-actions.log; docs/bypass.md, agent rules, tests

This commit is contained in:
CRTkafa 2026-10-22 02:27:34 +08:00
parent 2598c4953a
commit 9bd17c401d
21 changed files with 218 additions and 13 deletions

View file

@ -2,6 +2,7 @@
// Rules: one tool per CLI command; every tool that writes anything has `dry_run` defaulting to true; nothing that
// submits for review, releases to the public, resumes / completes a rollout, cancels a submission or deletes is
// exposed. Those stay terminal commands the owner runs (they also ask for confirmation there).
import { OWNER_COMMANDS } from './lib/bypass';
export interface JsonSchema { type: string; description?: string; enum?: (string | number)[]; default?: unknown; items?: JsonSchema; minimum?: number; maximum?: number; pattern?: string; additionalProperties?: JsonSchema | boolean }
@ -479,8 +480,17 @@ export const OUTPUT_SCHEMA = {
required: ['exitCode', 'command', 'output'],
} as const;
export function listTools(): Record<string, unknown>[] {
return TOOLS.map(t => ({
/** Bypass mode only (storebound settings bypass on): one owner-only command, dry run by default. */
export const OWNER_STEP: ToolDef = {
name: 'owner_step', title: 'Owner step (bypass mode)', write: true, destructive: true, network: true,
description: `Bypass mode is on: run one owner-only command yourself (${OWNER_COMMANDS.join(', ')}). Pass the CLI words as command and the rest as args (ids, build numbers, --track, --percent ...). Always call it with dry_run: true first and read the plan; then dry_run: false runs it with --yes. Every call is logged to .storebound/owner-actions.log. Legal agreements, passwords and 2FA stay with the owner.`,
command: [], props: { command: { type: 'string', enum: [...OWNER_COMMANDS], description: 'The owner-only command' }, args: { type: 'array', items: { type: 'string' }, description: 'Its arguments and options' } },
required: ['command'],
args: i => [...String(i.command).split(' '), ...(Array.isArray(i.args) ? i.args.map(String) : [])],
};
export function listTools(bypass = false): Record<string, unknown>[] {
return [...TOOLS, ...(bypass ? [OWNER_STEP] : [])].map(t => ({
name: t.name, title: t.title, description: t.description,
inputSchema: inputSchema(t), outputSchema: OUTPUT_SCHEMA,
annotations: { title: t.title, readOnlyHint: !t.write, destructiveHint: t.write ? !!t.destructive : false, idempotentHint: !!t.idempotent || !t.write, openWorldHint: t.network },