Files
tutti/packages/agent/runtimeprep/session_fork_provider_state.go
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

593 lines
17 KiB
Go

package runtimeprep
import (
"bufio"
"bytes"
"context"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
const codexHomeDirectory = "codex-home"
const tuttiAgentHomeDirectory = "tutti-agent-home"
var errCodexRolloutNotFound = errors.New("codex rollout was not found")
type codexRolloutFingerprint struct {
Size int64
SHA256 [sha256.Size]byte
}
type codexRolloutContentError struct {
err error
}
func (e *codexRolloutContentError) Error() string {
return e.err.Error()
}
func (e *codexRolloutContentError) Unwrap() error {
return e.err
}
type codexRolloutMetadata struct {
Type string `json:"type"`
Payload struct {
ID string `json:"id"`
SessionID string `json:"session_id"`
} `json:"payload"`
}
func (*DefaultPreparer) SupportsSessionForkProviderStateBinding(provider string) bool {
_, supported := sessionForkProviderHomeDirectory(provider)
return supported
}
func (p *DefaultPreparer) BindSessionForkProviderState(
ctx context.Context,
input SessionForkProviderStateBindingInput,
) error {
if err := ctx.Err(); err != nil {
return err
}
input.Provider = strings.TrimSpace(input.Provider)
providerHomeDirectory, supported :=
sessionForkProviderHomeDirectory(input.Provider)
if !supported {
return nil
}
input.WorkspaceID = strings.TrimSpace(input.WorkspaceID)
input.SourceAgentSessionID = strings.TrimSpace(input.SourceAgentSessionID)
input.TargetAgentSessionID = strings.TrimSpace(input.TargetAgentSessionID)
input.SourceProviderSessionID = strings.TrimSpace(input.SourceProviderSessionID)
input.TargetProviderSessionID = strings.TrimSpace(input.TargetProviderSessionID)
if input.WorkspaceID == "" ||
input.SourceAgentSessionID == "" ||
input.TargetAgentSessionID == "" ||
input.SourceProviderSessionID == "" ||
input.TargetProviderSessionID == "" ||
input.SourceAgentSessionID == input.TargetAgentSessionID ||
input.SourceProviderSessionID == input.TargetProviderSessionID {
return errors.New("app-server session fork provider state binding requires distinct source and target identities")
}
store := p.runtimeStore()
sourceRoot, err := store.RuntimeRoot(input.WorkspaceID, input.SourceAgentSessionID)
if err != nil {
return fmt.Errorf("resolve source runtime root: %w", err)
}
targetRoot, err := store.RuntimeRoot(input.WorkspaceID, input.TargetAgentSessionID)
if err != nil {
return fmt.Errorf("resolve target runtime root: %w", err)
}
if err := store.EnsureRuntimeRoot(targetRoot); err != nil {
return fmt.Errorf("ensure target runtime root: %w", err)
}
if err := syncDirectory(filepath.Dir(targetRoot)); err != nil {
return fmt.Errorf("sync target runtime parent directory: %w", err)
}
sourceProviderHome := filepath.Join(sourceRoot, providerHomeDirectory)
targetProviderHome := filepath.Join(targetRoot, providerHomeDirectory)
if err := ensureDirectoryTreeWithoutSymlinks(
targetRoot,
targetProviderHome,
); err != nil {
return fmt.Errorf("prepare target app-server provider home: %w", err)
}
targetPath, _, targetFingerprint, targetErr := findCodexRollout(
ctx,
targetProviderHome,
input.TargetProviderSessionID,
true,
)
if targetErr != nil && !errors.Is(targetErr, errCodexRolloutNotFound) {
var contentErr *codexRolloutContentError
if targetPath == "" || !errors.As(targetErr, &contentErr) {
return fmt.Errorf("inspect target app-server fork rollout: %w", targetErr)
}
}
if err := validateExistingDirectoryTreeWithoutSymlinks(
sourceRoot,
sourceProviderHome,
); err != nil {
if errors.Is(err, fs.ErrNotExist) && targetErr == nil {
return nil
}
return fmt.Errorf("validate source app-server provider home: %w", err)
}
sourcePath, relativePath, sourceFingerprint, err := findCodexRollout(
ctx,
sourceProviderHome,
input.TargetProviderSessionID,
false,
)
if err != nil {
if errors.Is(err, errCodexRolloutNotFound) && targetErr == nil {
return nil
}
return err
}
if targetErr != nil && targetPath != "" {
if targetFingerprint.Size == 0 ||
targetFingerprint.Size >= sourceFingerprint.Size {
return errors.New("damaged target app-server rollout diverges from accepted provider child state")
}
sourceExtendsDamagedTarget, err := regularFileHasExactPrefix(
sourcePath,
targetPath,
sourceFingerprint,
targetFingerprint,
)
if err != nil {
return fmt.Errorf("compare damaged target app-server rollout with fork baseline: %w", err)
}
if !sourceExtendsDamagedTarget {
return errors.New("damaged target app-server rollout diverges from accepted provider child state")
}
}
if targetErr == nil && targetFingerprint == sourceFingerprint {
return nil
}
if targetErr == nil {
switch {
case targetFingerprint.Size > sourceFingerprint.Size:
targetExtendsSource, err := regularFileHasExactPrefix(
targetPath,
sourcePath,
targetFingerprint,
sourceFingerprint,
)
if err != nil {
return fmt.Errorf("compare target app-server rollout with fork baseline: %w", err)
}
if targetExtendsSource {
return nil
}
return errors.New("target app-server rollout diverges from accepted provider child state")
case targetFingerprint.Size < sourceFingerprint.Size:
sourceExtendsTarget, err := regularFileHasExactPrefix(
sourcePath,
targetPath,
sourceFingerprint,
targetFingerprint,
)
if err != nil {
return fmt.Errorf("compare truncated target app-server rollout with fork baseline: %w", err)
}
if !sourceExtendsTarget {
return errors.New("target app-server rollout diverges from accepted provider child state")
}
default:
return errors.New("target app-server rollout diverges from accepted provider child state")
}
}
if targetPath == "" {
targetPath = filepath.Join(targetProviderHome, relativePath)
}
if err := ensurePathWithin(targetProviderHome, targetPath); err != nil {
return err
}
if err := ensureDirectoryTreeWithoutSymlinks(
targetProviderHome,
filepath.Dir(targetPath),
); err != nil {
return fmt.Errorf("prepare target app-server rollout directory: %w", err)
}
if err := copyRegularFileAtomically(
sourcePath,
targetPath,
sourceFingerprint,
); err != nil {
return fmt.Errorf("copy app-server fork rollout into target runtime: %w", err)
}
targetMatches, targetFingerprint, err := inspectCodexRollout(
targetPath,
input.TargetProviderSessionID,
)
if err != nil {
return fmt.Errorf("verify target app-server rollout: %w", err)
}
if !targetMatches || targetFingerprint != sourceFingerprint {
return errors.New("copied app-server fork rollout does not exactly match accepted provider child state")
}
return nil
}
func sessionForkProviderHomeDirectory(provider string) (string, bool) {
switch strings.ToLower(strings.TrimSpace(provider)) {
case "codex":
return codexHomeDirectory, true
case "tutti-agent":
return tuttiAgentHomeDirectory, true
default:
return "", false
}
}
func findCodexRollout(
ctx context.Context,
codexHome string,
targetProviderSessionID string,
tolerateInvalidContent bool,
) (string, string, codexRolloutFingerprint, error) {
var foundPath string
var foundRelativePath string
var foundFingerprint codexRolloutFingerprint
var foundContentErr error
for _, directory := range []string{"sessions", "archived_sessions"} {
root := filepath.Join(codexHome, directory)
walkErr := filepath.WalkDir(root, func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
if errors.Is(walkErr, fs.ErrNotExist) {
return nil
}
return walkErr
}
if err := ctx.Err(); err != nil {
return err
}
if entry.IsDir() {
return nil
}
if entry.Type()&os.ModeSymlink != 0 || !strings.Contains(entry.Name(), targetProviderSessionID) {
return nil
}
matches, fingerprint, err := inspectCodexRollout(
path,
targetProviderSessionID,
)
inspectionErr := err
if err != nil {
var contentErr *codexRolloutContentError
if tolerateInvalidContent && errors.As(err, &contentErr) {
if !matches {
return fmt.Errorf(
"cannot verify target provider session identity for damaged Codex rollout %s: %w",
path,
err,
)
}
} else {
return err
}
}
if !matches {
return nil
}
relativePath, relativeErr := filepath.Rel(codexHome, path)
if relativeErr != nil {
return relativeErr
}
if foundPath != "" && foundPath != path {
return fmt.Errorf("multiple Codex rollouts match provider session %s", targetProviderSessionID)
}
foundPath = path
foundRelativePath = relativePath
foundFingerprint = fingerprint
if inspectionErr != nil {
foundContentErr = inspectionErr
}
return nil
})
if walkErr != nil && !errors.Is(walkErr, fs.ErrNotExist) {
return "", "", codexRolloutFingerprint{}, fmt.Errorf("scan Codex fork rollouts: %w", walkErr)
}
}
if foundPath == "" {
return "", "", codexRolloutFingerprint{}, fmt.Errorf(
"%w for provider session %s",
errCodexRolloutNotFound,
targetProviderSessionID,
)
}
if foundContentErr != nil {
return foundPath, foundRelativePath, foundFingerprint, foundContentErr
}
return foundPath, foundRelativePath, foundFingerprint, nil
}
func codexRolloutMatches(path string, targetProviderSessionID string) (bool, error) {
matches, _, err := inspectCodexRollout(path, targetProviderSessionID)
return matches, err
}
func inspectCodexRollout(
path string,
targetProviderSessionID string,
) (bool, codexRolloutFingerprint, error) {
info, err := os.Lstat(path)
if err != nil {
return false, codexRolloutFingerprint{}, err
}
if !info.Mode().IsRegular() {
return false, codexRolloutFingerprint{}, fmt.Errorf("codex rollout is not a regular file: %s", path)
}
file, err := os.Open(path)
if err != nil {
return false, codexRolloutFingerprint{}, err
}
defer file.Close()
hasher := sha256.New()
reader := bufio.NewReader(file)
var metadata codexRolloutMetadata
var metadataMatches bool
var recordCount int
var bytesRead int64
for {
line, readErr := reader.ReadBytes('\n')
if len(line) > 0 {
bytesRead += int64(len(line))
if _, err := hasher.Write(line); err != nil {
return false, codexRolloutFingerprint{}, err
}
trimmed := bytes.TrimSpace(line)
if len(trimmed) > 0 {
if trimmed[0] != '{' || trimmed[len(trimmed)-1] != '}' {
return metadataMatches, makeCodexRolloutFingerprint(bytesRead, hasher.Sum(nil)), &codexRolloutContentError{
err: fmt.Errorf("codex rollout record %d is not a JSON object", recordCount+1),
}
}
var record codexRolloutMetadata
if err := json.Unmarshal(trimmed, &record); err != nil {
return metadataMatches, makeCodexRolloutFingerprint(bytesRead, hasher.Sum(nil)), &codexRolloutContentError{
err: fmt.Errorf("decode codex rollout record %d: %w", recordCount+1, err),
}
}
if recordCount == 0 {
metadata = record
metadataMatches = record.Type == "session_meta" &&
(strings.TrimSpace(record.Payload.ID) == targetProviderSessionID ||
strings.TrimSpace(record.Payload.SessionID) == targetProviderSessionID)
}
recordCount++
}
}
if readErr != nil {
if !errors.Is(readErr, io.EOF) {
return false, codexRolloutFingerprint{}, readErr
}
break
}
}
if recordCount == 0 || bytesRead != info.Size() {
return metadataMatches, makeCodexRolloutFingerprint(bytesRead, hasher.Sum(nil)), &codexRolloutContentError{
err: errors.New("codex rollout is empty or changed while being validated"),
}
}
fingerprint := makeCodexRolloutFingerprint(bytesRead, hasher.Sum(nil))
if metadata.Type != "session_meta" {
return false, fingerprint, nil
}
return metadataMatches, fingerprint, nil
}
func makeCodexRolloutFingerprint(size int64, sum []byte) codexRolloutFingerprint {
var fingerprint codexRolloutFingerprint
fingerprint.Size = size
copy(fingerprint.SHA256[:], sum)
return fingerprint
}
func regularFileHasExactPrefix(
fullPath,
prefixPath string,
expectedFull,
expectedPrefix codexRolloutFingerprint,
) (bool, error) {
if expectedFull.Size < expectedPrefix.Size {
return false, nil
}
prefixInfo, err := os.Lstat(prefixPath)
if err != nil {
return false, err
}
if !prefixInfo.Mode().IsRegular() {
return false, fmt.Errorf("prefix rollout is not a regular file: %s", prefixPath)
}
prefix, err := os.Open(prefixPath)
if err != nil {
return false, err
}
defer prefix.Close()
prefixHasher := sha256.New()
prefixBytes, err := io.Copy(prefixHasher, prefix)
if err != nil {
return false, err
}
if prefixBytes != expectedPrefix.Size ||
!bytes.Equal(prefixHasher.Sum(nil), expectedPrefix.SHA256[:]) {
return false, errors.New("prefix Codex rollout changed while being compared")
}
fullInfo, err := os.Lstat(fullPath)
if err != nil {
return false, err
}
if !fullInfo.Mode().IsRegular() {
return false, fmt.Errorf("full rollout is not a regular file: %s", fullPath)
}
full, err := os.Open(fullPath)
if err != nil {
return false, err
}
defer full.Close()
fullHasher := sha256.New()
fullPrefixHasher := sha256.New()
fullPrefixBytes, err := io.CopyN(
io.MultiWriter(fullHasher, fullPrefixHasher),
full,
expectedPrefix.Size,
)
if err != nil {
return false, err
}
if fullPrefixBytes != expectedPrefix.Size {
return false, errors.New("full Codex rollout changed while its prefix was being compared")
}
remainingBytes, err := io.Copy(fullHasher, full)
if err != nil {
return false, err
}
if fullPrefixBytes+remainingBytes != expectedFull.Size ||
!bytes.Equal(fullHasher.Sum(nil), expectedFull.SHA256[:]) {
return false, errors.New("full Codex rollout changed while being compared")
}
return bytes.Equal(fullPrefixHasher.Sum(nil), expectedPrefix.SHA256[:]), nil
}
func ensurePathWithin(root, path string) error {
root = filepath.Clean(root)
path = filepath.Clean(path)
relativePath, err := filepath.Rel(root, path)
if err != nil {
return fmt.Errorf("validate target provider state path: %w", err)
}
if relativePath == "." || relativePath == ".." ||
strings.HasPrefix(relativePath, ".."+string(filepath.Separator)) {
return errors.New("target provider state path escapes target runtime")
}
return nil
}
func ensureDirectoryTreeWithoutSymlinks(root, directory string) error {
if err := ensurePathWithin(root, filepath.Join(directory, "_")); err != nil {
return err
}
relativeDirectory, err := filepath.Rel(root, directory)
if err != nil {
return err
}
current := filepath.Clean(root)
for _, segment := range append(
[]string{"."},
strings.Split(relativeDirectory, string(filepath.Separator))...,
) {
if segment != "." {
current = filepath.Join(current, segment)
}
info, statErr := os.Lstat(current)
if statErr == nil {
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("managed provider state directory is not a regular directory: %s", current)
}
continue
}
if !errors.Is(statErr, fs.ErrNotExist) {
return statErr
}
if err := os.Mkdir(current, 0o755); err != nil && !errors.Is(err, fs.ErrExist) {
return err
}
if err := syncDirectory(filepath.Dir(current)); err != nil {
return err
}
}
return nil
}
func validateExistingDirectoryTreeWithoutSymlinks(root, directory string) error {
if err := ensurePathWithin(root, filepath.Join(directory, "_")); err != nil {
return err
}
relativeDirectory, err := filepath.Rel(root, directory)
if err != nil {
return err
}
current := filepath.Clean(root)
for _, segment := range append(
[]string{"."},
strings.Split(relativeDirectory, string(filepath.Separator))...,
) {
if segment != "." {
current = filepath.Join(current, segment)
}
info, err := os.Lstat(current)
if err != nil {
return err
}
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("managed provider state directory is not a regular directory: %s", current)
}
}
return nil
}
func copyRegularFileAtomically(
sourcePath,
targetPath string,
expected codexRolloutFingerprint,
) error {
sourceInfo, err := os.Lstat(sourcePath)
if err != nil {
return err
}
if !sourceInfo.Mode().IsRegular() {
return fmt.Errorf("source is not a regular file: %s", sourcePath)
}
targetDirectory := filepath.Dir(targetPath)
temp, err := os.CreateTemp(targetDirectory, ".tutti-codex-fork-*")
if err != nil {
return err
}
tempPath := temp.Name()
defer func() {
_ = temp.Close()
_ = os.Remove(tempPath)
}()
if err := temp.Chmod(sourceInfo.Mode().Perm()); err != nil {
return err
}
source, err := os.Open(sourcePath)
if err != nil {
return err
}
defer source.Close()
hasher := sha256.New()
copied, err := io.Copy(io.MultiWriter(temp, hasher), source)
if err != nil {
return err
}
if copied != expected.Size ||
!bytes.Equal(hasher.Sum(nil), expected.SHA256[:]) {
return errors.New("source Codex rollout changed while being copied")
}
if err := temp.Sync(); err != nil {
return err
}
if err := temp.Close(); err != nil {
return err
}
if err := os.Rename(tempPath, targetPath); err != nil {
return err
}
return syncDirectory(targetDirectory)
}