源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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
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
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
//go:build windows
|
||||
|
||||
package agentruntime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLocalProcessTransportUsesWindowsEnvironmentKeySemantics(t *testing.T) {
|
||||
const helperEnvironment = "TUTTI_TEST_WINDOWS_AGENT_ENVIRONMENT_HELPER"
|
||||
if os.Getenv(helperEnvironment) == "1" {
|
||||
fmt.Printf("cwd=%s\nplacement=%s\n", os.Getenv("TUTTI_AGENT_CWD"), os.Getenv("TUTTI_AGENT_RAIL_PLACEMENT"))
|
||||
return
|
||||
}
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("resolve test executable: %v", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
conn, err := NewLocalProcessTransport().Start(ctx, ProcessSpec{
|
||||
Command: []string{executable, "-test.run=^TestLocalProcessTransportUsesWindowsEnvironmentKeySemantics$"},
|
||||
Env: []string{
|
||||
helperEnvironment + "=1",
|
||||
"TUTTI_AGENT_CWD=C:\\stale-exact",
|
||||
"tutti_agent_cwd=C:\\stale-case-variant",
|
||||
"TUTTI_AGENT_CWD=C:\\workspace\\canonical",
|
||||
`TUTTI_AGENT_RAIL_PLACEMENT={"version":1,"kind":"conversations","sectionKey":"stale"}`,
|
||||
`tutti_agent_rail_placement={"version":1,"kind":"conversations","sectionKey":"case-variant"}`,
|
||||
`TUTTI_AGENT_RAIL_PLACEMENT={"version":1,"kind":"project","projectPath":"C:\\workspace","sectionKey":"project:C:\\workspace"}`,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("start environment helper: %v", err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
receiver, ok := conn.(ContextProcessConnection)
|
||||
if !ok {
|
||||
t.Fatal("local process connection does not support bounded receive")
|
||||
}
|
||||
|
||||
var stdout strings.Builder
|
||||
for {
|
||||
frame, err := receiver.RecvContext(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("receive environment helper output: %v", err)
|
||||
}
|
||||
stdout.Write(frame.Stdout)
|
||||
if frame.ExitCode == nil {
|
||||
continue
|
||||
}
|
||||
if *frame.ExitCode != 0 {
|
||||
t.Fatalf("environment helper exit code = %d, output=%q", *frame.ExitCode, stdout.String())
|
||||
}
|
||||
break
|
||||
}
|
||||
output := stdout.String()
|
||||
if !strings.Contains(output, "cwd=C:\\workspace\\canonical\n") {
|
||||
t.Fatalf("environment helper output = %q, want canonical cwd", output)
|
||||
}
|
||||
if !strings.Contains(output, `placement={"version":1,"kind":"project","projectPath":"C:\\workspace","sectionKey":"project:C:\\workspace"}`+"\n") {
|
||||
t.Fatalf("environment helper output = %q, want canonical rail placement", output)
|
||||
}
|
||||
if strings.Contains(output, "stale") || strings.Contains(output, "case-variant") {
|
||||
t.Fatalf("environment helper output retained an earlier case-insensitive value: %q", output)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCodexAppServerWindowsTurnStartContract crosses the real Codex
|
||||
// app-server process boundary. Unit request-shape tests cannot catch Rust's
|
||||
// platform-aware AbsolutePathBuf deserialization failures.
|
||||
func TestCodexAppServerWindowsTurnStartContract(t *testing.T) {
|
||||
command := strings.TrimSpace(os.Getenv("TUTTI_CODEX_APP_SERVER_CONTRACT_BIN"))
|
||||
if command == "" {
|
||||
t.Skip("set TUTTI_CODEX_APP_SERVER_CONTRACT_BIN to the native codex.exe")
|
||||
}
|
||||
command, err := filepath.Abs(command)
|
||||
if err != nil {
|
||||
t.Fatalf("resolve Codex executable: %v", err)
|
||||
}
|
||||
if filepath.Ext(command) != ".exe" {
|
||||
t.Fatalf("Codex contract executable = %q, want native .exe", command)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
cwd := t.TempDir()
|
||||
codexHome := filepath.Join(t.TempDir(), "codex-home")
|
||||
if err := os.MkdirAll(codexHome, 0o700); err != nil {
|
||||
t.Fatalf("create isolated CODEX_HOME: %v", err)
|
||||
}
|
||||
conn, err := NewLocalProcessTransport().Start(ctx, ProcessSpec{
|
||||
Provider: ProviderCodex,
|
||||
CWD: cwd,
|
||||
Command: []string{command, "app-server"},
|
||||
Env: append(
|
||||
withoutEnvironmentKey(os.Environ(), "CODEX_HOME"),
|
||||
"CODEX_HOME="+codexHome,
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("start Codex app-server: %v", err)
|
||||
}
|
||||
client := newCodexAppServerClient(conn)
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
if _, err := client.Initialize(ctx, 10*time.Second, map[string]any{
|
||||
"clientInfo": map[string]any{
|
||||
"name": "tutti-windows-contract-test", "title": "Tutti", "version": "0",
|
||||
},
|
||||
"capabilities": map[string]any{"experimentalApi": true},
|
||||
}, nil); err != nil {
|
||||
t.Fatalf("initialize Codex app-server: %v", err)
|
||||
}
|
||||
if err := client.Initialized(ctx); err != nil {
|
||||
t.Fatalf("notify initialized: %v", err)
|
||||
}
|
||||
|
||||
// The historical payload must fail in the real Windows deserializer. This
|
||||
// negative control proves the contract test would catch the regression.
|
||||
const missingThreadID = "00000000-0000-0000-0000-000000000001"
|
||||
historical := map[string]any{
|
||||
"threadId": missingThreadID,
|
||||
"input": []any{},
|
||||
"sandboxPolicy": map[string]any{
|
||||
"type": "workspaceWrite", "writableRoots": []string{"/sandbox-tmp"},
|
||||
},
|
||||
}
|
||||
if _, err := client.TurnStart(ctx, 10*time.Second, historical); err == nil ||
|
||||
!strings.Contains(err.Error(), "AbsolutePathBuf deserialized without a base path") {
|
||||
t.Fatalf("historical turn/start error = %v, want AbsolutePathBuf rejection", err)
|
||||
}
|
||||
|
||||
projectedTempWithBase := map[string]any{
|
||||
"threadId": missingThreadID,
|
||||
"input": []any{},
|
||||
"cwd": cwd,
|
||||
"sandboxPolicy": map[string]any{
|
||||
"type": "workspaceWrite", "writableRoots": []string{"/sandbox-tmp"},
|
||||
},
|
||||
}
|
||||
_, err = client.TurnStart(ctx, 10*time.Second, projectedTempWithBase)
|
||||
if err == nil || !strings.Contains(err.Error(), "AbsolutePathBuf deserialized without a base path") {
|
||||
t.Fatalf("projected temp turn/start error = %v, want AbsolutePathBuf rejection even with cwd", err)
|
||||
}
|
||||
|
||||
// The production builder supplies cwd and omits the POSIX-only writable
|
||||
// root on Windows. A missing thread is deliberate: it exercises request
|
||||
// deserialization without authenticating or starting a model turn.
|
||||
session := Session{CWD: cwd, PermissionModeID: "auto"}
|
||||
params := appServerTurnStartParams(
|
||||
session,
|
||||
missingThreadID,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"",
|
||||
"",
|
||||
false,
|
||||
)
|
||||
_, err = client.TurnStart(ctx, 10*time.Second, params)
|
||||
if err == nil {
|
||||
t.Fatal("production turn/start unexpectedly found the missing thread")
|
||||
}
|
||||
if strings.Contains(err.Error(), "AbsolutePathBuf") {
|
||||
t.Fatalf("production turn/start failed path deserialization: %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "thread not found") {
|
||||
t.Fatalf("production turn/start error = %v, want semantic missing-thread rejection", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user