mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-09 18:08:31 -10:00
Update the Go module path, imports, install scripts, docs, workflows, packaging and action defaults from Gitlawb/zero to Twigpine/zero. The npm scope (already @twigpine/zero on main, with its @gitlawb/zero migration notes), LICENSE, action author, the OpenGateway provider and historical CHANGELOG links are unchanged. The X-AIMLAPI-Integration-Repo header keeps Gitlawb/zero: it is what AIMLAPI sees for attribution, so changing it needs a maintainer's yes. docs/INSTALL.md keeps `go install github.com/Gitlawb/zero/...`, because every tagged release so far declares that module path. The Homebrew formula comment names the Twigpine/homebrew-tap tap. Co-authored-by: Claude Opus 5.5 <[email protected]>
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Twigpine/zero/internal/agent"
|
|
"github.com/Twigpine/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)
|
|
}
|
|
})
|
|
}
|
|
}
|