refactor(angular): signals-based reasoning collapse - #2
Open
rainerhahnekamp wants to merge 7 commits into
Open
Conversation
Adds angular-developer and angular-new-app skills via `npx skills add` from https://github.com/angular/skills into .agents/skills/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Apply the common Angular CLI schematics to code which has been added by Soverius. We can do a follow-up for the high amount of effects, but we have to be careful. We need tests first to do a refactoring for that.
Revert open-generative-ui-tool-renderer and drop empty imports arrays. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
rainerhahnekamp
force-pushed
the
feat/ng-a2ui-exp
branch
2 times, most recently
from
June 10, 2026 20:48
d0f3161 to
015701f
Compare
wolfmanfx
force-pushed
the
feat/ng-a2ui-exp
branch
from
June 11, 2026 09:47
c57ec9b to
0fa62b3
Compare
rainerhahnekamp
force-pushed
the
feat/ng-a2ui-exp
branch
from
June 15, 2026 19:21
0fa62b3 to
cedf661
Compare
rainerhahnekamp
pushed a commit
that referenced
this pull request
Jun 15, 2026
…-side parity (SU7-F1/F2/F3)
SU7-F1 — backend host pattern hardening:
- F1.1 Reject bare trailing ?/# in the backend host pattern
- F1.2 Strip internal tab/CR/LF from the backend host pattern
- F1.3 Warn when ignoring an empty-string local backend override
- F1.4 Reject empty-userinfo @ in the backend host pattern authority
- F1.5 Keep __proto__ keys as data in local-backend maps
- F1.6 Commit the local-backends memo key only after the value computes
- F1.7 Trim local backend overrides before validation and name the real
rejection
- F1.8 Honest FATAL when the pattern host is a stray scheme fragment
- F1.9 Canonicalize the pattern authority for parity with the override
path
- F1.10 Acknowledge the staging-to-prod fail-open in the pattern fallback
- F1.11 Harden backend-url/local-backends-env test hygiene
SU7-F2 — runtime-config & client-config edge cases:
- F2.1 Branch POSTHOG_HOST rejection reasons (scheme/degenerate/parse-
failure) instead of the catch-all mislabel
- F2.2 Reject loopback BASE_URL/DOCS_HOST in production instead of the
silent http:// prepend
- F2.3 Key the DOCS_HOST fallback once-guard on (mode, shellHost, value)
and mode-prefix all value-only guard keys
- F2.4 Reject a present-but-empty posthogKey in the client config reader
- F2.5 Drop the trailing slash from SSR_PLACEHOLDER_URL for structural
parity with server values
- F2.6 Attribute the DOCS_HOST slash-strip to readDocsHost itself
- F2.7 Normalize trailing-dot FQDN spellings in the docs self-host loop
guard (both compare sides)
- F2.8 Harden console spies to capture all log args; pin the full all-env
config shape; converge SSR simulation on vi.stubGlobal
SU7-F3 — script-side parity, table classification & test isolation:
- F3 #1 Handle a missing reference integration per the error contract
- F3 #2 Port the runtime backend-host-pattern normalization into the
generator — scheme/trailing-slash strip, degenerate fallback,
NEXT_PUBLIC fallback
- F3 #3 Treat non-mapping manifest parses (empty/null/scalar/array YAML)
as validation errors, not TypeErrors
- F3 #4 Label a missing/unreadable constraints.yaml per the stderr+exit(1)
error contract
- F3 #5 Align atomic-write tmp naming with the test harness straggler-
sweep convention; guard main() on direct invocation
- F3 #6 Correct the determineCellStatus unshipped docstring; replace
stale hardcoded cell counts with formulas
- F3 CopilotKit#7 Isolate the pattern suite on a per-suite tmpdir harness; snapshot
the generator's full write set
- F3 CopilotKit#8 Classify discarded duplicate wildcards as duplicates — hoist the
owner check above the destination warns
- F3 CopilotKit#9 Reject a root ("/") EXACT seo-redirect source — homepage-hijack
twin of the root-wildcard guard
- F3 CopilotKit#10 Reject seo-redirect entries with non-printable-ASCII source/
destination — close the silent-dead-entry class
- F3 CopilotKit#11 Strip trailing slashes in normalizePosthogHost before the scheme
test
- F3 CopilotKit#12 Message-filter the empty-slug-set error count; pin the single
matcher entry
rainerhahnekamp
pushed a commit
that referenced
this pull request
Jun 15, 2026
… not only an array nested value; dashboard mirror updated in lockstep (drift test pins byte-identity) (CR G2 #2)
rainerhahnekamp
pushed a commit
that referenced
this pull request
Jun 15, 2026
…cer-emitted degraded reads 'degraded', not 'stale' (CF6-G5 #2)
rainerhahnekamp
pushed a commit
that referenced
this pull request
Jun 15, 2026
… dereferencing probeKey (CF7 #2) A null/undefined/primitive enumerator element threw a TypeError out of the tick body, rejecting the tick promise and violating the 'tick never rejects' invariant. Non-object elements are now dropped loudly and counted in the existing invalid-spec accounting.
rainerhahnekamp
pushed a commit
that referenced
this pull request
Jun 15, 2026
…gray, never green (CF7-F3 #2) The D1-D4 gate fires only on d3.exists/d4.exists, so a cell with ONLY green D5/D6 rows (no e2e/chat/tools rows at all) slipped past it and rendered a green chip + green d6Effective at achievedDepth=0/ ceilingDepth=0 — a false top-of-ladder claim contradicting the strictness doctrine (PRESENT-but-null D4 grays; D5-no-data grays the ladder). A wholly absent D3/D4 family now collapses to the gray "unverified" chip (same shape as the d4NoData collapse) with red-D5/D6 dominance preserved, and d6Effective stays blocked (null). cell-model.ts:847-852 (gate) / :905-921 (d6Effective). One existing fixture (amber pass-through under reclaimed-pending) built its amber from an absent-D3/D4 map; it now carries green e2e/chat rows so the chip is genuinely amber through an intact ladder — the test's never-mask assertion is unchanged.
rainerhahnekamp
force-pushed
the
feat/ng-a2ui-exp
branch
2 times, most recently
from
June 17, 2026 15:57
3064fb9 to
cd3a94f
Compare
wolfmanfx
pushed a commit
that referenced
this pull request
Jul 10, 2026
…rwarded inbound headers (CopilotKit#5782) ## Problem When a self-hosted v2 `CopilotRuntime` is configured with a server-side agent (an `@ag-ui/client` `HttpAgent` with static `headers` for service-to-service auth), the runtime forwards inbound `authorization`/`x-*` request headers onto the agent's outgoing call **and lets them override the headers the server configured** — silently breaking service-to-service auth to a secured backend (e.g. a private Cloud Run agent behind IAM). `Fixes CopilotKit#5712` ## Root cause `packages/runtime/src/v2/runtime/handlers/shared/agent-utils.ts:125-128` merged forwarded inbound headers **last**, so they won on collision: ```ts agent.headers = { ...agent.headers, // server-configured ...extractForwardableHeaders(request), // inbound — overrode the above }; ``` There are actually **two** failure modes: 1. **Same-case collision** — inbound `authorization` overwrites a server `authorization` (last-write-wins). 2. **Case-mismatch collision** — `extractForwardableHeaders` lowercases inbound keys (`authorization`), while the server typically configures canonical casing (`Authorization`). A plain spread treats those as *distinct* keys and emits **both** — which undici downstream comma-joins into a single invalid `"Bearer A, Bearer B"` ("multiple JWTs") value. Flipping the spread order alone does **not** fix this case. ## Fix In `agent-utils.ts`, make server-configured `agent.headers` authoritative on collision, matched **case-insensitively**: drop any forwarded inbound header whose name (case-insensitively) is already set on the agent, and let non-colliding inbound headers pass through unchanged. This preserves the existing forward-for-auth behavior for headers the server does *not* set, while guaranteeing a server-set token is never overridden or duplicated. The merge logic lives in a shared `mergeForwardableHeaders(serverHeaders, request)` helper in `packages/runtime/src/v2/runtime/handlers/header-utils.ts` so the precedence semantics are defined in exactly one place. ### Scope note This is the conservative precedence + case-insensitive-dedup fix (the issue's suggested fix #1). I did **not** tighten the default allowlist to drop hop-by-hop/platform `x-*` headers (`x-serverless-*`, `x-forwarded-*`, …) or add an opt-out — those alter existing forwarding behavior and are worth a separate, deliberate change. The precedence fix alone resolves the reported breakage (the server-set token now wins regardless of what the platform injects on a colliding header name). A documented workaround already exists for users on released versions: pass a custom `fetch` to the `HttpAgent` that builds outgoing headers from scratch (it runs after `configureAgentForRequest` and survives the per-request `agent.clone()`). ## Red-green proof (the real fix — `/run` path) The load-bearing assertion: there must be exactly **one** authorization header carrying the **server** value. ### RED (fix stashed, against unmodified `agent-utils.ts`) ``` ❯ src/v2/runtime/__tests__/agent-header-precedence.test.ts (2 tests | 1 failed) × configureAgentForRequest — header precedence (CopilotKit#5712) > server-configured agent headers win over a colliding inbound header AssertionError: expected [ 'Authorization', 'authorization' ] to have a length of 1 but got 2 81| expect(authKeys).toHaveLength(1); Test Files 1 failed (1) Tests 1 failed | 1 passed (2) ``` The pre-existing `agent-utils-header-forwarding.test.ts` also failed, because it explicitly encoded the buggy behavior (`expect(...["x-aimock-context"]).toBe("new-context")` — inbound winning): ``` FAIL src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts > ... > request forwardable headers override matching pre-existing agent headers AssertionError: expected 'old-context' to be 'new-context' ``` ### GREEN (fix applied) ``` ✓ src/v2/runtime/__tests__/agent-header-precedence.test.ts (2 tests) 2ms ✓ src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts (8 tests) 3ms Test Files 2 passed (2) Tests 10 passed (10) ``` The colliding test (`agent-utils-header-forwarding.test.ts`) was updated from asserting the old bug to asserting corrected precedence + a new case-insensitive-dedup guard. The non-colliding-forward test is retained unchanged as a regression guard. ## Quality gates ``` NX Successfully ran target check-types for project @copilotkit/runtime NX Successfully ran target test for project @copilotkit/runtime — Test Files 113 passed (113), Tests 1576 passed (1576) ``` --- ## `/connect`-path change — forward-looking plumbing, inert today The original issue and a prior eval flagged the same forwarding pattern at `handlers/sse/connect.ts`. To keep the two paths' merge semantics consistent, the `/connect` path now builds the same server-wins merged headers (via the shared `mergeForwardableHeaders` helper) and passes them into `runner.connect()`. **This is not an active auth fix, and it is not red-green-proven as one — because there is no live bug to fix on the connect path today.** No shipped runner consumes the `headers` field of `AgentRunnerConnectRequest`: the in-memory, intelligence, telemetry, and sqlite runners all destructure only `threadId` from the connect request and ignore `headers` entirely. Connect is a thread replay/reconnect, not a fresh outgoing agent call. So whatever headers we pass into `runner.connect()` are dropped on the floor by every runner that ships. What this change actually does: - Threads the per-request agent clone through `handle-connect.ts → handleSseConnect` so the connect path *has access to* the server-configured `agent.headers` (it previously did not). - Passes `mergeForwardableHeaders(agent?.headers, request)` into `runner.connect()` — the correct, server-wins argument **shape** for a future outbound-connecting runner that *would* consume connect-path headers. - Rewrites the comments/JSDoc on this path to say this plainly, rather than implying an active auth fix. It also documents that the connect-site `cloneAgentForRequest` call is the sole `agentId`-existence guard (the intelligence branch never re-validates the id), and documents `cloneAgentForRequest`'s `AbstractAgent | Response` (404) dual-return contract that both callers depend on. The real outbound header forwarding — the thing that fixes CopilotKit#5712 — is the `/run` path's `agent.headers` mutation described above. The connect change is staged plumbing so that if/when a runner starts honoring connect-path headers, it inherits the same server-wins precedence without a second fix. ### Tests on the `/connect` path The connect tests assert the *merge shape* that reaches `runner.connect()` (server value wins on collision, exactly one `authorization` key, non-colliding `x-*` still forwards) and that the agent-undefined case (no server `agent.headers`) degrades to forwarding allowlisted inbound headers only and does not crash. These verify the argument we construct is correctly shaped — not that any shipped runner consumes it. ## Files - `packages/runtime/src/v2/runtime/handlers/header-utils.ts` — shared `mergeForwardableHeaders` helper (case-insensitive, server-wins). - `packages/runtime/src/v2/runtime/handlers/shared/agent-utils.ts` — `/run` path uses the helper so server headers win on collision (**the real fix**). - `packages/runtime/src/v2/runtime/handlers/sse/connect.ts` — `/connect` path uses the helper; forward-looking plumbing, inert until a runner consumes connect-path headers. - `packages/runtime/src/v2/runtime/handlers/handle-connect.ts` — threads the per-request agent clone into `handleSseConnect`. - `packages/runtime/src/v2/runtime/__tests__/agent-header-precedence.test.ts` — `/run` regression test exercising the real `configureAgentForRequest` surface with a real `HttpAgent`. - `packages/runtime/src/v2/runtime/__tests__/agent-utils-header-forwarding.test.ts` — updated the test that encoded the old (buggy) precedence; added a case-mismatch dedup guard. - `packages/runtime/src/v2/runtime/handlers/sse/__tests__/sse-connect-agent-id.test.ts` — connect-path merge-shape + agent-undefined coverage. ### Notes - A documented `@ag-ui/client` `HttpAgent` `fetch` workaround already exists for attaching service-to-service auth the runtime can't override (see the issue). This change makes the workaround unnecessary for the `/run` precedence case. - Conservative scope: this is the **precedence flip on `/run`** plus forward-looking connect plumbing. Tightening the default allowlist (dropping hop-by-hop / platform `x-serverless-*`, `x-forwarded-*`, `x-cloud-trace-context`, …) and an opt-out switch — issue suggestions #2/#3 — are intentionally left as a follow-up to keep the security-policy change minimal.
wolfmanfx
pushed a commit
that referenced
this pull request
Jul 20, 2026
…d failures (CopilotKit#5956) ## Two silent-failure gaps in the showcase build/deploy/notify pipeline These are **pre-existing** silent-failure holes surfaced in code review (not caused by any recent PR). This PR fixes the two load-bearing ones. ### 1. Green-but-zero-redeploy (silent "we thought we shipped but didn't") The `redeploy-staging` job computes the redeploy set as the intersection of the build matrix and the build-success set. This job **only runs when `aggregate-build-results.outputs.any_success == 'true'`** (job-level `if:` guard). So if that intersection comes back **EMPTY**, it does NOT mean "nothing to deploy" — it means at least one slot built successfully yet none of those successes maps back to a matrix `dispatch_name`. That's a `dispatch_name`↔ `service` contract skew (the aggregator's `service` values and the matrix's `dispatch_name` values drifted apart). The old code emitted `services=` (empty) and exited 0 → the build went **GREEN while redeploying NOTHING**, silently. **Fix:** on an empty intersection in this any_success-guaranteed step, fail loud (`::error::` + `exit 1`) with a diagnostic naming both sides of the skew. The legitimate "nothing changed / nothing succeeded" no-op paths are guarded at the **job level** (`has_changes=='true' && any_success=='true'`), so the fixed step never runs there — no false-red. ### 2. Starter build failures had no alert surface (invisible failures) The `notify` job's `needs` (and its `if: failure()`) omitted `detect-starter-changes` and `build-starters`, and `build-starters` wrote no per-slot build-result artifact. So a **failed starter image build produced NO Slack alert and NO PR comment** — it shipped silently. **Fix:** - Added `detect-starter-changes` + `build-starters` to `notify.needs` so `if: failure()` sees a starter build failure → Slack alert + PR comment. - Gave `build-starters` a per-slot build-result artifact **mirroring the main `build` matrix** (same `{service,status}` shape, `cancelled→skipped` normalization, `if: always()`, `if-no-files-found: error`), using a **distinct `starter-build-result-*` prefix** so it never matches the aggregator's `build-result-*` download pattern (starters must not pollute the showcase redeploy set keyed by `dispatch_name`). ### Red / Green **Finding #1** — extracted the step's shell/jq logic and drove it with synthetic inputs: RED (pre-fix), any_success=true + empty intersection: ``` No services in matrix ∩ success-set — skipping redeploy. Computed services CSV (matrix ∩ build-success): EXIT=0 # $GITHUB_OUTPUT: services= -> silent pass, redeploys NOTHING ``` GREEN (post-fix), same inputs: ``` ::error::Build succeeded (any_success=true) but matrix ∩ success-set is EMPTY — dispatch_name/service contract skew; nothing would be redeployed. Successful build service values: ["shell-RENAMED","mastra-RENAMED"] Scheduled matrix dispatch_name values: ["shell","mastra"] EXIT=1 # fails loud ``` No-regression: non-empty intersection → `EXIT=0 ; services=shell`. The nothing-changed/nothing-succeeded paths are skipped at the job level (never reach the step) → no false-red. **Finding #2** — modeled `if: failure()` (fires iff any `needs` job result is `failure`): ``` BEFORE (starters NOT in needs), starter=failure -> notify fires = False (INVISIBLE, the bug) AFTER (starters IN needs), starter=failure -> notify fires = True (FIXED) AFTER no-regression, starters=skipped, all green -> notify fires = False (quiet) ``` ### Validation - `python3 yaml.safe_load` parses OK. - `actionlint`: only pre-existing findings remain (matrix jq SC2086 + the known `depot-ubuntu-24.04-4` runner-label warning); no new errors in edited regions. - `yamllint`: only pre-existing line-length/document-start/truthy warnings. ### Scope Touches **only** `.github/workflows/showcase_build.yml`, and only these two concerns. Does NOT touch the `shell_dashboard` paths-filter region (PR CopilotKit#5955's domain), nor the other backlog debt (false-root-cause comment, double-alert, check-lockfile guard). Self-contained; not stacked on CopilotKit#5955.
wolfmanfx
pushed a commit
that referenced
this pull request
Jul 20, 2026
CopilotKit#6035) ## What Follow-up nit from [PR CopilotKit#5969](CopilotKit#5969) review ([OSS-488](https://linear.app/copilotkit/issue/OSS-488)). Fixes the `getMessages` JSDoc in `packages/channels-intelligence/src/intelligence-adapter.ts`, which oversold image support. The comment cited *"what was in the image"* as a `read_thread` use case, but image/file parts contribute **no text** in this mapping — `read_thread` is text-only by design. Image *content* reaches the model only via `conversationStore`'s seeding of `agent.messages`. The comment now says so explicitly instead of implying `read_thread` can see image content. Comment-only change — no runtime/API behavior change, so no changeset. ## Scope of OSS-488 The ticket listed three nits; this PR addresses the one still actionable: - **#1 (double-space when joining content parts)** — ✅ already resolved on `main` (the `.filter(Boolean).join(" ")` fix + updated test landed via the OSS-476 CR round). Nothing to do. - **#2 (JSDoc oversells image support)** — ✅ **this PR**. - **#3 (duplicate `getHistory` fetch per turn)** — intentionally **deferred**. A per-turn memo would need turn-scoped caching with a clear-on-turn-start hook; without one it risks serving stale history — a correctness regression on something the ticket itself rates "acceptable for an on-demand tool." Not worth it here. The **known limitation** (speakers collapse to `user`/`bot` because upstream `AgentMessage` carries no names) is unchanged and not actionable at this layer. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wolfmanfx
pushed a commit
that referenced
this pull request
Aug 6, 2026
Follows the maintainer's Correction #2 on issue 6363. An exact version in a docs install command is the same rot as the starlette==0.45.3 pin it replaced: it goes stale silently and nobody re-resolves prose. The 2.22.0 the docs shipped was already a version behind current the day it was written. - docs install lines use pydantic-ai-slim[ag-ui,openai]>=2,<3, which constrains the dep the pages actually care about and fails loudly at the v3 boundary - ag-ui-protocol drops out of the docs lines entirely; no doc snippet imports ag_ui, so naming it there was the transitive-dep noise the correction is about - starlette>=0.46.2 stays, because the v2 snippets import Starlette directly. A floor with no ceiling cannot force a downgrade, so it does not recreate the silent backtrack - examples/showcases/pydantic-ai-todos moves to a range in pyproject.toml and relocks; the uv.lock is what reproduces - examples/canvas/pydantic-ai keeps exact pins: it has no lockfile, so requirements.txt is its only reproducibility artifact Smoke-tested the open question from the issue: starlette 1.x works on pydantic-ai v2. All 8 doc pages pass on 2.23.0 + starlette 1.3.1 and on 2.23.0 + starlette 0.52.1, so Jordan's <1.0 guard can be dropped rather than raised.
wolfmanfx
pushed a commit
that referenced
this pull request
Aug 14, 2026
…wave slots could not reach Comment-only, no behaviour change. Each of these sat OUTSIDE the boundary of the slot whose work falsified it, so each described the tree as it was mid-migration. All verified against the current tree before editing. src/shell/skin-contract.ts - `RuntimeProviders`: "airline needs neither, so omits it". Airline omits `RuntimeProviders` and DOES supply `useRuntimeProperties` — the two are SEPARABLE, and airline is now the worked example: one account holder, no switcher, so its hook reads no context and returns a frozen module constant. Do not mount an empty provider for symmetry. - `useData`: "Omit when a skin has no shell-managed data (banking …)". Every skin omits it now; documented with the grep, plus WHY the field is kept (the shape is legitimate, it just has no worked example left). - `CanvasSurface`: the "omit if the skin has no report canvas" branch is currently unexercised — every shipped skin has one. src/shell/agent-registry.ts - The logistics entry said it ships "neither `intelligence/seed-memories.ts` nor `intelligence/forget-memories.ts`" and is "identity plumbing only — do NOT read it as a durable-memory demo". It ships both (`ls src/skins/*/intelligence/`), and its `dev/reset` sweeps and re-seeds through them. Corrected, with the same properties-forwarding caveat the other five entries carry, and a note that "expensive half built, cheap half skipped" was its state for two releases. src/skins/keel/data/types.ts - The `THE REST SUBSTRATE` banner said the two substrates are "deliberately not merged yet", that `useKeelData` "holds runs in `useState` and ticks them on a 900 ms interval", and that "the pages still read it through `useSkinData`". All three are false: one substrate, one clock, `useSkinData` returns undefined. Rewritten to name the server-settled read (`settle-runs.ts`, called by both `GET /ledger` and `GET /runs/[runId]`) and to record WHY the deleted client ticker was a defect rather than a design choice — it was a second clock that painted progress the server never heard of, which the next re-read after any write silently rewound. - The `KeelData` interface header claimed to be "the interface every page, component, and tool codes against". It is not referenced by any code at all (`grep -rn KeelData src` returns only comments). Marked HISTORICAL, with a do-not-add-a-consumer note. NOT deleted: it is a doc pass, several comments across the skin describe the migration in terms of this shape, and removing an exported type is a code change for a separate commit. Flagged as a follow-up. src/skins/keel/data/store.ts - "it does not advance them on a timer, because the ticker lives in `useKeelData` on the client. Whichever slot migrates that hook has to decide where the ticker ends up" — decided: the server is the only clock now. src/skins/keel/data/beat-map.md - Header: "Keel today is `useKeelData`, an in-memory `useState` store, and it hits about one beat." Marked BUILT and reframed as the design record. - Risk #2 (the two-substrates/ticker question, correctly called "the biggest single risk in the migration") marked RESOLVED, with the answer (move the clock, do not relocate the ticker) and the generalised lesson. src/skins/airline/data/beat-map.md - Header: the tools/prompt/pages/pills were "later slots". All landed. - § "What this slot did NOT build": every row of the deferral table has shipped. Kept as the retrofit record — which is the most useful thing about it — with a third column saying where each landed, and the three flagged traps marked resolved (including "the reset route says memoryBeats: unarmed on purpose", which was removed in exactly the change that added the seed module, as instructed). src/skins/airline/data/fare-waiver-codes.ts - "⚠️ THE LINT GUARD DOES NOT COVER THIS SKIN YET." It does: both `src/skins/airline/tools.tsx` and `agent.ts` are in `withheldGateVocabulary`'s `files` glob. Also dropped its "COUNT the selectors" instruction (that count has rotted twice) in favour of the resolved-selector table in `skins-config.test.ts`, and spelled out that a green lint still leaves the three prose channels AND `waiverGround` — which matches no `*_CODES` pattern, so the rule cannot see it — as hand-review items. src/skins/airline/tools.test.ts - Header said `statusKeyedTerminalRender` "covers logistics only; airline's glob entry is a later slot's, so until it lands this file is the whole guard" and that `withheldGateVocabulary`'s glob "does not list airline yet either". Both globs list airline now. Also fixed "Three defect classes" over a list of five. src/skins/keel/skin.tsx - "exactly as it does for the four other REST-backed skins" → every skin; nothing sets `useData`. src/proxy.ts - "matching how the other three skins behave" → numeral-free. This was one of the two known-stale instances named in `skin-roster-docs.test.ts`'s header; that header is updated in the app-docs commit, and the remaining one (`e2e/inset-layout.spec.ts`'s hardcoded four-skin loop) is deliberately left — fixing it means adding assertions against skins the spec has never visited, which is a coverage change rather than a prose fix. docs/teach-mode/README.md - It correctly refuses to write the roster into prose, but its verified-by-role paragraph named only banking/commerce/logistics/people and its "so copy commerce or logistics" line named the only two skins with pinned replay behaviour. The `offerWorkflowRecording` grep now returns every registered skin, and `ls src/skins/*/teach-mode-directives.ts` — added as the mechanical discriminator for role #3 — returns four. Also: "logistics and commerce both" skip project-scoped rows in `forget-memories.ts` is now every skin but banking, replaced with the grep that proves it. Does this make anything in .claude/skills/reskin/ stale? No — the reverse. The preceding commit updated the skill for exactly these facts, and these comments were brought into line with it. Checked: `grep -rn "useKeelData\|use-data\|in-memory" .claude/skills/reskin/` names no path or symbol that no longer exists. Verified: pnpm lint, pnpm exec tsc --noEmit, pnpm test:unit (197 files / 2227 tests), pnpm build — all green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
wolfmanfx
pushed a commit
that referenced
this pull request
Aug 19, 2026
…angGraph docs gaps (CopilotKit#6520) Bundles the CopilotKit-side work from **OSS-769**, **OSS-767** (partial), and the unshipped remainder of **OSS-609**. ## OSS-769 — `useCoAgent().nodeName` never updates `useAgentNodeName` tracked the current node in a ref and returned `nodeNameRef.current`. Mutating a ref schedules no render, so a component reading `useCoAgent().nodeName` kept showing whichever node was current at its last render and never updated on its own — it only appeared to work when something unrelated happened to re-render it. Backing the value with state fixes it. Five lines, no API change. ## OSS-767 (partial) — silent content-part drops (CopilotKit#1748) `normalizeMessageContent` handles only `text` and `binary` parts; anything else — the `{"type": "image", ...}` case from the report — maps to `""` and is filtered out with no signal, so an agent emitting structured content sees its output vanish silently. This makes the drop visible, once per unrecognised part type so streaming doesn't flood the log. Deliberately **not** the schema change: carrying structured assistant content needs an `AssistantMessage` decision upstream in `ag-ui`, which stays open on OSS-767. ## OSS-609 — the five docs gaps that never shipped Gap #2 shipped in CopilotKit#6403; gaps 1, 3, 4, 5 and 6 did not. | Gap | Where | Closes | | --- | --- | --- | | AWS Lambda self-hosting | `docs/deploy/aws-lambda.mdx` | CopilotKit#1151 | | Per-user thread authorization | `docs/auth.mdx` (new section) | CopilotKit#2241 | | Thread persistence without the platform | `docs/threads-self-managed.mdx` | CopilotKit#6090 | | DIY guardrails / DLP | `docs/integrations/langgraph/guardrails.mdx` | CopilotKit#3414 | | When you need an MCP App | `docs/agentic-protocols/mcp.mdx` (new section) | CopilotKit#5991 | The Lambda guide leads with the constraint that actually bites — streaming is opt-in on every front door, so a chat runtime deployed with the defaults appears to hang for the whole run and then dumps the reply at once. It documents the Function URL + `RESPONSE_STREAM` path as the default, and API Gateway REST + `responseTransferMode: STREAM` for anyone who needs a REST API in front. `threads-self-managed` follows the existing shared-snippet pattern with per-framework wrappers, because two nav contracts require it: every authored framework must publish the page, and every React destination must map to an Angular one (`ANGULAR_DOC_REDIRECTS`). All nine wrappers were confirmed necessary by deleting one and watching the suite fail. ## Review corrections Two blockers from @MikeRyanDev, both verified against primary sources before changing anything. **API Gateway REST APIs can stream** ([`91b3e632d5`](CopilotKit@91b3e632d5)). The guide was built on the pre-November-2025 limitation and claimed no API Gateway type supports response streaming, steering readers to a buffered `serverless-http` setup. REST gained it via `responseTransferMode: STREAM`, which also lifts the 10 MB cap and 29-second timeout. REST and HTTP are now split; REST is documented as a streaming front door (payload-format-1.0 event adapter, `AWS_PROXY` integration on the `2021-11-15/.../response-streaming-invocations` URI, CLI/CDK/SAM config), and the buffered fallback is scoped to HTTP APIs and ALB, which still have no streaming path. Added the constraints that actually matter for chat: the 30-second idle timeout on edge-optimized endpoints (5 min Regional), and the console Test tab always buffering so a working config looks broken. **`identifyUser` is the platform's thread-scoping binding** ([`91b3e632d5`](CopilotKit@91b3e632d5)). The section told every reader to build an ownership table and enforce it in `onBeforeHandler`. On the Intelligence path the runtime already resolves `identifyUser(request)` server-side and carries that id to the platform; `listThreads` is scoped by user *and* filtered by `agentId`, so the "every user of one project sees that project's threads" claim was wrong. `identifyUser` is now documented as the binding, and the DIY pattern is scoped to SSE runtimes, custom stores, and the local in-memory runner. **Follow-up correction — two routes are genuinely unscoped** ([`240672ff56`](CopilotKit@240672ff56)). My rewrite then over-claimed. `handleGetThreadEvents` and `handleGetThreadState` resolve the caller and discard it, and the platform client takes no `userId` on either method (`client.ts:1113`/`1135`) — unlike `getThreadMessages` at `1063`. Both hit project-authenticated `_inspect` endpoints, so any caller `identifyUser` accepts can read the event log and agent state of **any thread in the project** given its id. The blanket guarantee is replaced by a per-route table marking those two explicitly unscoped, plus an `onBeforeHandler` guard narrowed to them. That is a live gap in shipped runtime code, not a docs error, and it is tracked as **OSS-851** — a platform-side `_inspect` change plus matching runtime/client work and tests, out of scope for a docs PR. The interim callout in `auth.mdx` comes out when OSS-851 lands. ## Not in this PR **OSS-772** and **OSS-773** are already merged in `oss-path-to-production` (CopilotKit#237, CopilotKit#238). Both are telemetry-sink changes with no CopilotKit-side component. OSS-773's remaining half — re-keying runtime `distinct_id` from email to the Clerk subject — is recorded on the ticket as an open decision, not a task. ## Testing **OSS-769.** New `use-agent-nodename.test.tsx`, 5 tests. Against unmodified `origin/main`, **4 of 5 fail**: ``` × re-renders consumers on every node transition × reports 'end' when a run errors × resets to 'start' when a new run begins ✓ unsubscribes on unmount × carries the agent, thread, and current node Tests 4 failed | 1 passed (5) ``` With the fix: `Tests 5 passed (5)`. These assert only re-render behaviour under normal `act()` flushing — no manufactured intra-batch window. **Full react-core suite:** `Tests 7 failed | 1496 passed (1503)`. All 7 failures are **pre-existing** `ResizeObserver is not a constructor` under jsdom, confined to `CopilotChatView.pinToSend` and `use-pin-to-send` — neither of which this PR touches. **Typecheck:** `packages/react-core` → `tsc --noEmit` exit 0, no output. **OSS-767:** 3 new tests covering the warn, warn-once-per-type, and no-warn-for-supported-types. `src/graphql/message-conversion/` → `Tests 125 passed (125)`. **Docs:** `showcase/shell-docs` → `Tests 1 failed | 373 passed (374)`. The single failure (`channels-docs > publishes the Channels overview only through provider navigation`) is **pre-existing**; baselining with all changes stashed reproduces it and nothing else. Re-run unchanged after both review-correction commits. **Review corrections.** The AWS rewrite was checked against the AWS sources rather than written from memory — the REST streaming announcement, `configuration-response-streaming`, `response-transfer-mode` (endpoint-type idle timeouts, unsupported buffered-only features), `response-streaming-lambda-configure` (CLI/OpenAPI shapes), the CFN `Integration` reference, and the CDK `ResponseTransferMode` enum. Two details corrected in passing: `InvokeWithResponseStream` authorizes against plain `lambda:InvokeFunction` (no new grant, contrary to what the streaming URI suggests), and ALB still has no Lambda streaming path. The auth corrections were verified by reading the handlers and the platform client, not the tests — `resolve-intelligence-user.ts`, `intelligence/threads.ts` (every `resolveIntelligenceUser` call site), and `intelligence-platform/client.ts`. The existing tests assert the `threadId`-only call shape, so they pass under the defect and could not have surfaced it. Also corrected: there is no `threads/delete` route — delete is `DELETE` on `threads/update` (`fetch-handler.ts:606`). Both edited pages MDX-compile clean, and all inbound `#thread-authorization` anchors still resolve after the h3→h4 demotions. Two nav tests broke during this work and are fixed rather than papered over — adding a page to the Rich Threads group violated the cross-framework ordering contract and the React→Angular parity contract: ``` src/lib/__tests__/docs-render.test.ts src/lib/__tests__/angular-docs-content.test.ts Test Files 2 passed (2) Tests 33 passed (33) ``` All 15 internal links in the new pages resolve against the content tree. Closes CopilotKit#1151, CopilotKit#2241, CopilotKit#3414, CopilotKit#5991, CopilotKit#6090 Refs CopilotKit#1748, OSS-851
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Replace
effect()-driven collapse/expand behavior inCopilotChatReasoningMessagewith derived signals/computed state.Why
Avoid propagating UI state via
effect(); keep toggle behavior deterministic while streaming.Tests
pnpm -C packages/angular test -- -t "renders streaming reasoning messages"Made with Cursor