mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
v1.70.3
16126 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fb4f352032 |
chore: release monorepo v1.70.3 (#6960)
## Release monorepo v1.70.3 **Scope:** `monorepo` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `monorepo` packages to `1.70.3` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `monorepo` packages to npm at version `1.70.3` - Creates git tag `monorepo/v1.70.3` - Creates a GitHub Release with the final release notes ### Before merging - [ ] CI is green (tests, lint, types, build) - [ ] Version bumps look correct - [ ] Release notes are accurate (edit in Notion if a draft was created) --- > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR.v1.70.3 |
||
|
|
31d0cda168 |
fix(a2ui): report a generative-UI result that renders nothing (closes OSS-1048) (#6802)
A generative-UI result that does not paint reports nothing today. The turn finishes, the input returns to idle, the network calls are all 200, and the console is byte-identical to what it held before the request. The only signal is a human noticing a blank space in a screenshot. Three commits, each breaking one of those silences. Rendering behavior is unchanged throughout — every report is a development-only `console.warn`. ## A tool call with no renderer `use-render-tool-call.tsx` resolves a renderer by name, then by agentId, then by wildcard, then returns `null`. The existing comment defends that choice well: auto-painting a default card would leak internal tool names plus raw args and result JSON into every app's production chat. That argument is about *painting*, and it does not cover *warning*. The warning names the tool the agent called and lists the renderer names that are registered. When the cause is a name that does not match, that is the whole diagnosis. It is deferred one task past the commit that recorded the miss, then re-checks the registry. `useRenderTool` registers from an effect in the component that renders the chat, and React runs child effects before parent ones, so at effect time the resolver can see an empty registry even though the app did register a renderer. Removing that re-check makes two of the new tests fail on exactly that false positive. ## An A2UI surface that gets operations and paints nothing Two reports, both in `A2UIMessageRenderer.tsx`. **Operations arrived and no paint followed.** The renderer already waits 8s for a surface to report its first paint before dropping the loader, so reaching that fallback is itself the signal. No new threshold was invented. `surfaceHasRenderableContent` already knows which half is missing, so the message says which: no `updateComponents` at all, or `"path"`-bound components whose `updateDataModel` never carried a value. **Operations named a surface that was never created.** `A2UIRenderer` renders its `fallback` for an unknown surface id and that defaults to `null` — the card is absent and the log is empty. `processMessages` is synchronous, so a surface still missing after it was never created. This report is also deferred and re-checked, because operations stream and a snapshot can reach the processor before the `createSurface` that gives it somewhere to go. ## The two surface-id resolvers disagreed React read a top-level `operation.surfaceId` first and only then the nested v0.9 keys. The web-components path read the nested keys only, via `normalizeOperations`, and never looked at a top-level id at all. One payload grouped under its own id in React and under `"default"` in the Lit and Angular renderers. Nested wins in both now, with a top-level id as the fallback when the payload carries none. Nested is the correct half of that choice, not a coin toss: `MessageProcessor` creates the surface from the nested id. Grouping by a top-level id files the operations against a surface `createSurface` never made, and an unknown surface id renders the `null` fallback above. So the old React order could *produce* the silence the second commit teaches the renderer to report — which is what the new test asserts, by requiring that the missing-surface warning stay quiet. ## What this does not cover A surface that exists, holds complete components, and still draws nothing. `onReady` fires exactly when `surfaceHasRenderableContent` is true, so that case is invisible to the paint-fallback path by construction. Filed as OSS-1057 with a concrete mechanism: both renderers hard-code a root component id of `root`, and a components list without one shimmers forever. The other item on OSS-1048 — a turn whose only output is generative UI recording a `tool` message with no assistant parent — is a different repo and a real design decision. Filed as OSS-1056. ## Verification `react-core` 1565/1565, `a2ui-renderer` 24/24, both builds clean, lefthook green on all three commits. Every new negative assertion was mutation-tested against the pre-change code. Two of them are guarded twice over, and removing either guard alone left the test green — so both had to be removed before the test would fail, which is what confirms it is not vacuous. Closes OSS-1048. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Operations now consistently target the correct surface, prioritizing nested surface identifiers and falling back to top-level identifiers when needed. * Improved handling of operations for surfaces that are created later. * **Diagnostics** * Added development-time warnings when surfaces receive operations but render nothing. * Added warnings for operations targeting missing surfaces or unresolved root components, including likely causes. * Added warnings when tool calls have no matching renderer, with registered renderer details where available. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
69a940c70e | chore: release monorepo v1.70.3 | ||
|
|
30f67b18f1 |
fix(inspector): enable Learning without flags and correct status (#6957)
## Problem Inspector Learning required both runtime debug mode and a separate handler opt-in, while Threads did not. The Home and launcher Learning indicators also read Memory availability, so configured Learning could remain off. Enabled launcher toggles were purple rather than green |
||
|
|
fdb6ce0714 | fix(inspector): require Learning container configuration for status | ||
|
|
1b2d8eca85 | fix(runtime): sync public API manifest after removing Learning flag | ||
|
|
78e498bf33 | fix(inspector): derive green Learning status from its own endpoint | ||
|
|
290a8323ae | fix(runtime): expose Inspector Learning without extra flags | ||
|
|
b0f349fcfb |
fix(a2ui): report a surface whose root component never resolves (closes OSS-1057)
Both renderers begin walking a surface at the component with id "root" and treat an id they cannot find as not arrived yet, painting an animated placeholder. That is right while operations stream. Once operations have stopped it is not waiting, it is stuck — and every existing check calls it healthy: the surface exists, processMessages does not throw, the component type is never reached so the "Unknown component" branch cannot fire, and surfaceHasRenderableContent says yes on the strength of components plus a non-empty data model, so onReady fires and the never-painted report is suppressed by its own guard. A complete, accepted payload therefore animates a grey box forever with nothing in the console. Reports it on the existing paint deadline, measured from the last operations to land, so a root still missing when it expires is a root that is not coming. The check reads the live components model rather than scanning the operations for the id, which covers every way a root can fail to resolve — not only a payload that never named one — and the message says which of the two it is. Keeps the fixed root id: A2UI v0.9 dropped v0.8's rootComponentId, and createSurface carries only surfaceId, catalogId and theme, so a payload has no way to declare its own entry point. Deriving one instead would pick silently and wrongly whenever several components are unreferenced. The id moves to a single ROOT_COMPONENT_ID constant so the three sites that hard-coded the string, and the new report, agree by construction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
77dc28ea2c |
fix(a2ui): resolve a surface id the same way in both renderer paths (refs OSS-1048)
The two paths disagreed. React read a top-level `operation.surfaceId` first and only then the nested v0.9 keys. The web-components path read the nested keys only, via normalizeOperations, and never looked at a top-level id at all. So one payload grouped under its own id in React and under "default" in the Lit and Angular renderers. Nested wins in both now, with a top-level id as the fallback when the payload carries none. Nested is the correct half of that choice, not a coin toss: MessageProcessor creates the surface from the nested id. Grouping by a top-level id instead files the operations against a surface that createSurface never made, and an unknown surface id renders A2UIRenderer's null fallback. That is a card that paints nothing, which is what the previous commit taught the renderer to report. So the old React order could produce the silence, and the missing-surface report is what the new test uses to prove the grouping agrees with what got created. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0f7ee3501a |
fix(react-core): report an A2UI card that receives operations and paints nothing (refs OSS-1048)
Two ways an A2UI surface can render nothing without saying a word. The operations arrive and no paint follows. The renderer already waits 8s for the surface to report its first paint before dropping the loader, so reaching that fallback is itself the signal that nothing painted. Report it there, and use what surfaceHasRenderableContent already knows to say which half is missing: no updateComponents at all, or bound components whose updateDataModel never carried a value. The operations name a surface that was never created. A2UIRenderer renders its fallback for an unknown surface id and that defaults to null, so the card is absent and the log is empty. processMessages is synchronous, so a surface still missing after it was never created. The second report is deferred a task and re-checked, because operations stream and a snapshot can reach the processor before the createSurface that gives it somewhere to go. Removing both the deferral and the re-check makes the mid-stream test fail. Neither report covers a surface that exists and holds complete components and still draws nothing. That case needs the component catalog, which lives in @a2ui/web_core, and it stays silent for now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
22fa691e80 |
fix(react-core): warn when a tool call has no renderer instead of rendering nothing (refs OSS-1048)
A tool call that matches no registered renderer returns null. Nothing else happens: no console output, no empty-state card, and a finished turn. The only signal is a blank message container in the chat, which a developer has to notice in the DOM and then guess at. Report it in development. The warning names the tool the agent called, lists the renderer names that are registered, and points at useRenderTool and useDefaultRenderTool. When the cause is a name that does not match, that is the whole diagnosis. Rendering behavior is unchanged. Auto-painting a default card would leak tool names and raw args into production chat, which is why the resolver returns null, and that decision stands. The report is deferred one task past the commit that recorded the miss, then re-checks the registry. useRenderTool registers from an effect in the component that renders the chat, and React runs child effects before parent ones, so at effect time the resolver can see an empty registry even though the app did register a renderer. Removing that re-check makes two of the new tests fail on exactly that false positive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
313a454b7e |
fix(react-core): wait for the user on humanInTheLoop provider tools (#6851)
## Summary A human-in-the-loop tool declared through the `humanInTheLoop` **prop** on `<CopilotKitProvider>` never waited for the user. The handler resolved `undefined` the moment the agent invoked it, and the renderer was registered unwrapped, so it never received a working `respond`. The card appeared once at `Complete` over dead controls while the agent was told the tool had succeeded. This is not a regression. The placeholder handler is unchanged since the first v2 provider commit (`a2ef51aaf3`, #2794), so this documented prop has never waited. The reference page describes it as self-sufficient: "Declarative human-in-the-loop tool definitions. Each becomes both a tool handler and a tool call renderer." The `useHumanInTheLoop` hook path is unaffected and has always worked. ## Changes `packages/react-core/src/v2/providers/CopilotKitProvider.tsx`, matching `useHumanInTheLoop`: - park the tool call until the render calls `respond`, keyed by tool call id so one call of a tool cannot resolve another - wrap the registered renderer so it receives the full prop contract, with `respond` live only while the status is `Executing` - reject with `Human-in-the-loop interaction aborted` on abort, so core records an explicit error tool result rather than an empty success (#5554) - keep the invoked tool's name for a wildcard (`"*"`) registration instead of overwriting it with `"*"` Two existing unit tests asserted the placeholder behavior (the `console.warn`, and render identity). They now assert the waiting contract and that the wrapper renders the caller's component. ## Testing **New: `CopilotKitProvider.hitl.e2e.test.tsx` (4 tests).** These mount the real pipeline — `CopilotChat` -> tool calls view -> the provider's registered renderer — rather than calling the renderer directly, so a mismatch between the key the handler parks under and the tool call id the pipeline supplies cannot pass. All four fail on `main` for the right reason (`Unable to find an element by: [data-testid="hitl-respond"]` — the respond button never appears), and pass with the change. Each mechanism was mutation-checked separately: | Mutation | Result | | -- | -- | | handler resolves immediately (the old placeholder) | all 4 fail | | renderer registered unwrapped | all 4 fail | | wildcard branch removed | only the wildcard test fails | | park under the tool name instead of the tool call id | the 2 tests needing a live `respond` fail | **Suite, types, format** ``` $ node ../../node_modules/vitest/vitest.mjs run src/v2/providers/__tests__/ Test Files 17 passed (17) Tests 173 passed (173) $ tsc --noEmit (clean) ``` `oxfmt` applied to every changed file. The full `react-core` run also has 7 failures across `CopilotChatView.pinToSend.test.tsx` and `use-pin-to-send.test.tsx` (`ResizeObserver is not a constructor`). Those are pre-existing: I removed this change from the working tree and ran those two files on clean `main`, and they fail identically. **Live, in `examples/v2/react/demo` against a real agent and a rebuilt dist** | Page | Before | After | | -- | -- | -- | | provider prop, multi-route REST | card at `complete`, `respond` undefined, console warned, agent said "Please pick a time slot in the scheduler UI to confirm" over disabled buttons | parks at `executing` with live buttons for 17s, then "Booked a call with Sam about the Waterfall project meeting for Tue 14:00" | | provider prop, single route | same, and worse: "…has been scheduled" before anyone approved | parks, then reports the slot actually chosen (Wed 09:30) | | `useHumanInTheLoop` hook, multi-route REST | already correct | unchanged: `inProgress` -> `executing` -> `complete` with the chosen slot reaching the agent | ## Credit @Tushar-Khandelwal-2004 diagnosed this path and implemented the parking handler and the abort semantics first, in #4955. That PR predates the wildcard-name work and the current `main`, and its review asks were still open, so this lands the same fix rebased on `main` with the wildcard branch and pipeline-level tests added. #4955 is now closed in favor of this PR, with the credit staying with them. ## Related - #4955 — the earlier implementation of this fix (closed in favor of this PR) - #6527 — the identical Vue defect; the two changes agree on semantics - #4953 — the reporter hit the hook path, which does not reproduce on `main`; this is the confirmed provider-prop half of that report Angular already implements this path (`addHumanInTheLoop` -> `#hitl.onResult`), so React was the last framework missing it. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Human-in-the-loop tool calls now pause until a user responds. * Interactive renderers receive tool metadata and a callback for submitting responses. * Wildcard tools display the specific tool name that was invoked. * Aborted tool calls now return a clear error result. * **Bug Fixes** * Multiple pending tool calls can be resolved independently by their call ID. * Interactive handlers no longer emit an unnecessary warning while awaiting a response. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ef2f819a46 | docs(learning): explain Inspector setup and platform requirements | ||
|
|
2031643fa1 |
fix(react-core): wait for the user on humanInTheLoop provider tools
The `humanInTheLoop` prop on `CopilotKitProvider` registered a handler that
warned and resolved `undefined` the moment the agent invoked it, and it
registered the renderer unwrapped, so the render never received a working
`respond`. A tool declared that way jumped straight to Complete over dead
controls while the agent was told the tool had succeeded. The placeholder is
unchanged since the first v2 provider commit (
|
||
|
|
407249d4fc |
fix(react-core): let a custom catch-all renderer return null (#6942)
## Summary `useDefaultRenderTool`'s `render` was typed to return `React.ReactElement`. A caller who wants to render only *some* tool calls therefore could not return `null` to suppress the built-in default for the rest — the value flowed through correctly at runtime, but the type rejected it. This widens the public `render` return type, and the wrapper local that carries the user's value, to `React.ReactElement | null`. ```diff - render?: (props: DefaultRenderProps) => React.ReactElement; + render?: (props: DefaultRenderProps) => React.ReactElement | null; ``` The reference page hand-writes the same signature, so it is updated to match, with one behavior bullet describing what `null` does. ## Scope, and its relationship to #6533 #6533 already widens the same return type to `React.ReactElement | null` in `defineToolCallRenderer.ts` and `use-render-tool.tsx`. It does **not** touch `use-default-render-tool.tsx`, which is the remaining gap and the whole of this PR. There is **no file overlap**, so the two can land in either order. A structural sweep of `react-core/src/v2` for renders still typed `=> React.ReactElement` with no `| null` confirms this leaves nothing behind on this surface: ``` types/defineToolCallRenderer.ts:40,48,56 <- #6533 hooks/use-render-tool.tsx:41,72,109 <- #6533 hooks/use-default-render-tool.tsx:152 <- the deliberate bridge cast, below hooks/use-interrupt.tsx:89 <- different surface, out of scope components/chat/CopilotChatMessageView.tsx:418 <- different surface, out of scope ``` The `as unknown as` cast into `useRenderTool` is deliberately left in place: `useRenderTool` still requires a `ReactElement` return on `main` (verified again after the rebase — `use-render-tool.tsx:41`). Once #6533 lands, that cast can be tightened. The bridge comment is updated to say so. `DefaultToolCallRenderer`'s own return type stays `React.ReactElement` — the built-in default always renders an element. The Vue counterpart needs no equivalent change: its `render` already returns `VNodeChild`, which admits `null`, and `reference/vue/hooks/useDefaultRenderTool.mdx` already matches. ## Why the guard is a type test, not a runtime test TypeScript types are erased, so a `null`-returning render forwards identically before and after the widening. The runtime test passes against un-widened source, which makes it worthless as a guard for this change. So the real guard is `use-default-render-tool-types.test-d.ts`, using the `expectTypeOf` + `toEqualTypeOf` convention already documented in `v2/__tests__/headless-type-exports.test-d.ts`. `toEqualTypeOf` is required rather than assignability: a function returning `ReactElement` **is** assignable to one returning `ReactElement | null`, so an assignability check would pass against the un-widened type and assert nothing. The `.test-d.ts` basename is outside vitest's `include` globs, so nothing there executes; `tsc --noEmit` (`check-types`) is what reads it. Confirmed on this base: ``` $ vitest list --filesOnly | grep -c "test-d" 0 $ grep -n include -A4 packages/react-core/vitest.config.mjs include: [ "src/**/__tests__/**/*.{test,spec}.{ts,tsx}", "src/**/*.{test,spec}.{ts,tsx}", ], $ grep include packages/react-core/tsconfig.json "include": ["src/**/*"], ``` The runtime test is kept as well, since it still covers prop adaptation and forwarding. ## Testing All numbers below were re-measured after the rebase onto `main` (`42494df`). **Mutation check of the type guard** — revert the widening in the source, confirm the guard goes red: ``` ########## RUN A: rebased HEAD as-is ########## total errors: 63 --- errors in touched files --- none ########## RUN B: MUTATION - widening reverted in source ########## total errors: 65 --- guard file errors (expect FAIL) --- use-default-render-tool-types.test-d.ts(26,3): error TS2344: Type '((props: DefaultRenderProps) => ReactElement<...> | null) | undefined' does not satisfy the constraint '"Expected: undefined, Actual: never" | "Expected: function, Actual: never"'. use-default-render-tool.test.tsx(150,30): error TS2322: Type 'Mock<({ status }: DefaultRenderProps) => null>' is not assignable to type '(props: DefaultRenderProps) => ReactElement<...>'. Type 'null' is not assignable to type 'ReactElement<...>'. ``` The guard fails when the widening is reverted, and the two new errors are exactly the guard plus the runtime test's own use of it. Nothing else moves. **Typecheck** (`tsc -p packages/react-core --noEmit`) — error set byte-identical to pristine `origin/main` in the same worktree, none in the touched files: ``` ########## RUN C: pristine origin/main baseline ########## total errors on pristine main: 63 === diff: pristine-main errors vs HEAD errors === IDENTICAL -> the change introduces no new type errors ``` The 63 are pre-existing worktree noise: `react-core` resolves `@copilotkit/core` and `@copilotkit/shared` from a sibling checkout's `dist`, so unrelated exports read as missing. They are present on pristine `origin/main` in the same worktree, which is what the diff above shows. **Target test file:** ``` ✓ src/v2/hooks/__tests__/use-default-render-tool.test.tsx (13 tests) 38ms Test Files 1 passed (1) Tests 13 passed (13) ``` **Broader `src/v2/hooks` + `src/v2/types`** — failure counts identical to pristine `origin/main` in the same worktree, plus exactly the one new passing test: ``` === BASELINE (pristine origin/main in this worktree) === Test Files 22 failed | 17 passed (39) Tests 9 failed | 201 passed (210) === WITH my change === Test Files 22 failed | 17 passed (39) Tests 9 failed | 202 passed (211) ``` **Lint:** `oxlint packages/react-core/src/v2/hooks/` — `Found 62 warnings and 0 errors.` (all pre-existing exhaustive-deps warnings, none in the touched files). **Formatting:** `oxfmt --check` on the three source/test files — `All matched files use the correct format.` `oxfmt` does not process `.mdx`, so the reference page is out of its scope. **Public-API manifest:** no regeneration needed — `scripts/release/public-api/manifest.v1.json` records no type signatures and does not mention `useDefaultRenderTool` (`grep -c ReactElement` → `0`). ## Provenance Extracted from #5509 (@ataibarkai), which is 3915 commits behind `main` and being closed. That PR also changed `defineToolCallRenderer`'s schema default from `def.name === "*" && !def.args ? z.any() : def.args` to `def.args ?? z.any()`. **That change is deliberately not carried here** — it alters runtime behavior for named renderers declared without `args` (from `args: undefined` to `args: z.any()`) and deserves its own PR and its own verification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Custom renderers can now return `null` to suppress output when no UI should be displayed. * **Documentation** * Updated `useDefaultRenderTool` guidance to describe null-return behavior and selectively rendering tool calls. * **Tests** * Added coverage confirming null-render behavior and forwarded renderer properties. * Added compile-time validation for supported renderer return types. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ca00b1b12e |
fix(runtime): do not treat an unread stream as a pre-parsed body in the express bridge (#6490)
Follow-up to #6489. Same root cause — flags that report "the socket has the bytes" being read as "the application consumed the stream" — in a second, independent code path found while auditing for other copies of the check. Different trigger and different file, so it ships separately. **#6489 has since merged**, so this is the remaining half. ## Problem `hasPreParsedBody` in `packages/runtime/src/v2/runtime/endpoints/express-fetch-bridge.ts` decides whether to rebuild the Fetch `Request` from `req.body` instead of streaming the Node request: ```ts if (req.body === undefined || req.body === null) return false; const state = (req as any)._readableState; return Boolean( req.readableEnded || req.complete || state?.ended || state?.endEmitted, ); ``` `req.complete` and the private `_readableState` flags are set by the HTTP parser once the socket holds every byte, whether or not anything read them — so they do not establish that a parser ran. And `req.body` being set does not establish it either. **body-parser 1.x (Express 4)** assigns `req.body = req.body || {}` at [`lib/types/json.js:108`](https://github.com/expressjs/body-parser/blob/1.20.3/lib/types/json.js#L108) — *before* its `hasBody` (111) and `shouldParse` (120) checks. So a request it declines to parse arrives at the bridge with `req.body === {}` and a full, unread stream. `req.complete` satisfied the check, the bridge rebuilt the request as `JSON.stringify({})`, and the real payload was silently dropped. The reachable cases are any non-JSON POST behind a global `express.json()` — multipart uploads (audio transcription), `text/plain`, custom content types. On Express 5 / body-parser 2.x the declined path sets `req.body = undefined`, so the function's first guard already returns false. This is an Express 4 exposure specifically. ## Fix Gate on `req.readableEnded` alone — the one flag that only becomes true after a parser drains the stream to its end. When a parser genuinely ran, it is true and the rebuild path is taken exactly as before; when `req.body` is a placeholder over an unread stream, the request now falls through to the generic streaming handler, which reads the real body. ## Testing Reproduced on **express 4.22.2 / body-parser 1.20.6** (installed outside the repo, since the workspace pins express 5), with a global `express.json()` and an awaiting middleware standing in for a real app's auth/routing layer. `current` is the predicate on `main`, `fixed` is this change: | request | `req.body` | `readableEnded` | `complete` | `current` | `fixed` | what reaches the runtime today | |---|---|---|---|---|---|---| | `application/json` | `{"a":1}` | `true` | `true` | `true` | `true` | `{"a":1}` — correct | | `multipart/form-data` | `{}` | `false` | `true` | `true` | `false` | **`{}` — payload dropped** | | `text/plain` | `{}` | `false` | `true` | `true` | `false` | **`{}` — payload dropped** | Two regression tests added to `express-fetch-bridge.test.ts`. They reproduce that state without adding an Express 4 dependency: a middleware that assigns `req.body = req.body || {}` without reading the stream, then an awaiting middleware so the parser sets `complete`. Against the predicate on `main`, both fail with exactly the reported symptom: ``` × streams a text/plain body instead of rebuilding it from an empty req.body × streams a multipart body instead of rebuilding it from an empty req.body AssertionError: expected '{}' to be 'hello' ``` With the fix, the whole bridge suite passes — including the 14 pre-existing tests, of which `receives JSON body when express.json() runs first` is the guard on the genuinely-pre-parsed path: ``` $ vitest run src/v2/runtime/__tests__/express-fetch-bridge.test.ts Test Files 1 passed (1) Tests 16 passed (16) ``` Full `packages/runtime` suite: `132 passed | 6 failed` — the 6 failing files are the same ones that fail on a clean checkout of the base commit in this worktree (`parseInspectorMetadataV1 is not a function`, a stale cross-package `dist`), unrelated to this change. `oxfmt` clean; `oxlint` adds no new warnings (16 before and after, all pre-existing in this file). *(Those counts are the original pre-rebase run, against the old base. The rebased numbers are below and differ only because the base moved 1304 commits.)* ### Re-verified after rebase The branch was 1304 commits behind main, so I rebased it onto `origin/main` (`0f6c616` → `1a93586`) and re-ran everything. Neither changed file was touched on main since the old merge-base, and the rebase took no conflicts. A passing test does not prove the mechanism is live, so I mutation-checked both halves: with the old predicate restored, exactly the new tests fail and every pre-existing test still passes. | | tests | mutation check (old predicate restored) | |---|---|---| | this PR — `express-fetch-bridge.test.ts` | 16/16 pass | 2 fail: `expected '{}' to be 'hello'`, `expected '{}' to contain 'AUDIOBYTES'` | | #6489 on main — `request-handler.test.ts` | 5/5 pass | 2 fail: `consumedBefore` flips `false` → `true` | #6489's fix survived the v1 → `v1-deprecated/` tree move and is still live at `packages/runtime/src/v1-deprecated/lib/integrations/node-http/request-handler.ts:93`. **No regression from the rebase.** The full `packages/runtime` suite and `tsc --noEmit` give identical results on this branch and on a clean `origin/main` worktree: | | full suite | `tsc --noEmit` | |---|---|---| | `origin/main` | 72 failed files, 380 failed, 1472 passed | 65 errors | | this branch | 72 failed files, 380 failed, **1474** passed | 65 errors, byte-identical set | The +2 are the new tests. The shared failures are the stale cross-package `@copilotkit/shared` dist in the worktree (`parseInspectorMetadataV1`, `computeSamplingMeta`), not this change — none of the 65 type errors is in either changed file, and CI builds `shared` fresh and reports `check-types` green. `oxfmt` clean; `oxlint` 16 warnings and 0 errors, the same count before and after. I also re-ran the structural search for other copies of the predicate across the 1304 new commits. `_readableState` and `endEmitted` now appear only inside the explanatory comments at the two fixed sites, so there is no third copy. ## Note on the duplication #6489 and this PR now hold the same one-line predicate in two files. I did not extract a shared helper: the other copy lives in the v1 tree, now at `packages/runtime/src/v1-deprecated/lib/integrations/`, and importing it into `v2/runtime` would couple v2 to code that is on its way out. Each site carries a comment explaining the flag semantics instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Fixed Express request handling so text and multipart request bodies are preserved when legacy body-parsing middleware assigns an empty body without consuming the request stream. - Prevented valid request payloads from being replaced with empty data when the original request stream remains available. - **Tests** - Added coverage for legacy Express body-parser scenarios involving text and multipart requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
82cd7c01f7 |
fix(react-core): let a custom catch-all renderer return null
`useDefaultRenderTool`'s `render` was typed to return `React.ReactElement`, so a caller who wanted to render only some tool calls could not return `null` to suppress the built-in default for the rest. The value already flowed through correctly at runtime; only the type rejected it. Widen the public `render` return type, and the wrapper local that carries the user's value, to `React.ReactElement | null`. The `as unknown as` cast into `useRenderTool` stays, because `useRenderTool` still requires a `ReactElement` return on main; PR #6533 widens that hook, after which the cast can be tightened. Guarded by a `.test-d.ts` assertion rather than a runtime test: types are erased, so a null-returning render forwards identically before and after the widening and a runtime test would assert nothing. Extracted from #5509, which is otherwise stale. Co-authored-by: Atai Barkai <atai.barkai@gmail.com> |
||
|
|
30aba6b959 |
docs(showcase): state the frontend-tool requirement for the last nine frameworks (closes OSS-1036) (#6874)
## Problem `generative-ui/tool-based` is the terminal page every onboarding run fetches, on every framework route (OSS-1034). Its `## How it works in code` section is one `<FrameworkSetup concept="frontend-tools-setup" />` call that resolves a per-framework snippet. Nine frameworks shipped no snippet, so the section rendered nothing and the `.md` output emitted `<!-- setup skipped: ... -->`. That silence meant two different things at once: "this framework needs no agent-side wiring" and "it needs wiring and nobody wrote it down". Verified on the live host before the change: all nine emitted the skip comment, at ~2.4KB per page. One of the nine is `built-in-agent`, which is `ROOT_FRAMEWORK`. So the page carrying the gap included the unscoped default, `https://docs.copilotkit.ai/generative-ui/tool-based.md`. ## Solution A snippet for each of the nine. Each verdict was read out of the pinned adapter rather than out of the docs, which is what the ticket asked for. **Nothing to wire on the agent** — confirmed in the adapter: | framework | the line that decides it | | --- | --- | | `ag2` | `autogen/ag_ui/adapter.py` `run_stream` builds `client_tools` from `command.incoming.tools` | | `mastra` | `@ag-ui/mastra@1.1.0` reduces `input.tools` into `clientTools` for `agent.stream()` | | `strands` | `ag_ui_strands@0.2.2` calls `sync_proxy_tools(agent.tool_registry, input_data.tools, ...)` | | `strands-typescript` | `@ag-ui/aws-strands@0.2.3` registers a proxy tool per forwarded tool in `toolRegistry` | Both Strands adapters refuse to overwrite a native tool of the same name, so each snippet says to keep the `useComponent` name distinct. **Real wiring:** - `agno`. Its AG-UI interface never reads `RunAgentInput.tools`. Checked against `agno==2.6.19`: the only tool path out of `agno/os/interfaces/agui/` is `RunPausedEvent.tools_awaiting_external_execution`. So a component needs a declared `@tool(external_execution=True)` stub and the `Agent` needs a `db` to hold the paused run. - `deepagents`. `sdk-python/copilotkit/copilotkit_lg_middleware.py` merges `copilotkit.actions` into `request.tools` inside `wrap_model_call`, so `middleware=[CopilotKitMiddleware()]` is load-bearing. Its snippet lives in the docs-only tree, because `deepagents` has no integration package. **Mode-dependent:** - `built-in-agent`. Config mode merges `input.tools` for you (`packages/runtime/src/agent/index.ts`). A factory owns the model call and forwards nothing, so it has to pass them itself. Both halves are in the snippet, plus the collision rule: a frontend tool whose name matches a server tool is dropped in favor of the server executor. **In-repo evidence only:** - `ms-agent-dotnet`, `ms-agent-harness-dotnet`. Their own `gen-ui-tool-based` agents render charts with an empty tool list (`tools: []` / `Tools = []`) and their instructions name the chart tools. No .NET SDK was available here to read `Microsoft.Agents.AI.Hosting.AGUI.AspNetCore` directly. Six of the nine snippets also carry the "Tell the model when to call it" step, which is not filler. `GEN_UI_TOOL_BASED_PROMPT` in `showcase/integrations/built-in-agent/src/lib/factory/demo-prompts.ts` records what happens without it: the model emits `value: 0` for every point and renders a chart with no bars. ## Tests - `REQUIREMENT_NOT_ESTABLISHED` is now empty, and the docblock says a new framework arriving without a snippet is fixed with a snippet, not with a name. - The shape test pins all six auto-forwarding frameworks, plus the three that do not fit that shape (`agno`, `deepagents`, `built-in-agent`). - `setup-concept.test.ts` used `ag2` as its example of an unbundled concept, so closing the last gap would have turned it red for the right reason. It now uses a concept name that is deliberately never bundled. ## The gate never ran in CI Second commit. No workflow runs the shell-docs vitest suite: `test_integration-docs.yml` runs exactly one test file, `test_unit-showcase.yml` covers harness and shell-dashboard only, and `showcase_build_check.yml` matches `showcase/**` but only builds images. Neither path filter listed the snippet files either. So the ratchet added in #6777 was local-only. Added a scoped job and widened both filters. The job runs only the two setup-concept files on purpose: the full shell-docs suite is not green on `main`, and gating every snippet change on unrelated failures would be worse than no gate. ## Verification Ran against a local `shell-docs` dev server on this branch. Expected `setup skipped` count is 0 everywhere: ``` ag2 skip=0 bytes=3825 agno skip=0 bytes=4299 deepagents skip=0 bytes=3786 mastra skip=0 bytes=3857 ms-agent-dotnet skip=0 bytes=3706 ms-agent-harness-dotnet skip=0 bytes=4236 strands skip=0 bytes=4026 strands-typescript skip=0 bytes=4001 built-in-agent (root path) skip=0 bytes=4723 ``` `vitest run` on the two setup-concept files: 12 passed. `npm run typecheck` and `npm run lint` both exit 0. Four tests fail in the full shell-docs suite (`llm-text.test.ts` mastra tool-rendering, three in `angular-docs-content.test.ts`). All four fail identically at the pre-change baseline in the same worktree, so they are pre-existing on `main` and untouched by this branch. ## Two findings this turned up, not fixed here 1. **The `agno` showcase demo contradicts its own snippet.** Every `useFrontendTool` tool in agno's demos has a matching `external_execution` stub, but the `useComponent` ones do not: `render_bar_chart`, `render_pie_chart`, `query_notes` and `highlight_note` have no backend declaration, and `_run_main_agent_hitl_aware` in `src/agent_server.py` cannot forward them. Its e2e spec cannot catch this, because it asserts only that an `svg` is visible inside an assistant message, which any icon satisfies. 2. **Four frameworks redirect the HTML page away.** For `ag2`, `agno`, `deepagents` and `mastra`, `/<slug>/generative-ui/tool-based` 301s to `/<slug>/generative-ui/tool-rendering`. Confirmed on the live host as well as locally, so it predates this branch. Their `.md` output serves the page normally, which is the path onboarding runs fetch, but a human browsing those four never reaches it. Closes OSS-1036. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Added frontend tool setup guides for AG2, Agno, Built-in Agent, Mastra, Microsoft Agent integrations, Strands, and Deep Agents. - Documented tool registration, prompting, execution behavior, persistence, and middleware configuration. - **Tests** - Expanded coverage for frontend tool setup documentation across supported integrations. - Updated setup concept tests to use a stable, intentionally unbundled example. - **Chores** - Added automated workflow coverage for setup documentation and related tooling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b0c0b50826 |
docs(microsoft-agent-framework): document backend approval interrupts (#6922)
Closes #2770. ## Problem Our Microsoft Agent Framework human-in-the-loop page taught exactly one pattern: register a frontend tool whose name matches a backend tool. That is why #2770 was filed asking for a feature we already ship, and why two more people asked for an update on it. Backend-raised approvals have worked for months. Mark a tool with `approval_mode="always_require"` (Python) or wrap it in `ApprovalRequiredAIFunction` (.NET), and the agent ends the run with an AG-UI interrupt that `useInterrupt` renders. No name matching. Nothing on the docs site said so. ## What changed Split the page the way Mastra's already is, and add the missing half: - `human-in-the-loop/index.mdx` — picks between the two patterns. It keeps `/microsoft-agent-framework/human-in-the-loop` resolving, so the three existing pages that link there need no edit. - `human-in-the-loop/interrupt-flow.mdx` — new; the approval path. - `human-in-the-loop/tool-based.mdx` — the old page, retitled, content unchanged. - `custom-look-and-feel/headless-ui.mdx` — one sentence pointing backend approvals at the new page. The new page records four details that are easy to get wrong from reading the source. I got each of them wrong myself before running it: - `reason` is `"tool_call"`, not `"approval"`. - The tool name and arguments live under `metadata.agent_framework` **only on Python**. .NET sends no such field and names the tool in `message`. - `cancel()` and `resolve({ approved: false })` both leave the tool unrun, but cancel ends the run while a false approval lets the agent keep talking. - `renderInChat` defaults to `true`, so the UI renders nothing — silently, no warning — unless a `<CopilotChat />` is mounted. ## Testing Every claim on the new page was executed, not inferred. **End to end against a real agent.** Published `@copilotkit/react-core` over real HTTP against a real Microsoft Agent Framework agent on released `agent-framework-ag-ui` 1.2.2, with a tool marked `approval_mode="always_require"`, served through the real AG-UI FastAPI endpoint. A deterministic stub chat client stands in for the model, so no API key is needed. ``` ✓ CopilotKit <-> real Microsoft Agent Framework approval (2 tests) ✓ renders the backend approval and, on approve, the backend runs the tool ✓ on cancel, the backend does not run the tool Tests 2 passed (2) ``` Approving produced `TOOL_CALL_RESULT ... "content":"Deleted notes.txt."` from the backend and surfaced it in the frontend. Cancelling produced no tool result. **Version floors are measured, not read off a changelog.** The same test passes on `@copilotkit/react-core` 1.61.2 and fails on 1.61.1 (`Unable to find an element by: [data-testid="reason"]`), which is where the 1.61.2 in the requirements table comes from. `agent-framework-ag-ui` 1.2.0 ships byte-identical interrupt-conversion logic to the 1.2.2 I ran. `AGUI.Server` 0.0.6's shipped DLL contains the `tool_call` reason and the `Approval required for tool call` message, and ag-ui's own verified .NET baseline shows the same wire shape. **Mutation-checked.** Changing the backend tool's return string fails the approve test; clicking Approve in the cancel test fails it. An earlier version of the cancel test passed under that mutation — it settled with a bare `setTimeout` outside `act()` — so it was rewritten to poll with `waitFor` and require a timeout. **Docs suite.** Generators plus the node-environment tests that cover routing and the nav tree: ``` Test Files 4 passed (4) Tests 30 passed (30) ``` (`sitemap`, `page-tree-bridge`, `llms.txt`, `llms-mdx`) All three routes appear in the generated search index, including the preserved index URL: ``` microsoft-agent-framework/human-in-the-loop microsoft-agent-framework/human-in-the-loop/interrupt-flow microsoft-agent-framework/human-in-the-loop/tool-based ``` The jsdom-environment tests could not run locally — this checkout's `showcase/shell-docs` install predates `jsdom`, and it resolves through symlinks to the main checkout. CI covers them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added a Human-in-the-Loop section for Microsoft Agent Framework integrations. * Documented interrupt-based and tool-based workflows, including approval, denial, cancellation, and frontend interaction guidance. * Added details on approval-gated backend tools, interrupt payloads, and rendering requirements. * Updated the tool-based guide’s title and description. * Added the new section and page ordering to the documentation navigation. * Clarified how headless UI examples should render backend-tool approvals. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a278237447 |
docs(showcase): explain the AgentCore 401 "Missing Authentication Token" (#6920)
## What Adds a **Troubleshooting → `HTTP 401: Missing Authentication Token`** section to the AWS AgentCore deploy guide. Closes #2912. ## Why #2912 followed the AWS Strands quickstart, pointed the runtime at a deployed AgentCore endpoint, and got: ``` Agent execution failed: Error: HTTP 401: {"jsonrpc":"2.0","error":{"code":-32001,"message":"Missing Authentication Token"},"id":"null"} ``` That string is AWS's own response to an unsigned request or an unmatched route, not a CopilotKit or agent error — but nothing under `docs/` mentioned it, so it reads like a framework bug. `grep -rin "missing authentication\|401" showcase/shell-docs/src/content/docs/integrations/aws-strands/` returned zero hits before this change. The section covers the four real causes: 1. `AGENTCORE_ACCESS_TOKEN` unset on the **runtime server** — the header interpolates to `Bearer undefined`, which AWS rejects exactly like a missing header. 2. An expired Cognito access token. 3. `AGENTCORE_ENDPOINT_URL` that stops short of `/invocations` — an unmatched route, which API Gateway reports as `Missing Authentication Token` rather than a 404. 4. A browser calling AgentCore directly, which cannot do SigV4/OAuth2 signing. ## Why two files The AgentCore guide exists as two near-duplicate copies on `main`, and **both are live**: | File | Live route(s) | |---|---| | `docs/deploy/agentcore.mdx` | `/deploy/agentcore` | | `snippets/integrations/agentcore/index.mdx` | `/strands/deploy-agentcore`, `/langgraph/deploy-agentcore` | `src/lib/mdx-registry.tsx:548` resolves a bare `<Content framework="…" />` to `integrations/agentcore/index.mdx`, and both framework stubs use that bare form. The section lands in both so the two copies do not drift further. Deduplicating them is out of scope here. ## Testing **1. Both routes are live (so both copies need the section)** ``` $ for u in /deploy/agentcore /strands/deploy-agentcore /langgraph/deploy-agentcore; do ... done 200 /deploy/agentcore 200 /strands/deploy-agentcore 200 /langgraph/deploy-agentcore ``` **2. The snippet — not the deploy page — is what renders on the framework routes** ``` $ curl -sL https://docs.copilotkit.ai/strands/deploy-agentcore | grep -o "AGENTCORE_ACCESS_TOKEN" | wc -l 2 ``` Matches `snippets/integrations/agentcore/index.mdx:178,324`. Confirms the `<Content>` resolution traced in `mdx-registry.tsx:548`. **3. Both files compile as MDX** ``` $ node -e "compile(matter(read(f)).content, {jsx:true})" OK src/content/docs/deploy/agentcore.mdx 21245 chars OK src/content/snippets/integrations/agentcore/index.mdx 18447 chars ``` **4. Mutation-checked that the compile check is not vacuous** — dropped the closing `</Callout>` from the new section: ``` mutated: dropped closing </Callout> EXPECTED FAIL: Expected a closing tag for `<Callout>` (368:1-368:80) ``` Restored, and re-confirmed clean. **5. Content test suites — no new failures** `showcase/shell-docs` `npm test` cannot run locally (its `pretest` generators need `showcase/scripts` deps that are not installed; this fails identically on unmodified `main`). Ran the content-scanning suites directly with `vitest` and baselined them against the same worktree with the change reverted: | | `llm-text` + `angular-docs-content` | |---|---| | unmodified `origin/main` | 12 failing (claude-sdk / google-adk / angular) | | with this change | **same 12**, same names | All pre-existing, all unrelated to AgentCore. Full CI is the authority here. ## Note The two duplicated AgentCore copies are worth collapsing into one shared partial in a follow-up. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added troubleshooting guidance for AWS AgentCore `HTTP 401: Missing Authentication Token` errors. * Documented checks for runtime credentials, expired tokens, invocation URL formatting, and request routing. * Clarified that credentials belong on the runtime server and requests should pass through Copilot Runtime. * Noted that the issue applies to deployed environments, not local quickstarts running on `localhost:8000`. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1a935861a8 |
fix(runtime): do not treat an unread stream as a pre-parsed body in the express bridge
`hasPreParsedBody` gates on `req.body` being set, then confirms the stream is
gone via `req.readableEnded || req.complete || _readableState.ended ||
_readableState.endEmitted`. The last three are set by the Node HTTP parser once
the socket holds every byte, whether or not anything read them, so they do not
establish that a parser ran.
That matters because `req.body` being set does not establish it either.
body-parser 1.x (Express 4) assigns `req.body = req.body || {}` before its own
`hasBody`/`shouldParse` checks, so a request it declines to parse — multipart
upload, text/plain — reaches the bridge with `req.body === {}` and a full,
unread stream. `req.complete` then satisfied the check, the bridge rebuilt the
request from `{}`, and the real payload was silently dropped.
Gate on `readableEnded` alone, which only becomes true after a parser drains the
stream to its end. Verified on express 4.22.2 / body-parser 1.20.6 that a
multipart POST behind a global `express.json()` arrives with `req.body === {}`,
`readableEnded === false`, `complete === true`, and that the genuinely parsed
JSON case is unaffected.
Same root cause as #6489, which fixed the equivalent check in the node-http
request handler. Kept as a separate local predicate rather than sharing one
helper, to avoid coupling `v2/runtime` to the v1 integration tree.
|
||
|
|
0748b1a8e8 |
docs(showcase): explain the AgentCore 401 "Missing Authentication Token" (closes #2912)
Reporters following a framework quickstart and then pointing the runtime at a deployed AgentCore endpoint hit `HTTP 401: Missing Authentication Token` with no guidance anywhere in the docs. The string is AWS's own response to an unsigned request or an unmatched route, so it reads like a CopilotKit failure when it is not. Add a Troubleshooting section covering the four causes: an unset AGENTCORE_ACCESS_TOKEN interpolating to `Bearer undefined`, an expired Cognito token, an AGENTCORE_ENDPOINT_URL that stops short of /invocations, and a browser calling AgentCore directly instead of going through Copilot Runtime. The AgentCore guide exists as two near-duplicate copies -- docs/deploy/agentcore.mdx serves /deploy/agentcore, and snippets/integrations/agentcore/index.mdx is what <Content framework="..." /> renders at /strands/deploy-agentcore and /langgraph/deploy-agentcore. Both are live, so the section lands in both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5f8da811d9 |
docs(microsoft-agent-framework): document backend approval interrupts
Our Microsoft Agent Framework HITL page taught only the frontend-tool approach, where the frontend registers a tool whose name matches a backend tool. Backend-raised approvals have worked for months -- mark a tool with approval_mode="always_require" (Python) or ApprovalRequiredAIFunction (.NET) and the agent raises an AG-UI interrupt that useInterrupt renders -- but no page said so, so readers kept reaching for name matching (issue #2770). Split the page the way Mastra's is split and add the missing half: - human-in-the-loop/index.mdx picks between the two patterns. It keeps the existing /microsoft-agent-framework/human-in-the-loop URL working, so the three pages linking there are untouched. - human-in-the-loop/interrupt-flow.mdx documents the approval path. - human-in-the-loop/tool-based.mdx is the old page, retitled. The interrupt page records details that are easy to get wrong from reading the code: reason is "tool_call" rather than "approval"; the tool name and arguments sit under metadata.agent_framework only on Python, while .NET names the tool in message; cancel() and resolve({approved:false}) both skip the tool but end the turn differently; renderInChat defaults to true and renders nothing, silently, with no chat mounted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2e05c9ce81 |
docs(showcase): put the built-in-agent name-collision rule in the right mode (refs OSS-1036)
The snippet stated the collision rule under factory mode, which implied the runtime resolves it for you there. It does not. Config mode is where the runtime decides: `index.ts` builds the tool set from `convertToolsToVercelAITools(input.tools)` and then spreads the configured tools over it, so a shared name resolves to the backend tool. The rule now sits in that step. In factory mode the factory owns precedence, because nothing merges the two lists on its behalf. The showcase's own TanStack factory shows one choice, filtering forwarded tools through `!serverToolNames.has(t.name)`, but that is its decision rather than runtime behavior. The factory step now says so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b3462e01fe |
docs(showcase): fix the using directives in the two .NET snippets (refs OSS-1036)
Both snippets named namespaces that do not resolve. `Microsoft.Agents.AI.Harness` is the NuGet package name, not a namespace. `AsHarnessAgent` and `HarnessAgentOptions` come from `Microsoft.Agents.AI`, and no `.cs` file in the repo carries a `using` for the package name. Both snippets also needed `using Microsoft.Extensions.AI`, which is where `AsIChatClient()` and `ChatOptions` live. Every agent file in both showcase columns uses exactly `Microsoft.Agents.AI` plus `Microsoft.Extensions.AI`. Also define `HarnessMaxContextWindowTokens` and `HarnessMaxOutputTokens` in the harness snippet, which referenced them without showing a value. No .NET SDK is available here, so this was checked against the `using` blocks of the twelve agent files that call `AsHarnessAgent`, not by compiling. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
700fb1496b | style: auto-fix formatting | ||
|
|
d090494ec4 |
ci(docs): run the framework setup-concept gate in CI (refs OSS-1036)
The coverage ratchet added for OSS-1036 never ran in CI. No workflow runs the shell-docs vitest suite: `test_integration-docs.yml` runs exactly one test file, `test_unit-showcase.yml` covers harness and shell-dashboard only, and `showcase_build_check.yml` matches `showcase/**` but only builds images. On top of that, neither path filter listed the snippet files the gate reads, so a new `frontend-tools-setup.mdx` triggered nothing. Add a scoped job and widen both filters. The job is deliberately limited to the two setup-concept test files, because the full shell-docs suite is not green on main and would gate every snippet change on unrelated failures. The harness symlink step is the same one showcase/shell-docs/Dockerfile uses: `generate-registry.ts` imports the catalog fold out of the harness tree, which resolves `js-yaml` by walking up from `showcase/harness/`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
cf060da900 |
docs(showcase): state the frontend-tool requirement for the last nine frameworks (closes OSS-1036)
`generative-ui/tool-based` is the terminal page every onboarding run fetches, and its `## How it works in code` section renders a per-framework snippet. Nine frameworks shipped no snippet, so the section rendered nothing and the silence meant both "no agent-side wiring is needed" and "wiring is needed and nobody wrote it down". Each verdict was read out of the pinned adapter rather than the docs: - ag2: `run_stream` builds `client_tools` from `incoming.tools`. - mastra: the adapter reduces `input.tools` into `clientTools`. - strands, strands-typescript: a proxy tool per forwarded tool is registered in the agent's tool registry, and a native tool of the same name wins. - agno: its AG-UI interface never reads `RunAgentInput.tools`, so a component needs an `external_execution=True` stub and a `db` for the paused run. - deepagents: `CopilotKitMiddleware` merges `copilotkit.actions` into `request.tools`, so the middleware is load-bearing. - built-in-agent: config mode forwards for you, a factory does not. It is also the root framework, so this is the unscoped default page. The two .NET columns rest on their own demo agents, which render charts with an empty tool list. No .NET SDK was available to read the NuGet hosting package. REQUIREMENT_NOT_ESTABLISHED is now empty. The shape test pins each snippet, and setup-concept.test.ts no longer depends on ag2 being an open gap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
42494df607 |
docs(react-core): stop pointing v1 appendMessage at non-public sendMessage (#6940)
## Summary The v1 `useCopilotChat` JSDoc tells readers to use `sendMessage` instead of `appendMessage`. `sendMessage` is not part of the public v1 return type, so following that advice does not compile. `packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts:109` explicitly omits it: ```ts export type UseCopilotChatReturn = Omit< UseCopilotChatReturnInternal, | "messages" | "sendMessage" // <- the JSDoc points readers here ... ``` `sendMessage` exists only on `useCopilotChatInternal`. The public hook returns `appendMessage`, which is the working v1 programmatic-send path. This replaces the misdirection with the v2 migration pointer and states plainly what `appendMessage` is for. Comment-only change — no runtime effect. Found while closing #4215, where a user was told by our own docs to call a method we do not export. ## The published page does not change yet This fixes the source of truth. The generated page cannot be refreshed until #6939 is resolved: running the generator today would also embed the internal v1 deprecation banner ("AI CODING AGENTS: Never copy, suggest, or generate these v1 APIs") into 20 public reference pages. I deliberately excluded the regenerated `.mdx` files from this PR rather than ship that. Once #6939 lands, a regenerate publishes this wording. ## Testing **1. `oxfmt --check` — pass** ``` $ ./node_modules/.bin/oxfmt --check packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts Checking formatting... All matched files use the correct format. Finished in 33ms on 1 files using 18 threads. ``` **2. Generator reads this file successfully (26/26)** — confirms the JSDoc edit is picked up, and that the only thing blocking publication is #6939, not this change: ``` $ ./node_modules/.bin/tsx scripts/docs/gen.ts Successfully autogenerated showcase/shell-docs/src/content/reference/v1/hooks/useCopilotChat.mdx from packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts All reference docs processed (26/26 succeeded) ``` The regenerated page contained the new wording as expected; I then reverted the generated files per the section above. **3. Diff is a single comment hunk** — verified with `git diff --stat`: `1 file changed, 4 insertions(+), 1 deletion(-)`, all inside a JSDoc block. No exported symbol, type, or runtime line touched, so no typecheck or test surface is affected. **4. Claim verified against `origin/main`,** not a local branch: `git show origin/main:packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts` confirms both the misdirecting line (`:70`) and the `Omit` (`:109-121`). ## Notes - No changeset — comment-only. - Branch name is a leftover misnomer (`ben1/oss-docs-generator-v1-paths`); the change is the wording fix only. - @ataibarkai's #6653/#6654/#6655 stack would move this file back to `packages/react-core/src/hooks/`. If that stack lands, this one-line change needs carrying forward into the reapply. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Clarified the deprecated `appendMessage` option in `useCopilotChat`. - Documented its role for programmatic sending in v1. - Clarified the migration path for AG-UI format users moving to v2. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
380ad1220a |
chore(examples): update CLI starter SDK versions (#6950)
Updates all 19 first-party starter roots in the Intelligence CLI catalog
to CopilotKit 1.70.2 and CopilotKit for Angular 0.5.2, including nested
TypeScript agents and the AgentCore runtime Lambda. Aligns AG-UI
client/core/encoder/proto pins and overrides to 0.0.59, matching the SDK
releases, and regenerates npm lockfiles.
The Angular SDK requires Angular 22, so the Angular starter also updates
its Angular/TypeScript toolchain and migrates its two icons to the
compatible @lucide/angular package. Registers 1.70.2 in the
managed-entitlement compatibility checks.
Validation: clean npm installs and all 20 changed frontend/runtime
production builds pass; all three changed TypeScript agents typecheck.
Integration parity and all 159 Intelligence template contract tests
pass. Audited 144 SDK/AG-UI references across 23 manifests and verified
their lockfile pins. All 13 CI starter smoke jobs pass. Final CI: 45
successful checks and 3 expected skips on commit
|
||
|
|
9f57bced70 | docs(examples): align Angular starter Node prerequisites | ||
|
|
c2dd6cf3b8 | style: auto-fix formatting | ||
|
|
d6ad0f8d31 | fix(examples): align LangGraph starter AG-UI dependency trees | ||
|
|
e3b249f0f7 | chore(examples): update CLI starters to current SDK releases | ||
|
|
6e888d7d88 |
chore: release angular v0.5.2 (#6949)
## Release angular v0.5.2 **Scope:** `angular` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `angular` packages to `0.5.2` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `angular` packages to npm at version `0.5.2` - Creates git tag `angular/v0.5.2` - Creates a GitHub Release with the final release notes ### Before merging - [ ] CI is green (tests, lint, types, build) - [ ] Version bumps look correct - [ ] Release notes are accurate (edit in Notion if a draft was created) --- > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR.angular/v0.5.2 |
||
|
|
175e9c237c | chore: release angular v0.5.2 | ||
|
|
f43ec0d30e |
chore: release monorepo v1.70.2 (#6947)
## Release monorepo v1.70.2 **Scope:** `monorepo` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `monorepo` packages to `1.70.2` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `monorepo` packages to npm at version `1.70.2` - Creates git tag `monorepo/v1.70.2` - Creates a GitHub Release with the final release notes ### Before merging - [ ] CI is green (tests, lint, types, build) - [ ] Version bumps look correct - [ ] Release notes are accurate (edit in Notion if a draft was created) --- > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR.v1.70.2 |
||
|
|
16514e9424 | chore: release monorepo v1.70.2 | ||
|
|
6db6d39c43 |
feat(showcase): exec skin — conversational dashboard composition (Vantage) (#6876)
## What this adds
`exec` — **Vantage**, Cascade Industries' executive reporting desk — the
eighth skin in the reskinnable demo, and the first to demonstrate
**conversational dashboard composition**: the agent renders a metric
block *inline in the chat*, and the operator pins it onto a real
dashboard.
- **CEO / CFO dashboards** with a live exception strip, plus a **Metrics
Explorer** with four URL levers (department, period, top-N, threshold)
that round-trip through `navigateTo`.
- **A2UI blocks in the transcript.** `render_metric_block` returns
`a2ui_operations`; a new shell dispatch renders `block:`-prefixed
surfaces inline instead of taking over the canvas region. A pin control
writes the block server-side onto a dashboard.
- **A teachable publish gate.** Publishing a board pack refuses twice —
first for a countersign PIN, then `422 UNEXPLAINED_VARIANCE` — and the
narrative-code vocabulary that clears it is deliberately withheld from
every agent-readable channel, so the agent has to be taught it on stage.
- **A per-request budget-memo PDF**, seeded/forgettable Intelligence
memories, and a gated presenter reset.
Nine demo beats, one presenter reset. `docs/superpowers/` holds the
design spec and plan (local-only, gitignored).
## How it was built and reviewed
Built with a 14-wave micro-task execution (62 micro-tasks), then put
through a Tier-3 code-review loop: **four review rounds of ~30
independent Opus reviewers each** (~110 reviewer reports) and **four fix
cycles (49 agents)**, every behavioral fix landed with red-green
evidence and every new guard proved by mutation.
Bugs the loop caught that unit tests could not, among others:
- an inline block crashed the page (no `A2UIProvider` ancestor) and
every block render hijacked the app region;
- the withheld code vocabulary leaked through a zod enum error body;
- the AI SDK's default `stopWhen` ended runs after one tool call;
- schema-level value constraints hung the tool chip forever (the runtime
has no `tool-error` arm) — twice, in two different tools;
- an emptied dashboard published successfully, erasing the demo's
climactic refusal;
- the memo printed a future dateline on the 1st–4th of any month;
- untrusted-key lookups answered `500` where `404` belonged, and made
`publishPack` throw against its documented contract.
The loop was **closed deliberately, not by convergence.** A convergence
audit over rounds 2–4 showed the remaining findings were dominated by
documentation-accuracy items that each fix cycle's own new prose
generated (~85% of round 4's findings sat in code the previous cycle had
touched). Rather than run a fifth round, the final cycle fixed the
demo-facing runtime defects and added **structural levers** so the
recurring classes cannot silently recur:
- every restatement of the metric/department vocabulary is now
compiler-checked (`satisfies Record<K, true>`) — a new metric fails
typecheck at **five** sites, and a parity test compares all four shipped
schemas;
- the withheld-code sweep derives its list from the canonical type and
walks tool **parameter schemas**, not just prose (a
`z.literal("VAR-TIMING")` in a schema now goes red);
- a lint rule forbids truthiness-guarded plain-object registry lookups —
it caught a fresh instance during this very merge.
Everything the loop found and did not fix is filed as follow-up work
(see the linked issues); nothing was silently dropped.
## Since the review loop closed
A round of presenter-facing polish and two live-only bugs, all found by
looking at the running app rather than by tests:
- **The CEO dashboard is now nothing but composed blocks.** Two fixed
strips (an exception feed and an initiative RYG strip) sat above the
pinned grid while the seeded dashboard *also* pins an `exceptionList`
and an `initiativeTable` — so the page opened showing both sets of rows
twice, once in chrome the assistant cannot touch. For a demo whose claim
is "the assistant composes this page", static duplicates of the
composable blocks were the strongest argument against it. The page
readable is now derived per-kind from what is actually pinned, so
unpinning a block stops the agent claiming its rows are on screen.
- **One frame and one title per pinned block.** The shared `Tile` drew
card chrome inside a host that already draws it (two rings, two
backgrounds, two shadows), and `buildBlockOps` emitted a `Heading`
duplicating the card's own title. The heading is now emitted only when
the block is *not* pinned, so a block in the chat still names itself.
- **The initiative block no longer dwarfs the grid.** 757px of content
beside a 112px metric tile, which the row-stretch then paid for with
~600px of dead space in its neighbour. Owner moved onto the name line,
the note came off the card (still on `title`, still published verbatim
by the page readable), and the block is now 269px against 243px for its
peers.
- **Status is carried once, not three times.** The RYG cards stacked a
tone wash, a coloured left edge and a pill; on the dark theme the washes
read as a different material. Three composited contrast pairs were
*retired* rather than weakened, since the washes no longer render.
- **`Publish refused: … Burn Rate · all · 2026-08`.** The refusal
receipt resolved `metricId` through `metricLabel` and forwarded
`department` raw — and every breach the seeded demo refuses on is
company-wide, so the bare word "all" reached the room at the climax of
beat 6, two inches below an exception block reading "Company-wide". The
label map already existed in three identical copies and the receipt had
none; all four now share one module.
## Verification
`pnpm typecheck` · `pnpm test:unit` (**3,208 tests / 249 files**) ·
`pnpm lint` (0/0) · `pnpm build` — all green.
**Walked live against a real model.** All nine beats were driven
end-to-end against a running stack (self-hosted Intelligence + a real
LLM) and each one behaved:
| Beat | Evidence |
| --- | --- |
| 1 face | inline A2UI block + pin controls + one-sentence answer |
| 2 rich thread | block replays fully rendered after a hard reload |
| 3a drive the app | agent asked for the code instead of inventing one,
then filed it verbatim; ledger flipped `opex/distribution` to
`explained` |
| 3b sees my screen | named the pinned blocks in order and read the live
rows, including 3a's flip |
| 3c levers | navigated to `?department=distribution&top=10`; page
honoured both |
| 3d multimodal | genuinely parsed the PDF (quoted its figures and memo
id); filed with `source: "ingested-memo"` |
| 4 memory | chose Growth **QoQ** over YoY unprompted and named the
preference |
| 5 stored skill | replayed the month-end procedure unaided, pinning
three blocks |
| 6 teach a skill | 422 gate → recorder captured 2 steps → saved →
replay published (`200`, pack carries `narrativeIds`) |
⚠️ **Still outstanding:** the e2e spec remains `test.fixme`-gated
pending an aimock fixture, and the walkthrough above was driven
programmatically through the browser rather than by a presenter — a
human rehearsal is the remaining gate before this leaves draft.
Two known non-blockers, neither in this skin: a shared-shell
`renderActivityMessages must be a stable array` console warning from
`src/app/[skin]/layout.tsx` (affects every skin), and beat 3d's
suggestion pill not preparing the room for a two-turn beat the way beat
3a's was reworded to.
Closes CPK-8249.
|
||
|
|
7f6d6008b4 |
fix(showcase): say "Company-wide", not "all", in the publish refusal
The demo's climactic refusal listed its breaches with the raw department
id, two inches below an exception block spelling the same scope out:
Publish refused: …
Burn Rate · all · 2026-08 <- the receipt
Burn Rate · Company-wide <- the block above it
`publishRefusalPayload` resolved `metricId` through `metricLabel` and
forwarded `department` untouched, and every breach the seeded demo
refuses on is company-wide — so the one id that reached the room was the
bare word "all", which reads like a bug rather than a scope.
The label map already existed in THREE identical copies (the catalog
renderers, the CEO dashboard, the Metrics Explorer) and the receipt had
none, so a fourth copy would have fixed this line and left the next
surface to make the same mistake. All four now share one module, which
also gives the two pages that indexed the map directly the `Object.hasOwn`
guard only the renderers had.
Nothing round-trips the field back into a query — file_variance_narrative
takes a metric and a period, no department — so the label is safe as the
only form that leaves the payload.
The new test pins "all" specifically, plus an unmapped key still falling
through as itself, since that path exists to report a query the ledger
could not answer and the raw key is what names it. Verified live: the
receipt now reads "Burn Rate · Company-wide · 2026-08".
|
||
|
|
6d24c89ee7 |
fix(showcase): drop the note from the exec initiative card, one line per row
The note was the last thing making this block taller than its neighbours. Printed on every row it was 757px of content beside a 112px metric tile; restricted to off-track rows it was still 311px against 243px for the two blocks below it. A pinned dashboard card answers "what is the state of things" — five names, five owners, five statuses — and the prose behind any one of them is a follow-up question, not something to read five of at a glance. Every row is now a single 27px line and the card is 269px, level with the rest of the grid. THE NOTE IS UNPRINTED, NOT DELETED. It stays on the cell's `title`, and the page readable still publishes all five verbatim off the ledger, so "what am I looking at?" reads out every explanation exactly as before. The test asserts the `title` survives precisely so a later cleanup cannot drop the data path while looking like a no-op. Verified against three mutants: reprinting the note, stripping the owner along with it, and dropping the `title` each fail. |
||
|
|
1ea6cc58a4 |
fix(showcase): put the initiative owner on the name line so green rows cost one line
The previous pass moved Owner out of its own COLUMN, which fixed the name wrapping, but it landed on a sub-line of its own — so all five initiatives still cost two lines each, including the three green ones whose entire entry was then two lines to say a name and "on track". Owner now rides the name line. The sub-line is the note alone, which only a non-green row has, so a green row is a single 27px line and the block stops being the outlier on the dashboard. Measured on the CEO dashboard at a 533px card: 371 → 311 tall, against 243 for the two blocks below it. It was 757 before this sequence began. |
||
|
|
6e27380718 |
fix(showcase): stop the exec initiative block dwarfing every card beside it
Measured on the CEO dashboard: the initiative table was 667px of content next to a 112px metric tile, and since the grid stretches a row to its tallest card it padded Revenue vs Plan out to 757px — ~600px of dead space beside a six-line block. Two causes, both width rather than data. The table split three ways by content, so with the assistant open — the demo's normal state, leaving the card 286px — the Initiative column got 120px while Owner and Status took the other half for a person's name and a three-letter code, and every initiative name wrapped. Owner now sits on the sub-line where it costs no width, and `table-fixed` at 78% stops the widest owner name renegotiating the split. The note then earns its lines only when the status is not green: a green row's prose only repeats what its pill already says, and the rows that keep an explanation are the ones a reader has to act on. Nothing is lost — the full note stays on the cell's `title`, and the page readable publishes all five verbatim off the ledger. It is also clamped to two lines, since note length is ledger data and nothing upstream bounds it. 667 → 347 of content; the row goes 757 → 436. The renderer test that asserted every note rendered is replaced by two that pin the rule in both directions — printed for off-track, withheld for green, and reachable on `title` either way. Verified against three mutants: printing every note, printing none, and dropping the `title` each fail exactly one of them. |
||
|
|
1f6973c423 |
feat(showcase): leave the CEO dashboard with nothing on it but composed blocks
Two fixed strips sat above the pinned-block grid: an exception feed and an initiative RYG strip, both read straight off the ledger and neither agent-configurable. The seeded dashboard ALSO pins an `exceptionList` and an `initiativeTable` block, so the page opened showing each set of rows twice — once in chrome the assistant cannot touch, once in a block it can. For a demo whose whole claim is "the assistant composes this page", static duplicates of the composable blocks were the strongest argument against it. Both strips come off. A page header replaces them, matching the CFO dashboard's, so the two screens still read as the same screen with a different set of blocks on it. The page readable is now derived per-kind from what is actually pinned: unpin the exception block and the agent stops claiming exception rows are on screen, rather than reading out a feed the CEO is not looking at. `visibleExceptions` and `ceoReadableExceptions` are unchanged and keep their tests — they were always the rows the block shows. Two consequences worth naming. The strip's click-through into the Metrics Explorer goes with it: the block's rows are not links, so an exception can now only be drilled into by asking Vantage to navigate. And three contrast pairs cited `pages/ceo-dashboard.tsx` for the initiative pills; the pills now render only in the catalog's InitiativeTable, so the dead sites are dropped rather than the pairs weakened. |
||
|
|
bdec0dc513 |
fix(showcase): line up the bottoms of two exec blocks sharing a grid row
The grid was told `items-start`, which stopped a short card stretching but left its neighbour's extra height as a ragged gap. Dropping it returns the row to `items-stretch`; `h-full` on the card and `flex-1` on its body make the card actually use that height instead of leaving the slack outside its own border. Two cards side by side now line up top and bottom. |
||
|
|
6609d460c3 |
fix(showcase): let an exec initiative card carry status once, not three times
The RYG strip stacked a 40%-opacity status wash, a 4px coloured left edge and the status pill — three signals saying one word. On the dark theme the washes muddied the surface enough that the tinted cards read as a different material from every other card on the page. The pill stays as the carrier and the card keeps a hairline tinted to match, which is still enough to scan a row by colour. Cards also widen to 15rem and let the initiative name wrap to two lines instead of truncating, since several names lost their last word at the old width. theme.test.ts's contrast guard caught this immediately: three composited pairs it checks describe text over those washes, and the washes no longer render anywhere in exec. They are retired with a note rather than weakened, so the guard keeps failing if a wash comes back untested. |
||
|
|
fa92193883 |
fix(showcase): draw one frame and one title around a pinned exec block
Every block renders inside a host that already draws card chrome — the chat bubble in inline-block-surface.tsx and the dashboard card in dashboard-grid.tsx — and both the shared Tile and buildBlockOps were drawing their own on top of it. A pinned block came out with two rings, two backgrounds and two shadows, and with its title printed twice: once in the card header beside the move/remove controls, once as the Heading component the op builder emitted. Tile drops the chrome and keeps only its label; buildBlockOps emits the Heading only when the block is NOT pinned, so a block in the chat still names itself. The initiative table's label was a bare "Initiatives", which under a card titled "Key Initiatives" was the double title in a second form — it now carries the count and how much of it is off track, the way its sibling labels carry metric, department and period. The grid gains items-start so a short card no longer stretches to the height of the tallest one in its row. |
||
|
|
15eb3f95a1 | fix(showcase): render the block and disclose the setting a kind ignores | ||
|
|
201cfcacb3 | fix(showcase): send the model somewhere real when a block prop is refused |