Give bots skills: runbooks they load when a job matches

A skill is a runbook: a name, a unique slug, a one-line description of when
it applies, and the markdown body. A run only ever sees the slug and
description (one load_skill tool carries them), and reads the whole body
only when it calls the tool, so a long runbook costs nothing until needed.

Skills are the third extension kind beside MCP servers and custom tools, on
the same botExtensionsSql path, so per-bot, per-workspace and all-bots
grants work the same way, and attachDefaultExtensions hands an every-bot
skill to new bots. load_skill is a reserved tool name, so a custom tool
cannot shadow it, and a built-in wins a clash.

Managed on Settings > Skills; ExtensionRow links each kind to its own page
and a skill never reports a failure, since nothing connects or runs.
This commit is contained in:
2026-10-22 12:31:42 +01:00
parent 379ef35e08
commit 08d02e6033
62 changed files with 1438 additions and 79 deletions
+1
View File
@@ -86,6 +86,7 @@ export const JobStepToolLabels: Record<string, string> = {
assign_computer: "Changing computer access",
update_memory: "Updating memory",
update_instructions: "Updating instructions",
load_skill: "Reading a skill",
};
/**
+1
View File
@@ -66,6 +66,7 @@ export const ReservedToolNames = [
"delegate_task",
"delete_schedule",
"list_schedules",
"load_skill",
"report_run_failed",
"search_computers",
"set_bot_workspace_instructions",
+2
View File
@@ -1,5 +1,6 @@
import {
Bell,
BookOpen,
Bot,
CpuAi,
DatabaseBackup,
@@ -61,6 +62,7 @@ export const SettingsPages: SettingsPage[] = [
{ title: "Computers", url: SettingsComputersPath, Icon: Monitor, can_view: canManageCrew },
{ title: "MCPs", url: "/admin/settings/mcps", Icon: Plug, can_view: canManageCrew },
{ title: "Tools", url: "/admin/settings/tools", Icon: Wrench, can_view: canManageCrew },
{ title: "Skills", url: "/admin/settings/skills", Icon: BookOpen, can_view: canManageCrew },
{
title: "Users",
url: "/admin/settings/users",
+16
View File
@@ -0,0 +1,16 @@
/**
* What a skill is: a runbook a bot follows. It reads the name and
* description in its prompt and pulls the body with `load_skill` only when
* the skill applies, so a long runbook costs nothing until it's needed.
*/
export const SkillNoun = "skill";
/**
* The tool a bot calls to read a skill's body.
*/
export const LoadSkillToolName = "load_skill";
/**
* The slug format, for the settings form and its hint.
*/
export const SkillSlugPattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;