@mushinjs/plugin-agent 0.0.0-stage → 0.2.0-next.20261113103537

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n type ActionInfo, Context, field, fromToolName, MushinError, randomId, segmentsToText, Service, toToolName, z,\n} from '@mushinjs/core'\nimport type { LlmMessage, LlmResult, ToolCall, ToolDefinition } from '@mushinjs/plugin-providers'\nimport type {} from '@mushinjs/plugin-storage'\nimport { contextNote, type SceneInfo, skillBlock, SYSTEM_PROMPT, type TurnContext } from './prompt'\nimport {\n type AgentSettings, defaultPolicy, formatSkillMarkdown, parseSkillMarkdown, policyInput, profileInput, SCENE_KEYS, type SceneProfile, today,\n type TokenUsage, type ToolPolicy,\n} from './settings'\nimport { type AgentSkill, BUILTIN_SKILLS, type SkillDefinition, skillFields, skillInput } from './skills'\n\nexport * from './prompt'\nexport * from './settings'\nexport * from './skills'\n\ndeclare module '@mushinjs/core' {\n interface Context {\n agent: Agent\n }\n\n interface Events {\n 'agent/event'(event: AgentEvent): void\n /** Skills, page profiles or tool policies changed. */\n 'agent/changed'(): void\n }\n}\n\nexport type AgentEvent = { session: string } & (\n | { type: 'start' }\n | { type: 'delta'; text: string }\n | { type: 'tool-call'; id: string; action: string; input: unknown; risk: string }\n | { type: 'confirm'; id: string; action: string; input: unknown }\n | { type: 'tool-result'; id: string; ok: boolean; result?: unknown; error?: string }\n | { type: 'done'; text: string; usage?: { inputTokens: number; outputTokens: number } }\n /** `code` is `max_steps` when the turn ran out of tool rounds, so the dashboard can offer to go on. */\n | { type: 'error'; error: string; code?: 'max_steps' }\n)\n\n/**\n * A stored message; `display` holds the user's own words when the content carries a context note,\n * and `skills` the names of the skills picked for it.\n */\nexport type AgentMessage = LlmMessage & {\n display?: string\n skills?: string[]\n /** A chat quoted into the message, as the dashboard shows it: who said what, and where. */\n quote?: QuotedChat\n at: number\n}\n\n/** Messages of a bot's chat quoted into an Agent message, around one picked message. */\nexport interface QuotedChat {\n bot: string\n botName: string\n channel: { type: 'group' | 'private'; id: string; name?: string }\n /** The picked message's record id; a sample for flows.test and flows.preview. */\n focus: string\n lines: { id: string; direction: 'in' | 'out'; user: string; userId: string; text: string; at: number }[]\n}\n\n/** Where a conversation was started, for the history list. */\nexport interface SessionScene {\n key: string\n label?: string\n page?: string\n}\n\nexport interface AgentSession {\n id: string\n title: string\n createdAt: number\n updatedAt: number\n /** Tool list frozen at creation, so every request in the session sends identical tools. */\n tools: ToolDefinition[]\n messages: AgentMessage[]\n /** The page the conversation was started on. */\n scene?: SessionScene\n /** What it touched, for search: `scene:<key>`, `skill:<id>`, `command:<name>`, `resource:<kind>:<id>`. */\n tags?: string[]\n}\n\nexport interface SendOptions {\n /** Dashboard connection id; client actions such as ui.navigate are routed back to it. */\n client?: string\n page?: string\n /** The page as the dashboard describes it; its hint and state go into the context note. */\n scene?: SceneInfo\n /** Ids of skills whose instructions go into this message. */\n skills?: string[]\n /** A message of a bot's chat to quote, with a few messages around it. */\n quote?: { bot: string; channel: string; id: string; before?: number; after?: number }\n}\n\n/** Commands the dashboard runs itself, without asking the model. */\nexport const BUILTIN_COMMANDS = [\n { name: 'new', description: 'Start a new conversation', 'zh-CN': '开始新对话' },\n { name: 'history', description: 'Open the full conversation history', 'zh-CN': '打开完整历史' },\n { name: 'model', description: 'Switch the Agent\\'s model', 'zh-CN': '切换模型' },\n { name: 'status', description: 'Show how much context this conversation uses', 'zh-CN': '查看上下文用量' },\n { name: 'clear', description: 'Clear the input and close the conversation', 'zh-CN': '清空当前对话视图' },\n { name: 'help', description: 'Show what you can type', 'zh-CN': '查看可用的指令和技能' },\n]\n\nconst SETTINGS_KEY = 'agent/settings/main'\nconst USAGE_KEY = 'agent/settings/usage'\n\nconst MAX_RESULT_CHARS = 16_000\n\nexport const Config = z.object({\n provider: z.string().optional().meta(field('Provider', 'Provider or model profile for the Agent; empty uses the default provider', {\n 'zh-CN': { title: '提供商', description: '智能体使用的提供商或配置方案;留空使用默认提供商' },\n }, { choices: 'providers.choices' })),\n model: z.string().optional().meta(field('Model', 'Model for the Agent; empty uses the provider\\'s default', {\n 'zh-CN': { title: '模型', description: '智能体使用的模型;留空使用提供商默认模型' },\n })),\n maxSteps: z.number().int().min(1).max(50).default(20).meta(field('Max steps', 'Most tool-calling rounds in one reply', {\n 'zh-CN': { title: '最大步数', description: '一次回复中最多调用工具的轮数' },\n })),\n confirmTimeout: z.number().int().min(10).default(600).meta(field('Approval timeout', 'Seconds to wait for approval of a dangerous action', {\n 'zh-CN': { title: '确认超时', description: '等待用户批准危险操作的秒数' },\n })),\n dailyTokens: z.number().int().min(0).default(0).meta(field('Daily token budget', 'Input and output tokens the Agent may use per day; 0 means no limit', {\n 'zh-CN': { title: '每日 Token 预算', description: '智能体每天最多使用的输入和输出 Token;0 表示不限制' },\n })),\n})\n\nexport type Config = z.infer<typeof Config>\n\ninterface Running {\n abort: AbortController\n confirms: Map<string, (approved: boolean) => void>\n /** Tokens of every request in this turn. */\n usage: { inputTokens: number; outputTokens: number }\n /** Failed calls per tool in this turn, to tell the model to stop guessing. */\n failures: Map<string, number>\n}\n\n/** The turn used every tool round it may; the dashboard offers to continue. */\nclass StepLimit extends Error {\n constructor(steps: number) {\n super(`Stopped after ${steps} tool rounds; send another message to continue`)\n }\n}\n\n/** After this many failures of one tool in a turn, its errors ask the model to change course. */\nconst FAILURE_HINT_AFTER = 3\n\n/** Runs conversations in which the LLM operates Mushin.js through the action registry. */\nexport class Agent extends Service {\n static inject = ['actions', 'llm', 'storage']\n static Config = Config\n\n private home: Context\n private running = new Map<string, Running>()\n private pluginSkills = new Map<string, AgentSkill>()\n private settingsCache?: AgentSettings\n\n constructor(ctx: Context, public config: Config) {\n super(ctx, 'agent')\n this.home = ctx\n }\n\n *[Service.init]() {\n yield* this.defineActions()\n yield () => {\n for (const id of this.running.keys()) this.cancel(id)\n }\n }\n\n /** Tools offered to the model: every action except the Agent's own and those turned off. */\n async tools(): Promise<ToolDefinition[]> {\n const settings = await this.settings()\n return this.home.actions.list()\n .filter((action) => action.agent && !action.name.startsWith('agent.') && this.policyOf(settings, action) !== 'off')\n .map((action) => ({\n name: toToolName(action.name),\n description: describeTool(action),\n parameters: action.inputSchema,\n }))\n }\n\n // ---- settings ----\n\n async settings(): Promise<AgentSettings> {\n if (!this.settingsCache) {\n const saved = await this.home.storage.get<Partial<AgentSettings>>(SETTINGS_KEY)\n this.settingsCache = { profiles: {}, toolPolicy: {}, disabledSkills: [], ...saved }\n }\n return this.settingsCache\n }\n\n private async updateSettings(change: (settings: AgentSettings) => void) {\n const settings = await this.settings()\n change(settings)\n await this.home.storage.set(SETTINGS_KEY, settings)\n this.home.emit('agent/changed')\n return settings\n }\n\n private policyOf(settings: AgentSettings, action: Pick<ActionInfo, 'name' | 'risk'>): ToolPolicy {\n return settings.toolPolicy[action.name] ?? defaultPolicy(action.risk)\n }\n\n /** Every action with its policy, for the tool policy table. */\n async policies() {\n const settings = await this.settings()\n return this.home.actions.list()\n .filter((action) => action.agent && !action.name.startsWith('agent.'))\n .map((action) => ({\n name: action.name,\n description: action.description,\n plugin: action.plugin,\n risk: action.risk,\n client: action.client,\n policy: this.policyOf(settings, action),\n custom: action.name in settings.toolPolicy,\n }))\n }\n\n /** Sets the policy of some actions; `null` goes back to the default for the action's risk. */\n async setPolicy(changes: Record<string, ToolPolicy | null>) {\n await this.updateSettings((settings) => {\n for (const [name, policy] of Object.entries(changes)) {\n if (policy === null) delete settings.toolPolicy[name]\n else settings.toolPolicy[name] = policy\n }\n })\n return this.policies()\n }\n\n async profiles() {\n return (await this.settings()).profiles\n }\n\n async setProfile(scene: string, profile: SceneProfile | null) {\n const settings = await this.updateSettings((settings) => {\n if (profile === null) delete settings.profiles[scene]\n else settings.profiles[scene] = profile\n })\n return settings.profiles[scene] ?? {}\n }\n\n /** Tokens used today and the daily budget. */\n async usage() {\n const saved = await this.home.storage.get<TokenUsage>(USAGE_KEY)\n const used = saved?.day === today() ? saved : { day: today(), input: 0, output: 0 }\n return { ...used, budget: this.config.dailyTokens }\n }\n\n private async addUsage(usage: { inputTokens: number; outputTokens: number }) {\n const current = await this.usage()\n await this.home.storage.set(USAGE_KEY, { day: current.day, input: current.input + usage.inputTokens, output: current.output + usage.outputTokens })\n }\n\n private emit(event: AgentEvent) {\n this.home.emit('agent/event', event)\n }\n\n private async allSessions() {\n const keys = await this.home.storage.list('agent')\n const sessions = await Promise.all(keys.map((key) => this.home.storage.get<AgentSession>(key)))\n return sessions\n .filter((session): session is AgentSession => !!session && Array.isArray(session.messages))\n .sort((a, b) => b.updatedAt - a.updatedAt)\n }\n\n private summary({ id, title, createdAt, updatedAt, scene, tags }: AgentSession) {\n return { id, title, createdAt, updatedAt, scene, tags: tags ?? [], running: this.running.has(id) }\n }\n\n async sessions() {\n return (await this.allSessions()).map((session) => this.summary(session))\n }\n\n /**\n * Conversations matching every tag (`scene:flowEditor`, `skill:<id>`, `status:running`, …) and\n * containing the query in the title or in what the user or the Agent wrote.\n */\n async search(options: { query?: string; tags?: string[]; limit?: number } = {}) {\n const query = options.query?.trim().toLowerCase()\n const results = []\n for (const session of await this.allSessions()) {\n const summary = this.summary(session)\n const tags = [...summary.tags, `status:${summary.running ? 'running' : 'idle'}`]\n if (options.tags?.some((tag) => !tags.includes(tag))) continue\n let snippet: string | undefined\n if (query) {\n if (!session.title.toLowerCase().includes(query)) {\n const texts = session.messages\n .filter((message) => message.role === 'user' || message.role === 'assistant')\n .map((message) => (message.role === 'user' ? message.display ?? message.content : message.content))\n const hit = texts.find((text): text is string => typeof text === 'string' && text.toLowerCase().includes(query))\n if (!hit) continue\n const at = hit.toLowerCase().indexOf(query)\n snippet = `${at > 30 ? '…' : ''}${hit.slice(Math.max(0, at - 30), at + query.length + 50).replace(/\\s+/g, ' ')}`\n }\n }\n results.push({ ...summary, ...(snippet ? { snippet } : {}) })\n if (results.length >= (options.limit ?? 50)) break\n }\n return results\n }\n\n // ---- skills ----\n\n /** Adds a skill from a plugin; removed when the plugin unloads. */\n defineSkill(definition: SkillDefinition) {\n const ctx = this.ctx\n const skill: AgentSkill = { ...definition, source: 'plugin', plugin: ctx.fiber.name, enabled: true }\n return ctx.effect(() => {\n this.pluginSkills.set(skill.id, skill)\n return () => {\n if (this.pluginSkills.get(skill.id) === skill) this.pluginSkills.delete(skill.id)\n }\n }, `agent.defineSkill(${definition.id})`)\n }\n\n /** Built-in, plugin and user skills; user skills win on the same id. */\n async skills(): Promise<AgentSkill[]> {\n const disabled = new Set((await this.settings()).disabledSkills)\n const byId = new Map<string, AgentSkill>()\n for (const skill of BUILTIN_SKILLS) byId.set(skill.id, { ...skill, source: 'builtin', enabled: true })\n for (const skill of this.pluginSkills.values()) byId.set(skill.id, { ...skill })\n const keys = await this.home.storage.list('agent/skills')\n for (const skill of await Promise.all(keys.map((key) => this.home.storage.get<AgentSkill>(key)))) {\n if (skill?.id) byId.set(skill.id, { ...skill, source: 'user', enabled: skill.enabled !== false })\n }\n return [...byId.values()].map((skill) => (skill.source !== 'user' && disabled.has(skill.id) ? { ...skill, enabled: false } : skill))\n }\n\n async skill(id: string) {\n const skill = (await this.skills()).find((item) => item.id === id)\n if (!skill) throw new MushinError('not_found', `No skill \"${id}\"`)\n return skill\n }\n\n /** Saves a user skill. With the id of a built-in or plugin skill it replaces that one until deleted. */\n async saveSkill(input: SkillDefinition & { enabled?: boolean }) {\n const skill: AgentSkill = { ...input, source: 'user', enabled: input.enabled !== false }\n await this.home.storage.set(`agent/skills/${skill.id}`, skill)\n this.home.emit('agent/changed')\n return this.skill(skill.id)\n }\n\n async createSkill(input: Omit<SkillDefinition, 'id'> & { id?: string }) {\n const taken = new Set((await this.skills()).map((skill) => skill.id))\n let id = input.id ?? (input.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || randomId(6).toLowerCase())\n if (taken.has(id) && !input.id) id = `${id}-${randomId(4).toLowerCase()}`\n else if (taken.has(id)) throw new MushinError('conflict', `A skill with id \"${id}\" already exists`)\n return this.saveSkill({ ...input, id })\n }\n\n async updateSkill(id: string, patch: Partial<Omit<SkillDefinition, 'id'>>) {\n const current = await this.skill(id)\n const { source, plugin, enabled, ...rest } = current\n return this.saveSkill({ ...rest, ...patch, id, enabled })\n }\n\n /** Deletes a user skill; a built-in or plugin skill it replaced comes back. */\n async deleteSkill(id: string) {\n const current = await this.skill(id)\n if (current.source !== 'user') throw new MushinError('forbidden', 'Built-in and plugin skills cannot be deleted; turn them off instead')\n await this.home.storage.delete(`agent/skills/${id}`)\n this.home.emit('agent/changed')\n return { deleted: id, restored: (await this.skills()).some((skill) => skill.id === id) }\n }\n\n async setSkillEnabled(id: string, enabled: boolean) {\n const current = await this.skill(id)\n if (current.source === 'user') {\n await this.home.storage.set(`agent/skills/${id}`, { ...current, enabled })\n this.home.emit('agent/changed')\n return this.skill(id)\n }\n await this.updateSettings((settings) => {\n settings.disabledSkills = enabled ? settings.disabledSkills.filter((item) => item !== id) : [...new Set([...settings.disabledSkills, id])]\n })\n return this.skill(id)\n }\n\n /** Agent commands and the commands of every bot, for the `/` picker. */\n commands() {\n const bots = this.home.get('bots')\n const names = new Map<string, string>((bots?.list() ?? []).map((bot: { id: string; name: string }) => [bot.id, bot.name]))\n const listed = bots?.commandList().commands ?? []\n return {\n builtin: BUILTIN_COMMANDS,\n bots: listed.filter((entry: { hidden?: boolean }) => !entry.hidden).map((entry: any) => ({\n name: entry.name,\n description: entry.description,\n usage: entry.usage,\n permission: entry.permission ?? entry.role,\n bot: entry.botId,\n botName: names.get(entry.botId),\n source: entry.source,\n })),\n }\n }\n\n async session(id: string) {\n const session = await this.home.storage.get<AgentSession>(`agent/${id}`)\n if (!session) throw new MushinError('not_found', `No Agent session \"${id}\"`)\n return session\n }\n\n async createSession(): Promise<AgentSession> {\n const now = Date.now()\n const session: AgentSession = { id: randomId(), title: '', createdAt: now, updatedAt: now, tools: await this.tools(), messages: [] }\n await this.save(session)\n return session\n }\n\n async deleteSession(id: string) {\n this.cancel(id)\n await this.home.storage.delete(`agent/${id}`)\n return { deleted: id }\n }\n\n private save(session: AgentSession) {\n session.updatedAt = Date.now()\n return this.home.storage.set(`agent/${session.id}`, session)\n }\n\n private turnContext(page?: string, scene?: SceneInfo): TurnContext {\n const providers = this.home.llm.list()\n const bots = this.home.get('bots')?.list() ?? []\n return {\n page,\n scene,\n providers: providers.length,\n ready: this.home.llm.isReady(),\n bots: bots.length,\n online: bots.filter((bot: { status: string }) => bot.status === 'online').length,\n }\n }\n\n /** A bot's chat around one message, for quoting into an Agent message. */\n private async quoteChat(input: NonNullable<SendOptions['quote']>): Promise<QuotedChat> {\n const bots = this.home.get('bots')\n if (!bots) throw new MushinError('unavailable', 'Quoting a chat needs the bots plugin')\n const record = bots.get(input.bot)\n const list = await bots.messages(input.bot, { channel: input.channel, limit: 300 })\n const index = list.findIndex((item) => item.id === input.id)\n if (index < 0) throw new MushinError('not_found', 'That message is no longer in the chat\\'s recent messages')\n const picked = list.slice(Math.max(0, index - (input.before ?? 6)), index + 1 + (input.after ?? 2))\n return {\n bot: input.bot,\n botName: record.name,\n channel: { type: list[index].channel.type, id: list[index].channel.id, ...(list[index].channel.name ? { name: list[index].channel.name } : {}) },\n focus: input.id,\n lines: picked.map((item) => ({\n id: item.id,\n direction: item.direction,\n user: item.user.name ?? item.user.id,\n userId: item.user.id,\n text: segmentsToText(item.segments).trim().slice(0, 500),\n at: item.timestamp,\n })),\n }\n }\n\n /** Starts a turn and returns once it finishes. Progress streams as `agent/event` events. */\n async send(sessionId: string | undefined, text: string, options: SendOptions = {}) {\n if (!this.home.llm.list().length) {\n throw new MushinError('unavailable', 'Add an LLM provider before using the Agent')\n }\n const usage = await this.usage()\n if (usage.budget && usage.input + usage.output >= usage.budget) {\n throw new MushinError('unavailable', `The Agent used its daily budget of ${usage.budget} tokens; raise it in the Agent settings or wait until tomorrow`)\n }\n const session = sessionId ? await this.session(sessionId) : await this.createSession()\n if (this.running.has(session.id)) throw new MushinError('conflict', 'The Agent is still working on this conversation')\n const running: Running = { abort: new AbortController(), confirms: new Map(), usage: { inputTokens: 0, outputTokens: 0 }, failures: new Map() }\n this.running.set(session.id, running)\n const skills = options.skills?.length ? await Promise.all(options.skills.map((id) => this.skill(id))) : []\n if (!session.title) session.title = (text || skills.map((skill) => skill.name).join(', ')).replace(/\\s+/g, ' ').slice(0, 48)\n if (!session.messages.length && options.scene) {\n session.scene = { key: options.scene.key, ...(options.scene.label ? { label: options.scene.label } : {}), ...(options.page ? { page: options.page } : {}) }\n }\n session.tags = [...new Set([...session.tags ?? [], ...turnTags(text, options, skills)])]\n const quote = options.quote ? await this.quoteChat(options.quote) : undefined\n session.messages.push({\n role: 'user',\n content: [contextNote(this.turnContext(options.page, options.scene)), ...skills.map(skillBlock), quote ? quoteBlock(quote) : '', text].filter(Boolean).join('\\n\\n'),\n display: text,\n ...(skills.length ? { skills: skills.map((skill) => skill.name) } : {}),\n ...(quote ? { quote } : {}),\n at: Date.now(),\n })\n await this.save(session)\n this.emit({ session: session.id, type: 'start' })\n try {\n const final = await this.loop(session, running, options)\n this.emit({ session: session.id, type: 'done', text: final?.text ?? '', usage: running.usage })\n } catch (error) {\n const message = running.abort.signal.aborted ? 'Stopped' : error instanceof Error ? error.message : String(error)\n this.emit({ session: session.id, type: 'error', error: message, ...(error instanceof StepLimit && !running.abort.signal.aborted ? { code: 'max_steps' as const } : {}) })\n } finally {\n this.running.delete(session.id)\n await this.save(session)\n }\n return { session: session.id }\n }\n\n private async loop(session: AgentSession, running: Running, options: SendOptions) {\n let last: LlmResult | undefined\n for (let step = 0; step < this.config.maxSteps; step++) {\n const history: LlmMessage[] = [{ role: 'system', content: SYSTEM_PROMPT }, ...session.messages.map(stripMeta)]\n let result: LlmResult | undefined\n for await (const event of this.home.llm.stream({\n provider: this.config.provider,\n model: this.config.model,\n messages: history,\n tools: session.tools,\n signal: running.abort.signal,\n })) {\n if (event.type === 'delta') this.emit({ session: session.id, type: 'delta', text: event.text })\n else if (event.type === 'result') result = event.result\n }\n if (!result) throw new Error('The provider returned no result')\n last = result\n if (result.usage) {\n running.usage.inputTokens += result.usage.inputTokens\n running.usage.outputTokens += result.usage.outputTokens\n await this.addUsage(result.usage)\n }\n session.messages.push({\n role: 'assistant',\n content: result.text,\n ...(result.toolCalls.length ? { toolCalls: result.toolCalls } : {}),\n ...(result.raw ? { raw: result.raw } : {}),\n at: Date.now(),\n })\n if (!result.toolCalls.length) return result\n\n // A tool call cut off by the output limit, or in a refused turn, may be truncated: never run it.\n if (result.finishReason === 'length' || result.finishReason === 'refusal') {\n const reason = result.finishReason === 'length'\n ? 'The reply hit the output limit, so this tool call may be incomplete and was not run.'\n : 'The model declined this turn, so this tool call was not run.'\n for (const call of result.toolCalls) {\n session.messages.push({ role: 'tool', toolCallId: call.id, content: reason, isError: true, at: Date.now() })\n }\n throw new Error(reason)\n }\n\n for (const call of result.toolCalls) {\n await this.runTool(session, running, call, options)\n }\n // One round is left: ask for a summary instead of a turn that just stops mid-way.\n if (step === this.config.maxSteps - 2) {\n const last = session.messages.at(-1)!\n last.content += `\\n\\n[This was the last tool round for this message (the limit is ${this.config.maxSteps}). Do not call tools now: tell the user what is done, what is left, and that they can send another message to continue.]`\n }\n await this.save(session)\n }\n throw new StepLimit(this.config.maxSteps)\n }\n\n private async runTool(session: AgentSession, running: Running, call: ToolCall, options: SendOptions) {\n const name = fromToolName(call.name)\n const info = this.home.actions.get(name)\n const reply = (content: string, isError = false) => {\n session.messages.push({ role: 'tool', toolCallId: call.id, content, ...(isError ? { isError } : {}), at: Date.now() })\n }\n\n let input: unknown\n try {\n input = JSON.parse(call.arguments || '{}')\n } catch {\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: false, error: 'Arguments are not valid JSON' })\n return reply('The arguments were not valid JSON. Send a JSON object that matches the tool schema.', true)\n }\n if (!info || !info.agent) {\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: false, error: `Unknown tool ${call.name}` })\n return reply(`The tool ${call.name} is not available right now.`, true)\n }\n\n const policy = this.policyOf(await this.settings(), info)\n if (policy === 'off') {\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: false, error: 'Turned off in the Agent settings' })\n return reply(`The user turned ${call.name} off for the Agent. Do not use it; tell them if it is needed.`, true)\n }\n\n this.emit({ session: session.id, type: 'tool-call', id: call.id, action: name, input, risk: info.risk })\n\n if (policy === 'confirm') {\n this.emit({ session: session.id, type: 'confirm', id: call.id, action: name, input })\n const approved = await this.waitForApproval(running, call.id)\n if (!approved) {\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: false, error: 'Declined by the user' })\n return reply('The user declined this action. Do not retry it.', true)\n }\n }\n\n try {\n const result = await this.home.actions.call(name, input, { kind: 'agent', session: session.id, client: options.client })\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: true, result })\n reply(truncate(JSON.stringify(result ?? { ok: true })))\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.emit({ session: session.id, type: 'tool-result', id: call.id, ok: false, error: message })\n const failures = (running.failures.get(name) ?? 0) + 1\n running.failures.set(name, failures)\n const hint = failures >= FAILURE_HINT_AFTER\n ? `\\n${call.name} has failed ${failures} times in this reply. Stop retrying it with guessed inputs: take valid values from the error above or from the tool that lists them, or tell the user what is missing.`\n : ''\n reply(`Error: ${message}${hint}`, true)\n }\n }\n\n private waitForApproval(running: Running, callId: string) {\n return new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => finish(false), this.config.confirmTimeout * 1000)\n const finish = (approved: boolean) => {\n clearTimeout(timer)\n running.confirms.delete(callId)\n resolve(approved)\n }\n running.confirms.set(callId, finish)\n running.abort.signal.addEventListener('abort', () => finish(false), { once: true })\n })\n }\n\n confirm(sessionId: string, callId: string, approved: boolean) {\n const resolve = this.running.get(sessionId)?.confirms.get(callId)\n if (!resolve) throw new MushinError('not_found', 'No action is waiting for approval')\n resolve(approved)\n return { ok: true }\n }\n\n cancel(sessionId: string) {\n const running = this.running.get(sessionId)\n if (!running) return { ok: false }\n running.abort.abort()\n return { ok: true }\n }\n\n status() {\n return { ready: this.home.llm.isReady(), providers: this.home.llm.list().length, running: [...this.running.keys()] }\n }\n\n private *defineActions() {\n const actions = this.home.actions\n\n yield actions.define({\n name: 'agent.status',\n description: 'Whether the Agent can run (a provider exists and passed its test).',\n risk: 'read',\n input: z.object({}),\n handler: () => this.status(),\n })\n\n yield actions.define({\n name: 'agent.sessions',\n description: 'List Agent conversations, newest first.',\n risk: 'read',\n input: z.object({}),\n handler: () => this.sessions(),\n })\n\n yield actions.define({\n name: 'agent.session',\n description: 'Get one Agent conversation with its messages.',\n risk: 'read',\n input: z.object({ id: z.string() }),\n handler: async ({ id }) => {\n const { tools, ...session } = await this.session(id)\n const now = new Set((await this.tools()).map((tool) => tool.name))\n const toolsStale = tools.length !== now.size || tools.some((tool) => !now.has(tool.name))\n return { ...session, messages: session.messages.map(({ raw, ...message }: any) => message), running: this.running.has(id), toolsStale }\n },\n })\n\n yield actions.define({\n name: 'agent.send',\n description: 'Send a message to the Agent. Omit session to start a new conversation. Progress streams over the WebSocket.',\n risk: 'write',\n input: z.object({\n session: z.string().optional(),\n text: z.string(),\n page: z.string().optional(),\n scene: z.object({\n key: z.string(),\n label: z.string().max(200).optional(),\n hint: z.string().max(1000).optional(),\n state: z.record(z.string(), z.union([z.string().max(200), z.number(), z.boolean()])).optional(),\n resource: z.string().max(200).optional(),\n }).optional().describe('The dashboard page as the dashboard describes it'),\n skills: z.array(z.string()).max(5).optional().describe('Skill ids to apply to this message'),\n quote: z.object({\n bot: z.string(),\n channel: z.string().describe('The chat\\'s id'),\n id: z.string().describe('The quoted message\\'s record id, from bots.messages'),\n before: z.number().int().min(0).max(30).optional(),\n after: z.number().int().min(0).max(10).optional(),\n }).optional().describe('A message of a bot\\'s chat to quote, with a few messages around it'),\n }).refine((input) => input.text.trim() || input.skills?.length, 'Send some text or pick a skill'),\n handler: ({ session, text, ...options }, caller) => this.send(session, text, { client: caller.client, ...options }),\n })\n\n yield actions.define({\n name: 'agent.search',\n description: 'Search Agent conversations by text and tags (scene:<page kind>, skill:<id>, command:<name>, resource:<kind>:<id>, status:running).',\n risk: 'read',\n input: z.object({ query: z.string().optional(), tags: z.array(z.string()).optional(), limit: z.number().int().min(1).max(200).optional() }),\n handler: (input) => this.search(input),\n })\n\n yield actions.define({\n name: 'agent.commands',\n description: 'Commands for the Agent\\'s \"/\" picker: its own (new, history, …) and every bot\\'s commands.',\n risk: 'read',\n input: z.object({}),\n handler: () => this.commands(),\n })\n\n yield actions.define({\n name: 'agent.skills.list',\n description: 'List the Agent\\'s skills: built in, from plugins, and written by the user.',\n risk: 'read',\n input: z.object({}),\n handler: () => this.skills(),\n })\n\n yield actions.define({\n name: 'agent.skills.get',\n description: 'Get one Agent skill with its instructions.',\n risk: 'read',\n input: z.object({ id: z.string() }),\n handler: ({ id }) => this.skill(id),\n })\n\n const skillId = z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).describe('Lowercase letters, digits and dashes')\n\n yield actions.define({\n name: 'agent.skills.create',\n description: 'Create a user skill. Omit id to make one from the name.',\n risk: 'write',\n input: skillInput.extend({ id: skillId.optional() }),\n handler: (input) => this.createSkill(input),\n })\n\n yield actions.define({\n name: 'agent.skills.update',\n description: 'Change a skill. Changing a built-in or plugin skill saves your own copy that replaces it until deleted.',\n risk: 'write',\n input: z.object({\n id: z.string(),\n name: skillFields.name.optional(),\n description: z.string().trim().max(300).optional(),\n instructions: skillFields.instructions.optional(),\n whenToUse: skillFields.whenToUse,\n tools: skillFields.tools,\n scenes: skillFields.scenes,\n }),\n handler: ({ id, ...patch }) => this.updateSkill(id, patch),\n })\n\n yield actions.define({\n name: 'agent.skills.delete',\n description: 'Delete a user skill. A built-in or plugin skill it replaced comes back.',\n risk: 'write',\n input: z.object({ id: z.string() }),\n handler: ({ id }) => this.deleteSkill(id),\n })\n\n yield actions.define({\n name: 'agent.skills.setEnabled',\n description: 'Turn a skill on or off; off skills are not offered with $.',\n risk: 'write',\n input: z.object({ id: z.string(), enabled: z.boolean() }),\n handler: ({ id, enabled }) => this.setSkillEnabled(id, enabled),\n })\n\n yield actions.define({\n name: 'agent.skills.import',\n description: 'Import a skill from a SKILL.md file (YAML frontmatter with name and description, then Markdown instructions), as used by Claude and Codex skills.',\n risk: 'write',\n input: z.object({ markdown: z.string().min(1).max(40_000), replace: z.boolean().optional().describe('Overwrite a skill with the same id') }),\n handler: async ({ markdown, replace }) => {\n let parsed: ReturnType<typeof parseSkillMarkdown>\n try {\n parsed = parseSkillMarkdown(markdown)\n } catch (error) {\n throw new MushinError('invalid_input', error instanceof Error ? error.message : String(error))\n }\n const input = skillInput.extend({ id: skillId.optional() }).parse(parsed)\n if (replace && input.id) return this.saveSkill({ ...input, id: input.id })\n return this.createSkill(input)\n },\n })\n\n yield actions.define({\n name: 'agent.skills.export',\n description: 'A skill as a SKILL.md file.',\n risk: 'read',\n input: z.object({ id: z.string() }),\n handler: async ({ id }) => ({ fileName: 'SKILL.md', markdown: formatSkillMarkdown(await this.skill(id)) }),\n })\n\n yield actions.define({\n name: 'agent.profiles.list',\n description: 'What the Agent says and offers on each dashboard page, as the user changed it: hint, welcome line, own suggestions, hidden built-in suggestions, recommended skills.',\n risk: 'read',\n input: z.object({}),\n handler: async () => ({ scenes: SCENE_KEYS, profiles: await this.profiles() }),\n })\n\n yield actions.define({\n name: 'agent.profiles.update',\n description: 'Change what the Agent says and offers on one dashboard page. Send the whole profile; null goes back to the defaults.',\n risk: 'write',\n input: z.object({ scene: z.enum(SCENE_KEYS), profile: profileInput.nullable() }),\n handler: ({ scene, profile }) => this.setProfile(scene, profile),\n })\n\n yield actions.define({\n name: 'agent.policy.list',\n description: 'Every tool the Agent can use, with its plugin, risk and policy: auto (runs), confirm (asks the user first) or off (not offered).',\n risk: 'read',\n input: z.object({}),\n handler: () => this.policies(),\n })\n\n yield actions.define({\n name: 'agent.policy.set',\n description: 'Set the policy of tools by action name: auto, confirm, or off; null goes back to the default (dangerous actions ask, others run). Off tools leave new conversations only.',\n risk: 'dangerous',\n input: z.object({ policies: z.record(z.string(), policyInput.nullable()) }),\n handler: ({ policies }) => this.setPolicy(policies),\n })\n\n yield actions.define({\n name: 'agent.usage',\n description: 'Tokens the Agent used today and its daily budget (0 means no limit).',\n risk: 'read',\n input: z.object({}),\n handler: () => this.usage(),\n })\n\n yield actions.define({\n name: 'agent.confirm',\n description: 'Approve or decline an action the Agent is waiting on.',\n risk: 'write',\n input: z.object({ session: z.string(), id: z.string(), approved: z.boolean() }),\n handler: ({ session, id, approved }) => this.confirm(session, id, approved),\n })\n\n yield actions.define({\n name: 'agent.cancel',\n description: 'Stop the Agent\\'s current reply in a conversation.',\n risk: 'write',\n input: z.object({ session: z.string() }),\n handler: ({ session }) => this.cancel(session),\n })\n\n yield actions.define({\n name: 'agent.deleteSession',\n description: 'Delete an Agent conversation.',\n risk: 'write',\n input: z.object({ id: z.string() }),\n handler: ({ id }) => this.deleteSession(id),\n })\n }\n}\n\nfunction describeTool(action: ActionInfo) {\n const notes: string[] = []\n if (action.risk === 'dangerous') notes.push('Dangerous: the user must approve it.')\n if (action.client) notes.push('Runs in the user\\'s dashboard; fails if none is connected.')\n return [action.description, ...notes].join(' ')\n}\n\n/** Search tags for one message: its scene, skills, a leading command, and the page's resource. */\n/** A quoted chat as the model reads it; the picked message is marked, with its record id for samples. */\nfunction quoteBlock(quote: QuotedChat) {\n const where = quote.channel.type === 'group' ? `group ${quote.channel.name ? `${quote.channel.name} ` : ''}(${quote.channel.id})` : `private chat with ${quote.channel.id}`\n const lines = quote.lines.map((line) => {\n const time = new Date(line.at).toISOString().slice(11, 16)\n const who = line.direction === 'out' ? `${quote.botName} (bot)` : `${line.user} (${line.userId})`\n return `${line.id === quote.focus ? '→ ' : ' '}[${time}] ${who}: ${line.text}`\n })\n return [\n `<quote bot=\"${quote.bot}\" chat=\"${quote.channel.id}\" message=\"${quote.focus}\">`,\n `Messages from ${quote.botName} in the ${where}; the user picked the one marked →. To build or check a flow for it, pass sample { bot: \"${quote.bot}\", id: \"${quote.focus}\", channel: \"${quote.channel.id}\" } to flows.test or flows.preview.`,\n ...lines,\n '</quote>',\n ].join('\\n')\n}\n\nfunction turnTags(text: string, options: SendOptions, skills: AgentSkill[]) {\n const tags: string[] = []\n if (options.scene) tags.push(`scene:${options.scene.key}`)\n if (options.scene?.resource) tags.push(`resource:${options.scene.resource}`)\n for (const skill of skills) tags.push(`skill:${skill.id}`)\n const command = /^\\s*\\/([\\p{L}\\p{N}_-]+)/u.exec(text)\n if (command) tags.push(`command:${command[1]}`)\n return tags\n}\n\nfunction stripMeta(message: AgentMessage): LlmMessage {\n const { display, skills, quote, at, ...rest } = message\n return rest as LlmMessage\n}\n\nfunction truncate(text: string) {\n if (text.length <= MAX_RESULT_CHARS) return text\n return `${text.slice(0, MAX_RESULT_CHARS)}… [truncated ${text.length - MAX_RESULT_CHARS} characters; ask for less, e.g. with a filter or limit]`\n}\n\nexport default Agent\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export declare const SYSTEM_PROMPT = "You are the Agent inside Mushin.js, a chatbot framework where every feature is a plugin. You help the user run their Mushin.js instance by calling tools.\n\nWhat the user manages:\n- Providers: LLM API connections (OpenAI-compatible services such as DeepSeek, Qwen, Moonshot, Ollama; and Anthropic). Tools: providers__*.\n- Bots: chat bots. Each bot has an adapter connection and its own set of bot plugins. The OneBot adapter connects to NapCat (QQ). Tools: bots__*.\n- App plugins: features of Mushin.js itself. Tools: plugins__*.\n- Bot plugins: behaviors of one bot, such as LLM chat or echo. Tools: bots__plugins__*.\n- Conversation flows: node graphs that answer messages (keyword replies, branching, LLM calls, variables). Tools: flows__*.\n\nDashboard pages you can open with ui__navigate: /providers, /bots, /bots/<bot id>, /bots/<bot id>/plugins, /bots/<bot id>/live, /bots/<bot id>/runs, /bots/<bot id>/logs, /flows, /flows/<flow id> (add ?run=<run id> to show a run from flows.history), /personas, /plugins, /logs, /settings/<appearance|features|agent|about> (the Agent's own settings are /settings/agent/<scenes|skills|mcp|tools|model|styles>), /setup.\n\nHow to work:\n- Look before you change: list the relevant things first, and use the ids the tools return. Never invent ids.\n- Take the schemas from providers__types and bots__adapters before creating providers or bots.\n- After a provider is created, run providers__test and report the result.\n- NapCat setup: for forward mode the user enables a WebSocket server in NapCat (default port 3001) and you use url ws://127.0.0.1:3001; for reverse mode the user adds a WebSocket client in NapCat pointing at ws://<this server>/onebot/<bot id>. In both cases NapCat should send messages in \"array\" format, and the token must match.\n- Actions marked dangerous ask the user for approval; if they decline, accept it and do not retry.\n- After creating or changing something, open the matching dashboard page with ui__navigate when a dashboard is connected, so the user can see it.\n- When a bot misbehaves (wrong status, no replies), read bots__logs for that bot before guessing.\n- To build a behavior the user describes, prefer a flow: list flows__nodeTypes (one line per type), read the types you will use with flows__nodeTypes { types: [...] }, create the flow with the right bots, run flows__test with a sample message, fix any issues, then open /flows/<flow id> so the user can see it.\n- Use only node type ids that flows__nodeTypes lists; never guess one. If no node does what the user wants, say so and suggest the closest way to do it instead of searching for one.\n- When the user runs a bot command such as /checkin: if they name where (a group, or \"the master\"), run it for real with bots__commands__run and that target; otherwise run it without a target, which only simulates it.\n- Each user message starts with a <context> block describing the dashboard page they are on; it is not part of what they typed. A <skill> block is a procedure the user picked for this message: follow it.\n- When a choice is the user's to make (which bot, which provider, whether to replace something), ask with ui__ask and wait for the answer instead of guessing.\n- If a tool fails, read the error, fix the input, and try again once; otherwise explain what went wrong. Never retry with guessed names or ids.\n- Keep replies short and concrete. Answer in the language the user writes in.\n\nHow your replies are shown: the dashboard renders Markdown (headings, lists, tables, links, code blocks with syntax highlighting). It also renders these fenced blocks:\n- ```mushin-card with a JSON object { \"title\", \"subtitle\"?, \"status\"?: \"ok\" | \"warning\" | \"error\" | \"info\", \"fields\"?: [{ \"label\", \"value\" }], \"actions\"?: [{ \"label\", \"path\" }] } for something you created or checked, such as a bot or a provider; each action opens a dashboard path.\n- ```mushin-steps with a JSON array of { \"text\", \"done\"?: boolean } for a checklist of steps.\n- ```mermaid for a diagram, such as how a flow or a setup fits together.\nUse them where they help; plain text is fine for short answers.";
2
+ /** The dashboard page a message was sent from, as the dashboard describes it. */
3
+ export interface SceneInfo {
4
+ /** Page kind, such as `flowEditor` or `botDetail`. */
5
+ key: string;
6
+ /** What the page shows, e.g. `Flow editor · Check-in`. */
7
+ label?: string;
8
+ /** What the Agent can do on this page, from the scene settings. */
9
+ hint?: string;
10
+ /** Facts about the page, e.g. `{ errors: 2 }` in the flow editor. */
11
+ state?: Record<string, string | number | boolean>;
12
+ /** The resource the page is about, e.g. `flow:<id>` or `bot:<id>`. */
13
+ resource?: string;
14
+ }
15
+ export interface TurnContext {
16
+ page?: string;
17
+ scene?: SceneInfo;
18
+ providers: number;
19
+ ready: boolean;
20
+ bots: number;
21
+ online: number;
22
+ }
23
+ export declare function contextNote(context: TurnContext): string;
24
+ /** A skill the user picked, as it goes into the user message. */
25
+ export declare function skillBlock(skill: {
26
+ name: string;
27
+ instructions: string;
28
+ tools?: string[];
29
+ }): string;
package/dist/prompt.js ADDED
@@ -0,0 +1,53 @@
1
+ // src/prompt.ts
2
+ const SYSTEM_PROMPT = `You are the Agent inside Mushin.js, a chatbot framework where every feature is a plugin. You help the user run their Mushin.js instance by calling tools.
3
+
4
+ What the user manages:
5
+ - Providers: LLM API connections (OpenAI-compatible services such as DeepSeek, Qwen, Moonshot, Ollama; and Anthropic). Tools: providers__*.
6
+ - Bots: chat bots. Each bot has an adapter connection and its own set of bot plugins. The OneBot adapter connects to NapCat (QQ). Tools: bots__*.
7
+ - App plugins: features of Mushin.js itself. Tools: plugins__*.
8
+ - Bot plugins: behaviors of one bot, such as LLM chat or echo. Tools: bots__plugins__*.
9
+ - Conversation flows: node graphs that answer messages (keyword replies, branching, LLM calls, variables). Tools: flows__*.
10
+
11
+ Dashboard pages you can open with ui__navigate: /providers, /bots, /bots/<bot id>, /bots/<bot id>/plugins, /bots/<bot id>/live, /bots/<bot id>/runs, /bots/<bot id>/logs, /flows, /flows/<flow id> (add ?run=<run id> to show a run from flows.history), /personas, /plugins, /logs, /settings/<appearance|features|agent|about> (the Agent's own settings are /settings/agent/<scenes|skills|mcp|tools|model|styles>), /setup.
12
+
13
+ How to work:
14
+ - Look before you change: list the relevant things first, and use the ids the tools return. Never invent ids.
15
+ - Take the schemas from providers__types and bots__adapters before creating providers or bots.
16
+ - After a provider is created, run providers__test and report the result.
17
+ - NapCat setup: for forward mode the user enables a WebSocket server in NapCat (default port 3001) and you use url ws://127.0.0.1:3001; for reverse mode the user adds a WebSocket client in NapCat pointing at ws://<this server>/onebot/<bot id>. In both cases NapCat should send messages in "array" format, and the token must match.
18
+ - Actions marked dangerous ask the user for approval; if they decline, accept it and do not retry.
19
+ - After creating or changing something, open the matching dashboard page with ui__navigate when a dashboard is connected, so the user can see it.
20
+ - When a bot misbehaves (wrong status, no replies), read bots__logs for that bot before guessing.
21
+ - To build a behavior the user describes, prefer a flow: list flows__nodeTypes (one line per type), read the types you will use with flows__nodeTypes { types: [...] }, create the flow with the right bots, run flows__test with a sample message, fix any issues, then open /flows/<flow id> so the user can see it.
22
+ - Use only node type ids that flows__nodeTypes lists; never guess one. If no node does what the user wants, say so and suggest the closest way to do it instead of searching for one.
23
+ - When the user runs a bot command such as /checkin: if they name where (a group, or "the master"), run it for real with bots__commands__run and that target; otherwise run it without a target, which only simulates it.
24
+ - Each user message starts with a <context> block describing the dashboard page they are on; it is not part of what they typed. A <skill> block is a procedure the user picked for this message: follow it.
25
+ - When a choice is the user's to make (which bot, which provider, whether to replace something), ask with ui__ask and wait for the answer instead of guessing.
26
+ - If a tool fails, read the error, fix the input, and try again once; otherwise explain what went wrong. Never retry with guessed names or ids.
27
+ - Keep replies short and concrete. Answer in the language the user writes in.
28
+
29
+ How your replies are shown: the dashboard renders Markdown (headings, lists, tables, links, code blocks with syntax highlighting). It also renders these fenced blocks:
30
+ - \`\`\`mushin-card with a JSON object { "title", "subtitle"?, "status"?: "ok" | "warning" | "error" | "info", "fields"?: [{ "label", "value" }], "actions"?: [{ "label", "path" }] } for something you created or checked, such as a bot or a provider; each action opens a dashboard path.
31
+ - \`\`\`mushin-steps with a JSON array of { "text", "done"?: boolean } for a checklist of steps.
32
+ - \`\`\`mermaid for a diagram, such as how a flow or a setup fits together.
33
+ Use them where they help; plain text is fine for short answers.`;
34
+ const fact = (value) => typeof value === "string" ? value.replace(/[<>]/g, "") : String(value);
35
+ function contextNote(context) {
36
+ const parts = [
37
+ context.page ? `Dashboard page: ${context.page}` : "No dashboard page reported",
38
+ ...context.scene?.label ? [`Page: ${fact(context.scene.label)}`] : [],
39
+ ...Object.entries(context.scene?.state ?? {}).map(([key, value]) => `${key}: ${fact(value)}`),
40
+ `Providers: ${context.providers}${context.ready ? "" : " (none tested OK yet)"}`,
41
+ `Bots: ${context.bots} (${context.online} online)`
42
+ ];
43
+ const hint = context.scene?.hint ? ` On this page: ${fact(context.scene.hint)}` : "";
44
+ return `<context>${parts.join(". ")}.${hint}</context>`;
45
+ }
46
+ /** A skill the user picked, as it goes into the user message. */
47
+ function skillBlock(skill) {
48
+ const tools = skill.tools?.length ? `\nUseful tools: ${skill.tools.join(", ")}` : "";
49
+ return `<skill name="${skill.name.replace(/"/g, "'")}">\n${skill.instructions.trim()}${tools}\n</skill>`;
50
+ }
51
+ export { SYSTEM_PROMPT, contextNote, skillBlock };
52
+
53
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","names":[],"sources":["../src/prompt.ts"],"sourcesContent":["// The system prompt stays byte-identical for a session's whole life; per-turn facts (current page,\n// counts) are appended to each user message instead. Editing earlier context would invalidate\n// providers' cached prefixes and preserved reasoning.\nexport const SYSTEM_PROMPT = `You are the Agent inside Mushin.js, a chatbot framework where every feature is a plugin. You help the user run their Mushin.js instance by calling tools.\n\nWhat the user manages:\n- Providers: LLM API connections (OpenAI-compatible services such as DeepSeek, Qwen, Moonshot, Ollama; and Anthropic). Tools: providers__*.\n- Bots: chat bots. Each bot has an adapter connection and its own set of bot plugins. The OneBot adapter connects to NapCat (QQ). Tools: bots__*.\n- App plugins: features of Mushin.js itself. Tools: plugins__*.\n- Bot plugins: behaviors of one bot, such as LLM chat or echo. Tools: bots__plugins__*.\n- Conversation flows: node graphs that answer messages (keyword replies, branching, LLM calls, variables). Tools: flows__*.\n\nDashboard pages you can open with ui__navigate: /providers, /bots, /bots/<bot id>, /bots/<bot id>/plugins, /bots/<bot id>/live, /bots/<bot id>/runs, /bots/<bot id>/logs, /flows, /flows/<flow id> (add ?run=<run id> to show a run from flows.history), /personas, /plugins, /logs, /settings/<appearance|features|agent|about> (the Agent's own settings are /settings/agent/<scenes|skills|mcp|tools|model|styles>), /setup.\n\nHow to work:\n- Look before you change: list the relevant things first, and use the ids the tools return. Never invent ids.\n- Take the schemas from providers__types and bots__adapters before creating providers or bots.\n- After a provider is created, run providers__test and report the result.\n- NapCat setup: for forward mode the user enables a WebSocket server in NapCat (default port 3001) and you use url ws://127.0.0.1:3001; for reverse mode the user adds a WebSocket client in NapCat pointing at ws://<this server>/onebot/<bot id>. In both cases NapCat should send messages in \"array\" format, and the token must match.\n- Actions marked dangerous ask the user for approval; if they decline, accept it and do not retry.\n- After creating or changing something, open the matching dashboard page with ui__navigate when a dashboard is connected, so the user can see it.\n- When a bot misbehaves (wrong status, no replies), read bots__logs for that bot before guessing.\n- To build a behavior the user describes, prefer a flow: list flows__nodeTypes (one line per type), read the types you will use with flows__nodeTypes { types: [...] }, create the flow with the right bots, run flows__test with a sample message, fix any issues, then open /flows/<flow id> so the user can see it.\n- Use only node type ids that flows__nodeTypes lists; never guess one. If no node does what the user wants, say so and suggest the closest way to do it instead of searching for one.\n- When the user runs a bot command such as /checkin: if they name where (a group, or \"the master\"), run it for real with bots__commands__run and that target; otherwise run it without a target, which only simulates it.\n- Each user message starts with a <context> block describing the dashboard page they are on; it is not part of what they typed. A <skill> block is a procedure the user picked for this message: follow it.\n- When a choice is the user's to make (which bot, which provider, whether to replace something), ask with ui__ask and wait for the answer instead of guessing.\n- If a tool fails, read the error, fix the input, and try again once; otherwise explain what went wrong. Never retry with guessed names or ids.\n- Keep replies short and concrete. Answer in the language the user writes in.\n\nHow your replies are shown: the dashboard renders Markdown (headings, lists, tables, links, code blocks with syntax highlighting). It also renders these fenced blocks:\n- \\`\\`\\`mushin-card with a JSON object { \"title\", \"subtitle\"?, \"status\"?: \"ok\" | \"warning\" | \"error\" | \"info\", \"fields\"?: [{ \"label\", \"value\" }], \"actions\"?: [{ \"label\", \"path\" }] } for something you created or checked, such as a bot or a provider; each action opens a dashboard path.\n- \\`\\`\\`mushin-steps with a JSON array of { \"text\", \"done\"?: boolean } for a checklist of steps.\n- \\`\\`\\`mermaid for a diagram, such as how a flow or a setup fits together.\nUse them where they help; plain text is fine for short answers.`\n\n/** The dashboard page a message was sent from, as the dashboard describes it. */\nexport interface SceneInfo {\n /** Page kind, such as `flowEditor` or `botDetail`. */\n key: string\n /** What the page shows, e.g. `Flow editor · Check-in`. */\n label?: string\n /** What the Agent can do on this page, from the scene settings. */\n hint?: string\n /** Facts about the page, e.g. `{ errors: 2 }` in the flow editor. */\n state?: Record<string, string | number | boolean>\n /** The resource the page is about, e.g. `flow:<id>` or `bot:<id>`. */\n resource?: string\n}\n\nexport interface TurnContext {\n page?: string\n scene?: SceneInfo\n providers: number\n ready: boolean\n bots: number\n online: number\n}\n\nconst fact = (value: string | number | boolean) => (typeof value === 'string' ? value.replace(/[<>]/g, '') : String(value))\n\nexport function contextNote(context: TurnContext) {\n const parts = [\n context.page ? `Dashboard page: ${context.page}` : 'No dashboard page reported',\n ...(context.scene?.label ? [`Page: ${fact(context.scene.label)}`] : []),\n ...Object.entries(context.scene?.state ?? {}).map(([key, value]) => `${key}: ${fact(value)}`),\n `Providers: ${context.providers}${context.ready ? '' : ' (none tested OK yet)'}`,\n `Bots: ${context.bots} (${context.online} online)`,\n ]\n const hint = context.scene?.hint ? ` On this page: ${fact(context.scene.hint)}` : ''\n return `<context>${parts.join('. ')}.${hint}</context>`\n}\n\n/** A skill the user picked, as it goes into the user message. */\nexport function skillBlock(skill: { name: string; instructions: string; tools?: string[] }) {\n const tools = skill.tools?.length ? `\\nUseful tools: ${skill.tools.join(', ')}` : ''\n return `<skill name=\"${skill.name.replace(/\"/g, '\\'')}\">\\n${skill.instructions.trim()}${tools}\\n</skill>`\n}\n"],"mappings":";AAGA,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwD7B,MAAM,QAAQ,UAAsC,OAAO,UAAU,WAAW,MAAM,QAAQ,SAAS,EAAE,IAAI,OAAO,KAAK;AAEzH,SAAgB,YAAY,SAAsB;CAChD,MAAM,QAAQ;EACZ,QAAQ,OAAO,mBAAmB,QAAQ,SAAS;EACnD,GAAI,QAAQ,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC;EACrE,GAAG,OAAO,QAAQ,QAAQ,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,IAAI,KAAK,KAAK,GAAG;EAC5F,cAAc,QAAQ,YAAY,QAAQ,QAAQ,KAAK;EACvD,SAAS,QAAQ,KAAK,IAAI,QAAQ,OAAO;CAC3C;CACA,MAAM,OAAO,QAAQ,OAAO,OAAO,kBAAkB,KAAK,QAAQ,MAAM,IAAI,MAAM;CAClF,OAAO,YAAY,MAAM,KAAK,IAAI,EAAE,GAAG,KAAK;AAC9C;;AAGA,SAAgB,WAAW,OAAiE;CAC1F,MAAM,QAAQ,MAAM,OAAO,SAAS,mBAAmB,MAAM,MAAM,KAAK,IAAI,MAAM;CAClF,OAAO,gBAAgB,MAAM,KAAK,QAAQ,MAAM,GAAI,EAAE,MAAM,MAAM,aAAa,KAAK,IAAI,MAAM;AAChG"}
@@ -0,0 +1,75 @@
1
+ import { AgentSkill } from "./skills.js";
2
+ import { Risk, z } from "@mushinjs/core";
3
+ /** Whether the Agent runs a tool on its own, asks first, or never sees it. */
4
+ export type ToolPolicy = 'auto' | 'confirm' | 'off';
5
+ /** The default for a risk level: dangerous actions ask first, the rest run. */
6
+ export declare function defaultPolicy(risk: Risk): ToolPolicy;
7
+ /** A suggestion the user adds to a page. */
8
+ export interface CustomRecommendation {
9
+ text: string;
10
+ /** What is sent when it is clicked; the text when empty. */
11
+ prompt?: string;
12
+ skill?: string;
13
+ }
14
+ /** What the Agent says and offers on one dashboard page; empty fields keep the dashboard's defaults. */
15
+ export interface SceneProfile {
16
+ /** Replaces the page's hint, which goes into the <context> block. */
17
+ hint?: string;
18
+ /** Replaces the welcome line of an empty conversation. */
19
+ welcome?: string;
20
+ /** Shown before the built-in suggestions. */
21
+ recommendations?: CustomRecommendation[];
22
+ /** Ids of built-in suggestions to hide. */
23
+ hidden?: string[];
24
+ /** Skill ids recommended on this page, besides the skills' own scenes. */
25
+ skills?: string[];
26
+ /** Show urgent suggestions above the input while a conversation is open. On unless false. */
27
+ autoShow?: boolean;
28
+ }
29
+ export interface AgentSettings {
30
+ profiles: Record<string, SceneProfile>;
31
+ /** Overrides of the default policy, by action name. */
32
+ toolPolicy: Record<string, ToolPolicy>;
33
+ /** Built-in and plugin skills the user turned off. */
34
+ disabledSkills: string[];
35
+ }
36
+ export declare const SCENE_KEYS: readonly ["overview", "setup", "providers", "bots", "botDetail", "flows", "flowEditor", "personas", "knowledge", "database", "plugins", "logs", "settings", "agent"];
37
+ export declare const profileInput: z.ZodObject<{
38
+ hint: z.ZodOptional<z.ZodString>;
39
+ welcome: z.ZodOptional<z.ZodString>;
40
+ recommendations: z.ZodOptional<z.ZodArray<z.ZodObject<{
41
+ text: z.ZodString;
42
+ prompt: z.ZodOptional<z.ZodString>;
43
+ skill: z.ZodOptional<z.ZodString>;
44
+ }, z.core.$strip>>>;
45
+ hidden: z.ZodOptional<z.ZodArray<z.ZodString>>;
46
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
+ autoShow: z.ZodOptional<z.ZodBoolean>;
48
+ }, z.core.$strip>;
49
+ export declare const policyInput: z.ZodEnum<{
50
+ auto: "auto";
51
+ confirm: "confirm";
52
+ off: "off";
53
+ }>;
54
+ /** Token use of one day, for the daily budget. */
55
+ export interface TokenUsage {
56
+ day: string;
57
+ input: number;
58
+ output: number;
59
+ }
60
+ export declare function today(): string;
61
+ /**
62
+ * Reads a skill in the `SKILL.md` format of Claude and Codex skills: YAML frontmatter with `name`
63
+ * and `description`, then the instructions in Markdown. Only flat keys and simple lists are read.
64
+ */
65
+ export declare function parseSkillMarkdown(source: string): {
66
+ scenes?: string[] | undefined;
67
+ tools?: string[] | undefined;
68
+ whenToUse?: string | undefined;
69
+ name: string;
70
+ description: string;
71
+ instructions: string;
72
+ id?: string | undefined;
73
+ };
74
+ /** Writes a skill as a `SKILL.md` file. */
75
+ export declare function formatSkillMarkdown(skill: Pick<AgentSkill, 'id' | 'name' | 'description' | 'instructions' | 'whenToUse' | 'tools' | 'scenes'>): string;
@@ -0,0 +1,128 @@
1
+ import { z } from "@mushinjs/core";
2
+ // src/settings.ts
3
+ /** The default for a risk level: dangerous actions ask first, the rest run. */
4
+ function defaultPolicy(risk) {
5
+ return risk === "dangerous" ? "confirm" : "auto";
6
+ }
7
+ const SCENE_KEYS = [
8
+ "overview",
9
+ "setup",
10
+ "providers",
11
+ "bots",
12
+ "botDetail",
13
+ "flows",
14
+ "flowEditor",
15
+ "personas",
16
+ "knowledge",
17
+ "database",
18
+ "plugins",
19
+ "logs",
20
+ "settings",
21
+ "agent"
22
+ ];
23
+ const profileInput = z.object({
24
+ hint: z.string().max(1e3).optional(),
25
+ welcome: z.string().max(500).optional(),
26
+ recommendations: z.array(z.object({
27
+ text: z.string().trim().min(1).max(200),
28
+ prompt: z.string().max(2e3).optional(),
29
+ skill: z.string().optional()
30
+ })).max(10).optional(),
31
+ hidden: z.array(z.string()).max(30).optional(),
32
+ skills: z.array(z.string()).max(20).optional(),
33
+ autoShow: z.boolean().optional()
34
+ });
35
+ const policyInput = z.enum([
36
+ "auto",
37
+ "confirm",
38
+ "off"
39
+ ]);
40
+ function today() {
41
+ const now = /* @__PURE__ */ new Date();
42
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
43
+ }
44
+ function unquote(value) {
45
+ const text = value.trim();
46
+ if (text.startsWith("\"") && text.endsWith("\"") || text.startsWith("'") && text.endsWith("'")) return text.slice(1, -1);
47
+ return text;
48
+ }
49
+ function listValue(value) {
50
+ const text = value.trim();
51
+ if (text.startsWith("[") && text.endsWith("]")) return text.slice(1, -1).split(",").map(unquote).filter(Boolean);
52
+ return text.split(/[,\s]+/).map(unquote).filter(Boolean);
53
+ }
54
+ /**
55
+ * Reads a skill in the `SKILL.md` format of Claude and Codex skills: YAML frontmatter with `name`
56
+ * and `description`, then the instructions in Markdown. Only flat keys and simple lists are read.
57
+ */
58
+ function parseSkillMarkdown(source) {
59
+ const text = source.replace(/^/, "").replace(/\r\n/g, "\n");
60
+ const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(text);
61
+ const meta = {};
62
+ let body = text;
63
+ if (match) {
64
+ body = match[2];
65
+ let listKey;
66
+ let folded;
67
+ for (const line of match[1].split("\n")) {
68
+ const item = /^\s+-\s+(.*)$/.exec(line);
69
+ if (item && listKey) {
70
+ meta[listKey].push(unquote(item[1]));
71
+ continue;
72
+ }
73
+ if (folded && /^\s+\S/.test(line)) {
74
+ meta[folded] = `${meta[folded] ? `${meta[folded]} ` : ""}${line.trim()}`;
75
+ continue;
76
+ }
77
+ listKey = folded = void 0;
78
+ const pair = /^([A-Za-z][\w-]*):\s*(.*)$/.exec(line);
79
+ if (!pair) continue;
80
+ const [, key, value] = pair;
81
+ if (!value.trim()) {
82
+ meta[key] = [];
83
+ listKey = key;
84
+ } else if (/^[>|][-+]?$/.test(value.trim())) {
85
+ meta[key] = "";
86
+ folded = key;
87
+ } else meta[key] = value;
88
+ }
89
+ }
90
+ const str = (key) => {
91
+ const value = meta[key];
92
+ return typeof value === "string" ? unquote(value) : void 0;
93
+ };
94
+ const list = (key) => {
95
+ const value = meta[key];
96
+ if (value === void 0) return void 0;
97
+ return Array.isArray(value) ? value : listValue(value);
98
+ };
99
+ const heading = /^#\s+(.+)$/m.exec(body)?.[1]?.trim();
100
+ const name = str("name") ?? heading;
101
+ if (!name) throw new Error("The skill has no name: add \"name:\" to the frontmatter or start with a \"# Title\"");
102
+ return {
103
+ ...str("id") ? { id: str("id") } : {},
104
+ name,
105
+ description: str("description") ?? "",
106
+ instructions: body.trim(),
107
+ ...str("when_to_use") ?? str("whenToUse") ? { whenToUse: str("when_to_use") ?? str("whenToUse") } : {},
108
+ ...list("tools") ?? list("allowed-tools") ? { tools: list("tools") ?? list("allowed-tools") } : {},
109
+ ...list("scenes") ? { scenes: list("scenes") } : {}
110
+ };
111
+ }
112
+ const yamlString = (value) => /^[\w\s一-鿿,.()()、,。-]*$/.test(value) && value.trim() === value ? value : JSON.stringify(value);
113
+ /** Writes a skill as a `SKILL.md` file. */
114
+ function formatSkillMarkdown(skill) {
115
+ const lines = [
116
+ "---",
117
+ `name: ${yamlString(skill.name)}`,
118
+ `description: ${yamlString(skill.description)}`
119
+ ];
120
+ if (skill.whenToUse) lines.push(`when_to_use: ${yamlString(skill.whenToUse)}`);
121
+ if (skill.tools?.length) lines.push(`tools: [${skill.tools.join(", ")}]`);
122
+ if (skill.scenes?.length) lines.push(`scenes: [${skill.scenes.join(", ")}]`);
123
+ lines.push(`id: ${skill.id}`, "---", "", skill.instructions.trim(), "");
124
+ return lines.join("\n");
125
+ }
126
+ export { SCENE_KEYS, defaultPolicy, formatSkillMarkdown, parseSkillMarkdown, policyInput, profileInput, today };
127
+
128
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","names":[],"sources":["../src/settings.ts"],"sourcesContent":["import { type Risk, z } from '@mushinjs/core'\nimport type { AgentSkill } from './skills'\n\n/** Whether the Agent runs a tool on its own, asks first, or never sees it. */\nexport type ToolPolicy = 'auto' | 'confirm' | 'off'\n\n/** The default for a risk level: dangerous actions ask first, the rest run. */\nexport function defaultPolicy(risk: Risk): ToolPolicy {\n return risk === 'dangerous' ? 'confirm' : 'auto'\n}\n\n/** A suggestion the user adds to a page. */\nexport interface CustomRecommendation {\n text: string\n /** What is sent when it is clicked; the text when empty. */\n prompt?: string\n skill?: string\n}\n\n/** What the Agent says and offers on one dashboard page; empty fields keep the dashboard's defaults. */\nexport interface SceneProfile {\n /** Replaces the page's hint, which goes into the <context> block. */\n hint?: string\n /** Replaces the welcome line of an empty conversation. */\n welcome?: string\n /** Shown before the built-in suggestions. */\n recommendations?: CustomRecommendation[]\n /** Ids of built-in suggestions to hide. */\n hidden?: string[]\n /** Skill ids recommended on this page, besides the skills' own scenes. */\n skills?: string[]\n /** Show urgent suggestions above the input while a conversation is open. On unless false. */\n autoShow?: boolean\n}\n\nexport interface AgentSettings {\n profiles: Record<string, SceneProfile>\n /** Overrides of the default policy, by action name. */\n toolPolicy: Record<string, ToolPolicy>\n /** Built-in and plugin skills the user turned off. */\n disabledSkills: string[]\n}\n\nexport const SCENE_KEYS = ['overview', 'setup', 'providers', 'bots', 'botDetail', 'flows', 'flowEditor', 'personas', 'knowledge', 'database', 'plugins', 'logs', 'settings', 'agent'] as const\n\nexport const profileInput = z.object({\n hint: z.string().max(1000).optional(),\n welcome: z.string().max(500).optional(),\n recommendations: z.array(z.object({\n text: z.string().trim().min(1).max(200),\n prompt: z.string().max(2000).optional(),\n skill: z.string().optional(),\n })).max(10).optional(),\n hidden: z.array(z.string()).max(30).optional(),\n skills: z.array(z.string()).max(20).optional(),\n autoShow: z.boolean().optional(),\n})\n\nexport const policyInput = z.enum(['auto', 'confirm', 'off'])\n\n/** Token use of one day, for the daily budget. */\nexport interface TokenUsage {\n day: string\n input: number\n output: number\n}\n\nexport function today() {\n const now = new Date()\n return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`\n}\n\n// ---- SKILL.md ----\n\nfunction unquote(value: string) {\n const text = value.trim()\n if ((text.startsWith('\"') && text.endsWith('\"')) || (text.startsWith('\\'') && text.endsWith('\\''))) return text.slice(1, -1)\n return text\n}\n\nfunction listValue(value: string) {\n const text = value.trim()\n if (text.startsWith('[') && text.endsWith(']')) return text.slice(1, -1).split(',').map(unquote).filter(Boolean)\n return text.split(/[,\\s]+/).map(unquote).filter(Boolean)\n}\n\n/**\n * Reads a skill in the `SKILL.md` format of Claude and Codex skills: YAML frontmatter with `name`\n * and `description`, then the instructions in Markdown. Only flat keys and simple lists are read.\n */\nexport function parseSkillMarkdown(source: string) {\n const text = source.replace(/^/, '').replace(/\\r\\n/g, '\\n')\n const match = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/.exec(text)\n const meta: Record<string, string | string[]> = {}\n let body = text\n if (match) {\n body = match[2]\n let listKey: string | undefined\n let folded: string | undefined\n for (const line of match[1].split('\\n')) {\n const item = /^\\s+-\\s+(.*)$/.exec(line)\n if (item && listKey) {\n ;(meta[listKey] as string[]).push(unquote(item[1]))\n continue\n }\n if (folded && /^\\s+\\S/.test(line)) {\n meta[folded] = `${meta[folded] ? `${meta[folded]} ` : ''}${line.trim()}`\n continue\n }\n listKey = folded = undefined\n const pair = /^([A-Za-z][\\w-]*):\\s*(.*)$/.exec(line)\n if (!pair) continue\n const [, key, value] = pair\n if (!value.trim()) {\n meta[key] = []\n listKey = key\n } else if (/^[>|][-+]?$/.test(value.trim())) {\n meta[key] = ''\n folded = key\n } else {\n meta[key] = value\n }\n }\n }\n const str = (key: string) => {\n const value = meta[key]\n return typeof value === 'string' ? unquote(value) : undefined\n }\n const list = (key: string) => {\n const value = meta[key]\n if (value === undefined) return undefined\n return Array.isArray(value) ? value : listValue(value)\n }\n const heading = /^#\\s+(.+)$/m.exec(body)?.[1]?.trim()\n const name = str('name') ?? heading\n if (!name) throw new Error('The skill has no name: add \"name:\" to the frontmatter or start with a \"# Title\"')\n return {\n ...(str('id') ? { id: str('id') } : {}),\n name,\n description: str('description') ?? '',\n instructions: body.trim(),\n ...(str('when_to_use') ?? str('whenToUse') ? { whenToUse: str('when_to_use') ?? str('whenToUse') } : {}),\n ...(list('tools') ?? list('allowed-tools') ? { tools: list('tools') ?? list('allowed-tools') } : {}),\n ...(list('scenes') ? { scenes: list('scenes') } : {}),\n }\n}\n\nconst yamlString = (value: string) => (/^[\\w\\s一-鿿,.()()、,。-]*$/.test(value) && value.trim() === value ? value : JSON.stringify(value))\n\n/** Writes a skill as a `SKILL.md` file. */\nexport function formatSkillMarkdown(skill: Pick<AgentSkill, 'id' | 'name' | 'description' | 'instructions' | 'whenToUse' | 'tools' | 'scenes'>) {\n const lines = ['---', `name: ${yamlString(skill.name)}`, `description: ${yamlString(skill.description)}`]\n if (skill.whenToUse) lines.push(`when_to_use: ${yamlString(skill.whenToUse)}`)\n if (skill.tools?.length) lines.push(`tools: [${skill.tools.join(', ')}]`)\n if (skill.scenes?.length) lines.push(`scenes: [${skill.scenes.join(', ')}]`)\n lines.push(`id: ${skill.id}`, '---', '', skill.instructions.trim(), '')\n return lines.join('\\n')\n}\n"],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { z } from "@mushinjs/core";
2
+ /** Where a skill comes from: shipped with the Agent, added by a plugin, or written by the user. */
3
+ export type SkillSource = 'builtin' | 'plugin' | 'user';
4
+ /** A reusable way of doing something; picked with `$`, its instructions go into that one message. */
5
+ export interface AgentSkill {
6
+ id: string;
7
+ name: string;
8
+ /** One line: what it is for. */
9
+ description: string;
10
+ /** The steps, in Markdown. */
11
+ instructions: string;
12
+ /** When to use it, for people picking one. */
13
+ whenToUse?: string;
14
+ /** Actions it tends to use, e.g. `bots.logs`. */
15
+ tools?: string[];
16
+ /** Scene keys where it is recommended, e.g. `botDetail`. */
17
+ scenes?: string[];
18
+ source: SkillSource;
19
+ /** The plugin that added it. */
20
+ plugin?: string;
21
+ /** Off: not offered with `$`. */
22
+ enabled: boolean;
23
+ }
24
+ /** What a plugin passes to `ctx.agent.defineSkill()`. */
25
+ export type SkillDefinition = Omit<AgentSkill, 'source' | 'plugin' | 'enabled'>;
26
+ export declare const skillFields: {
27
+ name: z.ZodString;
28
+ description: z.ZodDefault<z.ZodString>;
29
+ instructions: z.ZodString;
30
+ whenToUse: z.ZodOptional<z.ZodString>;
31
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
32
+ scenes: z.ZodOptional<z.ZodArray<z.ZodString>>;
33
+ };
34
+ export declare const skillInput: z.ZodObject<{
35
+ name: z.ZodString;
36
+ description: z.ZodDefault<z.ZodString>;
37
+ instructions: z.ZodString;
38
+ whenToUse: z.ZodOptional<z.ZodString>;
39
+ tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
40
+ scenes: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ }, z.core.$strip>;
42
+ /** Skills that ship with the Agent. */
43
+ export declare const BUILTIN_SKILLS: SkillDefinition[];