Clarify inherited hook lifecycle ordering before empty-mode post-create plugin reset · Issue #2857 · github/copilot-sdk · GitHub
Skip to content

Clarify inherited hook lifecycle ordering before empty-mode post-create plugin reset #2857

Open

Description

@Flitschi7

Question

What supported SDK contract guarantees that inherited file or plugin hooks cannot execute before an embedding host can verify the session's effective isolation state?

This is a lifecycle/API-contract clarification, not a report of observed unauthorized startup execution. The phase-separated control below observes no start-hook marker; it does observe an end-hook marker during teardown.

Environment

  • Windows, Bun 1.4.3.
  • @github/copilot-sdk 1.0.24; bundled stdio runtime 1.0.101; RPC protocol 3.
  • mode: "empty", a fresh isolated baseDirectory and working directory, and useLoggedInUser: false for the offline controls.
  • No authentication, model call, user content, existing plugin installation, or user-state inspection is needed for the controls. Bun compatibility here is observed, not a claim of vendor certification.

Why the ordering matters

The multi-tenancy documentation describes empty mode applying installedPlugins: [] in a post-create/post-resume options update. The installed SDK client likewise awaits this update after the runtime creates the session, before returning from createSession().

A plugin snapshot obtained after createSession() returns, or pluginHookCount: 0 from a subsequent empty-plugin update, does not itself describe the earlier interval. On the pinned runtime, global hooks.discover also reports that the server does not support the plugin-hook discovery capability. An empty partial discovery result is therefore not a complete all-source inventory.

Synthetic phase-separated controls

Each case uses a new isolated directory, explicitly supplies one synthetic hook directory, and configures:

{
  enableFileHooks: false,
  enableConfigDiscovery: false, // varied in the final case
  availableTools: [],
  excludedTools: ["builtin:*", "mcp:*", "custom:*"],
  hookDirectories: [syntheticHookDirectory],
  pluginDirectories: [],
  onPermissionRequest: async () => ({ kind: "denied-interactively-by-user" }),
}

The synthetic hook JSON defines separate sessionStart and sessionEnd command actions. They run bun canary.ts start and bun canary.ts end, respectively; the script writes a different constant synthetic marker file for each phase. The host allocates the session UUID before creation, checks both markers immediately after createSession() returns, initializes/checks the offered tool list, deletes that UUID's session, stops the runtime, and checks both markers again. No prompt is sent.

Pre-start user setting Startup-injected managed settings Config discovery After creation (start, end) After teardown (start, end)
disableAllHooks: false None false (absent, absent) (absent, present)
disableAllHooks: true None false (absent, absent) (absent, present)
disableAllHooks: false permissions: { deny: ["shell(*)"], allow: [] } false (absent, absent) (absent, present)
disableAllHooks: true None true (absent, absent) (absent, present)

The user setting is written to the isolated settings.json before starting the runtime. The managed policy is supplied through managedSettings on createSession() and is accepted by the runtime. Every case has zero initialized offered tools and zero permission-callback invocations across the full lifecycle.

These are deliberately supplied explicit-hook controls, not evidence that restricted sessions inherit ambient hooks. They show end-hook execution, not a start-hook execution positive control or an early-execution vulnerability. They do not test whether managed permissions correctly deny ordinary model tool calls.

Requested clarification

  1. Can any inherited user, repository, plugin, or managed file hook execute during session.create, before the SDK's post-create empty-plugin update? If not, is that ordering guaranteed, and what operation first permits lifecycle-hook dispatch or lazy session materialization?
  2. If early execution is possible, which supported creation-time restriction disables all inherited hook sources before it, or exposes an authoritative verification gate before the first possible execution? An after-the-fact reset cannot establish that property.
  3. What is the intended SDK semantics of stored disableAllHooks for explicit hook roots, including sessionEnd, and which runtime/API capability should an embedding host use for authoritative inherited-plugin hook inspection?

A documented ordering guarantee paired with a regression would be sufficient; a new global-disable option is not assumed to be necessary. Please identify the runtime versions covered by the contract. No vendor source patch or different runtime was used in these observations.

Related, but different

References

The current public documentation was checked before filing, but no guarantee for the internal creation/post-create-update interval was identified. A pointer to an existing supported contract would resolve the question.

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