From a3e434f1580974eafe83053c910d78622bacbeec Mon Sep 17 00:00:00 2001 From: john-michael murphy Date: Fri, 7 Aug 2026 13:18:52 -0700 Subject: [PATCH 1/2] manifest: recognize the available_models config key Adds available_models to the KEYS validator (comma-separated ids or JSON array of ids / {id,label} objects, mirroring the add-in's parser) and documents it in manifest.md and bootstrap.md. Replaces the retired disabled_models/additional_models pair from #294: available_models is a single override list, so admins state the full picker rather than diffs. No-Verification-Needed: exercised build-manifest.mjs directly against valid and invalid inputs --- .../commands/bootstrap.md | 11 ++++++++ .../commands/manifest.md | 21 +++++++++++++++ .../scripts/build-manifest.mjs | 27 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/claude-for-msft-365-install/commands/bootstrap.md b/claude-for-msft-365-install/commands/bootstrap.md index f382b6a2e..f9004f195 100644 --- a/claude-for-msft-365-install/commands/bootstrap.md +++ b/claude-for-msft-365-install/commands/bootstrap.md @@ -260,6 +260,17 @@ layer. "disabled_features": ["skills.authoring"] ``` +### `available_models` + +Model-picker override for this user — same semantics and entry forms as the +[manifest key](manifest.md#available_models). A JSON array of ids and/or +`{id, label}` objects; because it is an override, list every model the user +should keep. + +```json +"available_models": [{ "id": "claude-opus-4-8", "label": "Opus 4.8" }, "claude-sonnet-5"] +``` + ### `access_policies` Native JSON array of allow/deny statements — the per-user layer of the diff --git a/claude-for-msft-365-install/commands/manifest.md b/claude-for-msft-365-install/commands/manifest.md index b3589531a..f7c9cac89 100644 --- a/claude-for-msft-365-install/commands/manifest.md +++ b/claude-for-msft-365-install/commands/manifest.md @@ -275,6 +275,27 @@ Unknown slugs are ignored (forward-compatible). Setting it here applies one policy org-wide; per-user policy belongs in [bootstrap](bootstrap.md#disabled_features) (JSON array) or extension attrs (comma-separated). +## available_models + +`available_models` **overrides** the model picker — when set, users see exactly +the models listed, in the listed order, and nothing else. It replaces the +retired `disabled_models` / `additional_models` pair: because it is an +override, it must include every backend model users should keep, not just +additions or removals. + +Two formats: + +```bash +# comma-separated ids +available_models='claude-opus-4-8,claude-sonnet-5' + +# JSON, when picker labels matter — an array of ids and/or {id, label} objects +available_models='[{"id":"claude-opus-4-8","label":"Opus 4.8"},"claude-sonnet-5"]' +``` + +Unset (or empty) means the picker is unchanged. Setting it here applies +org-wide; per-user policy belongs in [bootstrap](bootstrap.md#available_models). + ## access_policies `access_policies` is the IAM-shaped successor to `disabled_features` — a JSON diff --git a/claude-for-msft-365-install/scripts/build-manifest.mjs b/claude-for-msft-365-install/scripts/build-manifest.mjs index de47a95ef..78a4559c2 100644 --- a/claude-for-msft-365-install/scripts/build-manifest.mjs +++ b/claude-for-msft-365-install/scripts/build-manifest.mjs @@ -85,6 +85,33 @@ const KEYS = { pattern: /^[\w.]+(,[\w.]+)*$/, hint: "comma-separated feature slugs to lock for users, e.g. skills.authoring", }, + available_models: { + // Comma-separated model ids, or JSON: an array of ids and/or {id, label} + // objects (a lone object is single-entry shorthand). Mirrors the add-in's + // parseModelIdEntries leniency but warns where the runtime would silently drop. + pattern: /\S/, + hint: "override the model picker: comma-separated model ids, or JSON like [{\"id\":\"claude-opus-4-8\",\"label\":\"Opus 4.8\"}] — an OVERRIDE, list every model users should see", + validate: (v) => { + const t = v.trim(); + if (!t.startsWith("[") && !t.startsWith("{")) { + return t.split(",").some((id) => !id.trim()) ? ["blank entry in comma-separated id list"] : []; + } + let parsed; + try { + parsed = JSON.parse(t); + } catch (e) { + throw new Error(`available_models is not valid JSON: ${e.message}`); + } + const arr = Array.isArray(parsed) ? parsed : [parsed]; + return arr.flatMap((entry, i) => { + if (typeof entry === "string") return entry.trim() ? [] : [`entry[${i}]: blank id`]; + if (typeof entry === "object" && entry !== null) { + return typeof entry.id === "string" && entry.id.trim() ? [] : [`entry[${i}]: missing string "id"`]; + } + return [`entry[${i}]: expected an id string or {id, label} object`]; + }); + }, + }, access_policies: { pattern: /^\[.*\]$/s, hint: "JSON array of policy statements — see commands/access-policies.md; e.g. [{\"effect\":\"deny\",\"action\":\"addin.access\",\"resource\":{...}}]", From f4e86786d812eb549724e2b6eb48e6245f24abf2 Mon Sep 17 00:00:00 2001 From: john-michael murphy Date: Fri, 7 Aug 2026 13:24:48 -0700 Subject: [PATCH 2/2] bump claude-for-msft-365-install to 0.1.9 No-Verification-Needed: version bump only --- claude-for-msft-365-install/.claude-plugin/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claude-for-msft-365-install/.claude-plugin/plugin.json b/claude-for-msft-365-install/.claude-plugin/plugin.json index 26ce89cb2..186f0e3f1 100644 --- a/claude-for-msft-365-install/.claude-plugin/plugin.json +++ b/claude-for-msft-365-install/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "claude-for-msft-365-install", "description": "Provision direct cloud access (Vertex AI, Bedrock, or LLM gateway) for the Claude Office add-in. Generates the customized add-in manifest, walks through Azure admin consent, and writes per-user config via Microsoft Graph extension attributes.", - "version": "0.1.8", + "version": "0.1.9", "author": { "name": "Anthropic", "email": "support@anthropic.com"