Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Extension SessionHooks missing onPostToolUseFailure handler — failed tool results are invisible

未关闭
#1,220 4 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

@patniko 已经在做这个了。

开始于 2026年5月19日。

评估

这个 Issue 还没有评估数据。

描述

bug

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:

    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-pkg>/copilot-sdk/extension.js (the bundled SDK shipped with the Copilot CLI).

The runtime hook dispatcher exposes only 6 hook types:

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:

// 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:

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):

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:

 export interface SessionHooks {
   onPreToolUse?: PreToolUseHandler;
   onPostToolUse?: PostToolUseHandler;
+  onPostToolUseFailure?: PostToolUseFailureHandler;
   onUserPromptSubmitted?: UserPromptSubmittedHandler;
   onSessionStart?: SessionStartHandler;
   onSessionEnd?: SessionEndHandler;
   onErrorOccurred?: ErrorOccurredHandler;
 }
 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 <install>/copilot-sdk/extension.js.
  • Confirmed by inspecting the shipped JS and the TypeScript declarations in the same package.
主要语言
Java
星标
10.5k
派生
1.5k
平均合并
1 天 9 小时
30 天内合并 PR
130

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

github/copilot-sdk 的其他 Issue

查看 github/copilot-sdk 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。