Skip to content

feat: report per-program configuration status to the controller - #136

Closed
LutzLange wants to merge 4 commits into
agentdesktop-dev:mainfrom
LutzLange:upstream/s4-program-status
Closed

LutzLange wants to merge 4 commits into
agentdesktop-dev:mainfrom
LutzLange:upstream/s4-program-status

Conversation

@LutzLange

@LutzLange LutzLange commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Part of #86 (series overview: #86 overview). Stacked on #135; review the last commit.

Why

The controller sees only "applied" or "failed" per device. It can't tell which program was applied, which is in conflict, or which was skipped because the local proxy is down.

Change

  • Outcomes. Each apply reports every managed program's outcome with a detail: applied, unchanged, removed, conflict, failed, and two new ones.
    • Inactive: the program uses the gateway, but the proxy isn't running.
    • Blocked: the program wasn't applied because another program conflicted.
  • Agent. The daemon logs one line per program that wasn't applied and sends the list with its ConfigStatus.
  • Controller. It stores the latest list per device and returns it in GET /api/v1/devices/{id}. The device page shows a "Managed programs" card.

Risk and compatibility

  • The proto fields programs and programs_reported are additive.
  • Migration 0003 (device_program_status) is one-way, so upgrade the controller first.

How to review

  • reconcile/mod.rs and reconcile/plan.rs hold the outcome model and its precedence.
  • controller/src/database.rs and the migration hold storage.
  • DeviceView.tsx holds the card.
  • About 60% of the added lines are tests and stories.

Tests

CI green. Unit tests cover precedence, the report and log, and controller storage (a rejected report keeps the previous status). There are Storybook stories for the card. Lab-tested on a Linux VM: the controller's device page and API show each program's outcome.

Follow-ups

Screenshot

Controller device page, "Managed programs" card. Storybook: Controller/Device details, ConfigurationFailed.

Controller device page, "Managed programs" card. Storybook: Controller/Device details, ConfigurationFailed.

🤖 Generated with Claude Code

LutzLange and others added 2 commits October 7, 2026 01:12
Part of agentdesktop-dev#86. Two programs that point Copilot at the local LLM proxy, user
mode only:

- `programs.copilot` writes the Copilot CLI's BYOK provider registry
  (`~/.copilot/providers.json`, or `$COPILOT_HOME` / `COPILOT_PROVIDERS_CONFIG`):
  providers `agentdesktop` (OpenAI-shaped) and `agentdesktop-anthropic` on the
  proxy's `/copilot-cli` route, with the pairing header; the user's own
  providers are kept. Discovery reports the CLI and its version from the npm
  manifest.
- `programs.vscode` with `copilotChat: ownModels` (default) writes
  `chatLanguageModels.json` for Copilot Chat's built-in Custom Endpoint
  provider on `/vscode-copilot`; the user's entries are kept.
- `copilotChat: githubModels` keeps GitHub's own models and the user's
  Copilot seat: the daemon sets `github.copilot.advanced.debug.overrideCapiUrl`
  in the user `settings.json` to the proxy's `/vscode-copilot-capi/<pairing>`
  route (pairing in the path, since VS Code cannot add a header), forwards to
  `llmGateway.proxyUrl` with the client's Copilot token moved aside, tunnels
  the Responses WebSocket for agent mode, forwards `_ping` without a
  credential, and keeps the override out of Settings Sync. `settings.json` is
  edited in place (JSONC): only the managed keys change, comments, key order
  and formatting stay, and removal restores the file byte for byte.

Managed files are written 0600 and carry a sidecar that records what the
daemon added, so removal takes out exactly that. A file the daemon cannot
parse is a conflict and left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…recision

- Copilot CLI: removing our providers by their pairing header (no sidecar)
  no longer adds "models": null to a providers.json that has no models key.
- README: the CAPI WebSocket tunnel closes after 4 to 5 minutes with the
  controller's default gatewayJwt.lifetime, also mid-turn; VS Code's
  reconnect behaviour is not verified.
- README: the settings.json removal exceptions are listed in full (multi-line
  empty object, comment-only file).
- README: the providers.json sidecar holds the user's own apiKey values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LutzLange
LutzLange force-pushed the upstream/s4-program-status branch from 1a1bccb to 039e278 Compare October 6, 2026 23:19
LutzLange and others added 2 commits October 7, 2026 02:34
…idecar mode repaired

Review on agentdesktop-dev#135.

- `.settings.json.agentdesktop` that is not JSON or not a known settings
  state now fails the apply, as the providers.json and chatLanguageModels.json
  sidecars already do: like a conflict, every managed file on the device
  stays untouched, also while `programs.vscode` is not configured (removal
  reads the sidecar too), and the daemon does not start. Before, the sidecar
  was ignored and rebuilt from the sidecar-less rules, so a user's own
  `overrideCapiUrl` was lost on removal. The error names the sidecar path and,
  for a syntax error, the line and column only, never a value. A sidecar
  without `version` (the earlier whole-document form) is still upgraded; a
  leading byte-order mark is accepted.
- An identical sidecar whose mode is looser than 0600 is written again at
  0600, as settings.json is; settings.json and its sidecar use the same
  check (a 0700 settings.json is now also tightened). The rewrite of the
  sidecar alone is not shown in the plan summary, since settings.json is
  unchanged.
- README: the unreadable-sidecar behaviour and the sidecar's mode.

Behaviour change: the test `an_unreadable_sidecar_warns_and_apply_proceeds` is
replaced. Tests: 9 new, 2 of them unix-only. The file-mode tests in this
module (old and new) assume umask 022.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each apply reports every managed program's outcome with a detail: applied,
unchanged, removed, conflict, inactive (the program uses the gateway but the
local proxy is not running), blocked (not applied because another program
conflicted) or failed. The daemon logs one line per program that was not
applied and sends the list with its ConfigStatus (additive proto fields
`programs`, `programs_reported`).

The controller stores the latest list per device (migration 0003,
`device_program_status`), returns it in `GET /api/v1/devices/{id}`, and the
device page shows a "Managed programs" card. Upgrade the controller first
(migration 0003 is one-way).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LutzLange
LutzLange force-pushed the upstream/s4-program-status branch from 039e278 to e1780fd Compare October 7, 2026 01:19
@peterj

peterj commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

@LutzLange this needs rebasing

@LutzLange

Copy link
Copy Markdown
Contributor Author

Combined with the rest of the series into #146, one PR on main (rebased after #135 merged). The review threads here are addressed there in the same commits.

@LutzLange LutzLange closed this Oct 7, 2026
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.

2 participants