feat(events): внедрить Publisher в Core/Worker/Analyst + шины в app

- Core: setStatus публикует TaskStatusChanged на каждом переходе
  (handleStart/Cancel/Retry/Consent/Edit/Turn, runDecide).
- Worker: setStatus публикует TaskStatusChanged (running/timeout/failed/
  success, failTask), notify остаётся авто-уведомлением.
- Analyst: Decide публикует AgentActivity (stage=decide).
- app: создаёт доменную (256) и логовую (1024) шины, подключает их к
  Core/Worker/Analyst, редиректит log в обе (stderr + логовая шина).
This commit is contained in:
ki.sagidullin
2026-08-20 04:30:29 +05:00
parent 1f7ab9a67e
commit 4957a5552e
4 changed files with 102 additions and 36 deletions

View File

@@ -8,6 +8,7 @@ import (
"strings"
"github.com/kamelion/ratatoskr-go/internal/core"
"github.com/kamelion/ratatoskr-go/internal/events"
"github.com/kamelion/ratatoskr-go/internal/opencode"
"github.com/kamelion/ratatoskr-go/internal/storage"
)
@@ -35,6 +36,16 @@ type Analyst struct {
Runner OpenCodeRunner
Worktree string // каталог, откуда запускать opencode run
Agent string // имя агента (default "analyst")
// Events — издатель доменных событий для UI. nil — события выключены.
Events events.Publisher
}
// publish отправляет доменное событие, если задан издатель.
func (a *Analyst) publish(e events.Event) {
if a.Events != nil {
a.Events.Publish(e)
}
}
// AnalystResponse — структура JSON-ответа аналитика.
@@ -58,6 +69,8 @@ func (a *Analyst) Decide(ctx context.Context, history []core.Message, draft stor
agent = "analyst"
}
a.publish(events.AgentActivity{TaskID: draft.ID, Agent: agent, Stage: "decide"})
if len(history) == 0 && !force {
return core.Decision{}, fmt.Errorf("%w: пустая история диалога", ErrNotReady)
}