Skip to content

Expose enabled vs gated commands at connect time (#324)#340

Merged
tig merged 2 commits into
developfrom
feature/324-expose-gated-commands
Jul 8, 2026
Merged

Expose enabled vs gated commands at connect time (#324)#340
tig merged 2 commits into
developfrom
feature/324-expose-gated-commands

Conversation

@tig

@tig tig commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Closes #324.

Problem

A freshly provisioned session enables the standard observation/actuation tools (ToolCatalog.ProvisionedByDefault) but leaves launch and every raw send_command built-in (chars:, winr, mouse:, VK_* keys, chords) gated. Per the #318 field report, an agent could only discover that set by hitting command-disabled one command at a time.

Change

Expose the enabled/gated split at connect time so the agent can batch a single request-command-access before its first actuation.

  • initialize result now carries a commandAccess object:

    {
      "enabledTools": ["capture","query","displays","windows","window","find","wait-for","invoke","drag","click","focus","clipboard","record"],
      "gatedTools": ["launch"],
      "rawSendCommandGated": true,
      "note": "Gated commands need the operator's consent before first use: call request-command-access ... in ONE call ..."
    }

    Derived from ToolCatalog.CommandAccessDefaults (the ProvisionedByDefault flags) — single source of truth in code, so it never drifts as provisioning defaults change (acceptance criterion Possible to Map Command to Key Combo? #2). Omitted in bootstrap mode (only tools are provision-session/end-session; no gated surface).

  • session-start / session-status results stamp the live table via AgentToolExecutor.LiveCommandAccess, so an agent can re-check that a grant landed. Both surfaces share one builder (ToolCatalog.CommandAccessMap) and note, so shape/guidance can't diverge.

  • Connect-time prose (AgentInstructions.md PROVISION / COMMAND ACCESS) now points at the field instead of relying on trial-and-error; docs (agent_control.md, AGENTS.md) updated.

Why initialize over the provision handoff (issue Option A)

BuildAgentPrompt only renders on the operator's "Provision new…" dialog — a self-provisioning agent (bootstrap → provision-session → reconnect) never sees it, and it's operator-pasted prose decoupled from the instance. initialize is true connect-time, zero round-trip, reaches every path, and (via session-status) gives live post-grant accuracy.

Note on the "one call" goal

The realistic win is knowing the shape up front (launch + raw built-ins gated) so the agent batches the built-ins its plan uses in one round — it still can't enumerate specific built-ins it hasn't planned yet, so the target isn't literally zero command-disabled ever.

Acceptance

  • Agent connecting to a provisioned instance sees which commands need consent before first actuation (initialize.commandAccess).
  • List stays accurate when provisioning defaults change — derived from ToolCatalog, not hand-maintained prose.

Tests

  • Dispatch_Initialize_IncludesCommandAccessDefaultslaunch gated, capture/invoke enabled, rawSendCommandGated true, every gated name is a real catalog tool.
  • Dispatch_SessionStatus_IncludesLiveCommandAccess_ReflectingTheTable — live table drives the split; an enabled launch/chars: flips out of the gated set.
  • Full xUnit suite green (1039 passed, 22 skipped).

🤖 Generated with Claude Code

A freshly provisioned session enables the standard observation/actuation
tools but leaves `launch` and every raw `send_command` built-in (chars:,
winr, mouse:, VK_* keys, chords) gated. An agent could only learn that set
by hitting `command-disabled` one command at a time.

Expose the split up front so the agent can batch a single
request-command-access:

- `initialize` result now carries a `commandAccess` map
  (`enabledTools`/`gatedTools`/`rawSendCommandGated` + a recovery note),
  derived from `ToolCatalog.CommandAccessDefaults` (the ProvisionedByDefault
  flags — single source of truth, so it never drifts as defaults change).
- `session-start`/`session-status` stamp the LIVE table via
  `AgentToolExecutor.LiveCommandAccess`, so an agent can confirm a grant
  landed. Both surfaces share one builder (`ToolCatalog.CommandAccessMap`)
  and note, so shape and guidance can't diverge.
- Connect-time prose (AgentInstructions.md PROVISION/COMMAND ACCESS) now
  points at the field instead of relying on trial-and-error discovery.
- Docs (agent_control.md, AGENTS.md) updated.

Bootstrap mode omits the block (its only tools are
provision-session/end-session; no gated surface).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e010a4f9cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Services/AgentToolExecutor.cs Outdated
…#340)

Codex P2: deriving `rawSendCommandGated` from `chars:` alone meant a partial
grant (operator enables only `chars:`, a common first request) reported the
whole raw send_command surface as open, even though `winr`/`mouse:`/VK/chord
commands stayed disabled. An agent using session-status to confirm the grant
would then hit `command-disabled` on the next raw command.

Replace the lossy boolean with an explicit `enabledRawCommands` list:
- LiveCommandAccess enumerates the actual command table and lists every
  enabled entry that is not a catalog tool, so a partial grant shows exactly
  which raw commands are live (chars: present, winr/mouse: absent).
- CommandAccessDefaults reports it empty (no raw built-in is provisioned by
  default). Same shape on both surfaces via the shared CommandAccessMap.
- Note/docs/prose updated: a raw command not in enabledRawCommands still
  needs a request; a partial grant does not open the whole raw surface.

Test now grants chars: while leaving mouse: disabled and asserts mouse: is
absent from enabledRawCommands (and launch, a catalog tool, is not
double-reported as raw).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tig tig merged commit dd4d9f6 into develop Jul 8, 2026
7 checks passed
@tig tig deleted the feature/324-expose-gated-commands branch July 8, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose provisioned default enabled vs gated commands at connect

1 participant