Skip to content

feat(local-agent): sync and report ClawPro model configs - #393

Merged
jeff-r2026 merged 4 commits into
mainfrom
feature/apply-model-config
Sep 8, 2026
Merged

feat(local-agent): sync and report ClawPro model configs#393
jeff-r2026 merged 4 commits into
mainfrom
feature/apply-model-config

Conversation

@jeff-r2026

@jeff-r2026 jeff-r2026 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Consume apply_model_config tasks from /api/local-agent/sync, persist the candidate set only into the reporting CodeBuddy or Claude agent, and ack with type: "apply_model_config". Unsupported agents fail the task; user-owned models and symlinked config files are preserved; credential files are 0600; sync responses are redacted.
  • Immediately report a successfully applied model in the same sync run on POST /api/local-agent/report as user_level.models. Only models recorded in TeamAI's per-agent manifest are reported; user-owned models and api_key are never sent. CodeBuddy-added capability metadata no longer suppresses a delivered model.
  • Keep provider ownership isolated between CodeBuddy and Claude full snapshots, and preserve the whole Claude gateway when any managed field was user-edited.
  • Closes feat(local-agent): support model config sync from ClawPro (apply_model_config) #294. TAPD: --story=1020422209136345350.

Test plan

  • npx tsc --noEmit
  • npx vitest run (183 files, 2580 tests; 21 focused model-config cases)
  • npm run build
  • Built-CLI E2E with a stub backend: report → sync(apply_model_config) → ack(success) → immediate report; Kimi is reported as enterprise, remains reportable after CodeBuddy adds metadata, and ProxyToken is absent from report/ack traffic
  • Built-CLI E2E against the ClawPro test endpoint: user_level.models=[{provider:"tencentcodingplan",model_id:"kimi-k2.5",source:"enterprise"}] accepted with HTTP 200 for local_agent_id=0123456789abcdef
  • Symlink regression tests for CodeBuddy models and Claude settings; unsupported-agent failure; atomic Claude gateway removal; cross-agent full-snapshot ownership isolation
  • Code and security re-review: no remaining findings

jeff-r2026 and others added 2 commits September 2, 2026 16:53
Handle apply_model_config delivery into CodeBuddy/Claude, then report
the local model inventory on POST /api/local-agent/report.

--story=1020422209136345350
Fixes #294

Co-authored-by: Cursor <cursoragent@cursor.com>
Treat omitted, empty, or 0 (Go zero value) as the design-doc default so
models like kimi-k2.5 are not ack-failed for a missing output cap.

--story=1020422209136345350

Co-authored-by: Cursor <cursoragent@cursor.com>

@jeff-r2026 jeff-r2026 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found three P1 issues that reproduce locally against this head with an isolated HOME. Inline comments include the reproduction and impact.

Comment thread src/local-agent.ts Outdated
...(fullSnapshot ? {} : manifest.providers),
...Object.fromEntries(models.map((model) => [model.model_id, model.provider])),
};
await reconcileCodebuddyModels(models, fullSnapshot, manifest);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Apply model configs to the reporting agent before acknowledging success

applyModelConfig never receives context.tool and unconditionally reconciles only CodeBuddy and Claude. I reproduced a sync with tool: 'workbuddy' and a seeded ~/.workbuddy/models.json: that file remained byte-for-byte unchanged, while ~/.codebuddy/models.json and ~/.claude/settings.json were created and the command was acked as success. The backend therefore will not retry even though the reporting WorkBuddy instance cannot discover the candidate; scanModelsFromDisk also returns no inventory for WorkBuddy. Please route persistence/reporting to the actual agent (WorkBuddy uses its own ~/.workbuddy/models.json format), or fail unsupported agents instead of acknowledging an unapplied task.

Comment thread src/local-agent.ts Outdated
}
Object.assign(env, desired);
settings.env = env;
await writeJsonAtomic(settingsPath, settings);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve symlinked user settings files

writeJsonAtomic replaces the destination via rename, and its own contract explicitly says it is not for dotfile paths that may be symlinks. With ~/.claude/settings.json symlinked to a dotfiles-managed file, I reproduced this call replacing the symlink with a regular file while leaving the real target unchanged; the task still receives a success ack. This silently disconnects the user's source-of-truth configuration. The CodeBuddy write at line 2144 has the same issue. Resolve/follow the symlink (or use a symlink-preserving writer) before modifying these user dotfiles.

Comment thread src/local-agent.ts Outdated
const env = { ...(rawEnv as Record<string, unknown>) };

if (models.length === 0) {
for (const [key, hash] of Object.entries(previousHashes)) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Treat Claude gateway ownership atomically when removing a snapshot

The non-empty path treats any conflicting field as ownership of the whole gateway, but this empty-snapshot path deletes each still-matching field independently. I reproduced: apply a managed model, let the user change only ANTHROPIC_BASE_URL, then deliver {"models":[]}. The code preserves that URL but deletes ANTHROPIC_AUTH_TOKEN, ANTHROPIC_CUSTOM_MODEL_OPTION, and its name, leaving a broken half-gateway and acknowledging success. If any field in the previously managed gateway diverges, preserve the whole gateway (or track a bundle-level ownership hash) rather than stripping the remaining fields.

jeff-r2026 and others added 2 commits September 4, 2026 17:46
The backend cannot resolve user-owned models, so user_level.models now
includes only entries that still match an apply_model_config delivery.

--story=1020422209136345350

Co-authored-by: Cursor <cursoragent@cursor.com>
Report a successfully applied model in the same sync run and use scoped
manifest ownership so agent-normalized entries remain reportable.

Also route model writes to the reporting agent, preserve symlinked config,
and remove Claude gateway settings atomically.

--story=1020422209136345350

Co-authored-by: Cursor <cursoragent@cursor.com>
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

修复已推送到 4f27a87:模型应用成功后同一轮立即补 report;CodeBuddy 的正常元数据补充不再导致整对象 hash 失配漏报;provider ownership 按 agent 隔离。并已处理 review 中的 unsupported agent 误 ack、symlink 配置替换、Claude 空快照半删除问题。全量 2580 tests、tsc、build 通过;构建产物打真实 ClawPro report 返回 HTTP 200。

@jeff-r2026
jeff-r2026 merged commit 87205d1 into main Sep 8, 2026
7 checks passed
@jeff-r2026
jeff-r2026 deleted the feature/apply-model-config branch September 8, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(local-agent): support model config sync from ClawPro (apply_model_config)

1 participant