Skip to content

[FEATURE]: plugin session step hook for plugin-requested compaction #56744

Open

Description

@SyuTingSong

Describe the enhancement you want to request

Plugins can register tools (tool.transform), observe tool calls (tool.execute.before/after), and even take over how a running compaction produces its summary (session.compaction), but there is no hook where a plugin can decide that a compaction should happen before the next provider request streams.

Today the engine starts a compaction on engine-owned triggers only: manual (user), auto (threshold check before each step), and overflow (provider rejects the request as context overflow, packages/core/src/session/compaction.ts:218). If none of those fire, the turn proceeds; a plugin has no way to say "preempt this turn, compact first."

Why it belongs in OpenCode:

  • It fills the last ungated spot in the loop. OpenCode already has decision-returning hooks of exactly this shape in three places: session.prompt gates admission, tool.execute.before gates a call, session.retry gates continuation after failure. Between "request built" and "request sent" there is only a rewrite-only hook (session.model.request), no decision hook.
  • It enables a real use case while keeping policy out of core: plugin-proposed compaction (for example a tool the model calls to suggest compaction, evaluated at the next step boundary with the plugin's own cooldown). Core keeps only the generic trigger.
  • The hook is reflect-only on the request — edits make no sense at that point, since the request is rebuilt after a compaction and session.context stays the place for rewrites. An approved decision simply enters SessionCompaction.compact like the other triggers; the only schema change is widening the compaction reason from three literals to an open string so plugins can report their own reason (e.g. "plugin").

Proposed interface, in the style of the existing session hooks:

// fired once per step, after the request is built and before the provider streams
interface SessionStep {
  sessionID: Session.ID
  agent: Agent.ID
  step: number          // resets to 1 when a new inbox item delivers
  model: Model.Ref
  request: {            // reflect-only snapshot
    readonly system: readonly SystemPart[]
    readonly messages: readonly Message[]
    readonly tools: Readonly<Record<string, { description: string; input: JsonSchema }>>
  }
  entries: readonly SessionMessage.Info[]   // projected history, oldest first
  result?: { compact: boolean; reason?: string }  // request compaction at this boundary
}

I have this implemented and tested locally: the engine trigger in the session runner, the hook interface in packages/plugin, and a working suggest-compact plugin on top of it (tool registered via tool.transform, evaluated at the step boundary, requests compaction; verified end-to-end with requested → evaluated → compaction server logs plus the session-runner test suite).

Happy to push a branch with a small focused PR (the hook, and the plugin as a consumer) once there is a go-ahead on the design.

Activity

  1. opencode-agent commented on Oct 18, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed proposal and for already prototyping it!

    To restate it: you're asking for a per-step plugin hook that runs after the provider request is built and before it streams. It would give plugins a read-only view of the request and history, and let them ask for a compaction at that step boundary. The compaction reason would become an open string so plugins can report their own reason. Right now only the engine starts a compaction (manual, auto threshold, overflow), and the existing hooks can only rewrite the request or shape a compaction that is already running. So a plugin can't build this today without a hook like the one you describe.

    Maintainers will look at the design and reply about whether to go ahead with a PR. These issues are related but ask for different things:

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