docker: stop the wrapper typing into live sessions, and support per-agent logins
Both agents stopped, and the decoder diagnosed it itself:
"I received '2' and '1' but I don't have a pending question those would
answer -- I was in the middle of setting up the /loop cron job."
claude-autonomous matched the BARE SUBSTRINGS 'Choose', 'trust' and 'accept' to
answer Claude Code's one-time first-run gates. The /loop prompt is echoed into
the terminal, and that day's briefs contain 'accepted as-is' and 'least
trustworthy' -- so expect matched the agent's OWN INSTRUCTIONS and typed 2\r and
1\r into a running session, which then sat waiting for a human to explain them.
The old comment argued a multi-word pattern 'never matches' because the gate
text wraps. True of a literal string, false of a whitespace-tolerant regex, which
is what these now are: \s+ spans the wrap, and the terminal is 200 columns wide.
Measured, old against new, against the real brief text and a real gate:
{accept} brief 0 gate 1 (case-sensitive; briefs say 'accepted')
{Yes,\s*I\s+accept} brief 0 gate 1
{trust} brief 1 <- the trigger
{Do\s+you\s+trust\s+the\s+files} brief 0
Two defences, because one is not enough for something that can type: patterns
prose cannot match, and gates skipped ENTIRELY on resume (SYLPH_SKIP_GATES) --
a resumed session cannot show a first-run gate, so there is nothing to answer
and everything to lose. Timeout cut 90s -> 25s for the same reason.
Also: SYLPH_OWN_LOGIN. Remote Control stopped registering under the long-lived
token, and the likely reason is scope -- `claude auth login` requests
user:sessions:claude_code and the token's auth status reports no email, org or
subscription. A per-agent `claude auth login` restores Remote Control AND avoids
the rotation collision, because each agent holds its own grant rather than a copy
of one. The flag stops the entrypoint seeding the host's credentials over it.
This commit is contained in:
@@ -163,7 +163,13 @@ mkdir -p /exchange/files 2>/dev/null || true
|
||||
# clients on one rotating refresh token, the losers of a rotation race getting
|
||||
# their stored tokens CLEARED to empty strings and parking at "Login expired".
|
||||
# Measured 2026-09-04 -- see the launcher.
|
||||
if [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; then
|
||||
# 🔴 PER-AGENT LOGIN: never seed. Set SYLPH_OWN_LOGIN=1 once this container
|
||||
# has run `claude auth login` itself. Its grant is its OWN -- copying the
|
||||
# host's over it re-creates the rotation collision that empties credentials
|
||||
# and parks the session, which is the whole reason per-agent logins exist.
|
||||
if [ -n "${SYLPH_OWN_LOGIN:-}" ] && [ "${SYLPH_OWN_LOGIN}" != "0" ]; then
|
||||
log "auth: this agent has its own login; not seeding from the host"
|
||||
elif [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; then
|
||||
log "auth: using the long-lived token from the environment; not seeding OAuth"
|
||||
elif [ -d "$HOME/.claude.seed" ] && \
|
||||
{ [ ! -s "$HOME/.claude/.credentials.json" ] || \
|
||||
@@ -278,6 +284,10 @@ but the process that was running when it died is gone. Before anything else:
|
||||
2026-09-01."
|
||||
log "resuming session ${SYLPH_SESSION%%-*}… with a restart notice"
|
||||
fi
|
||||
# Tell the gate-answering wrapper to stand down: a resumed session cannot
|
||||
# show a first-run gate, and on 2026-09-04 its single-word patterns matched
|
||||
# the /loop prompt itself and typed "2" and "1" into a live session.
|
||||
[ "$SYLPH_RESUME" = "1" ] && export SYLPH_SKIP_GATES=1
|
||||
[ "$SYLPH_RESUME" = "1" ] && set -- --resume "$SYLPH_SESSION" "$@"
|
||||
|
||||
# The flag the user asked for. It is refused under root, which is why this
|
||||
|
||||
Reference in New Issue
Block a user