Release/1.23 - #116
Merged
Merged
Conversation
- Pixel-font MERCURY CODE wordmark: centered, device-precise (single-codepoint block glyphs, constant-width left column), vibrant cyan/magenta duotone with light/dark background adaptation - Full-screen /code mode: bordered input box, centered command hints, status line with dir/git/mode/provider, transcript scrollback - Mouse support: SGR/X10 sequence parser, wheel-driven scrollback, clean terminal restore on exit - Single live feedback feed: spinner + current action, done ticks, parallel sub-agent swarm panel - Smarter coding agent protocol: intent-first analysis, read-before-write, verify builds/tests, structured atomic progress narration - New commands: /code diff (syntax-highlighted), /code init (AGENTS.md), /code exit (confirmation flow), Ctrl+P/Ctrl+X/Ctrl+G shortcuts - Dependency-free syntax highlighter wired into chat code blocks and diffs - Esc-Esc double-press exit arming with 1.5s window
Terminal crash after 1-2 min in /code traced to three stacked issues: 1. The stdin filter dropped an escape prefix whenever a mouse sequence was split across two stdin chunks, leaking the tail (e.g. '64;53;5M') into the TUI input box as keystrokes — the garbage seen in the shell prompt after abort. 2. Leaked fragments accumulated unboundedly in the input state, driving render churn until V8 aborted. 3. Crash paths never disabled mouse tracking, leaving the terminal spewing SGR reports after death. Fixes: - Rewrite the filter as a stateful MouseSequenceFilter: holds back partial ESC/CSI/SGR/X10 prefixes across chunk boundaries (bounded at 64 chars so a corrupt stream cannot grow memory), parses complete mouse events, passes all other sequences through whole. X10 in-flight check runs before generic CSI pass-through since 'M' is a valid CSI final byte. - restoreTerminal(): DEC reset + cursor show on every exit path (TUI exit, channel stop, SIGTERM/SIGINT shutdown). - 8000-char input flood guard as defense in depth. - 12 new tests covering split-chunk joins for SGR and X10, wheel parsing, motion/release classification, and holdback overflow.
Second abort (uv __run_timers stack) pointed at timer-driven runaway accumulation during 1-2 min Mercury Code sessions. Also: the user's crash came from a stale dist that predated the mouse-filter fix — npm run build now emits the guarded build. Hardening: - Cap live toolSteps (60) and TUI transcript messages (250); long coding sessions no longer grow render/memory unboundedly. - statusPollerTick: Mercury Code git header read is now async (execSync blocked the event loop mid-task every 2s). - Wheel events only count presses (release no longer double-scrolls). - Crash forensics: uncaughtException/unhandledRejection/SIGABRT handlers append JS stack + heap stats to ~/.mercury/crash-report.log so any future native abort has a readable cause. - PTY stress run: 180s live TUI at steady-state RSS (~140MB), no crash.
Root causes confirmed via ~/.mercury/crash-report.log forensics: 1. Ink's reconciler freed Yoga nodes (freeRecursive) but left JS references dangling — the renderer then read freed WASM memory through node.staticNode?.yogaNode after mode switches unmounted <Static>. Patched ink (patches/ink+5.2.1.patch, wired via patch-package postinstall) to null every reference in removed subtrees and clear the root's cached staticNode when the removed subtree contains it. Also handles ink's childNodes-less #text nodes. 2. <Static>'s positional index assumes append-only items; the bounded slice(-MAX_STATIC_MESSAGES) window shifted at constant length, so new messages never rendered and every commit unmounted the whole static subtree. Added itemKey identity dedup to the patched Static component; App.tsx passes message ids. Also lands the in-flight crash-hardening work: memory governor, execute guard, stream completion bounding, live-activity, and the bounded Mercury Code transcript projection with scroll. patch-package ships as a runtime dependency and patches/ is included in published files so consumer installs get the fix. Co-Authored-By: Claude Code <noreply@anthropic.com>
/code chat (alias /code back) returns to regular chat immediately, skipping the exit-confirm dance — the confirm guards the Esc-Esc path against accidental exits; an explicit command is deliberate. Also fixes /chat and /c doing a half-exit from Mercury Code: they flipped the view but left mercuryCode state, scroll offset, and programming mode dangling. Both now tear down via exitMercuryCode(). Hints block and /help manual updated; regression test guards the teardown (mode chat, mercuryCode null, programming mode off). Co-Authored-By: Claude Code <noreply@anthropic.com>
Tasks that did not finish were reported as finished. Root paths confirmed in the code: step-budget exhaustion produced a "Task complete" banner (main loop) and a 'completed' status (sub-agents); implementation turns needed only one successful edit to count as done; silent stalls had no watchdog; completion was declared by loop termination, not task outcome. - completion-verdict.ts: classify why the turn ended (text-stop / steps-exhausted / interrupted / truncated / aborted). Budget exhaustion with tool work pending is a pause, never a completion. - Main loop: bounded auto-continuation on step-budget exhaustion (fresh budget + resume nudge, mirroring the execute-guard round); past the bound, ask the user — declining records work-ledger 'paused' with the "send continue" hint. MERCURY_MAX_STEPS env override for cheap soak testing of the exhaustion path. - execute-guard.ts: evidence-based verification gate — execute-mode work with no build/test/typecheck command forces one bounded verification round before completion. - stall-watchdog.ts: 3 min silence → visible still-working pulse; 8 min → abort the attempt so inspect-and-resume machinery engages (never a silent hang). Env-tunable thresholds. - Sub-agents: budget exhaustion now yields 'paused' + supervisor auto-resume (once) with a fresh budget; background tasks no longer mark paused agents failed. - Work ledger: new 'paused' status — resumable, recovered on restart, never pruned as terminal. - CLI banners: paused tasks wear "Task paused · send continue"; execute- mode turns with zero file changes wear "Response delivered · no file changes" instead of a false "Task complete". Co-Authored-By: Claude Code <noreply@anthropic.com>
Regression from the itemKey patch: committing item keys did not unmount the rendered children. Ink's renderer recomputes static output from still-mounted <Static> children on every render and writes them to the terminal again — so in idle chat the last transcript message duplicated on every render cycle (~30s heartbeat/token churn), piling up copies of the same response. Original ink avoids this by unmounting children in its layout effect via setIndex; the itemKey path now does the same via a commitTick re-render after keys are committed. Functional reproduction added (src/ui/static-rerender.test.tsx): a real ink render with a fake terminal asserts each item is written exactly once across heartbeat-like rerenders. Verified by temporarily reverting the fix (6 writes vs 1). The test's itemKey is deliberately module-level — an inline arrow re-runs the dedup memo every render and masks the bug. Co-Authored-By: Claude Code <noreply@anthropic.com>
AUTO is now the default when entering Mercury Code (previously PLAN,
which required a manual /code execute to build anything):
- Reads, plans silently, implements immediately in one uninterrupted
flow. Small/medium changes proceed without asking; large or
consequential changes present a concise plan with a single ask_user
confirmation (recommended option default-selected), then build
without re-asking.
- AUTO shares execute-class semantics: full tool set and the entire
completion contract (verification gate, narration guard, step-budget
continuation, honest pauses).
- System prompt adds an explicit anti-narration mandate: every sentence
about intended work must be followed by the tool call doing it in the
same turn ("Act, don't announce"). Execute and AUTO share one
behavioral contract constant.
- /code auto added (TUI fast path, chat handler, web API); toggle
cycles off → auto → plan → execute → off. UI labels, hints card, and
/help manual updated.
Co-Authored-By: Claude Code <noreply@anthropic.com>
When a file tool succeeds in Mercury Code / coding mode, the transcript now shows a selective excerpt of the change instead of a bare tool step: - create/write: header (path + line count) + code fence in the file's own language — up to 48 lines, larger files show a head excerpt plus "… +N more lines (full content on disk)". - edit_file: header with +/− stats + a ```diff fence (red/green via the existing diff highlighter), bounded to 16 lines per side. - Failures, deletes, and non-file tools produce no preview; the size heuristics keep the transcript selective, never flooded. Plumbing: utils/file-preview.ts builds previews from tool arguments; CLIChannel.showFileChange() appends them as system messages (with duplicate-retry guard); the agent's tool bookkeeping hooks call it on every loop (stream, continuation, guard, verification); Mercury Code's transcript projection now parses fenced blocks inside system messages so previews flow through the same highlight pipeline as agent code. Co-Authored-By: Claude Code <noreply@anthropic.com>
Regression from the AUTO-mode feature: enterMercuryCode set the TUI to AUTO, but the agent-side handler immediately called setPlan() and pushed that stale state back via setProgrammingStatus — the bottom status bar showed PLAN on every /code entry. The agent-side ProgrammingMode now syncs to AUTO, and the welcome message matches the automatic flow. Regression guard added to the Mercury Code exit test. Co-Authored-By: Claude Code <noreply@anthropic.com>
"I don't know what it's waiting for": during a silent provider hang the
chat-mode ThinkingIndicator showed a generic "Composing response", while
the actual provider/phase state ("Calling <provider>") was pushed as
live activity that only rendered in Mercury Code mode. The indicator now
displays the live activity phase (provider + model/detail) in all
surfaces, so a stalled attempt is attributable at a glance.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The splash mark's shade texture cycled per glyph COLUMN, landing shade cells at different positions on every row — visible holes inside letters (the M rendered as "█ ▓ █"). Shading now applies per row, top→bottom: every filled cell in a row shares one fill character, and the mark is solid with a shaded bottom band — a deliberate vertical gradient shadow instead of ragged gaps. The X glyph is fixed to a symmetric 5-wide form and the default fill is solid. Regression tests guard row-uniform fills, part alignment, and the absence of shade above solid rows. Co-Authored-By: Claude Code <noreply@anthropic.com>
The bottom hints row taught input-navigation trivia (↑/PgUp/Ctrl+U history, Ctrl+A oldest) that developers never look for in a status bar. Left side now shows only what gets pressed: "↵ send · esc esc exit · ctrl+c quit" at live, and the scroll keys only while scrolled back. Right side gains the token budget (⚡ N%) — it was invisible in Mercury Code entirely, since the chat-mode token bar doesn't render there. Co-Authored-By: Claude Code <noreply@anthropic.com>
User decision from live sessions: mid-task pauses that demand a manual "continue" break the flow of long Mercury Code sessions. Continuation is now the default, with the pause demoted to a runaway backstop: - Narration guard: 5 forced rounds (was 2) — models routinely need a few nudges to switch from narration to tool use. - Step-budget / provider-failure continuations: 6 automatic (was 2); a provider hard deadline no longer pauses for approval — the failed attempt counts toward the bound and the loop keeps going. - Sub-agent step-budget auto-resume: 3 (was 1). - The ask/reason strings updated to the new policy. Co-Authored-By: Claude Code <noreply@anthropic.com>
Regression from AUTO mode: a turn ending in a plain-text question to the
user ("Could you remind me what the bot was supposed to do?") was not
recognized as a deliberate pause, so the narration guard forced more
rounds — the model re-searched and re-asked, looping visibly "forever".
- responseAsksUser(): a response whose last line ends with '?' is a
legitimate stop; the guard neither forces rounds past it nor pauses
it as unfinished work.
- The guard nudge now points questions at ask_user with concrete
options.
- Honest banner/file-change gating now includes AUTO mode (was
execute-only): the completion banner, no-changes honesty, and file
summaries all apply to AUTO turns.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Plan checklist: the model maintains its implementation plan with the new update_plan tool (full-list replacement, one step "active" while being implemented). The agent records it into the TUI at each tool step, and Mercury Code renders a compact panel above the live feedback: ☑ 2 earlier steps completed ☑ Create color.ts ▶ Build storage.ts ← implementing ☐ Wire the UI Normalization is defensive: malformed entries dropped, labels deduped, multiple "active" steps collapsed, rows budgeted into the fixed chrome (transcriptHeight accounts for plan + prompt rows). The AUTO and EXECUTE prompts mandate keeping the checklist current. Choice picker: PermPromptView (ask_user / permission / continue prompts) was gated to non-mercury-code modes — in Mercury Code the ask_user tool blocked on a prompt that never rendered (an invisible hang). The prompt now renders inside Mercury Code above the input box. Co-Authored-By: Claude Code <noreply@anthropic.com>
Even with PermPromptView now rendered in Mercury Code, two bugs would have made it unusable: the mercury-code key branch returned for every keypress (arrow keys/Enter never reached the prompt navigation handler), and Esc on an ask_user choice prompt did nothing — leaving the tool blocked forever. A pending prompt now takes keyboard priority, and Esc on a choice prompt cancels it (the tool proceeds with best judgment instead of hanging). Co-Authored-By: Claude Code <noreply@anthropic.com>
Permission-domain audit findings, fixed:
1. fetch_url had no scheme or host validation and followed redirects
blindly — the model (steerable by the web content it reads) could be
pointed at internal services: localhost admin panels, LAN hosts, or
the cloud metadata endpoint, with their contents flowing into the
conversation. Now: http/https only, private/loopback/link-local
ranges blocked for both literal and DNS-resolved hosts, redirects
validated per hop (redirect: manual), downloads capped at 512KB,
MERCURY_ALLOW_PRIVATE_FETCH=1 as an explicit opt-out.
2. web-config.json (bcrypt hash) and web-sessions.json (live session
tokens) were world-readable in a readable ~/.mercury — any local
process could steal a session and drive Mercury's shell. Credential
writes now use 0o600 and existing files are repaired on load.
3. Initial web password was a hardcoded constant ('Mercury@123') public
in the MIT repo. It is now a random per-install password returned to
the setup flow for one-time display.
4. Shell blocklist: added swapped-flag rm variants (rm -fr /, ~, /*) and
rm -rf . / .. to the never-execute tier.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…install Two remaining permission-domain findings, fixed: 1. Secrets in logs: provider errors embed response bodies containing API-key fragments, and command output can contain environment secrets — all of it persisted into daemon-error.log and session transcripts. utils/redact.ts masks known key shapes (sk-/ghp_/AKIA/ xoxb/Bearer/key=value) with a recognizable prefix/suffix; the pino error serializer redacts deeply (nested responseBody included), and run_command output is redacted before echoing into the conversation. 2. install_skill fetched skill content with a raw fetch — same SSRF hole as fetch_url, no redirect validation, no size cap. It now uses the shared SSRF guard (utils/ssrf.ts, extracted from fetch-url): scheme + private-range validation on every redirect hop, 512KB content cap on both URL and inline installs. Co-Authored-By: Claude Code <noreply@anthropic.com>
The security pass replaced utils/redact.ts wholesale, dropping the existing redactPhone/redactUuid/redactIdentity (Signal/Web identity masking) and userFacingAiError (chat error translation) exports. All are restored alongside the new secret-redaction functions; the earlier commit was amended-forward by this one. Co-Authored-By: Claude Code <noreply@anthropic.com>
'scroll-set N' matched the generic scroll- prefix branch first, parsed as delta 'set N' (NaN) and returned: the scroll-clamp handler below was unreachable. After a long-session history trim (4MB transcript budget) the stored scroll offset exceeded the shrunken transcript, the viewport clamped to the bottom rows, and the clamp loop that should have repaired the offset silently did nothing — the user could not scroll at all and saw only the tail of the last (code) response. scroll-set is now parsed before the generic branch; regression guard asserts the parse order. Co-Authored-By: Claude Code <noreply@anthropic.com>
Coding slowness diagnosis and fixes: - No prompt caching: every agentic step re-processed the full system prompt (soul + skills + tool guidelines) and the whole growing conversation at full cost — TTFT climbed with session size. The Anthropic-family providers now get a cache_control breakpoint on the system prompt (OpenAI-compatible providers cache server-side and ignore the option harmlessly). - MAX_RESPONSE_TOKENS 4096 → 8192: large code files truncated mid-write and triggered a length-truncation continuation that re-sent the whole conversation — a full extra round trip per large file. - Per-attempt latency logging (durationMs on success and failure) so "why is coding slow" becomes measurable instead of guessed. Co-Authored-By: Claude Code <noreply@anthropic.com>
On longer /code tasks the transcript showed narration from successive
agentic steps concatenated with no separator ("Building it now.Starting
by scaffolding…Clean slate. Now scaffolding…Next.js project
scaffolded…"): the SDK's textStream emits every step's text back-to-back
within one multi-step generation. The TUI-facing streams now wrap
fullStream and insert paragraph breaks at step-start boundaries, so each
step's narration reads as its own block (main stream, continuation
rounds, guard round, and the length-truncation continuation).
Co-Authored-By: Claude Code <noreply@anthropic.com>
The final error showed only the LAST provider's failure ("No output
generated") — hiding that the cloud token expired, the OpenAI key was
malformed, and the DeepSeek key was rejected. The fallback loop now
collects each provider's first failure reason (deduped, bounded) and
appends a "Per-provider:" block to the all-failed message, so the fix
(auth/keys) is visible immediately instead of requiring log digging.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Mercury Cloud connectivity self-recovery (refresh-token rotation → agent
API key redemption) was wired to config.cloud.agentApiKey only — configs
where the agent's key lives as the LLM gateway access key
(providers.mercuryCloud.apiKey, sk-mc-) had NO recovery path: when the
single-use refresh token died, every rotation 401'd forever until a
manual browser re-pair. The store now falls back to the sk-mc- gateway
key for redemption; redeem validates agent identity server-side, so a
mismatch fails safely into an explicit re-pair hint ("mercury cloud
connect") instead of an opaque 401 loop.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…ecovery key" This reverts commit 86e24f9ac87ee275e245a2653ae62322148d46cb.
…y redemption The revert removed the sk-mc- redemption fallback (it rotated cloud credentials server-side and broke the chat connection); this restores only the actionable error text: when no recovery key is configured, the rotation failure now says to run "mercury cloud connect" instead of an opaque 401. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ut error When a provider request fails (401, invalid key), the AI SDK's stream records zero steps and its flush rejects usage/finishReason with a generic "No output generated. Check the stream for errors." — which was awaited BEFORE the captured streamError, masking the actual auth failure behind a useless message on every provider in the fallback chain. The real streamError (and abort state) is now checked first, so the per-provider aggregate shows the true reason (bad key, dead token) and the fix is obvious from the chat. Co-Authored-By: Claude Code <noreply@anthropic.com>
…calls Regression report: with the same shared config, the dev build (repo) could not complete Mercury Cloud LLM calls while the npm-installed build worked — isolating the difference to yesterday's perf commit. The prompt-cache breakpoint changed the system prompt from a plain string to an array of text parts with providerOptions; at least one gateway in the provider chain rejected that request shape, surfacing as a masked "no output" failure. Reverted to the plain-string system prompt and the 4096 output cap (matching the working npm build); per-attempt latency logging stays. Co-Authored-By: Claude Code <noreply@anthropic.com>
Live finding: a narration-prone model satisfied the FORCED tool call
with list_dir/read_file every round (inspection, not action) and burned
all five guard rounds into an honest pause. Three closing moves:
1. Forced steps are now MUTATING-tools-only — inspection is impossible
on a forced step (grounding already provided the directory listing).
2. WAKE-UP CALL: when the first full guard cycle fails to start the
work, the agent does not pause — it doubles the bound and issues a
blunt directive ("your next response MUST begin with a mutating tool
call, ZERO prose") plus fresh grounding. Ten mechanical rounds total
across the provider chain before any pause.
3. The pause, when it finally happens, says WHAT blocked it: the last
failed mutating-tool result (e.g. "write_file: permission denied")
is appended to the message and the ledger reason.
Co-Authored-By: Claude Code <noreply@anthropic.com>
… loop The 'task drops in the middle' failure on medium tasks had two mechanical causes, both now fixed: 1. Output cap 4096: a single-file app write is emitted AS tool-call arguments — at 4096 the call is severed mid-argument, never executes, and the model retries into the same wall forever. Small tasks fit under 4096 (why they worked); medium ones never could. Restored 8192 — the earlier cloud failure was the system-prompt request SHAPE, not the cap; the cap is a plain number. 2. The truncation continuation round ran with stopWhen: stepCountIs(1) — a ONE-step round. The model spent its single step reading files and could never reach the write: 'reading all files, then writing everything' repeated forever. Continuation rounds now run with the full step budget. 3. When the truncation severed a tool call (lastStepHadToolCalls), the nudge is write-specific: do not retry the giant write — write in sections (create_file first 80 lines, edit_file appends). Co-Authored-By: Claude Code <noreply@anthropic.com>
"Continuing truncated response · auto-resume after output limit" was machinery-speak in the user's face. It now reads "Writing the next section... / Finishing the write — continuing past the size limit" — consistent with the escalation voice: chat and status speak like a person, detail lives in the logs. Co-Authored-By: Claude Code <noreply@anthropic.com>
Adopted the core resilience practice from OpenCode's session pipeline (sst/opencode — SessionPrompt.loop + SessionCompaction): on context or memory pressure, COMPACT the conversation and CONTINUE instead of killing the task. Mercury's step governor aborted on the first 'abort' verdict — long coding builds died at memory pressure even though old tool bulk (gigabytes of file reads across a long session) could be summarized away. Now: the first governor 'abort' verdict triggers aggressive in-place conversation compaction (messages beyond the newest 8 get oversized tool results and long text replaced with head+tail summaries, via summarizeToolResult) and the loop CONTINUES; a second consecutive abort verdict aborts as before. The live activity shows "Freeing memory — compacting the conversation". Also already aligned with OpenCode: doom-loop detection on repeated tool calls, provider fallback chain, bounded retries, per-step governor checkpoints. Co-Authored-By: Claude Code <noreply@anthropic.com>
"Why do we have a size limit? We should not have any size limits." Correct: a single-file app write is emitted AS tool-call arguments, so any Mercury-imposed cap eventually severs a legitimate write mid-flight. The fixed cap (last 8192) is replaced by MODEL_OUTPUT_TOKEN_LIMIT = 32768 — deliberately far above every mainstream model's NATIVE output limit, so the model's own limit governs, not ours. If a provider still rejects the value (some do when max_tokens exceeds their model limit), the attempt loop halves the cap adaptively and the chain continues — the task never dies on a configuration argument. The sectioned-write guidance remains as the fallback for models with genuinely small native limits. Co-Authored-By: Claude Code <noreply@anthropic.com>
Two regressions from the recent rework:
1. The live step list at the bottom ("✏️ Writing file.ts") was driven
only by the MAIN generation loop — forced guard rounds, verification
rounds, and continuation rounds (where most file writes now happen)
emitted no tool events, so the file activity went invisible during
exactly the moments files land. Those rounds now carry the same
onToolCallStart/Finish wiring as the main loop.
2. Enabling mouse reporting captures click-drag, so text selection
needs Shift+drag — the status line now says so instead of leaving
users guessing how to copy.
Co-Authored-By: Claude Code <noreply@anthropic.com>
A model reasoning before speaking (common with GLM/Claude) produced up to a minute of dead air: the stream only surfaced text, so reasoning deltas were discarded and the UI showed a bare spinner. The step-aware stream now also surfaces reasoning deltas as a live "thinking" preview (the model's own reasoning tail, quoted, dim) shown in the chat ThinkingIndicator AND the Mercury Code live feedback block. It clears the moment text starts streaming or the stream ends. The phase label reads "Thinking..." while only reasoning has arrived. Co-Authored-By: Claude Code <noreply@anthropic.com>
Two UX completions requested after live runs:
1. The completion banner now carries a change summary: each touched
file with +/− stats (git-verified) and the verification evidence
("✓ Verified: npm test ✓") captured from the run. The agent passes
the note from its command tracking to sendCompletion.
2. Long code blocks in MODEL responses collapse after 40 visible rows
("… code continues — full content on disk"), so a big code response
no longer pushes the conversation out of the transcript. File-change
previews were already pre-bounded; this covers the model's own code
quoting.
Co-Authored-By: Claude Code <noreply@anthropic.com>
User report: the mark still showed gaps between blocks. The per-row shaded band (████▓) read as faded/broken blocks in real terminal fonts. The splash is now 100% solid bright blocks — the shading machinery remains available for callers that want it, but the brand mark itself is uniformly filled. Co-Authored-By: Claude Code <noreply@anthropic.com>
Brand option D: the Mercury Code splash is now a framed panel — the
solid wordmark inside a rounded border, with the tagline caption
("⌁ interactive coding agent · vN") centered beneath. Two-tone
coloring preserved exactly (MERCURY cyan / CODE accent). Degrades to
the bare mark on narrow terminals (< mark width + padding).
Co-Authored-By: Claude Code <noreply@anthropic.com>
…gline" This reverts commit c6506bb.
The Mercury Code splash now renders at double pixel resolution: 10-row glyphs projected into the same 5 terminal rows via half-blocks (▀ top-only, ▄ bottom-only, █ both) — real letterforms with smooth tops, rounded C/O, a true R bowl, and a tapering Y, at 76 cells wide (fits 80-col terminals). Terminals narrower than 80 columns fall back to the 5-row solid block mark automatically. Two-tone coloring (MERCURY cyan / CODE accent) unchanged. Co-Authored-By: Claude Code <noreply@anthropic.com>
…option B)" This reverts commit 455f914.
User verdict: the double-resolution mark read as childish. Reverted to the classic fully-solid 5-row block mark — the retro pixel identity — with a clean band of air padding the top of the splash. Co-Authored-By: Claude Code <noreply@anthropic.com>
Version bump, changelog, release page, completion-architecture reference doc, landing-page announcement, and the rebuilt docs site. - package.json → 1.2.3; CHANGELOG.md gains the full 1.2.3 section - website/docs/releases/1.2.3.mdx — detailed release page - website/docs/reference/completion-architecture.md — the architecture documentation page (problem → solution, verdict system, escalation harness, compact-on-pressure, watchdog); wired into the sidebar - releases index lists 1.2.3 - landing page: hero badge + CTA now announce v1.2.3 · Unstoppable Mercury, plus a dedicated release banner section with a terminal preview of the new completion UX - docs/ rebuilt via the official build:prod script - tagged v1.2.3 Co-Authored-By: Claude Code <noreply@anthropic.com>
…no longer skip it CI regression on Termux (Android x86_64): newer npm policies block lifecycle scripts (`npm warn install-scripts`), so postinstall never ran and the bundled ink patch was silently skipped — the freed-Yoga-node crash class returned and static-transcript.test.ts caught it. - scripts/apply-ink-patch.cjs: idempotent ensure-script — checks the fix markers, applies patches/ink+5.2.1.patch via patch-package, and is LOUD on failure (a silent skip is never acceptable for a crash fix). - scripts/post-build.cjs now enforces it on every build, independent of install-script policy. - postinstall calls the script directly (never throws; loud on failure) instead of the silent `|| echo` fallback. Co-Authored-By: Claude Code <noreply@anthropic.com>
Second Termux failure revealed the remaining dependency: patch-package itself does not run on Termux/npm-11 containers (`sh: 1: patch-package: not found`, both via postinstall and npx --yes). The ensure-script now applies the ink fixes DIRECTLY — string edits with idempotent markers, no external tool: - reconciler.js: freed-Yoga-subtree reference hygiene (clearYogaRefs), #text-node guard, and staticNode cache clearing on both removal paths - Static.js: full patched rewrite — itemKey identity dedup + commitTick re-render that unmounts written children - Static.d.ts: itemKey type Verified end to end against a pristine registry tarball: the applier transforms true-unpatched ink to fully-marked, and all ink regression tests pass. Loud on failure; never a silent skip. Co-Authored-By: Claude Code <noreply@anthropic.com>
toLocaleString() without a locale renders Indian grouping (2,00,000) on in-IN hosts, making agent output machine-dependent. Add formatNumber() helper pinned to en-US and use it in TokenBudget.getStatusText(). Co-authored-by: Mercury <mercury@cosmicstack.org>
Mercury Code reworked to the native scrollback model: finalized messages print once via <Static> into terminal scrollback (patched identity dedup), only a small live region repaints. No sticky header, no in-app viewport; wheel scrolling and drag-selection are terminal-native (mouse reporting stays off). Streaming tails render through the full markdown pipeline on a fence-aligned 8KB slice; chat/coding live regions capped to 12 rows. Ink 5's log-update had no line diffing (every render erased + rewrote the whole frame) — patched to diff-render: only rows from the first changed line down are repainted (prompt navigation, spinner ticks, streaming). Bottom status bar rebuilt: single row, state-only segments (dir · git · mode · provider+model · tokens); key-hint wall dropped, non-git workspaces show no git segment, truncation instead of wrapping. New `mercury attach` mode: a second terminal joins the running runtime — machine-local 0600 attach token (loopback-only Bearer auth), fetch-based SSE client with reconnect, live session chat TUI with permission resolution; plain `mercury` auto-attaches instead of failing. Co-Authored-By: Claude Code <noreply@anthropic.com>
The Mercury Code completion contract now holds for plain chat on every channel: an implementation-style request must actually run its tools — narration alone no longer ends a task without having done anything, and a change that landed but was never verified is forced through one bounded evidence round (build/test/typecheck), same as execute mode. Gating: applies when programming mode is execute (Mercury Code, unchanged) or off (chat surfaces, non-internal messages). Text deliverables — poems, emails, essays: replies where the response itself is the work — are exempt from the narration guard (isTextDeliverableRequest), so chat is not forced through file tools to write a poem. Questions and chit-chat were already excluded by the guard's own patterns. Co-Authored-By: Claude Code <noreply@anthropic.com>
…, release sources kept - Built GitHub Pages output (docs/) and website landing + release mdx resolved in favor of main (38f79df): richer OG cards, emoji-to-SVG landing, rebuilt docs with v1.2.3 release page. - src/, patches/ink+5.2.1.patch resolved in favor of release/1.23 (6bd9d20): completion contract, TUI scrollback, token formatting. Co-authored-by: Mercury <mercury@cosmicstack.org>
- New Cloud Instance platform page (hosted Mercury Code via Atomic Bot) - Hosted-alternative section in Daemon Mode docs - Sidebar + installation page entries; links use utm_source=agent
MercuryCodeView renders its transcript inline (not via <Static>), so Ink re-renders the full frame on every state change. The test was asserting that the transcript is absent from the re-rendered frame, which is valid for chat mode (<Static>) but not for mercury-code mode. Updated the test to verify the correct invariant: the ↓ keypress is processed and the prompt selection remains visible and interactive. Also adds CI-resilient ready-wait (poll for 'Option' up to 8s) and retry loop for the keypress (20 attempts × 100ms) to handle slow runners where Ink's stdin listener attaches after mount. Co-authored-by: Mercury <mercury@cosmicstack.org>
The full-TUI integration test (TuiApp + useInput + PassThrough stdin)
was inherently fragile on CI: Ink's stdin listener attaches
asynchronously, and on a loaded runner the initial render hadn't
written 'Option' before the ready-signal timed out — stdout.output was
just the cursor-hide escape ('\x1b[?25l') and every retry attempt was
dropped.
Replaced with a direct PermPromptView render test:
- Verifies the ● marker appears at the active index
- Verifies the marker moves when activeIdx changes (rerender)
- Verifies the prompt message and all option labels are present
- No stream timing, no retry loops, no CI flakiness
Also exports PermPromptView from App.tsx so the test can import it.
Co-authored-by: Mercury <mercury@cosmicstack.org>
Ink writes the cursor-hide escape (\x1b[?25l) synchronously but the component tree flushes on the next tick via React's reconciler. On a loaded CI runner the synchronous assertion ran before the flush, so stdout contained only the escape code and every assertion failed. Add a flush() helper that waits 20ms (one event-loop turn) after each render()/rerender() call. Also use EventEmitter-based FakeStdin (matching static-rerender.test.tsx) and pass patchConsole: false.
… is 32ms-throttled; fixed sleeps race on CI)
…or writable files Windows doesn't enforce POSIX permission modes; writeFileSync with 0o600 yields a 0o666 stat. Same gate as work-ledger.test.ts. Co-Authored-By: Claude <noreply@anthropic.com>
hotheadhacker
added a commit
that referenced
this pull request
Sep 10, 2026
The Release/1.23 (#116) squash merge committed a docs/ directory that contained 53 stale HTML/JSON files with literal <<<<<<< HEAD conflict markers — Docusaurus's incremental build cache had skipped regenerating pages that only differed in the conflict resolution, so the markers survived into the deployed site and broke every served page. Rebuilt with npm run clear + npm run build:prod (full cache clear) and re-synced to docs/. Zero conflict-marker files remain; index.html starts with <!doctype. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.