present_preview
Metadata
Name
present_preview
Updated
last month
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/presentPreview.ts — present_preview synthetic tool
*
* Like wait_for_user_ack, this is NOT a normal tool — it is a registration
* shim for a first-class G4 gate (the "present-preview" gate) routed
* specially in electron/agent/guards/execution.ts:executeStep().
*
* Why a tool at all?
* ------------------
* Agent plans reference tools by name. Registering present_preview in the
* MCP tool registry lets the planner emit it as a plan step and the
* execution LLM see its Zod schema (so it passes structured params like
* { title, summary, categories }). But the actual `run()` below is never
* invoked — G4 detects `meta.isUserWaitGate: true` and routes the step
* through runPresentPreviewGate() instead of the normal tool-execution
* pipeline.
*
* Schema overview
* ---------------
* Top-level (required): title, summary, categories[] (min 1)
* Per category (required): id (stable kebab-case), label, summary
* Per category (optional): detail, defaultSelected (defaults true),
* destructive (defaults false)
*
* Returns
* -------
* { selected: string[] }
*
* The string array contains the category ids the user kept checked.
* Empty array means cancel / dismiss / timeout / no items checked.
*/
import { z } from "zod";
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "present_preview",
description:
"Presents a categorised preview of pending actions to the user and " +
"waits for their selection. Use when a SKILL.md step says 'summarise " +
"and confirm', 'present a consolidated preview', or 'ask the user " +
"which categories to proceed with'.\n" +
"Empty array means cancel / dismiss / timeout / zero items.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: true,
/**
* The routing flag. G4's executeStep reads this and dispatches to
* runPresentPreviewGate() — the run() below is never invoked on the
* normal path.
*/
isUserWaitGate: true,
schema: {
title: z
.string()
.min(1)
.describe(
"Short heading shown at the top of the preview card " +
"(e.g. 'Cleanup Plan')."
),
summary: z
.string()
.min(1)
.describe(
"One-sentence framing shown under the title; typically mentions " +
"the aggregated total recovery / impact " +
"(e.g. 'You can recover 8.2 GB by cleaning the following:'). " +
"Author writes the shape with {placeholder} tokens; executor LLM " +
"substitutes runtime numbers from scratchpad before invoking."
),
categories: z
.array(
z.object({
id: z
.string()
.min(1)
.describe(
"Stable kebab-case identifier returned in the gate result — " +
"subsequent corrective steps reference this id via " +
"inputsFrom / When: clauses. Must NOT be derived from " +
"`label` (label edits or localization would silently break " +
"cross-step references). Unique within one call."
),
label: z
.string()
.min(1)
.describe(
"Short user-facing name shown next to the checkbox " +
"(e.g. 'Browser caches')."
),
summary: z
.string()
.min(1)
.describe(
"One-line description rendered next to the label, typically " +
"count + size " +
"(e.g. 'Chrome, Safari, Edge — 1.2 GB' or " +
"'12 installer files over 50 MB (3.4 GB)'). Author writes " +
"the shape with {placeholder} tokens; executor LLM " +
"substitutes runtime numbers from scratchpad."
),
detail: z
.string()
.optional()
.describe(
"Optional extra bullet text shown when the user expands the " +
"category. Use sparingly."
),
defaultSelected: z
.boolean()
.optional()
.describe(
"Whether the checkbox is checked by default. Defaults to " +
"true. Set false for destructive or non-obvious categories."
),
destructive: z
.boolean()
.optional()
.describe(
"When true, the renderer shows a ⚠ icon and warning style. " +
"Combine with defaultSelected: false so the user must " +
"explicitly opt in."
)
})
)
.min(1)
.describe(
"Ordered list of categories the user can pick from. Min 1 entry."
),
},
} as const;
// -- Exported run function ----------------------------------------------------
/**
* Safety-net stub. The run() function is never invoked on the normal path —
* G4's executeStep() detects meta.isUserWaitGate and routes to
* runPresentPreviewGate() in electron/agent/guards/execution.ts, bypassing
* the normal tool-execution pipeline. If this throws, the routing is broken.
*/
export async function run(): Promise<never> {
throw new Error(
"present_preview.run() was invoked directly — this should never happen. " +
"G4 is expected to route steps whose tool.meta.isUserWaitGate is true " +
"through runPresentPreviewGate() in electron/agent/guards/execution.ts, " +
"bypassing the normal tool-execution pipeline. Check G4's executeStep() " +
"routing."
);
}