Files
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

86 lines
2.5 KiB
Go

package agenthost
import (
"context"
"strings"
"sync"
)
type sessionActorEntry struct {
gate chan struct{}
refs int
}
// SessionActor serializes mutations for one canonical session. An adapter
// that constructs short-lived Host values must share actor instances across
// those Host values for every mutation lane it configures.
type SessionActor struct {
mu sync.Mutex
entries map[string]*sessionActorEntry
}
func NewSessionActor() *SessionActor {
return &SessionActor{entries: make(map[string]*sessionActorEntry)}
}
func (a *SessionActor) Do(ctx context.Context, ref SessionRef, fn func(context.Context) error) error {
if a == nil || strings.TrimSpace(ref.WorkspaceID) == "" || strings.TrimSpace(ref.AgentSessionID) == "" || fn == nil {
return ErrInvalidArgument
}
key := strings.TrimSpace(ref.WorkspaceID) + "\x00" + strings.TrimSpace(ref.AgentSessionID)
a.mu.Lock()
entry := a.entries[key]
if entry == nil {
entry = &sessionActorEntry{gate: make(chan struct{}, 1)}
entry.gate <- struct{}{}
a.entries[key] = entry
}
entry.refs++
a.mu.Unlock()
select {
case <-ctx.Done():
a.releaseReference(key, entry)
return ctx.Err()
case <-entry.gate:
}
if err := ctx.Err(); err != nil {
entry.gate <- struct{}{}
a.releaseReference(key, entry)
return err
}
err := fn(ctx)
entry.gate <- struct{}{}
a.releaseReference(key, entry)
return err
}
func (a *SessionActor) releaseReference(key string, entry *sessionActorEntry) {
a.mu.Lock()
entry.refs--
if entry.refs == 0 && a.entries[key] == entry {
delete(a.entries, key)
}
a.mu.Unlock()
}
func (h *Host) withSessionMutationActor(ctx context.Context, workspaceID, agentSessionID string, fn func(context.Context) error) error {
if h == nil || h.sessionMutationActor == nil {
return ErrInvalidArgument
}
return h.withWorkspaceRuntimeOperationInfo(ctx, WorkspaceRuntimeOperationInfo{
WorkspaceID: workspaceID, AgentSessionID: agentSessionID,
}, func(operationCtx context.Context) error {
return h.sessionMutationActor.Do(operationCtx, SessionRef{WorkspaceID: workspaceID, AgentSessionID: agentSessionID}, fn)
})
}
func (h *Host) withSessionMutationActors(ctx context.Context, workspaceID string, agentSessionIDs []string, fn func(context.Context) error) error {
if len(agentSessionIDs) == 0 {
return fn(ctx)
}
return h.withSessionMutationActor(ctx, workspaceID, agentSessionIDs[0], func(actorCtx context.Context) error {
return h.withSessionMutationActors(actorCtx, workspaceID, agentSessionIDs[1:], fn)
})
}