Сабагенты

Creo поддерживает параллельную многоагентную оркестрацию. Основной агент (parent) порождает независимых под-агентов (children) со своими ролями, моделями и инструментами, координируя их через шину событий.

Единая точка входа. Spawn'ит одного или нескольких детей, сразу возвращает список.

agent(agents: [
  {role: "reviewer", prompt: "...", model: "mini"},
  {role: "tester", prompt: "...", tools: ["read","grep","terminal"]}
])
→ [{child_id: "c1", role: "reviewer", status: "running"},
   {child_id: "c2", role: "tester", status: "running"}]

Каждый child:

  1. Создаётся через session.Manager.CreateChild (как раньше)
  2. Записывается в таблицу multiagent_children (task_id, role, model, status)
  3. Получает MultiAgentStartEvent в шину → listener запускает agent loop

Parent вызывает agent_manage для ожидания событий от детей и отправки сообщений.

Без messages — блокируется пока кто-то из детей не закончит turn:

agent_manage()
→ [{child_id: "c1", role: "explorer", status: "running"},
   {child_id: "c2", role: "reviewer", status: "idle", message: "Мне нужно уточнение..."},
   {child_id: "c3", role: "developer", status: "running"}]

С messages — отправляет указанным детям user-сообщения, затем блокируется:

agent_manage(messages: {"developer": "Для тебя вопрос от Reviewer: ABCD"})

FSM шаги: RESUME (crash recovery) → MESSAGES → CHECK (быстрый возврат если есть idle) → BLOCK (подписка на MultiAgentChildIdleEvent) → COLLECT.

agent_abort(abort: ["reviewer", "c3"])

Публикует AbortRequestEvent для указанных детей, ставит status=done.

Топик: multiagent.start

Издатель: agent tool, agent_manage (для messages и resume)

Подписчик: StartMultiAgentListenerrunMultiAgentChild

1type MultiAgentStartEvent struct {
2    ChildSessionID  string
3    ParentSessionID string
4    TaskID          string
5    Role            string
6    Model           string
7    TraceID         string
8    Message         string // initial prompt или resume message
9}

Топик: multiagent.child_idle

Издатель: runMultiAgentChild (после возврата Agent.Run)

Подписчик: agent_manage (блокируется в ожидании)

1type MultiAgentChildIdleEvent struct {
2    ChildSessionID  string
3    ParentSessionID string
4    TaskID          string
5}

StartMultiAgentListener в internal/agent/multiagent.go — подписывается на TopicMultiAgentStart и запускает каждого child в отдельной goroutine.

Паттерн: как Dream listener (internal/dream/listener.go).

StartMultiAgentListener
  └── for ev := range Subscribe[MultiAgentStartEvent]:
        go runMultiAgentChild(ev)
          ├── store.SetStatus(childID, running)
          ├── sm.Open(childSessionID)
          ├── LoadRole(globalDir, cwd, role)
          ├── resolveModelName(cfg, model)
          ├── llm.NewFromModel(modelCfg)
          ├── buildMultiAgentRegistry (BuildRegistry - agent - agent_manage - agent_abort)
          ├── GenerateSystemMessage + role.Prompt
          ├── agent.New(...).WithAutoApprove().WithCheckpoints(...)
          ├── relayChildStream / relayChildToolCalls / relayChildApproval (3 goroutine)
          ├── agent.Run(ctx, childID, "", userMsg)
          ├── store.SetStatus(childID, idle)
          └── bus.Publish(MultiAgentChildIdleEvent{...})

Те же 3 relay-горутины что и в старом SubagentRunner — пересылают события child-сессии на parentSessionID для TUI:

graph LR
    subgraph Child Session
        CT[StreamingToken]
        CTC[ToolCall]
        CA2[ApprovalRequest]
    end

    R1[relayChildStream] --> SSE[SubagentStreamEvent]
    R2[relayChildToolCalls] --> SSE
    R3[relayChildApproval] --> PA2[ApprovalRequest<br/>с parentSessionID<br/>и subagentSessionID]

    CT --> R1
    CTC --> R2
    CA2 --> R3

JSON-файлы в директориях ролей (например reviewer.json). Поиск ведётся в трёх местах в порядке приоритета — project перебивает global при совпадении имени:

  1. ./agents/ — project (высший приоритет)
  2. ./.creo/agents/ — project local
  3. ~/.config/creo/agents/ — global
1{
2  "prompt": "Ты — ревьюер кода. Анализируй изменения...",
3  "model": "mini",
4  "skills": ["code-review-skill"],
5  "tools": ["read", "grep", "glob"]
6}

Содержимое prompt добавляется к system prompt child'а. Поле model разрешается как "default" / "mini" / имя модели. Поле tools задаёт инструменты для авто-разрешения.

Состояние в таблице multiagent_children:

 1CREATE TABLE multiagent_children (
 2    child_id     TEXT PRIMARY KEY,
 3    task_id      TEXT NOT NULL,
 4    parent_id    TEXT NOT NULL,
 5    role         TEXT NOT NULL,
 6    model        TEXT,
 7    status       TEXT NOT NULL DEFAULT 'running',
 8    trace_id     TEXT NOT NULL,
 9    created_at   INTEGER NOT NULL,
10    completed_at INTEGER
11);

Crash recovery: agent_manage проверяет session.running flag. Если status=running но running=0 (процесс убит) → публикует MultiAgentStartEvent для resume.

После перезапуска Creo: listener стартует автоматически. При «продолжи» parent вызывает agent_manage → RESUME шаг → незавершённые children перезапускаются.

Максимум 1 уровень. Child-сессия не получает инструменты agent, agent_manage, agent_abort в своём registry — защищает от рекурсии.