chore(repo): reinitialize repository
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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=<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/<topic_id>/messages --data '{
|
||||
"workspace_id": "<workspace_id>",
|
||||
"from_role_name": "<current_role>",
|
||||
"to_expr": "<target_role>",
|
||||
"type": "chat",
|
||||
"stage": "execution",
|
||||
"body_markdown": "<message 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/<topic_id>/messages --data '{
|
||||
"workspace_id": "<workspace_id>",
|
||||
"from_role_name": "<current_role>",
|
||||
"to_expr": "<target_role>",
|
||||
"type": "question",
|
||||
"stage": "<current_stage>",
|
||||
"body_markdown": "<single concrete question and the decision you need>"
|
||||
}'
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user