mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 05:01:28 -10:00
* perf(tui): disable duplicate composer cursor blink * perf(tui): stop composer wakeups after idle * refactor(tools): publish immutable registry snapshots * perf(startup): defer optional tool discovery * perf(provider): reuse compatible response sessions * feat(provider): add native patch tool input * fix(provider): recognize saved chat sessions * perf(tui): render only on screen changes * fix(runtime): address performance review findings * fix(usage): expose cache efficiency details * perf(agent): reduce redundant bounded-task turns * fix: harden runtime review edge cases * fix: address runtime review findings * build: restore official Bubble Tea dependency * fix(cli): redact MCP registration errors * fix: harden turn session regressions * fix: keep run tools and patch roots consistent * fix: bind freeform patch writes to scoped roots * test: keep patch fixtures out of worktree * fix(tools): reject mixed-root freeform patches
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Gitlawb/zero/internal/agent"
|
|
"github.com/Gitlawb/zero/internal/tools"
|
|
)
|
|
|
|
// escalate_model is a control-only tool (SideEffectNone). The stream-json tool
|
|
// call must report sideEffect "none", not "unknown", so automation sees the
|
|
// promised value.
|
|
func TestStreamJSONSideEffectReportsNoneForControlTool(t *testing.T) {
|
|
registry := tools.NewRegistry()
|
|
registry.Register(tools.NewEscalateModelTool())
|
|
if got := streamJSONSideEffect("escalate_model", registry); got != "none" {
|
|
t.Fatalf("streamJSONSideEffect(escalate_model) = %q, want none", got)
|
|
}
|
|
}
|
|
|
|
func TestExecWriterPropagatesToolResultTruncation(t *testing.T) {
|
|
for _, format := range []execOutputFormat{execOutputJSON, execOutputStreamJSON} {
|
|
t.Run(string(format), func(t *testing.T) {
|
|
var stdout, stderr bytes.Buffer
|
|
writer := execEventWriter{
|
|
stdout: &stdout,
|
|
stderr: &stderr,
|
|
format: format,
|
|
runID: "run_budget",
|
|
streamedText: &strings.Builder{},
|
|
}
|
|
writer.toolResult(agent.ToolResult{
|
|
ToolCallID: "call_budget",
|
|
Name: "read_file",
|
|
Status: tools.StatusOK,
|
|
Output: "bounded output",
|
|
Truncated: true,
|
|
})
|
|
if writer.err != nil {
|
|
t.Fatalf("toolResult: %v", writer.err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(bytes.TrimSpace(stdout.Bytes()), &payload); err != nil {
|
|
t.Fatalf("decode output %q: %v", stdout.String(), err)
|
|
}
|
|
if payload["truncated"] != true {
|
|
t.Fatalf("truncated = %#v, want true; payload=%#v", payload["truncated"], payload)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExecWriterPropagatesCacheUsage(t *testing.T) {
|
|
for _, format := range []execOutputFormat{execOutputJSON, execOutputStreamJSON} {
|
|
t.Run(string(format), func(t *testing.T) {
|
|
var stdout bytes.Buffer
|
|
writer := execEventWriter{
|
|
stdout: &stdout,
|
|
format: format,
|
|
runID: "run_usage",
|
|
streamedText: &strings.Builder{},
|
|
}
|
|
writer.usage(agent.Usage{
|
|
InputTokens: 100,
|
|
OutputTokens: 20,
|
|
PromptTokens: 100,
|
|
CompletionTokens: 20,
|
|
CachedInputTokens: 60,
|
|
CacheWriteTokens: 10,
|
|
})
|
|
if writer.err != nil {
|
|
t.Fatalf("usage: %v", writer.err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal(bytes.TrimSpace(stdout.Bytes()), &payload); err != nil {
|
|
t.Fatalf("decode output %q: %v", stdout.String(), err)
|
|
}
|
|
cachedKey, writeKey := "cached_input_tokens", "cache_write_tokens"
|
|
if format == execOutputStreamJSON {
|
|
cachedKey, writeKey = "cachedInputTokens", "cacheWriteTokens"
|
|
}
|
|
if payload[cachedKey] != float64(60) || payload[writeKey] != float64(10) {
|
|
t.Fatalf("cache usage missing from payload: %#v", payload)
|
|
}
|
|
})
|
|
}
|
|
}
|