diff --git a/devlog/2026-08-13_fix-dcp-context-handled-error/REQ.md b/devlog/2026-08-13_fix-dcp-context-handled-error/REQ.md new file mode 100644 index 0000000..8a9b854 --- /dev/null +++ b/devlog/2026-08-13_fix-dcp-context-handled-error/REQ.md @@ -0,0 +1,13 @@ +# REQ — Fix __DCP_CONTEXT_HANDLED__ Error Leak (Issue #296) + +## Problem + +`/acp` commands throw `Error: __DCP_CONTEXT_HANDLED__` which leaks to the opencode error log. In opencode 1.18.18, this produces `level=ERROR` entries for every `/acp` invocation. + +## Root Cause + +The command handler in `hooks.ts` threw `new Error("__DCP_CONTEXT_HANDLED__")` after handling `/acp` commands. This sentinel was meant to abort command processing, but opencode catches and logs it as an error. + +## Fix + +Replace both `throw new Error("__DCP_CONTEXT_HANDLED__")` with `return`. The commands deliver output via `sendIgnoredMessage` (which writes directly to the session via `client.session.prompt`), so the hook can return normally without any side effects. diff --git a/devlog/2026-08-13_fix-dcp-context-handled-error/WORKLOG.md b/devlog/2026-08-13_fix-dcp-context-handled-error/WORKLOG.md new file mode 100644 index 0000000..fb26d89 --- /dev/null +++ b/devlog/2026-08-13_fix-dcp-context-handled-error/WORKLOG.md @@ -0,0 +1,13 @@ +# WORKLOG — Fix __DCP_CONTEXT_HANDLED__ Error Leak + +## Changes + +`lib/hooks.ts`: Replaced 2× `throw new Error("__DCP_CONTEXT_HANDLED__")` with `return` (lines 295, 299). +`tests/hooks-permission.test.ts`: Added regression test verifying handler returns normally. + +## Verification + +- TypeScript: 0 errors +- Tests: 977 pass, 0 fail +- Build: 391.50 KB +- Deployed locally diff --git a/lib/hooks.ts b/lib/hooks.ts index 9f0298b..85a8383 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -313,11 +313,10 @@ export function createCommandExecuteHandler( const sub = input.arguments?.trim().toLowerCase() if (sub === "stats" || sub === "status") { await handleStatsCommand(commandCtx) - throw new Error("__DCP_CONTEXT_HANDLED__") + return } await handleContextCommand(commandCtx) - throw new Error("__DCP_CONTEXT_HANDLED__") } } } diff --git a/tests/hooks-permission.test.ts b/tests/hooks-permission.test.ts index 48e2ce6..24bedc2 100644 --- a/tests/hooks-permission.test.ts +++ b/tests/hooks-permission.test.ts @@ -167,8 +167,32 @@ test("command execute exits after effective permission resolves to deny", async assert.deepEqual(output.parts, []) }) +test("command execute returns normally (no __DCP_CONTEXT_HANDLED__ throw) — issue #296", async () => { + let sessionMessagesCalls = 0 + const output = { parts: [] as any[] } + const handler = createCommandExecuteHandler( + { + session: { + messages: async () => { + sessionMessagesCalls += 1 + return { data: [] } + }, + }, + } as any, + createTestRegistry(createSessionState()), + new Logger(false), + buildConfig("allow"), + "/tmp", + { global: undefined, agents: {} }, + ) + + await handler({ command: "acp", sessionID: "session-1", arguments: "context" }, output) + + assert.equal(sessionMessagesCalls, 1) +}) + test("text complete strips hallucinated metadata tags", async () => { - const output = { text: "alpha beta omega" } + const output = { text: "alpha omega" } const handler = createTextCompleteHandler() await handler({ sessionID: "session-1", messageID: "message-1", partID: "part-1" }, output)