pingdotgg / pingdotgg/t3code

[Bug]: OpenCode CLI OSC title leak pollutes agent inventory — Agent not found → UnknownError

Open
#7,754 2 comments 0 reactions 0 assignees View on GitHub

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.*.log shows two session.error per turn: first is the polluted agent, second is the generic UnknownError.
  • projection_threads.model_selection_json polluted 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 with env -i PATH=... opencode agent list and TERM=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: shared stripTerminalEscapes/sanitizeTerminalValue
  • opencodeRuntime.ts: strip before parseModels/Agent/Skills, defensive at entry
  • OpenCodeProvider.ts: sanitize inventory agent names/variants + --version parsing
  • OpenCodeAdapter.ts + OpenCodeTextGeneration.ts: sanitize stored agent/variant before session.promptAsync
  • packages/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
  1. opencode agent list | cat -v shows OSC prefix
  2. Start T3 server, no external opencode server → loadInventoryFromCli stores polluted agent
  3. New thread → send → fails with UnknownError stack
Expected

Clean agent inventory, no OSC leak.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.