Repository navigation
Let Dreamforge agents hop to the primary Claude account - #149
Merged
Merged
Conversation
…oken Every agent's pool is token=env;fallback=<folder>, both on the second account, so a limit on it had nowhere to hop. A pool entry can now be label=dpapi:<path>: a long-lived token in a file encrypted for this Windows user, read at each launch and each sign-in check and handed to the child only in its environment. A file that gives no token fails its own entry and the pool moves on. A pool with no login left refuses to start the agent rather than running it on whatever its own environment holds. A folder entry used to send CLAUDE_CODE_OAUTH_TOKEN="", which the CLI reads as signed out. It now sends no token, and the variable is removed where the launch env is applied: the agent spawn and the sign-in check. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pool string names the primary's token file, and the agent's tool shell inherited it. It is now removed from the child, beside the Nostr key. Two gates had no test. Making the token removal unconditional, or moving it after the login's own env, stripped every token=env and dpapi login and passed the whole suite. So did loosening the refusal to start with no login left. A real child now shows each kind of login's token, and a pool with no login left starts nothing. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A real 401 on a dpapi login marked it signed out, and the next sign-in check cleared the mark: claude auth status answers loggedIn:true for any token without asking the server. The work went back onto the revoked token every 15 minutes, with a lost-sign-in text and an all-clear each time, and every agent shares that one file. A real 401 now remembers the file's modified time. A check's yes clears the mark only once the file has changed; a real answer still clears it. A mark set because the file could not be read still clears on a good read. token=env keeps today's behaviour. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A launch that could not read a token file marked the login signed out whatever the reason, and the owner was told "Claude said: not signed in" when Claude was never asked. A PowerShell timeout moved work off a good account as a lost sign-in. The card's remedy was also wrong for a dead file: signing in does not replace it. A silent or unstartable PowerShell now sets the login aside as a timed limit, tried again in 30 minutes, and tells the owner once. A dead file still takes the lost-sign-in path, in its own words: which login's file, what is wrong and that set-account.ps1 is the fix, on the text, the STATE record (class=token_file) and the card. A login already inside a limit keeps it. The sign-in check's handling of a dead file moves to one small function with a test: a file that gave no token is never judged by the CLI. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A dead token file at boot, a slot refill or an idle move queued the owner's text, and only a finished turn or a sign-in check that changed something sent it. An idle agent ran on another account and the phone stayed silent. The maintenance beat now sends a text that is waiting. It does nothing when none is, so a STATE record that merely fell due keeps its carrier and #factory keeps its cadence. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A launch could read the same dead file once per pool entry plus one, under the factory lock on the main loop, so a hung PowerShell held the loop for 30 s or more and the comment's 15 s was low. The loop now stops when the pool offers a login it already failed, and the comments give the real bound: 15 s per dpapi entry. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A typo in a dpapi entry (DPAPI: in capitals, a label with a dot or a space, a label over 32 characters) launched the agent with CLAUDE_CONFIG_DIR set to the whole string and any token removed. Nothing named the bad entry. It now fails that entry with a plain reason and the pool moves on. Claude-Account: russelledeming@gmail.com Co-Authored-By: Claude Opus 5.5 <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.
This needs Russell's read before it ships: it touches agent auth (
lane:hold).Decided 10 Nov 2026 (Russell): Reset Worker gets no
dpapi:entry until guest turns have a permission check. Adpapi:login puts the primary's long-lived token in the adapter's environment, and on Reset Worker an outside allowlisted key drives a shell with no permission gate, so that key could read it. So the env edits after this PR skip Reset Worker.token=envalready exposes the second account's token this way. This PR only stops handing the shell the pool string, which names the file.What it does
Slice 6 of the Claude account switchover.
token=env;fallback=<folder>, both on the second account, so a limit on it has nowhere to hop. An entry can now beprimary=dpapi:<path>: a setup token in a file encrypted for this Windows user. It uses the same PowerShell call as the kit'slib.mjs, with$ErrorActionPreference = 'Stop'added. The token is read at each launch and each 15-minute sign-in check, goes to the child only in its environment, and isn't kept anywhere.envlogin that is merely out is still offered when every login is out, as it is today. Off Windows, a dpapi entry fails plainly.class=token_file) and the card say which login's file failed and why, and nameset-account.ps1as the fix. A garbled entry's reason says to correct the agent's settings instead, though its card still names the script. A silent PowerShell gets the text and the record, with no card. A dead file on a spare login gets one text and nothing else. A text queued at a launch now goes out on the next 30-second maintenance beat, instead of waiting for a turn to finish. That beat only runs once the pool is up, so a boot or lazy wake where no agent starts never reaches it: the card is written, but a normal start exits with the text and the record unsent, and a lazy one holds them.claude auth statusnever asks the server, so it says yes to a revoked token. A real 401 on a dpapi login now waits for the file to change or a real answer, instead of a check putting the work back on it every 15 minutes.token=envkeeps today's behaviour.DPAPI:in capitals, a label with a dot or a space, or a label over 32 characters fails that entry with a plain reason. It used to become a folder login with the typo as its folder.CLAUDE_CODE_OAUTH_TOKEN="", and the CLI reads that as signed out. It now sends no token, and the variable is removed where the launch env goes on the command. A PC-wide token still can't win over a folder login. Both places (the agent spawn and the sign-in check) go through one function.The path is config and is treated as untrusted: it reaches PowerShell in an environment variable and never as command text. A failure says which login failed in fixed words. It never carries the token, the path, or anything PowerShell printed. The one name taken from what was written is a garbled entry's: with no usable label, it goes by a slug of the entry's last path part, as an unlabelled folder does.
The pool strings on this PC do not change with this PR. Nothing uses the new entry until the four env edits Russell approves afterwards. The primary's file also has to exist by then, and the kit's
set-account.ps1can't write it yet: it only has-Phonetoday, and-Accountcomes with slice 3. Until those edits, the changes that take effect are: folder logins get the token removed rather than blanked, the agent no longer inherits the pool string, and a waiting owner text goes out on the maintenance beat.What was exercised
cargo test -p buzz-acp --libon Windows: 1286 passed, 42 failed. The branch point 5e1fce0: 1270 passed, 42 failed. The 42 failing test names are identical, and the 16 extra passes are the new tests.origin/mainhas since moved to 849bd8b; the comparison is against the commit this branch is built on.cargo fmt, andcargo clippy -p buzz-acp --all-targets -- -D warnings, clean.claudeand agents were not run).'; New-Item ... #). Both exited 1 with empty stdout, and the injected command did not run.What was not exercised
loggedIn:truecomes from the CLI's docs and this crate's own note on it.claudewas not run.cargo testran on Windows only. The non-Windows branch was not run locally: it is left to CI's Linux run, wherejust test-unitruns buzz-acp's tests.Known limit
The desktop's agent panel has no wording for
class=token_file, so when a running harness has no login left for that reason its "Paused" line is blank, unless the newest turn failed. The text, the card and the STATE record still say why, and the presence dot shows away. Fixing it is a desktop change, so it is left out of this crates-only PR.Size
Over the design's 150 lines, and at 854 changed lines over the ~600 guide. 283 lines of code added and 48 removed, not counting comments, blank lines and tests. The first commit was 126 and 30; the review findings added the rest. Tests are 385 added and 16 removed. It stays one PR because the code and its failure handling only make sense together.
Factory-Task: cutover/agent-pool-primary-token
Built-by: claude-code/claude-opus-5-5
Claude-Account: russelledeming@gmail.com
Undo: put the old pool strings back if the env edits were made, and revert this PR's merge commit.
🤖 Generated with Claude Code