Skip to content

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

Open
jeff-r2026 wants to merge 3 commits into
mainfrom
feature/apply-model-config
Open

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

Conversation

@jeff-r2026

Copy link
Copy Markdown
Collaborator

Summary

  • Consume apply_model_config tasks from /api/local-agent/sync, persist the candidate set into CodeBuddy (~/.codebuddy/models.json) and Claude (~/.claude/teamai-models.json / settings.json when there is no user-owned gateway), and ack with type: "apply_model_config". User-owned models are preserved; credential files are 0600; sync responses are redacted so ProxyToken never lands in logs.
  • Report the current tool's model inventory on the existing POST /api/local-agent/report as user_level.models (provider + model_id required; omit the field when empty; never send api_key). source is enterprise when the on-disk entry still matches what TeamAI delivered, otherwise local.
  • 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 (2546 tests, including 13 new cases in local-agent-model-config.test.ts)
  • npm run build and run the built CLI hook-dispatch session-start --tool codebuddy against the ClawPro test endpoint; report HTTP 200 with user_level.models
  • Stub backend: deliver apply_model_config, then a second session reports the delivered model as source: "enterprise" and a user-seeded CodeBuddy model as source: "local"; ack success; ProxyToken absent from report/ack bodies
  • Same stub path for Claude: gateway written to ~/.claude/teamai-models.json, reported with the delivered provider, no token leak
  • Real CodeBuddy UI showed the delivered model in the picker (inference 401 is a backend ProxyToken placeholder, not a CLI persist bug)

Made with Cursor

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
...(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
}
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
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.

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>
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