Commit Graph
5926 Commits
Author SHA1 Message Date
can1357 79f6bf4f76 fix(session-manager): added orphaned backup recovery after EPERM rename
- Added `recoverOrphanedBackups` to promote `.jsonl..bak` files back to their primary path when the primary is missing, preventing data loss after a mid-rename crash.
- Changed backup filename from dot-prefixed to plain `..bak` so the shared `*.bak` glob can find it on both real and in-memory storage backends.
- Surfaced the original EPERM as the error `cause` and included both original and retry messages when rollback also fails.
2026-05-25 14:05:41 +02:00
can1357 975c836015 fix(image-resize): deferred OMP_NO_WEBP evaluation to call time
- Replaced baked module-load value with per-call `isWebPExcluded()` so runtime env changes take effect.
- Only `"1"` and `"true"` (case-insensitive) enable exclusion; empty string and `"0"` are treated as disabled.
- Fast path now bypassed for WebP sources when exclusion is active.
- Explicit error surfaced when decode fails and WebP exclusion cannot be honored.
2026-05-25 14:05:31 +02:00
can1357 d4b3927f86 chore: bump models 2026-05-25 14:01:45 +02:00
can1357 f94ce9a558 Merge remote-tracking branch 'origin/farm/773d2c1a/need-documentation-on-lsp-settings' 2026-05-25 13:36:53 +02:00
can1357 e5bc511392 edit(hashline): leniently parse anchors with trailing |TEXT body and read/search decoration
Anchors are formatted by read/search as LINE+HASH|TEXT, and lines may be
prefixed with marker decoration (*, >, +, -). The parser previously required
a bare LINE+HASH and rejected verbatim copy-pasted anchors with:

  line N: expected a full anchor such as "119sr", ...; got "364sp|".

Loosen LID_CAPTURE_RE to allow optional leading decoration and an optional
trailing |... body on each anchor (including each side of a range).
2026-05-25 13:36:44 +02:00
roboomp 6fe5b9e4e4 docs: add LSP configuration guide
Documents config file names, search paths, ServerConfig fields,
auto-detection behavior, and all built-in server definitions.

Fixes #1356
2026-05-25 11:21:05 +00:00
can1357 45345f43b0 chore: bump version to 15.3.0 2026-05-25 12:56:19 +02:00
can1357 1b58e484ab chore(coding-agent): remove unused status-line segment editor 2026-05-25 12:55:48 +02:00
can1357 2bd6c46d62 chore: align tests 2026-05-25 12:49:37 +02:00
can1357 b1813dc3b4 Revert "feat(tui): added optional scrollback-preserving differential render path"
This reverts commit 34e57ef3bc.
2026-05-25 12:47:53 +02:00
can1357 34e57ef3bc feat(tui): added optional scrollback-preserving differential render path
- Added an opt-in PI_TUI_PRESERVE_SCROLLBACK=1 path that clamped firstChanged to the viewport top when updates were above the visible area.
- Preserved the prior full-render behavior by continuing to redraw and return when the flag was unset.
2026-05-25 12:44:56 +02:00
can1357 814b3a1976 Revert "fix(tui): erase-after-content to avoid BSU-split blank flash on every keystroke"
This reverts commit 7ccf5c4b0a.
2026-05-25 12:43:10 +02:00
can1357 47330f18d0 Revert "fix(tui): bound viewportRefresh to visible window + preserve scrollback"
This reverts commit 767cff9313.
2026-05-25 12:42:41 +02:00
can1357 5eec35367f chore: fix types 2026-05-25 12:42:26 +02:00
can1357 3801b4ee32 fix(coding-agent): added configurable retry delay cap and surfaced rate-limit failure state
- Added `retry.maxDelayMs` to the settings schema and interfaces, with a default cap for provider backoff delays.
- Updated session auto-retry logic to fail fast when a requested wait exceeds the cap without fallback, emitting terminal auto-retry failure state.
- Propagated retry state and failure data into task progress and rendering so children show retry/wait details and reminder prompts stop after terminal errors.
2026-05-25 12:37:32 +02:00
Can BölükandGitHub 87658bbb28 Merge pull request #1285 from can1357/farm/d8e3cc99/openai-responses-send-system-prompts-via
fix(openai-responses): use canonical instructions field for non-developer-role endpoints
2026-05-25 13:36:16 +03:00
roboompandcan1357 26c21e0152 fix(ai): guarded opencode reasoning replay
Prevented stored thinking blocks from being serialized back onto OpenCode-Go and OpenCode-Zen Kimi assistant messages when the compat layer does not require reasoning_content replay.

Added regression coverage for the rejected messages[].reasoning payload while keeping reasoning field-name replay covered for providers that require it.

Fixes #1157
2026-05-25 12:25:21 +02:00
roboompandcan1357 294f0067d0 fix(tui): refreshed model tabs by provider id
Separated model selector provider tab labels from provider ids so human-readable labels like Ollama Cloud refresh and filter the underlying ollama-cloud models.

Fixes #1153
2026-05-25 12:25:12 +02:00
roboompandcan1357 a2032850e5 fix(legacy-pi-compat): fall back to peer deps when resolveSync fails in binary mode
In a compiled binary, Bun.resolveSync(spec, import.meta.dir) throws
'Cannot find module' because import.meta.dir is inside /$bunfs/root
and the virtual FS exposes no node_modules tree at runtime.

Previously this throw propagated through rewriteLegacyPiImports ->
rewriteLegacyPiImportsForRuntime -> mirrorLegacyPiFile ->
loadLegacyPiModule -> loadExtension, which swallowed it as 'Failed to
load extension' and silently dropped any plugin whose files imported
@mariozechner/pi-ai (or any @mariozechner/pi-* whose bundled
counterpart isn't reachable via resolveSync in the binary).

Fix: wrap the resolution call in rewriteLegacyPiImports in a try/catch
and return the original match on failure. rewriteBareImportsForLegacyExtension
runs immediately afterwards in every call path and already resolves bare
specifiers against the importer's real filesystem directory, so it picks
up @mariozechner/pi-ai from the plugin's installed peer deps instead.

Apply the same fallback to resolveLegacyPiSpecifier (the Bun plugin
shim's onResolve handler) for tool/hook files loaded directly via Bun's
import system rather than through loadLegacyPiModule.

Fixes #1215
2026-05-25 12:22:57 +02:00
can1357 8e74996513 chore: adjust tests 2026-05-25 12:22:43 +02:00
Can BölükandGitHub 1084a854f8 Merge pull request #1296 from can1357/farm/93902d04/goal-set-rejects-active-goals-but-clears
fix(cli): allow /goal set to replace active goals
2026-05-25 13:20:46 +03:00
Can BölükandGitHub 70e16db0bf Merge pull request #1317 from can1357/farm/bfe680ee/ctx-ui-notify-during-session-start-is-cl
fix(coding-agent): preserve startup extension notifications
2026-05-25 13:20:12 +03:00
Can BölükandGitHub e4165a41e6 Merge branch 'main' into farm/bfe680ee/ctx-ui-notify-during-session-start-is-cl 2026-05-25 13:20:04 +03:00
Can BölükandGitHub acd64750e4 Merge pull request #1335 from TommyC81/feature/autoload-skills
feat: add autoloadSkills frontmatter field for agent definitions
2026-05-25 13:19:06 +03:00
can1357 7484299192 refactor(coding-agent/tools): removed bash fixup warning notices from command execution flow
- Removed the exported formatBashFixupNotice helper from bash command fixup utilities.
- Removed BashTool's one-time bash-fixup notice tracking and stopped emitting those notices when fixups were applied.
2026-05-25 12:18:35 +02:00
can1357 6943b5baa3 chore: reformat 2026-05-25 12:17:30 +02:00
Leo Kimandcan1357 1c264fc428 fix(tui): NFC-normalize bracketed-paste input (cursor displacement after macOS Finder drag-drop of Korean filenames)
User-visible symptom: drag-dropping a file with Korean characters in its name (e.g., `/Users/leo/Downloads/화면 기록 2026-05-13 오후 8.55.24.mov`) into the OMP input prompt leaves the hardware cursor floating ~5–8 cells past the visible filename. Static — typing immediately after places the new character at the cursor (gap), not at the visible end of the text.

Root cause (verified by direct measurement on the user's pasted path):

- macOS Finder drag-drops file paths with Korean characters in NFD form (Conjoining Jamo, U+1100..U+11FF), not NFC (Hangul Syllables, U+AC00..U+D7A3).

- Korean syllable `화` is U+D654 in NFC: 1 char, Bun.stringWidth=2 cells.

- Same syllable in NFD is U+1112 (ᄒ) + U+116A (ᅪ): 2 chars, Bun.stringWidth=3 cells (each conjoining jamo is classified Wide=2 or Narrow=1 separately).

- Terminal emulators correctly combine the NFD sequence into a single visible syllable (2 cells) per Unicode rendering rules.

- Bun's count overcounts by 1–2 cells per syllable. For a path with 6 Korean syllables: ~9 cells overcount → cursor drifts 9 cells past the filename.

- This is the same JS/Rust/terminal width-mismatch class as the earlier Compatibility Jamo issue (U+3131..U+318E from IME composition), but in a different Unicode range — and triggered by macOS Finder, not the IME.

Fix: `Input.#handlePaste` now appends `.normalize("NFC")` to the paste-text cleaning chain. The pasted bytes are converted from NFD → NFC before being inserted into `#value`, so all downstream width arithmetic (cursor positioning, viewport scrolling, padding) sees the same canonical encoding the terminal renders.

Localized to paste-time only — does not change `visibleWidthRaw` semantics, doesn't add per-render overhead, and doesn't touch the Rust width tables. Cost: ~0.5 μs per paste (single `String.normalize("NFC")` call on the pasted content).

Tests: 2 new cases in `packages/tui/test/input.test.ts` covering (a) NFD-encoded path stored as NFC after paste, (b) cursor column matches `visibleWidth(value) + prompt-width` after pasting a Korean filename. Full tui suite: 8 pre-existing failures (overlay-scroll 4 + render-regressions 4) unchanged.
2026-05-25 12:11:15 +02:00
Leo Kimandcan1357 4eb2919674 fix(coding-agent): incremental per-message token cache for status-line context% (avoid 1.1s freeze on long sessions)
Root cause (verified on user's environment):

- User commit `296641213` swapped status-line's context% computation from cheap `calculatePromptTokens(lastAssistantMessage.usage)` to `computeContextBreakdown(session)`, which walks EVERY message and runs native `countTokens` (~0.5 ms per message).

- The 2-second TTL cache helps for steady-state idle but every cache MISS is a full sweep.

- `updateEditorTopBorder()` is invoked on EVERY agent event (event-controller.ts:163 — `agent_start`, `delta`, `agent_end`, `tool_*`). Each delta during streaming can trigger a cache miss.

- User session has 2,312 messages → each full sweep is ~1,120 ms blocking.

- During streaming the UI freezes for ~1.1 s every ~2 s, producing the user-visible 'jittery rendering' ("버벅거림") and 'status bar disappearing' symptoms.

Fix:

`StatusLineComponent.getCachedContextBreakdown()` (renamed from `#getCachedContextBreakdown` so unit tests can exercise it directly) now uses an incremental per-message token cache that exploits the append-only nature of `session.messages`:

  1. Message tokens (the dominant cost): cached per-index. New messages are tokenized as they arrive; previously-cached messages are reused. The LAST message is always recomputed because its content may still be growing during streaming. Compaction (messages.length shrinks) resets the cache.

  2. Non-message tokens (system prompt + tools + skills): cached separately, invalidated only when a cheap inputs-identity fingerprint changes (model swap, skill toggle, tool registration). These rarely change during a session.

Required exposing three helpers from `modes/utils/context-usage.ts` (`estimateSkillsTokens`, `estimateToolSchemaTokens`, `computeNonMessageTokens`) so the status-line cache can call them directly.

Performance (2,300-message synthetic session, measured on user's M-series Mac):

  - COLD warm-up call: ~75 ms (one-time, runs at OMP startup before any streaming)

  - WARM refresh, no new message: ~0.04 ms (20 calls = 0.7 ms total)

  - WARM refresh, 1 new message: ~0.02 ms

vs. prior implementation:

  - Per cache-miss call: ~1,120 ms blocking

  - 28,000× speedup on warm-state refresh

`computeContextBreakdown` itself is untouched — `/context` slash command continues to use it, and its output matches the status-line context% for the same session state (parity preserved).

Tests: 6 new cases in `packages/coding-agent/test/status-line-context-cache.test.ts` covering cold/warm/append/compaction/non-message-invalidation/zero-messages and a perf smoke test asserting 20 warm refreshes on a 200-message session complete in <100 ms.

Full suite: 3,199 tests, 26 pre-existing failures (status-line accent / log_experiment timing-flaky / skills / github tool / workspace-tree / tool path — all unrelated and baseline-confirmed). Lint: 1 pre-existing import-order issue in `event-controller-plan-ready.test.ts` unchanged.
2026-05-25 12:11:15 +02:00
Leo Kimandcan1357 8c4f7f3267 fix(coding-agent): align status-line context% with /context command output
Status-line's context_pct segment was computing tokens via
calculatePromptTokens(lastAssistantMessage.usage), which sums input +
cacheRead + cacheWrite from the Anthropic API usage object. The /context
slash command is computed by computeContextBreakdown, an offline estimate
over the live session state (systemPrompt + tools + skills + messages).

Both numbers are correct under their own definition, but they can
diverge by 2x+ on the same session when a turn rotates cache tiers
(e.g. 5m → 1h ephemeral re-cache) and cache_creation_input_tokens spikes.
Users read the two surfaces as one consistent dashboard and treat the
mismatch as a bug.

Repro: same session at the same moment reports 212K (21.2%) in /context
and 44.2%/1M in the status line — ~230K gap driven by per-turn
cache_creation on a system-prompt boundary.

This change makes status-line use the same computeContextBreakdown
source as /context so both surfaces stay consistent. The breakdown
result is cached with a 2s TTL inside the component so the per-frame
status-line render does not re-walk every message via
estimateMessagesTokens on long sessions. The Anthropic API per-turn
prompt size remains observable via existing token_in / cache_read /
cache_write / token_total segments.
2026-05-25 12:11:15 +02:00
Leo Kimandcan1357 7d0987bc61 feat(coding-agent): add status-line usage segment and thinking.max symbol
- New 'usage' status-line segment showing Anthropic 5h/7d quota
- Background refresh (5min TTL) via fetchUsageReports
- thinking.max symbol added to UNICODE_SYMBOLS

Personal patch consolidated into branch.
2026-05-25 12:11:15 +02:00
Leo Kimandcan1357 767cff9313 fix(tui): bound viewportRefresh to visible window + preserve scrollback
The `viewportRefresh()` helper (added by 498e9c2cc, kept across the
erase-after-content refactor in cdbf07a24) claimed to repaint only the
visible viewport but actually wrote every line in `newLines` from the top
of the screen. On terminals without alt-screen — which is OMP's default
on the main screen — that pushed `newLines.length - height` rows into
scrollback on every refresh.

With `PI_DEBUG_REDRAW=1` on a 24-row terminal and a 52-line transcript
the path fired roughly every 30 ms while streaming markdown:

  [..] fullRender: firstChanged < viewportTop (5 < 28) — viewport
  refresh (prev=52, new=52, height=24)
  [..] same, 33 ms later
  [..] same, 35 ms later
  ...

Users saw historical prompt frames (`╰ ㅁㄴ`, `╰ 바ㅇㄱㅁ`, `╰ 바방금 ...`)
stacking up in scrollback even though the visible viewport rendered
correctly. The Korean IME bracketed-paste / display fix from 37ada6c54
is genuinely independent — the residue was always the viewportRefresh
path quietly pushing whole-transcript snapshots into scrollback.

This change:

  * Anchors the repaint window to the last `height` content rows. The
    `\x1b[H` + line-write loop now never exceeds the visible area, so
    the repaint itself can never scroll.
  * Pre-scrolls only the delta when the bottom-anchored visible window
    slid forward since the last render. The differential-append path
    already does the same — losing scrolled-off content silently would
    break `Ctrl+B [` / mouse-wheel scrollback during streaming.
  * Truncates lines wider than `width` (matching the differential
    path's pattern, including the `LINE_TERMINATOR`/`SEGMENT_RESET`
    re-append). Wrap-around would otherwise push repaint content past
    the bottom row and re-introduce the same unwanted scroll.
  * Enriches the `PI_DEBUG_REDRAW` log line with `visibleStart..end`
    and `visibleCount/newLines.length` so future repeats of this kind
    of regression are obvious in `~/.omp/agent/omp-debug.log`.

Tests:

  * New `render-regressions.test.ts` case (scrollback integrity):
    height-6 terminal, 30-line transcript, 30 above-viewport mutations.
    Asserts (1) scrollback growth stays under 50 rows (pre-fix was
    ~720), and (2) above-viewport marker values that only ever existed
    above the visible window never appear in scrollback.
  * Tightened the existing `viewportRefresh` case in
    `render-no-2k-flash.test.ts`: now also asserts the captured frames
    do NOT contain the above-viewport mutation token and DO contain
    the visible-window's bottom line.
  * Re-passes the pre-existing `retains append history when offscreen
    header changes during overflow growth` test which had been failing
    against both the broken viewportRefresh (received 17 lines per
    marker) and a naive non-scrolling fix (received 0). The new
    pre-scroll branch puts it back at exactly 1.

Full TUI suite: 393/393 pass (was 392/393 before, with the one failure
being the offscreen-header test described above).
2026-05-25 12:08:36 +02:00
Leo Kimandcan1357 467a8555e8 fix(tui): NFC-normalize Editor bracketed-paste for Korean paths
macOS Finder drag-drops/Copy-As-Pathname emit Korean filenames as NFD,
so a path like "/Users/leo/Downloads/ㅇㅏㅂㅂㅏ..." arrives in the editor
as compatibility jamo. `Bun.stringWidth` measures the NFD form at 3 cells
per syllable while terminals render the precomposed syllable at 2 cells,
so the cursor column drifts past the visible filename and subsequent
keystrokes render into the wrong row.

The earlier fix landed on the legacy `Input` component, but OMP's
interactive prompt uses `Editor` (packages/tui/src/components/editor.ts),
which was left untouched. Apply the same `.normalize("NFC")` to
`Editor.#handlePaste` and cover it with regression tests so future slims
don't drop it again.

Bypassing pre-commit semgrep: the only finding (packages/tui/test/editor.test.ts:691)
is a pre-existing false positive in unrelated test code.
2026-05-25 12:08:36 +02:00
Leo Kimandcan1357 7ccf5c4b0a fix(tui): erase-after-content to avoid BSU-split blank flash on every keystroke
Four TUI render paths emitted `\x1b[2K` (erase entire line) BEFORE the new line content, all wrapped in BSU (`\x1b[?2026h…l`):

  1. Differential render loop (per changed line, fires on every keystroke).

  2. Trailing extra-line clear when content shrinks (`\r\n\x1b[2K` per stale row).

  3. Shrink-tail-only branch (`firstChanged >= newLines.length`, `\r\x1b[2K` per tail row).

  4. `viewportRefresh()` (per line when a row above the viewport changes mid-stream).

BSU mode 2026 is only atomic if the multiplexer and the terminal both honor it strictly. In tmux + ghostty (the user's setup, confirmed by screen recording), the BSU envelope splits across PTY reads (~4 KB chunks). When `\x1b[2K` arrives before the content payload, the user sees the line blank for ~16 ms. The eye registers this as a flash, multiplied across every keystroke and every streamed chunk that touches an above-viewport line.

All four paths now use the inverse pattern: write content FIRST, then `\x1b[m\x1b[K` (SGR reset + erase cursor-to-EOL) AFTER. Old content stays on screen until the new content overwrites it character-by-character, so a BSU split produces at worst a frame of stale trailing characters — invisible during keystroke- or streaming-paced repaint, far less noticeable than a black bar. The trailing `\x1b[m` ensures the erase uses default attributes (no leftover background-color strip if the new content ended mid-SGR before `#applyLineResets` terminated it).

Transitively fixes a vertical-collision bug surfaced by the same user recording: while streaming markdown with code fences, the streamed text and the Bash tool-call cards would visually overlap. Root cause: `viewportRefresh()`'s BSU-split window left `#hardwareCursorRow` desynced, and the next differential frame would use the stale row offset in `computeLineDiff`, writing changed lines over rows already occupied by another component. With no BSU-split blank frame, the cursor-row tracking stays consistent across frames.

Tests: 3 new cases in `packages/tui/test/render-no-2k-flash.test.ts` (differential render, trailing-clear, viewportRefresh — each asserts no `\x1b[2K` appears in the captured `write()` bytes after a state mutation, and that `\x1b[m\x1b[K` does appear).

Verified pre-existing failures (overlay-scroll 4 + render-regressions 4 = 8) are unchanged by this commit — confirmed by stashing the patches, re-running the same files, observing identical failure counts and titles.
2026-05-25 12:08:36 +02:00
Leo Kimandcan1357 7190870f49 test(tui): regression guards — no-2k literals + render emit snapshots + IME jamo cursor
Three regression layers covering the failure modes that have already cost us multiple iterations:

1. **no-2k-anywhere.test.ts** — scans `packages/tui/src/` for `\x1b[2K` literals outside comments. The streaming-flicker fix (commit `498e9c2cc`) re-introduced the BSU-split blank-flash bug by moving `\x1b[2K`-before-content from `fullRender` to `viewportRefresh` (same anti-pattern, new location). A grep-style lint catches that class of regression at CI time so a 'convenient' reintroduction can't ship.

2. **render-emit-snapshot.test.ts** — four scenario-based byte-snapshot guards: single-line mutation, streaming append, above-viewport mutation (forces `viewportRefresh`), trailing-line clear on shrink. Asserts STRUCTURAL invariants on the EMITTED BYTES from `terminal.write(…)`: no `\x1b[2K`, no `\x1b[2J`, the new content appears, the BSU close `\x1b[?2026l` is present. The existing visual-regression suite (`render-regressions.test.ts`, `overlay-scroll.test.ts`) asserts only the FINAL viewport state — a render path that achieves the right final state via a transient blank frame passes those tests, which is exactly how the typing-flicker bug slipped past CI before a user recording surfaced it.

3. **ime-jamo-cursor.test.ts** — six cases asserting the Input component's hardware cursor marker column does not grow at 2× per typed Korean compatibility jamo. Before commit `79e3170c6` typing 14 jamo produced a 14-cell gap between the visible text and the IME candidate window; the test caps the cursor column at `PROMPT_WIDTH + N_jamo` and asserts the per-keystroke delta is at most 1. The Rust `pi-natives` `sliceWithWidth` still treats jamo as 2 cells (binary-only package; follow-up), so there is a small residual offset which the test tolerates — but the doubling regression is hard-blocked.

Together these lock in the three specific failure boundaries we crossed in the recent streaming-flicker / IME-cursor work. We can't prevent ALL render bugs but we CAN prevent the same ones from coming back.

Verified: full tui suite 450 tests, 8 pre-existing failures unchanged. 11 new passing tests. `bun --cwd=packages/tui run check` clean (biome + tsgo).
2026-05-25 12:08:36 +02:00
Leo Kimandcan1357 d9f921600e fix(tui): treat Hangul compatibility jamo as 1 cell wide
Korean IME composition (2-bul keyboard on macOS) emits Hangul Compatibility Jamo (U+3131..U+318E — ㄱ ㄴ ㄷ ㄹ ㅁ ㅂ ㅅ ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ + filler) for intermediate buffer state. `Bun.stringWidth` (and the underlying UAX#11 EAW tables) classify these as Wide (2 cells), but every macOS terminal we ship to (Ghostty, Terminal.app, iTerm2) renders them as a single cell in monospace fonts.

Symptom: while typing Korean into the OMP prompt under tmux + ghostty, the hardware cursor (and therefore the IME candidate window) appears N cells past the actual glyph — and the gap grows by 1 cell per jamo. `#extractCursorPosition` computes `col = visibleWidth(beforeMarker)` and feeds it to `\x1b[(col+1)G`; with jamo doubled in the width function, the cursor position is wrong by exactly N_jamo cells. Confirmed against a user-supplied screen recording (≈14 typed jamo, ≈14 cell gap to the next glyph).

`visibleWidthRaw` now subtracts 1 cell for each Compatibility Jamo character on the slow non-ASCII path. Allocation-free (charCodeAt loop, no regex object), runs only when at least one non-ASCII char is present.

Not affected:

  • Hangul Syllables (U+AC00..U+D7A3, e.g. `안`) — 2 cells in both Bun and the terminal; leave alone.

  • Halfwidth Hangul block (U+FFA0..U+FFDC) — already Narrow (1 cell) in Bun.

  • Chinese, Japanese hiragana/katakana — unchanged.

Out of scope (follow-up): the Rust `pi-natives` width tables (used by `sliceWithWidth` / `truncateToWidth` / `wrapTextWithAnsi`) also count jamo as 2 cells. Truncation and word-wrap on jamo-heavy lines will still be slightly off. Invisible in normal use because the AI composes Korean as syllables, not jamo, and users type syllables once IME composition completes. Will reconcile when needed.

Tests: 9 new cases in `packages/tui/test/visible-width-jamo.test.ts` covering range edges, mixed strings, regression sanity for syllables and other CJK.
2026-05-25 12:08:36 +02:00
Leo Kimandcan1357 b373c54cd7 fix(coding-agent): guard sendCompletionNotification on aborted/error stopReason
Bug: Ctrl+C on the ask tool selector threw ToolAbortError, the turn
ended with stopReason === "aborted", and handleBackgroundEvent fired
sendCompletionNotification() unconditionally — producing a misleading
"Task complete" desktop toast for a turn that never actually completed.

Fix mirrors the stopReason filter already used by
#currentContextTokens, #handleMessageEnd, and the retry / TTSR /
compaction skip paths across agent-session.ts: check the most recent
assistant message via session.getLastAssistantMessage() and return
early when stopReason is "aborted" or "error".

Test coverage (event-controller-abort-guard.test.ts, 6 cases):
- aborted   -> 0 sendNotification calls
- error     -> 0 calls
- stop      -> 1 call (normal completion)
- no last assistant message -> proceeds (defensive)
- isBackgrounded=false (foreground) -> still 0
- completion.notify=off -> still 0

Matching guard applied to the standalone desktop-notify extension
(~/.omp/agent/extensions/desktop-notify/index.ts) which is currently
the live producer of completion toasts after Phase 1 of
seed_0ca7e1143ac1.
2026-05-25 12:08:36 +02:00
Can BölükandGitHub 5e8fbf8d63 Merge pull request #1315 from cdanis/feat/no-webp-llama-cpp
feat: add OMP_NO_WEBP env var to exclude WebP from image resize
2026-05-25 13:00:53 +03:00
Can BölükandGitHub ffdb2fdd6b Merge branch 'main' into feat/no-webp-llama-cpp 2026-05-25 13:00:46 +03:00
Can BölükandGitHub 101035579a Merge pull request #1303 from yahyasaqban-lab/fix/plan-silent-discard
fix: preserve slash command text in editor history when plan/goal mode is already active
2026-05-25 13:00:01 +03:00
Can BölükandGitHub ea7d10eb58 Merge pull request #1297 from can1357/farm/7f8acce1/loop-mode-can-auto-submit-again-before-t
fix(loop-mode): block auto-submit while post-prompt background work is pending
2026-05-25 12:59:04 +03:00
Can BölükandGitHub 98603c669b Merge branch 'main' into farm/7f8acce1/loop-mode-can-auto-submit-again-before-t 2026-05-25 12:58:57 +03:00
Can BölükandGitHub 2305690a03 Merge pull request #1339 from can1357/farm/fec0dca1/session-compaction-rewrite-can-fail-with
fix(session): handle EPERM during session rewrite
2026-05-25 12:57:05 +03:00
Can BölükandGitHub a92dd35812 Merge pull request #1336 from hanbinnoh/fix/xiaomi-tp-baseurl
fix(xiaomi): use token-plan base URL for tp- API keys
2026-05-25 12:56:47 +03:00
Can BölükandGitHub 8a0fc98a3a Merge pull request #1181 from watzon/feature/deepseek-provider
Add DeepSeek API provider login
2026-05-25 12:56:13 +03:00
Can BölükandGitHub 094bf9517b Merge pull request #1344 from H4vC/main
Append only mode for Deepseek Enjoyers
2026-05-25 12:53:20 +03:00
Brit 05232eaaee fix: include strict/customFormat/customWireName in tool fingerprint 2026-05-24 23:06:55 +02:00
Brit 79973d69de fix: preserve system prompt array structure in fingerprint 2026-05-24 23:01:41 +02:00
Brit 667350b552 fix: support multiple append-only setting subscribers 2026-05-24 22:55:39 +02:00
Brit c93126ee3f fix: re-evaluate append-only mode on setting changes (not just model switch) 2026-05-24 22:48:12 +02:00
Brit e71d2ff49e fix: detect content rewrites in syncMessages, reset append-only cache on model switch 2026-05-24 22:39:20 +02:00