Land the compaction-loop fix, last_turn counters, and UI cleanup on whip-rlm - #149
Conversation
…s it surfaced Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he late-ACK fix Move whip-eval campaigns onto Modal VM Sandboxes without changing the native Harbor/Pier workers, task bytes, model settings, deadlines, or grading: one Docker-capable VM per attempt, a detached coordinator function, immutable allowlisted bundles, and a CLI (whip-eval modal submit/status/logs/fetch/ cancel/reconcile) that survives client exit. Reports label cloud runs as a separately qualified environment; nothing promotes the native baseline. Qualification results recorded under evals/reports/modal-*: infrastructure, 90-way credential-free lifecycle (90/90), and the two-task real-model smoke (2/2 passed, $5.32 estimated). That smoke exposed the one production defect, a controller export acknowledgement racing a worker that had already exited; modal_cloud.py now skips the obsolete ACK on an exact-owned integer exit and only suppresses a typed NotFoundError after ownership re-proof. 102 tests pass. Plan and resume plan live in .ai-docs/plans/modal-evals. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Inference.net now lists the pinned route as moonshotai/kimi-k3 while the bare kimi-k3 id still serves completions at high effort. prepare.catalog falls back to a listing whose last path segment equals the pin, keeps kimi-k3 as the id whip calls and caches so routing and pricing lookups stay aligned, and records the listed id as listed_id. Exact listings still win; ambiguous or near-miss listings still fail closed. 103 tests pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-size limit Inference.net now rejects prompt + max_tokens above the 1,048,576-token context, and the pinned whip binary sends max_tokens equal to the catalog's max_completion_tokens when the config leaves maxOut at 0, so every smoke trial died with a 403 before its first model call. The frozen eval configuration now sets maxOut to 262,144: it cannot realistically bind and leaves about 786K tokens for prompts. Documented as a deviation from the native cohort, which ran effectively unbounded. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
🔍 loupe · go-review✅ no issues · 2 files PR-wide Go/web changes were previously reviewed; this pass reassesses only the two files changed since: the desktop-check workflow now installs the Playwright ffmpeg build (required by smoke.mjs's new recordVideo) and points the smoke at the already-uploaded evidence directory, and smoke.mjs gains drag-geometry diagnostics plus sidebar drag-to-split assertions. Callers verified: smoke.mjs is invoked only by this workflow (env paths are safe — the script mkdirs the artifacts dir, and the upload step at desktop-check.yml:58 already globbed that path); the 'Connect' and 'General' locator updates match the current host-connection-dialog and settings navigation in this PR; the new assertions match the WorkspaceTabs/WorkspaceLayout drag contract (fresh view per sidebar drop, existing tab order preserved, window bounds unchanged, draft preserved). No correctness, concurrency, or security defects found in the reassessed files. Run details
Last reviewed commit: |
…ion log Runs -a and -b are local preparation failures (key absent, provider model rename); -c ran end to end with the reviewed controller (complete, proven cleanup) but every trial died on the provider's new total-token limit; -d passed all six terminal tasks after the maxOut cap, with both repository tasks lost to a provider-side 30-second no-token stall. The plan's log carries the diagnosis and the pending decision. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings the detached Modal eval workflow, its qualification record, the late-ACK controller fix, and the 2026-09-13 smoke reruns onto the branch the whip fixes are landing on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Inference.net's kimi-k3 route now stalls on long calls (a 30-second no-token cutoff killed both repository tasks in today's smoke), so the pinned route moves to kimi-k3-fast. The model id was spread across the protocol file, whip's config writer, the observer's CLI arguments, the native runner's model label, and an adapter guard; it now lives in whip_evals.common.MODEL, the observer takes it from the contract's configuration, and a test asserts frontier/protocol.json agrees. The track name changes with it, so cloud reports form a separate environment from the native kimi-k3 cohort. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…no defaultModel The runtime-ab tests drive observe.run with a stub configuration; canonical trials still take the model from the contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Supersedes the 2026-09-11 MCP review with recorded decisions: repairs plus a truthfulness pass, definition server list as a boundary, eager connect with a separate project import source, project-over-user-import precedence, import confers trust, ACP-only additive attach, structured content and image handles, and a five-server acceptance matrix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The suite no longer reads the developer's ~/.codex/config.toml by default, and the opt-in run logs header counts instead of header values. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The daemon-managed stdio path copied cfg.Env into the process manager verbatim, so a documented "$VAR" reference reached the child as the literal string while the fallback path resolved it. Both transports now call one connectSecrets step. "!cmd" helpers run under the connect's own context via ResolveSecretContext, so a cancelled connect no longer leaves a helper running. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
headerTransport re-adds configured headers after Go's redirect logic has already stripped Authorization for a cross-origin hop, so a redirecting endpoint handed the bearer token to another host. The remote client now refuses redirects that leave the endpoint's scheme and host; same-origin redirects keep the headers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The daemon reads null as every host server and [] as none, but the SDK's generic list helper collapsed both undefined and [] to null, so an agent computed with an empty allowlist requested all servers. Only the MCP field changes; other list fields keep nil-equals-empty semantics. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Connect and catalog refresh read only the first page, so anything past it did not exist to the model. listAllTools follows cursors under the startup deadline, bounded to 64 pages, rejects a repeated cursor, and returns the whole catalog so callers publish it atomically. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
kickAutoReconnect only ran from the disconnect watcher, so one failed redial ended the chain at a single attempt despite the three-attempt budget. A failed automatic attempt now schedules the next one; a startup or manual attempt still stays failed for a human, and a manual reconnect resets the automatic budget. The give-up test asserts the exact count. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Codex TOML reader parsed the whole file, so one [[array]] table or an unsupported top-level value anywhere in ~/.codex/config.toml failed all MCP discovery, and the daemon showed "no servers" for "failed to parse". Only [mcp_servers...] tables are parsed now; foreign sections are skipped and malformed server tables still error. Discovery errors become failed status rows named by source, are logged at daemon startup, and are printed by whip mcp import. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er imports The repository's .mcp.json shared the claude gate with the user's global ~/.claude.json, and a user-global Codex entry silently overrode a project file of the same name. The project file is now a third import source, project, off unless enabled because a repository author wrote it, and the merge order is native, project, codex, claude-global. Blocked rows name their source. The daemon, TUI, web panel, and protocol status carry the new source; schemas and generated TypeScript are regenerated. Also adds SourceErrors to the client-control test fake so it satisfies the interface extended in the previous commit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Materialized imports dropped their Origin and Source, so an imported server copied into whip's own config is now trusted like a hand-written entry, no per-call consent. This is the one path from imported-and-prompting to native-and-trusted; the command says so on both dry-run and apply. Closes mcp-polish item 8 (overlay entries): a materialized import is native, not a patch over its source. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…itive Startup filtered discovery by the definition's server list while mcp.attach rebuilt the manager from every native server, so one authorized attach widened a restricted agent. mcp.Select is now the single selection step for the factory and for attachment. Attach no longer replaces the running manager or re-reads native configuration: attachments are untrusted and additive across names, names outside the definition's list or belonging to a native server are recorded as blocked rows, and re-attaching a non-native name replaces that entry. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
flattenResult dropped structuredContent whenever any text existed and reduced images to placeholders. A call's text now keeps every text part and appends structured content as JSON; image, audio and binary resource parts travel as attachments that the calling agent's services store as content handles named in the text. Image parts also reach the root's next turn through the existing screenshot sink; children get the handle only. CallChecked returns a result struct so nothing is lost between the manager and the dispatcher handler. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Policy-filtered and attach-refused servers showed as "disabled" and unreadable discovery sources as "failed", so clients could not tell a row the daemon can act on from one it cannot. Blocked rows now carry status "blocked" and source rows "unreadable"; both are never live servers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Removes the private JSON attach box: it duplicated whip mcp add with a
sensitive one-shot input, and attach is an ACP-facing operation now.
Renames "Available tools" to "Built-in tools" since it lists whip's own
schemas, not MCP tools. Each server row offers only the controls the
daemon can honor for its state (none for blocked or unreadable rows),
every control names its subject and scope ("Reconnect docs", "Disable docs
for this session"), and import toggles live under "Host import defaults"
with the project source.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The palette derived its server rows from local config, so imported, attached and remote-daemon servers appeared in status but not in the palette, and a stale local name offered controls the daemon could not honor. The model now keeps the last mcp.status rows and the palette offers each server only the actions its state supports, with blocked and unreadable rows pointing at the status view that explains them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Integrate Browser tabs while preserving the target branch's newer UI and MCP changes over the feature branch's earlier checkpoint. Retain both SDK fixture runner controls and tool-host handling, and regenerate guide digests from the combined source. Validated with the full Go suite, protocol generation/check, 368 SDK tests, 720 frontend tests, 118 desktop tests (10 opt-in SSH skips), 92 distribution tests, frontend build/typechecks, desktop typecheck, 49 live-daemon SDK acceptance tests, and the selected-holder Browser WebSocket regression (synthetic native bridge).
The shell guide line said shell.start "returns a job id" and listed shell.read(handle=...) with no source for the handle. In the Starlark run modal-full-20260917-starlark-b the model passed the whole start result as id (host saw "" and said no such job), indexed ["output"] on a wait view before the job had ended, and handed job ids to shell.read (rejected as an unauthorized content reference). The line now names the start result shape, says to pass job["id"], states the 25 s wait cap and timed_out, that tail's text is under "tail", that poll/wait carry output only after the job ends (inline or as a handle), and that shell.read takes that handle, never a job id. The result-shape rule points at the same source. Golden prompts regenerated for both dialects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, Starlark strings Three fixes chosen from the r2 and Starlark HALO analyses: keep "output" on large bounded results (the model followed the result-shape rule and the host withheld the key), make a wrong-typed or job-id shell argument answer with the fix or the job itself instead of "no such job \"\"" and "not authorized", and give the Starlark guide its multi-line and raw string sentence. Records Sam's decision not to touch QuickJS const redeclaration semantics. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Combine the upstream shell-job and content-handle guidance with the existing Browser instructions, and regenerate definition prompts and engine digests. Preserve the earlier Browser/chat integration and all subsequent UI work. Validation: go test ./internal/rlm ./internal/agentdef; race-enabled definition prompt and runtime-guide tests; app and SDK trees unchanged from 6ae8d5d.
|
See the Actions run logs for details. |
Record explicit compaction pin state in schema v21, normalize legacy timestamps transactionally, and recheck compaction after history growth. Use checked integer parsing for MCP settings, align browser probes with current presentation and stream readiness, and add boundary regression tests raising portable race coverage to 90.1%.
Enable the existing whipcode CodeQL configuration on PRs targeting whip-rlm so GitHub can compare against the branch baseline. Keep publishing branch-only, preserve its concurrency lock and permissions, and test the security and release dependency contracts.
Emit compaction start callbacks only after shared foldability preflight while preserving all three eventful paths and callback-free callers. Accept valid peer-completed v20 migrations in v19 and older compatible upgrade helpers without weakening schema identity checks. Cover no-op callback ordering and stale-opener migration idempotence with race-tested regressions.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 6 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ad81db0. Configure here.
Review follow-ups and final validationReview follow-ups are complete in
Independent hardening (not another thread): Compatibility note: old v20 compactions cannot reveal whether a pin was intentional, so the migration conservatively defaults legacy pins to false; raw messages and summaries are unchanged. Final validation: All checks passed on Desktop diagnostic caveat: The preceding |

Summary
Four commits from the local
whip-rlmbranch, routed through a PR becausewhip-rlmrequires thego,govulncheck, andcodeqlchecks and rejects direct pushes.internal/agent/agent.go): a fold may now cut inside an oversized turn on an assistant/tool-pair boundary, pinning the turn's opening user message verbatim after the summary. A fold with nothing left to fold makes no model call; a fold that cannot get back under the threshold stalls further proactive folds for the turn; a provider context-limit rejection with nothing left to fold fails the turn withErrCompactionExhausted. The reactive one-retry is decoupled from proactive folds. Background and evidence:.ai-docs/plans/compaction-loop/.internal/session/session.go):applyCompactionre-derives the pinned message when the fold point is not a user message.internal/session/turn_outcome.go, guide fragment, web agent list):last_turncarriesmodel_calls,compactions, andlast_activity_atfor the running turn; protocol types regenerated.cmd/whip/daemon.go): onecompaction.fallbackline in whip.log when summaries fall back to the conversation model.docs/agent-loop.md; Loupe SDK-ergonomics records; distribution test fix.packages/app,apps/web,apps/desktop, andpackages/ui.Merging into
whip-rlmtriggersrelease-whipcode.yml.Test plan
go test ./internal/... ./cmd/...green (one daemon acceptance test flaked once under full-suite load withdatabase is locked, passed on every isolated rerun).npm run checkinpackages/protocol(tsc, interop, drift),tsc -p packages/app/tsconfig.json --noEmit,npm run test:web(602 tests) green.🤖 Generated with Claude Code
Note
Low Risk
Documentation-only changes under
.ai-docs/plans/with no runtime, protocol, or security surface modified in this diff.Overview
Adds internal planning and validation ledgers under
.ai-docs/plans/for desktop Browser work—no application or daemon code appears in this diff.Browser Design Mode (
browser-design-mode/): documents the end-to-end Design Mode product contract (hover/select, floating composer, capture/send via existing chat paths, native overlay architecture), implementation slice checklist with automated gate results, and a narrow hover-outline motion follow-up (100ms ease-out, reduced-motion behavior, native/renderer contracts).Browser tabs & agent attachment (
browser-tabs/): a large phased plan for workspace Browser tabs, SSH previews, permissions, and agent control; frozen integration contracts; agent-initiated tabs andlist_tabs-style discovery (marked implemented in the working tree); and evidence notes for SDK/WebSocket, native SSH, and packaged acceptance/release gates.These files record decisions, ownership boundaries, and test status for reviewers—they are not canonical shipped docs (
docs/*links to some of them).Reviewed by Cursor Bugbot for commit 46b6a00. Bugbot is set up for automated code reviews on this repo. Configure here.