ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci / checks (push) Successful in 7m55s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 11m8s
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / release (push) Successful in 8m55s
The Agent panel counts the session's context. A gauge in the header shows how full the model's window is, from the CLI's usage on each answer and the window it reports; it turns yellow past 80 % and near compaction. Its popup lays the window out as one bar and a row per part: messages, scene, code, docs, project and schema, tool results, the editor's instructions and tool definitions, the CLI's own prompt, the compaction buffer and free room. The parts are the editor's estimate of what it sent, scaled to the usage; on a recorded session the estimate stays within 15 % of the CLI's count. A part opens to its contributions with Drop and Pin. The CLI cannot edit its history, so Drop starts the next message in a new session with a shortened history: messages in full, results as summaries, pinned ones in full, dropped ones left out. New Session with Summary asks the agent for a summary and sends it, with the names of the changed nodes, with the next message. The open scene and the selection can go with every message. A compaction shows in the transcript. Read tools keep large reads small: scene_tree up to a number of nodes and levels with the rest counted and a node argument for a part, docs_search up to a number of results, script_read in parts for a long script. The limits are in the popup and in prefs.aetext. MCP results are compact JSON. scene_run through the editor answers ok when the game ran.
104 lines
2.1 KiB
Go
104 lines
2.1 KiB
Go
package backend
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
// Step is one move of a scripted turn: text, or a tool called through the MCP server.
|
|
type Step struct {
|
|
Text string
|
|
Tool string
|
|
Args map[string]any
|
|
}
|
|
|
|
// Fake plays scripted turns, one per Send, for tests; a new conversation goes on with the next turn, and Sent keeps every message.
|
|
type Fake struct {
|
|
Turns [][]Step
|
|
mu sync.Mutex
|
|
n int
|
|
sent []string
|
|
}
|
|
|
|
// Sent is the messages the conversations got, in order.
|
|
func (f *Fake) Sent() []string {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return append([]string(nil), f.sent...)
|
|
}
|
|
|
|
func (f *Fake) Name() string {
|
|
return "fake"
|
|
}
|
|
|
|
func (f *Fake) Available(Config) (string, error) {
|
|
return "fake", nil
|
|
}
|
|
|
|
func (f *Fake) Start(cfg Config) (Conversation, error) {
|
|
return &fakeConv{f: f, client: NewClient(cfg.MCPURL, cfg.Token), events: make(chan Event, 64), stop: make(chan struct{})}, nil
|
|
}
|
|
|
|
type fakeConv struct {
|
|
f *Fake
|
|
once sync.Once
|
|
client *Client
|
|
events chan Event
|
|
stop chan struct{}
|
|
}
|
|
|
|
func (c *fakeConv) Send(text string) error {
|
|
f := c.f
|
|
f.mu.Lock()
|
|
f.sent = append(f.sent, text)
|
|
if f.n >= len(f.Turns) {
|
|
f.mu.Unlock()
|
|
return errors.New("the fake backend has no more turns")
|
|
}
|
|
turn := f.Turns[f.n]
|
|
f.n++
|
|
f.mu.Unlock()
|
|
go c.play(turn)
|
|
return nil
|
|
}
|
|
|
|
func (c *fakeConv) play(turn []Step) {
|
|
for i, st := range turn {
|
|
select {
|
|
case <-c.stop:
|
|
c.events <- Event{Kind: EvTurnEnd}
|
|
return
|
|
default:
|
|
}
|
|
if st.Tool == "" {
|
|
c.events <- Event{Kind: EvText, Text: st.Text}
|
|
continue
|
|
}
|
|
id := strconv.Itoa(i + 1)
|
|
args, _ := json.Marshal(st.Args)
|
|
c.events <- Event{Kind: EvToolStart, Tool: st.Tool, CallID: id, Args: args}
|
|
text, failed, err := c.client.Call(st.Tool, st.Args)
|
|
if err != nil {
|
|
c.events <- Event{Kind: EvError, Err: err}
|
|
return
|
|
}
|
|
c.events <- Event{Kind: EvToolResult, Tool: st.Tool, CallID: id, Result: text, Failed: failed}
|
|
}
|
|
c.events <- Event{Kind: EvTurnEnd}
|
|
}
|
|
|
|
func (c *fakeConv) Events() <-chan Event {
|
|
return c.events
|
|
}
|
|
|
|
func (c *fakeConv) Cancel() {
|
|
c.once.Do(func() { close(c.stop) })
|
|
}
|
|
|
|
func (c *fakeConv) Close() error {
|
|
c.Cancel()
|
|
return nil
|
|
}
|