chat: inject an Officer system prompt telling OpenCode its working directory (step 2)

OpenCode can't set a real per-session cwd (every session runs in the fixed server's
dir), so we tell the model its working directory via a system prompt appended to
OpenCode's own — sent as a system message, so it never appears in the visible chat
(verified against source + live). Claude doesn't need this (it honors cwd natively).

- client.postMessage(…, system?) forwards a `system` string on the message.
- send-opencode builds the Officer prompt from `workingDir` and sends it every turn.
- websocket: workingDir = the resolved cwd, except the general /chat (whose cwd is the
  claude_sessions grouping placeholder) uses the user's home.

Verified live: with the prompt, the model reports the injected dir as its cwd.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 11:06:17 +00:00
co-authored by Claude Opus 4.8
parent 7cd2be9fb9
commit 9a79a76b95
3 changed files with 35 additions and 6 deletions
+13 -3
View File
@@ -117,11 +117,21 @@ class ServerConnection {
return session.id;
}
async postMessage(sessionId: string, providerID: string, modelID: string, text: string): Promise<void> {
await this.postJson(`/session/${sessionId}/message`, {
async postMessage(
sessionId: string,
providerID: string,
modelID: string,
text: string,
system?: string,
): Promise<void> {
// `system` is appended to OpenCode's built-in system prompt (additive, not an override) and is
// sent to the LLM as a system message — it never appears as a visible chat part.
const body: Record<string, unknown> = {
model: { providerID, modelID },
parts: [{ type: 'text', text }],
});
};
if (system) body.system = system;
await this.postJson(`/session/${sessionId}/message`, body);
}
async abort(sessionId: string): Promise<void> {