Conversation
Add foundational orchestration capabilities to Agent as optional features: - Event system: subscribe()/on() for agent lifecycle events - Modes: named presets of instructions/model/tools with switchMode() - State: Zod-validated shared state with getState()/setState() These are the first step toward folding Harness capabilities into Agent, reducing the number of concepts users need to learn. All features are fully opt-in and have zero impact on existing Agent usage. Includes tests (20 passing) and reference documentation updates.
|
Cursor Agent can help with this pull request. Just |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 3988ee5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Add the orchestration loop to Agent as .send() — a higher-level method that wraps stream() with full event emission for streaming lifecycle, messages, tools, usage, and errors. - send() emits send_start/send_end, message_start/update/end, tool_start/end, tool_approval_required, usage_update, and error events - abort() cancels in-progress sends - respondToToolApproval() handles interactive tool approval flows - Expanded AgentEvent union with 12 new event types - 5 new tests (25 total passing) - Reference docs updated with send/abort/respondToToolApproval
Add five new server routes exposing Agent orchestration features: - GET /api/agents/:agentId/modes — list modes + current mode - POST /api/agents/:agentId/modes/switch — switch mode - GET /api/agents/:agentId/state — get shared state - POST /api/agents/:agentId/state — update shared state - POST /api/agents/:agentId/send — send message, stream events via SSE The /send endpoint subscribes to Agent events, calls agent.send(), and pipes all lifecycle events (message deltas, tool calls, usage, errors) to the client as Server-Sent Events. Includes Zod schemas, route registration, and documentation updates.
Each agent.send() call now returns a SendOperation with its own isolated:
- events: AsyncIterable<AgentEvent> (per-operation event stream)
- result: Promise<{ message }> (final message)
- abort(): cancel this specific operation
- respondToToolApproval(): handle approvals for this operation
This fixes the concurrency issue where multiple callers sharing the same
Agent singleton would stomp on each other's abort controllers, event
listeners, and pending approval state.
Events still bubble up to global agent.subscribe() listeners, but each
SendOperation's event stream only contains events from that operation.
The server /send route now pipes op.events directly instead of relying
on the global subscribe, making concurrent requests safe.
New test: concurrent sends have isolated event streams (27 total passing).
agent.send() now accepts an optional modeId parameter that selects the mode for that specific operation. In multi-user server contexts, each request passes its own modeId — switching modes never affects other concurrent requests. For single-user TUI contexts (like MastraCode), switchMode() still works as a convenience that sets the instance-level default. Server changes: - POST /send now accepts modeId in the request body - Removed POST /modes/switch and POST /state routes (singleton-unsafe) - Kept GET /modes and GET /state (read-only, safe for shared access)
The Agent is a singleton. Session-scoped state (modes, state) cannot
live on it without causing cross-user interference. This refactor:
- Adds `harness: { modes, stateSchema }` config key to Agent
- Removes singleton-mutating methods: switchMode(), getCurrentMode(),
getCurrentModeId(), getState(), setState()
- Keeps read-only config accessors: listModes(), hasModes(),
getDefaultMode(), getStateSchema()
- .send() accepts per-operation modeId and state, validated against
harness config
- Server: removes /modes/switch and /state routes entirely
- Server: /modes GET returns available modes from harness config
- Server: /send accepts modeId and state in request body
The harness config defines what's AVAILABLE. The caller owns session
state and passes it per-operation. This is safe for both single-user
TUI and multi-user server contexts.
Description
This PR initiates the refactoring effort to integrate the capabilities of the
Harnessprimitive directly into theAgentclass. The primary goal is to reduce cognitive overhead and simplify the architecture by makingAgenta more comprehensive and flexible primitive for building interactive agent applications, thereby eliminating the need for a separate, application-level orchestration layer.This first phase introduces three foundational, optional features to the
Agentclass:subscribe(),on(), andemitEvent()methods for internal and external event handling.instructions,model,tools) within a singleAgentinstance.getState()andsetState().These changes lay the groundwork for future phases to migrate more
Harnessfunctionality intoAgentand eventually deprecateHarness.Related Issue(s)
Type of Change
Checklist