fix(frontend): repair tsc build break on main (pr_link type + stray Chat onRename)#6
Closed
liorp wants to merge 85 commits into
Closed
fix(frontend): repair tsc build break on main (pr_link type + stray Chat onRename)#6liorp wants to merge 85 commits into
liorp wants to merge 85 commits into
Conversation
Derived from internal main @ d3b0d6b524329be6d6dd84e623811b1974d1bf54. Generated by .githooks/sync-public-release.sh — do not edit by hand.
…y real-data probe Restart survival: - Persist job state to <ba_home>/native_import_job.json, checkpointed after every item + on completion (atomic temp+replace writes). - resume_if_interrupted() on backend startup: a persisted "running" job can only mean a crash mid-import, so re-run for its provider scope — the idempotency registry skips already-imported sessions, finishing the remainder duplicate-free. Hooked into _recover_in_flight_task. - get_status() falls back to the persisted state when no in-memory job exists (right after restart, before resume fires). Bug fixes (all surfaced by an adversarial probe importing REAL ~/.claude, ~/.codex, ~/.gemini sessions — the synthetic suite missed them): - Leading metadata (ai-title / queue-operation / file-history-snapshot) arrived before the first user prompt and forked an empty leading assistant bubble. _segment_turns now buffers leading events into the first real turn. - Re-emitted/duplicate user prompts (agy) forked consecutive prompt-only turns with empty assistants. Consecutive boundaries now collapse. - gemini events carried no uuid, and apply_event only appends uuid-bearing events to the render tree — so gemini imported NOTHING (silent drop). gemini events now get deterministic uuid5 ids. - Turns that collapse to pure metadata leave an empty assistant; those pairs are dropped, and if every turn does, the session is deleted and import raises (no orphan empty bubbles). - Per-key import lock closes the TOCTOU window where two concurrent imports of one native session could create duplicate sessions. Tests: replaced the circular segmentation differential (compared the code to its own mirror — proved nothing) with independent structural invariants over 2800 sequences; added restart-survival + status-fallback tests; asserted gemini assistant msgs actually carry rendered events. Plus a manual real-data probe script. 11533 assertions; probe clean across claude/agy/gemini/codex. Co-Authored-By: Claude <noreply@anthropic.com>
Source filter (web / cli / import) in advanced search:
- Imported sessions are now tagged source="import" (a first-class value;
was "cli", indistinguishable from real CLI sessions). session_store,
session_manager fork path, and the frontend Session.source union accept it.
- Backend GET /api/sessions accepts a `sources` filter (web/cli/import)
threaded through the full filter chain.
- Frontend SessionList advanced search gains a Source filter mirroring the
existing provider/mode filters; useSession sends it as `sources`.
Import limit + safe in-backend trigger:
- start_import(provider_ids, limit): caps NEW imports (already-imported
still skipped). POST /api/native-import accepts {limit}.
- New /api/internal/native-import (+ /status) gated by X-Internal-Token so
the import can run INSIDE the live backend. This is the only safe path
when a backend is up: a separate process writing session.json races the
backend's in-memory cache (it re-persists and clobbers the render tree,
leaving empty assistant bubbles — found the hard way).
- scripts/import_real_native.py now drives the internal route when a
backend is reachable, falling back to standalone with a warning.
Co-Authored-By: Claude <noreply@anthropic.com>
First-comers get a native-session import step in the wizard (reuses the self-contained NativeImportSetting) so they can pull in their existing Claude/Codex/Gemini CLI history right after setup. Gracefully shows nothing-to-import when no native sessions exist yet. Co-Authored-By: Claude <noreply@anthropic.com>
session_manager._persist_root is an async debounced write; a short-lived standalone import process can exit before the session.json stubs land, so the backend wouldn't list those sessions until a later reload. Drain flush_pending_persists() once at the end of import_real_native.py's standalone path. (The render tree itself is durable in events.jsonl, which the backend reconciles on load regardless — verified the rows are stamped with the assistant message id.) Co-Authored-By: Claude <noreply@anthropic.com>
- Recover the working directory for claude imports (read the first `cwd` field from the session jsonl) instead of leaving cwd empty. codex/agy/ gemini already carried cwd. - Project filter on the import: enumerate_native_sessions / start_import accept project_paths; only sessions whose cwd is under a loaded project are returned, and junk cwds (system temp, the BA state home) are excluded. None disables both (legacy import-everything). Threaded through both native-import POST routes. - scripts/import_real_native.py defaults to the loaded BA projects (project_store.list_projects, resync) and shows each session's cwd; --projects overrides, --all disables the filter. Tests: cwd recovery from jsonl, junk detection, project-filter keep/drop. Co-Authored-By: Claude <noreply@anthropic.com>
Both wire-event types are dispatched globally via SessionWSBroadcaster._dispatch but were missing from Coordinator.GLOBAL_EVENT_ALLOWLIST, so broadcast_global raised ValueError on every fire. The throw landed in a fire-and-forget task (logged as 'Task exception was never retrieved'), so the WS frame was silently dropped — extension attention-marker dots on session tabs and per-message auto-retry pills never propagated to other tabs. Add both types and close the coverage gap: the allowlist test trusted the _dispatch dynamic-caller audit note but never verified the broadcaster's on_change 'type' literals. Now it scans them. Co-Authored-By: Claude <noreply@anthropic.com>
…nd alert Generalize the bcsize font sentinel into a bcstyle sentinel that also carries a transparent background highlight; render it in MessageBubble. Carry an optional marker.sound flag end-to-end and play a synthesized attention sound on session_marker_changed. Co-Authored-By: Claude <noreply@anthropic.com>
…tive Backend's session-list endpoint filters out sessions with no content_scores match, so brand-new sessions never appeared in the sidebar while a search query was active. Insert new sessions at the top unconditionally — refetching wouldn't help since the content index doesn't know about the new session yet.
f785a83d replaced the provisioned search-worker turn with a shallow local grep (title + first_prompt only), so Ask queries that matched only transcript content returned 0 results. Revert run_search_sessions_session to dispatch SEARCH_SPEC via provisioning.run; delete the dead _run_local_search_sync helper and update the test that locked the regression to assert the worker is called. Co-Authored-By: Claude <noreply@anthropic.com>
Distinguish human-started sessions from ones created by Better Agent's own
tooling. Backend derives `initiated_by` ("user"/"tool") from existing fields
(no migration): tool = imported (source=import), extension/virtual, internal
kinds (delegate_fork/sub_session/adv_sync_fork/supervisor_worker), or
caller_agent_session_id; everything else is user. Exposed on the session
list payload + a new `initiated_by` filter on GET /api/sessions. Frontend
gains an "Initiated by" toggle in advanced search.
Co-Authored-By: Claude <noreply@anthropic.com>
The non-user bucket is "agent initiated" (covers imports, delegate / sub-session forks, adv-sync review, supervisor, extensions) — clearer than "tool". Value/label renamed across backend derivation and frontend filter; classification unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
… lag Every keystroke in a modal textarea invalidated the page-wide blur layer, forcing GPU re-rasterization of everything behind it. The supervisor and new-session modals have no per-keystroke JS work yet still lagged — the backdrop-filter was the only shared cost.
Full rename across ids, fields, files, CSS classes, translations. Persisted session_store entries with old keys will be ignored on load. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regression test for the namespace-package shadow bug where five private extensions declared `module: "mcp.server"` and the launcher loaded the SDK package instead of the extension's own `mcp/server.py`, exposing zero tools. Asserts each affected manifest now uses `python: "mcp/server.py"` and the server file exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Identify native sessions Better Agent itself spawned — its own user sessions plus internal agent sessions (delegate forks, sub-sessions, adv-sync review runs, workers, supervisor) — via their run-dir session_id and agent_session_id references on current session trees. These already live in BA (or did), so importing their native transcripts would duplicate agent/internal sessions instead of recovering a real external conversation. enumerate_native_sessions now always excludes them (on this machine: 30/11657 claude sessions skipped). 0 of the already-imported 210 are affected — no cleanup needed. Co-Authored-By: Claude <noreply@anthropic.com>
The user/agent distinction only matters at import time (only import user sessions), which the BA-managed skip in native_import already handles. A persisted derived `initiated_by` field + advanced-search filter added nothing, so remove it entirely: backend _initiated_by helper, the filter param/chain, the payload field, and the frontend type/filter UI/plumbing. Co-Authored-By: Claude <noreply@anthropic.com>
Boundary fix: supervisor-specific state belonged in the supervisor extension, not core's config_store. Drops `last_supervisor_prompt` from `config_store` schema, removes the write from `/api/internal/supervisor/toggle`, and removes `last_prompt` from `/api/internal/supervisor/default-prompt`. The extension now owns persistence via SDK extension-scoped storage in its own routes. Deletes the orphan unit test for the removed core helpers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A live/streaming optimistic assistant placeholder (id `live-*`) carries no `seq`. The load-older cursor reduce used `m.seq ?? 0`, which folded the cursor to 0 whenever such a ghost was in the message list, silently disabling "Load older messages" for the whole thread. Compute the oldest cursor over numeric seqs only via a shared `oldestNumericSeq` helper; applied to Chat and both ForkSplitView load-older sites. Co-Authored-By: Claude <noreply@anthropic.com>
Add message_count==0 as the first order field in both backend _session_list_sort_key and frontend sortSessionsForList so a brand-new empty session always appears first, above pinned/recency. Also repair two sibling sort tests that broke on the now-required sort_by arg. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the advanced filter panel inside the .session-list-items scroll container as its first child so it scrolls together with the session rows instead of staying pinned above them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotProvider (subclasses GeminiProvider — reuses RunState, tailer bootstrap, completion watcher, disk recovery) drives `copilot -p ... \ --allow-all-tools` via runner_copilot.py. The runner tails Copilot's own session-state event log — current dir layout <config_dir>/session-state/<sid>/events.jsonl, legacy <sid>.jsonl — and normalizes its type-tagged events (user.message, assistant.message, tool.execution_start/complete) to Claude-shaped agent_message events. Subscription-only (GitHub OAuth via gh), native-mode only: no fork, no team/manager, no steering, no reasoning effort. Registered in the provider registry, model refresh + cold-start dispatch, and the setup installer map (brew install copilot-cli). Frontend gains a Copilot setup template + i18n (en/he). Verified end-to-end against the real CLI; 10 focused unit tests in backend/scripts/test_provider_copilot.py. Co-Authored-By: Claude <noreply@anthropic.com>
ui_hooks() iterated list_extensions() with the default include_hidden=False, so the marketplace — added to PUBLIC_EXTENSION_LIST_HIDDEN_IDS for the public release — also lost its Settings page hook, removing the Marketplace UI entirely. The hidden-list is meant to hide it from the manage-list only. Pass include_hidden=True so the page still surfaces. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ion DB agy/antigravity runs rendered the assistant response as one giant stdout block instead of ordered, separated text turns + tool calls. The parent conversation DB carries the real ordered turn structure (step types 14/15/ 127/132/101), but only the subagent fan-out was being decoded. Add _ParentMainState (per-step, stateful) that walks parent steps in idx order and emits user prompts, assistant text turns (with protobuf bot- suffix artifacts stripped), and non-subagent tool_use events. Dedup the type-15 vs type-127/132 duplicate tool pair by payload signature so each logical tool call emits once. invoke_subagent payloads stay routed to the worker-panel path (single representation, no double-render). _agy_worker_events now interleaves main-thread + worker-panel events in true step order, so the streaming watcher and the post-exit flush produce the same ordered list — parent text/tools stream live during the run. _run skips the single-stdout fallback when ordered parent events were emitted; falls back only when extraction yielded nothing or the run failed. Refactored _extract_parent_subagent_events into _ParentSubagentWalker (per-step, stateful) so the unified walker can merge by step idx without duplicating the worker-panel logic. Co-Authored-By: Claude <noreply@anthropic.com>
…ASKS__DONE blue dot Add a second tag rule so the agent can signal completion with <ALL_TASKS__DONE>, surfacing a blue attention dot that clears on view. Rename the extension (id/dir/name/instructions) to reflect both markers. Old id migrates via _OBSOLETE_EXTENSION_IDS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The marketplace is missing from Settings because the installed package is the public-repo MCP-only build (surfaces: ['runtime_mcp'], no frontend), not the full-UI private package. Its real UI is a frontend_modules "settings" slot rendered from the extensions list, not a ui_hooks() page/quick_button, so the prior include_hidden=True change in ui_hooks() fixed nothing. Reverting it and its test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…prompt agy model steps carry assistant prose AND a tool call in the same step (a Claude-style text+tool_use turn); the previous parent extractor returned early on the tool and dropped the accompanying prose. Emit text first then the tool so narration before acting is no longer lost. Strip the protobuf bot-identifier wherever it appears in the string (the blob often continues past the uuid, so the old end-anchored regex missed it and _meaningful_text's bot- guard rejected the whole turn). Stop re-emitting the type-14 user prompt as a main-thread event — it is already the session's user message; emitting it duplicated the user bubble. Track prompt texts only to suppress the type-23 echo. Co-Authored-By: Claude <noreply@anthropic.com>
…bble Two Ask UI bugs surfaced after restoring the LLM search worker: 1. No running indication while the ~40s worker fork is in flight. The Ask session never enters `_run_state`, so the normal running_changed recompute path can't flag it. Emit `session_running_changed` True at the start of `_ask_search` and False in a finally, guarded by latest-wins (`_inflight is current_task`) so a cancelled prior search doesn't snuff the newer one's badge. 2. The worker fork's full transcript (inherited provision "ready" priming + every grep tool_use) was grafted onto the Ask assistant message's `events`, leaking "ready" and noise into the bubble. The Ask message now keeps only its answer text (content, derived from the worker reply) + the picker; `events` is empty. The worker transcript remains in the worker panel/provenance. Co-Authored-By: Claude <noreply@anthropic.com>
All 26 extension test files now pass as standalone scripts (was 5 failing files, plus ~15 additional failures hidden behind the first failure in the large test_extension_store.py runner). Fixes are test-side corrections for stale assumptions; production behavior is unchanged and correct. - consent_gate: use the V2 first-party source type (better_agent_bundled) instead of the never-valid literal "builtin". - iframe_token_auth: assert the real security invariant — HTTP never authenticates via query-param tokens. Extension frontend assets are intentionally public static assets (browser dynamic-import constraint), so a ?token= is ignored there; protected APIs must reject query tokens. - provider_config_sync_extension, extension_store (harness/ask/marketplace/ supervisor/purge tests): trigger the explicit reconcile path (list_extensions_with_reconciliation / _load_with_changes) before reading. Pure reads (_load/get_extension) no longer seed/bootstrap by design. - extension_store: repurpose the dissolved-requirements rehydrate test to a non-managed synthetic id (requirements now ships from better-agent-private, so the managed-id skip correctly short-circuits it); fix the runtime-MCP token assertion to verify per-extension minting (not the global input passthrough); add mcp.server to the smoke fixture's python_modules; resolve the venv bin path for the macOS /var -> /private/var symlink; gate the two LLM-readiness tests on clearing providers (tasks now resolve via inheritance from the default provider, so clearing assignments no longer makes a task unready); reset the project-structure tombstone so reconcile re-seeds the fixture. Co-Authored-By: Claude <noreply@anthropic.com>
The selected session is shown in the pinned anchor above the toolbar, so drop it from the list pool; its sub-sessions reparent to root. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The offline backlog's create_session payload omitted folder_id from its Pick<Session>, but App.tsx passes queued.folder_id to createSession — breaking the production tsc -b build. Add folder_id so an offline-created session preserves its folder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ate the prompt rewind_and_retry returned the failed turn's prompt but left the failed user+assistant pair in history; the frontend then appended a fresh user message and re-sent, persisting two consecutive identical user prompts. The endpoint now rewinds the session to before the failed user message (removing the pair and broadcasting rewind_complete) before returning the prompt. When the failed user message has a provider anchor the CLI is rewound too; otherwise rewind_files runs with provider_rewind=False to truncate the render tree only. Drop the stale frontend comment that claimed the backend already deleted the pair. Replaces test_retry_preserves_history (which locked in the buggy "preserve" behavior) with test_retry_discards_failed_turn covering both the anchored-rewind and truncation-only paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inline span with a background wrapping block-level markdown collapsed its background box, so the bcstyle font-scale/highlight painted nothing. Render the styled segment as a .bc-font-scaled block with padding and rounded corners so the highlight and scale render reliably. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r cwd as a project Provisioned machine-completion workers (e.g. the ofek-dev.requirements extension worker, cwd = its own install dir) and TestApe-isolated runs leaked hash-named projects into the user's project list. Gate all three auto-register paths (session create, REST create route, seed-from-sessions) through a single session_store.should_auto_register_project predicate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Printing the minted password to stdout leaked a full-access credential to
terminal scrollback, tmux/screen buffers, and any CI/launchd/systemd log
capturing run.sh output. First-run now sets credentials without ever
echoing a secret:
- BA_PASSWORD set -> use it silently (headless / scripted)
- interactive TTY -> prompt the operator to choose one
- no TTY and no env -> mint a random password, do NOT print it; onboard
via the login-screen QR or ./run.sh --reset-auth
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…duplicate sends can't double-render A prompt re-dispatched with the SAME client_id (frontend offline-backlog replay after a brief WS reconnect) raced the send_message handler's lock-free dedup: the three read-only checks (user_messages / queued_prompts / active_prompt_for_client_id) all missed, the duplicate persisted and broadcast a phantom user_message_queued (kind=queued_behind, new lifecycle) bubble, and only then did submit_prompt dedup+remove its queued row — leaving an orphan duplicate bubble in the UI (one persisted message, two shown). Close the TOCTOU: the handler now atomically claims (session, client_id) via try_claim_prompt_client_id BEFORE persisting/emitting anything. A concurrent duplicate is detected and echoed (kind=send, in-flight lifecycle) instead of emitting a phantom; submit_prompt honors the handler's pre-claim via _client_id_claimed and the claim is released on submit failure. Also release the claim on the cancel-after-dequeue path (orchestrator) so a cancelled queued prompt can't leak its claim. Test: test_prompt_client_id_claim_race covers duplicate detection, claim-aware admission, and cancel claim-release. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p concurrent steer Adversarial review of the claim-admission fix found two gaps: - Leak window: the atomic client_id claim was released only on submit failure, but the interrupt/alter cancel_turn and add_queued_prompt between claim and submit can also raise. On those failures the claim leaked and, since the offline backlog re-dispatches the same client_id, the re-dispatch would be deduped and the prompt silently lost. Now every failure between claim and submit calls _release_claim_on_failure (fail closed toward not losing user intent). - Steer bypass: send_mode=steer injects into the live turn and never reached the claim, so a concurrent same-client_id steer could double-inject. Added a localized atomic claim gate on the steer path (claim before inject, drop duplicate, release in finally). Also make _client_id_claimed conditional on a client_id actually being claimed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rd console.error
POST /api/logs/frontend was a stub returning {dropped:true}; implement it to
validate+clip the payload and write via the frontend logger. Override
console.error in the frontend logger so React ErrorBoundary crashes and manual
console.error calls reach logs/frontend.log.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface the Claude CLI pr-link agent_message as an auto-dismissing (10s) toast in the chat panel. pr-link has no uuid so it never lands on the render tree; the live WS frame is diverted in useWebSocket (before the turn reducer) to an onPrLink callback that App renders as a floating bottom-right toast. Fires only on live push, not replay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ECISION emphasis + dot Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…time Worker/sub-session panels were placed by a backend-stamped insert_at captured synchronously at MCP-tool-fire time, before the triggering tool_use event was tail-appended to the message — so a create_sub_session -> ask sub-session group rendered BEFORE create_sub_session instead of after the ask. Derive each panel's anchor at render time from the actual tool_use entry position (tagEvents + render_stub.timeline_events), matching delegation tool_use blocks to panels in firing order by name->kind; fall back to stored insert_at when no match (Codex native panels, queued/cross-message). create_worker is excluded (approval-gated; its panel appears later via a separate delegation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…first - Portal the pinned selected-session anchor into a slot above the Sessions/Workers tabs; it hides automatically on the Workers tab (SessionList unmounts there). - Auto-select the remembered (else first) session when the route lands on the empty Ask home and the project has sessions; explicit Ask navigation opts out via an intentional-ask flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regression test proving the shipping tagEvents places the sub_session_created marker after create_sub_session and ask panels after their ask tool calls, using real render-tree data captured from a create_sub_session->ask session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded color - message-group-children padding-inline-start 16->14 so its border-to-child pitch (2+14=16) matches sub-agent-block's 16px grid (was 18, snapping the rail off-grid at the first delegation level). Mobile override aligned too. - auto-action-group + timeline-block left stripe 3px->2px so every nesting rail shares one width. - add a deeper sub-agent selector so L2+ dims past L1's 35%, keeping the rail readable past two levels.
Ask view overrode .ask-group margin-bottom to 24px while regular turns use .message-group 16px, making ask turns spaced wider. Align to 16px. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The manifest quick button bare-navigated to the Ask singleton path; the auto-select-first effect immediately bounced it to a real session, so Ask never opened. Detect the ask path in the navigate-action dispatcher and call openAsk (sets the intentional flag + ensures the singleton). Matches both encoded and raw spellings of the path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
agy stores several noisy variants of each assistant prose string in a step blob: a trailing "2(bot-<uuid>:)" identifier and a leading snake_case tool name + field-type marker bytes (e.g. "grep_searchB^I am searching...", "^I am...", "II am...", "UI am...", ">I will..."). The previous strip only handled the trailing bot suffix and a doubled first char, so the rendered text carried "grep_searchB^", "^", "II", ">" prefixes and trailing ":". Strip a short leading marker prefix (<=20 chars) up to the first sentence boundary (I / I' / Capitalized-word+space) and tolerate ':', ')', or a truncated uuid on the trailing bot identifier. Verified against a real conversation DB: all parent prose now renders clean. Co-Authored-By: Claude <noreply@anthropic.com>
…d extension Publishes the FACT that a session is being dragged (session_drag_start/end on the eventBus, carrying session id + name) and exports SESSION_DRAG_MIME, so the private agent-board extension can reveal a themed drop overlay. Session rows are now draggable outside folder view. App reflects the drag into a new session-drag-overlay extension slot. Adds a narrow /api/internal/agent-board/run-prompt endpoint gated to the agent-board builtin token identity, delivering a lane's prompt action to the dropped session via session_bridge._run — the privileged cross-session op stays in core. Registers ofek-dev.agent-board in the private extension tables. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng survives The NEEDS_USER_DECISION tag rule wrapped inner text in literal **...**. When the agent's prose already contained markdown (**bold**, `code`), the injected ** collided with it (****..**) and CommonMark mis-parsed the emphasis, dropping the inner styling inside the highlight box. Bold now rides the same frontend bcstyle sentinel (b=1 -> font-weight) used for font-scale/highlight, leaving the agent's markdown untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… guard Adversarial-review fixes: - Restore the provider-change-during-active-run 409 guard in update_session_selectors that a concurrent working-tree change had dropped (it was swept into the prior agent-board commit by staging the whole file). - run-prompt: validate the target session exists (no arbitrary/virtual ids), cap prompt length at the endpoint, and hold a strong ref to the delivery task so it can't be GC'd mid-await. - Route delivery through new public session_bridge.run_for_extension instead of the private _run. - Tests cover unknown-session 404 and over-long-prompt 400. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arity Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
run-prompt now rejects a busy target session synchronously with 409 instead of scheduling a delivery that run_for_extension would refuse and discard; the background _deliver also logs any residual error-dict from the race. Test covers the busy-target 409. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Crash recovery's _replay_and_apply picked the native replay reader by provider kind, but only special-cased "gemini"/"codex". agy and copilot (both GeminiProvider subclasses that write Gemini-shaped session_events.jsonl) fell through to the Claude parser, so recovered turns rendered empty/partial after a backend restart. Add a single _GEMINI_FAMILY_KINDS set used at both dispatch sites, and give agy/copilot bumped ingestion versions so runs reconciled under the broken path re-digest on next startup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hat onRename)
`npm run build` (tsc -b) fails on two errors, so run.sh produces no
frontend/dist and the backend crashes at mount_frontend:
1. MessageBubble.tsx switches on a "pr_link" WS event (rendering
PrLinkEvent), but "pr_link" was never added to the WSEventType union
→ TS2678. Add it to the union.
2. App.tsx passes onRename={renameSession} to <Chat>, but Chat's Props
has no onRename (the prop is handled by <SessionList>, which keeps
its onRename). The excess prop fails assignment → TS2322. Drop the
stray prop; Chat never read it, so behavior is unchanged.
Verified: npm run build passes and ./run.sh reaches "Backend is healthy."
Co-Authored-By: Claude Opus 4.8 (1M context) <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.
Problem
maindoes not build.npm run build(tsc -b, run by./run.sh) fails:Because
tscfails,vite buildnever runs, so there's nofrontend/dist, and the backend then crashes on startup:Fixes
pr_linkmissing fromWSEventType—MessageBubble.tsxhas acase "pr_link"renderingPrLinkEvent, but"pr_link"was never added to theWSEventTypeunion intypes.ts. Added it.onRenameon<Chat>—App.tsxpassedonRename={renameSession}to<Chat>, butChat'sPropsdeclares noonRenameand the component never reads it (rename is handled by<SessionList>, which keeps itsonRename). The excess prop broke assignment. Removed the stray prop — no behavior change.Verification
npm run buildpasses../run.shreachesBackend is healthy.and/healthz→ 200.🤖 Generated with Claude Code