Implement executor mesh support with Inbox, OnEvent, and ShouldStop#4
Implement executor mesh support with Inbox, OnEvent, and ShouldStop#4
Conversation
Extends executor.Config with Inbox (message injection), OnEvent (real-time streaming), and ShouldStop (custom termination) to support ratchet-cli's agent mesh. 6-step plan, all backward-compatible. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Implement executor mesh support: Inbox, OnEvent, ShouldStop Add three optional fields to executor.Config for mesh support: - Inbox: channel for injecting external messages into the conversation - OnEvent: callback for real-time event streaming - ShouldStop: custom termination hook All features are backward-compatible - nil values preserve existing behavior. Includes comprehensive unit and integration tests. Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/443ef504-27cc-40a0-88f3-b61e67d82a9f Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> * Address code review feedback: fix comment and optimize closed-channel handling Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/443ef504-27cc-40a0-88f3-b61e67d82a9f Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> * Address review: move inbox drain before compaction, copy tool args, fix drainInbox - Move drainInbox call before NeedsCompaction so injected messages are accounted for in compaction decisions - Copy tc.Arguments via copyArgs() before placing on EventToolCallStart to prevent OnEvent handlers from mutating the map used by tool execution - Fix drainInbox: remove dead `closed` variable (default branch now returns false directly), update doc comment to match actual behavior - Add TestExecute_ToolArgsEventIsCopy to verify arg isolation Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/79a383c2-cc77-4f96-8c82-963af9a41e07 Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds “executor mesh” features to enable external message injection, real-time event streaming, and custom termination conditions for agents running under a multi-agent mesh.
Changes:
- Extended
executor.ConfigwithInbox,OnEvent, andShouldStop, and integrated these into the mainExecute()loop. - Introduced an
executor.Eventmodel (executor/events.go) and emitted events for iteration/thinking/text/tool execution/completion/failure. - Added unit + integration-style tests covering inbox draining, event sequencing, termination behavior, and transcript recording.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
executor/executor.go |
Implements inbox draining, event emission, and ShouldStop termination inside the executor loop. |
executor/events.go |
Defines event types and the Event payload used by OnEvent. |
executor/executor_test.go |
Adds focused unit tests for inbox/events/ShouldStop/backward compatibility. |
executor/mesh_support_test.go |
Adds end-to-end style mesh scenario tests (inbox + events + termination). |
docs/plans/2026-04-04-executor-mesh-support-design.md |
Documents the design; currently has an ordering mismatch vs implementation for inbox draining vs compaction. |
docs/plans/2026-04-04-executor-mesh-support-plan.md |
Implementation plan; also has the same ordering mismatch. |
go.mod / go.sum |
Updates dependencies required by the broader mesh-related changes/tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return &Result{ | ||
| Content: reason, | ||
| Status: "completed", | ||
| Iterations: iterCount, |
There was a problem hiding this comment.
The ShouldStop early-return sets Status: "completed" but drops Result.Thinking, unlike the normal completion path (which returns finalThinking). Consider populating Thinking: finalThinking here as well so callers get consistent debugging information for completed runs.
| Iterations: iterCount, | |
| Iterations: iterCount, | |
| Thinking: finalThinking, |
| // Emit completed event | ||
| emit(cfg, Event{ | ||
| Type: EventCompleted, | ||
| AgentID: agentID, | ||
| Iteration: iterCount, | ||
| Content: finalContent, | ||
| }) |
There was a problem hiding this comment.
Terminal events are only emitted on the normal completion path (EventCompleted at the end) and on Provider.Chat errors (EventFailed). Other early-exit statuses returned from this function (e.g. approval timeout, human request expiry, loop detection) still return without emitting a terminal event, which can leave mesh/TUI consumers waiting for a final signal. Consider emitting a terminal event in every return path (or centralizing returns via a helper that always emits).
|
@copilot apply changes based on the comments in this thread |
…rminal events on all exits, fix docs ordering Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/568b084c-b021-4953-b847-6856a7d13916 Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/568b084c-b021-4953-b847-6856a7d13916 Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
All five review comments have been addressed in commits
New tests added: |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 8 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@copilot Fix the test failures |
…tching assetSuffixesForPlatform priority Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/f77f67f5-0445-4eea-a346-636e4fdd9eef Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
... Fixed in commit |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Emit tool call start event with a copy of the arguments to prevent | ||
| // any OnEvent handler from mutating the map used by the tool execution. | ||
| emit(cfg, Event{ | ||
| Type: EventToolCallStart, | ||
| AgentID: agentID, | ||
| Iteration: iterCount, | ||
| ToolName: tc.Name, | ||
| ToolCallID: tc.ID, | ||
| ToolArgs: copyArgs(tc.Arguments), | ||
| }) |
There was a problem hiding this comment.
The comment says the ToolArgs copy prevents OnEvent handlers from mutating the args used by tool execution, but copyArgs is only a shallow copy. If tc.Arguments contains nested maps/slices, an OnEvent handler can still mutate nested values and affect tool execution. Consider either updating the comment to reflect the shallow-copy limitation, or performing a deep copy for ToolArgs (with an eye on performance).
| for { | ||
| select { | ||
| case msg, ok := <-cfg.Inbox: | ||
| if !ok { | ||
| // Channel closed — stop draining. | ||
| return messages, true | ||
| } | ||
| messages = append(messages, msg) | ||
| _ = cfg.Transcript.Record(ctx, TranscriptEntry{ | ||
| ID: uuid.New().String(), | ||
| AgentID: agentID, | ||
| TaskID: cfg.TaskID, | ||
| ProjectID: cfg.ProjectID, | ||
| Iteration: iteration, | ||
| Role: msg.Role, | ||
| Content: msg.Content, | ||
| }) | ||
| default: | ||
| // No more pending messages. | ||
| return messages, false | ||
| } |
There was a problem hiding this comment.
drainInbox can starve the executor loop: if another goroutine is continuously sending to cfg.Inbox, the receive case will keep being ready and this for { select { ... } } loop may never hit default, preventing progress to compaction/LLM calls. Consider bounding the number of messages drained per iteration (e.g., max N) and/or adding a case <-ctx.Done(): to break out early.
| case msg, ok := <-cfg.Inbox: | ||
| if !ok { | ||
| // Channel closed — stop draining. | ||
| return messages, true | ||
| } | ||
| messages = append(messages, msg) | ||
| _ = cfg.Transcript.Record(ctx, TranscriptEntry{ | ||
| ID: uuid.New().String(), | ||
| AgentID: agentID, | ||
| TaskID: cfg.TaskID, | ||
| ProjectID: cfg.ProjectID, | ||
| Iteration: iteration, | ||
| Role: msg.Role, | ||
| Content: msg.Content, | ||
| }) |
There was a problem hiding this comment.
Inbox messages are appended to the conversation verbatim, including their Role, ToolCallID, and ToolCalls. Since the design/docs describe inbox injection as user-role mesh messages, allowing arbitrary roles here enables accidental or malicious injection of system/assistant/tool messages (prompt injection / transcript corruption). Consider normalizing inbox messages to RoleUser (and clearing tool-related fields) or validating/allowlisting permitted roles before appending/recording.
|
@copilot apply changes based on the comments in this thread |
…fix, inbox role normalization Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/94d3766c-9e42-4886-8dd9-9cc92d2cc3e4 Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
…t to cover ToolCalls clearing Agent-Logs-Url: https://github.com/GoCodeAlone/workflow-plugin-agent/sessions/94d3766c-9e42-4886-8dd9-9cc92d2cc3e4 Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
All three review points addressed in commit
|
emitto recover from panics in external OnEvent handlers (with logging)Thinking: finalThinkingtoShouldStopearly-return