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:
+127
@@ -2140,6 +2140,51 @@ const schema: BUN_SQLITE_DatabaseSchemaType = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tableName: "skills",
|
||||
tableDescription: `Runbooks bots follow (Settings, under Skills). A bot sees each one's name and description and reads the whole body only when it loads it with load_skill. Per bot (bot_skills), per workspace (workspace_skills) or every bot (all_bots)`,
|
||||
fields: [
|
||||
{
|
||||
fieldName: "name",
|
||||
fieldDescription: `What the settings page calls it, e.g. "Weekly SEO report"`,
|
||||
dataType: "TEXT",
|
||||
},
|
||||
{
|
||||
fieldName: "slug",
|
||||
fieldDescription: `What a bot passes to load_skill: lowercase letters, numbers and hyphens. Unique`,
|
||||
dataType: "TEXT",
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
fieldName: "description",
|
||||
fieldDescription: `The one line a bot reads to decide whether the skill applies`,
|
||||
dataType: "TEXT",
|
||||
},
|
||||
{
|
||||
fieldName: "body",
|
||||
fieldDescription: `The runbook itself, markdown. Read only when a bot loads the skill`,
|
||||
dataType: "TEXT",
|
||||
},
|
||||
{
|
||||
fieldName: "all_bots",
|
||||
fieldDescription: `1 gives it to every bot, the coordinator included`,
|
||||
dataType: "INTEGER",
|
||||
options: [0, 1],
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
fieldName: "enabled",
|
||||
dataType: "INTEGER",
|
||||
options: [0, 1],
|
||||
defaultValue: 1,
|
||||
},
|
||||
{
|
||||
fieldName: "user_id",
|
||||
fieldDescription: `Who created it. Not a foreign key: it belongs to the install`,
|
||||
dataType: "INTEGER",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tableName: "bot_mcp_servers",
|
||||
tableDescription: `A bot that gets an MCP server's tools everywhere it works`,
|
||||
@@ -2304,6 +2349,88 @@ const schema: BUN_SQLITE_DatabaseSchemaType = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tableName: "bot_skills",
|
||||
tableDescription: `A bot that gets a skill everywhere it works`,
|
||||
fields: [
|
||||
{
|
||||
fieldName: "bot_id",
|
||||
dataType: "INTEGER",
|
||||
foreignKey: {
|
||||
cascadeDelete: true,
|
||||
destinationTableName: "bots",
|
||||
destinationTableColumnName: "id",
|
||||
destinationTableColumnType: "INTEGER",
|
||||
foreignKeyName: "bot_skills_bots_fk",
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: "skill_id",
|
||||
dataType: "INTEGER",
|
||||
foreignKey: {
|
||||
cascadeDelete: true,
|
||||
destinationTableName: "skills",
|
||||
destinationTableColumnName: "id",
|
||||
destinationTableColumnType: "INTEGER",
|
||||
foreignKeyName: "bot_skills_skills_fk",
|
||||
},
|
||||
},
|
||||
],
|
||||
uniqueConstraints: [
|
||||
{
|
||||
alias: "one_skill_per_bot",
|
||||
constraintName: "A bot gets a skill once",
|
||||
constraintTableFields: [{ value: "bot_id" }, { value: "skill_id" }],
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
indexName: "idx_bot_skills_skill_id",
|
||||
indexTableFields: ["skill_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tableName: "workspace_skills",
|
||||
tableDescription: `Every bot in the workspace gets the skill there`,
|
||||
fields: [
|
||||
{
|
||||
fieldName: "workspace_id",
|
||||
dataType: "INTEGER",
|
||||
foreignKey: {
|
||||
cascadeDelete: true,
|
||||
destinationTableName: "workspaces",
|
||||
destinationTableColumnName: "id",
|
||||
destinationTableColumnType: "INTEGER",
|
||||
foreignKeyName: "workspace_skills_workspaces_fk",
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: "skill_id",
|
||||
dataType: "INTEGER",
|
||||
foreignKey: {
|
||||
cascadeDelete: true,
|
||||
destinationTableName: "skills",
|
||||
destinationTableColumnName: "id",
|
||||
destinationTableColumnType: "INTEGER",
|
||||
foreignKeyName: "workspace_skills_skills_fk",
|
||||
},
|
||||
},
|
||||
],
|
||||
uniqueConstraints: [
|
||||
{
|
||||
alias: "one_skill_per_workspace",
|
||||
constraintName: "A workspace gets a skill once",
|
||||
constraintTableFields: [{ value: "workspace_id" }, { value: "skill_id" }],
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
indexName: "idx_workspace_skills_skill_id",
|
||||
indexTableFields: ["skill_id"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
tableName: "logs",
|
||||
fields: [
|
||||
|
||||
Reference in New Issue
Block a user