Skip to content

Release 0.0.166: reliable JSONL logging (#86) + compaction-failure recovery (#88) - #90

Merged
justrach merged 2 commits into
mainfrom
release/0.0.166
Jun 23, 2026
Merged

Release 0.0.166: reliable JSONL logging (#86) + compaction-failure recovery (#88)#90
justrach merged 2 commits into
mainfrom
release/0.0.166

Conversation

@justrach

Copy link
Copy Markdown
Owner

TUI/harness fixes for two backend correctness bugs, plus the GUI version bump to 0.0.166. All changes are in src/main.zig (the Zig harness); the GUI ships the embedded harness, so it gets the fix via the version bump.

#86 — HttpConnectionClosing retries give up turn & corrupt trajectory log

  • JSONL corruption (root cause). Tracer.write / Trajectory.writeLocked serialized JSON directly into the shared buffered file writer and catch return on a partial serialize — leaving a half-record plus leftover buffer bytes that the next record concatenated onto (exactly the reported {"text":"You ar{"kind":… garbage). New writeJsonLine() builds the whole line in memory first, so a failed write emits nothing and the file stays valid JSONL. Both writers route through it.
  • Flaky retries. Transport flakes (HttpConnectionClosing, resets, truncated TLS) retried with zero delay — 3 instant re-fails against a just-closed keep-alive. Added a 250ms·2ⁿ backoff (capped 2s, Esc-cancellable) and one more attempt.
  • Error preservation. On network give-up we now set last_api_error to the real transport error (was stale/null), and write an adjacent {"kind":"turn_error", "parent":<turn>, "detail":…} archive record so a failed turn's reason survives instead of just ok:false.

#88 — compaction can fail/skip silently, leaving huge context

  • Root cause. Once context overflows the window, compact()'s own summarization request overflows too and fails — and that failure was swallowed (error.ApiError => {}), so the session wedged and every tiny follow-up failed at the same token count.
  • Fix. New compactOrRecover() surfaces the failure (no more silent skip) and, as a backstop, emergency-trims history at a safe user-turn boundary (emergencyCutIndex/cleanUserTurn, which correctly refuses an anthropic tool_result-only user message as a cut point). Compaction is now also attempted after a failed turn (context-overflow recovery), not only after successful ones.

Testing

  • 98/98 unit tests pass (4 new: writeJsonLine, cleanUserTurn, emergencyCutIndex incl. the tool_result-boundary case). zig build + zig build test green.
  • Runtime-verified: a real failing turn produces 4/4 valid trajectory records + 2/2 valid trace records, with the new turn_error record carrying the preserved error detail.

Not in this PR (scope)

Follow-up (not part of the reported bug)

Agent.emit / guiEmit (the --json/GUI stdout writers) still use the old hand-rolled Stringify → writeByte('\n') → flush pattern straight into shared g_out, so they carry the same #86 torn-write risk. Left out here because it's a hot path (printDelta → emit per streamed chunk) — doing it right needs a shared scratch buffer under g_gui_mu, not a per-call allocation. Worth a focused follow-up.

Fixes #86. Fixes #88.

…ures (#88)

#86 — HttpConnectionClosing retries + corrupted trajectory log:
- writeJsonLine(): serialize each trace/trajectory record to memory, then write
  the whole line in one shot. A partial serialization no longer leaves a
  half-record + leftover buffer bytes for the next record to concatenate onto,
  so harness.trace/trajectory.jsonl stay valid JSONL even when a write errors
  mid-record.
- back off between transport-flake retries (was zero delay → 3 instant
  re-fails) and allow one more attempt.
- preserve the real network error in last_api_error on give-up, and write an
  adjacent {"kind":"turn_error"} record so the failure reason survives in the
  archive instead of only ok:false.

#88 — compaction can fail/skip silently, leaving huge context:
- compactOrRecover(): never swallow a compaction failure silently; on failure
  emergency-trim the history at a safe user-turn boundary so the session can't
  wedge at a huge token count with every tiny follow-up turn failing.
- also attempt compaction after a *failed* turn (context-overflow recovery),
  not only after successful ones.

Unit tests for writeJsonLine, cleanUserTurn, emergencyCutIndex. Bump GUI to
0.0.166.

Out of scope: #89 (deepseek reasoning-budget) is a gateway-side change, not
this harness; #75 collapse and #85 mouse-select auto-copy need an alt-screen
TUI rewrite; #74/#78/#79 already shipped in 0.0.165.
…odel auth prompt

- New OpenAI-compatible providers fugu (api.sakana.ai) and fireworks
  (api.fireworks.ai) with model tables; SDKs regenerated (54 models).
- graff --version now prints a short what's-new changelog.
- /model on a keyless provider offers OAuth login or key paste instead of a
  dead-end (offerProviderAuth).
@justrach
justrach merged commit b8ac683 into main Jun 23, 2026
4 checks passed
@justrach
justrach deleted the release/0.0.166 branch August 4, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compaction can fail or skip silently, leaving huge context TUI: HttpConnectionClosing retries give up turn and corrupt trajectory log

2 participants