Files
aego/app/agentui/backend/replay.go
sanspie 70be14646a
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 9m1s
ci / release (push) Successful in 11m0s
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 6m39s
Stage 11b5b: follow mode and agent presence (D343 amended)
Follow mode leads the editor after the agent: each tool call opens the scene, selects the node in a second selection that Delete and Copy never read, frames it smoothly and shows it in the Inspector at the component; scripts open at the line without taking the keyboard, with the lines of a waiting card as ghost lines and changed lines marked in the gutter; runs and checks bring the Console or Problems forward with new entries lit. Changed fields light for two seconds, new nodes and components light whole, removed nodes fade where they were.

The agent's avatar marks where it works in the Hierarchy, the Scene View and the Inspector; the Agent panel says where it is, with a timeline of its steps to go back to them or undo to one. Follow is the eye in the header, Window → Follow the Agent or Ctrl+Alt+F, kept in prefs.aetext; the user's own input pauses it until Resume or 30 idle seconds. A user edit of a field a waiting card changes is made again after the card. During Play the agent edits the running game's scene, so its changes show in Play changes.

editor/core/presence holds who works where; ui/widgets/presence draws it. backend.Replay plays a recorded stream-json session against the editor's MCP server for the tests.
2026-10-26 00:52:59 +03:00

71 lines
1.8 KiB
Go

package backend
import (
"bytes"
"encoding/json"
)
// Replay plays a recorded Claude Code stream-json session, one turn per Send: its text and tool calls go through the same decoder as a live session, and the calls run against this editor's MCP server, so the results are this project's (11b5b).
type Replay struct {
// Turns are the stream-json lines of each turn, as claude -p --output-format stream-json writes them.
Turns [][]byte
}
func (r *Replay) Name() string {
return "replay"
}
func (r *Replay) Available(Config) (string, error) {
return "replay", nil
}
// ReplayTurns splits a recorded session into turns at its result lines.
func ReplayTurns(session []byte) [][]byte {
var out [][]byte
var cur bytes.Buffer
for _, line := range bytes.Split(session, []byte("\n")) {
if len(bytes.TrimSpace(line)) == 0 {
continue
}
cur.Write(line)
cur.WriteByte('\n')
var head struct {
Type string `json:"type"`
}
if json.Unmarshal(line, &head) == nil && head.Type == "result" {
out = append(out, append([]byte(nil), cur.Bytes()...))
cur.Reset()
}
}
if cur.Len() > 0 {
out = append(out, cur.Bytes())
}
return out
}
func (r *Replay) Start(cfg Config) (Conversation, error) {
var turns [][]Step
for _, t := range r.Turns {
turns = append(turns, replaySteps(t))
}
return (&Fake{Turns: turns}).Start(cfg)
}
// replaySteps decodes the lines of a turn and keeps its text and tool calls; the recorded results are dropped, the calls run again.
func replaySteps(turn []byte) []Step {
var steps []Step
for _, line := range bytes.Split(turn, []byte("\n")) {
for _, e := range Decode(line) {
switch e.Kind {
case EvText:
steps = append(steps, Step{Text: e.Text})
case EvToolStart:
var args map[string]any
_ = json.Unmarshal(e.Args, &args)
steps = append(steps, Step{Tool: e.Tool, Args: args})
}
}
}
return steps
}