github / github/copilot-sdk

Extension SessionHooks missing onPostToolUseFailure handler — failed tool results are invisible

Offen
#1,220 4 Kommentare 0 Reaktionen 1 zugewiesene Person Beansprucht von @patniko Auf GitHub ansehen
bug
Vorherrschende Sprache
Java
Sterne
10.5k
Forks
1.5k
Ø Merge
1 T. 11 Std.
Gemergte PRs (30 T.)
128

Beschreibung

## Summary

Extensions registering hook handlers via `joinSession({ hooks })` cannot observe non-success tool results. `onPostToolUse` only fires for `resultType === "success"` — failure / rejected / denied / timeout outcomes are silently invisible to extensions.

The underlying CLI does emit a `postToolUseFailure` hook event for failed tool calls, but the SDK never dispatches it: it is missing from both the `SessionHooks` interface and the runtime `_handleHooksInvoke` handler map.

## Repro

1. In an extension, register a session via `joinSession` with handlers for both `onPostToolUse` and (attempting) `onPostToolUseFailure`:

```js
import { joinSession } from "@github/copilot-sdk/extension";
const session = await joinSession({
hooks: {
onPostToolUse: async (input) => {
console.log("post:", input.toolName, input.toolResult?.resultType);
},
// Not declared on SessionHooks, but try anyway:
onPostToolUseFailure: async (input) => {
console.log("post-failure:", input.toolName);
},
},
});
```

2. Have Copilot run a tool that fails (e.g. a shell command that exits non-zero, then a tool that times out, then one that triggers a "rejected" / "denied" outcome).

3. Observe: `onPostToolUse` fires only for the successful tool calls. `onPostToolUseFailure` never fires for any of the failure modes.

## Expected

Extensions should be able to observe **all** terminal tool outcomes — success, failure, rejected, denied, timeout — through the hooks system. Either:

- Add `onPostToolUseFailure` to `SessionHooks` and dispatch it from `_handleHooksInvoke`, or
- Always fire `onPostToolUse` regardless of `resultType` (with the `resultType` field on the input distinguishing outcomes).

## Evidence in shipped CLI `1.0.43-0`

Path: `/copilot-sdk/extension.js` (the bundled SDK shipped with the Copilot CLI).

The runtime hook dispatcher exposes only 6 hook types:

```js
async _handleHooksInvoke(hookType, input) {
if (!this.hooks) return void 0;
const handlerMap = {
preToolUse: this.hooks.onPreToolUse,
postToolUse: this.hooks.onPostToolUse,
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
sessionStart: this.hooks.onSessionStart,
sessionEnd: this.hooks.onSessionEnd,
errorOccurred: this.hooks.onErrorOccurred,
};
const handler = handlerMap[hookType];
if (!handler) return void 0; // silently dropped
...
}
```

The matching TypeScript declaration in `copilot-sdk/types.d.ts` `SessionHooks` declares the same 6 handlers, so authors using TypeScript can't even discover the missing types.

Meanwhile, the host CLI (`app.js` in the same package) builds a hook proxy per connected extension that DOES include `postToolUseFailure`:

```js
// createHooksProxy(sessionId, connection)
return {
preToolUse: [o => n("preToolUse", o)],
postToolUse: [o => n("postToolUse", o)],
postToolUseFailure: [o => n("postToolUseFailure", o)],
userPromptSubmitted: [o => n("userPromptSubmitted", o)],
sessionStart: [o => n("sessionStart", o)],
sessionEnd: [o => n("sessionEnd", o)],
errorOccurred: [o => n("errorOccurred", o)],
};
```

And the host's `processToolExecutionResult` explicitly gates `postToolUse` on success, routing failures elsewhere:

```js
async processToolExecutionResult(toolName, toolArgs, toolResult) {
let s = (toolResult.resultType === "success"
? await h1(this.getEffectiveHooks()?.postToolUse, ...)
: void 0)?.modifiedResult ?? toolResult;

let a = s.resultType === "failure"
? await this.runPostToolUseFailureHooks(toolName, toolArgs, s)
: void 0;
...
}
```

So the CLI sends `hooks.invoke` requests with `hookType: "postToolUseFailure"` to the extension over IPC, but the SDK's `_handleHooksInvoke` doesn't recognize that hook type and returns `undefined` without invoking any handler.

The full set of hook types the CLI knows about (`app.js`):

```js
new Set([
"sessionStart", "sessionEnd", "userPromptSubmitted",
"preToolUse", "postToolUse", "postToolUseFailure",
"errorOccurred",
"agentStop", "subagentStop", "subagentStart",
"preCompact", "permissionRequest", "notification",
])
```

The SDK's hooks API exposes 6 of these to extensions. Several of the missing ones (like `permissionRequest`) are intentionally exposed via different APIs (`onPermissionRequest` in the `JoinSessionConfig`), so this report intentionally focuses on the most clear-cut gap: `postToolUseFailure`.

## Impact

Extensions that want to observe or react to tool failures — for telemetry, replay buffers, fault-injection tests, or UI that visualizes tool execution — have no reliable way to do so. Since post-only-on-success silently drops the failure cases, extensions can wrongly report "no result" or, if pairing pre with post, leak resources tracking calls whose post will never come.

## Suggested fix

Add the missing handler to `_handleHooksInvoke`'s map and to the `SessionHooks` TypeScript interface:

```diff
export interface SessionHooks {
onPreToolUse?: PreToolUseHandler;
onPostToolUse?: PostToolUseHandler;
+ onPostToolUseFailure?: PostToolUseFailureHandler;
onUserPromptSubmitted?: UserPromptSubmittedHandler;
onSessionStart?: SessionStartHandler;
onSessionEnd?: SessionEndHandler;
onErrorOccurred?: ErrorOccurredHandler;
}
```

```diff
const handlerMap = {
preToolUse: this.hooks.onPreToolUse,
postToolUse: this.hooks.onPostToolUse,
+ postToolUseFailure: this.hooks.onPostToolUseFailure,
userPromptSubmitted: this.hooks.onUserPromptSubmitted,
sessionStart: this.hooks.onSessionStart,
sessionEnd: this.hooks.onSessionEnd,
errorOccurred: this.hooks.onErrorOccurred,
};
```

The input shape can mirror `PostToolUseHookInput` (`toolName` + `toolArgs` + `toolResult`), since the CLI's emit site already provides the same payload.

## Environment

- `@github/copilot` `1.0.43-0` (Windows `win32-x64` package).
- Bundled SDK at `/copilot-sdk/extension.js`.
- Confirmed by inspecting the shipped JS and the TypeScript declarations in the same package.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.