Files
tutti/packages/agent/daemon/runtime/provider_launch_prepare.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

208 lines
5.6 KiB
Go

package agentruntime
import (
"context"
"log/slog"
"sync"
)
type providerLaunchRuntimeContextKey struct{}
func withProviderLaunchRuntimeContext(ctx context.Context, runtimeContext map[string]any) context.Context {
if len(runtimeContext) == 0 {
return ctx
}
return context.WithValue(ctx, providerLaunchRuntimeContextKey{}, clonePayload(runtimeContext))
}
func providerLaunchRuntimeContext(ctx context.Context) map[string]any {
if ctx == nil {
return nil
}
runtimeContext, _ := ctx.Value(providerLaunchRuntimeContextKey{}).(map[string]any)
return clonePayload(runtimeContext)
}
type ProviderLaunchPrepareInput struct {
Provider string
Session Session
Command []string
Env []string
CWD string
DirectStart bool
// SkipSkills is used by model-only probes that do not start a live Agent
// Session and therefore do not need provider Skill materialization.
SkipSkills bool
}
type ProviderLaunchPrepareResult struct {
Command []string
Env []string
CWD string
Cleanup func(context.Context) error
}
type ProviderLaunchPreparer func(context.Context, ProviderLaunchPrepareInput) (ProviderLaunchPrepareResult, error)
type ProviderLaunchPreparerAdapter interface {
SetProviderLaunchPreparer(ProviderLaunchPreparer)
}
func setProviderLaunchPreparer(adapters []Adapter, preparer ProviderLaunchPreparer) {
ApplyProviderLaunchPreparer(adapters, preparer)
}
func ApplyProviderLaunchPreparer(adapters []Adapter, preparer ProviderLaunchPreparer) {
if preparer == nil {
return
}
for _, adapter := range adapters {
if setter, ok := adapter.(ProviderLaunchPreparerAdapter); ok {
setter.SetProviderLaunchPreparer(preparer)
}
}
}
func prepareProviderLaunch(
ctx context.Context,
preparer ProviderLaunchPreparer,
session Session,
spec ProcessSpec,
) (ProcessSpec, func(context.Context), error) {
spec.Command = append([]string(nil), spec.Command...)
spec.Env = append([]string(nil), spec.Env...)
spec.ExecutableIdentity = cloneExecutableIdentity(spec.ExecutableIdentity)
if preparer == nil {
return spec, nil, nil
}
launchSession := cloneProviderLaunchSession(session)
if runtimeContext := providerLaunchRuntimeContext(ctx); runtimeContext != nil {
launchSession.RuntimeContext = runtimeContext
}
result, err := preparer(ctx, ProviderLaunchPrepareInput{
Provider: spec.Provider,
Session: launchSession,
Command: append([]string(nil), spec.Command...),
Env: append([]string(nil), spec.Env...),
CWD: spec.CWD,
DirectStart: spec.DirectStart,
})
if err != nil {
return ProcessSpec{}, nil, err
}
spec.Command = append([]string(nil), result.Command...)
spec.Env = append([]string(nil), result.Env...)
spec.CWD = result.CWD
return spec, providerLaunchCleanup(spec, result.Cleanup), nil
}
func cloneExecutableIdentity(value *ExecutableIdentity) *ExecutableIdentity {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
func cloneProviderLaunchSession(session Session) Session {
session.Env = append([]string(nil), session.Env...)
session.RuntimeContext = clonePayload(session.RuntimeContext)
session.ProviderTargetRef = clonePayload(session.ProviderTargetRef)
if session.Settings != nil {
session.Settings = cloneSessionSettings(*session.Settings)
}
return session
}
func providerLaunchCleanup(spec ProcessSpec, cleanup func(context.Context) error) func(context.Context) {
if cleanup == nil {
return nil
}
var once sync.Once
return func(ctx context.Context) {
once.Do(func() {
if ctx == nil {
ctx = context.Background()
}
if err := cleanup(ctx); err != nil {
slog.Warn("agent session provider launch cleanup failed",
"event", "agent_session.provider_launch.cleanup_failed",
"provider", spec.Provider,
"room_id", spec.RoomID,
"agent_session_id", spec.AgentSessionID,
"error", err.Error(),
)
}
})
}
}
func cleanupPreparedLaunch(cleanup func(context.Context)) {
if cleanup != nil {
cleanup(context.Background())
}
}
func wrapProviderLaunchCleanup(conn ProcessConnection, cleanup func(context.Context)) ProcessConnection {
if conn == nil || cleanup == nil {
return conn
}
wrapped := &providerLaunchCleanupConnection{
ProcessConnection: conn,
cleanup: cleanup,
}
if graceful, ok := conn.(GracefulProcessConnection); ok {
return &providerLaunchCleanupGracefulConnection{
providerLaunchCleanupConnection: wrapped,
graceful: graceful,
}
}
return wrapped
}
type providerLaunchCleanupConnection struct {
ProcessConnection
cleanup func(context.Context)
}
func (c *providerLaunchCleanupConnection) ProcessCassetteCaptureOrigin() ProcessCassetteCaptureOrigin {
checkpoint, ok := c.ProcessConnection.(ProcessCassetteCheckpointConnection)
if !ok {
return ""
}
return checkpoint.ProcessCassetteCaptureOrigin()
}
func (c *providerLaunchCleanupConnection) RecvContext(ctx context.Context) (ProcessFrame, error) {
if contextual, ok := c.ProcessConnection.(ContextProcessConnection); ok {
return contextual.RecvContext(ctx)
}
return c.Recv()
}
func (c *providerLaunchCleanupConnection) Close() error {
if c == nil {
return nil
}
err := c.ProcessConnection.Close()
c.cleanup(context.Background())
return err
}
type providerLaunchCleanupGracefulConnection struct {
*providerLaunchCleanupConnection
graceful GracefulProcessConnection
}
func (c *providerLaunchCleanupGracefulConnection) CloseInput() error {
return c.graceful.CloseInput()
}
func (c *providerLaunchCleanupGracefulConnection) Terminate() error {
return c.graceful.Terminate()
}
func (c *providerLaunchCleanupGracefulConnection) Kill() error {
return c.graceful.Kill()
}