feat: наблюдение за живой opencode-сессией (/status N показывает что делает агент)
LiveRegistry собирает live-шаги из stdout процесса opencode по taskID (через контекст, без изменения интерфейса Runner). /status N для running-задачи прикладывает живое состояние: активный субагент, давность последнего шага, последние text-шаги агента. Ничего не перезапускается и не убивается — только наблюдение по запросу.
This commit is contained in:
@@ -10,11 +10,20 @@ import (
|
||||
|
||||
// Core — ядро Ratatoskr: машина состояний поверх storage.
|
||||
type Core struct {
|
||||
Store *storage.Storage
|
||||
Decide Decider
|
||||
MaxTurns int // D3
|
||||
MaxConfirmCycles int // D4
|
||||
// Store — хранилище задач и трасс.
|
||||
Store *storage.Storage
|
||||
Decide Decider
|
||||
MaxTurns int // D3
|
||||
MaxConfirmCycles int // D4
|
||||
MaxQuestionsPerTurn int
|
||||
// Live — опциональный просмотр живой сессии задачи (для /status N).
|
||||
Live LiveProber
|
||||
}
|
||||
|
||||
// LiveProber — абстракция за журналом живых сессий (реализация — *opencode.LiveRegistry).
|
||||
// Возвращает текстовое описание текущего состояния сессии задачи и ok=была ли активна.
|
||||
type LiveProber interface {
|
||||
Probe(taskID int64) (description string, ok bool)
|
||||
}
|
||||
|
||||
// New создаёт Core с дефолтными лимитами.
|
||||
@@ -85,7 +94,7 @@ func (c *Core) handleCommand(ctx context.Context, taskID int64, text string) (Re
|
||||
return c.handleContinue(ctx, rest)
|
||||
default:
|
||||
return Result{
|
||||
Reply: "Неизвестная команда. Доступно: /start /cancel /skip /retry N /status N",
|
||||
Reply: "Неизвестная команда. Доступно: /start /cancel /skip /retry N /status N",
|
||||
Action: "send",
|
||||
TaskID: taskID,
|
||||
}, nil
|
||||
@@ -171,7 +180,7 @@ func (c *Core) handleRetry(ctx context.Context, rest string) (Result, error) {
|
||||
id, ok := parseTaskID(rest)
|
||||
if !ok {
|
||||
return Result{
|
||||
Reply: "Укажите номер задачи: `/retry 5`.",
|
||||
Reply: "Укажите номер задачи: `/retry 5`.",
|
||||
Action: "send",
|
||||
}, nil
|
||||
}
|
||||
@@ -199,7 +208,7 @@ func (c *Core) handleStatus(ctx context.Context, rest string) (Result, error) {
|
||||
id, ok := parseTaskID(rest)
|
||||
if !ok {
|
||||
return Result{
|
||||
Reply: "Укажите номер задачи: `/status 5`.",
|
||||
Reply: "Укажите номер задачи: `/status 5`.",
|
||||
Action: "send",
|
||||
}, nil
|
||||
}
|
||||
@@ -207,8 +216,15 @@ func (c *Core) handleStatus(ctx context.Context, rest string) (Result, error) {
|
||||
if err != nil {
|
||||
return c.notFoundReply(ctx, id, err)
|
||||
}
|
||||
reply := "Задача #" + itoa(id) + ": " + string(task.Status)
|
||||
// Для выполняемой задачи прикладываем живое состояние сессии (если настроено).
|
||||
if task.Status == storage.StatusRunning && c.Live != nil {
|
||||
if desc, ok := c.Live.Probe(id); ok && desc != "" {
|
||||
reply += "\n" + desc
|
||||
}
|
||||
}
|
||||
return Result{
|
||||
Reply: "Задача #" + itoa(id) + ": " + string(task.Status),
|
||||
Reply: reply,
|
||||
Action: "send",
|
||||
TaskID: id,
|
||||
Status: task.Status,
|
||||
@@ -220,7 +236,7 @@ func (c *Core) handleContinue(ctx context.Context, rest string) (Result, error)
|
||||
id, ok := parseTaskID(rest)
|
||||
if !ok {
|
||||
return Result{
|
||||
Reply: "Укажите номер задачи: `/continue 5`.",
|
||||
Reply: "Укажите номер задачи: `/continue 5`.",
|
||||
Action: "send",
|
||||
}, nil
|
||||
}
|
||||
@@ -229,7 +245,7 @@ func (c *Core) handleContinue(ctx context.Context, rest string) (Result, error)
|
||||
return c.notFoundReply(ctx, id, err)
|
||||
}
|
||||
return Result{
|
||||
Reply: "Задача #" + itoa(id) + " в статусе " + string(task.Status) +
|
||||
Reply: "Задача #" + itoa(id) + " в статусе " + string(task.Status) +
|
||||
". Резюм сессии — пока не реализован.",
|
||||
Action: "send",
|
||||
TaskID: id,
|
||||
@@ -449,4 +465,4 @@ func itoa(n int64) string {
|
||||
buf[i] = '-'
|
||||
}
|
||||
return string(buf[i:])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user