dsh-capability-panel 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1048,6 +1048,11 @@ function readMcp(services, degraded, disabledServers, disabledTools, agent, pres
1048
1048
  if (agent !== void 0) collect(agent);
1049
1049
  const globalNames = /* @__PURE__ */ new Set();
1050
1050
  for (const schema of tools.schemas()) if (typeof schema.name === "string" && schema.name.startsWith("mcp__")) globalNames.add(schema.name);
1051
+ const sessionNames = /* @__PURE__ */ new Set();
1052
+ if (agent !== void 0) {
1053
+ for (const schema of tools.schemas(agent)) if (typeof schema.name === "string" && schema.name.startsWith("mcp__")) sessionNames.add(schema.name);
1054
+ }
1055
+ const reachable = (name) => agent === void 0 || sessionNames.has(name);
1051
1056
  const configuredServers = readConfiguredMcpServers(services);
1052
1057
  const groups = groupMcpTools(names).map((group) => {
1053
1058
  const enabled = !disabledServers.has(group.server);
@@ -1058,7 +1063,7 @@ function readMcp(services, degraded, disabledServers, disabledTools, agent, pres
1058
1063
  name,
1059
1064
  label: tool,
1060
1065
  ...description === void 0 ? {} : { description },
1061
- enabled: enabled && !disabledTools.has(name)
1066
+ enabled: enabled && !disabledTools.has(name) && reachable(name)
1062
1067
  };
1063
1068
  });
1064
1069
  const allGlobal = group.tools.every((tool) => globalNames.has(`mcp__${group.server}__${tool}`));
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["counts: Record<string, number>","record: StatsRecord","parsed: unknown","out: SkillLoadRecord[]","out: string[]","state: SkillLoadState","status: 400 | 404 | 409 | 413 | 415 | 503","lines: string[]","exec","prefix","groups: readonly (readonly [CapabilityKind, Readonly<Record<string, boolean>>])[]","EMPTY_STATE: SessionCapabilityState","out: { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[]","names: string[]","groups: McpServerEntry[]","entries: McpToolEntry[]","reachable: Set<string> | undefined","degraded: string[]","presetName: string | undefined","presetPath: string | undefined","presetDirs: { key: string; path: string }[]","settingsScope","entries: ToolSummary[]","skillRows: PresetSkillRow[]","mountError: string | undefined","scope: unknown","mcp: PresetMcpServer[]","tools","stored: ToolkitSettings","visible: PresetSkillRow[]","LOOPBACK_ADDRS: ReadonlySet<string>","KINDS: readonly CapabilityKind[]","sessionId","folderPath: string | undefined","dshHome","persistNote: string | undefined","next: SessionOverrideState","sessions: Record<string, SessionOverrideState>","SessionOverrideSchema: unknown","ToolkitSettingsSchema: unknown","scope: SettingsScopeLike<ToolkitSettings> | undefined","writeQueue: Promise<unknown>","appendWarnings: string[]","text: string","records: StatsRecord[]","warnings: string[]","validLines: string[]"],"sources":["../src/stats.ts","../src/load-state.ts","../src/host/errors.ts","../src/host/reserved.ts","../src/host/capabilities.ts","../src/host/preset-enforcement.ts","../src/host/mcp-connections.ts","../src/host/catalog.ts","../src/host/preset-tools.ts","../src/loopback.ts","../src/host/open-folder.ts","../src/host/route.ts","../src/host/session-overrides.ts","../src/host/settings-scope.ts","../src/host/stats-store.ts","../src/index.ts"],"sourcesContent":["/**\n * Usage statistics: did the agent still reach for a capability AFTER the user\n * turned it off? That answer drives the panel's optimization direction, so it\n * must be measured, not guessed.\n *\n * Detection is pure (this module has no I/O): the host feeds every\n * `tools/result` observation through {@link classifyBlockedCall} and appends\n * the returned record to a JSONL log. Two blocked shapes exist:\n *\n * - skill: the `skill` loader tool is still registered (only the catalog entry\n * is shadowed), so a blocked attempt fails INSIDE the tool with\n * \"not available for model invocation\". The skill name rides the arguments.\n * - mcp: a restricted tool is invisible, so dispatch fails BEFORE the body\n * with code UNKNOWN_TOOL. The tool name is the call name itself.\n *\n * Toggle operations are logged too (kind 'disable'/'enable') so later analysis\n * can correlate \"turned off at T\" with \"attempted at T+n\".\n */\n\n/** Stable prefix of the guard's denial text, marking a hard call to a preset-layer tool the user turned off. */\nexport const GUARD_DENIAL_PREFIX = 'capability-panel: tool disabled';\n\nexport interface StatsRecord {\n readonly ts: string;\n readonly sessionId: string | null;\n readonly kind: 'disable' | 'enable' | 'blocked-skill' | 'blocked-tool';\n /** Skill name or full tool name, by kind. */\n readonly name: string;\n readonly detail?: string;\n}\n\n/** Minimal readonly view of a settled tool call, as `tools/result` hands it over. */\nexport interface SettledCall {\n readonly name?: unknown;\n readonly arguments?: unknown;\n readonly agent?: { readonly id?: unknown };\n readonly error?: { readonly message?: unknown; readonly info?: { readonly code?: unknown } };\n}\n\n/**\n * Classify one settled call against the session's CURRENT disabled sets.\n * Returns null for everything that is not a blocked attempt — including\n * unknown-tool failures for tools nobody disabled (genuine model typos).\n */\nexport function classifyBlockedCall(\n call: SettledCall,\n disabledSkills: ReadonlySet<string>,\n disabledToolNames: ReadonlySet<string>,\n): { kind: 'blocked-skill' | 'blocked-tool'; name: string; sessionId: string | null } | null {\n if (call.error === undefined) return null;\n const sessionId = typeof call.agent?.id === 'string' ? call.agent.id : null;\n\n if (call.name === 'skill') {\n const message = typeof call.error.message === 'string' ? call.error.message : '';\n if (!message.includes('not available for model invocation')) return null;\n const args = call.arguments;\n const skillName =\n args !== null && typeof args === 'object' && typeof (args as { name?: unknown }).name === 'string'\n ? (args as { name: string }).name\n : null;\n if (skillName === null || !disabledSkills.has(skillName)) return null;\n return { kind: 'blocked-skill', name: skillName, sessionId };\n }\n\n if (typeof call.name === 'string' && disabledToolNames.has(call.name)) {\n // Global tools: restrict reports UNKNOWN_TOOL before dispatch; preset-layer tools: the guard denies before execution.\n const message = typeof call.error.message === 'string' ? call.error.message : '';\n if (call.error.info?.code !== 'UNKNOWN_TOOL' && !message.startsWith(GUARD_DENIAL_PREFIX)) return null;\n return { kind: 'blocked-tool', name: call.name, sessionId };\n }\n\n return null;\n}\n\n/** Aggregate a JSONL log into a name → blocked-attempt count map. */\nexport function aggregateBlocked(lines: Iterable<string>): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let record: StatsRecord;\n try {\n record = JSON.parse(trimmed) as StatsRecord;\n } catch {\n continue;\n }\n // 'blocked-mcp' is the pre-tool-level name for the same fact.\n if (record.kind !== 'blocked-skill' && record.kind !== 'blocked-tool' && (record.kind as string) !== 'blocked-mcp') {\n continue;\n }\n if (typeof record.name !== 'string') continue;\n counts[record.name] = (counts[record.name] ?? 0) + 1;\n }\n return counts;\n}\n","import type { SkillEntry, SkillLoadState } from './contract.js';\n\n/**\n * One `skill` tool call found in the durable log.\n *\n * Verified against real session logs: a load is a\n * `tool/call` event whose `data.name` is `\"skill\"` and whose `data.arguments` is\n * a JSON *string* holding `{ name }`. The nested shapes suggested by package\n * READMEs (`data.call.name`, `data.call.args`) do NOT exist here — reading them\n * silently found zero loads in sessions that had five.\n */\nexport interface SkillLoadRecord {\n readonly seq: number;\n readonly skillName: string;\n readonly callId: string;\n}\n\n/**\n * The raw event shape this package reads, verified against real logs. Surface\n * membership is never read from the events: the live session's\n * `surface.nodes` is authoritative, so `surfaceOp` is deliberately absent\n * here (it is a TOP-LEVEL field on real events — the string `\"append\"` for a\n * node that joined the surface tail, an object for a replacement, and `null`\n * on non-surface events like `tool/call`; anyone reintroducing a surfaceOp\n * read should read that position, not `data.surfaceOp`).\n */\nexport interface RawEvent {\n readonly type?: string;\n readonly seq?: number;\n readonly data?: {\n readonly name?: string;\n readonly arguments?: string;\n readonly callId?: string;\n /** tool/result payload; the pairing callId lives at message.source.callId. */\n readonly message?: {\n readonly source?: { readonly callId?: unknown };\n /** Result blocks; read for prune-marker detection on surface nodes. */\n readonly content?: unknown;\n };\n };\n}\n\n/** Pull the skill name out of a `skill` tool call's stringified arguments. */\nfunction skillNameOf(args: string | undefined): string | null {\n if (typeof args !== 'string') return null;\n try {\n const parsed: unknown = JSON.parse(args);\n if (parsed !== null && typeof parsed === 'object' && 'name' in parsed) {\n const name = (parsed as { name?: unknown }).name;\n return typeof name === 'string' && name !== '' ? name : null;\n }\n } catch {\n // A malformed argument blob is not a load we can attribute; skip it rather\n // than guessing a name.\n }\n return null;\n}\n\nexport function collectLoadRecords(events: readonly RawEvent[]): SkillLoadRecord[] {\n const out: SkillLoadRecord[] = [];\n for (const event of events) {\n if (event.type !== 'tool/call') continue;\n if (event.data?.name !== 'skill') continue;\n const skillName = skillNameOf(event.data.arguments);\n if (skillName === null) continue;\n const seq = event.seq;\n if (typeof seq !== 'number') continue;\n out.push({ seq, skillName, callId: event.data.callId ?? '' });\n }\n return out;\n}\n\n/**\n * Map a tool result's pairing callId to its seq.\n *\n * Verified against real logs: `tool/result` events carry no\n * top-level callId; the pairing lives at `data.message.source.callId` and\n * matches the call's `data.callId` exactly.\n *\n * Last write wins on purpose: the middle-pruner appends a stub `tool/result`\n * carrying the SAME callId and a replace surfaceOp over the original's seq\n * (verified against a real compacted session), so the callId resolves to the\n * stub — the node whose fold verdict actually tracks the surface position.\n */\nexport function indexToolResultSeqs(events: readonly RawEvent[]): Map<string, number> {\n const out = new Map<string, number>();\n for (const event of events) {\n if (event.type !== 'tool/result') continue;\n const callId = event.data?.message?.source?.callId;\n const seq = event.seq;\n if (typeof callId !== 'string' || callId === '' || typeof seq !== 'number') continue;\n out.set(callId, seq);\n }\n return out;\n}\n\n/**\n * The load seqs whose skill content is gone from the model surface.\n *\n * A `tool/call` never joins the surface itself — SURFACE_EVENT_TYPES in\n * dsh-session is exactly { user/message, assistant/message, tool/result }, and\n * real skill calls carry `surfaceOp: null`. What the model actually sees of a\n * skill is its tool RESULT (a surface node), so eviction keys on the paired\n * result's surface membership, not on the call's position.\n *\n * `surfaceSeqs` is the live session's CURRENT surface (`session.surface.nodes`),\n * which the session maintains incrementally — reading it is O(1), no log fold\n * is ever run for this panel. A paired result seq absent from that set was\n * displaced by a prune stub's or a summary's `replace` op, i.e. shadowed.\n *\n * A load with no paired result is in flight or its result never landed; it is\n * NOT counted as shadowed, so it reports `loaded` — the honest reading of \"the\n * model is about to see it\".\n */\nexport function shadowedLoadSeqs(\n loads: readonly SkillLoadRecord[],\n resultSeqByCallId: ReadonlyMap<string, number>,\n surfaceSeqs: ReadonlySet<number>,\n): Set<number> {\n const out = new Set<number>();\n for (const load of loads) {\n const resultSeq = resultSeqByCallId.get(load.callId);\n if (resultSeq === undefined) continue;\n if (!surfaceSeqs.has(resultSeq)) out.add(load.seq);\n }\n return out;\n}\n\n/**\n * Substring of dsh-compaction-tool-result-pruner's PRUNE_MARKER. Matching on\n * the bracketed phrase (without the surrounding newlines) keeps the check\n * robust to marker framing changes across pruner versions.\n */\nexport const PRUNE_MARKER_TEXT = '[... tool result middle pruned ...]';\n\nfunction textBlocksOf(event: RawEvent): readonly string[] {\n const content = event.data?.message?.content;\n if (!Array.isArray(content)) return [];\n const out: string[] = [];\n // A tool/result message wraps its blocks one level deep: message.content is\n // [{ type: 'tool_result', content: blocks }] on the pruner's write path.\n const walk = (blocks: readonly unknown[]): void => {\n for (const block of blocks) {\n if (block === null || typeof block !== 'object') continue;\n const record = block as { type?: unknown; text?: unknown; content?: unknown };\n if (record.type === 'text' && typeof record.text === 'string') out.push(record.text);\n else if (Array.isArray(record.content)) walk(record.content);\n }\n };\n walk(content);\n return out;\n}\n\n/**\n * The load seqs whose paired tool result sits on the surface TRUNCATED —\n * head and tail visible, middle replaced by the pruner's marker. The model\n * partially sees these skills, so they read as their own state rather than\n * either extreme. Only surface-resident results are inspected; a shadowed\n * result is already reported by shadowedLoadSeqs and its content is\n * irrelevant to the model now.\n */\nexport function prunedLoadSeqs(\n loads: readonly SkillLoadRecord[],\n resultSeqByCallId: ReadonlyMap<string, number>,\n surfaceSeqs: ReadonlySet<number>,\n events: readonly RawEvent[],\n): Set<number> {\n const prunedResults = new Set<number>();\n for (const event of events) {\n if (event.type !== 'tool/result') continue;\n const seq = event.seq;\n if (typeof seq !== 'number' || !surfaceSeqs.has(seq)) continue;\n if (textBlocksOf(event).some((text) => text.includes(PRUNE_MARKER_TEXT))) prunedResults.add(seq);\n }\n const out = new Set<number>();\n for (const load of loads) {\n const resultSeq = resultSeqByCallId.get(load.callId);\n if (resultSeq !== undefined && prunedResults.has(resultSeq)) out.add(load.seq);\n }\n return out;\n}\n\n/**\n * Decide each skill's state.\n *\n * `shadowedSeqs` holds LOAD seqs whose paired tool result is absent from the\n * current surface (see shadowedLoadSeqs for why the result, not the call,\n * carries the verdict); `prunedSeqs` holds load seqs whose result survives on\n * the surface with its middle truncated. Do NOT re-derive either from\n * replacement ranges here: after a replacement lands, a high-seq summary node\n * sits at the shadowed range's *position*, so surface order stops tracking\n * seq order and a numeric `start <= seq <= end` test silently misjudges later\n * compactions.\n *\n * Verified against real data: the middle-pruner had replaced the lark-shared /\n * lark-im / lark-event results with stubs (those now read `pruned`), and a\n * later full-history compaction (one replace over [7..16114]) shadowed those\n * stubs plus the find-skills / git-worktree-discipline results (those read\n * `evicted`).\n */\nexport function decideStates(\n available: readonly { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[],\n loads: readonly SkillLoadRecord[],\n shadowedSeqs: ReadonlySet<number>,\n disabledSkills: ReadonlySet<string> = new Set(),\n prunedSeqs: ReadonlySet<number> = new Set(),\n): SkillEntry[] {\n const byName = new Map<string, SkillLoadRecord[]>();\n for (const record of loads) {\n const bucket = byName.get(record.skillName);\n if (bucket === undefined) byName.set(record.skillName, [record]);\n else bucket.push(record);\n }\n\n return available.map(({ name, description, masked, source, provider, path, group }) => {\n const records = byName.get(name) ?? [];\n let state: SkillLoadState = 'unloaded';\n if (records.length > 0) {\n // A reload after an eviction is still loaded: any surviving record wins.\n const current = records.filter((r) => !shadowedSeqs.has(r.seq));\n if (current.length === 0) state = 'evicted';\n // One fully intact copy in context is enough; truncation shows only\n // when every surviving copy was middle-pruned.\n else state = current.some((r) => !prunedSeqs.has(r.seq)) ? 'loaded' : 'pruned';\n }\n return {\n name,\n ...(description === undefined ? {} : { description }),\n state,\n // Off if THIS panel switched it off, or if something else already\n // withdrew model invocation -- a preset default, most often.\n enabled: !disabledSkills.has(name) && masked !== true,\n loadCount: records.length,\n source,\n provider,\n ...(path === undefined ? {} : { path }),\n ...(group === undefined ? {} : { group }),\n };\n });\n}\n\n/** Group MCP tools by server: `mcp__<server>__<tool>`. */\nexport function groupMcpTools(toolNames: readonly string[]): { server: string; tools: string[] }[] {\n const byServer = new Map<string, string[]>();\n for (const raw of toolNames) {\n if (!raw.startsWith('mcp__')) continue;\n const rest = raw.slice('mcp__'.length);\n const cut = rest.indexOf('__');\n // A name with no second separator has no tool part to attribute.\n if (cut <= 0) continue;\n const server = rest.slice(0, cut);\n const tool = rest.slice(cut + 2);\n if (tool === '') continue;\n const bucket = byServer.get(server);\n if (bucket === undefined) byServer.set(server, [tool]);\n else bucket.push(tool);\n }\n return [...byServer.entries()]\n .map(([server, tools]) => ({ server, tools: tools.sort() }))\n .sort((a, b) => a.server.localeCompare(b.server));\n}\n","export class HttpError extends Error {\n constructor(\n readonly status: 400 | 404 | 409 | 413 | 415 | 503,\n message: string,\n ) {\n super(message);\n }\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * `run_code` is the Code Mode transport the harness itself depends on. It must\n * stay reachable in every session, so it can never be switched off: the\n * settings route rejects the attempt with 409, the panels render its switch\n * disabled, and enforcement ignores a stored entry naming it. One constant\n * keeps the four of those from drifting apart.\n */\nexport const RESERVED_TOOL = 'run_code';\n","import { classifyBlockedCall, GUARD_DENIAL_PREFIX } from '../stats.js';\nimport type { StatsRecord } from '../stats.js';\nimport { groupMcpTools } from '../load-state.js';\nimport { HttpError } from './errors.js';\nimport { RESERVED_TOOL } from './reserved.js';\nimport type {\n AgentLike,\n CapabilityKind,\n HostServices,\n ScopedSkillsRegistry,\n ScopedSystemPrompt,\n ScopedToolsRegistry,\n SessionCapabilityState,\n SessionOverrideState,\n} from './types.js';\n\nexport interface PresetDefaults {\n /** Disabled tool names: `mcp__`-prefixed land on the MCP map, the rest are system tools. */\n readonly tools: readonly string[];\n readonly skills: readonly string[];\n}\n\nexport interface CapabilityController {\n readonly states: ReadonlyMap<string, SessionCapabilityState>;\n state(sessionId: string): SessionCapabilityState | undefined;\n set(sessionId: string, kind: CapabilityKind, name: string, enabled: boolean): Promise<void>;\n /**\n * Apply a preset's stored defaults to a freshly created session. Seeded\n * masks live in the SAME session state the panel's own switches use, which\n * is what lets a session switch a preset default back on: the enable path\n * disposes the entry whichever layer put it there. Seeding never throws --\n * a default that can no longer apply is dropped, not escalated -- and never\n * overwrites a mask already in place.\n */\n seed(sessionId: string, defaults: PresetDefaults): Promise<void>;\n /**\n * Re-apply one session's persisted switch positions onto a freshly created\n * agent (e.g. a restored session after a restart). Runs AFTER seeding:\n * preset defaults first, the session's own recorded toggles second, so the\n * user's last word wins — including an explicit re-enable of a preset\n * default. Never throws and never writes stats: a position that can no\n * longer apply (skill uninstalled, MCP server gone) is dropped, and no\n * toggle actually happened here.\n */\n restore(sessionId: string, overrides: SessionOverrideState): Promise<void>;\n /**\n * Re-derive a session's masks after its preset changed mid-life (the user\n * switched the agent preset before the first turn). Seeded masks live in\n * the agent's own scope layer and survive the rebind, so the state is torn\n * down wholesale before the new preset's defaults and the session's stored\n * overrides land again.\n */\n reseed(sessionId: string, defaults: PresetDefaults, overrides: SessionOverrideState | undefined): Promise<void>;\n /**\n * Re-apply the tool masks a session should hold, for names that exist NOW\n * but did not at seed time (an on-demand MCP server whose local process\n * just connected). Idempotent: seed and restore skip names already masked.\n * Tool-only — skills do not late-register the way reconnecting tools do.\n */\n remask(sessionId: string, defaults: PresetDefaults, overrides: SessionOverrideState | undefined): Promise<void>;\n}\n\nfunction renderDisabledNote(state: SessionCapabilityState): string {\n const lines: string[] = [];\n if (state.skills.size > 0) lines.push(`- Skills: ${[...state.skills.keys()].join(', ')}`);\n if (state.mcpServers.size > 0) lines.push(`- MCP servers: ${[...state.mcpServers.keys()].join(', ')}`);\n if (state.mcpTools.size > 0) lines.push(`- MCP tools: ${[...state.mcpTools.keys()].join(', ')}`);\n if (state.systemTools.size > 0) lines.push(`- System tools: ${[...state.systemTools.keys()].join(', ')}`);\n if (lines.length === 0) return '';\n return [\n 'The user has turned off the following capabilities for this session:',\n ...lines,\n 'Do not attempt to call them. If the user\\'s request depends on one, say it is disabled and can be re-enabled from the Capability Panel.',\n ].join('\\n');\n}\n\nexport function createCapabilityController(\n ctx: HostServices,\n appendStats: (record: StatsRecord) => void,\n blockedCounts: Record<string, number>,\n): CapabilityController {\n const states = new Map<string, SessionCapabilityState>();\n const stateFor = (sessionId: string): SessionCapabilityState => {\n let state = states.get(sessionId);\n if (state === undefined) {\n state = { skills: new Map(), mcpServers: new Map(), mcpTools: new Map(), systemTools: new Map(), userToggled: new Set() };\n states.set(sessionId, state);\n }\n return state;\n };\n const disabledToolNames = (state: SessionCapabilityState): Set<string> => {\n const names = new Set([...state.mcpTools.keys(), ...state.systemTools.keys()]);\n for (const schema of state.mcpServers.size > 0 ? (ctx.get('tools')?.schemas() ?? []) : []) {\n if (typeof schema.name !== 'string' || !schema.name.startsWith('mcp__')) continue;\n const server = schema.name.slice('mcp__'.length, schema.name.indexOf('__', 'mcp__'.length));\n if (state.mcpServers.has(server)) names.add(schema.name);\n }\n return names;\n };\n\n ctx.on('system-prompt/assemble', async (_assembly, context, next) => {\n const assembled = await next();\n const sessionId = typeof context.agent?.id === 'string' ? context.agent.id : null;\n const state = sessionId === null ? undefined : states.get(sessionId);\n if (state === undefined || state.systemTools.size === 0 || assembled.tools === undefined) return assembled;\n return { ...assembled, tools: assembled.tools.filter((tool) => !state.systemTools.has(String(tool.name))) };\n });\n\n let guardRegistered = false;\n const ensureGuard = (): void => {\n if (guardRegistered) return;\n const guardDispose = ctx.get('tools')?.guard?.((execution) => {\n const sessionId = typeof execution.agent?.id === 'string' ? execution.agent.id : null;\n const state = sessionId === null ? undefined : states.get(sessionId);\n const name = typeof execution.name === 'string' ? execution.name : null;\n if (state === undefined || name === null || !state.systemTools.has(name)) return undefined;\n return `${GUARD_DENIAL_PREFIX} \"${name}\" (re-enable from the Capability Panel)`;\n });\n if (guardDispose === undefined) return;\n guardRegistered = true;\n ctx.effect(() => guardDispose, 'capability-panel: tool guard');\n };\n ensureGuard();\n\n ctx.on('tools/result', (exec, result) => {\n const agent = exec.agent;\n if (agent === undefined || typeof agent.id !== 'string') return;\n const state = states.get(agent.id);\n if (state === undefined) return;\n const hit = classifyBlockedCall(\n {\n name: exec.name,\n arguments: exec.arguments,\n agent,\n ...(result.isError && result.error !== undefined ? { error: result.error } : {}),\n },\n new Set(state.skills.keys()),\n disabledToolNames(state),\n );\n if (hit === null) return;\n blockedCounts[hit.name] = (blockedCounts[hit.name] ?? 0) + 1;\n appendStats({ ts: new Date().toISOString(), sessionId: agent.id, kind: hit.kind, name: hit.name });\n });\n\n const ensurePromptNote = (agent: AgentLike, state: SessionCapabilityState): void => {\n if (state.noteDispose !== undefined) return;\n const systemPrompt = agent.ctx?.get('systemPrompt') as ScopedSystemPrompt | undefined;\n if (systemPrompt === undefined) return;\n state.noteDispose = systemPrompt.context({\n name: 'capability-panel:disabled-capabilities',\n order: 900,\n text: () => renderDisabledNote(state),\n });\n };\n\n const getAgentTools = (sessionId: string): { agent: AgentLike; tools: ScopedToolsRegistry } => {\n const agent = ctx.get('agents')?.get(sessionId);\n const tools = agent?.ctx?.get('tools') as ScopedToolsRegistry | undefined;\n if (agent === undefined) throw new HttpError(404, 'session agent is not available');\n if (tools === undefined) throw new HttpError(503, 'session tools service is not available');\n return { agent, tools };\n };\n\n const setSkill = async (sessionId: string, name: string, enabled: boolean): Promise<void> => {\n const state = stateFor(sessionId);\n const existing = state.skills.get(name);\n if (enabled && existing !== undefined) {\n existing();\n state.skills.delete(name);\n return;\n }\n const agent = ctx.get('agents')?.get(sessionId);\n const scopedSkills = agent?.ctx?.get('skills') as ScopedSkillsRegistry | undefined;\n if (agent === undefined || scopedSkills === undefined) {\n throw new HttpError(agent === undefined ? 404 : 503, agent === undefined ? 'session agent is not available' : 'session skills service is not available');\n }\n if (enabled || existing !== undefined) return;\n const skills = ctx.get('skills');\n if (skills === undefined) throw new HttpError(503, 'skills service unavailable');\n const cwd = agent.session?.header?.cwd;\n const original = await skills.get(name, { ...(cwd === undefined ? {} : { cwd }), scope: agent });\n if (original === undefined || typeof original.name !== 'string' || typeof original.description !== 'string' || typeof original.content !== 'string') {\n throw new HttpError(404, `skill \"${name}\" is not available in this session`);\n }\n state.skills.set(name, scopedSkills.register({\n name: original.name,\n description: original.description,\n content: original.content,\n // The shadow wins the listing outright (nearest layer), so it must\n // carry the original's provenance — hardcoding 'custom' would mislabel\n // every panel-disabled skill's source in the catalogue.\n source: typeof original.source === 'string' ? original.source : 'custom',\n provider: typeof original.provider === 'string' ? original.provider : 'capability-panel',\n ...(original.resourceBase === undefined ? {} : { resourceBase: original.resourceBase }),\n invocation: { modelInvocable: false, userInvocable: true },\n }));\n ensurePromptNote(agent, state);\n };\n\n const setServer = (sessionId: string, server: string, enabled: boolean): void => {\n const state = stateFor(sessionId);\n const existing = state.mcpServers.get(server);\n if (enabled) {\n // Enabling from the server row must clear EVERY mask under this server:\n // the server-level entry AND any per-tool entries a partial preset\n // default (or per-tool switch) left behind — otherwise the row reads on\n // while those tools stay denied.\n const prefix = `mcp__${server}__`;\n for (const [name, dispose] of [...state.mcpTools]) {\n if (name.startsWith(prefix)) {\n dispose();\n state.mcpTools.delete(name);\n }\n }\n if (existing !== undefined) {\n existing.dispose();\n state.mcpServers.delete(server);\n }\n return;\n }\n if (existing !== undefined) return;\n const { agent, tools } = getAgentTools(sessionId);\n const toolService = ctx.get('tools');\n if (toolService === undefined) throw new HttpError(503, 'tools service unavailable');\n const prefix = `mcp__${server}__`;\n const names = [...toolService.schemas()]\n .map((schema) => schema.name)\n .filter((name): name is string => typeof name === 'string' && name.startsWith(prefix));\n if (names.length === 0) throw new HttpError(404, `MCP server \"${server}\" exposes no tools`);\n state.mcpServers.set(server, { dispose: tools.restrict({ deny: names }), names });\n ensurePromptNote(agent, state);\n };\n\n const setTool = (sessionId: string, name: string, enabled: boolean, system: boolean): void => {\n const state = stateFor(sessionId);\n const map = system ? state.systemTools : state.mcpTools;\n const existing = map.get(name);\n if (enabled) {\n // Enabling is a pure state cleanup: an unmasked tool needs no services\n // (a server-row enable may already have swept this tool's mask).\n if (existing !== undefined) {\n existing();\n map.delete(name);\n }\n return;\n }\n if (existing !== undefined) return;\n if (system && name === RESERVED_TOOL) {\n throw new HttpError(409, 'run_code is the reserved Code Mode transport and cannot be restricted');\n }\n const { agent, tools } = getAgentTools(sessionId);\n const toolService = ctx.get('tools');\n if (toolService === undefined) throw new HttpError(503, 'tools service unavailable');\n const globalNames = new Set(\n [...toolService.schemas()]\n .map((schema) => schema.name)\n .filter((entry): entry is string => typeof entry === 'string'),\n );\n const scopedNames = system\n ? new Set(\n [...toolService.schemas(agent)]\n .map((schema) => schema.name)\n .filter((entry): entry is string => typeof entry === 'string'),\n )\n : globalNames;\n const exists = system ? scopedNames.has(name) : globalNames.has(name) && name.startsWith('mcp__');\n if (!exists) {\n throw new HttpError(404, `${system ? 'system tool' : 'MCP tool'} \"${name}\" is not available in this session`);\n }\n if (system) ensureGuard();\n map.set(name, globalNames.has(name) ? tools.restrict({ deny: [name] }) : () => {});\n ensurePromptNote(agent, state);\n };\n\n ctx.effect(() => () => {\n for (const state of states.values()) {\n for (const dispose of state.skills.values()) dispose();\n for (const mask of state.mcpServers.values()) mask.dispose();\n for (const dispose of state.mcpTools.values()) dispose();\n for (const dispose of state.systemTools.values()) dispose();\n state.noteDispose?.();\n }\n states.clear();\n }, 'capability-panel: capability masks');\n\n const seed = async (sessionId: string, defaults: PresetDefaults, includeSkills = true): Promise<void> => {\n const agent = ctx.get('agents')?.get(sessionId);\n if (agent === undefined) return;\n // The state is created on the first mask that actually lands: a session\n // whose stored defaults all name things it cannot see keeps no state at\n // all, rather than an empty entry that reads as \"nothing is off\".\n let state = states.get(sessionId);\n const ensureState = (): SessionCapabilityState => {\n if (state === undefined) state = stateFor(sessionId);\n return state;\n };\n let maskedAny = false;\n\n const scopedTools = agent.ctx?.get('tools') as ScopedToolsRegistry | undefined;\n const toolsService = ctx.get('tools');\n if (scopedTools !== undefined && toolsService !== undefined) {\n const globalNames = new Set<string>();\n const scopedNames = new Set<string>();\n for (const schema of toolsService.schemas()) {\n if (typeof schema.name === 'string') globalNames.add(schema.name);\n }\n for (const schema of toolsService.schemas(agent)) {\n if (typeof schema.name === 'string') scopedNames.add(schema.name);\n }\n for (const name of defaults.tools) {\n if (!globalNames.has(name)) continue;\n // MCP defaults are grouped and applied below, one level per server.\n if (name.startsWith('mcp__')) continue;\n // The reserved transport stays reachable no matter what was stored.\n if (name === RESERVED_TOOL || state?.systemTools.has(name) === true) continue;\n // A default for a tool this agent cannot see is simply not applied.\n if (!scopedNames.has(name)) continue;\n ensureState().systemTools.set(name, scopedTools.restrict({ deny: [name] }));\n ensureGuard();\n maskedAny = true;\n }\n // MCP defaults restore at the granularity they were recorded with: a\n // default covering every tool a server exposes is a SERVER-level mask\n // (one restrict, and the panel's server row reads `mcpServers`, so a\n // full default must land there to display as off); a partial default\n // keeps per-tool entries.\n const mcpDefaults = defaults.tools.filter((name) => globalNames.has(name));\n const fullByServer = new Map(groupMcpTools([...globalNames]).map((group) => [group.server, group.tools]));\n for (const group of groupMcpTools(mcpDefaults)) {\n // mcpDefaults ⊆ globalNames, so the full server roster always exists.\n const full = fullByServer.get(group.server)!;\n const coversAll = full.every((tool) => group.tools.includes(tool));\n if (coversAll) {\n const st = ensureState();\n const prefix = `mcp__${group.server}__`;\n const fullNames = full.map((tool) => `${prefix}${tool}`);\n const existingMask = st.mcpServers.get(group.server);\n // A standing mask whose deny list predates a roster growth (the\n // server reconnected with MORE tools) cannot cover the new names:\n // replace it with the fresh roster.\n if (existingMask !== undefined && fullNames.every((name) => existingMask.names.includes(name))) continue;\n // Promote any per-tool masks an earlier partial application left\n // (e.g. the server was mid-startup at seed time) into the\n // server-level entry — one owner per server, no double bookkeeping.\n for (const [name, dispose] of [...st.mcpTools]) {\n if (name.startsWith(prefix)) {\n dispose();\n st.mcpTools.delete(name);\n }\n }\n existingMask?.dispose();\n st.mcpServers.set(group.server, { dispose: scopedTools.restrict({ deny: fullNames }), names: fullNames });\n maskedAny = true;\n continue;\n }\n for (const tool of group.tools) {\n const name = `mcp__${group.server}__${tool}`;\n if (state?.mcpTools.has(name) === true) continue;\n ensureState().mcpTools.set(name, scopedTools.restrict({ deny: [name] }));\n maskedAny = true;\n }\n }\n }\n\n const scopedSkills = agent.ctx?.get('skills') as ScopedSkillsRegistry | undefined;\n const skillsService = ctx.get('skills');\n if (includeSkills && scopedSkills !== undefined && skillsService !== undefined) {\n const cwd = agent.session?.header?.cwd;\n const lookup = { ...(cwd === undefined ? {} : { cwd }), scope: agent };\n // Read every original concurrently: each get is an I/O read, and the\n // session is already live while its masks go in, so serial reads would\n // widen a window that serves nobody.\n const disposers = await Promise.all(defaults.skills.map(async (name) => {\n if (state?.skills.has(name) === true) return undefined;\n try {\n const original = await skillsService.get(name, lookup);\n if (original === undefined) return undefined;\n if (typeof original.name !== 'string' || typeof original.description !== 'string') return undefined;\n if (typeof original.content !== 'string') return undefined;\n return scopedSkills.register({\n name: original.name,\n description: original.description,\n content: original.content,\n // Same provenance rule as the panel's own switch: the shadow\n // replaces the original in the listing, so it must inherit the\n // original's source/provider.\n source: typeof original.source === 'string' ? original.source : 'custom',\n provider: typeof original.provider === 'string' ? original.provider : 'capability-panel',\n ...(original.resourceBase === undefined ? {} : { resourceBase: original.resourceBase }),\n invocation: { modelInvocable: false, userInvocable: true },\n });\n } catch {\n // Unreadable or already shadowed; the skill stays as composed.\n return undefined;\n }\n }));\n for (let i = 0; i < disposers.length; i += 1) {\n const dispose = disposers[i];\n const name = defaults.skills[i];\n if (dispose !== undefined && name !== undefined && state?.skills.has(name) !== true) {\n ensureState().skills.set(name, dispose);\n maskedAny = true;\n }\n }\n }\n\n // The model should KNOW these are off rather than discover it call by\n // call; the note covers seeded defaults exactly like panel switches.\n if (maskedAny) ensurePromptNote(agent, ensureState());\n };\n\n // Per-session mutation queue. seed/restore/reseed/set all interleave await\n // points (skill I/O) with each other, and reseed's synchronous teardown\n // would otherwise land mid-seed: a pending created-time seed would write\n // old-preset masks into the fresh post-reseed state, and a panel toggle\n // mid-switch would write into a detached state object. Serializing per\n // session makes every mutation see a coherent state; cross-session work\n // stays parallel.\n const mutationQueues = new Map<string, Promise<unknown>>();\n const enqueue = <T>(sessionId: string, op: () => Promise<T>): Promise<T> => {\n const next = (mutationQueues.get(sessionId) ?? Promise.resolve()).then(op, op);\n // The stored link never rejects: a failed mutation must not jam the queue.\n mutationQueues.set(sessionId, next.then(() => undefined, () => undefined));\n return next;\n };\n\n const restoreImpl = async (sessionId: string, overrides: SessionOverrideState): Promise<void> => {\n const groups: readonly (readonly [CapabilityKind, Readonly<Record<string, boolean>>])[] = [\n ['skill', overrides.skills],\n ['mcp-server', overrides.mcpServers],\n ['mcp-tool', overrides.mcpTools],\n ['system-tool', overrides.systemTools],\n ];\n for (const [kind, positions] of groups) {\n for (const [name, enabled] of Object.entries(positions)) {\n // A panel toggle landing mid-restore (restore awaits skill I/O per\n // entry) is newer than the record on disk; re-applying the stored\n // position would silently diverge memory from it.\n if (states.get(sessionId)?.userToggled.has(`${kind}:${name}`) === true) continue;\n try {\n if (kind === 'skill') await setSkill(sessionId, name, enabled);\n else if (kind === 'mcp-server') setServer(sessionId, name, enabled);\n else setTool(sessionId, name, enabled, kind === 'system-tool');\n } catch {\n // A stored position that can no longer apply is dropped, not\n // escalated -- same rule as seeding a stale preset default.\n }\n }\n }\n // Positions that all dropped (or all re-enabled neutral) must not leave\n // an empty state entry behind: \"no state\" is the panel's \"nothing is\n // off\" signal, and stale entries would only ever accumulate.\n const st = states.get(sessionId);\n if (st !== undefined && st.userToggled.size === 0 && st.skills.size === 0 && st.mcpServers.size === 0 && st.mcpTools.size === 0 && st.systemTools.size === 0) {\n st.noteDispose?.();\n delete st.noteDispose;\n states.delete(sessionId);\n }\n };\n\n const controller: CapabilityController = {\n states,\n state: (sessionId) => states.get(sessionId),\n seed: (sessionId, defaults) => enqueue(sessionId, () => seed(sessionId, defaults)),\n restore: (sessionId, overrides) => enqueue(sessionId, () => restoreImpl(sessionId, overrides)),\n /**\n * Re-derive a session's masks after its preset changed: every existing\n * mask was seeded for the OLD composition and survives the scope rebind\n * (scoped masks live in the agent's own layer), so the session state is\n * torn down wholesale before the new preset's defaults and the session's\n * own stored overrides land again. `userToggled` clears with the masks —\n * its entries guarded in-flight restore races against masks that no\n * longer exist, and the durable override record is what restore replays.\n */\n reseed: (sessionId, defaults, overrides) => enqueue(sessionId, async () => {\n const st = states.get(sessionId);\n if (st !== undefined) {\n for (const dispose of st.systemTools.values()) dispose();\n for (const mask of st.mcpServers.values()) mask.dispose();\n for (const dispose of st.mcpTools.values()) dispose();\n for (const dispose of st.skills.values()) dispose();\n for (const map of [st.systemTools, st.mcpServers, st.mcpTools, st.skills]) {\n map.clear();\n }\n st.noteDispose?.();\n delete st.noteDispose;\n st.userToggled.clear();\n states.delete(sessionId);\n }\n // Inside the queue: call the impls directly — enqueuing here would\n // deadlock the session's own queue.\n if (defaults.tools.length > 0 || defaults.skills.length > 0) await seed(sessionId, defaults);\n if (overrides !== undefined) await restoreImpl(sessionId, overrides);\n }),\n /**\n * Re-apply the masks a session SHOULD hold, for names that exist NOW but\n * did not at seed time — an on-demand MCP server whose local process just\n * connected. Tool-only by design: seed and restore skip every name\n * already masked, so this is a no-op when nothing new registered, and\n * skills are excluded (they do not late-register the way a reconnecting\n * server's tools do, and re-reading every default skill per registry\n * change would be I/O for nobody).\n */\n remask: (sessionId, defaults, overrides) => enqueue(sessionId, async () => {\n if (defaults.tools.length > 0) await seed(sessionId, defaults, false);\n if (overrides !== undefined) {\n await restoreImpl(sessionId, { skills: {}, mcpServers: overrides.mcpServers, mcpTools: overrides.mcpTools, systemTools: overrides.systemTools });\n }\n }),\n set: (sessionId, kind, name, enabled) => enqueue(sessionId, async () => {\n stateFor(sessionId).userToggled.add(`${kind}:${name}`);\n if (kind === 'skill') await setSkill(sessionId, name, enabled);\n else if (kind === 'mcp-server') setServer(sessionId, name, enabled);\n else setTool(sessionId, name, enabled, kind === 'system-tool');\n appendStats({ ts: new Date().toISOString(), sessionId, kind: enabled ? 'enable' : 'disable', name: `${kind}:${name}` });\n }),\n };\n return controller;\n}\n","import type { CapabilityController } from './capabilities.js';\nimport type { PresetToolController } from './preset-tools.js';\nimport type { SessionOverrideStore } from './session-overrides.js';\nimport type { AgentCreatedPayload, HostServices } from './types.js';\n\n/**\n * Applies stored capability positions to freshly created agents, in two\n * layers: the preset's stored defaults first, then the session's own recorded\n * toggles. The second layer is what survives a restart — a restored session\n * creates a new agent, and its session-bound overrides land on top of the\n * preset defaults, so the user's last word wins (including an explicit\n * re-enable of a preset default).\n *\n * This is deliberately NOT part of the HTTP controller: the listener has no\n * request, and living next to one is how it grew a settings schema, a write\n * queue, and two event subscriptions in a single file. Here it owns exactly\n * one job -- at agent/created, read both stored layers and seed them into the\n * session's capability state.\n *\n * Seeding into the session state (rather than registering private masks) is\n * the point: the session panel's enable path disposes whatever the state\n * holds, whichever layer put it there, so a stored position remains a\n * starting point the user can flip in the session instead of an invisible\n * wall. Session overrides are keyed by session id, so one session's switches\n * can never leak into another.\n */\nexport function registerPresetEnforcement(\n ctx: HostServices,\n capabilities: CapabilityController,\n presetTools: PresetToolController,\n sessionOverrides: SessionOverrideStore,\n): void {\n /**\n * Subscribe to `agent/created` so that NOTHING this plugin does can veto the\n * agent. Cordis treats a synchronous listener failure as a veto and only\n * reports a rejected promise, so both halves are contained: the synchronous\n * prelude (reading settings, resolving services) is wrapped here, and the\n * asynchronous body reports through the returned promise. Applying a stored\n * preference is never worth costing the user their session.\n */\n ctx.on('agent/created', ({ agent }: AgentCreatedPayload) => {\n try {\n if (typeof agent.id !== 'string') return undefined;\n const sessionId = agent.id;\n const presetId = ctx.get('agentPresets')?.composedPreset(agent.ctx);\n const defaults = presetId === undefined ? undefined : presetTools.defaultsFor(presetId);\n const hasDefaults = defaults !== undefined && (defaults.tools.length > 0 || defaults.skills.length > 0);\n const overrides = sessionOverrides.overridesFor(sessionId);\n if (!hasDefaults && overrides === undefined) return undefined;\n return (async () => {\n if (hasDefaults && defaults !== undefined) await capabilities.seed(sessionId, defaults);\n if (overrides !== undefined) await capabilities.restore(sessionId, overrides);\n })();\n } catch {\n // A settings section that cannot be read, or a service that is not there\n // yet, leaves the agent exactly as its preset composed it; the panel\n // surfaces the fault on its next read.\n return undefined;\n }\n });\n\n /**\n * A preset switch lands AFTER creation (the session header records the\n * composing preset at creation, and the picker's select() recomposes the\n * blank session later): the first agent/created seeded the ORIGINAL\n * preset's defaults, so the new preset's defaults must re-seed on top of a\n * clean slate. Session overrides replay last — the user's own switches in\n * this session outrank either preset's defaults.\n */\n ctx.on('agent-preset/selected', (sessionId: unknown, presetId: unknown) => {\n try {\n if (typeof sessionId !== 'string' || typeof presetId !== 'string') return undefined;\n const defaults = presetTools.defaultsFor(presetId) ?? { tools: [], skills: [] };\n const overrides = sessionOverrides.overridesFor(sessionId);\n return capabilities.reseed(sessionId, defaults, overrides);\n } catch {\n return undefined;\n }\n });\n\n /**\n * Re-mask when the tool registry changes. seed() can only mask names that\n * are registered at that moment (tools.restrict refuses unknown names), so\n * an on-demand MCP server that connects AFTER the session was created would\n * otherwise arrive with every tool enabled, preset default or not. The\n * registry broadcasts every layer change through this event — late\n * registration, a reconnect's fresh generation, a teardown — and remask is\n * idempotent over names already masked.\n *\n * The guard breaks the echo loop: our own restrict() calls re-emit\n * tools/change, and without it every mask write would schedule another\n * sweep. A change arriving mid-sweep is not lost, though: it is marked\n * pending and one trailing sweep runs when the current one settles, so an\n * external registration landing while a session's slow queue is still\n * processing does not leave stale masks behind.\n */\n let remasking = false;\n let pendingSweep = false;\n ctx.on('tools/change', () => {\n if (remasking) {\n pendingSweep = true;\n return undefined;\n }\n try {\n const agents = ctx.get('agents');\n if (agents === undefined || typeof agents.list !== 'function') return undefined;\n return (async () => {\n do {\n remasking = true;\n pendingSweep = false;\n try {\n let live;\n try {\n live = agents.list();\n } catch {\n // The registry is mid-reload; the next change event re-sweeps.\n return;\n }\n for (const agent of live) {\n try {\n const sessionId = agent?.id;\n if (typeof sessionId !== 'string') continue;\n const presetId = ctx.get('agentPresets')?.composedPreset(agent.ctx);\n const defaults = presetId === undefined ? undefined : presetTools.defaultsFor(presetId);\n const overrides = sessionOverrides.overridesFor(sessionId);\n const hasToolDefaults = defaults !== undefined && defaults.tools.length > 0;\n const hasToolOverrides = overrides !== undefined\n && (Object.keys(overrides.mcpServers).length > 0\n || Object.keys(overrides.mcpTools).length > 0\n || Object.keys(overrides.systemTools).length > 0);\n if (!hasToolDefaults && !hasToolOverrides) continue;\n await capabilities.remask(\n sessionId,\n defaults ?? { tools: [], skills: [] },\n overrides,\n );\n } catch {\n // One session whose stored positions cannot be read keeps its\n // current masks; the sweep continues for the others.\n }\n }\n } finally {\n remasking = false;\n }\n } while (pendingSweep);\n })();\n } catch {\n return undefined;\n }\n });\n}\n","/**\n * The connection half of MCP visibility: which servers this host DECLARES,\n * and how to restart one on demand.\n *\n * The tool registry answers \"which tools can the model call right now\" — the\n * only truth the switches act on — but it says nothing about a server whose\n * local process is down. An on-demand server (IDA, dnSpy, msfrpcd; also the\n * mock server used in tests) is configured in the host composition yet\n * registers nothing while down. The loader entry is that missing half of the\n * truth: it is where the server is declared, and restarting it is exactly\n * what an HMR reload of that entry does.\n *\n * Nothing here polls. The MCP client already owns connection state and\n * retries on its own schedule; the panel only reads the declaration on demand\n * and restarts an entry when the user asks.\n */\nimport { HttpError } from './errors.js';\nimport type { HostServices, LoaderEntryLike } from './types.js';\n\nconst MCP_CLIENT_NAME = '@deepseek-ai/dsh-mcp-client';\n\nfunction entryConfig(entry: LoaderEntryLike): Record<string, unknown> | undefined {\n const config = entry.options?.config;\n if (config === null || typeof config !== 'object' || Array.isArray(config)) return undefined;\n return config as Record<string, unknown>;\n}\n\nfunction declaredServerName(entry: LoaderEntryLike): string | undefined {\n // The loader stores the plugin package name on `options.name` (the field it\n // imports and logs), NOT a top-level `name`. Reading the wrong one silently\n // matches nothing.\n if (entry.options?.name !== MCP_CLIENT_NAME) return undefined;\n const serverName = entryConfig(entry)?.['serverName'];\n return typeof serverName === 'string' && serverName !== '' ? serverName : undefined;\n}\n\n/**\n * Server names the host composition declares via `@deepseek-ai/dsh-mcp-client`\n * entries — connected or not. Matched by plugin name, not by the `serverName`\n * key: an unrelated entry carrying a same-named config key is not an MCP\n * server.\n */\nexport function readConfiguredMcpServers(ctx: HostServices): Set<string> {\n const servers = new Set<string>();\n const loader = ctx.loader;\n if (loader === undefined) return servers;\n try {\n for (const entry of loader.entries()) {\n // A deliberately disabled entry is not \"no tools registered\" — it is\n // off by config. Listing it would promise a reload that can only 409.\n if (entry.disabled === true) continue;\n const name = declaredServerName(entry);\n if (name !== undefined) servers.add(name);\n }\n } catch {\n // A tree mid-reload reports what it could walk; the panel degrades by\n // showing fewer servers rather than failing the page.\n }\n return servers;\n}\n\nfunction entryFor(ctx: HostServices, server: string): LoaderEntryLike | undefined {\n const loader = ctx.loader;\n if (loader === undefined) return undefined;\n try {\n for (const entry of loader.entries()) {\n if (declaredServerName(entry) === server) return entry;\n }\n } catch {\n return undefined;\n }\n return undefined;\n}\n\n/**\n * Reload one declared MCP server's plugin instance: dispose its fiber, then\n * refresh the entry — the loader's own hot-swap pair. This is exactly a\n * manual HMR reload: it tears down the current connection (and stops its\n * reconnect timer) and re-initializes the client, which reconnects and re-syncs\n * the whole tool generation.\n *\n * Honest scope of what this guarantees: only that a fresh connection attempt\n * starts NOW instead of waiting out the client's backoff. It does NOT prove\n * the server was \"down\" (the panel cannot observe connection state), and for a\n * stdio transport the reload respawns the child process. Registration lands\n * asynchronously after refresh resolves; the registry's `tools/change`\n * broadcast re-applies stored defaults, so callers need no readiness wait.\n *\n * Assumes a globally unique serverName: the settings view is host-global, and\n * dsh reserves one serverName per global mcp-client instance, so at most one\n * entry matches here. Agent-scoped instances may reuse a name across Agents,\n * but those are not reachable from this global walk.\n */\nexport async function restartMcpServer(ctx: HostServices, server: string): Promise<void> {\n const entry = entryFor(ctx, server);\n if (entry === undefined) throw new HttpError(404, `MCP server \"${server}\" is not configured on this host`);\n if (entry.disabled === true) {\n throw new HttpError(409, `MCP server \"${server}\" is disabled in the host composition; enable it there instead of reloading it`);\n }\n await entry._dispose();\n await entry.refresh();\n}\n","import type { InspectorPayload, McpServerEntry, McpToolEntry, SkillEntry, ToolEntry } from '../contract.js';\nimport { collectLoadRecords, decideStates, groupMcpTools, indexToolResultSeqs, prunedLoadSeqs, shadowedLoadSeqs } from '../load-state.js';\nimport type { RawEvent } from '../load-state.js';\nimport type { AgentLike, HostServices, SessionCapabilityState } from './types.js';\nimport { RESERVED_TOOL } from './reserved.js';\nimport { readConfiguredMcpServers } from './mcp-connections.js';\n\n/** Coerce a skill summary field to a string, falling back when the runtime shape is wrong. */\nexport function coerceString(value: unknown, fallback: string): string {\n return typeof value === 'string' ? value : fallback;\n}\n\n/**\n * The source ROOT a skill was discovered under. The skills service reports\n * each skill's own directory as resourceBase (`<root>/<name>`), so the group\n * folder is its parent.\n */\nexport function parentDir(path: string): string {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '') return '/';\n const slash = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'));\n return slash > 0 ? trimmed.slice(0, slash) : trimmed;\n}\n\n/**\n * Abbreviate a host path for display: the session cwd becomes a relative\n * path, the user's home becomes `~`. Everything else stays absolute.\n */\nexport function displayPath(path: string, cwd?: string): string {\n if (cwd !== undefined && cwd !== '' && path.startsWith(`${cwd}/`)) return path.slice(cwd.length + 1);\n const home = process.env['HOME'];\n // Require a path boundary: /home/user2 must not abbreviate against /home/user.\n if (home !== undefined && home !== '' && (path === home || path.startsWith(`${home}/`))) return `~${path.slice(home.length)}`;\n return path;\n}\n\n/** The DSH home, matching the runtime's own resolution order. */\nexport function dshHome(): string | undefined {\n return process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n}\n\n// Shared singleton for \"no switches on this session\". Never mutate it: every\n// reader treats the maps as read-only, and a write here would leak into every\n// session that has no state of its own.\nexport const EMPTY_STATE: SessionCapabilityState = {\n skills: new Map(),\n mcpServers: new Map(),\n mcpTools: new Map(),\n systemTools: new Map(),\n userToggled: new Set(),\n};\n\nasync function readAvailable(\n services: HostServices,\n sessionId: string,\n degraded: string[],\n presetDirs: readonly { key: string; path: string }[] = [],\n): Promise<{ name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[]> {\n const skills = services.get('skills');\n if (skills === undefined) {\n degraded.push('skills service unavailable');\n return [];\n }\n try {\n const agents = services.get('agents');\n if (agents === undefined) {\n degraded.push('agents service unavailable: session skill view cannot be determined');\n return [];\n }\n const agent = agents.get(sessionId);\n if (agent === undefined) {\n degraded.push(`session agent \"${sessionId}\" unavailable: session skill view cannot be determined`);\n return [];\n }\n const cwd = agent.session?.header?.cwd;\n const list = await skills.list({ ...(cwd === undefined ? {} : { cwd }), scope: agent });\n // A masked skill is listed twice: the original entry and the same-name\n // shadow that withdrew model invocation. Both this panel's own switches and\n // the preset panel's produce such a shadow, so a shadow is recorded as\n // \"off\" rather than skipped -- skipping it made a preset-disabled skill\n // vanish from this panel entirely, leaving the user unable to see it, and\n // unable to switch it back on. Keeping the first entry per name preserves\n // the richer original description.\n const out: { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[] = [];\n const seen = new Map<string, { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }>();\n // A custom dir that lives inside a preset's own directory is that\n // preset's bundled skills (shipped presets register their skills/ via\n // customSkillDirs). Grouping is display-only; `source` stays the raw\n // runtime value so the open-folder route can still resolve by it.\n const groupFor = (source: string, rawRoot: string): string | undefined => {\n if (source !== 'custom' || rawRoot === '') return undefined;\n const owner = presetDirs.find((p) => rawRoot === p.path || rawRoot.startsWith(`${p.path}/`));\n return owner === undefined ? undefined : `preset:${owner.key}`;\n };\n for (const item of list) {\n if (typeof item.name !== 'string' || item.name === '') continue;\n const masked = item.invocation?.modelInvocable === false;\n const source = coerceString(item.source, 'unknown');\n const provider = coerceString(item.provider, 'unknown');\n // resourceBase carries the skill's own directory — the group folder is\n // its parent, abbreviated for display (~/…, cwd-relative).\n const base = item.resourceBase;\n const rawPath = base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory'\n ? coerceString((base as { path?: unknown }).path, '')\n : '';\n const rawRoot = rawPath === '' ? '' : parentDir(rawPath);\n const path = rawRoot === '' ? '' : displayPath(rawRoot, cwd);\n const group = groupFor(source, rawRoot);\n const existing = seen.get(item.name);\n if (existing !== undefined) {\n if (masked) existing.masked = true;\n // The shadow registered by the panel has provider 'capability-panel'.\n // When the shadow appears before the original in the listing, keep\n // the original's meaningful provenance.\n if (existing.provider === 'capability-panel' && provider !== 'capability-panel') {\n existing.source = source;\n existing.provider = provider;\n if (path !== '') existing.path = path;\n if (group !== undefined) existing.group = group;\n }\n continue;\n }\n const description = typeof item.description === 'string' ? item.description : undefined;\n const row = {\n name: item.name,\n ...(description === undefined ? {} : { description }),\n ...(masked ? { masked: true } : {}),\n source,\n provider,\n ...(path === '' ? {} : { path }),\n ...(group === undefined ? {} : { group }),\n };\n seen.set(item.name, row);\n out.push(row);\n }\n return out;\n } catch (error) {\n degraded.push(`skills read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\n/**\n * Read load facts off the LIVE session's in-memory log — the same object the\n * agent loop itself reads to assemble the next request.\n *\n * This used to go through `sessionQuery.readSession` + `listEvents`, which are\n * built for cross-session/cold reads: each call structuredClones the ENTIRE\n * event log, `readSession` additionally replay-validates it via Session.create,\n * and a write landing between the two parallel reads forced a whole second\n * round. On a long session (60k events / tens of MB) that meant four full-log\n * clones plus two full replays of synchronous CPU work on every panel open —\n * blocking the Node event loop and freezing the GUI served by the same process.\n *\n * The live session needs none of that: `snapshotEvents()` hands back borrowed\n * references (zero-copy), and `surface.nodes` is maintained incrementally as\n * events land, so \"what the model sees right now\" is an O(1) membership test.\n * Scanning references for the few `skill` tool calls costs microseconds even\n * on the longest log. Both reads happen in one synchronous tick, so the view\n * is always self-consistent — no cross-read race, no retry loop.\n *\n * A session without a live in-memory view (e.g. restored but not yet attached)\n * degrades honestly instead of paying for a cold-log read the panel never\n * asked for.\n */\nfunction readLogFacts(\n services: HostServices,\n sessionId: string,\n degraded: string[],\n): { loads: ReturnType<typeof collectLoadRecords>; shadowed: Set<number>; pruned: Set<number> } {\n const empty = { loads: [], shadowed: new Set<number>(), pruned: new Set<number>() };\n try {\n const session = services.get('agents')?.get(sessionId)?.session;\n const nodes = session?.surface?.nodes;\n if (session === undefined || typeof session.snapshotEvents !== 'function' || !Array.isArray(nodes)) {\n degraded.push('live session view unavailable: load states cannot be determined');\n return empty;\n }\n // snapshotEvents must stay BOUND to the session: the real Session's\n // signature is snapshotEvents(fromSeq = 0, toSeqExclusive = this.seq),\n // so a detached call crashes on `this.seq`.\n const events = session.snapshotEvents();\n // A wrong shape must surface as degraded, never read as \"no loads\".\n if (!Array.isArray(events)) {\n degraded.push('snapshotEvents() returned an unexpected shape; cannot read skill loads');\n return empty;\n }\n const surfaceSeqs = new Set<number>();\n for (const seq of nodes) if (typeof seq === 'number') surfaceSeqs.add(seq);\n const loads = collectLoadRecords(events as readonly RawEvent[]);\n const resultSeqs = indexToolResultSeqs(events);\n return {\n loads,\n shadowed: shadowedLoadSeqs(loads, resultSeqs, surfaceSeqs),\n pruned: prunedLoadSeqs(loads, resultSeqs, surfaceSeqs, events),\n };\n } catch (error) {\n degraded.push(`event read failed: ${error instanceof Error ? error.message : String(error)}`);\n return empty;\n }\n}\n\nexport function readMcp(\n services: HostServices,\n degraded: string[],\n disabledServers: ReadonlySet<string>,\n disabledTools: ReadonlySet<string>,\n agent?: AgentLike,\n presetName?: string,\n presetPath?: string,\n maskedServerNames?: ReadonlyMap<string, readonly string[]>,\n): McpServerEntry[] {\n const tools = services.get('tools');\n if (tools === undefined) {\n degraded.push('tools service unavailable');\n return [];\n }\n try {\n const names: string[] = [];\n const descriptions = new Map<string, string>();\n const collect = (scope: AgentLike | undefined): void => {\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || !schema.name.startsWith('mcp__')) continue;\n if (!names.includes(schema.name)) {\n names.push(schema.name);\n if (typeof schema.description === 'string' && schema.description !== '') descriptions.set(schema.name, schema.description);\n }\n }\n };\n collect(undefined);\n if (agent !== undefined) collect(agent);\n const globalNames = new Set<string>();\n for (const schema of tools.schemas()) {\n if (typeof schema.name === 'string' && schema.name.startsWith('mcp__')) globalNames.add(schema.name);\n }\n const configuredServers = readConfiguredMcpServers(services);\n const groups: McpServerEntry[] = groupMcpTools(names).map((group) => {\n const enabled = !disabledServers.has(group.server);\n const entries: McpToolEntry[] = group.tools.map((tool) => {\n const name = `mcp__${group.server}__${tool}`;\n const description = descriptions.get(name);\n return {\n name,\n label: tool,\n ...(description === undefined ? {} : { description }),\n enabled: enabled && !disabledTools.has(name),\n };\n });\n const allGlobal = group.tools.every((tool) => globalNames.has(`mcp__${group.server}__${tool}`));\n const source = allGlobal ? 'host' : (presetName ?? 'preset');\n const rawPath = allGlobal ? dshHome() : presetPath;\n return { server: group.server, tools: entries, enabled, ...(configuredServers.has(group.server) ? { reconnectable: true } : {}), source, ...(rawPath === undefined ? {} : { path: displayPath(rawPath) }) };\n });\n // A declared server that currently registers no tools would vanish with\n // every mask the session holds for it. The row stays: marked \"no tools\n // registered\" (a proven fact — NOT a claim the service is down, which the\n // panel cannot observe), listing exactly the names this session already\n // stores off (the server mask's recorded roster first, then per-tool names).\n const hostPath = dshHome();\n for (const server of configuredServers) {\n if (groups.some((group) => group.server === server)) continue;\n const prefix = `mcp__${server}__`;\n const roster = maskedServerNames?.get(server) ?? [...disabledTools].filter((name) => name.startsWith(prefix)).sort();\n groups.push({\n server,\n tools: roster.map((name) => ({ name, label: name.slice(prefix.length), enabled: false })),\n enabled: false,\n unavailable: true,\n reconnectable: true,\n source: 'host',\n ...(hostPath === undefined ? {} : { path: displayPath(hostPath) }),\n });\n }\n groups.sort((a, b) => a.server.localeCompare(b.server));\n return groups;\n } catch (error) {\n degraded.push(`tool read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\nexport function readSystemTools(\n services: HostServices,\n degraded: string[],\n disabledTools: ReadonlySet<string>,\n agent?: AgentLike,\n): ToolEntry[] {\n const tools = services.get('tools');\n if (tools === undefined) {\n // Each reader owns its diagnostic, while the combined payload emits it once.\n if (!degraded.includes('tools service unavailable')) degraded.push('tools service unavailable');\n return [];\n }\n try {\n // What the agent can actually call right now. A tool the preset denied is\n // absent here while still present globally, and the panel has to tell\n // those apart: the row stays listed, because the session may switch it\n // back on, but reporting it as enabled would claim the model can reach\n // something it cannot.\n let reachable: Set<string> | undefined;\n if (agent !== undefined) {\n reachable = new Set<string>();\n for (const schema of tools.schemas(agent)) {\n if (typeof schema.name === 'string') reachable.add(schema.name);\n }\n }\n const byName = new Map<string, ToolEntry>();\n const collect = (scope: AgentLike | undefined): void => {\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || schema.name.startsWith('mcp__') || byName.has(schema.name)) continue;\n const description = typeof schema.description === 'string' && schema.description !== '' ? schema.description : undefined;\n byName.set(schema.name, {\n name: schema.name,\n label: schema.name,\n ...(description === undefined ? {} : { description }),\n enabled:\n !disabledTools.has(schema.name) && (reachable === undefined || reachable.has(schema.name)),\n ...(schema.name === RESERVED_TOOL ? { reserved: true } : {}),\n });\n }\n };\n collect(undefined);\n if (agent !== undefined) collect(agent);\n return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));\n } catch (error) {\n degraded.push(`tool read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\nexport async function buildPayload(\n services: HostServices,\n sessionId: string | null,\n capabilityState: SessionCapabilityState = EMPTY_STATE,\n blocked: Record<string, number> = {},\n): Promise<InspectorPayload> {\n const degraded: string[] = [];\n const disabledSkills = new Set(capabilityState.skills.keys());\n const disabledServers = new Set(capabilityState.mcpServers.keys());\n const disabledTools = new Set(capabilityState.mcpTools.keys());\n const disabledSystem = new Set(capabilityState.systemTools.keys());\n if (sessionId === null) {\n return {\n sessionId: null,\n skills: [],\n mcp: readMcp(services, degraded, disabledServers, disabledTools),\n systemTools: readSystemTools(services, degraded, disabledSystem),\n blocked,\n ...(degraded.length > 0 ? { degraded } : {}),\n };\n }\n const agent = services.get('agents')?.get(sessionId);\n let presetName: string | undefined;\n let presetPath: string | undefined;\n let presetDirs: { key: string; path: string }[] = [];\n if (agent !== undefined) {\n const presetId = services.get('agentPresets')?.composedPreset(agent.ctx);\n try {\n const presets = await services.get('agentPresets')?.list();\n presetDirs = (presets ?? [])\n .filter((p) => typeof p.path === 'string' && p.path !== '')\n .map((p) => ({ key: p.name ?? p.id, path: p.path as string }));\n if (presetId !== undefined) {\n const preset = presets?.find((p) => p.id === presetId);\n presetName = preset?.name ?? presetId;\n presetPath = preset?.path;\n }\n } catch {\n presetName = presetId;\n }\n }\n const available = await readAvailable(services, sessionId, degraded, presetDirs);\n const logFacts = readLogFacts(services, sessionId, degraded);\n const skills: SkillEntry[] = decideStates(available, logFacts.loads, logFacts.shadowed, disabledSkills, logFacts.pruned);\n return {\n sessionId,\n skills,\n mcp: readMcp(\n services,\n degraded,\n disabledServers,\n disabledTools,\n agent,\n presetName,\n presetPath,\n new Map([...capabilityState.mcpServers].map(([server, mask]) => [server, mask.names])),\n ),\n systemTools: readSystemTools(services, degraded, disabledSystem, agent),\n blocked,\n ...(degraded.length > 0 ? { degraded } : {}),\n };\n}\n","import { errorMessage, HttpError } from './errors.js';\nimport { displayPath, dshHome, parentDir } from './catalog.js';\nimport { readConfiguredMcpServers } from './mcp-connections.js';\nimport type {\n AgentPresetLike,\n AgentPresetsService,\n HostServices,\n PresetMcpServer,\n PresetSkillRow,\n PresetToolEntry,\n PresetToolPayload,\n PresetToolRow,\n SkillsService,\n ToolsService,\n ToolkitSettings,\n} from './types.js';\nimport type { ToolkitSettingsAccess } from './settings-scope.js';\nimport { groupMcpTools } from '../load-state.js';\nimport { RESERVED_TOOL } from './reserved.js';\nexport { RESERVED_TOOL };\n\nfunction requireService<T>(service: T | undefined, message: string): T {\n if (service === undefined) throw new HttpError(503, message);\n return service;\n}\n\ninterface ToolSummary {\n readonly name: string;\n readonly description?: string;\n}\n\nfunction toolSummaries(tools: ToolsService, scope: unknown): ToolSummary[] {\n const entries = new Map<string, ToolSummary>();\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || schema.name === '' || entries.has(schema.name)) continue;\n entries.set(schema.name, {\n name: schema.name,\n ...(typeof schema.description === 'string' ? { description: schema.description } : {}),\n });\n }\n return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Read the skills one preset can see, marking those that came from the reading\n * process's project root.\n *\n * The panel has no agent and therefore no session cwd, so it reports what THIS\n * workspace would contribute. A project skill is marked rather than dropped:\n * hiding it would silently shorten the list, while marking it says plainly\n * that a session opened elsewhere will not see the row.\n *\n * Each row also carries the same provenance the session payload reports:\n * the raw runtime source, the discovery root (parent of the skill's own\n * directory, abbreviated for display), and a `preset:<name>` group when the\n * root sits inside a preset's directory.\n */\nasync function presetSkillRows(\n skills: SkillsService,\n scope: unknown,\n disabled: ReadonlySet<string>,\n cwd: string,\n presetDirs: readonly { key: string; path: string }[] = [],\n): Promise<PresetSkillRow[]> {\n const seen = new Map<string, PresetSkillRow>();\n const add = (summaries: readonly { name?: unknown; description?: unknown; source?: unknown; resourceBase?: unknown }[], project: boolean): void => {\n for (const summary of summaries) {\n if (typeof summary.name !== 'string' || summary.name === '' || seen.has(summary.name)) continue;\n const description = typeof summary.description === 'string' ? summary.description : undefined;\n const source = typeof summary.source === 'string' ? summary.source : undefined;\n const base = summary.resourceBase;\n const rawPath = base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory'\n ? (base as { path?: unknown }).path\n : undefined;\n const rawRoot = typeof rawPath === 'string' && rawPath !== '' ? parentDir(rawPath) : '';\n const path = rawRoot === '' ? undefined : displayPath(rawRoot, cwd);\n // A custom dir that lives inside a preset's own directory is that\n // preset's bundled skills — the settings page groups them under the\n // preset's name, exactly like the session panel.\n const owner = source === 'custom' && rawRoot !== ''\n ? presetDirs.find((p) => rawRoot === p.path || rawRoot.startsWith(`${p.path}/`))\n : undefined;\n seen.set(summary.name, {\n name: summary.name,\n ...(description === undefined ? {} : { description }),\n enabled: !disabled.has(summary.name),\n ...(project ? { project: true } : {}),\n ...(source === undefined ? {} : { source }),\n ...(path === undefined ? {} : { path }),\n ...(owner === undefined ? {} : { group: `preset:${owner.key}` }),\n });\n }\n };\n // Without cwd first: anything the workspace-aware read adds on top of this\n // set is exactly what the project root contributed.\n add(await skills.list({ scope }), false);\n add(await skills.list({ scope, cwd }), true);\n return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nasync function presetAndTools(\n agentPresets: AgentPresetsService,\n tools: ToolsService,\n presetId: string,\n): Promise<{ preset: AgentPresetLike; tools: ToolSummary[] }> {\n const presets = await agentPresets.list();\n const preset = presets.find((entry) => entry.id === presetId);\n if (preset === undefined) throw new HttpError(404, `preset \"${presetId}\" is not available`);\n if (preset.broken !== undefined) throw new HttpError(409, `preset \"${presetId}\" is broken: ${preset.broken}`);\n try {\n const scope = await agentPresets.standingKeyFor(presetId);\n return { preset, tools: toolSummaries(tools, scope) };\n } catch (error) {\n throw new HttpError(503, `preset \"${presetId}\" tools are unavailable: ${errorMessage(error)}`);\n }\n}\n\nexport interface PresetToolController {\n list(): Promise<PresetToolPayload>;\n set(presetId: string, name: string, enabled: boolean): Promise<PresetToolPayload>;\n /** Toggle every tool of one MCP server in a single write. */\n setServer(presetId: string, server: string, enabled: boolean): Promise<PresetToolPayload>;\n setSkill(presetId: string, name: string, enabled: boolean): Promise<PresetToolPayload>;\n /**\n * The stored defaults for one preset, for the enforcement listener. Returns\n * undefined when settings cannot be read: enforcement treats that as \"no\n * defaults\" rather than fail a session over a preference it cannot read.\n */\n defaultsFor(presetId: string): { tools: readonly string[]; skills: readonly string[] } | undefined;\n}\n\nexport function createPresetToolController(ctx: HostServices, access: ToolkitSettingsAccess): PresetToolController {\n const settingsScope = (): ReturnType<ToolkitSettingsAccess['scope']> => access.scope();\n\n const services = (): { agentPresets: AgentPresetsService; tools: ToolsService; settings: NonNullable<ReturnType<ToolkitSettingsAccess['scope']>> } => ({\n agentPresets: requireService(ctx.get('agentPresets'), 'agentPresets service unavailable'),\n tools: requireService(ctx.get('tools'), 'tools service unavailable'),\n settings: requireService(settingsScope(), 'settings service unavailable'),\n });\n\n const list = async (): Promise<PresetToolPayload> => {\n const { agentPresets, tools, settings: settingsScope } = services();\n const stored = settingsScope.get();\n const configured = stored.presets;\n const skills = ctx.get('skills');\n // The reading process's workspace: the panel has no session, so this is\n // the only project root it can honestly report against.\n const cwd = process.cwd();\n const presets = await agentPresets.list();\n const presetDirs = presets\n .filter((p) => typeof p.path === 'string' && p.path !== '')\n .map((p) => ({ key: p.name ?? p.id, path: p.path as string }));\n // Global tool names tell a host-composition MCP server from a\n // preset-scoped one, the same test the session payload uses.\n const configuredServers = readConfiguredMcpServers(ctx);\n const globalNames = new Set<string>();\n for (const schema of tools.schemas()) {\n if (typeof schema.name === 'string' && schema.name.startsWith('mcp__')) globalNames.add(schema.name);\n }\n const entries: PresetToolEntry[] = await Promise.all(presets.map(async (preset) => {\n let entries: ToolSummary[] = [];\n let skillRows: PresetSkillRow[] = [];\n // A preset that fails to MOUNT (valid yaml, but a row's config no longer\n // satisfies a plugin's schema after a host upgrade) must not take the\n // whole listing down with it: report it like a broken preset, with the\n // mount error as the reason, and let the others list normally.\n let mountError: string | undefined;\n if (preset.broken === undefined) {\n let scope: unknown;\n try {\n scope = await agentPresets.standingKeyFor(preset.id);\n entries = toolSummaries(tools, scope);\n if (skills !== undefined) {\n const disabledSkills = new Set(stored.presetSkills[preset.id] ?? []);\n skillRows = await presetSkillRows(skills, scope, disabledSkills, cwd, presetDirs);\n }\n } catch (error) {\n mountError = errorMessage(error);\n }\n }\n const disabled = new Set(configured[preset.id] ?? []);\n const byName = new Map(entries.map((entry) => [entry.name, entry]));\n const row = (name: string, label: string): PresetToolRow => {\n const description = byName.get(name)?.description;\n return {\n name,\n label,\n ...(description === undefined ? {} : { description }),\n // The reserved transport is reported as on because it IS on: the\n // enforcement listener drops it from the deny list unconditionally.\n // A stale entry naming it (hand-edited, or written before it became\n // reserved) must not render a switch that says \"off\" over a tool the\n // model can still call -- especially since that switch is locked and\n // the user could never clear the claim.\n enabled: name === RESERVED_TOOL || !disabled.has(name),\n ...(name === RESERVED_TOOL ? { reserved: true } : {}),\n };\n };\n // Same split the session panel uses, from the same helper: MCP tools\n // collapse under their server, everything else is a system tool.\n const presetName = preset.name ?? preset.id;\n const mcp: PresetMcpServer[] = groupMcpTools(entries.map((entry) => entry.name)).map((group) => {\n const tools = group.tools.map((tool) => row(`mcp__${group.server}__${tool}`, tool));\n const allGlobal = group.tools.every((tool) => globalNames.has(`mcp__${group.server}__${tool}`));\n const rawPath = allGlobal ? dshHome() : preset.path;\n return {\n server: group.server,\n tools,\n enabled: tools.some((tool) => tool.enabled),\n ...(configuredServers.has(group.server) ? { reconnectable: true } : {}),\n source: allGlobal ? 'host' : presetName,\n ...(rawPath === undefined ? {} : { path: displayPath(rawPath) }),\n };\n });\n // A declared server that currently registers no tools would otherwise\n // vanish with its stored default. The row stays: dimmed, marked \"no\n // tools registered\" (a proven fact — NOT a claim that the service is\n // down, which the panel cannot observe), listing exactly the names this\n // preset already stores off for it.\n const hostPath = dshHome();\n for (const server of configuredServers) {\n if (mcp.some((group) => group.server === server)) continue;\n const prefix = `mcp__${server}__`;\n mcp.push({\n server,\n tools: [...disabled]\n .filter((name) => name.startsWith(prefix))\n .sort()\n .map((name) => row(name, name.slice(prefix.length))),\n enabled: false,\n unavailable: true,\n reconnectable: true,\n source: 'host',\n ...(hostPath === undefined ? {} : { path: displayPath(hostPath) }),\n });\n }\n // Declared servers sort in among the connected ones: the list is an\n // inventory of what this preset can configure, not of what is up.\n mcp.sort((a, b) => a.server.localeCompare(b.server));\n const systemTools = entries\n .filter((entry) => !entry.name.startsWith('mcp__'))\n .map((entry) => row(entry.name, entry.name));\n return {\n id: preset.id,\n name: preset.name ?? preset.id,\n trust: preset.trust,\n ...(preset.description === undefined ? {} : { description: preset.description }),\n ...(preset.broken !== undefined\n ? { broken: preset.broken }\n : mountError === undefined\n ? {}\n : { broken: `failed to mount: ${mountError}` }),\n skills: skillRows,\n mcp,\n systemTools,\n };\n }));\n return { presets: entries, writable: ctx.get('settings')?.writable === true };\n };\n\n // Every write is read-modify-write over the whole namespace, serialized\n // through the SHARED access queue (settings-scope.ts) so a preset edit and\n // a session toggle cannot interleave their snapshots.\n\n /** Persist one preset's disabled set for one registry, then re-read. */\n const persist = (\n presetId: string,\n settingsScope: NonNullable<ReturnType<ToolkitSettingsAccess['scope']>>,\n disabled: ReadonlySet<string>,\n registry: 'presets' | 'presetSkills' = 'presets',\n ): Promise<PresetToolPayload> => access.serialize(async () => {\n const stored = settingsScope.get();\n // All maps are always present: each carries a schema default, so a section\n // written before skills/sessions existed still resolves with empty ones\n // rather than missing keys. The compatibility layer is the schema, not a\n // guard here -- an optional read would only imply a shape the settings\n // service cannot hand back.\n const prune = (map: Readonly<Record<string, readonly string[]>>): Record<string, readonly string[]> =>\n Object.fromEntries(Object.entries(map).filter(([, value]) => value.length > 0));\n const next = prune({ ...stored[registry], [presetId]: [...disabled].sort() });\n const presets = registry === 'presets' ? next : prune(stored.presets);\n const presetSkills = registry === 'presetSkills' ? next : prune(stored.presetSkills);\n // `replace`, not `update`: the settings merge recurses into the stored\n // section, so a merge patch can only ever add or grow keys. Re-enabling a\n // preset's last disabled tool has to REMOVE its key, and only a wholesale\n // replace of this namespace's user section can express that. `sessions`\n // passes through untouched: session-bound overrides belong to the other\n // writer sharing this namespace, and dropping them here would silently\n // unpersist every session's switches.\n await settingsScope.replace({ presets, presetSkills, sessions: stored.sessions });\n return list();\n });\n\n return {\n defaultsFor(presetId) {\n let stored: ToolkitSettings;\n try {\n const settings = settingsScope();\n if (settings === undefined) return undefined;\n stored = settings.get();\n } catch {\n return undefined;\n }\n const tools = (stored.presets[presetId] ?? []).filter((name) => name !== RESERVED_TOOL);\n return { tools, skills: stored.presetSkills[presetId] ?? [] };\n },\n list,\n async set(presetId, name, enabled) {\n if (name === RESERVED_TOOL && !enabled) {\n throw new HttpError(409, 'run_code is the reserved Code Mode transport and cannot be restricted');\n }\n const { agentPresets, tools, settings: settingsScope } = services();\n const { tools: available } = await presetAndTools(agentPresets, tools, presetId);\n if (!available.some((tool) => tool.name === name)) throw new HttpError(404, `tool \"${name}\" is not available in preset \"${presetId}\"`);\n const disabled = new Set(settingsScope.get().presets[presetId] ?? []);\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n return persist(presetId, settingsScope, disabled);\n },\n async setServer(presetId, server, enabled) {\n const { agentPresets, tools, settings: settingsScope } = services();\n const { tools: available } = await presetAndTools(agentPresets, tools, presetId);\n // One write for the whole server: with 200 MCP tools behind two servers,\n // switching them one request at a time is the interaction this replaces.\n const prefix = `mcp__${server}__`;\n const names = available.map((tool) => tool.name).filter((name) => name.startsWith(prefix));\n if (names.length === 0) throw new HttpError(404, `MCP server \"${server}\" is not available in preset \"${presetId}\"`);\n const disabled = new Set(settingsScope.get().presets[presetId] ?? []);\n for (const name of names) {\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n }\n return persist(presetId, settingsScope, disabled);\n },\n async setSkill(presetId, name, enabled) {\n const { agentPresets, settings: settingsScope } = services();\n const skills = requireService(ctx.get('skills'), 'skills service unavailable');\n const presets = await agentPresets.list();\n const preset = presets.find((entry) => entry.id === presetId);\n if (preset === undefined) throw new HttpError(404, `preset \"${presetId}\" is not available`);\n if (preset.broken !== undefined) throw new HttpError(409, `preset \"${presetId}\" is broken: ${preset.broken}`);\n let visible: PresetSkillRow[];\n try {\n const scope = await agentPresets.standingKeyFor(presetId);\n visible = await presetSkillRows(skills, scope, new Set(), process.cwd());\n } catch (error) {\n throw new HttpError(503, `preset \"${presetId}\" skills are unavailable: ${errorMessage(error)}`);\n }\n if (!visible.some((skill) => skill.name === name)) {\n throw new HttpError(404, `skill \"${name}\" is not available in preset \"${presetId}\"`);\n }\n const disabled = new Set(settingsScope.get().presetSkills[presetId] ?? []);\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n return persist(presetId, settingsScope, disabled, 'presetSkills');\n },\n };\n}\n","/** The minimal readable surface of a request object inside a route handler. */\nexport interface IncomingLike {\n readonly url?: string;\n readonly method?: string;\n readonly headers: Record<string, string | string[] | undefined>;\n /**\n * Present on node's http.IncomingMessage. Prefer it for the loopback\n * decision: the Host and Origin headers are client-controlled and forgeable.\n */\n readonly socket?: { readonly remoteAddress?: string };\n}\n\nexport const LOOPBACK_ADDRS: ReadonlySet<string> = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1']);\n\n/**\n * Loopback-only guard.\n *\n * The connection's peer address is the only trustworthy evidence, and it is the\n * only one that still holds when the host binds to a LAN interface\n * (`--host` / `--trusted-host`). The Host and Origin headers are set by the\n * caller, so they are a fallback used only when the socket is unavailable —\n * with neither present, this fails closed.\n */\nexport function isLoopback(req: IncomingLike): boolean {\n const addr = req.socket?.remoteAddress;\n if (typeof addr === 'string') return LOOPBACK_ADDRS.has(addr);\n const originHeader = req.headers['origin'];\n const origin = typeof originHeader === 'string' ? originHeader : '';\n if (origin === '') {\n const hostHeader = req.headers['host'];\n const host = typeof hostHeader === 'string' ? hostHeader : '';\n return /^(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/.test(host);\n }\n return /^https?:\\/\\/(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/.test(origin);\n}\n","import { exec } from 'node:child_process';\n\n/**\n * Open a folder in the system file manager.\n *\n * Covers macOS (`open`), Windows (`start`), and every freedesktop Linux\n * desktop (GNOME, KDE, XFCE, …) via `xdg-open`. Server-only Linux distros\n * that lack a desktop cannot open folders, but the error is graceful.\n */\nexport function openFolder(path: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'start'\n : 'xdg-open';\n exec(`${cmd} \"${path}\"`, (error) => {\n if (error) reject(new Error(`failed to open folder: ${error.message}`));\n else resolve();\n });\n });\n}","import { isLoopback } from '../loopback.js';\nimport { buildPayload, EMPTY_STATE, parentDir } from './catalog.js';\nimport type { CapabilityController } from './capabilities.js';\nimport { errorMessage, HttpError } from './errors.js';\nimport { restartMcpServer } from './mcp-connections.js';\nimport { openFolder } from './open-folder.js';\nimport type { PresetToolController } from './preset-tools.js';\nimport type { SessionOverrideStore } from './session-overrides.js';\nimport type { StatsStore } from './stats-store.js';\nimport type { CapabilityKind, HostServices, IncomingLike, ServerResponseLike } from './types.js';\n\nexport const ROUTE = '/api/capability-panel';\nconst KINDS: readonly CapabilityKind[] = ['skill', 'mcp-server', 'mcp-tool', 'system-tool'];\n\nclass ClientRequestError extends HttpError {\n constructor(message: string, status: 400 | 413 = 400) {\n super(status, message);\n }\n}\n\nexport function readRequestBody(req: IncomingLike): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (req.on === undefined) {\n reject(new ClientRequestError('request body stream unavailable'));\n return;\n }\n let body = '';\n let settled = false;\n const fail = (error: unknown): void => {\n if (settled) return;\n settled = true;\n reject(error instanceof ClientRequestError ? error : new Error(String(error)));\n };\n req.on('data', (chunk) => {\n if (settled) return;\n body += String(chunk);\n if (body.length > 16_384) fail(new ClientRequestError('request body too large', 413));\n });\n req.on('end', () => {\n if (settled) return;\n settled = true;\n try {\n resolve(body === '' ? {} : JSON.parse(body));\n } catch {\n reject(new ClientRequestError('invalid JSON body'));\n }\n });\n req.on('error', fail);\n });\n}\n\n/**\n * Every response here reflects live process state, so `no-store` is the\n * default and opting out is explicit. The flag reads as what it does: the\n * previous spelling was `cache = true` on the ERROR branch, which left a\n * transient 503 as the only cacheable response the route produced.\n */\nfunction json(res: ServerResponseLike, status: number, body: unknown, allowCaching = false): void {\n res.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n ...(allowCaching ? {} : { 'cache-control': 'no-store' }),\n });\n res.end(JSON.stringify(body));\n}\n\n/**\n * `kind` mirrors the session route's toggle shape: one tool, or a whole MCP\n * server in a single write.\n */\nfunction validatePresetToggle(body: unknown): { presetId: string; kind: 'tool' | 'mcp-server' | 'skill'; name: string; enabled: boolean } {\n if (body === null || typeof body !== 'object') throw new ClientRequestError('invalid request body');\n const record = body as { presetId?: unknown; kind?: unknown; name?: unknown; enabled?: unknown };\n if (typeof record.presetId !== 'string' || record.presetId === '') throw new ClientRequestError('presetId is required');\n const kind = record.kind ?? 'tool';\n if (kind !== 'tool' && kind !== 'mcp-server' && kind !== 'skill') {\n throw new ClientRequestError('kind must be \"tool\", \"mcp-server\" or \"skill\"');\n }\n if (typeof record.name !== 'string' || record.name === '') throw new ClientRequestError('name is required');\n if (typeof record.enabled !== 'boolean') throw new ClientRequestError('enabled must be boolean');\n return { presetId: record.presetId, kind, name: record.name, enabled: record.enabled };\n}\n\nfunction validatePresetContentType(req: IncomingLike, res: ServerResponseLike): boolean {\n const contentType = req.headers['content-type'];\n if (typeof contentType === 'string' && contentType.startsWith('application/json')) return true;\n res.writeHead(415, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('expected application/json');\n return false;\n}\n\nfunction validateToggle(sessionId: string | null, body: unknown): { sessionId: string; kind: CapabilityKind; name: string; enabled: boolean } {\n if (sessionId === null) throw new ClientRequestError('session is required');\n if (body === null || typeof body !== 'object') throw new ClientRequestError('invalid request body');\n const record = body as { kind?: unknown; name?: unknown; enabled?: unknown };\n if (typeof record.kind !== 'string' || !KINDS.includes(record.kind as CapabilityKind) || typeof record.enabled !== 'boolean') {\n throw new ClientRequestError('kind must be skill, mcp-server, mcp-tool or system-tool and enabled must be boolean');\n }\n if (typeof record.name !== 'string' || record.name === '') throw new ClientRequestError('name is required');\n return { sessionId, kind: record.kind as CapabilityKind, name: record.name, enabled: record.enabled };\n}\n\nexport function createRouteHandler(\n services: HostServices,\n capabilities: CapabilityController,\n stats: StatsStore,\n blockedCounts: Record<string, number>,\n presetTools: PresetToolController,\n sessionOverrides: SessionOverrideStore,\n): (req: IncomingLike, res: ServerResponseLike) => Promise<void> {\n return async (req, res) => {\n if (!isLoopback(req)) {\n res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('forbidden');\n return;\n }\n try {\n const url = new URL(req.url ?? '/', 'http://dsh.local');\n if (url.pathname === `${ROUTE}/presets`) {\n if (req.method !== 'GET' && req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (req.method === 'POST') {\n if (!validatePresetContentType(req, res)) return;\n const toggle = validatePresetToggle(await readRequestBody(req));\n json(res, 200, toggle.kind === 'skill'\n ? await presetTools.setSkill(toggle.presetId, toggle.name, toggle.enabled)\n : toggle.kind === 'mcp-server'\n ? await presetTools.setServer(toggle.presetId, toggle.name, toggle.enabled)\n : await presetTools.set(toggle.presetId, toggle.name, toggle.enabled));\n return;\n }\n json(res, 200, await presetTools.list());\n return;\n }\n if (url.pathname === `${ROUTE}/stats`) {\n if (req.method !== 'GET') {\n res.writeHead(405, { allow: 'GET', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n const snapshot = stats.read();\n json(res, 200, { logFile: stats.file, blocked: blockedCounts, records: snapshot.records, ...(snapshot.warnings.length > 0 ? { warnings: snapshot.warnings } : {}) }, true);\n return;\n }\n if (url.pathname === `${ROUTE}/reconnect`) {\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (!validatePresetContentType(req, res)) return;\n const body = await readRequestBody(req);\n if (body === null || typeof body !== 'object') {\n json(res, 400, { error: 'invalid request body' });\n return;\n }\n const record = body as { server?: unknown };\n if (typeof record.server !== 'string' || record.server === '') {\n json(res, 400, { error: 'server is required' });\n return;\n }\n // Restart the entry; tool registration lands asynchronously and the\n // registry's tools/change broadcast re-applies stored defaults.\n await restartMcpServer(services, record.server);\n json(res, 200, { ok: true, server: record.server });\n return;\n }\n if (url.pathname === `${ROUTE}/open-folder`) {\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (!validatePresetContentType(req, res)) return;\n const body = await readRequestBody(req);\n if (body === null || typeof body !== 'object') {\n json(res, 400, { error: 'invalid request body' });\n return;\n }\n const record = body as { sessionId?: unknown; source?: unknown };\n if (typeof record.source !== 'string' || record.source === '') {\n json(res, 400, { error: 'source is required' });\n return;\n }\n const source = record.source;\n const sessionId = typeof record.sessionId === 'string' ? record.sessionId : null;\n let folderPath: string | undefined;\n // Authoritative path first: re-list the session's skills and take the\n // resourceBase directory of the first entry carrying this source. This\n // covers every source the filesystem provider knows — including\n // 'custom' (customSkillDirs) — without guessing paths.\n if (sessionId !== null) {\n try {\n const skills = services.get('skills');\n const agent = services.get('agents')?.get(sessionId);\n if (skills !== undefined && agent !== undefined) {\n const cwd = agent.session?.header?.cwd;\n const list = await skills.list({ ...(cwd === undefined ? {} : { cwd }), scope: agent });\n for (const item of list) {\n if (item.source !== source) continue;\n const base = item.resourceBase;\n if (base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory') {\n const path = (base as { path?: unknown }).path;\n if (typeof path === 'string' && path !== '') {\n // resourceBase is the skill's own folder; the group folder\n // is its parent (the source root).\n folderPath = parentDir(path);\n break;\n }\n }\n }\n }\n } catch { /* listing failed — fall through to the heuristic */ }\n }\n // Heuristic fallbacks for sources with no live skill entry, and for\n // MCP sources (which are preset names or 'host').\n if (folderPath === undefined && (source === 'user-dsh' || source === 'user-agents')) {\n const dshHome = process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n if (dshHome !== undefined) {\n folderPath = source === 'user-dsh' ? `${dshHome}/skills` : `${process.env['DSH_AGENTS_HOME'] ?? `${process.env['HOME']}/.agents`}/skills`;\n }\n } else if (folderPath === undefined && (source === 'project-dsh' || source === 'project-agents')) {\n // Sessionless callers (the settings page) report project rows\n // against the dsh process's own cwd — resolve those the same way.\n const agent = sessionId === null ? undefined : services.get('agents')?.get(sessionId);\n const cwd = agent?.session?.header?.cwd ?? (sessionId === null ? process.cwd() : undefined);\n if (cwd !== undefined) {\n folderPath = source === 'project-dsh' ? `${cwd}/.dsh/skills` : `${cwd}/.agents/skills`;\n }\n } else if (folderPath === undefined && source === 'host') {\n // Host composition entries live under DSH_HOME — open the home.\n folderPath = process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n } else if (folderPath === undefined && source !== 'bundled' && source !== 'runtime') {\n // Anything else is a preset name — look up its path.\n try {\n const presets = await services.get('agentPresets')?.list();\n const preset = presets?.find((p) => p.id === source || p.name === source);\n if (preset !== undefined) folderPath = preset.path;\n } catch { /* preset lookup failed — leave folderPath undefined */ }\n }\n if (folderPath === undefined) {\n json(res, 404, { error: `cannot open folder for source \"${source}\"` });\n return;\n }\n try {\n await openFolder(folderPath);\n json(res, 200, { ok: true });\n } catch (error) {\n json(res, 500, { error: `failed to open folder: ${errorMessage(error)}` });\n }\n return;\n }\n // The catalogue answers the prefix itself, not everything beneath it.\n // Falling through meant /api/capability-panel/bogus returned the catalogue\n // with a 200, which hides a caller's typo and would silently change\n // meaning the day a real sub-path is added under that name. A request\n // whose url is absent is the degenerate case the prefix router can hand\n // down; it reads as the root, not as an unknown path.\n if (url.pathname !== ROUTE && url.pathname !== '/') {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' });\n res.end('not found');\n return;\n }\n if (req.method !== 'GET' && req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n const sessionId = url.searchParams.get('session');\n let persistNote: string | undefined;\n if (req.method === 'POST') {\n const contentType = req.headers['content-type'];\n if (typeof contentType !== 'string' || !contentType.startsWith('application/json')) {\n res.writeHead(415, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('expected application/json');\n return;\n }\n const toggle = validateToggle(sessionId, await readRequestBody(req));\n await capabilities.set(toggle.sessionId, toggle.kind, toggle.name, toggle.enabled);\n // The mask is live; persisting it for the session's next agent is\n // best-effort. A failed write must degrade the payload, not the\n // toggle the user already made.\n try {\n await sessionOverrides.record(toggle.sessionId, toggle.kind, toggle.name, toggle.enabled);\n } catch (error) {\n persistNote = `switch applied for this session but could not be persisted across a restart: ${errorMessage(error)}`;\n }\n }\n const payload = await buildPayload(\n services,\n sessionId,\n sessionId === null ? EMPTY_STATE : (capabilities.state(sessionId) ?? EMPTY_STATE),\n blockedCounts,\n );\n json(res, 200, persistNote === undefined\n ? payload\n : { ...payload, degraded: [...(payload.degraded ?? []), persistNote] });\n } catch (error) {\n json(res, error instanceof HttpError ? error.status : 500, { error: errorMessage(error) });\n }\n };\n}\n","import { HttpError } from './errors.js';\nimport type { ToolkitSettingsAccess } from './settings-scope.js';\nimport type { CapabilityKind, SessionOverrideState } from './types.js';\n\n/**\n * Session-bound switch positions, persisted in the capability-panel settings namespace\n * so a restored session gets its own toggles back after a restart.\n *\n * The binding is the session id: a record is read only by the session that\n * made it, so one session's switch can never leak into another. What changes\n * at a restart is only WHEN it is read — the masks themselves are re-applied\n * by the same capability controller the panel uses, over the fresh agent.\n *\n * The store is a record of user intent, not a snapshot of derived state: an\n * explicit `true` matters only when a preset default masks the name, and\n * recording every toggle's final value keeps the write path dumb (no preset\n * lookup needed) while restore order (defaults first, overrides second) makes\n * the user's last word win.\n */\n\n/** Kind keys map 1:1 onto SessionOverrideState's fields. */\nconst KIND_KEYS = {\n skill: 'skills',\n 'mcp-server': 'mcpServers',\n 'mcp-tool': 'mcpTools',\n 'system-tool': 'systemTools',\n} as const;\n\n/**\n * Retained session records cap. A record costs a handful of names, and a\n * session the user never revisits is pruned oldest-first — the alternative\n * (unbounded growth in a settings file) trades a real read cost for a\n * scenario nobody has.\n */\nexport const MAX_SESSION_RECORDS = 200;\n\nexport interface SessionOverrideStore {\n /**\n * The stored positions for one session, for the agent/created listener.\n * Returns undefined when settings cannot be read: enforcement treats that\n * as \"no overrides\" rather than fail a session over a preference.\n */\n overridesFor(sessionId: string): SessionOverrideState | undefined;\n /**\n * Persist one toggle's final position for one session. Throws when the\n * settings service is absent or the write fails — the caller has already\n * applied the in-memory mask, so it reports this as a degraded note, not a\n * failed toggle.\n */\n record(sessionId: string, kind: CapabilityKind, name: string, enabled: boolean): Promise<void>;\n}\n\nexport function createSessionOverrideStore(access: ToolkitSettingsAccess): SessionOverrideStore {\n return {\n overridesFor(sessionId) {\n try {\n return access.scope()?.get().sessions[sessionId];\n } catch {\n return undefined;\n }\n },\n async record(sessionId, kind, name, enabled) {\n const scope = access.scope();\n if (scope === undefined) throw new HttpError(503, 'settings service unavailable');\n await access.serialize(async () => {\n const stored = scope.get();\n const kindKey = KIND_KEYS[kind];\n const current = stored.sessions[sessionId];\n const next: SessionOverrideState = {\n skills: { ...current?.skills },\n mcpServers: { ...current?.mcpServers },\n mcpTools: { ...current?.mcpTools },\n systemTools: { ...current?.systemTools },\n [kindKey]: { ...current?.[kindKey], [name]: enabled },\n };\n // Rebuild rather than mutate: dropping the id first and re-appending\n // it makes it the newest entry, so the cap evicts oldest-first\n // (Object.entries preserves string-key insertion order).\n const entries = Object.entries(stored.sessions).filter(([id]) => id !== sessionId);\n entries.push([sessionId, next]);\n const sessions: Record<string, SessionOverrideState> = Object.fromEntries(entries.slice(-MAX_SESSION_RECORDS));\n await scope.replace({\n presets: stored.presets,\n presetSkills: stored.presetSkills,\n sessions,\n });\n });\n },\n };\n}\n","import z from '@deepseek-ai/schemastery';\nimport type { HostServices, SettingsScopeLike, ToolkitSettings } from './types.js';\n\n// dsh-settings 0.1.2 removed the settingsNamespace() brander: register takes\n// the literal and validates it (lowercase-hyphenated) at the type level and\n// at runtime.\nexport const TOOLKIT_SETTINGS_NAMESPACE = 'capability-panel';\n\n// The schema crosses no declaration boundary: the settings service takes it\n// as `unknown`, and annotating it as such sidesteps the non-portable inferred\n// type schemastery's z.object produces under declaration emit.\nconst SessionOverrideSchema: unknown = z.object({\n skills: z.dict(z.boolean()).default({}),\n mcpServers: z.dict(z.boolean()).default({}),\n mcpTools: z.dict(z.boolean()).default({}),\n systemTools: z.dict(z.boolean()).default({}),\n});\n\nexport const ToolkitSettingsSchema: unknown = z.object({\n presets: z.dict(z.array(z.string())).default({}),\n presetSkills: z.dict(z.array(z.string())).default({}),\n sessions: z.dict(SessionOverrideSchema as never).default({}),\n});\n\nexport interface ToolkitSettingsAccess {\n /**\n * Lazily registered on first read, not at apply time: this row does not\n * `inject` settings, so at composition the service may not be published yet.\n * Binding it eagerly would freeze an early `undefined` into a permanent 503\n * even after settings arrives.\n */\n scope(): SettingsScopeLike<ToolkitSettings> | undefined;\n /**\n * One write queue for the whole namespace. Every writer (preset defaults,\n * session overrides) persists via a read-modify-`replace` of the same user\n * section, and the scope exposes no revision to write against — so two\n * writers racing would each persist a section computed from the same\n * pre-read snapshot and silently undo each other. Serializing read+write as\n * one critical section is what keeps a session toggle from wiping a preset\n * edit made in another tab.\n */\n serialize<T>(work: () => Promise<T>): Promise<T>;\n}\n\nexport function createToolkitSettingsAccess(ctx: HostServices): ToolkitSettingsAccess {\n let scope: SettingsScopeLike<ToolkitSettings> | undefined;\n let writeQueue: Promise<unknown> = Promise.resolve();\n return {\n scope() {\n if (scope === undefined) {\n scope = ctx.get('settings')?.register<ToolkitSettings>(\n TOOLKIT_SETTINGS_NAMESPACE,\n ToolkitSettingsSchema,\n { applies: 'live' },\n );\n }\n return scope;\n },\n serialize(work) {\n const next = writeQueue.then(work, work);\n writeQueue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n },\n };\n}\n","import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { aggregateBlocked } from '../stats.js';\nimport type { StatsRecord } from '../stats.js';\n\nexport interface StatsSnapshot {\n readonly records: StatsRecord[];\n readonly blocked: Record<string, number>;\n readonly warnings: string[];\n}\n\nexport interface StatsStore {\n readonly file: string;\n read(): StatsSnapshot;\n append(record: StatsRecord): string | null;\n}\n\nexport function statsFilePath(environment: NodeJS.ProcessEnv = process.env, home = homedir()): string {\n return join(environment['DSH_HOME'] ?? join(home, '.dsh'), 'capability-panel', 'stats.jsonl');\n}\n\nfunction isEnoent(error: unknown): boolean {\n return error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nexport function createStatsStore(file = statsFilePath()): StatsStore {\n const appendWarnings: string[] = [];\n return {\n file,\n read() {\n let text: string;\n try {\n text = readFileSync(file, 'utf8');\n } catch (error) {\n if (isEnoent(error)) return { records: [], blocked: {}, warnings: [...appendWarnings] };\n return {\n records: [],\n blocked: {},\n warnings: [`stats read failed: ${String(error)}`, ...appendWarnings],\n };\n }\n const records: StatsRecord[] = [];\n const warnings: string[] = [...appendWarnings];\n const validLines: string[] = [];\n for (const [index, line] of text.split('\\n').entries()) {\n if (line.trim() === '') continue;\n try {\n const parsed = JSON.parse(line) as StatsRecord;\n records.push(parsed);\n validLines.push(line);\n } catch (error) {\n warnings.push(`stats line ${index + 1} skipped: ${String(error)}`);\n }\n }\n return { records, blocked: aggregateBlocked(validLines), warnings };\n },\n append(record) {\n try {\n mkdirSync(dirname(file), { recursive: true });\n appendFileSync(file, `${JSON.stringify(record)}\\n`);\n return null;\n } catch (error) {\n const warning = `stats append failed: ${String(error)}`;\n appendWarnings.push(warning);\n return warning;\n }\n },\n };\n}\n","import { createCapabilityController } from './host/capabilities.js';\nimport { registerPresetEnforcement } from './host/preset-enforcement.js';\nimport { createPresetToolController } from './host/preset-tools.js';\nimport { createRouteHandler, ROUTE } from './host/route.js';\nimport { createSessionOverrideStore } from './host/session-overrides.js';\nimport { createToolkitSettingsAccess } from './host/settings-scope.js';\nimport { createStatsStore } from './host/stats-store.js';\nimport type { HostServices } from './host/types.js';\n\n/** Host composition root: construct stores/controllers and register the route. */\nexport function apply(ctx: HostServices): void {\n const webServer = ctx.webServer;\n if (webServer === undefined) return;\n\n const stats = createStatsStore();\n const blockedCounts = stats.read().blocked;\n const appendStats = (record: Parameters<typeof stats.append>[0]): void => {\n stats.append(record);\n };\n const capabilities = createCapabilityController(ctx, appendStats, blockedCounts);\n // One settings namespace, one write queue: preset defaults and session\n // overrides share both, so the two writers can never clobber each other's\n // read-modify-replace.\n const settingsAccess = createToolkitSettingsAccess(ctx);\n const presetTools = createPresetToolController(ctx, settingsAccess);\n const sessionOverrides = createSessionOverrideStore(settingsAccess);\n registerPresetEnforcement(ctx, capabilities, presetTools, sessionOverrides);\n const handler = createRouteHandler(ctx, capabilities, stats, blockedCounts, presetTools, sessionOverrides);\n\n ctx.effect(\n () => webServer.register({ kind: 'prefix', path: ROUTE, handler }),\n 'capability-panel: data route',\n );\n}\n\n// Only the route is a hard dependency. Optional services report 503 on preset requests.\nexport const inject = ['webServer'];\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["counts: Record<string, number>","record: StatsRecord","parsed: unknown","out: SkillLoadRecord[]","out: string[]","state: SkillLoadState","status: 400 | 404 | 409 | 413 | 415 | 503","lines: string[]","exec","prefix","groups: readonly (readonly [CapabilityKind, Readonly<Record<string, boolean>>])[]","EMPTY_STATE: SessionCapabilityState","out: { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[]","names: string[]","groups: McpServerEntry[]","entries: McpToolEntry[]","reachable: Set<string> | undefined","degraded: string[]","presetName: string | undefined","presetPath: string | undefined","presetDirs: { key: string; path: string }[]","settingsScope","entries: ToolSummary[]","skillRows: PresetSkillRow[]","mountError: string | undefined","scope: unknown","mcp: PresetMcpServer[]","tools","stored: ToolkitSettings","visible: PresetSkillRow[]","LOOPBACK_ADDRS: ReadonlySet<string>","KINDS: readonly CapabilityKind[]","sessionId","folderPath: string | undefined","dshHome","persistNote: string | undefined","next: SessionOverrideState","sessions: Record<string, SessionOverrideState>","SessionOverrideSchema: unknown","ToolkitSettingsSchema: unknown","scope: SettingsScopeLike<ToolkitSettings> | undefined","writeQueue: Promise<unknown>","appendWarnings: string[]","text: string","records: StatsRecord[]","warnings: string[]","validLines: string[]"],"sources":["../src/stats.ts","../src/load-state.ts","../src/host/errors.ts","../src/host/reserved.ts","../src/host/capabilities.ts","../src/host/preset-enforcement.ts","../src/host/mcp-connections.ts","../src/host/catalog.ts","../src/host/preset-tools.ts","../src/loopback.ts","../src/host/open-folder.ts","../src/host/route.ts","../src/host/session-overrides.ts","../src/host/settings-scope.ts","../src/host/stats-store.ts","../src/index.ts"],"sourcesContent":["/**\n * Usage statistics: did the agent still reach for a capability AFTER the user\n * turned it off? That answer drives the panel's optimization direction, so it\n * must be measured, not guessed.\n *\n * Detection is pure (this module has no I/O): the host feeds every\n * `tools/result` observation through {@link classifyBlockedCall} and appends\n * the returned record to a JSONL log. Two blocked shapes exist:\n *\n * - skill: the `skill` loader tool is still registered (only the catalog entry\n * is shadowed), so a blocked attempt fails INSIDE the tool with\n * \"not available for model invocation\". The skill name rides the arguments.\n * - mcp: a restricted tool is invisible, so dispatch fails BEFORE the body\n * with code UNKNOWN_TOOL. The tool name is the call name itself.\n *\n * Toggle operations are logged too (kind 'disable'/'enable') so later analysis\n * can correlate \"turned off at T\" with \"attempted at T+n\".\n */\n\n/** Stable prefix of the guard's denial text, marking a hard call to a preset-layer tool the user turned off. */\nexport const GUARD_DENIAL_PREFIX = 'capability-panel: tool disabled';\n\nexport interface StatsRecord {\n readonly ts: string;\n readonly sessionId: string | null;\n readonly kind: 'disable' | 'enable' | 'blocked-skill' | 'blocked-tool';\n /** Skill name or full tool name, by kind. */\n readonly name: string;\n readonly detail?: string;\n}\n\n/** Minimal readonly view of a settled tool call, as `tools/result` hands it over. */\nexport interface SettledCall {\n readonly name?: unknown;\n readonly arguments?: unknown;\n readonly agent?: { readonly id?: unknown };\n readonly error?: { readonly message?: unknown; readonly info?: { readonly code?: unknown } };\n}\n\n/**\n * Classify one settled call against the session's CURRENT disabled sets.\n * Returns null for everything that is not a blocked attempt — including\n * unknown-tool failures for tools nobody disabled (genuine model typos).\n */\nexport function classifyBlockedCall(\n call: SettledCall,\n disabledSkills: ReadonlySet<string>,\n disabledToolNames: ReadonlySet<string>,\n): { kind: 'blocked-skill' | 'blocked-tool'; name: string; sessionId: string | null } | null {\n if (call.error === undefined) return null;\n const sessionId = typeof call.agent?.id === 'string' ? call.agent.id : null;\n\n if (call.name === 'skill') {\n const message = typeof call.error.message === 'string' ? call.error.message : '';\n if (!message.includes('not available for model invocation')) return null;\n const args = call.arguments;\n const skillName =\n args !== null && typeof args === 'object' && typeof (args as { name?: unknown }).name === 'string'\n ? (args as { name: string }).name\n : null;\n if (skillName === null || !disabledSkills.has(skillName)) return null;\n return { kind: 'blocked-skill', name: skillName, sessionId };\n }\n\n if (typeof call.name === 'string' && disabledToolNames.has(call.name)) {\n // Global tools: restrict reports UNKNOWN_TOOL before dispatch; preset-layer tools: the guard denies before execution.\n const message = typeof call.error.message === 'string' ? call.error.message : '';\n if (call.error.info?.code !== 'UNKNOWN_TOOL' && !message.startsWith(GUARD_DENIAL_PREFIX)) return null;\n return { kind: 'blocked-tool', name: call.name, sessionId };\n }\n\n return null;\n}\n\n/** Aggregate a JSONL log into a name → blocked-attempt count map. */\nexport function aggregateBlocked(lines: Iterable<string>): Record<string, number> {\n const counts: Record<string, number> = {};\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n let record: StatsRecord;\n try {\n record = JSON.parse(trimmed) as StatsRecord;\n } catch {\n continue;\n }\n // 'blocked-mcp' is the pre-tool-level name for the same fact.\n if (record.kind !== 'blocked-skill' && record.kind !== 'blocked-tool' && (record.kind as string) !== 'blocked-mcp') {\n continue;\n }\n if (typeof record.name !== 'string') continue;\n counts[record.name] = (counts[record.name] ?? 0) + 1;\n }\n return counts;\n}\n","import type { SkillEntry, SkillLoadState } from './contract.js';\n\n/**\n * One `skill` tool call found in the durable log.\n *\n * Verified against real session logs: a load is a\n * `tool/call` event whose `data.name` is `\"skill\"` and whose `data.arguments` is\n * a JSON *string* holding `{ name }`. The nested shapes suggested by package\n * READMEs (`data.call.name`, `data.call.args`) do NOT exist here — reading them\n * silently found zero loads in sessions that had five.\n */\nexport interface SkillLoadRecord {\n readonly seq: number;\n readonly skillName: string;\n readonly callId: string;\n}\n\n/**\n * The raw event shape this package reads, verified against real logs. Surface\n * membership is never read from the events: the live session's\n * `surface.nodes` is authoritative, so `surfaceOp` is deliberately absent\n * here (it is a TOP-LEVEL field on real events — the string `\"append\"` for a\n * node that joined the surface tail, an object for a replacement, and `null`\n * on non-surface events like `tool/call`; anyone reintroducing a surfaceOp\n * read should read that position, not `data.surfaceOp`).\n */\nexport interface RawEvent {\n readonly type?: string;\n readonly seq?: number;\n readonly data?: {\n readonly name?: string;\n readonly arguments?: string;\n readonly callId?: string;\n /** tool/result payload; the pairing callId lives at message.source.callId. */\n readonly message?: {\n readonly source?: { readonly callId?: unknown };\n /** Result blocks; read for prune-marker detection on surface nodes. */\n readonly content?: unknown;\n };\n };\n}\n\n/** Pull the skill name out of a `skill` tool call's stringified arguments. */\nfunction skillNameOf(args: string | undefined): string | null {\n if (typeof args !== 'string') return null;\n try {\n const parsed: unknown = JSON.parse(args);\n if (parsed !== null && typeof parsed === 'object' && 'name' in parsed) {\n const name = (parsed as { name?: unknown }).name;\n return typeof name === 'string' && name !== '' ? name : null;\n }\n } catch {\n // A malformed argument blob is not a load we can attribute; skip it rather\n // than guessing a name.\n }\n return null;\n}\n\nexport function collectLoadRecords(events: readonly RawEvent[]): SkillLoadRecord[] {\n const out: SkillLoadRecord[] = [];\n for (const event of events) {\n if (event.type !== 'tool/call') continue;\n if (event.data?.name !== 'skill') continue;\n const skillName = skillNameOf(event.data.arguments);\n if (skillName === null) continue;\n const seq = event.seq;\n if (typeof seq !== 'number') continue;\n out.push({ seq, skillName, callId: event.data.callId ?? '' });\n }\n return out;\n}\n\n/**\n * Map a tool result's pairing callId to its seq.\n *\n * Verified against real logs: `tool/result` events carry no\n * top-level callId; the pairing lives at `data.message.source.callId` and\n * matches the call's `data.callId` exactly.\n *\n * Last write wins on purpose: the middle-pruner appends a stub `tool/result`\n * carrying the SAME callId and a replace surfaceOp over the original's seq\n * (verified against a real compacted session), so the callId resolves to the\n * stub — the node whose fold verdict actually tracks the surface position.\n */\nexport function indexToolResultSeqs(events: readonly RawEvent[]): Map<string, number> {\n const out = new Map<string, number>();\n for (const event of events) {\n if (event.type !== 'tool/result') continue;\n const callId = event.data?.message?.source?.callId;\n const seq = event.seq;\n if (typeof callId !== 'string' || callId === '' || typeof seq !== 'number') continue;\n out.set(callId, seq);\n }\n return out;\n}\n\n/**\n * The load seqs whose skill content is gone from the model surface.\n *\n * A `tool/call` never joins the surface itself — SURFACE_EVENT_TYPES in\n * dsh-session is exactly { user/message, assistant/message, tool/result }, and\n * real skill calls carry `surfaceOp: null`. What the model actually sees of a\n * skill is its tool RESULT (a surface node), so eviction keys on the paired\n * result's surface membership, not on the call's position.\n *\n * `surfaceSeqs` is the live session's CURRENT surface (`session.surface.nodes`),\n * which the session maintains incrementally — reading it is O(1), no log fold\n * is ever run for this panel. A paired result seq absent from that set was\n * displaced by a prune stub's or a summary's `replace` op, i.e. shadowed.\n *\n * A load with no paired result is in flight or its result never landed; it is\n * NOT counted as shadowed, so it reports `loaded` — the honest reading of \"the\n * model is about to see it\".\n */\nexport function shadowedLoadSeqs(\n loads: readonly SkillLoadRecord[],\n resultSeqByCallId: ReadonlyMap<string, number>,\n surfaceSeqs: ReadonlySet<number>,\n): Set<number> {\n const out = new Set<number>();\n for (const load of loads) {\n const resultSeq = resultSeqByCallId.get(load.callId);\n if (resultSeq === undefined) continue;\n if (!surfaceSeqs.has(resultSeq)) out.add(load.seq);\n }\n return out;\n}\n\n/**\n * Substring of dsh-compaction-tool-result-pruner's PRUNE_MARKER. Matching on\n * the bracketed phrase (without the surrounding newlines) keeps the check\n * robust to marker framing changes across pruner versions.\n */\nexport const PRUNE_MARKER_TEXT = '[... tool result middle pruned ...]';\n\nfunction textBlocksOf(event: RawEvent): readonly string[] {\n const content = event.data?.message?.content;\n if (!Array.isArray(content)) return [];\n const out: string[] = [];\n // A tool/result message wraps its blocks one level deep: message.content is\n // [{ type: 'tool_result', content: blocks }] on the pruner's write path.\n const walk = (blocks: readonly unknown[]): void => {\n for (const block of blocks) {\n if (block === null || typeof block !== 'object') continue;\n const record = block as { type?: unknown; text?: unknown; content?: unknown };\n if (record.type === 'text' && typeof record.text === 'string') out.push(record.text);\n else if (Array.isArray(record.content)) walk(record.content);\n }\n };\n walk(content);\n return out;\n}\n\n/**\n * The load seqs whose paired tool result sits on the surface TRUNCATED —\n * head and tail visible, middle replaced by the pruner's marker. The model\n * partially sees these skills, so they read as their own state rather than\n * either extreme. Only surface-resident results are inspected; a shadowed\n * result is already reported by shadowedLoadSeqs and its content is\n * irrelevant to the model now.\n */\nexport function prunedLoadSeqs(\n loads: readonly SkillLoadRecord[],\n resultSeqByCallId: ReadonlyMap<string, number>,\n surfaceSeqs: ReadonlySet<number>,\n events: readonly RawEvent[],\n): Set<number> {\n const prunedResults = new Set<number>();\n for (const event of events) {\n if (event.type !== 'tool/result') continue;\n const seq = event.seq;\n if (typeof seq !== 'number' || !surfaceSeqs.has(seq)) continue;\n if (textBlocksOf(event).some((text) => text.includes(PRUNE_MARKER_TEXT))) prunedResults.add(seq);\n }\n const out = new Set<number>();\n for (const load of loads) {\n const resultSeq = resultSeqByCallId.get(load.callId);\n if (resultSeq !== undefined && prunedResults.has(resultSeq)) out.add(load.seq);\n }\n return out;\n}\n\n/**\n * Decide each skill's state.\n *\n * `shadowedSeqs` holds LOAD seqs whose paired tool result is absent from the\n * current surface (see shadowedLoadSeqs for why the result, not the call,\n * carries the verdict); `prunedSeqs` holds load seqs whose result survives on\n * the surface with its middle truncated. Do NOT re-derive either from\n * replacement ranges here: after a replacement lands, a high-seq summary node\n * sits at the shadowed range's *position*, so surface order stops tracking\n * seq order and a numeric `start <= seq <= end` test silently misjudges later\n * compactions.\n *\n * Verified against real data: the middle-pruner had replaced the lark-shared /\n * lark-im / lark-event results with stubs (those now read `pruned`), and a\n * later full-history compaction (one replace over [7..16114]) shadowed those\n * stubs plus the find-skills / git-worktree-discipline results (those read\n * `evicted`).\n */\nexport function decideStates(\n available: readonly { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[],\n loads: readonly SkillLoadRecord[],\n shadowedSeqs: ReadonlySet<number>,\n disabledSkills: ReadonlySet<string> = new Set(),\n prunedSeqs: ReadonlySet<number> = new Set(),\n): SkillEntry[] {\n const byName = new Map<string, SkillLoadRecord[]>();\n for (const record of loads) {\n const bucket = byName.get(record.skillName);\n if (bucket === undefined) byName.set(record.skillName, [record]);\n else bucket.push(record);\n }\n\n return available.map(({ name, description, masked, source, provider, path, group }) => {\n const records = byName.get(name) ?? [];\n let state: SkillLoadState = 'unloaded';\n if (records.length > 0) {\n // A reload after an eviction is still loaded: any surviving record wins.\n const current = records.filter((r) => !shadowedSeqs.has(r.seq));\n if (current.length === 0) state = 'evicted';\n // One fully intact copy in context is enough; truncation shows only\n // when every surviving copy was middle-pruned.\n else state = current.some((r) => !prunedSeqs.has(r.seq)) ? 'loaded' : 'pruned';\n }\n return {\n name,\n ...(description === undefined ? {} : { description }),\n state,\n // Off if THIS panel switched it off, or if something else already\n // withdrew model invocation -- a preset default, most often.\n enabled: !disabledSkills.has(name) && masked !== true,\n loadCount: records.length,\n source,\n provider,\n ...(path === undefined ? {} : { path }),\n ...(group === undefined ? {} : { group }),\n };\n });\n}\n\n/** Group MCP tools by server: `mcp__<server>__<tool>`. */\nexport function groupMcpTools(toolNames: readonly string[]): { server: string; tools: string[] }[] {\n const byServer = new Map<string, string[]>();\n for (const raw of toolNames) {\n if (!raw.startsWith('mcp__')) continue;\n const rest = raw.slice('mcp__'.length);\n const cut = rest.indexOf('__');\n // A name with no second separator has no tool part to attribute.\n if (cut <= 0) continue;\n const server = rest.slice(0, cut);\n const tool = rest.slice(cut + 2);\n if (tool === '') continue;\n const bucket = byServer.get(server);\n if (bucket === undefined) byServer.set(server, [tool]);\n else bucket.push(tool);\n }\n return [...byServer.entries()]\n .map(([server, tools]) => ({ server, tools: tools.sort() }))\n .sort((a, b) => a.server.localeCompare(b.server));\n}\n","export class HttpError extends Error {\n constructor(\n readonly status: 400 | 404 | 409 | 413 | 415 | 503,\n message: string,\n ) {\n super(message);\n }\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * `run_code` is the Code Mode transport the harness itself depends on. It must\n * stay reachable in every session, so it can never be switched off: the\n * settings route rejects the attempt with 409, the panels render its switch\n * disabled, and enforcement ignores a stored entry naming it. One constant\n * keeps the four of those from drifting apart.\n */\nexport const RESERVED_TOOL = 'run_code';\n","import { classifyBlockedCall, GUARD_DENIAL_PREFIX } from '../stats.js';\nimport type { StatsRecord } from '../stats.js';\nimport { groupMcpTools } from '../load-state.js';\nimport { HttpError } from './errors.js';\nimport { RESERVED_TOOL } from './reserved.js';\nimport type {\n AgentLike,\n CapabilityKind,\n HostServices,\n ScopedSkillsRegistry,\n ScopedSystemPrompt,\n ScopedToolsRegistry,\n SessionCapabilityState,\n SessionOverrideState,\n} from './types.js';\n\nexport interface PresetDefaults {\n /** Disabled tool names: `mcp__`-prefixed land on the MCP map, the rest are system tools. */\n readonly tools: readonly string[];\n readonly skills: readonly string[];\n}\n\nexport interface CapabilityController {\n readonly states: ReadonlyMap<string, SessionCapabilityState>;\n state(sessionId: string): SessionCapabilityState | undefined;\n set(sessionId: string, kind: CapabilityKind, name: string, enabled: boolean): Promise<void>;\n /**\n * Apply a preset's stored defaults to a freshly created session. Seeded\n * masks live in the SAME session state the panel's own switches use, which\n * is what lets a session switch a preset default back on: the enable path\n * disposes the entry whichever layer put it there. Seeding never throws --\n * a default that can no longer apply is dropped, not escalated -- and never\n * overwrites a mask already in place.\n */\n seed(sessionId: string, defaults: PresetDefaults): Promise<void>;\n /**\n * Re-apply one session's persisted switch positions onto a freshly created\n * agent (e.g. a restored session after a restart). Runs AFTER seeding:\n * preset defaults first, the session's own recorded toggles second, so the\n * user's last word wins — including an explicit re-enable of a preset\n * default. Never throws and never writes stats: a position that can no\n * longer apply (skill uninstalled, MCP server gone) is dropped, and no\n * toggle actually happened here.\n */\n restore(sessionId: string, overrides: SessionOverrideState): Promise<void>;\n /**\n * Re-derive a session's masks after its preset changed mid-life (the user\n * switched the agent preset before the first turn). Seeded masks live in\n * the agent's own scope layer and survive the rebind, so the state is torn\n * down wholesale before the new preset's defaults and the session's stored\n * overrides land again.\n */\n reseed(sessionId: string, defaults: PresetDefaults, overrides: SessionOverrideState | undefined): Promise<void>;\n /**\n * Re-apply the tool masks a session should hold, for names that exist NOW\n * but did not at seed time (an on-demand MCP server whose local process\n * just connected). Idempotent: seed and restore skip names already masked.\n * Tool-only — skills do not late-register the way reconnecting tools do.\n */\n remask(sessionId: string, defaults: PresetDefaults, overrides: SessionOverrideState | undefined): Promise<void>;\n}\n\nfunction renderDisabledNote(state: SessionCapabilityState): string {\n const lines: string[] = [];\n if (state.skills.size > 0) lines.push(`- Skills: ${[...state.skills.keys()].join(', ')}`);\n if (state.mcpServers.size > 0) lines.push(`- MCP servers: ${[...state.mcpServers.keys()].join(', ')}`);\n if (state.mcpTools.size > 0) lines.push(`- MCP tools: ${[...state.mcpTools.keys()].join(', ')}`);\n if (state.systemTools.size > 0) lines.push(`- System tools: ${[...state.systemTools.keys()].join(', ')}`);\n if (lines.length === 0) return '';\n return [\n 'The user has turned off the following capabilities for this session:',\n ...lines,\n 'Do not attempt to call them. If the user\\'s request depends on one, say it is disabled and can be re-enabled from the Capability Panel.',\n ].join('\\n');\n}\n\nexport function createCapabilityController(\n ctx: HostServices,\n appendStats: (record: StatsRecord) => void,\n blockedCounts: Record<string, number>,\n): CapabilityController {\n const states = new Map<string, SessionCapabilityState>();\n const stateFor = (sessionId: string): SessionCapabilityState => {\n let state = states.get(sessionId);\n if (state === undefined) {\n state = { skills: new Map(), mcpServers: new Map(), mcpTools: new Map(), systemTools: new Map(), userToggled: new Set() };\n states.set(sessionId, state);\n }\n return state;\n };\n const disabledToolNames = (state: SessionCapabilityState): Set<string> => {\n const names = new Set([...state.mcpTools.keys(), ...state.systemTools.keys()]);\n for (const schema of state.mcpServers.size > 0 ? (ctx.get('tools')?.schemas() ?? []) : []) {\n if (typeof schema.name !== 'string' || !schema.name.startsWith('mcp__')) continue;\n const server = schema.name.slice('mcp__'.length, schema.name.indexOf('__', 'mcp__'.length));\n if (state.mcpServers.has(server)) names.add(schema.name);\n }\n return names;\n };\n\n ctx.on('system-prompt/assemble', async (_assembly, context, next) => {\n const assembled = await next();\n const sessionId = typeof context.agent?.id === 'string' ? context.agent.id : null;\n const state = sessionId === null ? undefined : states.get(sessionId);\n if (state === undefined || state.systemTools.size === 0 || assembled.tools === undefined) return assembled;\n return { ...assembled, tools: assembled.tools.filter((tool) => !state.systemTools.has(String(tool.name))) };\n });\n\n let guardRegistered = false;\n const ensureGuard = (): void => {\n if (guardRegistered) return;\n const guardDispose = ctx.get('tools')?.guard?.((execution) => {\n const sessionId = typeof execution.agent?.id === 'string' ? execution.agent.id : null;\n const state = sessionId === null ? undefined : states.get(sessionId);\n const name = typeof execution.name === 'string' ? execution.name : null;\n if (state === undefined || name === null || !state.systemTools.has(name)) return undefined;\n return `${GUARD_DENIAL_PREFIX} \"${name}\" (re-enable from the Capability Panel)`;\n });\n if (guardDispose === undefined) return;\n guardRegistered = true;\n ctx.effect(() => guardDispose, 'capability-panel: tool guard');\n };\n ensureGuard();\n\n ctx.on('tools/result', (exec, result) => {\n const agent = exec.agent;\n if (agent === undefined || typeof agent.id !== 'string') return;\n const state = states.get(agent.id);\n if (state === undefined) return;\n const hit = classifyBlockedCall(\n {\n name: exec.name,\n arguments: exec.arguments,\n agent,\n ...(result.isError && result.error !== undefined ? { error: result.error } : {}),\n },\n new Set(state.skills.keys()),\n disabledToolNames(state),\n );\n if (hit === null) return;\n blockedCounts[hit.name] = (blockedCounts[hit.name] ?? 0) + 1;\n appendStats({ ts: new Date().toISOString(), sessionId: agent.id, kind: hit.kind, name: hit.name });\n });\n\n const ensurePromptNote = (agent: AgentLike, state: SessionCapabilityState): void => {\n if (state.noteDispose !== undefined) return;\n const systemPrompt = agent.ctx?.get('systemPrompt') as ScopedSystemPrompt | undefined;\n if (systemPrompt === undefined) return;\n state.noteDispose = systemPrompt.context({\n name: 'capability-panel:disabled-capabilities',\n order: 900,\n text: () => renderDisabledNote(state),\n });\n };\n\n const getAgentTools = (sessionId: string): { agent: AgentLike; tools: ScopedToolsRegistry } => {\n const agent = ctx.get('agents')?.get(sessionId);\n const tools = agent?.ctx?.get('tools') as ScopedToolsRegistry | undefined;\n if (agent === undefined) throw new HttpError(404, 'session agent is not available');\n if (tools === undefined) throw new HttpError(503, 'session tools service is not available');\n return { agent, tools };\n };\n\n const setSkill = async (sessionId: string, name: string, enabled: boolean): Promise<void> => {\n const state = stateFor(sessionId);\n const existing = state.skills.get(name);\n if (enabled && existing !== undefined) {\n existing();\n state.skills.delete(name);\n return;\n }\n const agent = ctx.get('agents')?.get(sessionId);\n const scopedSkills = agent?.ctx?.get('skills') as ScopedSkillsRegistry | undefined;\n if (agent === undefined || scopedSkills === undefined) {\n throw new HttpError(agent === undefined ? 404 : 503, agent === undefined ? 'session agent is not available' : 'session skills service is not available');\n }\n if (enabled || existing !== undefined) return;\n const skills = ctx.get('skills');\n if (skills === undefined) throw new HttpError(503, 'skills service unavailable');\n const cwd = agent.session?.header?.cwd;\n const original = await skills.get(name, { ...(cwd === undefined ? {} : { cwd }), scope: agent });\n if (original === undefined || typeof original.name !== 'string' || typeof original.description !== 'string' || typeof original.content !== 'string') {\n throw new HttpError(404, `skill \"${name}\" is not available in this session`);\n }\n state.skills.set(name, scopedSkills.register({\n name: original.name,\n description: original.description,\n content: original.content,\n // The shadow wins the listing outright (nearest layer), so it must\n // carry the original's provenance — hardcoding 'custom' would mislabel\n // every panel-disabled skill's source in the catalogue.\n source: typeof original.source === 'string' ? original.source : 'custom',\n provider: typeof original.provider === 'string' ? original.provider : 'capability-panel',\n ...(original.resourceBase === undefined ? {} : { resourceBase: original.resourceBase }),\n invocation: { modelInvocable: false, userInvocable: true },\n }));\n ensurePromptNote(agent, state);\n };\n\n const setServer = (sessionId: string, server: string, enabled: boolean): void => {\n const state = stateFor(sessionId);\n const existing = state.mcpServers.get(server);\n if (enabled) {\n // Enabling from the server row must clear EVERY mask under this server:\n // the server-level entry AND any per-tool entries a partial preset\n // default (or per-tool switch) left behind — otherwise the row reads on\n // while those tools stay denied.\n const prefix = `mcp__${server}__`;\n for (const [name, dispose] of [...state.mcpTools]) {\n if (name.startsWith(prefix)) {\n dispose();\n state.mcpTools.delete(name);\n }\n }\n if (existing !== undefined) {\n existing.dispose();\n state.mcpServers.delete(server);\n }\n return;\n }\n if (existing !== undefined) return;\n const { agent, tools } = getAgentTools(sessionId);\n const toolService = ctx.get('tools');\n if (toolService === undefined) throw new HttpError(503, 'tools service unavailable');\n const prefix = `mcp__${server}__`;\n const names = [...toolService.schemas()]\n .map((schema) => schema.name)\n .filter((name): name is string => typeof name === 'string' && name.startsWith(prefix));\n if (names.length === 0) throw new HttpError(404, `MCP server \"${server}\" exposes no tools`);\n state.mcpServers.set(server, { dispose: tools.restrict({ deny: names }), names });\n ensurePromptNote(agent, state);\n };\n\n const setTool = (sessionId: string, name: string, enabled: boolean, system: boolean): void => {\n const state = stateFor(sessionId);\n const map = system ? state.systemTools : state.mcpTools;\n const existing = map.get(name);\n if (enabled) {\n // Enabling is a pure state cleanup: an unmasked tool needs no services\n // (a server-row enable may already have swept this tool's mask).\n if (existing !== undefined) {\n existing();\n map.delete(name);\n }\n return;\n }\n if (existing !== undefined) return;\n if (system && name === RESERVED_TOOL) {\n throw new HttpError(409, 'run_code is the reserved Code Mode transport and cannot be restricted');\n }\n const { agent, tools } = getAgentTools(sessionId);\n const toolService = ctx.get('tools');\n if (toolService === undefined) throw new HttpError(503, 'tools service unavailable');\n const globalNames = new Set(\n [...toolService.schemas()]\n .map((schema) => schema.name)\n .filter((entry): entry is string => typeof entry === 'string'),\n );\n const scopedNames = system\n ? new Set(\n [...toolService.schemas(agent)]\n .map((schema) => schema.name)\n .filter((entry): entry is string => typeof entry === 'string'),\n )\n : globalNames;\n const exists = system ? scopedNames.has(name) : globalNames.has(name) && name.startsWith('mcp__');\n if (!exists) {\n throw new HttpError(404, `${system ? 'system tool' : 'MCP tool'} \"${name}\" is not available in this session`);\n }\n if (system) ensureGuard();\n map.set(name, globalNames.has(name) ? tools.restrict({ deny: [name] }) : () => {});\n ensurePromptNote(agent, state);\n };\n\n ctx.effect(() => () => {\n for (const state of states.values()) {\n for (const dispose of state.skills.values()) dispose();\n for (const mask of state.mcpServers.values()) mask.dispose();\n for (const dispose of state.mcpTools.values()) dispose();\n for (const dispose of state.systemTools.values()) dispose();\n state.noteDispose?.();\n }\n states.clear();\n }, 'capability-panel: capability masks');\n\n const seed = async (sessionId: string, defaults: PresetDefaults, includeSkills = true): Promise<void> => {\n const agent = ctx.get('agents')?.get(sessionId);\n if (agent === undefined) return;\n // The state is created on the first mask that actually lands: a session\n // whose stored defaults all name things it cannot see keeps no state at\n // all, rather than an empty entry that reads as \"nothing is off\".\n let state = states.get(sessionId);\n const ensureState = (): SessionCapabilityState => {\n if (state === undefined) state = stateFor(sessionId);\n return state;\n };\n let maskedAny = false;\n\n const scopedTools = agent.ctx?.get('tools') as ScopedToolsRegistry | undefined;\n const toolsService = ctx.get('tools');\n if (scopedTools !== undefined && toolsService !== undefined) {\n const globalNames = new Set<string>();\n const scopedNames = new Set<string>();\n for (const schema of toolsService.schemas()) {\n if (typeof schema.name === 'string') globalNames.add(schema.name);\n }\n for (const schema of toolsService.schemas(agent)) {\n if (typeof schema.name === 'string') scopedNames.add(schema.name);\n }\n for (const name of defaults.tools) {\n if (!globalNames.has(name)) continue;\n // MCP defaults are grouped and applied below, one level per server.\n if (name.startsWith('mcp__')) continue;\n // The reserved transport stays reachable no matter what was stored.\n if (name === RESERVED_TOOL || state?.systemTools.has(name) === true) continue;\n // A default for a tool this agent cannot see is simply not applied.\n if (!scopedNames.has(name)) continue;\n ensureState().systemTools.set(name, scopedTools.restrict({ deny: [name] }));\n ensureGuard();\n maskedAny = true;\n }\n // MCP defaults restore at the granularity they were recorded with: a\n // default covering every tool a server exposes is a SERVER-level mask\n // (one restrict, and the panel's server row reads `mcpServers`, so a\n // full default must land there to display as off); a partial default\n // keeps per-tool entries.\n const mcpDefaults = defaults.tools.filter((name) => globalNames.has(name));\n const fullByServer = new Map(groupMcpTools([...globalNames]).map((group) => [group.server, group.tools]));\n for (const group of groupMcpTools(mcpDefaults)) {\n // mcpDefaults ⊆ globalNames, so the full server roster always exists.\n const full = fullByServer.get(group.server)!;\n const coversAll = full.every((tool) => group.tools.includes(tool));\n if (coversAll) {\n const st = ensureState();\n const prefix = `mcp__${group.server}__`;\n const fullNames = full.map((tool) => `${prefix}${tool}`);\n const existingMask = st.mcpServers.get(group.server);\n // A standing mask whose deny list predates a roster growth (the\n // server reconnected with MORE tools) cannot cover the new names:\n // replace it with the fresh roster.\n if (existingMask !== undefined && fullNames.every((name) => existingMask.names.includes(name))) continue;\n // Promote any per-tool masks an earlier partial application left\n // (e.g. the server was mid-startup at seed time) into the\n // server-level entry — one owner per server, no double bookkeeping.\n for (const [name, dispose] of [...st.mcpTools]) {\n if (name.startsWith(prefix)) {\n dispose();\n st.mcpTools.delete(name);\n }\n }\n existingMask?.dispose();\n st.mcpServers.set(group.server, { dispose: scopedTools.restrict({ deny: fullNames }), names: fullNames });\n maskedAny = true;\n continue;\n }\n for (const tool of group.tools) {\n const name = `mcp__${group.server}__${tool}`;\n if (state?.mcpTools.has(name) === true) continue;\n ensureState().mcpTools.set(name, scopedTools.restrict({ deny: [name] }));\n maskedAny = true;\n }\n }\n }\n\n const scopedSkills = agent.ctx?.get('skills') as ScopedSkillsRegistry | undefined;\n const skillsService = ctx.get('skills');\n if (includeSkills && scopedSkills !== undefined && skillsService !== undefined) {\n const cwd = agent.session?.header?.cwd;\n const lookup = { ...(cwd === undefined ? {} : { cwd }), scope: agent };\n // Read every original concurrently: each get is an I/O read, and the\n // session is already live while its masks go in, so serial reads would\n // widen a window that serves nobody.\n const disposers = await Promise.all(defaults.skills.map(async (name) => {\n if (state?.skills.has(name) === true) return undefined;\n try {\n const original = await skillsService.get(name, lookup);\n if (original === undefined) return undefined;\n if (typeof original.name !== 'string' || typeof original.description !== 'string') return undefined;\n if (typeof original.content !== 'string') return undefined;\n return scopedSkills.register({\n name: original.name,\n description: original.description,\n content: original.content,\n // Same provenance rule as the panel's own switch: the shadow\n // replaces the original in the listing, so it must inherit the\n // original's source/provider.\n source: typeof original.source === 'string' ? original.source : 'custom',\n provider: typeof original.provider === 'string' ? original.provider : 'capability-panel',\n ...(original.resourceBase === undefined ? {} : { resourceBase: original.resourceBase }),\n invocation: { modelInvocable: false, userInvocable: true },\n });\n } catch {\n // Unreadable or already shadowed; the skill stays as composed.\n return undefined;\n }\n }));\n for (let i = 0; i < disposers.length; i += 1) {\n const dispose = disposers[i];\n const name = defaults.skills[i];\n if (dispose !== undefined && name !== undefined && state?.skills.has(name) !== true) {\n ensureState().skills.set(name, dispose);\n maskedAny = true;\n }\n }\n }\n\n // The model should KNOW these are off rather than discover it call by\n // call; the note covers seeded defaults exactly like panel switches.\n if (maskedAny) ensurePromptNote(agent, ensureState());\n };\n\n // Per-session mutation queue. seed/restore/reseed/set all interleave await\n // points (skill I/O) with each other, and reseed's synchronous teardown\n // would otherwise land mid-seed: a pending created-time seed would write\n // old-preset masks into the fresh post-reseed state, and a panel toggle\n // mid-switch would write into a detached state object. Serializing per\n // session makes every mutation see a coherent state; cross-session work\n // stays parallel.\n const mutationQueues = new Map<string, Promise<unknown>>();\n const enqueue = <T>(sessionId: string, op: () => Promise<T>): Promise<T> => {\n const next = (mutationQueues.get(sessionId) ?? Promise.resolve()).then(op, op);\n // The stored link never rejects: a failed mutation must not jam the queue.\n mutationQueues.set(sessionId, next.then(() => undefined, () => undefined));\n return next;\n };\n\n const restoreImpl = async (sessionId: string, overrides: SessionOverrideState): Promise<void> => {\n const groups: readonly (readonly [CapabilityKind, Readonly<Record<string, boolean>>])[] = [\n ['skill', overrides.skills],\n ['mcp-server', overrides.mcpServers],\n ['mcp-tool', overrides.mcpTools],\n ['system-tool', overrides.systemTools],\n ];\n for (const [kind, positions] of groups) {\n for (const [name, enabled] of Object.entries(positions)) {\n // A panel toggle landing mid-restore (restore awaits skill I/O per\n // entry) is newer than the record on disk; re-applying the stored\n // position would silently diverge memory from it.\n if (states.get(sessionId)?.userToggled.has(`${kind}:${name}`) === true) continue;\n try {\n if (kind === 'skill') await setSkill(sessionId, name, enabled);\n else if (kind === 'mcp-server') setServer(sessionId, name, enabled);\n else setTool(sessionId, name, enabled, kind === 'system-tool');\n } catch {\n // A stored position that can no longer apply is dropped, not\n // escalated -- same rule as seeding a stale preset default.\n }\n }\n }\n // Positions that all dropped (or all re-enabled neutral) must not leave\n // an empty state entry behind: \"no state\" is the panel's \"nothing is\n // off\" signal, and stale entries would only ever accumulate.\n const st = states.get(sessionId);\n if (st !== undefined && st.userToggled.size === 0 && st.skills.size === 0 && st.mcpServers.size === 0 && st.mcpTools.size === 0 && st.systemTools.size === 0) {\n st.noteDispose?.();\n delete st.noteDispose;\n states.delete(sessionId);\n }\n };\n\n const controller: CapabilityController = {\n states,\n state: (sessionId) => states.get(sessionId),\n seed: (sessionId, defaults) => enqueue(sessionId, () => seed(sessionId, defaults)),\n restore: (sessionId, overrides) => enqueue(sessionId, () => restoreImpl(sessionId, overrides)),\n /**\n * Re-derive a session's masks after its preset changed: every existing\n * mask was seeded for the OLD composition and survives the scope rebind\n * (scoped masks live in the agent's own layer), so the session state is\n * torn down wholesale before the new preset's defaults and the session's\n * own stored overrides land again. `userToggled` clears with the masks —\n * its entries guarded in-flight restore races against masks that no\n * longer exist, and the durable override record is what restore replays.\n */\n reseed: (sessionId, defaults, overrides) => enqueue(sessionId, async () => {\n const st = states.get(sessionId);\n if (st !== undefined) {\n for (const dispose of st.systemTools.values()) dispose();\n for (const mask of st.mcpServers.values()) mask.dispose();\n for (const dispose of st.mcpTools.values()) dispose();\n for (const dispose of st.skills.values()) dispose();\n for (const map of [st.systemTools, st.mcpServers, st.mcpTools, st.skills]) {\n map.clear();\n }\n st.noteDispose?.();\n delete st.noteDispose;\n st.userToggled.clear();\n states.delete(sessionId);\n }\n // Inside the queue: call the impls directly — enqueuing here would\n // deadlock the session's own queue.\n if (defaults.tools.length > 0 || defaults.skills.length > 0) await seed(sessionId, defaults);\n if (overrides !== undefined) await restoreImpl(sessionId, overrides);\n }),\n /**\n * Re-apply the masks a session SHOULD hold, for names that exist NOW but\n * did not at seed time — an on-demand MCP server whose local process just\n * connected. Tool-only by design: seed and restore skip every name\n * already masked, so this is a no-op when nothing new registered, and\n * skills are excluded (they do not late-register the way a reconnecting\n * server's tools do, and re-reading every default skill per registry\n * change would be I/O for nobody).\n */\n remask: (sessionId, defaults, overrides) => enqueue(sessionId, async () => {\n if (defaults.tools.length > 0) await seed(sessionId, defaults, false);\n if (overrides !== undefined) {\n await restoreImpl(sessionId, { skills: {}, mcpServers: overrides.mcpServers, mcpTools: overrides.mcpTools, systemTools: overrides.systemTools });\n }\n }),\n set: (sessionId, kind, name, enabled) => enqueue(sessionId, async () => {\n stateFor(sessionId).userToggled.add(`${kind}:${name}`);\n if (kind === 'skill') await setSkill(sessionId, name, enabled);\n else if (kind === 'mcp-server') setServer(sessionId, name, enabled);\n else setTool(sessionId, name, enabled, kind === 'system-tool');\n appendStats({ ts: new Date().toISOString(), sessionId, kind: enabled ? 'enable' : 'disable', name: `${kind}:${name}` });\n }),\n };\n return controller;\n}\n","import type { CapabilityController } from './capabilities.js';\nimport type { PresetToolController } from './preset-tools.js';\nimport type { SessionOverrideStore } from './session-overrides.js';\nimport type { AgentCreatedPayload, HostServices } from './types.js';\n\n/**\n * Applies stored capability positions to freshly created agents, in two\n * layers: the preset's stored defaults first, then the session's own recorded\n * toggles. The second layer is what survives a restart — a restored session\n * creates a new agent, and its session-bound overrides land on top of the\n * preset defaults, so the user's last word wins (including an explicit\n * re-enable of a preset default).\n *\n * This is deliberately NOT part of the HTTP controller: the listener has no\n * request, and living next to one is how it grew a settings schema, a write\n * queue, and two event subscriptions in a single file. Here it owns exactly\n * one job -- at agent/created, read both stored layers and seed them into the\n * session's capability state.\n *\n * Seeding into the session state (rather than registering private masks) is\n * the point: the session panel's enable path disposes whatever the state\n * holds, whichever layer put it there, so a stored position remains a\n * starting point the user can flip in the session instead of an invisible\n * wall. Session overrides are keyed by session id, so one session's switches\n * can never leak into another.\n */\nexport function registerPresetEnforcement(\n ctx: HostServices,\n capabilities: CapabilityController,\n presetTools: PresetToolController,\n sessionOverrides: SessionOverrideStore,\n): void {\n /**\n * Subscribe to `agent/created` so that NOTHING this plugin does can veto the\n * agent. Cordis treats a synchronous listener failure as a veto and only\n * reports a rejected promise, so both halves are contained: the synchronous\n * prelude (reading settings, resolving services) is wrapped here, and the\n * asynchronous body reports through the returned promise. Applying a stored\n * preference is never worth costing the user their session.\n */\n ctx.on('agent/created', ({ agent }: AgentCreatedPayload) => {\n try {\n if (typeof agent.id !== 'string') return undefined;\n const sessionId = agent.id;\n const presetId = ctx.get('agentPresets')?.composedPreset(agent.ctx);\n const defaults = presetId === undefined ? undefined : presetTools.defaultsFor(presetId);\n const hasDefaults = defaults !== undefined && (defaults.tools.length > 0 || defaults.skills.length > 0);\n const overrides = sessionOverrides.overridesFor(sessionId);\n if (!hasDefaults && overrides === undefined) return undefined;\n return (async () => {\n if (hasDefaults && defaults !== undefined) await capabilities.seed(sessionId, defaults);\n if (overrides !== undefined) await capabilities.restore(sessionId, overrides);\n })();\n } catch {\n // A settings section that cannot be read, or a service that is not there\n // yet, leaves the agent exactly as its preset composed it; the panel\n // surfaces the fault on its next read.\n return undefined;\n }\n });\n\n /**\n * A preset switch lands AFTER creation (the session header records the\n * composing preset at creation, and the picker's select() recomposes the\n * blank session later): the first agent/created seeded the ORIGINAL\n * preset's defaults, so the new preset's defaults must re-seed on top of a\n * clean slate. Session overrides replay last — the user's own switches in\n * this session outrank either preset's defaults.\n */\n ctx.on('agent-preset/selected', (sessionId: unknown, presetId: unknown) => {\n try {\n if (typeof sessionId !== 'string' || typeof presetId !== 'string') return undefined;\n const defaults = presetTools.defaultsFor(presetId) ?? { tools: [], skills: [] };\n const overrides = sessionOverrides.overridesFor(sessionId);\n return capabilities.reseed(sessionId, defaults, overrides);\n } catch {\n return undefined;\n }\n });\n\n /**\n * Re-mask when the tool registry changes. seed() can only mask names that\n * are registered at that moment (tools.restrict refuses unknown names), so\n * an on-demand MCP server that connects AFTER the session was created would\n * otherwise arrive with every tool enabled, preset default or not. The\n * registry broadcasts every layer change through this event — late\n * registration, a reconnect's fresh generation, a teardown — and remask is\n * idempotent over names already masked.\n *\n * The guard breaks the echo loop: our own restrict() calls re-emit\n * tools/change, and without it every mask write would schedule another\n * sweep. A change arriving mid-sweep is not lost, though: it is marked\n * pending and one trailing sweep runs when the current one settles, so an\n * external registration landing while a session's slow queue is still\n * processing does not leave stale masks behind.\n */\n let remasking = false;\n let pendingSweep = false;\n ctx.on('tools/change', () => {\n if (remasking) {\n pendingSweep = true;\n return undefined;\n }\n try {\n const agents = ctx.get('agents');\n if (agents === undefined || typeof agents.list !== 'function') return undefined;\n return (async () => {\n do {\n remasking = true;\n pendingSweep = false;\n try {\n let live;\n try {\n live = agents.list();\n } catch {\n // The registry is mid-reload; the next change event re-sweeps.\n return;\n }\n for (const agent of live) {\n try {\n const sessionId = agent?.id;\n if (typeof sessionId !== 'string') continue;\n const presetId = ctx.get('agentPresets')?.composedPreset(agent.ctx);\n const defaults = presetId === undefined ? undefined : presetTools.defaultsFor(presetId);\n const overrides = sessionOverrides.overridesFor(sessionId);\n const hasToolDefaults = defaults !== undefined && defaults.tools.length > 0;\n const hasToolOverrides = overrides !== undefined\n && (Object.keys(overrides.mcpServers).length > 0\n || Object.keys(overrides.mcpTools).length > 0\n || Object.keys(overrides.systemTools).length > 0);\n if (!hasToolDefaults && !hasToolOverrides) continue;\n await capabilities.remask(\n sessionId,\n defaults ?? { tools: [], skills: [] },\n overrides,\n );\n } catch {\n // One session whose stored positions cannot be read keeps its\n // current masks; the sweep continues for the others.\n }\n }\n } finally {\n remasking = false;\n }\n } while (pendingSweep);\n })();\n } catch {\n return undefined;\n }\n });\n}\n","/**\n * The connection half of MCP visibility: which servers this host DECLARES,\n * and how to restart one on demand.\n *\n * The tool registry answers \"which tools can the model call right now\" — the\n * only truth the switches act on — but it says nothing about a server whose\n * local process is down. An on-demand server (IDA, dnSpy, msfrpcd; also the\n * mock server used in tests) is configured in the host composition yet\n * registers nothing while down. The loader entry is that missing half of the\n * truth: it is where the server is declared, and restarting it is exactly\n * what an HMR reload of that entry does.\n *\n * Nothing here polls. The MCP client already owns connection state and\n * retries on its own schedule; the panel only reads the declaration on demand\n * and restarts an entry when the user asks.\n */\nimport { HttpError } from './errors.js';\nimport type { HostServices, LoaderEntryLike } from './types.js';\n\nconst MCP_CLIENT_NAME = '@deepseek-ai/dsh-mcp-client';\n\nfunction entryConfig(entry: LoaderEntryLike): Record<string, unknown> | undefined {\n const config = entry.options?.config;\n if (config === null || typeof config !== 'object' || Array.isArray(config)) return undefined;\n return config as Record<string, unknown>;\n}\n\nfunction declaredServerName(entry: LoaderEntryLike): string | undefined {\n // The loader stores the plugin package name on `options.name` (the field it\n // imports and logs), NOT a top-level `name`. Reading the wrong one silently\n // matches nothing.\n if (entry.options?.name !== MCP_CLIENT_NAME) return undefined;\n const serverName = entryConfig(entry)?.['serverName'];\n return typeof serverName === 'string' && serverName !== '' ? serverName : undefined;\n}\n\n/**\n * Server names the host composition declares via `@deepseek-ai/dsh-mcp-client`\n * entries — connected or not. Matched by plugin name, not by the `serverName`\n * key: an unrelated entry carrying a same-named config key is not an MCP\n * server.\n */\nexport function readConfiguredMcpServers(ctx: HostServices): Set<string> {\n const servers = new Set<string>();\n const loader = ctx.loader;\n if (loader === undefined) return servers;\n try {\n for (const entry of loader.entries()) {\n // A deliberately disabled entry is not \"no tools registered\" — it is\n // off by config. Listing it would promise a reload that can only 409.\n if (entry.disabled === true) continue;\n const name = declaredServerName(entry);\n if (name !== undefined) servers.add(name);\n }\n } catch {\n // A tree mid-reload reports what it could walk; the panel degrades by\n // showing fewer servers rather than failing the page.\n }\n return servers;\n}\n\nfunction entryFor(ctx: HostServices, server: string): LoaderEntryLike | undefined {\n const loader = ctx.loader;\n if (loader === undefined) return undefined;\n try {\n for (const entry of loader.entries()) {\n if (declaredServerName(entry) === server) return entry;\n }\n } catch {\n return undefined;\n }\n return undefined;\n}\n\n/**\n * Reload one declared MCP server's plugin instance: dispose its fiber, then\n * refresh the entry — the loader's own hot-swap pair. This is exactly a\n * manual HMR reload: it tears down the current connection (and stops its\n * reconnect timer) and re-initializes the client, which reconnects and re-syncs\n * the whole tool generation.\n *\n * Honest scope of what this guarantees: only that a fresh connection attempt\n * starts NOW instead of waiting out the client's backoff. It does NOT prove\n * the server was \"down\" (the panel cannot observe connection state), and for a\n * stdio transport the reload respawns the child process. Registration lands\n * asynchronously after refresh resolves; the registry's `tools/change`\n * broadcast re-applies stored defaults, so callers need no readiness wait.\n *\n * Assumes a globally unique serverName: the settings view is host-global, and\n * dsh reserves one serverName per global mcp-client instance, so at most one\n * entry matches here. Agent-scoped instances may reuse a name across Agents,\n * but those are not reachable from this global walk.\n */\nexport async function restartMcpServer(ctx: HostServices, server: string): Promise<void> {\n const entry = entryFor(ctx, server);\n if (entry === undefined) throw new HttpError(404, `MCP server \"${server}\" is not configured on this host`);\n if (entry.disabled === true) {\n throw new HttpError(409, `MCP server \"${server}\" is disabled in the host composition; enable it there instead of reloading it`);\n }\n await entry._dispose();\n await entry.refresh();\n}\n","import type { InspectorPayload, McpServerEntry, McpToolEntry, SkillEntry, ToolEntry } from '../contract.js';\nimport { collectLoadRecords, decideStates, groupMcpTools, indexToolResultSeqs, prunedLoadSeqs, shadowedLoadSeqs } from '../load-state.js';\nimport type { RawEvent } from '../load-state.js';\nimport type { AgentLike, HostServices, SessionCapabilityState } from './types.js';\nimport { RESERVED_TOOL } from './reserved.js';\nimport { readConfiguredMcpServers } from './mcp-connections.js';\n\n/** Coerce a skill summary field to a string, falling back when the runtime shape is wrong. */\nexport function coerceString(value: unknown, fallback: string): string {\n return typeof value === 'string' ? value : fallback;\n}\n\n/**\n * The source ROOT a skill was discovered under. The skills service reports\n * each skill's own directory as resourceBase (`<root>/<name>`), so the group\n * folder is its parent.\n */\nexport function parentDir(path: string): string {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '') return '/';\n const slash = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\\\'));\n return slash > 0 ? trimmed.slice(0, slash) : trimmed;\n}\n\n/**\n * Abbreviate a host path for display: the session cwd becomes a relative\n * path, the user's home becomes `~`. Everything else stays absolute.\n */\nexport function displayPath(path: string, cwd?: string): string {\n if (cwd !== undefined && cwd !== '' && path.startsWith(`${cwd}/`)) return path.slice(cwd.length + 1);\n const home = process.env['HOME'];\n // Require a path boundary: /home/user2 must not abbreviate against /home/user.\n if (home !== undefined && home !== '' && (path === home || path.startsWith(`${home}/`))) return `~${path.slice(home.length)}`;\n return path;\n}\n\n/** The DSH home, matching the runtime's own resolution order. */\nexport function dshHome(): string | undefined {\n return process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n}\n\n// Shared singleton for \"no switches on this session\". Never mutate it: every\n// reader treats the maps as read-only, and a write here would leak into every\n// session that has no state of its own.\nexport const EMPTY_STATE: SessionCapabilityState = {\n skills: new Map(),\n mcpServers: new Map(),\n mcpTools: new Map(),\n systemTools: new Map(),\n userToggled: new Set(),\n};\n\nasync function readAvailable(\n services: HostServices,\n sessionId: string,\n degraded: string[],\n presetDirs: readonly { key: string; path: string }[] = [],\n): Promise<{ name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[]> {\n const skills = services.get('skills');\n if (skills === undefined) {\n degraded.push('skills service unavailable');\n return [];\n }\n try {\n const agents = services.get('agents');\n if (agents === undefined) {\n degraded.push('agents service unavailable: session skill view cannot be determined');\n return [];\n }\n const agent = agents.get(sessionId);\n if (agent === undefined) {\n degraded.push(`session agent \"${sessionId}\" unavailable: session skill view cannot be determined`);\n return [];\n }\n const cwd = agent.session?.header?.cwd;\n const list = await skills.list({ ...(cwd === undefined ? {} : { cwd }), scope: agent });\n // A masked skill is listed twice: the original entry and the same-name\n // shadow that withdrew model invocation. Both this panel's own switches and\n // the preset panel's produce such a shadow, so a shadow is recorded as\n // \"off\" rather than skipped -- skipping it made a preset-disabled skill\n // vanish from this panel entirely, leaving the user unable to see it, and\n // unable to switch it back on. Keeping the first entry per name preserves\n // the richer original description.\n const out: { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }[] = [];\n const seen = new Map<string, { name: string; description?: string; masked?: boolean; source: string; provider: string; path?: string; group?: string }>();\n // A custom dir that lives inside a preset's own directory is that\n // preset's bundled skills (shipped presets register their skills/ via\n // customSkillDirs). Grouping is display-only; `source` stays the raw\n // runtime value so the open-folder route can still resolve by it.\n const groupFor = (source: string, rawRoot: string): string | undefined => {\n if (source !== 'custom' || rawRoot === '') return undefined;\n const owner = presetDirs.find((p) => rawRoot === p.path || rawRoot.startsWith(`${p.path}/`));\n return owner === undefined ? undefined : `preset:${owner.key}`;\n };\n for (const item of list) {\n if (typeof item.name !== 'string' || item.name === '') continue;\n const masked = item.invocation?.modelInvocable === false;\n const source = coerceString(item.source, 'unknown');\n const provider = coerceString(item.provider, 'unknown');\n // resourceBase carries the skill's own directory — the group folder is\n // its parent, abbreviated for display (~/…, cwd-relative).\n const base = item.resourceBase;\n const rawPath = base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory'\n ? coerceString((base as { path?: unknown }).path, '')\n : '';\n const rawRoot = rawPath === '' ? '' : parentDir(rawPath);\n const path = rawRoot === '' ? '' : displayPath(rawRoot, cwd);\n const group = groupFor(source, rawRoot);\n const existing = seen.get(item.name);\n if (existing !== undefined) {\n if (masked) existing.masked = true;\n // The shadow registered by the panel has provider 'capability-panel'.\n // When the shadow appears before the original in the listing, keep\n // the original's meaningful provenance.\n if (existing.provider === 'capability-panel' && provider !== 'capability-panel') {\n existing.source = source;\n existing.provider = provider;\n if (path !== '') existing.path = path;\n if (group !== undefined) existing.group = group;\n }\n continue;\n }\n const description = typeof item.description === 'string' ? item.description : undefined;\n const row = {\n name: item.name,\n ...(description === undefined ? {} : { description }),\n ...(masked ? { masked: true } : {}),\n source,\n provider,\n ...(path === '' ? {} : { path }),\n ...(group === undefined ? {} : { group }),\n };\n seen.set(item.name, row);\n out.push(row);\n }\n return out;\n } catch (error) {\n degraded.push(`skills read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\n/**\n * Read load facts off the LIVE session's in-memory log — the same object the\n * agent loop itself reads to assemble the next request.\n *\n * This used to go through `sessionQuery.readSession` + `listEvents`, which are\n * built for cross-session/cold reads: each call structuredClones the ENTIRE\n * event log, `readSession` additionally replay-validates it via Session.create,\n * and a write landing between the two parallel reads forced a whole second\n * round. On a long session (60k events / tens of MB) that meant four full-log\n * clones plus two full replays of synchronous CPU work on every panel open —\n * blocking the Node event loop and freezing the GUI served by the same process.\n *\n * The live session needs none of that: `snapshotEvents()` hands back borrowed\n * references (zero-copy), and `surface.nodes` is maintained incrementally as\n * events land, so \"what the model sees right now\" is an O(1) membership test.\n * Scanning references for the few `skill` tool calls costs microseconds even\n * on the longest log. Both reads happen in one synchronous tick, so the view\n * is always self-consistent — no cross-read race, no retry loop.\n *\n * A session without a live in-memory view (e.g. restored but not yet attached)\n * degrades honestly instead of paying for a cold-log read the panel never\n * asked for.\n */\nfunction readLogFacts(\n services: HostServices,\n sessionId: string,\n degraded: string[],\n): { loads: ReturnType<typeof collectLoadRecords>; shadowed: Set<number>; pruned: Set<number> } {\n const empty = { loads: [], shadowed: new Set<number>(), pruned: new Set<number>() };\n try {\n const session = services.get('agents')?.get(sessionId)?.session;\n const nodes = session?.surface?.nodes;\n if (session === undefined || typeof session.snapshotEvents !== 'function' || !Array.isArray(nodes)) {\n degraded.push('live session view unavailable: load states cannot be determined');\n return empty;\n }\n // snapshotEvents must stay BOUND to the session: the real Session's\n // signature is snapshotEvents(fromSeq = 0, toSeqExclusive = this.seq),\n // so a detached call crashes on `this.seq`.\n const events = session.snapshotEvents();\n // A wrong shape must surface as degraded, never read as \"no loads\".\n if (!Array.isArray(events)) {\n degraded.push('snapshotEvents() returned an unexpected shape; cannot read skill loads');\n return empty;\n }\n const surfaceSeqs = new Set<number>();\n for (const seq of nodes) if (typeof seq === 'number') surfaceSeqs.add(seq);\n const loads = collectLoadRecords(events as readonly RawEvent[]);\n const resultSeqs = indexToolResultSeqs(events);\n return {\n loads,\n shadowed: shadowedLoadSeqs(loads, resultSeqs, surfaceSeqs),\n pruned: prunedLoadSeqs(loads, resultSeqs, surfaceSeqs, events),\n };\n } catch (error) {\n degraded.push(`event read failed: ${error instanceof Error ? error.message : String(error)}`);\n return empty;\n }\n}\n\nexport function readMcp(\n services: HostServices,\n degraded: string[],\n disabledServers: ReadonlySet<string>,\n disabledTools: ReadonlySet<string>,\n agent?: AgentLike,\n presetName?: string,\n presetPath?: string,\n maskedServerNames?: ReadonlyMap<string, readonly string[]>,\n): McpServerEntry[] {\n const tools = services.get('tools');\n if (tools === undefined) {\n degraded.push('tools service unavailable');\n return [];\n }\n try {\n const names: string[] = [];\n const descriptions = new Map<string, string>();\n const collect = (scope: AgentLike | undefined): void => {\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || !schema.name.startsWith('mcp__')) continue;\n if (!names.includes(schema.name)) {\n names.push(schema.name);\n if (typeof schema.description === 'string' && schema.description !== '') descriptions.set(schema.name, schema.description);\n }\n }\n };\n collect(undefined);\n if (agent !== undefined) collect(agent);\n const globalNames = new Set<string>();\n for (const schema of tools.schemas()) {\n if (typeof schema.name === 'string' && schema.name.startsWith('mcp__')) globalNames.add(schema.name);\n }\n // The session's own view is the reachability truth: a name absent from it\n // is not callable by the model right now, whoever masked it (this panel's\n // switch, or another plugin's session-scoped restrict — e.g. a lazy-load\n // manager). The list still shows the name (merged from the global view so\n // a masked row can be re-enabled), but it must not read as \"on\".\n const sessionNames = new Set<string>();\n if (agent !== undefined) {\n for (const schema of tools.schemas(agent)) {\n if (typeof schema.name === 'string' && schema.name.startsWith('mcp__')) sessionNames.add(schema.name);\n }\n }\n const reachable = (name: string): boolean => agent === undefined || sessionNames.has(name);\n const configuredServers = readConfiguredMcpServers(services);\n const groups: McpServerEntry[] = groupMcpTools(names).map((group) => {\n const enabled = !disabledServers.has(group.server);\n const entries: McpToolEntry[] = group.tools.map((tool) => {\n const name = `mcp__${group.server}__${tool}`;\n const description = descriptions.get(name);\n return {\n name,\n label: tool,\n ...(description === undefined ? {} : { description }),\n enabled: enabled && !disabledTools.has(name) && reachable(name),\n };\n });\n const allGlobal = group.tools.every((tool) => globalNames.has(`mcp__${group.server}__${tool}`));\n const source = allGlobal ? 'host' : (presetName ?? 'preset');\n const rawPath = allGlobal ? dshHome() : presetPath;\n return { server: group.server, tools: entries, enabled, ...(configuredServers.has(group.server) ? { reconnectable: true } : {}), source, ...(rawPath === undefined ? {} : { path: displayPath(rawPath) }) };\n });\n // A declared server that currently registers no tools would vanish with\n // every mask the session holds for it. The row stays: marked \"no tools\n // registered\" (a proven fact — NOT a claim the service is down, which the\n // panel cannot observe), listing exactly the names this session already\n // stores off (the server mask's recorded roster first, then per-tool names).\n const hostPath = dshHome();\n for (const server of configuredServers) {\n if (groups.some((group) => group.server === server)) continue;\n const prefix = `mcp__${server}__`;\n const roster = maskedServerNames?.get(server) ?? [...disabledTools].filter((name) => name.startsWith(prefix)).sort();\n groups.push({\n server,\n tools: roster.map((name) => ({ name, label: name.slice(prefix.length), enabled: false })),\n enabled: false,\n unavailable: true,\n reconnectable: true,\n source: 'host',\n ...(hostPath === undefined ? {} : { path: displayPath(hostPath) }),\n });\n }\n groups.sort((a, b) => a.server.localeCompare(b.server));\n return groups;\n } catch (error) {\n degraded.push(`tool read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\nexport function readSystemTools(\n services: HostServices,\n degraded: string[],\n disabledTools: ReadonlySet<string>,\n agent?: AgentLike,\n): ToolEntry[] {\n const tools = services.get('tools');\n if (tools === undefined) {\n // Each reader owns its diagnostic, while the combined payload emits it once.\n if (!degraded.includes('tools service unavailable')) degraded.push('tools service unavailable');\n return [];\n }\n try {\n // What the agent can actually call right now. A tool the preset denied is\n // absent here while still present globally, and the panel has to tell\n // those apart: the row stays listed, because the session may switch it\n // back on, but reporting it as enabled would claim the model can reach\n // something it cannot.\n let reachable: Set<string> | undefined;\n if (agent !== undefined) {\n reachable = new Set<string>();\n for (const schema of tools.schemas(agent)) {\n if (typeof schema.name === 'string') reachable.add(schema.name);\n }\n }\n const byName = new Map<string, ToolEntry>();\n const collect = (scope: AgentLike | undefined): void => {\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || schema.name.startsWith('mcp__') || byName.has(schema.name)) continue;\n const description = typeof schema.description === 'string' && schema.description !== '' ? schema.description : undefined;\n byName.set(schema.name, {\n name: schema.name,\n label: schema.name,\n ...(description === undefined ? {} : { description }),\n enabled:\n !disabledTools.has(schema.name) && (reachable === undefined || reachable.has(schema.name)),\n ...(schema.name === RESERVED_TOOL ? { reserved: true } : {}),\n });\n }\n };\n collect(undefined);\n if (agent !== undefined) collect(agent);\n return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));\n } catch (error) {\n degraded.push(`tool read failed: ${error instanceof Error ? error.message : String(error)}`);\n return [];\n }\n}\n\nexport async function buildPayload(\n services: HostServices,\n sessionId: string | null,\n capabilityState: SessionCapabilityState = EMPTY_STATE,\n blocked: Record<string, number> = {},\n): Promise<InspectorPayload> {\n const degraded: string[] = [];\n const disabledSkills = new Set(capabilityState.skills.keys());\n const disabledServers = new Set(capabilityState.mcpServers.keys());\n const disabledTools = new Set(capabilityState.mcpTools.keys());\n const disabledSystem = new Set(capabilityState.systemTools.keys());\n if (sessionId === null) {\n return {\n sessionId: null,\n skills: [],\n mcp: readMcp(services, degraded, disabledServers, disabledTools),\n systemTools: readSystemTools(services, degraded, disabledSystem),\n blocked,\n ...(degraded.length > 0 ? { degraded } : {}),\n };\n }\n const agent = services.get('agents')?.get(sessionId);\n let presetName: string | undefined;\n let presetPath: string | undefined;\n let presetDirs: { key: string; path: string }[] = [];\n if (agent !== undefined) {\n const presetId = services.get('agentPresets')?.composedPreset(agent.ctx);\n try {\n const presets = await services.get('agentPresets')?.list();\n presetDirs = (presets ?? [])\n .filter((p) => typeof p.path === 'string' && p.path !== '')\n .map((p) => ({ key: p.name ?? p.id, path: p.path as string }));\n if (presetId !== undefined) {\n const preset = presets?.find((p) => p.id === presetId);\n presetName = preset?.name ?? presetId;\n presetPath = preset?.path;\n }\n } catch {\n presetName = presetId;\n }\n }\n const available = await readAvailable(services, sessionId, degraded, presetDirs);\n const logFacts = readLogFacts(services, sessionId, degraded);\n const skills: SkillEntry[] = decideStates(available, logFacts.loads, logFacts.shadowed, disabledSkills, logFacts.pruned);\n return {\n sessionId,\n skills,\n mcp: readMcp(\n services,\n degraded,\n disabledServers,\n disabledTools,\n agent,\n presetName,\n presetPath,\n new Map([...capabilityState.mcpServers].map(([server, mask]) => [server, mask.names])),\n ),\n systemTools: readSystemTools(services, degraded, disabledSystem, agent),\n blocked,\n ...(degraded.length > 0 ? { degraded } : {}),\n };\n}\n","import { errorMessage, HttpError } from './errors.js';\nimport { displayPath, dshHome, parentDir } from './catalog.js';\nimport { readConfiguredMcpServers } from './mcp-connections.js';\nimport type {\n AgentPresetLike,\n AgentPresetsService,\n HostServices,\n PresetMcpServer,\n PresetSkillRow,\n PresetToolEntry,\n PresetToolPayload,\n PresetToolRow,\n SkillsService,\n ToolsService,\n ToolkitSettings,\n} from './types.js';\nimport type { ToolkitSettingsAccess } from './settings-scope.js';\nimport { groupMcpTools } from '../load-state.js';\nimport { RESERVED_TOOL } from './reserved.js';\nexport { RESERVED_TOOL };\n\nfunction requireService<T>(service: T | undefined, message: string): T {\n if (service === undefined) throw new HttpError(503, message);\n return service;\n}\n\ninterface ToolSummary {\n readonly name: string;\n readonly description?: string;\n}\n\nfunction toolSummaries(tools: ToolsService, scope: unknown): ToolSummary[] {\n const entries = new Map<string, ToolSummary>();\n for (const schema of tools.schemas(scope)) {\n if (typeof schema.name !== 'string' || schema.name === '' || entries.has(schema.name)) continue;\n entries.set(schema.name, {\n name: schema.name,\n ...(typeof schema.description === 'string' ? { description: schema.description } : {}),\n });\n }\n return [...entries.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Read the skills one preset can see, marking those that came from the reading\n * process's project root.\n *\n * The panel has no agent and therefore no session cwd, so it reports what THIS\n * workspace would contribute. A project skill is marked rather than dropped:\n * hiding it would silently shorten the list, while marking it says plainly\n * that a session opened elsewhere will not see the row.\n *\n * Each row also carries the same provenance the session payload reports:\n * the raw runtime source, the discovery root (parent of the skill's own\n * directory, abbreviated for display), and a `preset:<name>` group when the\n * root sits inside a preset's directory.\n */\nasync function presetSkillRows(\n skills: SkillsService,\n scope: unknown,\n disabled: ReadonlySet<string>,\n cwd: string,\n presetDirs: readonly { key: string; path: string }[] = [],\n): Promise<PresetSkillRow[]> {\n const seen = new Map<string, PresetSkillRow>();\n const add = (summaries: readonly { name?: unknown; description?: unknown; source?: unknown; resourceBase?: unknown }[], project: boolean): void => {\n for (const summary of summaries) {\n if (typeof summary.name !== 'string' || summary.name === '' || seen.has(summary.name)) continue;\n const description = typeof summary.description === 'string' ? summary.description : undefined;\n const source = typeof summary.source === 'string' ? summary.source : undefined;\n const base = summary.resourceBase;\n const rawPath = base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory'\n ? (base as { path?: unknown }).path\n : undefined;\n const rawRoot = typeof rawPath === 'string' && rawPath !== '' ? parentDir(rawPath) : '';\n const path = rawRoot === '' ? undefined : displayPath(rawRoot, cwd);\n // A custom dir that lives inside a preset's own directory is that\n // preset's bundled skills — the settings page groups them under the\n // preset's name, exactly like the session panel.\n const owner = source === 'custom' && rawRoot !== ''\n ? presetDirs.find((p) => rawRoot === p.path || rawRoot.startsWith(`${p.path}/`))\n : undefined;\n seen.set(summary.name, {\n name: summary.name,\n ...(description === undefined ? {} : { description }),\n enabled: !disabled.has(summary.name),\n ...(project ? { project: true } : {}),\n ...(source === undefined ? {} : { source }),\n ...(path === undefined ? {} : { path }),\n ...(owner === undefined ? {} : { group: `preset:${owner.key}` }),\n });\n }\n };\n // Without cwd first: anything the workspace-aware read adds on top of this\n // set is exactly what the project root contributed.\n add(await skills.list({ scope }), false);\n add(await skills.list({ scope, cwd }), true);\n return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));\n}\n\nasync function presetAndTools(\n agentPresets: AgentPresetsService,\n tools: ToolsService,\n presetId: string,\n): Promise<{ preset: AgentPresetLike; tools: ToolSummary[] }> {\n const presets = await agentPresets.list();\n const preset = presets.find((entry) => entry.id === presetId);\n if (preset === undefined) throw new HttpError(404, `preset \"${presetId}\" is not available`);\n if (preset.broken !== undefined) throw new HttpError(409, `preset \"${presetId}\" is broken: ${preset.broken}`);\n try {\n const scope = await agentPresets.standingKeyFor(presetId);\n return { preset, tools: toolSummaries(tools, scope) };\n } catch (error) {\n throw new HttpError(503, `preset \"${presetId}\" tools are unavailable: ${errorMessage(error)}`);\n }\n}\n\nexport interface PresetToolController {\n list(): Promise<PresetToolPayload>;\n set(presetId: string, name: string, enabled: boolean): Promise<PresetToolPayload>;\n /** Toggle every tool of one MCP server in a single write. */\n setServer(presetId: string, server: string, enabled: boolean): Promise<PresetToolPayload>;\n setSkill(presetId: string, name: string, enabled: boolean): Promise<PresetToolPayload>;\n /**\n * The stored defaults for one preset, for the enforcement listener. Returns\n * undefined when settings cannot be read: enforcement treats that as \"no\n * defaults\" rather than fail a session over a preference it cannot read.\n */\n defaultsFor(presetId: string): { tools: readonly string[]; skills: readonly string[] } | undefined;\n}\n\nexport function createPresetToolController(ctx: HostServices, access: ToolkitSettingsAccess): PresetToolController {\n const settingsScope = (): ReturnType<ToolkitSettingsAccess['scope']> => access.scope();\n\n const services = (): { agentPresets: AgentPresetsService; tools: ToolsService; settings: NonNullable<ReturnType<ToolkitSettingsAccess['scope']>> } => ({\n agentPresets: requireService(ctx.get('agentPresets'), 'agentPresets service unavailable'),\n tools: requireService(ctx.get('tools'), 'tools service unavailable'),\n settings: requireService(settingsScope(), 'settings service unavailable'),\n });\n\n const list = async (): Promise<PresetToolPayload> => {\n const { agentPresets, tools, settings: settingsScope } = services();\n const stored = settingsScope.get();\n const configured = stored.presets;\n const skills = ctx.get('skills');\n // The reading process's workspace: the panel has no session, so this is\n // the only project root it can honestly report against.\n const cwd = process.cwd();\n const presets = await agentPresets.list();\n const presetDirs = presets\n .filter((p) => typeof p.path === 'string' && p.path !== '')\n .map((p) => ({ key: p.name ?? p.id, path: p.path as string }));\n // Global tool names tell a host-composition MCP server from a\n // preset-scoped one, the same test the session payload uses.\n const configuredServers = readConfiguredMcpServers(ctx);\n const globalNames = new Set<string>();\n for (const schema of tools.schemas()) {\n if (typeof schema.name === 'string' && schema.name.startsWith('mcp__')) globalNames.add(schema.name);\n }\n const entries: PresetToolEntry[] = await Promise.all(presets.map(async (preset) => {\n let entries: ToolSummary[] = [];\n let skillRows: PresetSkillRow[] = [];\n // A preset that fails to MOUNT (valid yaml, but a row's config no longer\n // satisfies a plugin's schema after a host upgrade) must not take the\n // whole listing down with it: report it like a broken preset, with the\n // mount error as the reason, and let the others list normally.\n let mountError: string | undefined;\n if (preset.broken === undefined) {\n let scope: unknown;\n try {\n scope = await agentPresets.standingKeyFor(preset.id);\n entries = toolSummaries(tools, scope);\n if (skills !== undefined) {\n const disabledSkills = new Set(stored.presetSkills[preset.id] ?? []);\n skillRows = await presetSkillRows(skills, scope, disabledSkills, cwd, presetDirs);\n }\n } catch (error) {\n mountError = errorMessage(error);\n }\n }\n const disabled = new Set(configured[preset.id] ?? []);\n const byName = new Map(entries.map((entry) => [entry.name, entry]));\n const row = (name: string, label: string): PresetToolRow => {\n const description = byName.get(name)?.description;\n return {\n name,\n label,\n ...(description === undefined ? {} : { description }),\n // The reserved transport is reported as on because it IS on: the\n // enforcement listener drops it from the deny list unconditionally.\n // A stale entry naming it (hand-edited, or written before it became\n // reserved) must not render a switch that says \"off\" over a tool the\n // model can still call -- especially since that switch is locked and\n // the user could never clear the claim.\n enabled: name === RESERVED_TOOL || !disabled.has(name),\n ...(name === RESERVED_TOOL ? { reserved: true } : {}),\n };\n };\n // Same split the session panel uses, from the same helper: MCP tools\n // collapse under their server, everything else is a system tool.\n const presetName = preset.name ?? preset.id;\n const mcp: PresetMcpServer[] = groupMcpTools(entries.map((entry) => entry.name)).map((group) => {\n const tools = group.tools.map((tool) => row(`mcp__${group.server}__${tool}`, tool));\n const allGlobal = group.tools.every((tool) => globalNames.has(`mcp__${group.server}__${tool}`));\n const rawPath = allGlobal ? dshHome() : preset.path;\n return {\n server: group.server,\n tools,\n enabled: tools.some((tool) => tool.enabled),\n ...(configuredServers.has(group.server) ? { reconnectable: true } : {}),\n source: allGlobal ? 'host' : presetName,\n ...(rawPath === undefined ? {} : { path: displayPath(rawPath) }),\n };\n });\n // A declared server that currently registers no tools would otherwise\n // vanish with its stored default. The row stays: dimmed, marked \"no\n // tools registered\" (a proven fact — NOT a claim that the service is\n // down, which the panel cannot observe), listing exactly the names this\n // preset already stores off for it.\n const hostPath = dshHome();\n for (const server of configuredServers) {\n if (mcp.some((group) => group.server === server)) continue;\n const prefix = `mcp__${server}__`;\n mcp.push({\n server,\n tools: [...disabled]\n .filter((name) => name.startsWith(prefix))\n .sort()\n .map((name) => row(name, name.slice(prefix.length))),\n enabled: false,\n unavailable: true,\n reconnectable: true,\n source: 'host',\n ...(hostPath === undefined ? {} : { path: displayPath(hostPath) }),\n });\n }\n // Declared servers sort in among the connected ones: the list is an\n // inventory of what this preset can configure, not of what is up.\n mcp.sort((a, b) => a.server.localeCompare(b.server));\n const systemTools = entries\n .filter((entry) => !entry.name.startsWith('mcp__'))\n .map((entry) => row(entry.name, entry.name));\n return {\n id: preset.id,\n name: preset.name ?? preset.id,\n trust: preset.trust,\n ...(preset.description === undefined ? {} : { description: preset.description }),\n ...(preset.broken !== undefined\n ? { broken: preset.broken }\n : mountError === undefined\n ? {}\n : { broken: `failed to mount: ${mountError}` }),\n skills: skillRows,\n mcp,\n systemTools,\n };\n }));\n return { presets: entries, writable: ctx.get('settings')?.writable === true };\n };\n\n // Every write is read-modify-write over the whole namespace, serialized\n // through the SHARED access queue (settings-scope.ts) so a preset edit and\n // a session toggle cannot interleave their snapshots.\n\n /** Persist one preset's disabled set for one registry, then re-read. */\n const persist = (\n presetId: string,\n settingsScope: NonNullable<ReturnType<ToolkitSettingsAccess['scope']>>,\n disabled: ReadonlySet<string>,\n registry: 'presets' | 'presetSkills' = 'presets',\n ): Promise<PresetToolPayload> => access.serialize(async () => {\n const stored = settingsScope.get();\n // All maps are always present: each carries a schema default, so a section\n // written before skills/sessions existed still resolves with empty ones\n // rather than missing keys. The compatibility layer is the schema, not a\n // guard here -- an optional read would only imply a shape the settings\n // service cannot hand back.\n const prune = (map: Readonly<Record<string, readonly string[]>>): Record<string, readonly string[]> =>\n Object.fromEntries(Object.entries(map).filter(([, value]) => value.length > 0));\n const next = prune({ ...stored[registry], [presetId]: [...disabled].sort() });\n const presets = registry === 'presets' ? next : prune(stored.presets);\n const presetSkills = registry === 'presetSkills' ? next : prune(stored.presetSkills);\n // `replace`, not `update`: the settings merge recurses into the stored\n // section, so a merge patch can only ever add or grow keys. Re-enabling a\n // preset's last disabled tool has to REMOVE its key, and only a wholesale\n // replace of this namespace's user section can express that. `sessions`\n // passes through untouched: session-bound overrides belong to the other\n // writer sharing this namespace, and dropping them here would silently\n // unpersist every session's switches.\n await settingsScope.replace({ presets, presetSkills, sessions: stored.sessions });\n return list();\n });\n\n return {\n defaultsFor(presetId) {\n let stored: ToolkitSettings;\n try {\n const settings = settingsScope();\n if (settings === undefined) return undefined;\n stored = settings.get();\n } catch {\n return undefined;\n }\n const tools = (stored.presets[presetId] ?? []).filter((name) => name !== RESERVED_TOOL);\n return { tools, skills: stored.presetSkills[presetId] ?? [] };\n },\n list,\n async set(presetId, name, enabled) {\n if (name === RESERVED_TOOL && !enabled) {\n throw new HttpError(409, 'run_code is the reserved Code Mode transport and cannot be restricted');\n }\n const { agentPresets, tools, settings: settingsScope } = services();\n const { tools: available } = await presetAndTools(agentPresets, tools, presetId);\n if (!available.some((tool) => tool.name === name)) throw new HttpError(404, `tool \"${name}\" is not available in preset \"${presetId}\"`);\n const disabled = new Set(settingsScope.get().presets[presetId] ?? []);\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n return persist(presetId, settingsScope, disabled);\n },\n async setServer(presetId, server, enabled) {\n const { agentPresets, tools, settings: settingsScope } = services();\n const { tools: available } = await presetAndTools(agentPresets, tools, presetId);\n // One write for the whole server: with 200 MCP tools behind two servers,\n // switching them one request at a time is the interaction this replaces.\n const prefix = `mcp__${server}__`;\n const names = available.map((tool) => tool.name).filter((name) => name.startsWith(prefix));\n if (names.length === 0) throw new HttpError(404, `MCP server \"${server}\" is not available in preset \"${presetId}\"`);\n const disabled = new Set(settingsScope.get().presets[presetId] ?? []);\n for (const name of names) {\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n }\n return persist(presetId, settingsScope, disabled);\n },\n async setSkill(presetId, name, enabled) {\n const { agentPresets, settings: settingsScope } = services();\n const skills = requireService(ctx.get('skills'), 'skills service unavailable');\n const presets = await agentPresets.list();\n const preset = presets.find((entry) => entry.id === presetId);\n if (preset === undefined) throw new HttpError(404, `preset \"${presetId}\" is not available`);\n if (preset.broken !== undefined) throw new HttpError(409, `preset \"${presetId}\" is broken: ${preset.broken}`);\n let visible: PresetSkillRow[];\n try {\n const scope = await agentPresets.standingKeyFor(presetId);\n visible = await presetSkillRows(skills, scope, new Set(), process.cwd());\n } catch (error) {\n throw new HttpError(503, `preset \"${presetId}\" skills are unavailable: ${errorMessage(error)}`);\n }\n if (!visible.some((skill) => skill.name === name)) {\n throw new HttpError(404, `skill \"${name}\" is not available in preset \"${presetId}\"`);\n }\n const disabled = new Set(settingsScope.get().presetSkills[presetId] ?? []);\n if (enabled) disabled.delete(name);\n else disabled.add(name);\n return persist(presetId, settingsScope, disabled, 'presetSkills');\n },\n };\n}\n","/** The minimal readable surface of a request object inside a route handler. */\nexport interface IncomingLike {\n readonly url?: string;\n readonly method?: string;\n readonly headers: Record<string, string | string[] | undefined>;\n /**\n * Present on node's http.IncomingMessage. Prefer it for the loopback\n * decision: the Host and Origin headers are client-controlled and forgeable.\n */\n readonly socket?: { readonly remoteAddress?: string };\n}\n\nexport const LOOPBACK_ADDRS: ReadonlySet<string> = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1']);\n\n/**\n * Loopback-only guard.\n *\n * The connection's peer address is the only trustworthy evidence, and it is the\n * only one that still holds when the host binds to a LAN interface\n * (`--host` / `--trusted-host`). The Host and Origin headers are set by the\n * caller, so they are a fallback used only when the socket is unavailable —\n * with neither present, this fails closed.\n */\nexport function isLoopback(req: IncomingLike): boolean {\n const addr = req.socket?.remoteAddress;\n if (typeof addr === 'string') return LOOPBACK_ADDRS.has(addr);\n const originHeader = req.headers['origin'];\n const origin = typeof originHeader === 'string' ? originHeader : '';\n if (origin === '') {\n const hostHeader = req.headers['host'];\n const host = typeof hostHeader === 'string' ? hostHeader : '';\n return /^(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/.test(host);\n }\n return /^https?:\\/\\/(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/.test(origin);\n}\n","import { exec } from 'node:child_process';\n\n/**\n * Open a folder in the system file manager.\n *\n * Covers macOS (`open`), Windows (`start`), and every freedesktop Linux\n * desktop (GNOME, KDE, XFCE, …) via `xdg-open`. Server-only Linux distros\n * that lack a desktop cannot open folders, but the error is graceful.\n */\nexport function openFolder(path: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'start'\n : 'xdg-open';\n exec(`${cmd} \"${path}\"`, (error) => {\n if (error) reject(new Error(`failed to open folder: ${error.message}`));\n else resolve();\n });\n });\n}","import { isLoopback } from '../loopback.js';\nimport { buildPayload, EMPTY_STATE, parentDir } from './catalog.js';\nimport type { CapabilityController } from './capabilities.js';\nimport { errorMessage, HttpError } from './errors.js';\nimport { restartMcpServer } from './mcp-connections.js';\nimport { openFolder } from './open-folder.js';\nimport type { PresetToolController } from './preset-tools.js';\nimport type { SessionOverrideStore } from './session-overrides.js';\nimport type { StatsStore } from './stats-store.js';\nimport type { CapabilityKind, HostServices, IncomingLike, ServerResponseLike } from './types.js';\n\nexport const ROUTE = '/api/capability-panel';\nconst KINDS: readonly CapabilityKind[] = ['skill', 'mcp-server', 'mcp-tool', 'system-tool'];\n\nclass ClientRequestError extends HttpError {\n constructor(message: string, status: 400 | 413 = 400) {\n super(status, message);\n }\n}\n\nexport function readRequestBody(req: IncomingLike): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (req.on === undefined) {\n reject(new ClientRequestError('request body stream unavailable'));\n return;\n }\n let body = '';\n let settled = false;\n const fail = (error: unknown): void => {\n if (settled) return;\n settled = true;\n reject(error instanceof ClientRequestError ? error : new Error(String(error)));\n };\n req.on('data', (chunk) => {\n if (settled) return;\n body += String(chunk);\n if (body.length > 16_384) fail(new ClientRequestError('request body too large', 413));\n });\n req.on('end', () => {\n if (settled) return;\n settled = true;\n try {\n resolve(body === '' ? {} : JSON.parse(body));\n } catch {\n reject(new ClientRequestError('invalid JSON body'));\n }\n });\n req.on('error', fail);\n });\n}\n\n/**\n * Every response here reflects live process state, so `no-store` is the\n * default and opting out is explicit. The flag reads as what it does: the\n * previous spelling was `cache = true` on the ERROR branch, which left a\n * transient 503 as the only cacheable response the route produced.\n */\nfunction json(res: ServerResponseLike, status: number, body: unknown, allowCaching = false): void {\n res.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n ...(allowCaching ? {} : { 'cache-control': 'no-store' }),\n });\n res.end(JSON.stringify(body));\n}\n\n/**\n * `kind` mirrors the session route's toggle shape: one tool, or a whole MCP\n * server in a single write.\n */\nfunction validatePresetToggle(body: unknown): { presetId: string; kind: 'tool' | 'mcp-server' | 'skill'; name: string; enabled: boolean } {\n if (body === null || typeof body !== 'object') throw new ClientRequestError('invalid request body');\n const record = body as { presetId?: unknown; kind?: unknown; name?: unknown; enabled?: unknown };\n if (typeof record.presetId !== 'string' || record.presetId === '') throw new ClientRequestError('presetId is required');\n const kind = record.kind ?? 'tool';\n if (kind !== 'tool' && kind !== 'mcp-server' && kind !== 'skill') {\n throw new ClientRequestError('kind must be \"tool\", \"mcp-server\" or \"skill\"');\n }\n if (typeof record.name !== 'string' || record.name === '') throw new ClientRequestError('name is required');\n if (typeof record.enabled !== 'boolean') throw new ClientRequestError('enabled must be boolean');\n return { presetId: record.presetId, kind, name: record.name, enabled: record.enabled };\n}\n\nfunction validatePresetContentType(req: IncomingLike, res: ServerResponseLike): boolean {\n const contentType = req.headers['content-type'];\n if (typeof contentType === 'string' && contentType.startsWith('application/json')) return true;\n res.writeHead(415, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('expected application/json');\n return false;\n}\n\nfunction validateToggle(sessionId: string | null, body: unknown): { sessionId: string; kind: CapabilityKind; name: string; enabled: boolean } {\n if (sessionId === null) throw new ClientRequestError('session is required');\n if (body === null || typeof body !== 'object') throw new ClientRequestError('invalid request body');\n const record = body as { kind?: unknown; name?: unknown; enabled?: unknown };\n if (typeof record.kind !== 'string' || !KINDS.includes(record.kind as CapabilityKind) || typeof record.enabled !== 'boolean') {\n throw new ClientRequestError('kind must be skill, mcp-server, mcp-tool or system-tool and enabled must be boolean');\n }\n if (typeof record.name !== 'string' || record.name === '') throw new ClientRequestError('name is required');\n return { sessionId, kind: record.kind as CapabilityKind, name: record.name, enabled: record.enabled };\n}\n\nexport function createRouteHandler(\n services: HostServices,\n capabilities: CapabilityController,\n stats: StatsStore,\n blockedCounts: Record<string, number>,\n presetTools: PresetToolController,\n sessionOverrides: SessionOverrideStore,\n): (req: IncomingLike, res: ServerResponseLike) => Promise<void> {\n return async (req, res) => {\n if (!isLoopback(req)) {\n res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('forbidden');\n return;\n }\n try {\n const url = new URL(req.url ?? '/', 'http://dsh.local');\n if (url.pathname === `${ROUTE}/presets`) {\n if (req.method !== 'GET' && req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (req.method === 'POST') {\n if (!validatePresetContentType(req, res)) return;\n const toggle = validatePresetToggle(await readRequestBody(req));\n json(res, 200, toggle.kind === 'skill'\n ? await presetTools.setSkill(toggle.presetId, toggle.name, toggle.enabled)\n : toggle.kind === 'mcp-server'\n ? await presetTools.setServer(toggle.presetId, toggle.name, toggle.enabled)\n : await presetTools.set(toggle.presetId, toggle.name, toggle.enabled));\n return;\n }\n json(res, 200, await presetTools.list());\n return;\n }\n if (url.pathname === `${ROUTE}/stats`) {\n if (req.method !== 'GET') {\n res.writeHead(405, { allow: 'GET', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n const snapshot = stats.read();\n json(res, 200, { logFile: stats.file, blocked: blockedCounts, records: snapshot.records, ...(snapshot.warnings.length > 0 ? { warnings: snapshot.warnings } : {}) }, true);\n return;\n }\n if (url.pathname === `${ROUTE}/reconnect`) {\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (!validatePresetContentType(req, res)) return;\n const body = await readRequestBody(req);\n if (body === null || typeof body !== 'object') {\n json(res, 400, { error: 'invalid request body' });\n return;\n }\n const record = body as { server?: unknown };\n if (typeof record.server !== 'string' || record.server === '') {\n json(res, 400, { error: 'server is required' });\n return;\n }\n // Restart the entry; tool registration lands asynchronously and the\n // registry's tools/change broadcast re-applies stored defaults.\n await restartMcpServer(services, record.server);\n json(res, 200, { ok: true, server: record.server });\n return;\n }\n if (url.pathname === `${ROUTE}/open-folder`) {\n if (req.method !== 'POST') {\n res.writeHead(405, { allow: 'POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n if (!validatePresetContentType(req, res)) return;\n const body = await readRequestBody(req);\n if (body === null || typeof body !== 'object') {\n json(res, 400, { error: 'invalid request body' });\n return;\n }\n const record = body as { sessionId?: unknown; source?: unknown };\n if (typeof record.source !== 'string' || record.source === '') {\n json(res, 400, { error: 'source is required' });\n return;\n }\n const source = record.source;\n const sessionId = typeof record.sessionId === 'string' ? record.sessionId : null;\n let folderPath: string | undefined;\n // Authoritative path first: re-list the session's skills and take the\n // resourceBase directory of the first entry carrying this source. This\n // covers every source the filesystem provider knows — including\n // 'custom' (customSkillDirs) — without guessing paths.\n if (sessionId !== null) {\n try {\n const skills = services.get('skills');\n const agent = services.get('agents')?.get(sessionId);\n if (skills !== undefined && agent !== undefined) {\n const cwd = agent.session?.header?.cwd;\n const list = await skills.list({ ...(cwd === undefined ? {} : { cwd }), scope: agent });\n for (const item of list) {\n if (item.source !== source) continue;\n const base = item.resourceBase;\n if (base !== null && typeof base === 'object' && (base as { kind?: unknown }).kind === 'directory') {\n const path = (base as { path?: unknown }).path;\n if (typeof path === 'string' && path !== '') {\n // resourceBase is the skill's own folder; the group folder\n // is its parent (the source root).\n folderPath = parentDir(path);\n break;\n }\n }\n }\n }\n } catch { /* listing failed — fall through to the heuristic */ }\n }\n // Heuristic fallbacks for sources with no live skill entry, and for\n // MCP sources (which are preset names or 'host').\n if (folderPath === undefined && (source === 'user-dsh' || source === 'user-agents')) {\n const dshHome = process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n if (dshHome !== undefined) {\n folderPath = source === 'user-dsh' ? `${dshHome}/skills` : `${process.env['DSH_AGENTS_HOME'] ?? `${process.env['HOME']}/.agents`}/skills`;\n }\n } else if (folderPath === undefined && (source === 'project-dsh' || source === 'project-agents')) {\n // Sessionless callers (the settings page) report project rows\n // against the dsh process's own cwd — resolve those the same way.\n const agent = sessionId === null ? undefined : services.get('agents')?.get(sessionId);\n const cwd = agent?.session?.header?.cwd ?? (sessionId === null ? process.cwd() : undefined);\n if (cwd !== undefined) {\n folderPath = source === 'project-dsh' ? `${cwd}/.dsh/skills` : `${cwd}/.agents/skills`;\n }\n } else if (folderPath === undefined && source === 'host') {\n // Host composition entries live under DSH_HOME — open the home.\n folderPath = process.env['DSH_HOME'] ?? (process.env['HOME'] !== undefined ? `${process.env['HOME']}/.dsh` : undefined);\n } else if (folderPath === undefined && source !== 'bundled' && source !== 'runtime') {\n // Anything else is a preset name — look up its path.\n try {\n const presets = await services.get('agentPresets')?.list();\n const preset = presets?.find((p) => p.id === source || p.name === source);\n if (preset !== undefined) folderPath = preset.path;\n } catch { /* preset lookup failed — leave folderPath undefined */ }\n }\n if (folderPath === undefined) {\n json(res, 404, { error: `cannot open folder for source \"${source}\"` });\n return;\n }\n try {\n await openFolder(folderPath);\n json(res, 200, { ok: true });\n } catch (error) {\n json(res, 500, { error: `failed to open folder: ${errorMessage(error)}` });\n }\n return;\n }\n // The catalogue answers the prefix itself, not everything beneath it.\n // Falling through meant /api/capability-panel/bogus returned the catalogue\n // with a 200, which hides a caller's typo and would silently change\n // meaning the day a real sub-path is added under that name. A request\n // whose url is absent is the degenerate case the prefix router can hand\n // down; it reads as the root, not as an unknown path.\n if (url.pathname !== ROUTE && url.pathname !== '/') {\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' });\n res.end('not found');\n return;\n }\n if (req.method !== 'GET' && req.method !== 'POST') {\n res.writeHead(405, { allow: 'GET, POST', 'content-type': 'text/plain; charset=utf-8' });\n res.end('method not allowed');\n return;\n }\n const sessionId = url.searchParams.get('session');\n let persistNote: string | undefined;\n if (req.method === 'POST') {\n const contentType = req.headers['content-type'];\n if (typeof contentType !== 'string' || !contentType.startsWith('application/json')) {\n res.writeHead(415, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('expected application/json');\n return;\n }\n const toggle = validateToggle(sessionId, await readRequestBody(req));\n await capabilities.set(toggle.sessionId, toggle.kind, toggle.name, toggle.enabled);\n // The mask is live; persisting it for the session's next agent is\n // best-effort. A failed write must degrade the payload, not the\n // toggle the user already made.\n try {\n await sessionOverrides.record(toggle.sessionId, toggle.kind, toggle.name, toggle.enabled);\n } catch (error) {\n persistNote = `switch applied for this session but could not be persisted across a restart: ${errorMessage(error)}`;\n }\n }\n const payload = await buildPayload(\n services,\n sessionId,\n sessionId === null ? EMPTY_STATE : (capabilities.state(sessionId) ?? EMPTY_STATE),\n blockedCounts,\n );\n json(res, 200, persistNote === undefined\n ? payload\n : { ...payload, degraded: [...(payload.degraded ?? []), persistNote] });\n } catch (error) {\n json(res, error instanceof HttpError ? error.status : 500, { error: errorMessage(error) });\n }\n };\n}\n","import { HttpError } from './errors.js';\nimport type { ToolkitSettingsAccess } from './settings-scope.js';\nimport type { CapabilityKind, SessionOverrideState } from './types.js';\n\n/**\n * Session-bound switch positions, persisted in the capability-panel settings namespace\n * so a restored session gets its own toggles back after a restart.\n *\n * The binding is the session id: a record is read only by the session that\n * made it, so one session's switch can never leak into another. What changes\n * at a restart is only WHEN it is read — the masks themselves are re-applied\n * by the same capability controller the panel uses, over the fresh agent.\n *\n * The store is a record of user intent, not a snapshot of derived state: an\n * explicit `true` matters only when a preset default masks the name, and\n * recording every toggle's final value keeps the write path dumb (no preset\n * lookup needed) while restore order (defaults first, overrides second) makes\n * the user's last word win.\n */\n\n/** Kind keys map 1:1 onto SessionOverrideState's fields. */\nconst KIND_KEYS = {\n skill: 'skills',\n 'mcp-server': 'mcpServers',\n 'mcp-tool': 'mcpTools',\n 'system-tool': 'systemTools',\n} as const;\n\n/**\n * Retained session records cap. A record costs a handful of names, and a\n * session the user never revisits is pruned oldest-first — the alternative\n * (unbounded growth in a settings file) trades a real read cost for a\n * scenario nobody has.\n */\nexport const MAX_SESSION_RECORDS = 200;\n\nexport interface SessionOverrideStore {\n /**\n * The stored positions for one session, for the agent/created listener.\n * Returns undefined when settings cannot be read: enforcement treats that\n * as \"no overrides\" rather than fail a session over a preference.\n */\n overridesFor(sessionId: string): SessionOverrideState | undefined;\n /**\n * Persist one toggle's final position for one session. Throws when the\n * settings service is absent or the write fails — the caller has already\n * applied the in-memory mask, so it reports this as a degraded note, not a\n * failed toggle.\n */\n record(sessionId: string, kind: CapabilityKind, name: string, enabled: boolean): Promise<void>;\n}\n\nexport function createSessionOverrideStore(access: ToolkitSettingsAccess): SessionOverrideStore {\n return {\n overridesFor(sessionId) {\n try {\n return access.scope()?.get().sessions[sessionId];\n } catch {\n return undefined;\n }\n },\n async record(sessionId, kind, name, enabled) {\n const scope = access.scope();\n if (scope === undefined) throw new HttpError(503, 'settings service unavailable');\n await access.serialize(async () => {\n const stored = scope.get();\n const kindKey = KIND_KEYS[kind];\n const current = stored.sessions[sessionId];\n const next: SessionOverrideState = {\n skills: { ...current?.skills },\n mcpServers: { ...current?.mcpServers },\n mcpTools: { ...current?.mcpTools },\n systemTools: { ...current?.systemTools },\n [kindKey]: { ...current?.[kindKey], [name]: enabled },\n };\n // Rebuild rather than mutate: dropping the id first and re-appending\n // it makes it the newest entry, so the cap evicts oldest-first\n // (Object.entries preserves string-key insertion order).\n const entries = Object.entries(stored.sessions).filter(([id]) => id !== sessionId);\n entries.push([sessionId, next]);\n const sessions: Record<string, SessionOverrideState> = Object.fromEntries(entries.slice(-MAX_SESSION_RECORDS));\n await scope.replace({\n presets: stored.presets,\n presetSkills: stored.presetSkills,\n sessions,\n });\n });\n },\n };\n}\n","import z from '@deepseek-ai/schemastery';\nimport type { HostServices, SettingsScopeLike, ToolkitSettings } from './types.js';\n\n// dsh-settings 0.1.2 removed the settingsNamespace() brander: register takes\n// the literal and validates it (lowercase-hyphenated) at the type level and\n// at runtime.\nexport const TOOLKIT_SETTINGS_NAMESPACE = 'capability-panel';\n\n// The schema crosses no declaration boundary: the settings service takes it\n// as `unknown`, and annotating it as such sidesteps the non-portable inferred\n// type schemastery's z.object produces under declaration emit.\nconst SessionOverrideSchema: unknown = z.object({\n skills: z.dict(z.boolean()).default({}),\n mcpServers: z.dict(z.boolean()).default({}),\n mcpTools: z.dict(z.boolean()).default({}),\n systemTools: z.dict(z.boolean()).default({}),\n});\n\nexport const ToolkitSettingsSchema: unknown = z.object({\n presets: z.dict(z.array(z.string())).default({}),\n presetSkills: z.dict(z.array(z.string())).default({}),\n sessions: z.dict(SessionOverrideSchema as never).default({}),\n});\n\nexport interface ToolkitSettingsAccess {\n /**\n * Lazily registered on first read, not at apply time: this row does not\n * `inject` settings, so at composition the service may not be published yet.\n * Binding it eagerly would freeze an early `undefined` into a permanent 503\n * even after settings arrives.\n */\n scope(): SettingsScopeLike<ToolkitSettings> | undefined;\n /**\n * One write queue for the whole namespace. Every writer (preset defaults,\n * session overrides) persists via a read-modify-`replace` of the same user\n * section, and the scope exposes no revision to write against — so two\n * writers racing would each persist a section computed from the same\n * pre-read snapshot and silently undo each other. Serializing read+write as\n * one critical section is what keeps a session toggle from wiping a preset\n * edit made in another tab.\n */\n serialize<T>(work: () => Promise<T>): Promise<T>;\n}\n\nexport function createToolkitSettingsAccess(ctx: HostServices): ToolkitSettingsAccess {\n let scope: SettingsScopeLike<ToolkitSettings> | undefined;\n let writeQueue: Promise<unknown> = Promise.resolve();\n return {\n scope() {\n if (scope === undefined) {\n scope = ctx.get('settings')?.register<ToolkitSettings>(\n TOOLKIT_SETTINGS_NAMESPACE,\n ToolkitSettingsSchema,\n { applies: 'live' },\n );\n }\n return scope;\n },\n serialize(work) {\n const next = writeQueue.then(work, work);\n writeQueue = next.then(\n () => undefined,\n () => undefined,\n );\n return next;\n },\n };\n}\n","import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { aggregateBlocked } from '../stats.js';\nimport type { StatsRecord } from '../stats.js';\n\nexport interface StatsSnapshot {\n readonly records: StatsRecord[];\n readonly blocked: Record<string, number>;\n readonly warnings: string[];\n}\n\nexport interface StatsStore {\n readonly file: string;\n read(): StatsSnapshot;\n append(record: StatsRecord): string | null;\n}\n\nexport function statsFilePath(environment: NodeJS.ProcessEnv = process.env, home = homedir()): string {\n return join(environment['DSH_HOME'] ?? join(home, '.dsh'), 'capability-panel', 'stats.jsonl');\n}\n\nfunction isEnoent(error: unknown): boolean {\n return error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nexport function createStatsStore(file = statsFilePath()): StatsStore {\n const appendWarnings: string[] = [];\n return {\n file,\n read() {\n let text: string;\n try {\n text = readFileSync(file, 'utf8');\n } catch (error) {\n if (isEnoent(error)) return { records: [], blocked: {}, warnings: [...appendWarnings] };\n return {\n records: [],\n blocked: {},\n warnings: [`stats read failed: ${String(error)}`, ...appendWarnings],\n };\n }\n const records: StatsRecord[] = [];\n const warnings: string[] = [...appendWarnings];\n const validLines: string[] = [];\n for (const [index, line] of text.split('\\n').entries()) {\n if (line.trim() === '') continue;\n try {\n const parsed = JSON.parse(line) as StatsRecord;\n records.push(parsed);\n validLines.push(line);\n } catch (error) {\n warnings.push(`stats line ${index + 1} skipped: ${String(error)}`);\n }\n }\n return { records, blocked: aggregateBlocked(validLines), warnings };\n },\n append(record) {\n try {\n mkdirSync(dirname(file), { recursive: true });\n appendFileSync(file, `${JSON.stringify(record)}\\n`);\n return null;\n } catch (error) {\n const warning = `stats append failed: ${String(error)}`;\n appendWarnings.push(warning);\n return warning;\n }\n },\n };\n}\n","import { createCapabilityController } from './host/capabilities.js';\nimport { registerPresetEnforcement } from './host/preset-enforcement.js';\nimport { createPresetToolController } from './host/preset-tools.js';\nimport { createRouteHandler, ROUTE } from './host/route.js';\nimport { createSessionOverrideStore } from './host/session-overrides.js';\nimport { createToolkitSettingsAccess } from './host/settings-scope.js';\nimport { createStatsStore } from './host/stats-store.js';\nimport type { HostServices } from './host/types.js';\n\n/** Host composition root: construct stores/controllers and register the route. */\nexport function apply(ctx: HostServices): void {\n const webServer = ctx.webServer;\n if (webServer === undefined) return;\n\n const stats = createStatsStore();\n const blockedCounts = stats.read().blocked;\n const appendStats = (record: Parameters<typeof stats.append>[0]): void => {\n stats.append(record);\n };\n const capabilities = createCapabilityController(ctx, appendStats, blockedCounts);\n // One settings namespace, one write queue: preset defaults and session\n // overrides share both, so the two writers can never clobber each other's\n // read-modify-replace.\n const settingsAccess = createToolkitSettingsAccess(ctx);\n const presetTools = createPresetToolController(ctx, settingsAccess);\n const sessionOverrides = createSessionOverrideStore(settingsAccess);\n registerPresetEnforcement(ctx, capabilities, presetTools, sessionOverrides);\n const handler = createRouteHandler(ctx, capabilities, stats, blockedCounts, presetTools, sessionOverrides);\n\n ctx.effect(\n () => webServer.register({ kind: 'prefix', path: ROUTE, handler }),\n 'capability-panel: data route',\n );\n}\n\n// Only the route is a hard dependency. Optional services report 503 on preset requests.\nexport const inject = ['webServer'];\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-capability-panel",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Inspect and manage the skills, tools, and MCP capabilities available to a DeepSeek Harness agent session.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,11 +49,11 @@
49
49
  },
50
50
  "peerDependencies": {
51
51
  "@deepseek-ai/cordis": "^4.0.2",
52
- "@deepseek-ai/dsh-client-locale": "*",
52
+ "@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1",
53
53
  "@deepseek-ai/dsh-client-store": "*",
54
54
  "@deepseek-ai/dsh-client-ui-layout": "*",
55
55
  "@deepseek-ai/dsh-client-ui-primitives": "*",
56
- "@deepseek-ai/dsh-client-ui-renderer": "*",
56
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1",
57
57
  "@deepseek-ai/schemastery": "^3.18.1",
58
58
  "react": "*",
59
59
  "react-dom": "*"