Pending permission recovery: handler not re-invoked and skill context missing after approval · Issue #2852 · github/copilot-sdk · GitHub
Skip to content

Pending permission recovery: handler not re-invoked and skill context missing after approval #2852

Open

Description

@mingtwan

Description

We observed two issues when resuming a session with a pending permission request:

  1. With continuePendingWork: true, onPermissionRequest is not re-invoked, contrary to the documentation.
  2. Explicitly approving the restored permission through handlePendingPermissionRequest() successfully executes the original skill, but its instructions are missing from the subsequent model request.

Environment

  • Node.js SDK: @github/copilot-sdk version 1.0.17
  • Copilot CLI: 1.0.93
  • Local deterministic model endpoint; built-in skill execution, permission handling, persistence, and recovery use the real runtime.

The model endpoint requests a local, harmless skill on its first call. We inspect the actual subsequent model request to determine whether the skill instructions are included; we do not infer this from the model’s final response.

Reproduction

Create a session with a local skill and these handlers:

hooks: {
  onPreToolUse: (input) =>
    input.toolName === "skill"
      ? {
          permissionDecision: "ask",
          permissionDecisionReason: "Local consent probe",
        }
      : undefined,
},
onPermissionRequest: (request, invocation) => {
  if (request.kind === "hook" && request.toolName === "skill") {
    return { kind: "no-result" };
  }
  return approveAll(request, invocation);
},
  1. Trigger the skill and wait for permission.requested. Record its requestId.

  2. Explicitly save the session:

    await client.rpc.sessions.save({ sessionId });
  3. Stop the runtime, start a new client, and resume the same session with the same configuration and handlers:

    const session = await newClient.resumeSession(sessionId, {
      ...config,
      continuePendingWork: true,
    });
  4. Initially, do not send a new prompt or complete the pending permission. Observe the handler and query session.rpc.permissions.pendingRequests().

  5. Then explicitly approve the original pending request:

    await session.rpc.permissions.handlePendingPermissionRequest({
      requestId: originalRequestId,
      result: { kind: "approve-once" },
    });
  6. Inspect tool events, post-tool hook input, and the next request received by the model endpoint.

Issue 1: Permission handler is not re-invoked

The continuePendingWork documentation states:

For permission requests, the runtime re-emits permission.requested so the registered onPermissionRequest handler can re-prompt.

Expected: The registered handler is invoked again for the restored pending permission.

Actual: At 1, 5, and 15 seconds after resume:

  • The original permission remains pending with the same requestId.
  • onPermissionRequest is not invoked again.
  • No new permission.requested event appears in session history.
  • No further tool execution or model request occurs.

Handler invocations were recorded inside the callback itself, including during resumeSession(), rather than relying only on an event listener attached afterward.

Issue 2: Approval executes the skill but does not inject its context

Explicitly completing the restored permission returns { success: true }. The original skill then executes successfully, without a new prompt or a new model-generated tool call.

However:

  • The post-tool hook receives a successful skill result containing the full instructions, including newMessages with a <skill-context> message and skillInvocation metadata.

  • tool.execution_complete.result.detailedContent also contains the full instructions.

  • No skill.invoked event is recorded.

  • The subsequent model request contains only the short tool receipt, not the skill instructions:

    Skill "consent-probe" loaded successfully.
    Follow the instructions in the skill context.
    

Expected: After approval, the skill instructions should be included in the subsequent model request, as they are when approval occurs without restarting the runtime.

Actual: The runtime reports successful skill loading, but the model receives no corresponding skill context.

The observed event sequence is:

permission.requested
session.shutdown
session.resume                 // continuePendingWork: true
                               // no permission handler re-invocation
permission.completed           // explicit completion RPC
tool.execution_start           // original toolCallId
preToolUse
postToolUse                    // full skill result available
tool.execution_complete        // success; detailedContent contains instructions
                               // no skill.invoked event
next model request             // short receipt only; skill context missing

Controls and additional observations

  • Both issues reproduce after graceful shutdown and after SIGKILL following an explicit session save.
  • Without a restart, approving the pending permission produces skill.invoked and includes the skill instructions in the next model request.
  • Replacing the initial handler’s no-result return with an unresolved Promise reproduces both behaviors after restart.
  • The original tool call completes successfully exactly once after approval.

Questions

  1. Is the absence of permission re-emission expected for this version, or is it a discrepancy between implementation and documentation?
  2. Does the resumed tool-completion path apply the skill result’s additional messages and invocation metadata in the same way as the normal execution path? The result contains the instructions, but they are absent from the next model request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions