diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index b746fdecf23..c042242fd40 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -256,6 +256,11 @@ export namespace SessionPrompt { await using _ = defer(async () => { await processor.end() }) + + // Dynamically append plugin prompts for this turn + const dynamicPrompts = await appendPluginPrompts(input.sessionID, agent, msgs) + const turnSystem = [...system, ...dynamicPrompts] + const doStream = () => streamText({ onError(error) { @@ -308,7 +313,7 @@ export namespace SessionPrompt { temperature: params.temperature, topP: params.topP, messages: [ - ...system.map( + ...turnSystem.map( (x): ModelMessage => ({ role: "system", content: x, @@ -483,6 +488,38 @@ export namespace SessionPrompt { return Provider.defaultModel() } + async function appendPluginPrompts( + sessionID: string, + agent: Agent.Info, + history: MessageV2.WithParts[], + ): Promise { + const appendedPrompts: string[] = [] + try { + const output = { prompt: [] as string[] } + await Plugin.trigger( + "system.prompt.append", + { + sessionID, + agent, + // Convert internal MessageV2 types to SDK types for plugin boundary + history: history.map((m) => ({ + info: m.info as any, // Cast to SDK message types + parts: m.parts, + })), + }, + output, + ) + // Basic validation + if (Array.isArray(output.prompt)) { + appendedPrompts.push(...output.prompt.filter((p) => typeof p === "string")) + } + } catch (error) { + console.error(`Error triggering 'system.prompt.append' hook:`, error) + // Gracefully continue without plugin content + } + return appendedPrompts + } + async function resolveSystemPrompt(input: { system?: string agent: Agent.Info diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index 0601e587767..7689ced0fbd 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -6,9 +6,11 @@ import type { Provider, Permission, UserMessage, + AssistantMessage, Part, Auth, Config, + Agent, } from "@opencode-ai/sdk" import type { BunShell } from "./shell" @@ -16,6 +18,8 @@ import { type ToolDefinition } from "./tool" export * from "./tool" +export type MessageWithParts = { info: UserMessage | AssistantMessage; parts: Part[] } + export type PluginInput = { client: ReturnType project: Project @@ -167,4 +171,19 @@ export interface Hooks { metadata: any }, ) => Promise + /** + * Append text to the system prompt. + * This hook is called at the beginning of each step in a turn, allowing plugins + * to dynamically add context to the system prompt based on the latest history. + */ + "system.prompt.append"?: ( + context: { + sessionID: string + agent: Agent + history: MessageWithParts[] + }, + output: { + prompt: string[] + }, + ) => Promise }