Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
301 lines
9.3 KiB
Go
301 lines
9.3 KiB
Go
package storesqlite
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
|
|
)
|
|
|
|
func TestCommandOutputAliasMigrationCompactsOnlyOversizedTerminalCommands(
|
|
t *testing.T,
|
|
) {
|
|
t.Parallel()
|
|
|
|
store := openTestStore(t, testOptions(&staticProjectPaths{}))
|
|
ctx := context.Background()
|
|
if _, err := store.ReportSessionState(ctx, SessionStateReport{
|
|
WorkspaceID: "ws-command-alias",
|
|
AgentSessionID: "session-command-alias",
|
|
Provider: "codex",
|
|
OccurredAtUnixMS: 10,
|
|
}); err != nil {
|
|
t.Fatalf("ReportSessionState() error = %v", err)
|
|
}
|
|
|
|
large := strings.Repeat("x", canonical.ToolOutputTextMaxBytes/2+256)
|
|
nearWireLimit := strings.Repeat(
|
|
"n",
|
|
canonical.ToolCallPayloadMaxBytes/2+2048,
|
|
)
|
|
legacyRaw := " " + strings.Repeat(
|
|
"p",
|
|
canonical.ToolOutputTextMaxBytes+64,
|
|
) + "\n"
|
|
legacyText := canonical.TruncateToolOutputText(strings.TrimSpace(legacyRaw))
|
|
legacyStdout := canonical.TruncateToolOutputText(legacyRaw)
|
|
if legacyText == strings.TrimSpace(legacyStdout) {
|
|
t.Fatal("legacy independently truncated fixture still compares as alias")
|
|
}
|
|
tests := []struct {
|
|
messageID string
|
|
status string
|
|
payload map[string]any
|
|
wantText bool
|
|
wantBudgeted bool
|
|
}{
|
|
{
|
|
messageID: "oversized-terminal-alias",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Bash",
|
|
"input": map[string]any{"command": "print output"},
|
|
"output": map[string]any{"text": large, "stdout": large + "\n"},
|
|
"seq": json.Number("9007199254740993"),
|
|
},
|
|
wantText: false,
|
|
wantBudgeted: true,
|
|
},
|
|
{
|
|
messageID: "below-wire-limit-terminal-alias",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Bash",
|
|
"input": map[string]any{"command": "print output"},
|
|
"output": map[string]any{
|
|
"text": nearWireLimit,
|
|
"stdout": nearWireLimit + "\n",
|
|
},
|
|
},
|
|
wantText: false,
|
|
wantBudgeted: true,
|
|
},
|
|
{
|
|
messageID: "independently-truncated-terminal-output",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Bash",
|
|
"input": map[string]any{"command": "print output"},
|
|
"output": map[string]any{
|
|
"text": legacyText,
|
|
"stdout": legacyStdout,
|
|
},
|
|
},
|
|
wantText: true,
|
|
wantBudgeted: true,
|
|
},
|
|
{
|
|
messageID: "small-terminal-alias",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Bash",
|
|
"input": map[string]any{"command": "print output"},
|
|
"output": map[string]any{"text": "small", "stdout": "small\n"},
|
|
},
|
|
wantText: true,
|
|
},
|
|
{
|
|
messageID: "oversized-non-command",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Edit",
|
|
"input": map[string]any{"command": "domain command"},
|
|
"output": map[string]any{"text": large, "stdout": large + "\n"},
|
|
},
|
|
wantText: true,
|
|
},
|
|
{
|
|
messageID: "oversized-running-command",
|
|
status: "running",
|
|
payload: map[string]any{
|
|
"toolName": "exec_command",
|
|
"input": map[string]any{"cmd": "print output"},
|
|
"output": map[string]any{"text": large, "stdout": large + "\n"},
|
|
},
|
|
wantText: true,
|
|
},
|
|
{
|
|
messageID: "oversized-distinct-command-text",
|
|
status: "completed",
|
|
payload: map[string]any{
|
|
"toolName": "Bash",
|
|
"input": map[string]any{"command": "print output"},
|
|
"output": map[string]any{
|
|
"text": strings.Repeat("y", len(large)),
|
|
"stdout": large + "\n",
|
|
},
|
|
},
|
|
wantText: true,
|
|
wantBudgeted: true,
|
|
},
|
|
{
|
|
messageID: "running-task-recursive-terminal-step",
|
|
status: "running",
|
|
payload: map[string]any{
|
|
"toolName": "Task",
|
|
"output": map[string]any{"text": "task running"},
|
|
"steps": []any{map[string]any{
|
|
"toolName": "Task",
|
|
"status": "running",
|
|
"toolResult": map[string]any{
|
|
"steps": []any{map[string]any{
|
|
"toolName": "Bash",
|
|
"status": "completed",
|
|
"toolInput": map[string]any{
|
|
"command": "print nested output",
|
|
},
|
|
"toolResult": map[string]any{
|
|
"text": large,
|
|
"stdout": large + "\n",
|
|
},
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
wantText: true,
|
|
wantBudgeted: true,
|
|
},
|
|
}
|
|
|
|
for index, test := range tests {
|
|
payloadJSON, err := json.Marshal(test.payload)
|
|
if err != nil {
|
|
t.Fatalf("marshal %s payload: %v", test.messageID, err)
|
|
}
|
|
if _, err := store.db.ExecContext(ctx, `
|
|
INSERT INTO workspace_agent_messages (
|
|
workspace_id, agent_session_id, message_id, version, role, kind, status,
|
|
payload_json, occurred_at_unix_ms, started_at_unix_ms, completed_at_unix_ms,
|
|
created_at_unix_ms, updated_at_unix_ms
|
|
) VALUES (?, ?, ?, ?, 'assistant', 'tool_call', ?, ?, 101, 102, 103, 104, 105)
|
|
`, "ws-command-alias", "session-command-alias", test.messageID, index+1,
|
|
test.status, string(payloadJSON)); err != nil {
|
|
t.Fatalf("insert %s: %v", test.messageID, err)
|
|
}
|
|
}
|
|
if _, err := store.db.ExecContext(ctx, `
|
|
UPDATE workspace_agent_sessions
|
|
SET deleted_at_unix_ms = 99
|
|
WHERE workspace_id = ? AND agent_session_id = ?
|
|
`, "ws-command-alias", "session-command-alias"); err != nil {
|
|
t.Fatalf("mark fixture session deleted: %v", err)
|
|
}
|
|
if _, err := store.db.ExecContext(ctx, `
|
|
UPDATE workspace_agent_messages
|
|
SET deleted_at_unix_ms = 99
|
|
WHERE workspace_id = ? AND agent_session_id = ? AND message_id = ?
|
|
`, "ws-command-alias", "session-command-alias", "oversized-terminal-alias"); err != nil {
|
|
t.Fatalf("mark fixture message deleted: %v", err)
|
|
}
|
|
|
|
if _, err := store.db.ExecContext(ctx, `
|
|
DELETE FROM agent_store_schema_migrations WHERE id = ?
|
|
`, schemaMigrationWorkspaceAgentCommandOutputAliasesV1); err != nil {
|
|
t.Fatalf("reset command output alias migration marker: %v", err)
|
|
}
|
|
if err := store.Migrate(ctx); err != nil {
|
|
t.Fatalf("rerun command output alias migration: %v", err)
|
|
}
|
|
|
|
for index, test := range tests {
|
|
var (
|
|
payloadJSON string
|
|
version int
|
|
occurredAt, startedAt, completedAt int64
|
|
createdAt, updatedAt int64
|
|
)
|
|
if err := store.db.QueryRowContext(ctx, `
|
|
SELECT payload_json, version, occurred_at_unix_ms, started_at_unix_ms,
|
|
completed_at_unix_ms, created_at_unix_ms, updated_at_unix_ms
|
|
FROM workspace_agent_messages
|
|
WHERE workspace_id = ? AND agent_session_id = ? AND message_id = ?
|
|
`, "ws-command-alias", "session-command-alias", test.messageID).Scan(
|
|
&payloadJSON,
|
|
&version,
|
|
&occurredAt,
|
|
&startedAt,
|
|
&completedAt,
|
|
&createdAt,
|
|
&updatedAt,
|
|
); err != nil {
|
|
t.Fatalf("read %s: %v", test.messageID, err)
|
|
}
|
|
var payload map[string]any
|
|
if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil {
|
|
t.Fatalf("decode %s: %v", test.messageID, err)
|
|
}
|
|
output := payload["output"].(map[string]any)
|
|
_, hasText := output["text"]
|
|
if hasText != test.wantText {
|
|
t.Fatalf("%s output = %#v, want text present %t", test.messageID, output, test.wantText)
|
|
}
|
|
if test.wantBudgeted && len(payloadJSON) > canonical.ToolCallPayloadMaxBytes {
|
|
t.Fatalf(
|
|
"%s payload has %d bytes, safe budget is %d",
|
|
test.messageID,
|
|
len(payloadJSON),
|
|
canonical.ToolCallPayloadMaxBytes,
|
|
)
|
|
}
|
|
if test.messageID == "oversized-terminal-alias" &&
|
|
!strings.Contains(payloadJSON, `"seq":9007199254740993`) {
|
|
t.Fatalf("%s lost exact JSON integer: %s", test.messageID, payloadJSON[:128])
|
|
}
|
|
if test.messageID == "oversized-terminal-alias" && output["stdout"] != large+"\n" {
|
|
t.Fatal("oversized terminal alias migration changed raw stdout")
|
|
}
|
|
if test.messageID == "independently-truncated-terminal-output" {
|
|
for _, key := range []string{"text", "stdout"} {
|
|
value := output[key].(string)
|
|
if !strings.HasSuffix(value, canonical.ToolOutputTruncationMarker) {
|
|
t.Fatalf("legacy output.%s lost truncation marker", key)
|
|
}
|
|
}
|
|
}
|
|
if test.messageID == "running-task-recursive-terminal-step" {
|
|
steps := payload["steps"].([]any)
|
|
nested := steps[0].(map[string]any)["toolResult"].(map[string]any)["steps"].([]any)[0].(map[string]any)["toolResult"].(map[string]any)
|
|
if _, exists := nested["text"]; exists {
|
|
t.Fatalf("recursive completed step retained text alias: %#v", nested)
|
|
}
|
|
}
|
|
if version != index+1 || occurredAt != 101 || startedAt != 102 ||
|
|
completedAt != 103 || createdAt != 104 || updatedAt != 105 {
|
|
t.Fatalf(
|
|
"%s metadata changed: version=%d times=%d/%d/%d/%d/%d",
|
|
test.messageID,
|
|
version,
|
|
occurredAt,
|
|
startedAt,
|
|
completedAt,
|
|
createdAt,
|
|
updatedAt,
|
|
)
|
|
}
|
|
}
|
|
|
|
var compactedJSON string
|
|
if err := store.db.QueryRowContext(ctx, `
|
|
SELECT payload_json FROM workspace_agent_messages WHERE message_id = ?
|
|
`, "oversized-terminal-alias").Scan(&compactedJSON); err != nil {
|
|
t.Fatalf("read compacted payload: %v", err)
|
|
}
|
|
if len(compactedJSON) > canonical.ToolCallPayloadMaxBytes {
|
|
t.Fatalf("compacted payload has %d bytes, want at most %d", len(compactedJSON), canonical.ToolCallPayloadMaxBytes)
|
|
}
|
|
if err := store.Migrate(ctx); err != nil {
|
|
t.Fatalf("repeat command output alias migration: %v", err)
|
|
}
|
|
var repeatedJSON string
|
|
if err := store.db.QueryRowContext(ctx, `
|
|
SELECT payload_json FROM workspace_agent_messages WHERE message_id = ?
|
|
`, "oversized-terminal-alias").Scan(&repeatedJSON); err != nil {
|
|
t.Fatalf("read repeated payload: %v", err)
|
|
}
|
|
if repeatedJSON != compactedJSON {
|
|
t.Fatal("repeated migration changed the compacted payload")
|
|
}
|
|
}
|