Repository navigation
feat(plugin): expose session instruction entries to plugins - #56550
kitlangton wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
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.
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,SessionDomainon the plugin context omittedinstructions, leaving plugins without access to the keyed-context API. As a result, plugins such as GenUI fell back tosession.syntheticto 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:ctx.session.instructions.entry.list{ sessionID }ReadonlyArray<InstructionEntry.Info>Session.NotFoundErrorwhen the session does not exist.ctx.session.instructions.entry.put{ sessionID, key, value }voidapi/<key>), announced at the next step boundary without enqueuing inbox work; preservesSession.NotFoundErrorandInstructionEntry.ValueTooLargeError.ctx.session.instructions.entry.remove{ sessionID, key }voidSession.NotFoundErrorwhen 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 theserver.sessionprotocol group (session.instructions.entry.{list,put,remove}) and are generated ontoSessionApi["instructions"]["entry"]. Following the existing convention inSessionDomain—wheresession.wait(/api/experimental/session/:sessionID/wait) is picked directly fromSessionApiontoctx.session.wait, whilectx.experimentalis reserved for the separateserver.experimentalHTTP group andexperimental.ws.*for plugin-only hook names—instructionsis picked directly ontoSessionDomain.Scope
This PR exposes
ctx.session.instructions.entry.{list,put,remove}on the Effect and Promise plugin contexts, wiresPluginHosttoInstructionEntry.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 typecheckpackages/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 preserveSession.NotFoundErrorandInstructionEntry.ValueTooLargeError. Promise tests verify invalid-key decoding for put and remove, missing-session failures for all three methods, and void write results.packages/pluginandpackages/coretypechecks passed with 0 errors (and the pre-push workspacesdk-docs,oxlint,ast-grep, andturbo typecheckhooks 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)