|
| 1 | +# Runtimes — Mastra |
| 2 | + |
| 3 | +Third entry on the one-capability-many-runtimes axis |
| 4 | +(`cockpit/runtimes/<runtime>/`): the same neutral `Agent` contract and the |
| 5 | +same `@threadplane/chat` UI primitives as every other AG-UI example, over a |
| 6 | +backend that is genuinely not LangGraph — and, uniquely on this axis, not |
| 7 | +Python either. |
| 8 | + |
| 9 | +## What it demonstrates |
| 10 | + |
| 11 | +| Surface | How | |
| 12 | +| --- | --- | |
| 13 | +| Messages | Streamed assistant text (`TEXT_MESSAGE_CHUNK`) from a Mastra `Agent` via the `@ag-ui/mastra` bridge. | |
| 14 | +| Tool calls | `check_conditions` executes server-side, no pause. | |
| 15 | +| Shared state | Working memory bridged honestly: the agent's `packing_list` working-memory schema streams as a `STATE_SNAPSHOT` plus real JSON-Patch `STATE_DELTA` events while the agent updates the list (measured in the spike's 04a capture) — the only runtime on this axis that emits deltas. | |
| 16 | +| Interrupts | `reserve_campsite` suspends the run via its `suspendSchema`/`resumeSchema` pair; the bridge emits a `CUSTOM on_interrupt` payload (`{ toolCallId, toolName, suspendPayload, runId }`) followed by the protocol-standard `RUN_FINISHED.outcome = { type: 'interrupt', interrupts: [...] }`. The adapter resumes with the Mastra wire shape `forwardedProps.command = { resume, interruptEvent: { toolCallId, runId } }`. | |
| 17 | +| Subagents | Not demonstrated — upstream reserves `ACTIVITY_*` events for background tasks, so delegation has no per-subagent stream (measured red in the 2026-08-31 runtime matrix). | |
| 18 | + |
| 19 | +Unlike the Python-lane runtimes, the interrupt path here does use a `CUSTOM |
| 20 | +on_interrupt` event — it is the one convention the Mastra bridge shares with |
| 21 | +the LangGraph bridge — but the run still finishes with the outcome-provenance |
| 22 | +`RUN_FINISHED` shape added in #888/#889/#891, so the reducer treats all three |
| 23 | +runtimes identically. |
| 24 | + |
| 25 | +Suspend/resume REQUIRES persistent storage: Mastra writes suspended-run |
| 26 | +snapshots to LibSQL file storage and resume loads them back, so an in-memory |
| 27 | +store would orphan every pending approval across HTTP requests. |
| 28 | + |
| 29 | +## The hosting service (Node lane) |
| 30 | + |
| 31 | +Upstream `@ag-ui/mastra` ships no plain AG-UI HTTP endpoint — only the |
| 32 | +in-process `MastraAgent` bridge and a CopilotKit runtime mount. The backend |
| 33 | +is therefore the hand-written Node service `deployments/ag-ui-mastra/`: |
| 34 | +`server.mjs` subscribes to `MastraAgent.run(input)` (the raw AG-UI event |
| 35 | +Observable) and encodes each event as one SSE `data:` frame — exactly what |
| 36 | +`@ag-ui/client`'s `HttpAgent` consumes. It mirrors the Python lane's |
| 37 | +behavior contract (`GET /ok` unauthenticated, `X-Internal-Token` on every |
| 38 | +other route, topics at `POST /agent/<topic>`, Observable errors mapped to a |
| 39 | +`RUN_ERROR` frame). The agent itself lives in `agents.mjs`, next to the |
| 40 | +shim, because there is no per-example Python module to stage into a |
| 41 | +generated deployment. This is why `cockpit/runtimes/mastra/` has no |
| 42 | +`python/` directory and its assets live in the `angular` lane. |
| 43 | + |
| 44 | +## Model client |
| 45 | + |
| 46 | +Mastra's model router resolves the plain string `openai/gpt-4o-mini` on |
| 47 | +`OPENAI_API_KEY` — no provider SDK wiring. `OPENAI_BASE_URL` is honored, |
| 48 | +which is how the aimock e2e harness intercepts model calls without a code |
| 49 | +fork. |
| 50 | + |
| 51 | +## Running locally |
| 52 | + |
| 53 | +```sh |
| 54 | +npx tsx apps/cockpit/scripts/serve-example.ts --capability=rt-mastra |
| 55 | +``` |
| 56 | + |
| 57 | +Angular dev server on :4332. The serve script only auto-starts Python |
| 58 | +backends, so start the Node service manually (see |
| 59 | +`deployments/ag-ui-mastra/README.md`): |
| 60 | + |
| 61 | +```sh |
| 62 | +cd deployments/ag-ui-mastra && npm ci |
| 63 | +AG_UI_INTERNAL_TOKEN=dev-local-token OPENAI_API_KEY=sk-... PORT=5332 node server.mjs |
| 64 | +``` |
| 65 | + |
| 66 | +The example's dev proxy rewrites `/agent` to |
| 67 | +`http://localhost:5332/agent/mastra` and injects the dev token. |
0 commit comments