Let the coordinator manage skills, MCP servers and tools, behind a confirm card

- Coordinator tools in its own chat (super admin only): list_extensions,
  set_extension_access, delete_extension, and update_skill /
  update_mcp_server / update_custom_tool for what can't change where
  anything is sent (on/off, names, arguments, left-out tools)
- Adding a skill, MCP server or tool, or changing a skill's words, a
  server's URL or headers or a tool's kind or config, goes through
  propose_skill / propose_mcp_server / propose_custom_tool: stored in
  extension_proposals with the bot's background check, shown as a
  [proposal:ID] chip that opens a card with Confirm and Reject
- The card's facts are worked out by the app from what Confirm saves: the
  host, every {{env.KEY}} and whether it's saved, the config; for a skill
  its links, hosts, tool names, mentions of secrets and a diff against
  the saved runbook
- Confirming decides once and checks again; a server or tool missing a key
  is saved switched off, and the outcome is posted to the bot in chat
- Never from a bot: stdio servers, a URL whose host a template decides,
  envs in a prompt tool, a key's value

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-25 06:08:09 +01:00
co-authored by Claude Opus 5.5
parent e6c278198b
commit fefc295d15
77 changed files with 3307 additions and 12 deletions
+58
View File
@@ -34,6 +34,7 @@ import type { ContainerStatus } from "../dict/computer-dict";
import type { BotFace, BotMessageReactionEmoji } from "../dict/bot-dict";
import type { HarnessCallSettings, TokenUsageSummary } from "../harness/usage/types";
import type { McpServerStatus, McpTransport } from "../dict/mcp-dict";
import type { ExtensionProposalKind, ExtensionProposalStatus } from "../dict/extension-proposal-dict";
import type {
CustomToolHttpMethod,
CustomToolKind,
@@ -639,6 +640,63 @@ export type SkillRecord = {
enabled: boolean;
};
/**
* One env a proposed MCP server or tool sends (`extensionEnvUses`), and
* where it's saved: `general`, or the bots and workspaces it's saved for
* only. Nothing saved means the user has to add it.
*/
export type ExtensionProposalEnv = {
key: string;
where: string;
saved_in: string[];
};
/**
* What a proposed MCP server or tool does, as the confirm card shows it:
* each line read from the config by the app, never from what the bot said.
*/
export type ExtensionProposalDetail = {
label: string;
value: string;
/**
* Long or code-like: shown as a block.
*/
block?: boolean;
};
/**
* An MCP server or tool the coordinator proposed (`extension_proposals`),
* as its chip and confirm card show it to the super admin.
*/
export type ExtensionProposalView = {
id: number;
kind: ExtensionProposalKind;
/**
* The one it changes, null for a new one.
*/
extension_id: number | null;
name: string;
status: ExtensionProposalStatus;
/**
* Still pending, but past `expires_at`: it can't be confirmed.
*/
expired: boolean;
expires_at: number;
outcome: string;
result_id: number | null;
/**
* Where it sends requests from the app; null for a script or a prompt.
*/
host: string | null;
envs: ExtensionProposalEnv[];
details: ExtensionProposalDetail[];
/**
* The bot's background check, in its own words.
*/
research: string;
sources: string[];
};
/**
* A skill as the Skills settings page shows it: nothing secret in one, so
* it's the row with who gets it instead of `all_bots`.