GitHub Copilot: one hook for Copilot CLI and VS Code agent mode (0.26.0) - #44
Merged
Merged
Conversation
Copilot CLI and VS Code both read ~/.copilot/hooks/*.json in Claude Code's
hook format, but they disagree in two places the hook has to absorb:
- VS Code sends its own tool ids (run_in_terminal, create_file,
replace_string_in_file ...) with camelCase inputs, while Copilot CLI's
PascalCase payload already uses Claude's names. Canonicalize VS Code's
dialect once, before any decision, so the hardline floor, dotted Bash.*
policy and the gateway classifier see one vocabulary. Exact-match only,
and the dialect tell excludes names Cursor also uses.
- Copilot CLI documents the bare {permissionDecision} output; VS Code
documents the hookSpecificOutput wrapper. Under ACP_HARNESS=copilot every
PreToolUse verdict is written in both shapes.
Attribute VS Code calls as copilot-vscode. On a box where Claude Code was
wired without the plugin, VS Code also runs the ~/.claude/settings.json
hook; when the Copilot registration exists that hook stands down for VS
Code's calls, so one governed call produces one row. No vendor-token
updatedInput under copilot: the CLI ignores it and VS Code schema-drops it.
Version 0.23.0: the hook file changed, and attestation is trust-on-first-use
per (client, version), so shipping this under 0.22.0 would flag every fresh
install as an edited hook.
Nine tests spawn the real hook against a stub gateway: both output shapes
for deny and ask, VS Code canonicalization and attribution, camelCase key
twins, string tool_input, stand-down with and without the registration,
local-mode hardline deny, and no token injection.
davidcrowe
force-pushed
the
feat/copilot-harness
branch
from
September 22, 2026 20:04
ecd942b to
a278b71
Compare
Collaborator
Author
|
Bumped to 0.23.0 (plugin.json, marketplace.json, govern.mjs). Attestation is trust-on-first-use per (client, version), so a changed hook under 0.22.0 would have flagged every fresh install as hash_mismatch. After merge, set the registry's latest to 0.23.0 (gatewaystack-connect scripts/_tmp_set_hook_registry_0914.mjs 0.23.0 0.16.0), and consider adding copilot and copilot-vscode to its client list. |
main moved to 0.25.0 while this branch was cut at 0.22.0. Only the three version lines conflicted. Attestation is trust-on-first-use per version, so this ships as 0.26.0, above the live 0.25.0. Full suite 278/278 on the merged tree.
Collaborator
Author
|
Correction: main was already at 0.25.0, so this now ships as 0.26.0 (merged origin/main in; only the version lines conflicted; 278/278). The registry was set to latest=0.23.0 in the meantime, which is below the live 0.25.0 — after merging, re-run: node scripts/_tmp_set_hook_registry_0914.mjs 0.26.0 0.16.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GitHub Copilot: one hook for Copilot CLI and VS Code agent mode
Copilot CLI and VS Code's agent mode both read
~/.copilot/hooks/*.jsonin Claude Code's hook format. The installer (agenticcontrolplane.com, companion PR) writes one file there; this PR makesgovern.mjsabsorb the two places the readers disagree.What changes under
ACP_HARNESS=copilotrun_in_terminal,create_file,replace_string_in_file, …) with camelCase inputs (filePath). Copilot CLI's PascalCase payload already uses Claude's names. VS Code's dialect is canonicalized once, before any decision, so the hardline floor, dottedBash.*policy and the gateway classifier see one vocabulary. Exact-match only.{permissionDecision}output; VS Code documents thehookSpecificOutputwrapper. Every PreToolUse verdict is written in both shapes.copilot-vscode; CLI calls staycopilot.~/.claude/settings.jsonhook. When the Copilot registration exists, that hook stands down for VS Code's calls: one governed call, one row.updatedInputvendor-token injection under copilot. Copilot CLI ignores it (preToolUse hookSpecificOutput.updatedInput is ignored in Copilot CLI github/copilot-cli#2013) and VS Code drops anyupdatedInputthat fails the tool's schema.What does not change for other harnesses
The only code that runs for a non-copilot registration is the VS Code-dialect tell, and the tell set deliberately excludes names Cursor also uses (
grep_search,file_search,read_file). Stringtool_inputparsing is scoped toACP_HARNESS=copilot.Tests
test/copilot-dialect.test.mjsspawns the real hook against a stub gateway: both output shapes for deny and ask, VS Code canonicalization and attribution, camelCase key twins, stringtool_input, stand-down with and without the registration, local-mode hardline deny, no token injection. Full suite 252/252.Verification limits, stated plainly
Contract-verified against GitHub's hooks reference, the Copilot CLI hooks guide and VS Code's hooks reference (2026-09-21). Not exercised against a live Copilot session: Copilot CLI 1.0.87 picks up the
ghlogin and this account has no Copilot access. VS Code agent hooks are Preview. GitHub's tracker records a CLI version where apreToolUsedeny was not honoured (github/copilot-cli#3874).Merge order
This first.
install.shfetchesbin/govern.mjsfrommainat install time, so the site PR's Copilot registration governs nothing until this lands.