Files
Agent-Native/runtime/r2_regression_test.go
T
weichenandCursor 17f2a4ecfa Close the native P1 gaps the audit listed for this batch
Why: token budget let the current tool batch run, compaction could split a tool pair or race an append, writes were not crash-atomic, and several MCP and session edges only existed as comments.
Evidence: GOWORK=off go test -count=1 -race ./runtime/ ./runtime/llm/ ./runtime/session/ ./runtime/tools/ ./runtime/mcp/ ./adapters/native/ ./adapters/intentgate/ passed, including the TestR2* cases for budget, replacement, cancel, compaction, SSE, UTF-8, schema, MCP retry, session terminate, poison reload, and registry reload.
Impact: over-budget tool calls are skipped and paired; Stop.Replacement is appended to the session; the intent gate can follow Registry.Reload.
Refs: N5, N6, N7, N8, N9, N11, N12, N15, N16, N17, N18, N19, N21, I9
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 13:05:58 +00:00

131 lines
3.4 KiB
Go

package runtime
import (
"context"
"encoding/json"
"path/filepath"
"strings"
"testing"
"agent-native/runtime/llm"
"agent-native/runtime/session"
"agent-native/runtime/tools"
)
type countingTool struct {
name string
calls int
hook func()
}
func (c *countingTool) Name() string { return c.name }
func (c *countingTool) Description() string { return c.name }
func (c *countingTool) Parameters() json.RawMessage {
return json.RawMessage(`{"type":"object","properties":{}}`)
}
func (c *countingTool) Execute(ctx context.Context, args json.RawMessage) (tools.Result, error) {
c.calls++
if c.hook != nil {
c.hook()
}
return tools.Result{Content: "ok"}, nil
}
func TestR2TokenBudgetSkipsTools(t *testing.T) {
tool := &countingTool{name: "list_dir"}
reg := tools.NewRegistry(t.TempDir())
reg.Register(tool)
res, err := Run(context.Background(), Config{
Model: "fake",
Tools: reg,
Budget: &Budget{MaxPromptTokens: 10},
Stream: usageScript(llm.Usage{PromptTokens: 500},
llm.Message{Role: llm.RoleAssistant, StopReason: llm.StopToolCalls, ToolCalls: []llm.ToolCall{{
ID: "c1", Name: "list_dir", Arguments: `{}`,
}}},
),
}, llm.Message{Role: llm.RoleUser, Content: "go"})
if err != nil {
t.Fatal(err)
}
if tool.calls != 0 {
t.Fatalf("tool ran after token budget: %d", tool.calls)
}
if res.Err == nil || !strings.Contains(res.Err.Error(), "prompt tokens") {
t.Fatalf("budget error: %v", res.Err)
}
}
func TestR2StopReplacementIsPersisted(t *testing.T) {
store, err := session.Open(filepath.Join(t.TempDir(), "t.jsonl"))
if err != nil {
t.Fatal(err)
}
_, err = Run(context.Background(), Config{
Model: "fake",
MaxTurns: 3,
Session: store,
Stream: script(
llm.Message{Role: llm.RoleAssistant, Content: "draft", StopReason: llm.StopEnd},
llm.Message{Role: llm.RoleAssistant, Content: "final", StopReason: llm.StopEnd},
),
Stop: func(last llm.Message, info StopInfo) StopDecision {
if info.Continuations == 0 {
return StopDecision{Continue: true, Replacement: "corrected-claim"}
}
return StopDecision{}
},
}, llm.Message{Role: llm.RoleUser, Content: "go"})
if err != nil {
t.Fatal(err)
}
msgs, err := store.Load()
if err != nil {
t.Fatal(err)
}
var saw bool
for _, m := range msgs {
if m.Content == "corrected-claim" {
saw = true
}
}
if !saw {
t.Fatalf("replacement missing from session: %+v", msgs)
}
}
func TestR2SequentialToolsStopAfterCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
first := &countingTool{name: "first", hook: cancel}
second := &countingTool{name: "second"}
reg := tools.NewRegistry(t.TempDir())
reg.Register(first)
reg.Register(second)
res, err := Run(ctx, Config{
Model: "fake",
Tools: reg,
Stream: script(
llm.Message{Role: llm.RoleAssistant, StopReason: llm.StopToolCalls, ToolCalls: []llm.ToolCall{
{ID: "1", Name: "first", Arguments: `{}`},
{ID: "2", Name: "second", Arguments: `{}`},
}},
llm.Message{Role: llm.RoleAssistant, Content: "after", StopReason: llm.StopEnd},
),
}, llm.Message{Role: llm.RoleUser, Content: "go"})
if err != nil {
t.Fatal(err)
}
if first.calls != 1 || second.calls != 0 {
t.Fatalf("calls first=%d second=%d", first.calls, second.calls)
}
var paired int
for _, m := range res.Messages {
if m.Role == llm.RoleTool {
paired++
}
}
if paired != 2 {
t.Fatalf("tool results = %d, want a pair for each call", paired)
}
}