feat: наблюдение за живой opencode-сессией (/status N показывает что делает агент)
All checks were successful
CI / test (push) Successful in 54s
CI / build-and-package (amd64, linux) (push) Successful in 35s
CI / build-and-package (amd64, windows) (push) Successful in 43s

LiveRegistry собирает live-шаги из stdout процесса opencode по taskID
(через контекст, без изменения интерфейса Runner). /status N для running-задачи
прикладывает живое состояние: активный субагент, давность последнего шага,
последние text-шаги агента. Ничего не перезапускается и не убивается —
только наблюдение по запросу.
This commit is contained in:
Hermes
2026-08-17 08:05:15 +05:00
parent 2255917949
commit da775ac227
9 changed files with 329 additions and 26 deletions

46
internal/app/live.go Normal file
View File

@@ -0,0 +1,46 @@
package app
import (
"fmt"
"strings"
"time"
"github.com/kamelion/ratatoskr-go/internal/opencode"
)
// liveProbe адаптирует *opencode.LiveRegistry под интерфейс core.LiveProber.
// Формирует короткое человекочитаемое описание живой сессии задачи.
type liveProbe struct {
reg *opencode.LiveRegistry
}
func (p *liveProbe) Probe(taskID int64) (string, bool) {
if p == nil || p.reg == nil {
return "", false
}
snap, ok := p.reg.Snap(taskID)
if !ok {
return "", false
}
var b strings.Builder
b.WriteString(fmt.Sprintf("Агент **%s** активен", snap.Agent))
if idl := time.Since(snap.Last).Round(time.Second); idl > 0 {
b.WriteString(fmt.Sprintf(" (последний шаг %s назад)", idl))
}
// последние text-шаги — что агент делает прямо сейчас
n := 0
for i := len(snap.Steps) - 1; i >= 0 && n < 3; i-- {
st := snap.Steps[i]
if st.Type != "text" || strings.TrimSpace(st.Text) == "" {
continue
}
text := strings.TrimSpace(st.Text)
if len(text) > 140 {
text = text[:140] + "…"
}
text = strings.ReplaceAll(text, "\n", " ")
b.WriteString("\n▸ " + text)
n++
}
return b.String(), true
}