e0ipso / e0ipso/kenkeep

OpenCode headless adapter never captures assistant text — parses an event schema opencode does not emit

Open Beginner friendly
#121 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
26
Forks
2
PR merge metrics
No merged PRs in 30d

Description

## Summary

The opencode headless adapter accumulates assistant text only from `message.part.updated` events, but `opencode run --format json` emits `type: "text"` events with a different payload shape. Nothing ever matches, `accumulatedText` stays empty, and every proposal extraction fails with `opencode subprocess produced no assistant text` — even though the model produced complete, valid output.

**Impact:** background proposal extraction is completely non-functional under the opencode harness. Session logs are captured with intact transcripts, then marked `proposal_status: failed`. Because there is no supported way to reset a log back to `pending` (`session-log update-proposals --status` accepts only `done` or `failed`), recovery requires hand-editing frontmatter.

## Versions

- kenkeep 1.16.0 and 1.17.0 (current latest) — identical code, both affected
- opencode 1.18.15
- Node 20 / Linux

## Root cause

`src/harnesses/opencode/headless.ts` (shipped as `dist/hooks/opencode/kk-proposal-drain.cjs`):

```js
if (parsed.type === "message.part.updated") {
const messageId = parsed.properties?.messageID;
const part = parsed.properties?.part;
if (messageId && part && part.type === "text" && typeof part.text === "string") {
accumulatedText += part.text;
}
}
```

This expects the server-side event-bus shape. The CLI emits a flattened shape.

## Reproduction

```bash
KENKEEP_BUILDER_INTERNAL=1 opencode run --format json 'Reply with exactly: PONG'
```

Actual output:

```json
{"type":"text","timestamp":1786311024084,"sessionID":"ses_...","part":{"id":"prt_...","messageID":"msg_...","type":"text","text":"PONG","time":{"start":1786311024010,"end":1786311024020}}}
```

Two differences from what the adapter matches:

- discriminator is `text`, not `message.part.updated`
- payload is at top-level `part`, not `properties.part`

Running the adapter's predicate verbatim over a real captured `_logs/proposal/*.jsonl`:

```
event types present: {"step_start":1,"text":1,"step_finish":1}
lines matching kenkeep predicate: 0
accumulatedText.length: 0 -> throws "produced no assistant text"
```

The captured log for that run contains a complete, well-formed proposal JSON object (two practice candidates and one map candidate) in the `text` event. The output was produced correctly and then discarded.

## Secondary symptom

Some runs report `opencode subprocess timed out after 60000ms; accumulated text: ` instead. In one captured log the model emitted `step_finish` (reason `stop`) at 49s, roughly 11s before the 60s deadline — the opencode process simply did not exit, so execa's timer fired. Same underlying failure; it would have thrown "no assistant text" regardless.

## Not the cause

A `[claude-mem] OpenCode plugin loading (project: opencode)` banner appears as the first stdout line in my logs. It is harmlessly skipped by the adapter's `try/catch` and is not related to this bug.

## Suggested fix

Accept both shapes in the splitter callback — match `parsed.type === "text"` with top-level `parsed.part`, alongside the existing `message.part.updated` branch, so older and newer opencode both work.

Separately, please consider allowing `session-log update-proposals --status pending`, so logs that failed extraction can be re-queued through the supported path rather than by editing frontmatter by hand.

Contributor guide

Open the contributing guide

Research direction

Start with src/harnesses/opencode/headless.ts and reproduce the issue with the provided KENKEEP_BUILDER_INTERNAL=1 opencode run --format json command. Compare the existing message.part.updated parsing with captured _logs/proposal/*.jsonl events. Done means assistant text is captured for both event shapes and proposal extraction no longer reports missing assistant text.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
cli, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.