Commit Graph

670 Commits

Author SHA1 Message Date
Eyejoker
ab408ab998 style: prettier formatting 2026-03-25 07:09:01 +09:00
Eyejoker
b6d3f879ef refactor: add recovery mode + shared JSON/error/fetch utilities
- GroupQueue recovery mode: limit to 3 concurrent agents for 60s after restart
  to prevent API rate-limit storms (configurable via env vars)
- getErrorMessage: replace 14 occurrences of instanceof Error pattern
- readJsonFile/writeJsonFile: replace 19 occurrences of JSON+fs pattern
- fetchWithTimeout: replace 3 occurrences of AbortController pattern

354/354 tests passing
2026-03-25 07:08:49 +09:00
Eyejoker
a576d623a1 fix: limit inline file parsing to data files only
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.
2026-03-25 05:02:22 +09:00
Eyejoker
5ea3439c5f refactor: extract shared utilities, protocol constants, and retry loop
Phase 3: provider-retry.ts — shared Claude rotation loop (SSOT)
  - retryClaudeWithRotation extracted from message-agent-executor + task-scheduler
  - ~200 lines of duplicated retry logic removed

Phase 4: types.ts — AgentOutputPhase + VisiblePhase unified
Phase 5: types.ts — AgentConfig extended with claudeThinking/claudeThinkingBudget

Utilities (utils.ts):
  - getErrorMessage: 14 occurrences of instanceof Error pattern → 1 function
  - readJsonFile/writeJsonFile: 19 occurrences of JSON+fs pattern → 2 functions
  - fetchWithTimeout: 3 occurrences of AbortController pattern → 1 function
  - formatElapsedKorean: deduplicated from task-watch-status + message-turn-controller

Protocol (agent-protocol.ts):
  - OUTPUT_START/END_MARKER centralized (runners keep local copies with SSOT reference)
  - IMAGE_TAG_RE, IPC constants documented

Runner: show "대화 요약 중..." progress message during auto-compact

Net: -137 lines, 354/354 tests passing
2026-03-25 04:59:49 +09:00
Eyejoker
b9f98fcc19 feat: log token count on auto-compact trigger
Log pre_tokens and trigger type from compact_boundary message
in both main query loop and /compact slash command path.
2026-03-25 04:01:48 +09:00
Eyejoker
0a64e8c008 chore: add pnpm-lock.yaml 2026-03-25 03:57:52 +09:00
Eyejoker
80490ab162 refactor: extract shared error detection and token rotation utilities (SSOT)
- Create agent-error-detection.ts: single source for error classification
  (isClaudeUsageExhaustedMessage, isClaudeAuthExpiredMessage, classifyAgentError, etc.)
- Create token-rotation-base.ts: shared rotation algorithm
  (parseRetryAfterFromError, computeCooldownUntil, findNextAvailable)
- Remove 6 duplicated functions from message-agent-executor and task-scheduler
- Delegate detectFallbackTrigger/detectCodexRotationTrigger to shared classifiers
- Split canFallback into isClaudeAgent/canRotateToken/canFallback (3-way separation)
- Cache env.ts with getEnv() to eliminate repeated disk I/O
- Migrate config, discord, provider-fallback, token-rotation, memento-client to getEnv()

Net: -146 lines, 0 new dependencies, 354/354 tests passing
2026-03-25 03:56:22 +09:00
Eyejoker
906a3dfadb config: add auto-compact settings for Claude and Codex agents
Claude: CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=50 (~500K on 1M context)
Codex: model_auto_compact_token_limit=258000 (matching CLI default)
2026-03-25 03:56:03 +09:00
Eyejoker
744e2ce6e2 style: format agent rotation updates 2026-03-24 17:09:17 +09:00
Eyejoker
8b2c46b89e fix: harden agent shutdown and auth rotation 2026-03-24 17:08:48 +09:00
Eyejoker
77b4ab1c83 fix: use token suffix for usage cache key (prefix is identical)
All OAuth tokens start with "sk-ant-oat01-" so slice(0,12) produced
the same cache key for every token. Use slice(-8) instead.
2026-03-24 15:45:58 +09:00
Eyejoker
6fc1c77418 fix: suppress 401 auth errors from chat output
401 authentication errors are now filtered from Discord messages
and only logged. Prevents "Failed to authenticate" from reaching users.
2026-03-24 15:42:29 +09:00
Eyejoker
55d865586d style: prettier formatting 2026-03-24 15:35:46 +09:00
Eyejoker
f4b02af4dd feat: restore OAuth token auto-refresh with multi-account support
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
2026-03-24 15:35:37 +09:00
Eyejoker
991f2bf2fd test: align fallback expectations after merge 2026-03-24 03:56:07 +09:00
Eyejoker
0724914a43 merge: integrate remote token rotation + memory pipeline
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>
2026-03-24 03:53:15 +09:00
Eyejoker
5cd424017f style: prettier formatting from pre-commit hook
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 03:49:00 +09:00
Eyejoker
3467e245d5 feat: auto memory pipeline — host-driven Memento recall/reflect
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>
2026-03-24 03:48:36 +09:00
Eyejoker
f6ba879e8a fix: usage API rate-limit guard and cleanup debug logging 2026-03-24 03:47:46 +09:00
Eyejoker
6a73421d4f fix: use ignoreRateLimits in retryClaudeWithRotation loops
Without this, token rotation was blocked by stale cooldowns even
when the target token had available usage.
2026-03-24 03:47:22 +09:00
Eyejoker
120f16e9e1 fix: Claude token rotation, usage-exhausted fallback, and usage API caching
- 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
2026-03-24 03:40:51 +09:00
Eyejoker
2ab6c25f0e feat: fetch Claude account profiles (plan type) on startup
- 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
2026-03-24 01:41:23 +09:00
Eyejoker
16f4e6ccb9 fix: force dashboard refresh after Codex account scan completes 2026-03-24 01:39:15 +09:00
Eyejoker
ed9123d66c fix: always capture reset times even at 0% usage (>= instead of >) 2026-03-24 01:38:28 +09:00
Eyejoker
c51d80bd07 style: prettier formatting 2026-03-24 01:37:14 +09:00
Eyejoker
92c82c192a fix: fixed-width reset format Xd Yh / Xh Ym for alignment 2026-03-24 01:35:00 +09:00
Eyejoker
9f8b4ae0aa fix: space between h/m in reset format, padStart(7) 2026-03-24 01:33:48 +09:00
Eyejoker
2ffb4d5696 fix: fixed-width English reset format for monospace alignment 2026-03-24 01:32:18 +09:00
Eyejoker
6ac706f9c5 fix: unify reset time format to relative (Xh Ym 후) for all rows 2026-03-24 01:31:10 +09:00
Eyejoker
0375912c2b fix: render reset times in dashboard usage rows 2026-03-24 01:29:32 +09:00
Eyejoker
9e091df6f6 fix: scan all Codex limits for max 5h/7d, persist d7 reset times
- 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
2026-03-24 01:28:48 +09:00
Eyejoker
c7a3a56386 feat: scan all Codex accounts on startup + hourly, cache 5h/7d usage
- 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
2026-03-24 01:25:02 +09:00
Eyejoker
60dab43a3f feat: parse retry-after from errors, cache codex usage per account
- 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
2026-03-24 01:16:54 +09:00
Eyejoker
2e6fc4a08e fix: add warn log for usage content build failures 2026-03-23 23:18:47 +09:00
Eyejoker
7401626356 feat: persist rotation state across restarts, fix dashboard 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
2026-03-23 23:16:54 +09:00
Eyejoker
30be180f4b fix: codex rate-limit retry immediately instead of next request 2026-03-23 23:07:35 +09:00
Eyejoker
816b0a39cc feat: Codex account rotation + fix rate-limit detection
- 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
2026-03-23 23:06:17 +09:00
Eyejoker
0b86f937f4 fix: read tokens from .env file, clean up debug logs
- token-rotation reads from readEnvFile() (systemd doesn't load .env)
- fetchAllClaudeUsage polling log demoted to debug level
- Remove verbose dashboard send/create logs
2026-03-23 22:41:07 +09:00
Eyejoker
83b25fb7bd feat: Claude usage API + multi-account dashboard display
- 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
2026-03-23 22:15:06 +09:00
Eyejoker
d378598f2e feat: Claude OAuth token rotation on rate-limit
- 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)
2026-03-23 22:01:06 +09:00
Eyejoker
ee65330e66 fix: rollback codex SDK to 0.115.0 (0.116.0 causes stuck turns) 2026-03-23 20:38:30 +09:00
Eyejoker
45a332f6bb docs: update Node requirement to 24+ 2026-03-23 20:30:29 +09:00
Eyejoker
961b8ccf65 docs: add SDK version badges to README 2026-03-23 20:17:52 +09:00
Eyejoker
2161b555ec fix: dedup intermediate/final, filter long descriptions, update SDKs
- Track lastIntermediateText to prevent duplicate Discord delivery
  when same text arrives as both intermediate and final
- Filter task_progress descriptions >80 chars (AI summaries)
- Mark completed subagents with  instead of removing
- Update claude-agent-sdk 0.2.76→0.2.81, codex 0.115→0.116
2026-03-23 20:16:54 +09:00
Eyejoker
21a6be84b4 feat: multi-agent progress tracking with per-subagent display
Track each subagent separately via agentId from SDK task events.
Single subagent shows detailed view with activity sub-lines,
multiple subagents show compact one-line-each format.
2026-03-23 19:46:08 +09:00
Eyejoker
8c98ad9faf fix: intermediate text as standalone messages, preserve progress
Add 'intermediate' phase for assistant text between tool calls.
These are sent as separate Discord messages without finalizing
or disrupting the active progress message.
2026-03-23 19:29:16 +09:00
Eyejoker
720086808a fix: intermediate text as separate messages, not progress heading
Intermediate assistant text (e.g., "서브에이전트 4개 병렬로 띄웠어요")
is now emitted as regular messages (no phase), not progress heading
updates. Progress heading is only set by task_started (🔄) events.
2026-03-23 19:26:20 +09:00
Eyejoker
be9dfa64b0 fix: deduplicate progress at runner level, not host revert
Buffer intermediate assistant text in the runner. When result arrives
with matching text, discard the buffered progress instead of emitting
it. This prevents the duplicate from ever reaching Discord, eliminating
the flash-then-revert behavior.
2026-03-23 19:22:14 +09:00
Eyejoker
83df0e7dc1 fix: use task_started description as immediate heading, dedupe final
- Show subagent description from task_started immediately (no 30s wait)
- Remove AI summary (agentProgressSummaries) — task_started is sufficient
- Revert progress heading to previous when final text matches current
- Track previousProgressText in direct-update path
2026-03-23 19:20:23 +09:00
Eyejoker
a8a1c9b45a fix: throttle progress edits via 5s ticker, prevent Discord rate limit
- Remove per-tool-activity sync calls (was flooding Discord with edits)
- Ticker-only sync at 5s intervals for time + tool activity updates
- Summary updates heading directly when progress message exists
- Prevent duplicate progress message creation with progressCreating flag
2026-03-23 19:13:51 +09:00