Created
February 12, 2026 06:50
-
-
Save cameroncking/848fa78054993762cda96e8e24048162 to your computer and use it in GitHub Desktop.
Easily debug OpenCode system prompts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // .opencode/plugins/log-system-prompts.js | |
| // log system prompts to .opencode/system-prompts.jsonl | |
| // | |
| // This makes it easier to directly investigate how OpenCode constructs | |
| // system prompts and what environment data is being provided to the model. | |
| // This does not include tool call schema information. | |
| // | |
| import fs from "fs/promises" | |
| import path from "path" | |
| export const LogSystemPrompts = async ({ worktree, client }) => { | |
| const outPath = path.join(worktree, ".opencode", "system-prompts.jsonl") | |
| // We don't get message metadata in `experimental.chat.system.transform`, so we | |
| // capture the latest system array per session and then log it in `chat.params`. | |
| /** @type {Map<string, { at: number, system: string[] }>} */ | |
| const lastSystemBySession = new Map() | |
| async function append(line) { | |
| try { | |
| await fs.appendFile(outPath, line + "\n", "utf8") | |
| } catch (err) { | |
| // Avoid crashing the whole session if logging fails. | |
| try { | |
| const message = err instanceof Error ? err.message : String(err) | |
| await client.app.log({ | |
| body: { | |
| service: "log-system-prompts", | |
| level: "error", | |
| message: `Failed to append to ${outPath}: ${message}`, | |
| }, | |
| }) | |
| } catch { | |
| // ignore | |
| } | |
| } | |
| } | |
| return { | |
| "experimental.chat.system.transform": async (input, output) => { | |
| const sessionID = input?.sessionID ?? "" | |
| if (!sessionID) return | |
| const system = Array.isArray(output?.system) ? output.system.map(String) : [] | |
| lastSystemBySession.set(sessionID, { at: Date.now(), system }) | |
| }, | |
| "chat.params": async (input, output) => { | |
| const sessionID = input?.sessionID ?? "" | |
| if (!sessionID) return | |
| const sys = lastSystemBySession.get(sessionID) | |
| // In case hooks fire in an unexpected order, still log what we can. | |
| const system = sys?.system ?? [] | |
| const systemAgeMs = sys ? Date.now() - sys.at : null | |
| const payload = { | |
| at: new Date().toISOString(), | |
| outPath, | |
| sessionID, | |
| agent: input?.agent?.name ?? input?.agent?.mode ?? null, | |
| model: { | |
| providerID: input?.model?.providerID ?? null, | |
| modelID: input?.model?.id ?? input?.model?.modelID ?? null, | |
| }, | |
| provider: { | |
| id: input?.provider?.id ?? null, | |
| name: input?.provider?.name ?? null, | |
| }, | |
| message: { | |
| id: input?.message?.id ?? null, | |
| }, | |
| system, | |
| systemAgeMs, | |
| // OpenAI OAuth (Codex) uses `options.instructions` (not a system message). | |
| requestInstructions: | |
| output?.options && typeof output.options.instructions === "string" ? output.options.instructions : null, | |
| } | |
| await append(JSON.stringify(payload)) | |
| }, | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment