package sqlite import ( "context" "database/sql" "errors" "fmt" "inbox/internal/domain/skill" ) type builtinSkillSeed struct { Definition skill.Definition RoleNames []string } const inboxSkillMarkdown = `--- name: inbox description: "Use Inbox V2 from a role runtime. Supports two operations only: send and ask. Use this when you need to post a workflow message or a blocking question into an existing topic with inbox api." --- # Inbox Use this skill when a role needs to communicate through Inbox V2. This skill has two operations only: - send: post a normal workflow message into an existing topic - ask: post a blocking question that needs an answer from another role ## Before using either operation Resolve the topic id first if you only know the workspace and topic slug: inbox api GET "/api/v2/topics?workspace_id=" Pick the topic record you want, then use its id in the message API below. ## send Use send for handoffs, updates, decisions, or summaries. inbox api POST /api/v2/topics//messages --data '{ "workspace_id": "", "from_role_name": "", "to_expr": "", "type": "chat", "stage": "execution", "body_markdown": "" }' Rules: - Set type to the actual intent: chat, proposal, decision, or summary. - Keep stage aligned with the current workflow state. - Add reply_to_message_id when replying to a specific message. ## ask Use ask when you are blocked and need a concrete answer. inbox api POST /api/v2/topics//messages --data '{ "workspace_id": "", "from_role_name": "", "to_expr": "", "type": "question", "stage": "", "body_markdown": "" }' Rules: - Ask one concrete blocker per message. - State the decision, artifact, or approval you need back. - Keep stage aligned with the task or planning stage you are in. - Prefer ask over vague status pings.` var builtinSkillSeeds = []builtinSkillSeed{ { Definition: skill.Definition{ SkillKey: "inbox", Name: "Inbox", Description: "Use Inbox V2 from a role runtime with two operations: send and ask.", SourceType: "capability", ContentMarkdown: inboxSkillMarkdown, Status: "active", }, RoleNames: []string{ "leader", "worker", }, }, } func (s *Store) ensureBuiltinSkills(ctx context.Context) error { tx, err := s.db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("begin builtin skill seed: %w", err) } defer tx.Rollback() now := s.now() for _, seed := range builtinSkillSeeds { if err := s.seedBuiltinSkillTx(ctx, tx, now, seed); err != nil { return err } } if err := tx.Commit(); err != nil { return fmt.Errorf("commit builtin skill seed: %w", err) } return nil } func (s *Store) seedBuiltinSkillTx(ctx context.Context, tx *sql.Tx, now string, seed builtinSkillSeed) error { item, err := getSkillByKeyTx(ctx, tx, seed.Definition.SkillKey) if err != nil { if !errors.Is(err, sql.ErrNoRows) { return err } item = seed.Definition item.ID = "builtin-skill-" + seed.Definition.SkillKey item.Version = 1 item.CreatedAt = now item.UpdatedAt = now if _, err := tx.ExecContext(ctx, ` INSERT INTO skills(id, skill_key, name, description, source_type, content_markdown, status, version, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, item.ID, item.SkillKey, item.Name, item.Description, item.SourceType, item.ContentMarkdown, item.Status, item.Version, item.CreatedAt, item.UpdatedAt, ); err != nil { return fmt.Errorf("insert builtin skill %q: %w", item.SkillKey, err) } } for _, roleName := range seed.RoleNames { if err := seedBuiltinRoleSkillBindingTx(ctx, tx, now, roleName, item.ID, item.SkillKey); err != nil { return err } } return nil } func seedBuiltinRoleSkillBindingTx(ctx context.Context, tx *sql.Tx, now, roleName, skillID, skillKey string) error { if _, err := getRoleSkillBindingTx(ctx, tx, roleName, "", skillID); err == nil { return nil } else if !errors.Is(err, sql.ErrNoRows) { return err } if _, err := tx.ExecContext(ctx, ` INSERT INTO role_skill_bindings(id, role_name, workspace_id, skill_id, is_enabled, sort_order, config_json, version, updated_by, created_at, updated_at) VALUES(?, ?, NULL, ?, 1, 100, ?, 1, 'builtin-seed', ?, ?) `, "builtin-role-skill-binding-"+roleName+"-"+skillKey, roleName, skillID, `{"category":"capability"}`, now, now, ); err != nil { return fmt.Errorf("insert builtin role skill binding %q/%q: %w", roleName, skillKey, err) } return nil }