Resolve legacy workspace project references at the daemon boundary while keeping projectKey as the cross-host identity. Complete generic SSH normalization without collapsing distinct repositories.
Preserve platform path semantics and generic remote transports in cross-host keys, and compare legacy checkout roots using filesystem equivalence before materializing ownership.
Random host-local project IDs had replaced the persisted cross-host value carried by projectKey. Keep projectId for routing while reconciliation owns and persists projectKey for grouping.
Random host-local project IDs replaced the remote-derived value that had also served as grouping identity. Persist a separate opaque group key and let normal boot reconciliation backfill older records without a migration.
* fix(quota): restore Grok Settings usage for current CLI auth/billing
Grok CLI no longer stores a top-level access_token or usage.creditUsage.
Read nested auth key tokens and config.used.val so Settings → Usage
shows monthly credits again. Keep legacy shapes and env tokens working.
Fixes#2352
* fix(quota): make Grok auth-file tests work on Windows
Inject homeDir into GrokQuotaProvider like Kimi so nested
~/.grok/auth.json tests do not depend on os.homedir() which
ignores $HOME on Windows (USERPROFILE).
* feat(server): support symlink worktree includes
* refactor(server): simplify worktree include handling
* fix(server): constrain worktree include traversal
* fix(server): clean up failed worktree branches
* fix(server): skip missing worktree include entries
* fix(server): make worktree includes best effort
* fix(server): skip failed worktree includes
* fix(server): harden worktree include planning
* fix(server): preserve reused worktree result shape
Materialization reports describe one creation attempt, not durable worktree identity. Carry them beside the worktree so newly created and reused results keep the same stable shape.
* fix(server): harden worktree include boundaries
Replace directory snapshots exactly, keep canonical Git metadata protected, report recovery skips, and only roll back branches owned before worktree creation.
* fix(server): follow safe include aliases
Traverse canonical in-checkout directory links during glob planning and treat coded revalidation failures as per-entry skips.
* fix(server): make include preflight race safe
Create fetched checkout refs atomically, retain overlapping copy entries for independent fallback, and protect the full managed-worktree base.
* fix(server): preserve staged recovery state
Validate completed directory snapshots, retain backups after failed restoration, and derive atomic ref guards from the repository object ID width.
* fix(server): preserve partial include progress
Keep safe glob matches, enforce recursive-directory types, validate staged roots, and retain OID guards through rollback.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(app): stop completed turns from appearing stuck
Turn activity was inferred from a user-message row flag, so a stale duplicate row could keep the working footer active after the turn ended. Track submission lifecycle separately and route every send path through the shared submission flow.
* fix(app): guard pending message identity
* test(app): measure submission layout independent of scroll
* fix(app): keep submitted messages consistent through reconnects
Track each in-flight send until its own RPC establishes acceptance, and keep canonical timeline placement authoritative. Restore legacy cached rewind IDs during cache deserialization.
* fix(app): close canonical submission races
* test(app): cover canonical submission races in browser
* fix(app): keep replacement submissions authoritative
Keep current pending rows out of legacy cache migration and leave ambiguous terminal lifecycle events to the daemon snapshot. This prevents fabricated rewind identities and stale completion events from marking replacement turns idle.
* fix(app): keep submitted messages stable across sync
Give submission transport and canonical timeline ingestion separate authority. Preserve every unresolved local send across replacement, reconcile provider identity once, and bridge RPC acceptance to authoritative running state without deriving lifecycle from timeline rows.
* fix(app): settle submissions in either acknowledgement order
Complete submission transactions when RPC and provider acknowledgement arrive in either order. Cache only transaction-owned local rows as transient data, preserve canonical ID-less prompts, and invalidate ambiguous legacy display caches instead of inventing provider identity.
* fix(app): keep agent visible during history handoff
Running and terminal updates could clear create continuity before the initial authoritative timeline arrived, leaving a streaming agent behind the loading screen. End the handoff only when authoritative history is applied.
* fix(app): settle attachment-only submissions
Canonical providers can acknowledge image-only prompts with empty text. Reconcile those events by client identity without rendering a blank canonical row.
* fix(agent): settle out-of-band message submissions
Accepted commands that do not allocate a foreground turn previously had no canonical user acknowledgement. Record the command before its handler runs so submission state and reconnect history converge through the normal timeline producer.
* fix(app): settle out-of-band submissions compatibly
* fix(app): preserve canonical prompt order
* test(app): keep workspace status check timing-independent
The workspace-status scenario asserted footer settlement before initial agent creation had necessarily completed. The dedicated draft-handoff coverage owns that lifecycle contract.
* fix(files): keep large downloads connected
Large file reads were emitted as one frame, crossing the physical socket high-water mark and terminating otherwise healthy connections. Stream bounded chunks from one file handle and pace each transfer by its own send completion without globally queueing unrelated traffic.
* fix(files): keep paced relay transfers bounded
Carry send completion through encryption to the physical WebSocket callback, cap growing files at their advertised size, and preserve text classification when a UTF-8 code point crosses the sample boundary.
* fix(files): reject changing transfer snapshots
Abort when a file shrinks below its advertised size, finalize UTF-8 validation for complete samples, and let the physical relay socket remain the sole authority for queued-byte accounting.
* fix(files): validate metadata before transfer
Keep the daemon handshake pending until the ready frame is physically sent, and classify file content with a bounded full-handle scan so streaming preserves the previous binary/text behavior.
* fix(files): detect changing transfer sources
* fix(files): enforce transfer snapshot integrity
* fix(server): keep active agent trees resident
Idle collection treated an inactive parent turn as process quiescence even when descendant work was still running. Protect managed and provider-owned subagent trees, carry descendant activity into the idle window, and use a conservative 30-minute fallback.
* fix(server): close idle collection races
Retain managed ancestry across closed intermediates, refresh descendant state after each awaited close, and invalidate running provider subagents when their runtime is terminated.
* fix(server): serialize agent tree collection
Keep ancestors resident until managed descendants are collected, serialize descendant registration with idle collection, and preserve provider subagent state across hot reloads.
* fix(server): retain closed descendant activity
Carry persisted descendant activity through runtime closure, avoid error children pinning parents, and cancel stale provider children when reload replacement aborts.
* test(server): make descendant expiry deterministic
* fix(server): scope idle child protection
* fix(server): tighten idle tree coordination
* fix(server): reduce aggressive idle cleanup
Keep the mitigation conservative: extend the idle timeout without inferring tree liveness from incomplete lifecycle signals.
* fix(server): keep parents alive during child work
Idle cleanup now respects running managed and provider-native children while retaining a conservative 30-minute fallback.
* fix(server): clear running child state on close
* fix(server): drain child events before close
* fix(claude): select the correct Opus 5 context window
Bare Opus 5 IDs resolve to 200K behind third-party gateways. Expose explicit context variants and require a Claude Code version that recognizes the model.
* fix(claude): add Fable and Sonnet context variants
* test(cli): expect Claude context variants
* test(cli): compose Claude catalog expectations
* fix(claude): preserve catalog on version probe failure
* fix(claude): preserve parsed context variants
* chore(release): cut 0.2.2
* feat(server): control Hub execution lifecycle
* fix(server): archive only execution-owned worktrees
* fix(server): serialize Hub execution control after create
* test(server): compare Hub worktree paths canonically
* fix(server): make absent Hub controls idempotent
A Hub can own an execution before agent creation materializes. Treating an absent daemon-scoped record as already stopped or archived lets finality complete without revealing or affecting another daemon's execution.
* fix(omp): limit thinking levels to model's reported efforts
The omp provider exposed all six thinking levels (off/minimal/low/
medium/high/xhigh) whenever a model had reasoning enabled, ignoring the
per-model thinking config that omp reports via RPC. The OmpModelSchema
didn't parse the thinking field at all, so the model's efforts subset
and defaultLevel were discarded.
Parse model.thinking (efforts/defaultLevel/effortMap) in OmpModelSchema
and filter the thinking options in mapOmpModel to only the model's
reported efforts. The default is the reported defaultLevel when it's
in the filtered set, otherwise the first (lowest) effort. Older omp
versions that don't report thinking.efforts keep getting the full set.
Also fix createSession to not hardcode 'medium' as the thinking fallback
when the client doesn't send a thinkingOptionId — pass undefined so omp
uses its own model default instead of overriding it with a level that
may not even be in the model's effort set.
* test(omp): drop --thinking medium from provider-registry launch assertions
The thinking-level filtering PR changed createSession to pass undefined
instead of "medium" when no thinking option is selected, so buildOmpLaunch
no longer emits --thinking. agent.test.ts was updated but provider-registry.test.ts
still expected --thinking medium in two OMP launch assertions, failing server-tests
on ubuntu and windows.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
The base ref mismatch error printed the same value on both sides, so it
always read "expected master, got master". All four throw sites computed
`baseRef = compare.baseRef ?? resolvedBaseRef`, but the guard only fires
when the caller passed a ref, so `baseRef` and the "got" value were the
same string by construction. The value that actually differs -- the
stored `baseRefName` from the worktree metadata -- was never shown,
which left the error undiagnosable from the UI alone.
Build the message in one place and have the four guards throw it, naming
the refs `stored` and `requested`. The wording deliberately avoids
"expected": a caller's ref can be stale, but the stored ref can equally
be the wrong one, so the message states both facts and leaves the
diagnosis open.
The guards stay where they are. Only the message moves, so the condition
that fires each throw is still visible at the call site, and both refs
are narrowed to plain strings by the time the message is built.
Throw conditions are unchanged; only the message text differs.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>