Repository navigation
[Feature] synchronize Claude Code agent definitions during proxy startup #2200
Description
Area
Service lifecycle
What are you trying to accomplish?
Keep the generated Claude Code agent roster synchronized with the live OpenCodex configuration whenever the proxy daemon starts, including background starts and service restarts.
What prevents this today?
The files ~/.claude/agents/ocx-*.md are derived artifacts from config.json / subagentModels. In the current dev path, injectClaudeAgentDefs() runs at the explicit ocx claude launch and syncClaudeAgentDefsBestEffort() runs after selected Management API mutations. A manual edit to config.json, a Task Scheduler/background service start, or a plain proxy restart does not necessarily execute either hook.
That leaves stale or orphaned definitions loaded by Claude Code even though the live config is already correct. We observed a dead ocx-gemini-3-6-flash.md surviving while config pointed to the replacement model until a supported API mutation forced a sync.
What should OpenCodex do?
During the idempotent proxy startup/ensure routine, reconcile the owned ocx-*.md files from the current config and context-window map. The startup hook should:
- write definitions for the current enabled subagentModels roster;
- remove stale files previously owned by OpenCodex;
- respect claudeCode.enabled and claudeCode.injectAgents disablement;
- remain best-effort so provider discovery failure does not prevent the proxy from starting;
- be safe to run repeatedly on every daemon/service start.
Example usage or interface
- Edit config.json so subagentModels replaces gpt-5.6-sol with gemini-3.7-flash.
- Start or restart the proxy through Task Scheduler / ocx restart.
- Before invoking Claude Code, ~/.claude/agents/ocx-gemini-3-7-flash.md should exist and the old ocx-gpt-5-6-sol.md should be gone.
- Running the same start again should produce the same files without duplicates or churn.
Alternatives or workarounds
The current workaround is to repeat a supported Management API mutation (for example PUT /api/subagent-models with the already-saved list) or invoke ocx claude before using Claude Code. Both are surprising for a daemon/service workflow and do not repair the roster at the moment a background service becomes healthy.
Additional context
The launch-time injection implementation is in src/claude/agents-inject.ts; current call sites are src/cli/claude.ts, src/server/system-env.ts, and selected management routes. The gap is the absence of an idempotent startup hook in the proxy start/ensure path. The issue was re-verified against the v2.28.0 migration stack (commit c0dcef24) after typecheck and migration-focused tests passed.
Related lifecycle/config synchronization code should be reused rather than adding a second roster format.
Activity
lidge-jun commentedon Aug 20, 2026
리뷰 · 우선순위 54 / 80
데몬 시작이 로스터를 안 맞추는 거 맞음.
injectClaudeAgentDefs는src/cli/claude.ts:330이랑src/server/system-env.ts:415랑 매니지먼트 mutation 뒤에만 돈다.startServer(src/server/index.ts:495)는 컨피그 마이그레이션이랑 캐시 무효화는 해도 에이전트 md 싱크는 안 함. 백그라운드ocx start/스케줄러가 그 구멍임.구현은 이미 있음.
src/claude/agents-inject.ts:249가claudeCode.enabled/injectAgentsfalse면 소유 파일을 지우고, 아니면syncClaudeAgentDefs로 현재 로스터를 씀. 포맷을 새로 만들 거 아님.management-api.ts:195의syncClaudeAgentDefsBestEffort를 부팅에도 한 번 호출하면 됨.best-effort가 맞음. 프로바이더 디스커버리 실패로 프록시가 못 뜨면 안 됨. 매니지먼트 훅은 이미 try/catch로 삼킴. 스타트업도 같게. 반복 실행 안전하다고 함수가 이미 idempotent임.
수동
config.json편집은 저장 훅이 없어서 재시작 전까지 낡은 md가 남는 게 정상임. 그래서 스타트업 훅이 필요함. GUI에서 subagent 바꾸면 지금 mutation 경로로 싱크됨. 데몬만 빠짐.types.ts 스플릿이랑 상관 없음. 2.28 블로커도 아님. 닫을 중복 아님.
해결방안:
startServer끝쪽(마이그레이션/baseline 다음, listen 전후 상관없음. 실패해도 서버는 떠야 함)에 기존injectClaudeAgentDefs/syncClaudeAgentDefsBestEffort를 한 번 넣음. 새 로스터 포맷 금지.claudeCode.enabled/injectAgents가드는 그대로.이 댓글은 grok-bot이 작성했습니다
Ingwannu commentedon Aug 20, 2026
Confirmed against current
dev(f2ebd3067). The existing call insystem-env.tsdoes not close this gap cross-platform: it sits insideinjectSystemEnv, which returns before agent synchronization on non-macOS systems and whenclaudeCode.systemEnvis disabled. The other call sites cover explicitocx claudelaunches and selected Management API writes, not an ordinary Linux/Windows service restart.I am taking this as a focused lifecycle fix. I will reuse the existing owned-file reconciliation and context-window builder, call it from the proxy startup/ensure path, keep it best-effort and idempotent, preserve
claudeCode.enabled/injectAgentspruning semantics, and avoid adding a second roster format. Tests will cover stale-file removal on startup, disabled pruning, repeated convergence, catalog failure fallback, and the non-macOS path.Ingwannu commentedon Aug 20, 2026
Implemented in #2202. The startup hook reuses the existing owned Claude-agent reconciliation outside
startServer, remains best-effort/idempotent, preserves disabled pruning, and covers Linux/Windows/background startup paths.The branch is now rebased onto current
devat5df962b0d952e1143b34249eeb1e1d405f181b98. Focused tests passed 62/62 and typecheck/diff-check passed locally. I have requested @lidge-jun exact-head review and will leave this issue open until the PR is approved and merged.Ingwannu commentedon Aug 21, 2026
Completed and merged into dev via #2202 (merge commit 1ca08e6). The startup path now reuses the existing best-effort, idempotent Claude agent reconciliation, including disabled-state pruning and non-macOS/background service startup coverage. Closing this as completed.