diff options
Diffstat (limited to 'internal/agentchannel')
| -rw-r--r-- | internal/agentchannel/agentchannel.go | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/internal/agentchannel/agentchannel.go b/internal/agentchannel/agentchannel.go new file mode 100644 index 0000000..2730dfa --- /dev/null +++ b/internal/agentchannel/agentchannel.go @@ -0,0 +1,65 @@ +// Package agentchannel holds the runner-agnostic types describing how a Runner +// reports agent-originated signals (summary, questions, subtasks, progress) +// back to the rest of the system, plus the sentinel/blocked-run error types +// that flow through that channel. +// +// These types used to live in internal/executor. They were extracted here in +// Phase 1 of the multi-provider refactor so that internal/agentloop (which +// needs to construct/inspect them while running the provider-neutral tool-use +// loop) does not have to import internal/executor — and internal/executor, in +// turn, can import internal/agentloop (via NativeRunner) without creating an +// import cycle. internal/executor re-exports everything here via type +// aliases/var assignments (see internal/executor/channel.go) so existing code +// that refers to executor.AgentChannel, executor.SubtaskSpec, +// executor.BlockedError, and executor.ErrAgentBlocked keeps compiling +// unchanged. +package agentchannel + +import ( + "context" + "errors" + "fmt" +) + +// ErrAgentBlocked is returned by AgentChannel.AskUser when the transport cannot +// deliver an answer within the current run — e.g. the file transport, which can +// only write-and-exit. Runners translate it into a *BlockedError so the task +// blocks and resumes later. Transports that can suspend the agent in-session +// (MCP, Phase 2) instead block and return the answer. +var ErrAgentBlocked = errors.New("agent blocked awaiting user input") + +// SubtaskSpec describes a child task an agent wants to spawn. +type SubtaskSpec struct { + Name string + Instructions string + Model string + MaxBudgetUSD float64 +} + +// AgentChannel is how a Runner reports agent-originated signals to the rest of +// the system. Implementations translate these into stored artifacts and events. +// The transport by which a Runner detects these signals — post-exit files +// today, MCP/tool-use in Phase 2 — is the Runner's private concern. +type AgentChannel interface { + // AskUser records that the agent needs human input. Transports that can + // suspend the agent in-session return the answer; those that cannot return + // ErrAgentBlocked. + AskUser(ctx context.Context, questionJSON string) (answer string, err error) + // ReportSummary records the agent's final summary text. + ReportSummary(ctx context.Context, summary string) error + // SpawnSubtask creates a child task and returns its ID. + SpawnSubtask(ctx context.Context, spec SubtaskSpec) (taskID string, err error) + // RecordProgress records a free-form progress note from the agent. + RecordProgress(ctx context.Context, message string) error +} + +// BlockedError is returned by Run when the agent asked the user a question and +// the run stopped to await an answer. The pool transitions the task to BLOCKED +// and stores the question for the user. +type BlockedError struct { + QuestionJSON string // raw JSON from the question + SessionID string // claude session to resume once the user answers + SandboxDir string // preserved sandbox path; resume must run here so the agent finds its session files +} + +func (e *BlockedError) Error() string { return fmt.Sprintf("task blocked: %s", e.QuestionJSON) } |
