From c7d95f3992d24f86ff71e5f3e18260a8ef8a09f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 24 May 2026 08:47:07 +0000 Subject: feat(executor): introduce AgentChannel seam for runner signals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defines AgentChannel — the normalized interface by which a runner reports agent-originated signals (AskUser, ReportSummary, SpawnSubtask, RecordProgress) — plus a default storeChannel implementation backed by storage. Runner.Run now takes an AgentChannel; the pool constructs one per execution. The file transport routes its post-exit summary detection through ch.ReportSummary (buffered onto the execution so the pool still applies its extract/synthesize fallbacks, no double-write). AskUser returns ErrAgentBlocked since write-and-exit cannot answer in-session; question persistence stays with the pool's BlockedError handling. SpawnSubtask and RecordProgress are implemented and tested, ready for the MCP transport in Phase 2 where the channel becomes fully load-bearing. Store gains CreateEvent so the channel can emit agent_message events. https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39 --- internal/executor/gemini.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'internal/executor/gemini.go') diff --git a/internal/executor/gemini.go b/internal/executor/gemini.go index 3abec05..d229361 100644 --- a/internal/executor/gemini.go +++ b/internal/executor/gemini.go @@ -49,7 +49,7 @@ func (r *GeminiRunner) binaryPath() string { // inspect partial work. If the agent writes a question file before exiting, // Run returns *BlockedError with SandboxDir populated so a resume execution // can pick up in the same directory. -func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Execution) error { +func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Execution, ch AgentChannel) error { projectDir := t.Agent.ProjectDir if projectDir != "" { @@ -136,7 +136,7 @@ func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi questionJSON := strings.TrimSpace(string(data)) if isCompletionReport(questionJSON) { r.Logger.Info("treating question file as completion report", "taskID", e.TaskID) - e.Summary = extractQuestionText(questionJSON) + _ = ch.ReportSummary(ctx, extractQuestionText(questionJSON)) } else { // Preserve sandbox on BLOCKED so a resume can pick up in the same dir. return &BlockedError{QuestionJSON: questionJSON, SessionID: e.SessionID, SandboxDir: sandboxDir} @@ -147,7 +147,7 @@ func (r *GeminiRunner) Run(ctx context.Context, t *task.Task, e *storage.Executi summaryFile := filepath.Join(logDir, "summary.txt") if summaryData, readErr := os.ReadFile(summaryFile); readErr == nil { os.Remove(summaryFile) - e.Summary = strings.TrimSpace(string(summaryData)) + _ = ch.ReportSummary(ctx, strings.TrimSpace(string(summaryData))) } // Merge sandbox back to project_dir and clean up. -- cgit v1.2.3