Skip to content

feat(plugin): expose session instruction entries to plugins - #56550

Open
kitlangton wants to merge 2 commits into
devfrom
feat/plugin-session-instructions
Open

kitlangton wants to merge 2 commits into
devfrom
feat/plugin-session-instructions

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Why

OpenCode V2 already supports durable, keyed per-session instruction entries (session.instructions.entry.{list,put,remove}), which render as <context key="..."> blocks and announce updates or removals at the next step boundary without queuing inbox work or waking execution. However, SessionDomain on the plugin context omitted instructions, leaving plugins without access to the keyed-context API. As a result, plugins such as GenUI fell back to session.synthetic to persist session reminders, which admits a steered inbox item and triggered an unintended extra model turn.

What Changes

Both Effect and Promise plugins can now manage keyed session instruction entries through ctx.session.instructions.entry:

await ctx.session.instructions.entry.put({
  sessionID,
  key: "deploy-target",
  value: "production",
})

const entries = await ctx.session.instructions.entry.list({ sessionID })

await ctx.session.instructions.entry.remove({
  sessionID,
  key: "deploy-target",
})
Method Input Output Behavior
ctx.session.instructions.entry.list { sessionID } ReadonlyArray<InstructionEntry.Info> Lists active instruction entries attached to the session; fails with Session.NotFoundError when the session does not exist.
ctx.session.instructions.entry.put { sessionID, key, value } void Attaches or replaces a keyed JSON instruction entry (api/<key>), announced at the next step boundary without enqueuing inbox work; preserves Session.NotFoundError and InstructionEntry.ValueTooLargeError.
ctx.session.instructions.entry.remove { sessionID, key } void Marks the keyed entry removed so the next step boundary announces its removal; fails with Session.NotFoundError when the session does not exist.

Surface Naming Convention

Although the underlying HTTP routes live under /api/experimental/session/:sessionID/instructions/entries, the endpoints belong to the server.session protocol group (session.instructions.entry.{list,put,remove}) and are generated onto SessionApi["instructions"]["entry"]. Following the existing convention in SessionDomain—where session.wait (/api/experimental/session/:sessionID/wait) is picked directly from SessionApi onto ctx.session.wait, while ctx.experimental is reserved for the separate server.experimental HTTP group and experimental.ws.* for plugin-only hook names—instructions is picked directly onto SessionDomain.

Scope

This PR exposes ctx.session.instructions.entry.{list,put,remove} on the Effect and Promise plugin contexts, wires PluginHost to InstructionEntry.Service, and documents the plugin surface. Migrating GenUI or Plan mode onto instruction entries is handled separately. Merge #56689 first so Effect plugin writes also validate instruction entry keys in Core; Promise plugins already decode keys through the protocol schemas.

Verification

taskpolicy -c utility bun --cwd packages/core test test/plugin/session-instructions.test.ts
taskpolicy -c utility bun --cwd packages/plugin typecheck
taskpolicy -c utility bun --cwd packages/core typecheck
  • packages/core/test/plugin/session-instructions.test.ts: 2 passed, 26 assertions. Both plugin APIs put, list, and remove entries observed by request-history assembly without enqueuing inbox items; the Effect test also verifies updates. Both preserve Session.NotFoundError and InstructionEntry.ValueTooLargeError. Promise tests verify invalid-key decoding for put and remove, missing-session failures for all three methods, and void write results.
  • packages/plugin and packages/core typechecks passed with 0 errors (and the pre-push workspace sdk-docs, oxlint, ast-grep, and turbo typecheck hooks passed across all 34 packages).

Added by the opencode bot (AI-generated).

Public API: adds ctx.session.instructions.entry.{list,put,remove} to the Effect and Promise plugin contexts (backwards compatible)

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I ran the core plugin test suite (395 pass, including the new session-instructions.test.ts) and the packages/plugin and packages/core typechecks (clean). The host checks the session exists before each operation and calls the same InstructionEntry.Service as the HTTP handlers. The Promise adapter reuses the existing SessionEndpoints definitions, so input validation matches the other session methods.

Public API: adds ctx.session.instructions.entry.list, .put and .remove to the Effect and Promise plugin contexts (SessionDomain now picks instructions). This is backwards compatible for existing plugins. Only code that builds its own Plugin.Context object would need to add the new field.

No blocking concerns.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant