[pull] main from CopilotKit:main - #427
Merged
Merged
Conversation
…tool-based HITL - Rewrite interrupt-flow.mdx to explicitly state Mastra lacks native interrupt support - Add clear warning callout and comparison table showing tool-based as the working approach - Update index.mdx to mark interrupt-based as 'Not Supported' and tool-based as 'Supported' - Provide concrete migration path with useHumanInTheLoop examples - Fixes FAC-64: prevents users from following broken interrupt examples
…target the real agent (#5000) `useAgent` always returns a fully-constructed `AbstractAgent`: a provisional stand-in while the runtime is still connecting (or in an error state), swapped for the real agent once the `/info` sync resolves. The returned type claimed `agent` was always the real agent, giving consumers no way to tell the two apart — so one-time subscriptions (e.g. `onRunFinalized`) registered during the provisional window landed on the placeholder and missed events until the effect re-ran after the swap. Add an `isReady` flag to the return value: `false` while the agent is provisional, `true` once the real (or locally-registered) agent is bound. Additive and backward compatible. Also fix the docs' "Event Subscription" example, which used an empty `useEffect` dependency array and therefore never re-subscribed when the agent reference changed. Note: the original crash from #5000 ("Cannot read properties of undefined (reading 'subscribers')") no longer reproduces on `main` — the provisional-agent work (#5533/#5635) guarantees a fully-constructed agent, so `subscribe()` is always safe. The added tests lock in that no-crash behavior and cover the new `isReady` transition. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ash) The agno starter's Dockerfile installs Python deps via 'uv pip install --system -e .', resolving from pyproject.toml (not uv.lock). agno>=1.7.8 now resolves to 2.7.4, which dropped python-multipart as a hard dependency. AgentOS registers a FastAPI form-data route, so the app raised 'RuntimeError: Form data requires python-multipart' at import time, crash-looping starter-agno. Pin python-multipart>=0.0.20 directly so it installs regardless of agno's transitive deps.
…tool-based HITL (#5895) ## Summary Fixes FAC-64: Mastra Interrupts docs example fails on missing agentId and suspendPayload guard This PR rewrites the Mastra interrupt documentation to correctly reflect that **Mastra does not support native interrupt flow**. The framework lacks LangGraph-style `interrupt()` primitives and does not emit AG-UI interrupt events. ## Changes ### 📝 Documentation Updates 1. **`interrupt-flow.mdx`**: Completely rewritten to: - Add prominent warning callout that Mastra doesn't support interrupts - Explain why the interrupt pattern doesn't work with Mastra - Provide working alternative using `useHumanInTheLoop` - Include comparison table between interrupt-based and tool-based approaches - Redirect users to the tool-based HITL guide 2. **`index.mdx`**: Updated to: - Mark interrupt-based approach as "Not Supported" - Mark tool-based approach as "Supported" (the working pattern) - Reorder cards to prioritize the working approach ## Rationale The original docs documented `useInterrupt` with examples that would: - Fail with "Agent 'default' not found" (missing `agentId` parameter) - Fail with "Cannot destructure property 'action'" (incorrect payload access) - Silently fail (hook listens for events Mastra never emits) Research revealed: - `showcase/integrations/mastra/manifest.yaml` explicitly lists `gen-ui-interrupt` under `not_supported_features` - The actual working demo uses `useHumanInTheLoop`, not `useInterrupt` - Comments in the code confirm: "This framework has no LangGraph-style `interrupt()` primitive" ## Migration Path Users following the old docs can now: 1. See clear warning that interrupts aren't supported 2. Learn the correct `useHumanInTheLoop` pattern 3. Follow link to complete tool-based HITL guide with working examples ## Testing - ✅ Documentation changes only (no runtime code affected) - ✅ Verified redirect links work correctly - ✅ Checked against actual working implementation in `showcase/integrations/mastra/src/app/demos/gen-ui-interrupt/page.tsx` ## Related - Linear: FAC-64 - QA Report: Documented three specific runtime errors from the broken examples - Research: Identified Option B (rewrite for useHumanInTheLoop) as the correct approach
…ess/harness-workers) The aimock-wiring drift probe excluded `harness` and `harness-workers`, so when their aimock pointers drifted to the billed PUBLIC `*.up.railway.app` host instead of `showcase-aimock.railway.internal:4010` the probe never flagged it (~$657/mo egress on staging). Un-exclude the harness fleet and verify it as aimock consumers via a new AIMOCK_CONSUMER_SERVICES set. Consumers use HARNESS_FLEET_CANDIDATE_ENV_VARS (the standard OPENAI/ANTHROPIC/GEMINI candidates plus AIMOCK_URL), because `harness` exposes ONLY AIMOCK_URL as its aimock pointer. AIMOCK_URL is scoped to the harness-fleet path only, so a regular backend's stray AIMOCK_URL can't mask a missing real base-URL pointer. Pure-infra services (aimock/shell/ dashboard/docs/dojo/pocketbase/webhooks) stay excluded.
…ash) (#6061) ## The crash `starter-agno` (Railway service, built from `examples/integrations/agno`) was crash-looping at import time: ``` RuntimeError: Form data requires "python-multipart" to be installed. fastapi/dependencies/utils.py ensure_multipart_is_installed() agno/os/routers/agents/router.py get_agent_router(...) agno/os/app.py _add_built_in_routes(...) ``` The app dies during startup import — before any network/LLM activity. ## Root cause The starter's top-level `Dockerfile` installs Python deps with: ```dockerfile RUN cd agent && uv pip install --system -e . ``` `uv pip install -e .` resolves from `pyproject.toml` and **does not consult `uv.lock`**. The pyproject pinned `agno>=1.7.8`, which now resolves to **agno 2.7.4**. agno **dropped `python-multipart` as a hard dependency** in the 2.7.x line (it was still a hard dep in the locked 2.3.3, and in 2.6.19). agno's `AgentOS` registers a FastAPI form-data route, and FastAPI's `ensure_multipart_is_installed()` raises at route-registration (import) time when the package is absent — so the agent never starts. The pinned `uv.lock` (agno 2.3.3) *did* carry `python-multipart` transitively, which is why the lockfile looked fine while the deployed image (which bypasses the lock) broke. ## Fix Add `python-multipart>=0.0.20` directly to the starter agent's `pyproject.toml` dependencies (and refresh `uv.lock`) so it installs regardless of agno's transitive-dependency changes. ## Red → Green proof Reproduced against the **real deployed install path** (`uv pip install -e .` from pyproject, lock bypassed) in a clean Python 3.12 venv. ### RED (main, before fix) ``` agno resolved: 2.7.4 fastapi: 0.139.2 python-multipart: ABSENT $ python -c "import main" RuntimeError: Form data requires "python-multipart" to be installed. You can install "python-multipart" with: pip install python-multipart ``` ### GREEN (after fix) ``` agno resolved: 2.7.4 fastapi: 0.139.2 python-multipart: 0.0.32 (now installed) $ python -c "import main" IMPORT OK - AgentOS app built, no RuntimeError $ uvicorn main:app ... INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8123 $ curl /health -> {"status":"ok",...} ``` ## Docker build (local, real starter image) Built the actual starter image from the top-level `Dockerfile` (the deployed path using `uv pip install --system -e .`) with the local BuildKit builder, then ran it: ``` docker buildx build -f Dockerfile -t agno-fix-test . -> EXIT 0 docker run ... agno-fix-test agent /health -> 200 {"status":"ok",...} agent log: "Application startup complete." / "Uvicorn running on http://0.0.0.0:8000" RuntimeError count in container logs: 0 in-image check: python-multipart present: 0.0.32 ``` ## Other agno variants `showcase/integrations/agno` uses a **separate** dep file (`requirements.txt`, not this pyproject/uv.lock) and pins `agno==2.6.19`, which still carries `python-multipart` transitively — so it is **not currently broken**, but it is fragile (a bump past 2.7.x would hit the same crash). No shared dep file, so it is intentionally out of scope here; recommend a follow-up to add an explicit `python-multipart` pin there too.
…ess/harness-workers) (#6062) ## The incident this prevents The showcase pays egress whenever a service reaches aimock over the PUBLIC `*.up.railway.app` host instead of the free `showcase-aimock.railway.internal:4010`. On STAGING, `harness-workers` (the 6-replica probe fleet) had `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` / `AIMOCK_URL` pointing at PUBLIC aimock, and `harness` had a public `AIMOCK_URL` — together ~$657/mo of egress. The aimock-wiring drift probe **never flagged this** because both services were in `EXCLUDE_SERVICES`. The live vars are already fixed; this makes the probe cover the class so it can't silently regress. ## Design choice + justification Two facts constrained the fix: 1. `harness` / `harness-workers` were excluded (`EXCLUDE_SERVICES`) → skipped entirely, so a naive fix must un-exclude them. 2. `harness` exposes its aimock pointer **only** as `AIMOCK_URL`, which is **not** in `CANDIDATE_ENV_VARS` (`OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` / `GOOGLE_GEMINI_BASE_URL`). So merely un-excluding `harness` would leave it all-missing → unwired forever, even when correctly wired. Chosen approach — a dedicated **aimock-consumer** class: - Remove `harness` / `harness-workers` from `EXCLUDE_SERVICES`. - Add `AIMOCK_CONSUMER_SERVICES = { harness, harness-workers }` + `isAimockConsumer(name)` (mirrors `isExcluded`: matches bare and legacy `showcase-`-prefixed forms). - Add `HARNESS_FLEET_CANDIDATE_ENV_VARS = [...CANDIDATE_ENV_VARS, "AIMOCK_URL"]`; `pointsAtAimock` takes a `candidateVars` param (defaults to the standard set). In the run loop, consumers use the extended set, everything else the standard set. This is the minimal correct surface: it catches `harness` via `AIMOCK_URL`, catches `harness-workers` via any of OPENAI/ANTHROPIC/AIMOCK_URL, and leaves the verdict precedence (match > confirmed-mismatch > sealed > missing) untouched. ### Why `AIMOCK_URL` is scoped to the harness-fleet path (and safe) Adding `AIMOCK_URL` to the **global** candidate set is not safe: a regular demo backend that happens to expose `AIMOCK_URL` (pointed anywhere) could then count as "wired" and **mask a missing real `OPENAI_BASE_URL`/etc pointer**, hiding genuine drift. Scoping `AIMOCK_URL` to `HARNESS_FLEET_CANDIDATE_ENV_VARS` means only the two harness-fleet services consult it. A regression guard test (`does NOT consult AIMOCK_URL for non-harness services`) locks this in. Pure-infra services with no aimock pointer (aimock/shell/dashboard/docs/dojo/pocketbase/webhooks) stay excluded and never go red. ## Red → Green proof Tests added in `aimock-wiring.test.ts`. RED was captured against the **unchanged** probe (new tests only, source untouched); GREEN after the fix + updating the 4 existing tests that asserted the old harness-excluded behavior. **RED** (new behavior tests fail on current code — harness fleet excluded, so the incident is not flagged): ``` FAIL aimock-wiring.test.ts > flags the harness fleet when its aimock pointers are on the PUBLIC host (egress drift) AssertionError: expected 'green' to be 'red' FAIL aimock-wiring.test.ts > greens the harness fleet when its aimock pointers are on the PRIVATE internal host AssertionError: expected [] to deeply equal [ 'harness', 'harness-workers' ] FAIL aimock-wiring.test.ts > verifies `harness` via its only aimock pointer, AIMOCK_URL AssertionError: expected 'green' to be 'red' Test Files 1 failed (1) Tests 3 failed | 44 passed (47) ``` **GREEN** (after the fix): ``` Test Files 1 passed (1) Tests 47 passed (47) ``` Test coverage added: - `flags the harness fleet when its aimock pointers are on the PUBLIC host (egress drift)` — the exact incident → red. - `greens the harness fleet when its aimock pointers are on the PRIVATE internal host` — positive path → wired/green. - `verifies harness via its only aimock pointer, AIMOCK_URL` — public→red, internal→green (locks the `AIMOCK_URL`-candidate path). - `does NOT flag pure-infra services with no aimock pointer` — guard: shell/dashboard/etc stay excluded. - `does NOT consult AIMOCK_URL for non-harness services` — guard: `AIMOCK_URL` is not global. ## Local quality - oxfmt (formatter) — clean on both files - oxlint — 0 warnings, 0 errors - `tsc --noEmit` (typecheck) — clean - `tsc -p tsconfig.build.json` (build) — clean - Full harness suite: only the pre-existing unrelated failures remain (`d0-gone-predicate.test.ts`, `d5-mapping-drift.test.ts`, and `cvdiag/staged-ts-scrub-parity.test.ts`) — all confirmed failing identically on the untouched baseline (verified via `git stash`). This PR adds **no** new failures. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QCLub2Vb5Y56cPttSzkip1
…iptions (#5000) (#6041) ## Summary Closes #5000. `useAgent` (v2) always returns a **fully-constructed** `AbstractAgent` — a *provisional* stand-in while the runtime is still connecting (or in an error state), swapped for the real agent once the `/info` sync resolves. The return type claimed `agent` was always the real agent, so consumers had **no way to tell the provisional instance from the real one**. One-time subscriptions registered during the provisional window (e.g. `onRunFinalized`) landed on the placeholder and missed events until the effect re-ran after the swap. This PR adds an **`isReady`** flag to the return value: - `false` — `agent` is provisional (runtime connecting / error) - `true` — `agent` is the real, runtime-synced (or locally-registered) instance This is exactly the API the issue requests in its *Expected Behavior*. It is **additive and backward compatible** — existing `const { agent } = useAgent()` callers are unaffected. ```tsx const { agent, isReady } = useAgent({ agentId }); useEffect(() => { if (!isReady) return; // only subscribe once the real agent is bound const sub = agent.subscribe({ onRunFinalized: (p) => console.log(p) }); return () => sub.unsubscribe(); }, [agent, isReady]); ``` ## On the original crash The crash reported in #5000 — `Cannot read properties of undefined (reading 'subscribers')` at `AbstractAgent.subscribe` — **no longer reproduces on `main`**. The provisional-agent work landed for #5533 / #5635 now guarantees `useAgent` always returns a fully-constructed `AbstractAgent`, so `subscribe()` is always safe to call. The added tests lock in that no-crash behavior. What remained unaddressed was the missing readiness signal, which this PR provides. ## Changes - **`packages/react-core/src/v2/hooks/use-agent.tsx`** — `useMemo` now returns `{ agent, isReady }`; real agent → `isReady: true`, provisional paths → `isReady: false`. Documented with JSDoc. - **`use-agent-subscribe-ready.test.tsx`** (new) — regression + behavior coverage: `subscribe()` does not throw while connecting (effect + during-render), `isReady` transitions `false → true` on sync and swaps the instance, local agent is ready immediately. - **`showcase/shell-docs/.../hooks/useAgent.mdx`** — signature + return-value docs updated; the *Event Subscription* example fixed (it used an empty `useEffect` dep array and never re-subscribed when the agent reference changed). ## Testing - New test file: 4/4 pass. - Full `react-core` v2 hooks suite: **35 files / 299 tests pass** (the `useMemo` return-shape change breaks nothing). - `tsc --noEmit` clean. ## Notes - Scope is React only, matching the issue. The Vue `useAgent` (`packages/vue`) is structured differently (reactive `shallowRef`, `agent` can be `null`); happy to add matching `isReady` as a follow-up if maintainers want cross-framework parity. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )