# Client-side confirmation (`--yes`, `[y/N]`)

Some tool actions have external side effects (gmail/outlook send, slack
send, tweet post, calendar invites, ...). When the server supports the
interactive-confirm contract, the CLI confirms these **locally, before the
single server call** — there is no pending state on the server and no
second call.

## How it works

1. `GET /api/tool_cli/tools` annotates confirmation-required tools/actions
   (`requires_confirmation`, plus `interactive_only` for the schema-locked
   actions: `slack send`/`react`/`upload`/`invite` and
   `google_drive share`) and, when both the CLI capability
   (`cli-confirm-v1`) and the server-side gate agree, advertises
   `"features": {"interactive_confirm": true}`. Both are cached with the
   tool schemas (24h TTL). Actions that dispatch on one argument (genteam
   `members` on `op`, teams `api` on `method`, ...) additionally carry
   `confirmation_exempt: {"param": ..., "values": [...]}` naming the
   read-only values of that argument (plus `"default"` when the server
   substitutes a value for an omitted argument, e.g. `method` → `GET`); the
   CLI skips the prompt only when the final merged argument — or, if it is
   omitted, the declared default — equals one of them exactly.
2. Before executing an annotated action, the CLI prints a preview of the
   arguments you typed (attachments listed by name only) and asks
   `About to execute <tool> <action>. Proceed? [y/N]` on **stderr**,
   reading one line from **stdin** — so `echo y | gsk ...` works and EOF
   declines.
3. On `y`/`yes` (or `--yes`), the request carries `cli_confirmed: true` and
   the server executes directly. On anything else the CLI prints
   `Aborted, nothing was sent.` and exits 1 with **zero server calls**.

## Flags and exit codes

| Situation | Behavior | Exit code |
|---|---|---|
| Prompt answered `y`/`yes` | one server call, executes | tool result |
| Prompt declined / EOF | no server call | 1 |
| `--yes` (or `-y`) on a normal action | no prompt, executes | tool result |
| **deprecated** `--skip_confirmation true` / `auto_skip_confirmation` on a normal action | no prompt, no `cli_confirmed` — the param is forwarded verbatim and the server honors it as before; one `[DEPRECATED] ... use --yes` line on stderr | tool result |
| `--yes` or a deprecated skip param on an interactive-only action | warning + prompt anyway (locked skip params are server-stripped); no deprecation hint | per answer |
| `--no-input` without `--yes` on a gated action/value | error, no server call | 2 |
| `--args-file -` (stdin consumed) without `--yes` on a gated action/value | error suggesting `--yes`, no server call | 2 |
| `--args-file -` with an exempt value (e.g. `{"op": "list", ...}`) | no prompt, no `cli_confirmed`, one server call | tool result |
| omitted argument whose annotation declares an exempt `default` (e.g. `teams api --path /me` with no `--method`) | no prompt, no `cli_confirmed`, one server call | tool result |
| exempt-looking value in another spelling (`--op LIST`), non-string, or omitted without a declared `default` (`members` with no `--op`) | treated as gated: prompt / `--yes` / exit 2 as above | per row above |

## Per-operation actions

Some actions are gated as a whole (`requires_confirmation`) but only write
for some values of one argument. The server declares the read values as
`confirmation_exempt`; the CLI runs those without a prompt and gates
everything else:

| Action | Runs without a prompt | Confirmation required |
|---|---|---|
| `genteam members` | `--op list` | `add`, `remove` |
| `genteam pins` | `--op list` | `pin`, `unpin` |
| `genteam invite_link` | `--op list` | `create`, `revoke` |
| `genteam dm` | `--op list` | `open` |
| `genteam tasks` | `--op list`, `get`, `candidates` | `claim`, `unclaim`, `status`, `create`, `assign`, `rename` |
| `teams api` (alias of `microsoft_teams`) | `--method GET`, or no `--method` at all (GET is the server default) | `POST`, `PATCH`, `PUT`, `DELETE` |

Pass the value exactly as listed (case-sensitive, no surrounding
whitespace); anything else prompts. An omitted argument counts as the
annotation's `default` only when the server declares one — `teams api`
does (an omitted `method` is a GET), the genteam `op` actions do not (an
omitted `op` is a validation error, so a missing `--op` still prompts). The
match is evaluated on the final merged arguments, so the value may come
from a flag, a short alias or `--args-file` (including `-`). An exempt
call sends neither `cli_confirmed` nor a skip parameter; the server's own
gate still backs every write. The exemption is not the only unprompted
path: a deprecated `skip_confirmation` / `auto_skip_confirmation` parameter
(see below) still makes a write proceed without a prompt, as it did before
per-operation exemptions. Note that the client gate is keyed on
the resolved action: an action name the CLI cannot resolve (e.g.
`{"action": "Members"}` in an args file) is not client-gated and is sent
as-is; the server rejects it as an unknown action, so nothing executes.

## Deprecated skip parameters

`skip_confirmation` and `auto_skip_confirmation` were the pre-contract way
to run a gated action unattended: a server-side parameter, honored by the
server for CLI callers. On this CLI `--yes` replaces them.

- A server that supports the contract **omits both parameters from the
  schema it serves to this CLI**: they no longer appear in `--help`, and
  the generated skills do not teach them. Every gated command's `--help`
  ends with a `Confirmation:` note that names `--yes` and `--no-input`
  instead.
- They are still **accepted**, as hidden flags
  (`--skip_confirmation [value]`, `--auto_skip_confirmation [value]`) and
  as `--args-file` keys, on any tool or action the schema marks
  `requires_confirmation`. The CLI
  forwards them verbatim (no `cli_confirmed`), the server honors them
  exactly as before, and a truthy value skips the prompt on a normal action
  (equivalent to `--yes`, plus the one-time hint). This covers every
  confirmation-gated tool or action, including ones whose schema never
  listed the parameters before (e.g. `hubspot_*`, calendar delete).
  Non-gated tools still refuse them as unknown parameters. In an
  `--args-file` the value must be a JSON boolean; `"false"`, `1`, `[]` or
  `null` exit 1 (`skip_confirmation must be a JSON boolean ... prefer --yes`)
  with no server call.
- When a truthy skip parameter is what bypassed the prompt, the CLI prints
  one line on stderr, **at most once per process**:

  ```
  [DEPRECATED] --skip_confirmation / --auto_skip_confirmation still work but are deprecated on this CLI — use --yes instead.
  ```

  A cron caller that still passes `skip_confirmation: true` (e.g. through
  `--args-file -`) sees exactly one such line per run; stdout is unchanged.
  Exempt read values
  (`--op list` with `skip_confirmation: true`, the #60498 shape), a
  falsy value (`--skip_confirmation false` prompts), interactive-only
  actions and feature-off servers print no hint.

- Migration: replace `--skip_confirmation true` / `--auto_skip_confirmation`
  with `--yes` (`-y`). For `--args-file` payloads, drop the key and add
  `--yes` to the command line.

## Compatibility

- Old server / feature disabled: the `features` bit is absent from the
  cache, the CLI never prompts, and the server's own confirmation flow
  (web confirmation card, pending envelope) applies unchanged. The schema
  then still carries `skip_confirmation` / `auto_skip_confirmation`, so
  they stay visible in `--help` (the CLI registers no hidden duplicate)
  and the `Confirmation:` help note is not shown; no deprecation hint.
- New-contract schema: only a CLI declaring **both** `cli-confirm-v1` and
  `cli-yes-only-v1` in `X-GSK-CLI-Caps`, whose user is inside the
  server-side gate, receives the schema without the skip parameters (and
  without the "Older CLIs require --project-id ..." sentence in action
  descriptions). `cli-yes-only-v1` is sent only by CLIs that register the
  hidden legacy flags and the `--args-file` allowlist described above.
  Earlier 1.9–1.11.x CLIs send `cli-confirm-v1` alone: they keep the
  legacy view, their `--skip_confirmation` / `auto_skip_confirmation`
  flags stay visible and keep working, and their args-file payloads still
  validate. Non-CLI callers and users outside the gate receive today's
  schema unchanged. The tools cache filename is derived from the caps
  string, so a cache written by an older CLI is never read by this one
  (and vice versa). A schema cache written before the server upgrade keeps
  the old view for up to 24h; `gsk --refresh ls` heals it.
- `--acp` mode: stdin is the JSON-RPC channel, so the ACP bridge keeps the
  old contract (interactive confirmation over ACP is a separate track).
- Stale schema cache: if the server still answers
  `data.status == "pending_confirmation"`, the CLI prints
  `schema cache stale, run 'gsk --refresh ls' and retry` and exits 1.
  The CLI never replays a call.
- `confirmation_exempt` is additive. CLIs that do not understand it ignore
  the key and keep whole-action gating (read ops still need `--yes` or an
  explicit skip parameter there). On an older server the key is absent and
  a CLI that understands it keeps whole-action gating too. A schema cache
  written before the server upgrade lacks the key for up to 24h — the
  result is whole-action gating (fail-closed), and `gsk --refresh ls`
  heals it immediately.
