[Bug]: OpenCode CLI OSC title leak pollutes agent inventory — Agent not found → UnknownError
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Bug
OpenCode <=1.18 writes OSC title sequence ESC ]0;<cwd>: ready BEL to stdout for every non-help command even when stdout is a pipe. T3 captures that via ChildProcessSpawner + collectStreamAsString and the parsers stored a polluted agent id:
Agent not found: "\u001b]0;imbios: ready\u0007build". Available agents: build, explore, general, plan
Followed by generic wrapper:
UnknownError: UnknownError
at <anonymous> (//root/chunk-09733s3k.js:1096:2069)
at SessionPrompt.createUserMessage (//root/chunk-09733s3k.js:1096:9954)
...
at SessionHttpApi.promptAsync (//root/chunk-ys5kjase.js:2:84381)
Evidence
~/.t3/userdata/logs/provider/events.*.logshows twosession.errorper turn: first is the polluted agent, second is the generic UnknownError.projection_threads.model_selection_jsonpolluted for 3 threads:
{"instanceId":"opencode","model":"opencode-go/mimo-v2.5","options":[{"id":"agent","value":"\u001b]0;imbios: ready\u0007build"}]}
- Repro:
opencode agent list 2>&1 | cat -v→^[]0;tmp: ready^Gbuild (primary)even withenv -i PATH=... opencode agent listandTERM=dumb— opencode binary itself leaks, not shell/ghostty. - Also
opencode models --verbose 2>&1 | cat -v→^[]0;tmp: ready^Gopencode/big-pickle— caused model inventory to drop to 0 without the fix (SLUG_RE didn't match). Skills JSON degraded to[].
Root cause
apps/server/src/provider/opencodeRuntime.ts:208,270 parseModelsCliOutput/parseAgentListCliOutput assumed clean stdout. No strip of OSC ESC ]...BEL / CSI ESC[...m.
Fix
See PR from fork (ImBIOS/t3code#2 merged to ImBIOS:main, will open upstream PR). Summary:
packages/shared/src/stripTerminalEscapes.ts: sharedstripTerminalEscapes/sanitizeTerminalValueopencodeRuntime.ts: strip beforeparseModels/Agent/Skills, defensive at entryOpenCodeProvider.ts: sanitize inventory agent names/variants +--versionparsingOpenCodeAdapter.ts+OpenCodeTextGeneration.ts: sanitize storedagent/variantbeforesession.promptAsyncpackages/shared/src/model.ts: sanitize persisted option values and model slugs on read (repairs polluted threads without DB migration)- Tests: regression cases for OSC/ANSI in both parsers
Upstream
Should be fixed in sst/opencode as well: CLI should write OSC title to /dev/tty/stderr only when isTTY, not stdout pipe. T3 fix is defensive regardless.
Repro steps
opencode agent list | cat -vshows OSC prefix- Start T3 server, no external opencode server →
loadInventoryFromClistores polluted agent - New thread → send → fails with UnknownError stack
Expected
Clean agent inventory, no OSC leak.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at apps/server/src/provider/opencodeRuntime.ts, especially parseModelsCliOutput and parseAgentListCliOutput, and trace how their results reach the provider and persisted model options. Review packages/shared/src/stripTerminalEscapes.ts, OpenCodeProvider.ts, OpenCodeAdapter.ts, OpenCodeTextGeneration.ts, and packages/shared/src/model.ts. Done means OSC and ANSI output no longer pollute inventories, prompts, or stored values, with regression cases covering both parsers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100