Repository navigation
Conversation
…ooks The cross-process lock in state.ts assumed POSIX semantics: any non-EEXIST error from link/readFile on loops.json.lock was fatal. On Windows, benign handoffs (delete-pending handles, antivirus scans) surface as EPERM/EBUSY/EACCES/ENOTEMPTY on open, so normal dialogue would occasionally fail with EPERM on loops.json.lock: every chat.message / system.transform / event acquires the session lease, and the lease heartbeat does it every 30s. - Retry transient lock errors briefly (5s budget) instead of throwing; genuine contention still waits on the EEXIST path. - Make lock release best-effort so a release failure can never turn a successful mutation into a user-visible error; sweep dead-pid lock.owner.* orphans on acquisition. - Passive V1 hooks (chat.message, system.transform, compacting, event) and V2 hooks (context, event consumer) log and continue instead of breaking dialogue. The V2 consumer previously died permanently on a single bad event. Verified with: bun test (106 pass, 4 new), lint, typecheck, build, plus a concurrent-mutation stress run and the original Windows APPDATA failure case.
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
On Windows, normal dialogue occasionally fails with
EPERM: operation not permitted, open '.../loops.json.lock'.The cross-process lock in
src/state.tsassumed POSIX semantics: any non-EEXISTerror fromlink/readFileon the lock file was fatal. On Windows, benign handoffs (delete-pending handles, antivirus scans) surface asEPERM/EBUSY/EACCES/ENOTEMPTYon open, and everychat.message/system.transform/eventplus the 30s lease heartbeat acquires the session lease — so the race is hit regularly. OnlyENOENTwas tolerated, so the transient error propagated into user-visible dialogue failures.A second, latent bug: the V2 event consumer loop has no per-event handling, so a single failing event kills the whole consumer and silently disables all future event-driven scheduling until process restart.
Evidence from a real machine: orphaned
loops.json.lock.owner.<pid>-*andloops.json.<pid>.ts.tmpfiles from dead processes, plus live multi-context contender files — confirming ongoing lock contention.Approach
src/state.ts: retry transient lock errors briefly (5s budget) on owner-create/link/read/release instead of throwing; genuine contention still waits on theEEXISTpath with the 30s deadline. Lockrelease()is now best-effort so a release failure can never turn a successful mutation into a user-visible error. Opportunistically sweep dead-pidlock.owner.*orphans on acquisition (live contenders are never touched).src/server.ts: passive V1 hooks (chat.message,system.transform,compacting,event) and V2 hooks (context, event consumer) log and continue instead of breaking dialogue. Tool paths still surface errors so the agent can retry. The V2 consumer now catches per event.dist/server.jsrebuilt per CONTRIBUTING step 5.Verification
bun test: 106 pass (102 baseline + 4 new), 0 fail.isTransientLockErrorclassification table; V1 passive hooks + V2 context hook resolve (not throw) when state is unusable; V2 consumer keeps processing after a failing event. Confirmed the new tests fail on unfixed code (4 fail + 1 import error) and pass with the fix.bun run lint,bun run typecheck,bun run build,pack:dry-runall green.Need help on this PR? Tag
@codesmith-botwith what you need.