diff --git a/integrations/README.md b/integrations/README.md index 52a9930..b25d0dc 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -6,6 +6,8 @@ The provider-level contract represents the default stable runtime. `contract_var Grok Build adapters write native user hook files under `$GROK_HOME/hooks` or `~/.grok/hooks`. Project `.grok/hooks` files require `/hooks-trust` or `--trust`; Claude Code and Cursor hook files are read-only compatibility sources for Elydora. +GitHub Copilot command `preToolUse` hooks deny on crashes and every non-zero exit. Command timeouts continue through the normal permission flow, so `timeout_failure_mode` records that explicit exception when it differs from `failure_mode`. + `delivery_state` is computed from the `node`, `python`, and `go` adapter flags: | State | Meaning | diff --git a/integrations/catalog.json b/integrations/catalog.json index da8df72..d40aea8 100644 --- a/integrations/catalog.json +++ b/integrations/catalog.json @@ -122,6 +122,7 @@ "integration_mode": "command_hooks", "config_format": "json", "config_paths": [ + "$COPILOT_HOME/hooks/*.json", "~/.copilot/hooks/*.json", ".github/hooks/*.json" ], @@ -129,9 +130,15 @@ "before_tool": "preToolUse", "after_tool": "postToolUse" }, + "event_fields": { + "name": "toolName", + "input": "toolArgs", + "session": "sessionId" + }, "blocking": { - "mechanism": "exit_code_2", - "failure_mode": "fail_open" + "mechanism": "any_nonzero_exit", + "failure_mode": "fail_closed", + "timeout_failure_mode": "fail_open" }, "adapters": { "node": true, "python": true, "go": true }, "delivery_state": "available", diff --git a/integrations/catalog.schema.json b/integrations/catalog.schema.json index 0f83a15..e1e3dcc 100644 --- a/integrations/catalog.schema.json +++ b/integrations/catalog.schema.json @@ -148,6 +148,12 @@ "fail_closed", "fail_open" ] + }, + "timeout_failure_mode": { + "enum": [ + "fail_closed", + "fail_open" + ] } } }, @@ -332,6 +338,12 @@ "fail_closed", "fail_open" ] + }, + "timeout_failure_mode": { + "enum": [ + "fail_closed", + "fail_open" + ] } } }, diff --git a/test/integration-catalog.test.mjs b/test/integration-catalog.test.mjs index b74f4ed..958f977 100644 --- a/test/integration-catalog.test.mjs +++ b/test/integration-catalog.test.mjs @@ -219,6 +219,10 @@ test('schema freezes the provider contract and supported enums', async () => { 'json_stdout_cancel', ), ); + assert.deepEqual( + providerSchema.properties.blocking.properties.timeout_failure_mode.enum, + ['fail_closed', 'fail_open'], + ); assert.deepEqual(providerSchema.properties.delivery_state.enum, ['available', 'partial', 'planned']); assert.equal(providerSchema.properties.contract_variants.items.$ref, '#/$defs/contractVariant'); assert.deepEqual(schema.$defs.contractVariant.properties.release_channel.enum, [ @@ -273,6 +277,25 @@ test('high-drift providers retain their verified hook contracts', async () => { '~/.codex/hooks.json', '.codex/hooks.json', ]); + assert.deepEqual(providers.get('copilot').config_paths, [ + '$COPILOT_HOME/hooks/*.json', + '~/.copilot/hooks/*.json', + '.github/hooks/*.json', + ]); + assert.deepEqual(providers.get('copilot').events, { + before_tool: 'preToolUse', + after_tool: 'postToolUse', + }); + assert.deepEqual(providers.get('copilot').event_fields, { + name: 'toolName', + input: 'toolArgs', + session: 'sessionId', + }); + assert.deepEqual(providers.get('copilot').blocking, { + mechanism: 'any_nonzero_exit', + failure_mode: 'fail_closed', + timeout_failure_mode: 'fail_open', + }); assert.deepEqual(providers.get('droid').config_paths, [ '~/.factory/hooks.json', '~/.factory/settings.json',