Commit Graph

4035 Commits

Author SHA1 Message Date
roboomp 7ec00a5007 fix(edit): skipped notebook bom sniffing
Skipped binary BOM detection for notebook-backed hashline reads so virtual cell text is serialized without a leading U+FEFF marker.
2026-06-30 05:58:01 +00:00
roboomp dd4eb68b37 style: bun run fix 2026-06-30 05:52:02 +00:00
roboomp d1e412eeff Revert "style: bun run fix"
This reverts commit 45d2bf8641.
2026-06-30 05:51:53 +00:00
roboomp 45d2bf8641 style: bun run fix 2026-06-30 05:49:41 +00:00
roboomp d7c6472824 fix(edit): preserved hashline utf-8 bom
Added binary BOM detection to the hashline patcher and filesystem adapters so edits restore BOM bytes when text decoding hides U+FEFF.

Fixes #3867
2026-06-30 05:48:37 +00:00
can1357 09061ed801 feat(coding-agent/web): aligned duckduckgo search requests with native browser behavior
- Updated the default browser User-Agent string to emulate a modern version of Chrome.
- Added typical browser headers to the outgoing fetch request, including Sec-Ch-Ua, Sec-Fetch flags, and Referer.
- Added a blank "b" parameter to the form body to match native DuckDuckGo HTML search behavior.
2026-06-30 07:02:12 +02:00
can1357 b704ac698a chore: update changelogs 2026-06-30 06:55:13 +02:00
can1357 38d40122f9 Merge remote-tracking branch 'origin/farm/8e0a06a8/fix-buffered-double-esc-and-task-end-loader' 2026-06-30 06:43:10 +02:00
roboomp c2d6c8cf18 fix(tui): deliver buffered double-Esc as two events and re-arm loader after task completion
StdinBuffer held a bare `\x1b\x1b` chunk and timer-flushed it as one
sequence. `parseKey("\x1b\x1b")` returns undefined, so CustomEditor
fell through to the base editor and never fired the configured `onEscape` —
the double-escape gesture and the second-press single-Esc handler both went
dead whenever the terminal batched the two presses into one stdin read.

Split an exact bare `\x1b\x1b` into two ESC events only after the
flush window proves no follower arrived. If a follower does arrive, emit the
first ESC and restart parsing at the second ESC so legacy Alt chords
(`\x1bd`, `\x1b\x7f`) remain one downstream keypress. Meta-CSI/SS3
chords (`\x1b\x1b[A`, `\x1b\x1bO…`) still emit as one combined
sequence.

EventController.tool_execution_update re-armed the working loader when a
transient overlay (auto-compaction / auto-retry / handoff) had torn it down
mid-tool; tool_execution_end did not. A subagent (`task`) call only fires
_end, so a task result landing after such an overlay left the UI looking
idle even though the session was still streaming. Mirror the reconciler
call in #handleToolExecutionEnd.

Fixes #3857
2026-06-30 04:30:42 +00:00
roboomp 7871591b4a fix(coding-agent): restored working loader after subagent ends inside overlay window
`tool_execution_end` for a long-running tool (`task` subagent, async bash
poll, …) is the next streaming event on the parent session when an inner
transient overlay (auto-snapcompact, auto-context-full, auto-retry) nulled the
working loader between the tool's start and end. The overlay-end handlers are
the only loader restorers keyed off the missing reference; if the subagent's
`tool_execution_end` lands while the overlay is still active (or its end
handler errored before re-arming), the spinner stays gone for the rest of the
parent turn even though the agent keeps streaming.

`#handleToolExecutionEnd` now calls `#ensureWorkingLoaderWhileStreaming()`
at the top, mirroring `tool_execution_update` so the working loader survives
a subagent completing inside the overlay window.

Refs #3858
2026-06-30 04:09:20 +00:00
roboomp 64734021a7 fix(tui): deliver buffered double-Esc as two events and re-arm loader after task completion
StdinBuffer held a bare `\x1b\x1b` chunk (or emitted it as one when followed by
a non-CSI byte). `parseKey("\x1b\x1b")` returns undefined, so CustomEditor
fell through to the base editor and never fired the configured `onEscape` —
the double-escape gesture and the second-press single-Esc handler both went
dead whenever the terminal batched the two presses into one stdin read.

Split a bare `\x1b\x1b` into two ESC events at the buffer layer, mirroring
the existing split for ESC + SGR mouse report. Meta-CSI/SS3 chords
(`\x1b\x1b[A`, `\x1b\x1bO…`) still emit as one combined sequence.

EventController.tool_execution_update re-armed the working loader when a
transient overlay (auto-compaction / auto-retry / handoff) had torn it down
mid-tool; tool_execution_end did not. A subagent (`task`) call only fires
_end, so a task result landing after such an overlay left the UI looking
idle even though the session was still streaming. Mirror the reconciler
call in #handleToolExecutionEnd.

Fixes #3857
2026-06-30 03:41:24 +00:00
can1357 d20e6c0829 feat: migrated service tier settings to a per-model-family architecture
- Migrated global service tier settings to a per-model-family architecture (OpenAI, Anthropic, Google).
- Implemented `ServiceTierByFamily` mapping to allow independent configuration and resolution per provider.
- Added automatic migration logic for legacy service tier and fast-mode application settings.
- Updated telemetry, session management, and task execution to support provider-specific tier resolution.
2026-06-30 04:14:48 +02:00
can1357 d4be774eb2 chore: update stale tests 2026-06-30 03:35:30 +02:00
can1357 5c382e6b7e fix(coding-agent): formatted and type-fixed tiny-model download test
Reflow biome wrap and replace delete on non-optional isTTY with
Reflect.deleteProperty in #3840's text-failure test.
2026-06-30 03:06:06 +02:00
can1357 8cd23ebd15 merge #3844: 3-way dirty-context fallback for isolated branch merges
# Conflicts:
#	packages/coding-agent/src/task/worktree.ts
#	packages/coding-agent/test/task/worktree.test.ts
2026-06-30 03:03:43 +02:00
can1357 9038488cb0 merge #3845: preserve agent commit history across isolated branch merges 2026-06-30 03:01:02 +02:00
can1357 ce20cfb68e merge #3829: align /extensions MCP status with /mcp list and persist re-enable 2026-06-30 03:01:01 +02:00
can1357 e750485a38 merge #3840: surface tiny-model download errors instead of bare false 2026-06-30 03:01:01 +02:00
can1357 ea6d9f32c4 merge #3836: surface pending IRC asides in inbox without double-injecting 2026-06-30 03:01:01 +02:00
can1357 5f4af63e30 merge #3847: skip rebuilding previous session context on different-session switch 2026-06-30 03:01:01 +02:00
can1357 e8090bb48a feat: introduced binary file detection to prevent encoding corruption
- Introduced `isProbablyBinary` utility to sniff file headers for NUL bytes or invalid UTF-8 sequences.
- Updated `ReadTool` to use the binary sniffer, preventing mojibake corruption in output when reading non-text files.
- Refined `file-mentions` auto-reads to skip binary files and mark them as `binary` in the message transcript.
- Added comprehensive unit tests for binary detection logic, covering NUL bytes, truncated multibyte characters, and path-based file sniffing.
2026-06-30 02:59:41 +02:00
can1357 a7efaf4a53 test(cli): cover tiny model text download errors 2026-06-30 02:52:57 +02:00
roboomp d8bf76dfd6 fix(coding-agent): skipped rebuilding previous session display context on different-session switch
AgentSession.switchSession() eagerly called buildDisplaySessionContext()
before setSessionFile, walking the previous session's branch and expanding
every compaction entry's snapcompact archive and openaiRemoteCompaction
replacementHistory into messages. For huge pre-fix sessions that materialized
GBs of data and OOMed in-TUI /resume even after the streaming loader fix.

The snapshot is only needed for same-session reloads, where
#didSessionMessagesChange compares the pre/post message arrays to detect
rollback edits. Different-session switches skip the call entirely; the
error-recovery path rebuilds the previous context on demand from the
restored state so MCP-selection restoration still has its inputs.

Added a regression test (test/agent-session-switch-prev-context.test.ts)
that spies on sessionManager.buildSessionContext across switchSession and
asserts the expected call count and target file per branch.

Fixes #3846
2026-06-30 00:34:02 +00:00
roboomp d120ba6b7d fix(coding-agent): filter baseline wip from preserved agent commits
Dirty isolated baselines can be accidentally committed by subagents that run git add -A. Fetching the raw isolation HEAD then cherry-picking the range would replay that baseline WIP into parent history.

Add a dirty-baseline replay path that rewrites each agent commit against the captured baseline tree, preserving the agent commit message and author while excluding staged, unstaged, and untracked changes that existed before isolation started. Clean baselines still use the raw git fetch path, and nested-only changes keep returning patches without creating an empty root branch.

Add a regression for baseline staged + untracked WIP committed by the agent, asserting the task branch contains only the agent file and parent WIP remains staged/untracked after merge.

Fixes #3842
2026-06-30 00:28:27 +00:00
roboomp da715aae7c fix(coding-agent): preserve agent commits across isolated branch merges
When an isolated task agent commits its own changes before yielding, the
harness used to collapse the captured delta into one AI-summarized commit
and discard the agent's commit messages and authorship entirely. This
violated commit discipline for agentic swarms — multiple logical commits
("fix bug" + "add test") became a single opaque commit, and the
agent's commit object (which lived in isolation/.git/objects under
overlayfs/rcopy) was lost when cleanupIsolation tore down the overlay.

commitToBranch now detects when isolation HEAD moved past baseline.root
.headCommit. When it has, the function git-fetches the agent's HEAD into
the parent repo as omp/task/${taskId} so the commit objects survive
cleanupIsolation, and stamps the captured baselineSha onto the returned
CommitToBranchResult. mergeTaskBranches cherry-picks the inclusive range
baseSha..branchName when baseSha is provided, replaying each agent
commit verbatim with its original message and author. Any uncommitted
leftover (staged, unstaged, untracked) on top of the agent's last commit
becomes one trailing AI-summarized commit on the same branch.

Falls back to the legacy single-commit path when the agent never moved
HEAD (purely dirty working tree); existing patch-mode flow is untouched.

Fixes #3842
2026-06-30 00:13:07 +00:00
roboomp 4b98211c64 fix(coding-agent): fixed dirty isolated branch merges
Applied isolated branch patches with three-way fallback when unrelated parent dirt appears in patch context.

Surfaced branch preparation failures instead of reporting no changes.

Fixes #3841
2026-06-30 00:09:34 +00:00
roboomp f5be8953c4 fix(cli): surfaced tiny model download errors
Preserved worker-side download errors through TinyTitleClient and included them in tiny-models text and JSON failures.

Fixes #3839
2026-06-29 22:56:40 +00:00
roboomp 66eba507f5 fix(irc): consumed peeked pending asides to avoid double inject
- Always remove surfaced irc:incoming records from the pending-aside queue; the inbox tool result already injects the body, so leaving them queued would auto-inject a duplicate at the next step.

- Updated the inbox tool to drain pending asides regardless of peek.

- Added a regression test asserting a peeked pending aside does not auto-inject.
2026-06-29 22:29:10 +00:00
roboomp 3104d232aa fix(irc): surfaced pending asides in inbox
- Drained running-session IRC asides through the inbox tool before the model step consumes them.

- Added a regression test for messages delivered while the recipient is already running.

Fixes #3834
2026-06-29 22:21:24 +00:00
roboomp e34f2a81e9 fix(tui): force-enabled MCP from tool-owned sources via enabledServers
Codex review on #3829: when an MCP server lives in a non-writable
source config such as opencode.json with enabled:false, the dashboard
re-enable had nowhere to write to — the writable mcp.json fallback
did not own the server, so setMcpServerEnabled fell through to the
denylist and the source's enabled:false kept the row disabled.

Added a parallel allowlist to the user-level mcp.json that overrides
a non-writable source's enabled:false flag without ever mutating the
foreign config:

- types + schema: new enabledServers array (mirrors disabledServers).
- config-writer: readEnabledServers + setServerForceEnabled helpers,
  and setMcpServerEnabled now writes to enabledServers on enable
  when no writable mcp.json owns the server, clears it whenever a
  writable source becomes the source of truth, and always clears the
  override on disable so a force-enabled server can be turned off.
- mcp/config (runtime loader) and state-manager (dashboard read):
  honor enabledServers as an override on enabled:false, while still
  letting disabledServers win.
- Added a regression test that walks the full lifecycle for an
  opencode.json server: enabled:false is surfaced as disabled, the
  dashboard re-enable force-enables via enabledServers without
  touching opencode.json, then disable clears the override and
  populates disabledServers.

Fixes #3827
2026-06-29 20:39:50 +00:00
roboomp 16ef3c54f4 fix(tui): re-enabled MCP alternate config sources
Codex review on #3829: the dashboard re-enable path still missed MCP
servers loaded from supported non-primary native config files such as
.omp/.mcp.json or user .mcp.json. Those rows carry enabled:false from
their source file, so falling back to the user disabledServers denylist
could not make the row active again.

- setMcpServerEnabled now accepts the loaded row's sourcePath and checks
  it before the primary project/user mcp.json paths.
- extension-dashboard passes the source path for writable MCP providers
  (native and mcp-json), avoiding accidental edits to third-party tool
  configs while still updating .omp/.mcp.json and standalone MCP JSON
  sources.
- Added a regression test for a server loaded from .omp/.mcp.json with
  enabled:false; re-enable flips that file to enabled:true and does not
  write the denylist.

Fixes #3827
2026-06-29 20:26:16 +00:00
roboomp 812b246e7e fix(tui): dashboard re-enable flips enabled:false in mcp.json
Codex review on #3829: when an MCP server's mcp.json entry carries
enabled:false, the dashboard toggle previously only removed the name
from the user-level disabledServers denylist. state-manager's new
`server.enabled === false` check (state-manager.ts:156) then still
marked the row disabled, leaving such servers impossible to re-enable
from /extensions.

Extracted setMcpServerEnabled() into mcp/config-writer.ts mirroring
/mcp enable | /mcp disable semantics:

- Server defined in project mcp.json -> update enabled on that entry.
- Else server defined in user mcp.json -> update enabled on that entry.
- Else (discovered third-party server) -> use the user-level disabledServers denylist.
- On re-enable, always clear any stale denylist entry.

extension-dashboard.ts routes mcp:* toggles through this helper. Added
four new regression tests covering: enabled:false re-enable, mixed
flag+denylist re-enable, disable on a config-resident server writing
enabled:false (not denylist), and discovered-server denylist round-trip.

Fixes #3827
2026-06-29 20:13:15 +00:00
roboomp 6256f97eaf fix(tui): aligned /extensions MCP status with /mcp list
Two read paths previously diverged on whether an MCP server was active or
disabled. /mcp list (slash-commands/helpers/mcp.ts:388) treats a server
as disabled when config.enabled === false OR the name is in the
user-level disabledServers denylist; the runtime MCP loader does the
same in mcp/config.ts:115. The /extensions dashboard only consulted
the dashboard-private settings.disabledExtensions array, so a server
disabled via /mcp disable or enabled:false kept showing as active.

Toggling MCP servers from the dashboard had the mirror problem: it only
wrote to settings.disabledExtensions, so /mcp list never noticed.

- state-manager: read user-level disabledServers from mcp.json once and
  consider enabled:false / denylist membership when deriving each MCP
  extension's state, matching /mcp list semantics.
- extension-dashboard: route mcp:* toggles through setServerDisabled
  against the canonical mcp.json denylist, and clean any legacy
  settings.disabledExtensions entry on re-enable so it doesn't keep the
  server marked disabled.
- Added a regression test exercising both read signals and the
  setServerDisabled round-trip the dashboard's MCP toggle now uses.

Fixes #3827
2026-06-29 20:05:04 +00:00
can1357 d03ea52891 refactor(ai): improved provider in-flight lease signal routing
- Update `releaseProviderInFlightLease` to signal into the specific directory path associated with the lease rather than recomputing it from the root.
- Introduce `signalProviderInFlightWaitersInDir` to decoupling waking waiters from global provider path resolution.
- Remove redundant tests from `coding-agent`.
2026-06-29 20:04:24 +02:00
can1357 ffa8519801 Merge remote-tracking branch 'origin/farm/3f9e6955/mcp-reauth-force-fresh-login' 2026-06-29 19:47:23 +02:00
roboomp 4ce9d659c9 fix(mcp/oauth): align prompt behavior with mcp sdk
The initial PR update changed the MCP OAuth default prompt to 'login consent'.
The reporter verified Cloudflare's flow actually matches the reference MCP SDK
when no prompt parameter is sent: Cloudflare then reuses the existing account
grant and opens the scope/permission picker first. Forcing any prompt keeps the
flow on Cloudflare's account/consent page instead.

Match the reference SDK behavior: omit prompt by default and send
'prompt=consent' only when the requested scope contains offline_access, where
OIDC Core requires re-consent for offline access. Explicit oauth.prompt values,
including the empty-string omit escape hatch, still take precedence.

Also rename the dynamically registered MCP OAuth client from Codex to oh-my-pi
so Cloudflare consent screens show the current product name.

Fixes #3817
2026-06-29 17:32:58 +00:00
roboomp a49c7027bf fix(mcp/oauth): force login screen before consent on /mcp reauth
The MCP OAuth flow defaulted the authorization-request prompt parameter
to 'consent'. Per OpenID Connect Core 1.0 §3.1.2.1 that asks the
authorization server to re-prompt for consent only while reusing the
existing browser authentication session. Cloudflare's MCP OAuth server
(and other strict OIDC providers) honor that literally, so /mcp reauth
landed on the consent screen attached to whichever account the browser
cookie was for, leaving no way to switch the signed-in account.

Default to 'login consent' instead so the provider first re-prompts for
authentication (the page Claude Code shows on its reauth flow) and then
re-confirms consent, preserving the original intent of always
re-displaying the authorize screen. RFC 6749 §3.1 requires providers to
ignore prompt values they do not support, so the two-value form is safe
for non-OIDC servers. Existing per-server overrides via mcp.json's
`oauth.prompt` (including the empty-string escape hatch) are unchanged.

Fixes #3817
2026-06-29 17:21:28 +00:00
can1357 64be11e289 Merge PR #3806: fix(cli): preserve Windows extension paths (@roboomp) 2026-06-29 16:45:47 +02:00
can1357 6f8f76be43 Keep DuckDuckGo result cap unchanged 2026-06-29 16:45:47 +02:00
can1357 579b4f3f10 Merge PR #3800: fix(web-search): scrape DuckDuckGo HTML frontend instead of Instant Answer API (@roboomp) 2026-06-29 16:45:46 +02:00
can1357 45df90bdac fix(mcp): preserve non-npx cmd-shim direct launch 2026-06-29 16:45:46 +02:00
can1357 c90bef647e Merge PR #3796: fix(mcp): preserve npx cmd shim launching (@roboomp) 2026-06-29 16:45:46 +02:00
can1357 6259792bd7 Merge PR #3811: fix(providers): support Gemini API key web search (@roboomp) 2026-06-29 16:45:46 +02:00
can1357 17c4aa0391 Merge PR #3795: fix(cli): honor web search provider settings in omp search (@roboomp) 2026-06-29 16:45:45 +02:00
can1357 5d2f972edd Merge PR #3790: fix(session): avoid ctrl-c rewrites for compacted sessions (@roboomp) 2026-06-29 16:45:45 +02:00
can1357 48add4fc7e Merge PR #3787: fix(coding-agent): bound edit-tool oldText/newText snapshots in tool-result details (@roboomp) 2026-06-29 16:45:45 +02:00
can1357 1eb32a5df1 feat(coding-agent/bench-cli): tracked and close provider session states
- Added management of provider session states during benchmark execution.
- Implemented a teardown process to close and clear session states after request completion.
2026-06-29 16:31:21 +02:00
roboomp 530113eb71 fix(providers): supported gemini api key search
Enabled the Gemini web search provider to use standard Google developer API credentials when Cloud Code Assist OAuth is absent.

Added developer API request coverage for native Google Search grounding and preserved existing OAuth request serialization.

Fixes #3810
2026-06-29 13:51:42 +00:00
roboomp 777b609e63 fix(cli): preserved windows extension paths
Rejoined split Windows extension module paths before launch parsing finishes and stripped extended-length Win32 prefixes before Bun import and worker spawn APIs see them.

Fixes #3804
2026-06-29 11:50:36 +00:00
roboomp 75db042744 style: bun run fix 2026-06-29 09:49:49 +00:00