2026-10-18 19:21:26 +09:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2026-10-22 22:05:33 +09:00
|
|
|
"encoding/json"
|
2026-10-18 19:21:26 +09:00
|
|
|
"math"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestIntentProvenance(t *testing.T) {
|
2026-10-22 22:05:33 +09:00
|
|
|
for _, status := range []goalStatus{active, paused, "complete", "budgetLimited", "usageLimited", blocked, "cleared"} {
|
|
|
|
|
got, ok, err := (&intentLog{}).parse(intentLine(status))
|
2026-10-18 19:21:26 +09:00
|
|
|
if err != nil || !ok || got != status {
|
|
|
|
|
t.Fatalf("status=%s got=%s match=%v error=%v", status, got, ok, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
line := string(intentLine(active))
|
|
|
|
|
for _, text := range []string{
|
|
|
|
|
strings.Replace(line, "user.goal", "user.input", 1),
|
|
|
|
|
strings.Replace(line, "\"role\":\"user\"", "\"role\":\"assistant\"", 1),
|
|
|
|
|
strings.Replace(line, "input_text", "output_text", 1),
|
|
|
|
|
} {
|
2026-10-22 22:05:33 +09:00
|
|
|
if _, ok, err := (&intentLog{}).parse([]byte(text)); err != nil || ok {
|
2026-10-18 19:21:26 +09:00
|
|
|
t.Fatalf("accepted untrusted instruction: match=%v error=%v", ok, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestIntentTail(t *testing.T) {
|
|
|
|
|
path := filepath.Join(t.TempDir(), "rollout.jsonl")
|
|
|
|
|
if err := os.WriteFile(path, intentLine(active), 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
log, err := openIntent(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatal("replayed historical authorization")
|
|
|
|
|
}
|
|
|
|
|
data := intentLine(paused)
|
|
|
|
|
appendIntent(t, path, data[:len(data)/2])
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatal("accepted partial record")
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, data[len(data)/2:])
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != paused {
|
|
|
|
|
t.Fatalf("partial completion: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, intentLine(active))
|
|
|
|
|
if err := log.seal(); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatal("replayed pre-cancellation authorization")
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, intentLine(active))
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != active {
|
|
|
|
|
t.Fatalf("new authorization: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestIntentFence(t *testing.T) {
|
|
|
|
|
path := filepath.Join(t.TempDir(), "rollout.jsonl")
|
|
|
|
|
if err := os.WriteFile(path, nil, 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
log, err := openIntent(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
data := intentLine(active)
|
|
|
|
|
appendIntent(t, path, data[:len(data)/2])
|
|
|
|
|
if err := log.seal(); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, data[len(data)/2:])
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatalf("replayed instruction spanning cancellation: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, intentLine(active))
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != active {
|
|
|
|
|
t.Fatalf("new instruction after cancellation: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestIntentCut(t *testing.T) {
|
|
|
|
|
path := filepath.Join(t.TempDir(), "rollout.jsonl")
|
|
|
|
|
if err := os.WriteFile(path, nil, 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
log, err := openIntent(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, intentLine(paused))
|
|
|
|
|
end, err := log.end()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, intentLine(active))
|
|
|
|
|
if status, err := log.read(end); err != nil || status != paused {
|
|
|
|
|
t.Fatalf("read beyond captured input: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != active {
|
|
|
|
|
t.Fatalf("lost later input: %s %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-10-22 22:05:33 +09:00
|
|
|
|
|
|
|
|
func agentCall(id, name, namespace string, status goalStatus) []byte {
|
|
|
|
|
return append(mustJSON(map[string]any{"type": "response_item", "payload": map[string]any{
|
|
|
|
|
"type": "function_call", "name": name, "namespace": namespace, "call_id": id,
|
|
|
|
|
"arguments": string(mustJSON(struct {
|
|
|
|
|
Status goalStatus `json:"status"`
|
|
|
|
|
}{status})),
|
|
|
|
|
}}), '\n')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func agentReply(id, output string) []byte {
|
|
|
|
|
return append(mustJSON(map[string]any{"type": "response_item", "payload": map[string]any{
|
|
|
|
|
"type": "function_call_output", "call_id": id, "output": output,
|
|
|
|
|
}}), '\n')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAgentIntent(t *testing.T) {
|
|
|
|
|
blockedReply := string(mustJSON(goalResult{Goal: testGoal(blocked)}))
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
name, tool, namespace string
|
|
|
|
|
status goalStatus
|
|
|
|
|
id, replyID, output string
|
|
|
|
|
want goalStatus
|
|
|
|
|
}{
|
|
|
|
|
{"plain", "update_goal", "", blocked, "call-1", "call-1", blockedReply, blocked},
|
|
|
|
|
{"functions", "update_goal", "functions", blocked, "call-1", "call-1", blockedReply, blocked},
|
|
|
|
|
{"external", "update_goal", "plugin", blocked, "call-1", "call-1", blockedReply, ""},
|
|
|
|
|
{"read", "get_goal", "", blocked, "call-1", "call-1", blockedReply, ""},
|
|
|
|
|
{"other tool", "other", "", blocked, "call-1", "call-1", blockedReply, ""},
|
|
|
|
|
{"other status", "update_goal", "", paused, "call-1", "call-1", blockedReply, ""},
|
|
|
|
|
{"no id", "update_goal", "", blocked, "", "", blockedReply, ""},
|
|
|
|
|
{"other id", "update_goal", "", blocked, "call-1", "call-2", blockedReply, ""},
|
|
|
|
|
{"rejected", "update_goal", "", blocked, "call-1", "call-1", "failed to update goal", ""},
|
|
|
|
|
{"no goal", "update_goal", "", blocked, "call-1", "call-1", string(mustJSON(goalResult{})), ""},
|
|
|
|
|
{"not blocked", "update_goal", "", blocked, "call-1", "call-1", string(mustJSON(goalResult{Goal: testGoal(active)})), ""},
|
|
|
|
|
} {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
path := filepath.Join(t.TempDir(), "rollout.jsonl")
|
|
|
|
|
if err := os.WriteFile(path, nil, 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
log, err := openIntent(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, agentCall(tc.id, tc.tool, tc.namespace, tc.status))
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatalf("unfinished call produced intent %q: %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, agentReply(tc.replyID, tc.output))
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != tc.want {
|
|
|
|
|
t.Fatalf("intent=%q want=%q error=%v", status, tc.want, err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, agentReply(tc.id, blockedReply))
|
|
|
|
|
if tc.replyID == tc.id {
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatalf("replayed completed call: %q %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAgentPending(t *testing.T) {
|
|
|
|
|
path := filepath.Join(t.TempDir(), "rollout.jsonl")
|
|
|
|
|
if err := os.WriteFile(path, nil, 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
log, err := openIntent(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, agentCall("call-1", "update_goal", "", blocked))
|
|
|
|
|
if status, err := log.read(math.MaxInt64); err != nil || status != "" {
|
|
|
|
|
t.Fatalf("unfinished call produced intent %q: %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
data, err := json.Marshal(log)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var restored intentLog
|
|
|
|
|
if err := json.Unmarshal(data, &restored); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
appendIntent(t, path, agentReply("call-1", string(mustJSON(goalResult{Goal: testGoal(blocked)}))))
|
|
|
|
|
if status, err := restored.read(math.MaxInt64); err != nil || status != blocked {
|
|
|
|
|
t.Fatalf("lost pending call across checkpoint: %q %v", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestAgentOtherItems(t *testing.T) {
|
|
|
|
|
var log intentLog
|
|
|
|
|
for _, payload := range []map[string]any{
|
|
|
|
|
{"type": "tool_search_call", "arguments": map[string]string{"query": "blocked"}},
|
|
|
|
|
{"type": "function_call", "name": "update_goal", "call_id": "call-1", "arguments": "invalid JSON"},
|
|
|
|
|
{"type": "function_call_output", "call_id": "call-1", "output": string(mustJSON(goalResult{Goal: testGoal(blocked)}))},
|
|
|
|
|
} {
|
|
|
|
|
line := mustJSON(map[string]any{"type": "response_item", "payload": payload})
|
|
|
|
|
if status, match, err := log.parse(line); err != nil || match || status != "" {
|
|
|
|
|
t.Fatalf("unrelated item produced intent=%q match=%v error=%v", status, match, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|