Conversation
… ensure errors are logged to client
There was a problem hiding this comment.
Pull request overview
This PR updates the kibi-opencode plugin’s logging behavior to be silent in normal operation while keeping errors visible, and adjusts prompt-injection hooks to avoid overwriting existing system prompt entries.
Changes:
- Route
info/warnlogs through structuredclient.app.log()and keep error-class events visible viaconsole.error. - Change scheduler + bootstrap-needed messaging to emit via the error channel for terminal visibility.
- Update prompt injection behavior and tests to preserve existing
output.systementries.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/opencode/src/logger.ts | Introduces client-backed structured logging with console-visible errors. |
| packages/opencode/src/index.ts | Injects logger client; changes prompt injection to append instead of replace; adjusts log levels. |
| packages/opencode/src/scheduler.ts | Treats sync/check failures as errors (terminal-visible). |
| packages/opencode/tests/logging-policy.test.ts | Adds coverage ensuring console silence for info/warn and structured log routing. |
| packages/opencode/tests/index.test.ts | Resets logger client between tests; updates system prompt assertions. |
| packages/opencode/tests/config.test.ts | Switches config warning assertions to spy on logger.warn. |
| packages/opencode/tests/hook-contract.test.ts | Adds contract tests for append-only system.transform and chat.params behavior. |
| packages/opencode/README.md | Documents the logging policy. |
| packages/opencode/package.json | Bumps package version. |
| packages/opencode/CHANGELOG.md | Adds 0.5.3 entry describing the logging change. |
packages/opencode/src/logger.ts
Outdated
| if (client) { | ||
| client.app.log({ | ||
| body: { | ||
| service: "kibi-opencode", | ||
| level: "warn", | ||
| message: msg, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Same as info: this client.app.log(...) Promise is not awaited/handled, which can lead to unhandled promise rejections if the host rejects. Handle the Promise explicitly (e.g., void + .catch(...)).
packages/opencode/src/logger.ts
Outdated
| if (client) { | ||
| client.app.log({ | ||
| body: { | ||
| service: "kibi-opencode", | ||
| level: "error", | ||
| message: msg, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
The structured client.app.log(...) call is not awaited/handled here either. Since console.error is synchronous but the structured log is async, failures will surface as unhandled rejections unless you explicitly handle the Promise (e.g., void ... .catch(...)).
packages/opencode/src/index.ts
Outdated
| // Inject structured logger client so startup/hook messages route to | ||
| // client.app.log() instead of console when the host provides a client. | ||
| if (input.client) { | ||
| logger.setClient(input.client); | ||
| } |
There was a problem hiding this comment.
logger keeps client in a module-global variable, but the plugin only calls setClient when input.client is truthy. If the plugin is instantiated multiple times in-process, a previous client's logger can leak into a later invocation (including when input.client is omitted). Consider resetting the logger client at the start of each plugin invocation and then setting it when provided.
packages/opencode/src/index.ts
Outdated
| if (injected === currentSystem) { | ||
| return; | ||
| } | ||
| const last = | ||
| output.system.length > 0 | ||
| ? output.system[output.system.length - 1] | ||
| : undefined; | ||
| if (last !== injected) { | ||
| output.system.push(injected); |
There was a problem hiding this comment.
In system.transform, you append injectPrompt(currentSystem, ...) to output.system, but injectPrompt returns ${current}\n\n${buildPrompt(...)}. When output.system already has entries, currentSystem is the join of them, so the appended entry contains the entire existing system prompt again, effectively duplicating prior prompts. To truly be append-only, append only the guidance block (e.g., buildPrompt(context)), and/or check for the sentinel across existing entries instead of using injectPrompt with the joined content.
| if (injected === currentSystem) { | |
| return; | |
| } | |
| const last = | |
| output.system.length > 0 | |
| ? output.system[output.system.length - 1] | |
| : undefined; | |
| if (last !== injected) { | |
| output.system.push(injected); | |
| // If injectPrompt made no changes, there's nothing to append. | |
| if (injected === currentSystem) { | |
| return; | |
| } | |
| // injectPrompt is expected to return `${currentSystem}\n\n${guidance}`. | |
| // Derive just the new guidance suffix to avoid duplicating prior content. | |
| let guidance = injected.slice(currentSystem.length); | |
| if (guidance.startsWith("\n\n")) { | |
| guidance = guidance.slice(2); | |
| } | |
| guidance = guidance.trimStart(); | |
| if (guidance.length === 0) { | |
| return; | |
| } | |
| const last = | |
| output.system.length > 0 | |
| ? output.system[output.system.length - 1] | |
| : undefined; | |
| if (last !== guidance) { | |
| output.system.push(guidance); |
packages/opencode/package.json
Outdated
| { | ||
| "name": "kibi-opencode", | ||
| "version": "0.5.2", | ||
| "version": "0.5.3", |
There was a problem hiding this comment.
This PR manually bumps version in package.json. The repo’s release flow uses Changesets to manage versions/changelogs; without a .changeset/*.md file, automated versioning will drift and the release tooling may not pick up this change correctly. Add an appropriate changeset and let changeset version update package.json/changelog.
| "version": "0.5.3", | |
| "version": "0.5.2", |
packages/opencode/CHANGELOG.md
Outdated
| ## 0.5.3 | ||
|
|
||
| ### Patch Changes | ||
|
|
||
| - b543f92: Quieter terminal behavior: normal-operation logs now route through structured `client.app.log()` instead of `console.log`/`console.warn`. Error-class events (bootstrap-needed, sync/check failure, hook/init failure) remain visible in terminal. Preserved error visibility and hook compatibility for prompt injection. | ||
|
|
There was a problem hiding this comment.
Changelog entry appears to be written manually. This repo uses Changesets to generate changelog updates; please add a changeset instead and let the versioning workflow update CHANGELOG.md to keep release automation consistent.
packages/opencode/src/logger.ts
Outdated
| interface PluginClient { | ||
| app: { | ||
| log: (payload: Record<string, unknown>) => Promise<void>; | ||
| }; | ||
| } | ||
|
|
||
| let client: PluginClient | null = null; | ||
|
|
||
| // implements REQ-opencode-kibi-plugin-v1 | ||
| export function setClient(c: PluginClient): void { | ||
| client = c; | ||
| } |
There was a problem hiding this comment.
setClient is exported but its parameter type PluginClient is not exported. With declaration: true this will fail TypeScript declaration emit (Exported function has or is using private name 'PluginClient'). Export PluginClient (or inline the type / use a structural type in the function signature) so the package builds.
packages/opencode/src/logger.ts
Outdated
| if (client) { | ||
| client.app.log({ | ||
| body: { | ||
| service: "kibi-opencode", | ||
| level: "info", | ||
| message: msg, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
client.app.log(...) returns a Promise but the call is neither awaited nor handled. If the host logger rejects, this can create unhandled promise rejections. Consider explicitly fire-and-forget with void ... and attach a .catch(...) (or make the logger methods async and await).
|
@copilot apply changes based on the comments in this thread in a new subpr |
…log promises, reset client per-invocation, fix system.transform dedup, use changeset for versioning Agent-Logs-Url: https://github.com/Looted/kibi/sessions/57278c42-7fda-4283-b6b6-1cf55c043aa9 Co-authored-by: Looted <6255880+Looted@users.noreply.github.com>
fix(opencode): address logger correctness and prompt dedup review feedback
No description provided.