diff options
Diffstat (limited to 'internal/executor/channel.go')
| -rw-r--r-- | internal/executor/channel.go | 59 |
1 files changed, 43 insertions, 16 deletions
diff --git a/internal/executor/channel.go b/internal/executor/channel.go index 76df94f..541694b 100644 --- a/internal/executor/channel.go +++ b/internal/executor/channel.go @@ -4,10 +4,10 @@ import ( "context" "encoding/json" "errors" + "sync" "time" "github.com/thepeterstone/claudomator/internal/event" - "github.com/thepeterstone/claudomator/internal/storage" "github.com/thepeterstone/claudomator/internal/task" "github.com/google/uuid" ) @@ -50,37 +50,64 @@ type channelStore interface { CreateEvent(e *event.Event) error } -// storeChannel is the default AgentChannel backed by storage. Summary reports -// are buffered onto the execution record so the pool persists them once (with -// its extract/synthesize fallbacks); other signals are written immediately. +// storeChannel is the default AgentChannel backed by storage. Summary and +// question signals are buffered here under a mutex (they may arrive on an MCP +// handler goroutine mid-run); the pool flushes the summary to the execution and +// the runner reads the pending question to build a BlockedError after the +// subprocess exits. SpawnSubtask/RecordProgress write immediately. type storeChannel struct { store channelStore taskID string - exec *storage.Execution + + mu sync.Mutex + summary string + summarySet bool + question string + blocked bool } var _ AgentChannel = (*storeChannel)(nil) -func newStoreChannel(store channelStore, taskID string, exec *storage.Execution) *storeChannel { - return &storeChannel{store: store, taskID: taskID, exec: exec} +func newStoreChannel(store channelStore, taskID string) *storeChannel { + return &storeChannel{store: store, taskID: taskID} } -// AskUser on the default (file-transport) channel cannot deliver an answer -// in-session, so it always reports the run as blocked. Question persistence is -// owned by the pool's BlockedError handling. -func (c *storeChannel) AskUser(_ context.Context, _ string) (string, error) { +// AskUser buffers the question and flags the run as blocked. The default +// transport cannot deliver an answer in-session, so it returns ErrAgentBlocked; +// the runner converts the buffered question into a BlockedError after exit, and +// question persistence is owned by the pool's BlockedError handling. +func (c *storeChannel) AskUser(_ context.Context, questionJSON string) (string, error) { + c.mu.Lock() + c.question = questionJSON + c.blocked = true + c.mu.Unlock() return "", ErrAgentBlocked } -// ReportSummary buffers the summary onto the execution record. The pool persists -// it (preferring this value over its extract/synthesize fallbacks). +// ReportSummary buffers the summary; the pool flushes it onto the execution +// after the run (preferring it over its extract/synthesize fallbacks). func (c *storeChannel) ReportSummary(_ context.Context, summary string) error { - if c.exec != nil { - c.exec.Summary = summary - } + c.mu.Lock() + c.summary = summary + c.summarySet = true + c.mu.Unlock() return nil } +// ReportedSummary returns the buffered summary if report_summary was called. +func (c *storeChannel) ReportedSummary() (string, bool) { + c.mu.Lock() + defer c.mu.Unlock() + return c.summary, c.summarySet +} + +// PendingQuestion returns the buffered ask_user question if the run is blocked. +func (c *storeChannel) PendingQuestion() (string, bool) { + c.mu.Lock() + defer c.mu.Unlock() + return c.question, c.blocked +} + func (c *storeChannel) SpawnSubtask(_ context.Context, spec SubtaskSpec) (string, error) { now := time.Now().UTC() child := &task.Task{ |
