getApiKey() retry loop (3 attempts, ~12s) couldn't outlast the 30s
rate-limit backoff window, causing cooldown errors to cascade through
the auto-loop and trigger a hard stop after 3 consecutive failures.
- Add AuthStorage.getEarliestBackoffExpiry() to expose when the next
credential becomes available
- Update getApiKey() to sleep until backoff expiry (up to 60s) instead
of fixed 2s/4s/6s delays
- Add isTransientCooldownError() detector in infra-errors.ts
- Auto-loop now waits 35s on cooldown errors without incrementing the
consecutive error counter
Closes#4052
PR #3564 narrowed the internal overlay to @gsd* prefixes only, which
dropped non-hoisted optional deps like @anthropic-ai/claude-agent-sdk
from the merged ~/.gsd/agent/node_modules directory. Revert to overlaying
all non-dotfile internal entries so optional deps resolve correctly.
- Use content fingerprint (packageRoot + sorted entry names from both
dirs) in .gsd-merged marker so pnpm add/remove triggers rebuild
- Restrict overlay loop to @gsd* scopes only, preventing accidental
shadowing of hoisted deps with internal versions
- Guard marker write behind linkedCount > 0 to avoid stamping success
on a broken/empty merged directory
- Log warnings when readdirSync fails on hoisted/internal roots
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pnpm's virtual-store layout doesn't hoist @gsd/* workspace scopes to
the parent node_modules, so the simple symlink-to-hoisted approach from
the original fix (#3529) left workspace packages unresolvable.
Detect when workspace scopes are missing from the hoisted root and
create a real node_modules directory with symlinks from both the hoisted
root (external deps) and internal root (workspace packages). A .gsd-merged
marker file skips rebuild on subsequent startups.
Restores behavioral tests deleted in the original PR and adds unit tests
for the pnpm merge path and scope detection logic.
Reported-by: @moekify
Fixes: #3564 (comment)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pinned "Latest Output" zone was only cleared at agent_end, but during
flows with form elicitation (e.g. discuss-phase), there is a gap between
message_end and agent_end where the agent waits for user input. During this
gap, the same content was visible in both the chat history and the pinned
zone. Clear the pinned zone at message_end when the assistant message is
finalized in the chat container.
Cover the composable helpers extracted from deriveStateFromDb:
reconcileDiskToDb, buildCompletenessSet, buildRegistryAndFindActive,
handleNoActiveMilestone, resolveSliceDependencies, reconcileSliceTasks,
detectBlockers, checkReplanTrigger, checkInterruptedWork, and queue
order sorting.