wait_for_user_ack

wait_for_user_ack

Waits for the user to confirm an out-of-band action (e.g. 'did you finish resetting your password in the browser?'). Emits a UserAckCard to the renderer with a prompt and clickable options; returns the chosen option id to the agent.

Metadata

Name

wait_for_user_ack

Updated

last month

Source

GitHub source

Risk

Low

Requires consent

false

Affected scope

user

Code

/**
 * mcp/skills/waitForUserAck.ts — wait_for_user_ack synthetic tool
 *
 * This is NOT a normal tool.  It is a registration shim for a first-class
 * G4 gate — the "user-ack" gate — which is routed specially in
 * electron/agent/guards/execution.ts:executeStep().
 *
 * Returns
 * -------
 *   { choice: string }
 *
 */

import { z } from "zod";

// -- Meta ---------------------------------------------------------------------

export const meta = {
  name: "wait_for_user_ack",
  description:
    "Waits for the user to confirm an out-of-band action (e.g. 'did you " +
    "finish resetting your password in the browser?'). Emits a UserAckCard " +
    "to the renderer with a prompt and clickable options; returns the " +
    "chosen option id to the agent.",
  riskLevel:       "low",
  destructive:     false,
  requiresConsent: false,
  supportsDryRun:  false,
  affectedScope:   ["user"],
  auditRequired:   false,
  isUserWaitGate:  true,
  schema: {
    prompt: z
      .string()
      .min(1)
      .describe(
        "Short question to show the user in the UserAckCard " +
        "(e.g. 'Did you complete the password reset in the browser?').",
      ),
    options: z
      .array(
        z.object({
          id: z
            .string()
            .min(1)
            .describe(
              "Stable identifier returned in the gate result — the agent " +
              "branches on this value. Use 'done' for the happy-path continue " +
              "option; 'failed', 'cancel', 'timeout' for the sad paths.",
            ),
          label: z
            .string()
            .min(1)
            .describe("Human-readable button text shown in the UserAckCard."),
          kind: z
            .enum(["primary", "secondary", "cancel"])
            .optional()
            .describe(
              "Optional visual hint for the button (primary = green/emerald, " +
              "secondary = neutral zinc, cancel = muted). Defaults to 'secondary'.",
            ),
        }),
      )
      .min(1)
      .max(4)
      .describe(
        "Ordered list of clickable options. Include one 'done' option on " +
        "the happy path plus 1-3 sad-path options.",
      ),
  },
} 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 runUserAckGate()
 * before the tool-execution block.  If this throws, the routing is broken.
 */
export async function run(): Promise<never> {
  throw new Error(
    "wait_for_user_ack.run() was invoked directly — this should never happen. " +
    "G4 is expected to route steps whose tool.meta.isUserWaitGate is true " +
    "through runUserAckGate() in electron/agent/guards/execution.ts, bypassing " +
    "the normal tool-execution pipeline. Check G4's executeStep() routing.",
  );
}