{"version":3,"file":"subagents-admin.d.ts","sourceRoot":"","sources":["../../../src/slash/subagents-admin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAiahF,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAuE1G","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ExtensionAPI, ExtensionContext } from \"@lpb-work/pi-coding-agent\";\nimport { editableAgentConfig, preservedAgentFrontmatterFields } from \"../agents/agent-management.ts\";\nimport { serializeAgent } from \"../agents/agent-serializer.ts\";\nimport {\n\ttype AgentConfig,\n\tagentHasFrontmatterField,\n\ttype BuiltinAgentOverrideBase,\n\tdiscoverAgentsAll,\n\tEXTRA_AGENT_DIRS_ENV,\n\tfrontmatterNameForConfig,\n\tmergeBuiltinAgentOverride,\n\tremoveBuiltinAgentOverrideFields,\n} from \"../agents/agents.ts\";\nimport { findModelInfo, getSupportedThinkingLevels, toModelInfo } from \"../shared/model-info.ts\";\nimport { SelectorComponent, type SelectorItem, type SelectorResult } from \"./selector.ts\";\n\nconst ADMIN_MESSAGE_TYPE = \"subagents-admin\";\nconst INHERIT_MODEL_CHOICE = \"Default / inherit session model\";\nconst INHERIT_THINKING_CHOICE = \"Default / inherit session thinking\";\n\ntype ModelInfo = { provider: string; id: string };\n\nfunction sourceRank(source: AgentConfig[\"source\"]): number {\n\tif (source === \"project\") return 0;\n\tif (source === \"user\") return 1;\n\tif (source === \"package\") return 2;\n\treturn 3;\n}\n\nfunction allVisibleAgents(cwd: string): AgentConfig[] {\n\tconst d = discoverAgentsAll(cwd);\n\treturn [...d.project, ...d.user, ...d.package, ...d.builtin]\n\t\t.filter((agent) => !agent.disabled)\n\t\t.sort((a, b) => a.name.localeCompare(b.name) || sourceRank(a.source) - sourceRank(b.source));\n}\n\nfunction agentLabel(agent: AgentConfig): string {\n\tconst model = agent.model ? ` · ${agent.model}` : \"\";\n\treturn `${agent.name} [${agent.source}]${model} — ${agent.description}`;\n}\n\nfunction agentChoices(agents: AgentConfig[]): Map<string, AgentConfig> {\n\tconst labels = agents.map(agentLabel);\n\tconst counts = new Map<string, number>();\n\tfor (const label of labels) counts.set(label, (counts.get(label) ?? 0) + 1);\n\treturn new Map(\n\t\tagents.map((agent, index) => {\n\t\t\tconst label = labels[index]!;\n\t\t\treturn [counts.get(label) === 1 ? label : `${label} · ${agent.filePath}`, agent] as const;\n\t\t}),\n\t);\n}\n\nfunction agentSelectItems(byLabel: Map<string, AgentConfig>): SelectorItem[] {\n\treturn [...byLabel.keys()].map((label) => ({ value: label, label }));\n}\n\nfunction agentMatches(agent: AgentConfig, rawName: string): boolean {\n\tconst name = rawName.trim();\n\treturn agent.name === name || frontmatterNameForConfig(agent) === name;\n}\n\nfunction sendAdminMessage(pi: ExtensionAPI, content: string): void {\n\tpi.sendMessage({\n\t\tcustomType: ADMIN_MESSAGE_TYPE,\n\t\tcontent,\n\t\tdisplay: true,\n\t});\n}\n\nfunction modelFullId(model: ModelInfo): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction liveAvailableModels(ctx: ExtensionContext) {\n\ttry {\n\t\tctx.modelRegistry.refresh?.();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tctx.ui.notify(`Could not refresh the model registry; using the last loaded choices. ${message}`, \"warning\");\n\t}\n\treturn ctx.modelRegistry.getAvailable();\n}\n\nfunction buildBuiltinBase(agent: AgentConfig): BuiltinAgentOverrideBase {\n\treturn {\n\t\t...(agent.model !== undefined ? { model: agent.model } : {}),\n\t\t...(agent.fallbackModels !== undefined ? { fallbackModels: [...agent.fallbackModels] } : {}),\n\t\t...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),\n\t\tsystemPromptMode: agent.systemPromptMode,\n\t\tinheritProjectContext: agent.inheritProjectContext,\n\t\tinheritSkills: agent.inheritSkills,\n\t\t...(agent.defaultContext !== undefined ? { defaultContext: agent.defaultContext } : {}),\n\t\t...(agent.acceptanceRole !== undefined ? { acceptanceRole: agent.acceptanceRole } : {}),\n\t\t...(agent.disabled !== undefined ? { disabled: agent.disabled } : {}),\n\t\tsystemPrompt: agent.systemPrompt,\n\t\t...(agent.skills !== undefined ? { skills: [...agent.skills] } : {}),\n\t\t...(agent.tools !== undefined ? { tools: [...agent.tools] } : {}),\n\t\t...(agent.mcpDirectTools !== undefined ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),\n\t\t...(agent.subagentOnlyExtensions !== undefined\n\t\t\t? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] }\n\t\t\t: {}),\n\t\t...(agent.completionGuard !== undefined ? { completionGuard: agent.completionGuard } : {}),\n\t\t...(agent.toolBudget !== undefined ? { toolBudget: agent.toolBudget } : {}),\n\t};\n}\n\ntype EditableOverrideField = \"model\" | \"thinking\" | \"systemPrompt\";\n\ntype AgentSelection =\n\t| { kind: \"selected\"; agent: AgentConfig }\n\t| { kind: \"cancelled\" }\n\t| { kind: \"not-found\"; agents: AgentConfig[]; requestedName?: string }\n\t| { kind: \"ambiguous\"; requestedName: string; matches: AgentConfig[] };\n\nfunction savesThroughSettings(agent: AgentConfig, field: EditableOverrideField): boolean {\n\tif (agent.source === \"builtin\") return true;\n\tif (agent.source === \"package\") {\n\t\tif (field === \"systemPrompt\") return false;\n\t\treturn !agentHasFrontmatterField(agent, field);\n\t}\n\tif (!agent.override) return false;\n\t// A lower-scope override can flow into a higher-scope custom agent with the\n\t// same name. Persist that agent's edits in its own frontmatter instead of\n\t// rewriting the shared lower-scope override used by another agent.\n\tif (agent.source !== agent.override.scope) return false;\n\t// Custom-agent overrides fill only fields absent from frontmatter. Compare the\n\t// effective value with the pre-override base so an override on one field does\n\t// not redirect edits to an unrelated frontmatter-owned field.\n\treturn agent[field] !== agent.override.base[field];\n}\n\nfunction isReadOnlyExtraAgent(agent: AgentConfig): boolean {\n\tconst configured = process.env[EXTRA_AGENT_DIRS_ENV];\n\tif (!configured || agent.source !== \"user\") return false;\n\tconst filePath = path.resolve(agent.filePath);\n\treturn configured\n\t\t.split(path.delimiter)\n\t\t.map((dir) => dir.trim())\n\t\t.filter(Boolean)\n\t\t.some((dir) => {\n\t\t\tconst root = path.resolve(dir);\n\t\t\tconst relative = path.relative(root, filePath);\n\t\t\treturn (\n\t\t\t\trelative !== \"\" && relative !== \"..\" && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)\n\t\t\t);\n\t\t});\n}\n\nfunction readOnlyAgentMessage(agent: AgentConfig, field: EditableOverrideField): string | undefined {\n\tif (agent.source === \"package\") {\n\t\treturn `Cannot update '${agent.name}' ${field} because that field is owned by its read-only package definition.`;\n\t}\n\treturn isReadOnlyExtraAgent(agent)\n\t\t? `Cannot update '${agent.name}' because its definition in PI_SUBAGENT_EXTRA_AGENT_DIRS is read-only.`\n\t\t: undefined;\n}\n\nasync function selectAgent(ctx: ExtensionContext, args: string): Promise<AgentSelection> {\n\tconst agents = allVisibleAgents(ctx.cwd);\n\tconst requestedName = args.trim().split(/\\s+/)[0] ?? \"\";\n\tif (agents.length === 0) return { kind: \"not-found\", agents, requestedName: requestedName || undefined };\n\n\tif (requestedName) {\n\t\tconst matches = agents.filter((agent) => agentMatches(agent, requestedName));\n\t\tif (matches.length === 1) return { kind: \"selected\", agent: matches[0]! };\n\t\tif (matches.length > 1 && !ctx.hasUI) return { kind: \"ambiguous\", requestedName, matches };\n\t\tif (matches.length > 1) {\n\t\t\tconst byLabel = agentChoices(matches);\n\t\t\tconst choice = await selectFromList(\n\t\t\t\tctx,\n\t\t\t\t`Multiple subagents named '${requestedName}'`,\n\t\t\t\tundefined,\n\t\t\t\tagentSelectItems(byLabel),\n\t\t\t);\n\t\t\treturn choice ? { kind: \"selected\", agent: byLabel.get(choice)! } : { kind: \"cancelled\" };\n\t\t}\n\t\treturn { kind: \"not-found\", agents, requestedName };\n\t}\n\n\tif (!ctx.hasUI) return { kind: \"not-found\", agents };\n\tconst byLabel = agentChoices(agents);\n\tconst choice = await selectFromList(ctx, \"Select subagent\", undefined, agentSelectItems(byLabel));\n\treturn choice ? { kind: \"selected\", agent: byLabel.get(choice)! } : { kind: \"cancelled\" };\n}\n\nfunction metadataFor(agent: AgentConfig): string {\n\tconst tools = [...(agent.tools ?? []), ...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`)];\n\tconst lines = [\n\t\t`Agent: ${agent.name} (${agent.source})`,\n\t\t`Path: ${agent.filePath}`,\n\t\t`Description: ${agent.description}`,\n\t];\n\tif (agent.packageName) {\n\t\tlines.push(`Local name: ${frontmatterNameForConfig(agent)}`);\n\t\tlines.push(`Package: ${agent.packageName}`);\n\t}\n\tlines.push(`Model: ${agent.model ?? \"default / inherit\"}`);\n\tif (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(\", \")}`);\n\tif (agent.thinking !== undefined) lines.push(`Thinking: ${agent.thinking === false ? \"off\" : agent.thinking}`);\n\tif (tools.length) lines.push(`Tools: ${tools.join(\", \")}`);\n\tif (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(\", \")}`);\n\tlines.push(`System prompt mode: ${agent.systemPromptMode}`);\n\tlines.push(`Inherit project context: ${agent.inheritProjectContext ? \"true\" : \"false\"}`);\n\tlines.push(`Inherit skills: ${agent.inheritSkills ? \"true\" : \"false\"}`);\n\tif (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);\n\tif (agent.output) lines.push(`Output: ${agent.output}`);\n\tif (agent.defaultReads?.length) lines.push(`Reads: ${agent.defaultReads.join(\", \")}`);\n\tif (agent.defaultProgress) lines.push(\"Progress: true\");\n\tif (agent.maxSubagentDepth !== undefined) lines.push(`Max subagent depth: ${agent.maxSubagentDepth}`);\n\tif (agent.source === \"builtin\") lines.push(`Disabled: ${agent.disabled ? \"true\" : \"false\"}`);\n\tif (agent.override) lines.push(`Override: ${agent.override.scope} (${agent.override.path})`);\n\tif (agent.systemPrompt.trim()) lines.push(\"\", \"System Prompt:\", agent.systemPrompt);\n\treturn lines.join(\"\\n\");\n}\n\nasync function selectFromList(\n\tctx: ExtensionContext,\n\ttitle: string,\n\tsubtitle: string | undefined,\n\titems: SelectorItem[],\n): Promise<string | undefined> {\n\tif (typeof ctx.ui.custom === \"function\") {\n\t\tconst result = await ctx.ui.custom<SelectorResult>(\n\t\t\t(tui, theme, kb, done) => new SelectorComponent(tui, theme, kb, { title, subtitle, items, done }),\n\t\t\t{ overlay: false },\n\t\t);\n\t\treturn result?.confirmed ? result.value : undefined;\n\t}\n\tconst flatTitle = subtitle ? `${title}\\nCurrent: ${subtitle}` : title;\n\tconst labelToValue = new Map(items.map((item) => [item.label, item.value] as const));\n\tconst choice = await ctx.ui.select(\n\t\tflatTitle,\n\t\titems.map((item) => item.value),\n\t);\n\treturn choice ? (items.find((item) => item.value === choice)?.value ?? labelToValue.get(choice)) : undefined;\n}\n\nasync function chooseModel(ctx: ExtensionContext, agent: AgentConfig): Promise<string | undefined | null> {\n\tconst models = liveAvailableModels(ctx);\n\tconst current = agent.model ?? INHERIT_MODEL_CHOICE;\n\tconst items: SelectorItem[] = [{ value: INHERIT_MODEL_CHOICE, label: INHERIT_MODEL_CHOICE, current: !agent.model }];\n\tif (agent.model && !models.some((model) => modelFullId(model) === agent.model)) {\n\t\titems.push({ value: agent.model, label: agent.model, current: true });\n\t}\n\tfor (const model of models) {\n\t\tconst fullId = modelFullId(model);\n\t\titems.push({ value: fullId, label: model.id, badge: model.provider, current: fullId === agent.model });\n\t}\n\tconst choice = await selectFromList(ctx, `Select model for ${agent.name}`, current, items);\n\tif (choice === undefined) return null;\n\treturn choice === INHERIT_MODEL_CHOICE ? undefined : choice;\n}\n\nasync function chooseThinking(ctx: ExtensionContext, agent: AgentConfig): Promise<string | undefined | null> {\n\tconst availableModels = liveAvailableModels(ctx).map(toModelInfo);\n\tconst effectiveModel = agent.model ?? (ctx.model ? modelFullId(ctx.model) : undefined);\n\tconst modelInfo = findModelInfo(effectiveModel, availableModels, ctx.model?.provider);\n\tconst levels = getSupportedThinkingLevels(modelInfo);\n\tconst current = agent.thinking === false ? \"off\" : (agent.thinking ?? INHERIT_THINKING_CHOICE);\n\tconst values: string[] = [INHERIT_THINKING_CHOICE, ...levels];\n\tif (current !== INHERIT_THINKING_CHOICE && !values.includes(current)) values.splice(1, 0, current);\n\tconst modelNote = agent.model\n\t\t? `Model: ${agent.model}`\n\t\t: effectiveModel\n\t\t\t? `Session model: ${effectiveModel}`\n\t\t\t: \"Model: default / inherit\";\n\tconst items: SelectorItem[] = values.map((value) => ({ value, label: value, current: value === current }));\n\tconst choice = await selectFromList(\n\t\tctx,\n\t\t`Select thinking level for ${agent.name}`,\n\t\t`${modelNote} · ${current}`,\n\t\titems,\n\t);\n\tif (choice === undefined) return null;\n\treturn choice === INHERIT_THINKING_CHOICE ? undefined : choice;\n}\n\nasync function chooseOverrideScope(ctx: ExtensionContext, agent: AgentConfig): Promise<\"user\" | \"project\" | undefined> {\n\tif (agent.override?.scope) return agent.override.scope;\n\tconst d = discoverAgentsAll(ctx.cwd);\n\tif (!d.projectSettingsPath || !ctx.hasUI) return \"user\";\n\tconst choice = await ctx.ui.select(`Save builtin override for ${agent.name}`, [\"user\", \"project\"]);\n\treturn choice === \"user\" || choice === \"project\" ? choice : undefined;\n}\n\nfunction persistSettingsField(\n\tctx: ExtensionContext,\n\tagent: AgentConfig,\n\tscope: \"user\" | \"project\",\n\tfield: EditableOverrideField,\n\tvalue: string | undefined,\n): { filePath: string; overridden: boolean } {\n\tconst base = agent.override?.base ?? buildBuiltinBase(agent);\n\tif (value === undefined || value === base[field]) {\n\t\treturn {\n\t\t\tfilePath: removeBuiltinAgentOverrideFields(ctx.cwd, agent.name, scope, [field]).path,\n\t\t\toverridden: false,\n\t\t};\n\t}\n\treturn {\n\t\tfilePath: mergeBuiltinAgentOverride(ctx.cwd, agent.name, scope, { [field]: value }),\n\t\toverridden: true,\n\t};\n}\n\nasync function saveAgentModel(\n\tctx: ExtensionContext,\n\tagent: AgentConfig,\n\tselectedModel: string | undefined,\n): Promise<string | null> {\n\tif (savesThroughSettings(agent, \"model\")) {\n\t\tconst scope = await chooseOverrideScope(ctx, agent);\n\t\tif (!scope) return null;\n\t\tconst { filePath, overridden } = persistSettingsField(ctx, agent, scope, \"model\", selectedModel);\n\t\treturn overridden\n\t\t\t? `Saved ${scope} settings override for '${agent.name}' with model '${selectedModel}' in ${filePath}.`\n\t\t\t: `Cleared model settings override for '${agent.name}' in ${filePath}.`;\n\t}\n\n\tconst readOnlyMessage = readOnlyAgentMessage(agent, \"model\");\n\tif (readOnlyMessage) return readOnlyMessage;\n\tconst updated = editableAgentConfig(agent);\n\tif (selectedModel === undefined) delete updated.model;\n\telse updated.model = selectedModel;\n\tfs.writeFileSync(\n\t\tupdated.filePath,\n\t\tserializeAgent(updated, {\n\t\t\tpreserveFrontmatterFields: preservedAgentFrontmatterFields(agent, { model: selectedModel }),\n\t\t}),\n\t\t\"utf-8\",\n\t);\n\treturn selectedModel\n\t\t? `Updated '${agent.name}' model to '${selectedModel}' in ${updated.filePath}.`\n\t\t: `Cleared '${agent.name}' model in ${updated.filePath}.`;\n}\n\nasync function saveAgentThinking(\n\tctx: ExtensionContext,\n\tagent: AgentConfig,\n\tselectedThinking: string | undefined,\n): Promise<string | null> {\n\tif (savesThroughSettings(agent, \"thinking\")) {\n\t\tconst scope = await chooseOverrideScope(ctx, agent);\n\t\tif (!scope) return null;\n\t\tconst { filePath, overridden } = persistSettingsField(ctx, agent, scope, \"thinking\", selectedThinking);\n\t\treturn overridden\n\t\t\t? `Saved ${scope} settings override for '${agent.name}' with thinking '${selectedThinking}' in ${filePath}.`\n\t\t\t: `Cleared thinking settings override for '${agent.name}' in ${filePath}.`;\n\t}\n\n\tconst readOnlyMessage = readOnlyAgentMessage(agent, \"thinking\");\n\tif (readOnlyMessage) return readOnlyMessage;\n\tconst updated = editableAgentConfig(agent);\n\tif (selectedThinking === undefined) delete updated.thinking;\n\telse updated.thinking = selectedThinking;\n\tfs.writeFileSync(\n\t\tupdated.filePath,\n\t\tserializeAgent(updated, {\n\t\t\tpreserveFrontmatterFields: preservedAgentFrontmatterFields(agent, { thinking: selectedThinking }),\n\t\t}),\n\t\t\"utf-8\",\n\t);\n\treturn selectedThinking\n\t\t? `Updated '${agent.name}' thinking to '${selectedThinking}' in ${updated.filePath}.`\n\t\t: `Cleared '${agent.name}' thinking in ${updated.filePath}.`;\n}\n\n/** Compact one-line summary shown in the interactive picker (no full system prompt dump). */\nfunction metadataSummary(agent: AgentConfig): string {\n\treturn [\n\t\t`Source: ${agent.source}`,\n\t\t`Model: ${agent.model ?? \"default / inherit\"}`,\n\t\t`Thinking: ${agent.thinking === false ? \"off\" : (agent.thinking ?? \"default / inherit\")}`,\n\t].join(\" · \");\n}\n\nasync function saveAgentSystemPrompt(\n\tctx: ExtensionContext,\n\tagent: AgentConfig,\n\tsystemPrompt: string,\n): Promise<string | null> {\n\tconst nextPrompt = systemPrompt.replace(/\\s+$/, \"\");\n\tif (savesThroughSettings(agent, \"systemPrompt\")) {\n\t\tconst scope = await chooseOverrideScope(ctx, agent);\n\t\tif (!scope) return null;\n\t\tconst { filePath, overridden } = persistSettingsField(ctx, agent, scope, \"systemPrompt\", nextPrompt);\n\t\treturn overridden\n\t\t\t? `Saved ${scope} settings override for '${agent.name}' system prompt in ${filePath}.`\n\t\t\t: `Cleared system prompt settings override for '${agent.name}' in ${filePath}.`;\n\t}\n\tconst readOnlyMessage = readOnlyAgentMessage(agent, \"systemPrompt\");\n\tif (readOnlyMessage) return readOnlyMessage;\n\tconst updated: AgentConfig = { ...editableAgentConfig(agent), systemPrompt: nextPrompt };\n\tfs.writeFileSync(\n\t\tupdated.filePath,\n\t\tserializeAgent(updated, {\n\t\t\tpreserveFrontmatterFields: preservedAgentFrontmatterFields(agent, { systemPrompt: nextPrompt }),\n\t\t}),\n\t\t\"utf-8\",\n\t);\n\treturn `Updated '${agent.name}' system prompt in ${updated.filePath}.`;\n}\n\nasync function editSystemPrompt(ctx: ExtensionContext, agent: AgentConfig): Promise<string | null> {\n\tif (!savesThroughSettings(agent, \"systemPrompt\")) {\n\t\tconst readOnlyMessage = readOnlyAgentMessage(agent, \"systemPrompt\");\n\t\tif (readOnlyMessage) return readOnlyMessage;\n\t}\n\tconst edited = await ctx.ui.editor(`Edit '${agent.name}' system prompt`, agent.systemPrompt ?? \"\");\n\tif (edited === undefined) return null;\n\tif (edited.replace(/\\s+$/, \"\") === (agent.systemPrompt ?? \"\").replace(/\\s+$/, \"\")) {\n\t\treturn `System prompt for '${agent.name}' left unchanged.`;\n\t}\n\treturn saveAgentSystemPrompt(ctx, agent, edited);\n}\n\nexport async function openSubagentsAdmin(pi: ExtensionAPI, ctx: ExtensionContext, args = \"\"): Promise<void> {\n\tconst selection = await selectAgent(ctx, args);\n\tif (selection.kind === \"cancelled\") return;\n\tif (selection.kind === \"ambiguous\") {\n\t\tsendAdminMessage(\n\t\t\tpi,\n\t\t\t`Subagent '${selection.requestedName}' is ambiguous. Choose a scope in interactive mode:\\n${selection.matches.map((agent) => `- ${agent.source}: ${agent.filePath}`).join(\"\\n\")}`,\n\t\t);\n\t\treturn;\n\t}\n\tif (selection.kind === \"not-found\") {\n\t\tconst text = selection.requestedName\n\t\t\t? `Subagent '${selection.requestedName}' not found.\\n\\nAvailable subagents:\\n${selection.agents.map((agent) => `- ${agent.name} (${agent.source})`).join(\"\\n\") || \"- (none)\"}`\n\t\t\t: `Available subagents:\\n${selection.agents.map((agent) => `- ${agent.name} (${agent.source})`).join(\"\\n\") || \"- (none)\"}`;\n\t\tsendAdminMessage(pi, text);\n\t\treturn;\n\t}\n\tconst agent = selection.agent;\n\n\tif (!ctx.hasUI) {\n\t\t// Non-interactive (tool/headless): emit full metadata as the inspection result.\n\t\tsendAdminMessage(pi, metadataFor(agent));\n\t\treturn;\n\t}\n\n\tconst requestedAction = args.trim().split(/\\s+/)[1]?.toLowerCase();\n\tlet action: string | undefined;\n\tif (requestedAction === \"model\") action = \"Change model\";\n\telse if (requestedAction === \"thinking\") action = \"Change thinking level\";\n\telse if (requestedAction === \"prompt\" || requestedAction === \"system-prompt\" || requestedAction === \"edit\")\n\t\taction = \"Edit system prompt\";\n\telse if (requestedAction === \"details\" || requestedAction === \"info\") action = \"Show details\";\n\tif (!action) {\n\t\taction = await ctx.ui.select(`Administer ${agent.name}\\n${metadataSummary(agent)}`, [\n\t\t\t\"Change model\",\n\t\t\t\"Change thinking level\",\n\t\t\t\"Edit system prompt\",\n\t\t\t\"Show details\",\n\t\t\t\"Done\",\n\t\t]);\n\t}\n\n\ttry {\n\t\tif (action === \"Change model\") {\n\t\t\tconst selectedModel = await chooseModel(ctx, agent);\n\t\t\tif (selectedModel === null) return;\n\t\t\tconst message = await saveAgentModel(ctx, agent, selectedModel);\n\t\t\tif (message === null) return;\n\t\t\tctx.ui.notify(message, \"info\");\n\t\t\tsendAdminMessage(pi, message);\n\t\t} else if (action === \"Change thinking level\") {\n\t\t\tconst selectedThinking = await chooseThinking(ctx, agent);\n\t\t\tif (selectedThinking === null) return;\n\t\t\tconst message = await saveAgentThinking(ctx, agent, selectedThinking);\n\t\t\tif (message === null) return;\n\t\t\tctx.ui.notify(message, \"info\");\n\t\t\tsendAdminMessage(pi, message);\n\t\t} else if (action === \"Edit system prompt\") {\n\t\t\tconst message = await editSystemPrompt(ctx, agent);\n\t\t\tif (message === null) return;\n\t\t\tctx.ui.notify(message, \"info\");\n\t\t\tsendAdminMessage(pi, message);\n\t\t} else if (action === \"Show details\") {\n\t\t\t// Full metadata is now opt-in (previously always posted to the thread).\n\t\t\tsendAdminMessage(pi, metadataFor(agent));\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tctx.ui.notify(message, \"error\");\n\t\tsendAdminMessage(pi, `Failed to update '${agent.name}': ${message}`);\n\t}\n}\n"]}