Two leak paths caused Discord typing indicators to persist indefinitely:
1. If runAgent() threw before turnController.finish(), setTyping(false)
was never called. Wrap the agent run in try/finally to guarantee cleanup.
2. Follow-up message piping called setTyping(true) fire-and-forget. If
the Promise resolved after finish()'s setTyping(false), the typing
interval was recreated with nobody to clear it. Await the call to
serialize the on/off sequence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove OPENAI_API_KEY from .env and Codex child process env to prevent
API billing when subscription quota is exhausted
- Remove writeCodexApiKeyAuth entirely — Codex now uses OAuth only
- Add 9-pattern secret redaction in formatOutbound() to prevent key leaks
- Fix Codex usage bucket aggregation: use 'codex' bucket only instead of
max across all buckets (bengalfox = Codex Spark, not needed)
- Add d7≥100% auto-rotation in updateCodexAccountUsage to skip exhausted
accounts and prevent API billing fallback
- Add findNextCodexAvailable that checks both rate-limits and d7 usage
- Clean stale apikey auth.json files from all session directories
- Update tests: OAuth-only auth, d7 auto-skip (3 new tests), dashboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, intermediate text was sent as separate messages when
subagents were active, causing message floods. Now always edit
the progress message regardless of subagent state.
The narrowed list (.txt, .md, .csv, .json, .log) was for outbound,
but was mistakenly applied to inbound. Restore full list for user
file uploads (input direction).
Restrict Discord attachment inline parsing to .txt, .md, .csv, .json, .log
and text/* content type. Code files (.ts, .tsx, .js, .py, etc.) now show
filename only instead of being inlined into the message.
Restores token-refresh.ts that was deleted in 35356dd. Now works
with multi-account token rotation:
- Refreshes tokens from ~/.claude/.credentials.json (account 0)
and ~/.claude-accounts/{n}/.credentials.json (account 1+)
- Updates token-rotation memory and .env on refresh
- Syncs credentials to session directories
- 5-min check interval, refreshes 30 min before expiry
- forceRefreshToken() export for 401 recovery
Merge remote main (token rotation, usage API, dashboard fixes)
with local memory pipeline changes. Smart quote encoding fixed.
Test alignment pending.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add automatic memory integration so EJClaw host directly calls Memento
MCP for recall at session start and reflect on compact, removing
reliance on agent voluntary tool use.
Stage 1: New sessions get room memory briefing injected into system
prompt (CLAUDE.md / AGENTS.md) via host-side MCP client.
Stage 2: PreCompact hook automatically calls reflect + remember to
persist session summaries as room-memory fragments.
Also restores missing source files (token-rotation, codex-token-rotation,
claude-usage exports) to fix full build from source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix token rotation not taking effect: getCurrentToken() was shadowed
by static .env CLAUDE_CODE_OAUTH_TOKEN value in agent environment
- Don't fall back to Kimi on usage-exhausted (only on transient 429/network)
- Add disk cache for Claude usage API data (survives restarts, 429s)
- Rate-limit usage API calls to 1 per token per 5 minutes
- Add ignoreRateLimits option to rotateToken() for exhausted recovery
- Rotate to next token in getActiveProvider() when current is exhausted
- Add isUsageExhausted() helper to provider-fallback
- Call /api/oauth/profile for each token, cache in memory
- Display plan type (max/pro/free) next to Claude account names
- Profiles fetched once on startup, no periodic refresh needed
- Iterate all rate limits per account instead of just display[0]
- Track and persist 7-day reset time separately (resetD7At)
- Show both 5h and 7d cached usage for inactive Codex accounts
- Scan all Codex accounts by spawning app-server with each auth dir
- Cache primary (5h) and secondary (7d) usage per account
- Show cached usage + reset remaining time on dashboard
- Persist d7 usage in rotation state file
- formatResetRemaining: "Xh Ym 후" / "N일 후" format
- Remove isRateLimited guard for cached display
- Parse "try again at" time from rate-limit errors instead of
fixed 1-hour cooldown, with 3-min buffer after reset
- Pass error message to rotateToken/rotateCodexToken for parsing
- Cache usage % and reset time per Codex account for dashboard
- Persist cached usage in rotation state file across restarts
- Show rate-limited Codex accounts with cached 100% + reset time
- Fix dashboard text indicators (*!/space) for monospace alignment
- Save currentIndex + rateLimits to JSON file in DATA_DIR
- Restore on startup so rotation survives service restarts
- Replace emoji indicators with text (*/!) for monospace alignment
- Dynamic column width based on longest name
- Show Codex accounts with plan type on dashboard
- New codex-token-rotation module: rotates between multiple
~/.codex-accounts/{n}/auth.json on rate-limit
- Copies active account auth to session dir before each spawn
- Fix detectFallbackTrigger to match "usage limit" / "hit your limit"
- Fix streamed error detection: remove claude-only guard so codex
rate-limit errors are caught even when output.status is "success"
- Add rotation in both task-scheduler and message-agent-executor
- Replace expect/CLI hack with direct HTTP API call
(GET api.anthropic.com/api/oauth/usage with OAuth token)
- Add fetchAllClaudeUsage() for per-token usage fetching
- Export getAllTokens() from token-rotation for usage queries
- Dashboard shows each account separately (Claude1⚡, Claude2)
with ⚡ for active token, 🚫 for rate-limited
- New token-rotation module: stores multiple tokens from
CLAUDE_CODE_OAUTH_TOKENS env var (comma-separated)
- On rate-limit, rotates to next healthy token before falling
back to Kimi provider
- Also fixes: intermediate text routes to progress message
when no subagents active (prevents message flooding)