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
71 lines
1.8 KiB
Go
71 lines
1.8 KiB
Go
package workspace
|
|
|
|
import "sync"
|
|
|
|
// IssueRunLaunchGate coordinates the short local handoff between a durable Run
|
|
// claim and an external Agent launch. It never holds a mutex across external
|
|
// work. Stop records intent and returns immediately; launch either skips the
|
|
// claim or compensates after the external call completes.
|
|
type IssueRunLaunchGate struct {
|
|
mu sync.Mutex
|
|
states map[string]issueRunLaunchGateState
|
|
}
|
|
|
|
type issueRunLaunchGateState struct {
|
|
launching bool
|
|
cancelRequested bool
|
|
}
|
|
|
|
func NewIssueRunLaunchGate() *IssueRunLaunchGate {
|
|
return &IssueRunLaunchGate{states: make(map[string]issueRunLaunchGateState)}
|
|
}
|
|
|
|
func (g *IssueRunLaunchGate) begin(workspaceID string, runID string) bool {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
key := workspaceID + "/" + runID
|
|
state := g.states[key]
|
|
if state.cancelRequested {
|
|
return false
|
|
}
|
|
state.launching = true
|
|
g.states[key] = state
|
|
return true
|
|
}
|
|
|
|
func (g *IssueRunLaunchGate) finish(workspaceID string, runID string) bool {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
key := workspaceID + "/" + runID
|
|
state := g.states[key]
|
|
cancelRequested := state.cancelRequested
|
|
delete(g.states, key)
|
|
return cancelRequested
|
|
}
|
|
|
|
func (g *IssueRunLaunchGate) requestCancel(workspaceID string, runID string) bool {
|
|
g.mu.Lock()
|
|
defer g.mu.Unlock()
|
|
key := workspaceID + "/" + runID
|
|
state := g.states[key]
|
|
launching := state.launching
|
|
state.cancelRequested = true
|
|
g.states[key] = state
|
|
return launching
|
|
}
|
|
|
|
func (g *IssueRunLaunchGate) clear(workspaceID string, runID string) {
|
|
g.mu.Lock()
|
|
delete(g.states, workspaceID+"/"+runID)
|
|
g.mu.Unlock()
|
|
}
|
|
|
|
var fallbackIssueRunLaunchGate = NewIssueRunLaunchGate()
|
|
|
|
func (s IssueManagerService) runLaunchGate() *IssueRunLaunchGate {
|
|
if s.RunLaunchGate != nil {
|
|
return s.RunLaunchGate
|
|
}
|
|
return fallbackIssueRunLaunchGate
|
|
}
|