mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
codex/cloudplot-showcase-migration
15424 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8e59bfd16b |
feat(skills): add channels-setup pointing at the hosted onboarding guide (#6366)
## Summary The Channels onboarding workflow is served at **https://copilotkit.ai/channels-guide.md**, and every other entry point now copies one line that points there — the docs surfaces in #6357, the website strip in [website#444](https://github.com/CopilotKit/website/pull/444), the README in [channels-sdk#15](https://github.com/CopilotKit/channels-sdk/pull/15). Coding agents reached through a **skill** had no such pointer. They matched `setup-slack-channel`, which is scoped to Slack, to the provider half, and to an OpenTag checkout — so "help me get my agent into Teams" landed on a workflow that does not cover it. `channels-setup` closes that gap as a **pointer, not a copy**. The workflow stays in one place and is corrected there, instead of becoming a seventh surface that drifts against the CLI on its own schedule. ## Verifying the fetch is the substance of the file A thin pointer has one non-obvious failure mode, and it is the reason this skill is more than two sentences: ``` $ curl -sL -o /dev/null -w "%{http_code} %{content_type}\n" https://copilotkit.ai/channels-guide.md 200 text/html; charset=utf-8 $ curl -sL https://www.copilotkit.ai/channels-guide.md | grep -o "<title>[^<]*</title>" <title>Page not found | CopilotKit | ...</title> ``` **A missing guide does not return 404.** The site answers unknown paths with a "Page not found" HTML page under HTTP 200. An agent that keys on the status code gets a 73KB marketing page, concludes the fetch succeeded, and improvises channel setup from memory — which is exactly what the guide's boundaries exist to prevent, and it fails in the most expensive way available here: the project installs cleanly and answers nothing. So the skill checks the **body**: markdown rather than HTML, the guide's `# Build and prove a CopilotKit Channels agent` H1, and five `## Phase` headings. If any fail it stops and hands the user the URL, no matter what the status code said. ## Notes - **`RESERVED_LIFECYCLE_SLUGS` is not optional.** Standalone skills are not generated from `packages/*/skills`, so without the entry `sync-plugin-skills` treats the directory as an orphan and deletes it. Test updated alongside, including the hard-coded `size` (10 → 11). - **No existing skill is modified.** `setup-slack-channel` and `copilotkit-channels` keep their descriptions and bodies. Three skills now match channel work; if that proves too ambiguous in practice, narrowing the other two's `description` frontmatter is a follow-up, not a body rewrite. - **No manifest change.** `plugin.json` and `marketplace.json` do not enumerate skills individually. - No changeset — `packages/**` is untouched. ## Blocked on **The guide is not live yet.** [website#444](https://github.com/CopilotKit/website/pull/444) is still open, so the URL currently serves the 404 page shown above. This skill is inert until that merges and deploys — at which point it works with no further change here. The sentinel means the pre-deploy state is a clean stop rather than a wrong answer, so merging early is safe; it just isn't useful yet. ## Validation - `npx vitest run scripts/__tests__/sync-plugin-skills.test.ts` — 10 passed - `pnpm run check:plugin-skills` — `plugin skill mirror in sync`, exit 0; `skills/channels-setup/` survives the orphan pass - `npx oxfmt --check` on both changed TS files — clean - `npx oxlint scripts/` — 0 errors - `SKILL.md` frontmatter parsed with js-yaml: `name=channels-setup`, `version=1.0.0`, description 660 chars - Soft-404 behaviour confirmed against production with the `curl` commands above 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
14a294f2d8 |
Port the Pydantic AI example to Pydantic AI v2 (#6355)
This pull request was posted by Claude Code using claude-opus-5 on behalf of David. David has not reviewed this diff. `examples/integrations/pydantic-ai` only runs on Pydantic AI **v1**. `Agent.to_ag_ui()`, `AGUIApp`, and the `pydantic_ai.ag_ui` shim were all removed in v2 (pydantic/pydantic-ai#5464, announced in pydantic/pydantic-ai#5345). The example pins `pydantic-ai-slim==1.0.18`, so anyone installing it against current Pydantic AI (2.22.0) fails at import. This ports it to v2. ## Changes 1. `agent/pyproject.toml` — `pydantic-ai-slim[ag-ui,openai]>=2.0.0`, `ag-ui-protocol>=0.1.19` 2. `agent/src/agent.py` — `StateDeps` moved from `pydantic_ai.ag_ui` to `pydantic_ai.ui` 3. `agent/src/main.py` — serve via `AGUIAdapter.dispatch_request` on a Starlette route 4. `agent/uv.lock` — relocked (resolves `pydantic-ai-slim` 2.22.0, `ag-ui-protocol` 0.1.19) ## One fix beyond the mechanical port The old wiring built the app once around a single shared `StateDeps` instance. `dispatch_request` mutates `deps.state` with the state the client sends, so one shared instance lets state leak between threads, channels and users — which matters more for Channels than it did for a single browser tab. Each request now gets its own `replace(deps)` copy, matching the pattern in [Pydantic AI's own AG-UI examples](https://github.com/pydantic/pydantic-ai/blob/main/examples/pydantic_ai_examples/ag_ui/api/shared_state.py). <details><summary>Verified end to end</summary> `uv sync` + a request through the actual ASGI app (model overridden with `TestModel` so no API call), with a Channels-shaped payload (`threadId`, `state`, `forwardedProps`): ``` health: 200 {'status': 'ok'} POST / -> 200 text/event-stream; charset=utf-8 event types: ['RUN_STARTED', 'TOOL_CALL_START', 'TOOL_CALL_END', 'TOOL_CALL_START', 'TOOL_CALL_ARGS', 'TOOL_CALL_END', ..., 'TOOL_CALL_RESULT', 'STATE_SNAPSHOT', 'TOOL_CALL_RESULT', 'STATE_SNAPSHOT', 'TEXT_MESSAGE_START', 'TEXT_MESSAGE_CONTENT', ..., 'TEXT_MESSAGE_END', 'RUN_FINISHED'] threadId echoed: slack-C123-thread-1 tool call: get_proverbs tool call: add_proverbs tool call: set_proverbs tool call: get_weather ``` The emitted event set is exactly what `channels-slack`'s `RunRenderer` subscribes to (`RUN_*`, `TEXT_MESSAGE_*`, `TOOL_CALL_*`), plus `STATE_SNAPSHOT` which it ignores. </details> <details><summary>Note on <code>ag-ui-protocol</code>: 0.1.19, not 0.1.18</summary> Not required by this port, but worth pinning forward: typed multimodal input content (`ImageInputContent` &c.) landed in 0.1.15 and the interrupt lifecycle in 0.1.19. We found that a Pydantic AI install below 0.1.15 rejects an inbound image attachment with a 422 rather than skipping it — so a Channels gateway forwarding a Slack image needs the newer floor. We're tracking that on our side. </details> |
||
|
|
d19bd0f81c |
docs(pydantic-ai): port integration docs and demos to Pydantic AI v2
Agent.to_ag_ui(), AGUIApp and the pydantic_ai.ag_ui module were removed in Pydantic AI v2. The docs installed pydantic-ai unpinned, so anyone following the quickstart got 2.22.0 and failed first at dependency resolution (starlette==0.45.3 conflicts with the >=0.46.2 the ag-ui extra needs) and then at AttributeError. - 8 doc pages under showcase/shell-docs .../integrations/pydantic-ai serve the agent from a Starlette route via AGUIAdapter.dispatch_request - StateDeps moves from pydantic_ai.ag_ui to pydantic_ai.ui - stateful snippets build StateDeps per request; dispatch_request writes the client's state into deps.state, so a shared instance leaks state between users - install commands exact-pin pydantic-ai-slim==2.22.0 and ag-ui-protocol==0.1.19 - examples/canvas/pydantic-ai and examples/showcases/pydantic-ai-todos ported and pinned, todos relocked - skills/copilotkit-integrations reference updated to the same shape showcase/integrations/pydantic-ai is deliberately untouched; it is tracked separately. |
||
|
|
7b5cc8ccf9 |
fix(channels): recover from transient gateway outages (#6347)
## What changed - Mark initial gateway HTTP 5xx and transient transport failures as retryable. - Retry initial managed Channel activation with exponential backoff from 1 second to a 30-second cap until it connects or the manager stops. - Preserve retry hints from `gateway_draining` join replies and retry initial join timeouts. - Keep HTTP 4xx and NXDOMAIN failures terminal. - Back off established-session outage reminders from 30 seconds to a 15-minute cap while Phoenix continues reconnecting. ## Why The OpenTag Railway runtime saw the gateway host return HTTP 502 during an outage. Established Phoenix sessions keep retrying, but a runtime that starts during the outage stops after its one initial connect window. It cannot recover when the gateway comes back unless the process restarts. Fixed 30-second reminder logs also flood long outages. The gateway drain work now rejects new joins with a structured retryable response. The client must preserve that response so the runtime can retry instead of leaving the Channel in a terminal error state. ## Companion change CopilotKit/OpenTag#25 keeps the Railway HTTP server alive while an initial Channel retry is pending. OpenTag must consume a CopilotKit release containing this PR before that companion change can recover by itself. ## Validation - `pnpm nx run-many -t test,check-types,build -p @copilotkit/runtime,@copilotkit/channels-intelligence` - `pnpm nx run-many -t publint,attw -p @copilotkit/runtime,@copilotkit/channels-intelligence` - pre-commit tests and package checks for all affected projects - `pnpm exec oxfmt --check` on all five changed files - `pnpm exec oxlint` on all five changed files - `git diff --check` |
||
|
|
4be25161b1 |
fix(examples): pin pydantic-ai starter deps and build deps per request
Address review on CopilotKit/CopilotKit#6355: - Pin `pydantic-ai-slim[ag-ui,openai]==2.22.0` and `ag-ui-protocol==0.1.19`, matching the starter fleet standard. Open-ended floors would pull a breaking major on release, and `docker/Dockerfile.agent:16` runs a bare `uv sync` that would silently re-resolve forward. - Mirror the specifiers in `uv.lock`'s `requires-dist` so `uv sync --frozen` at `Dockerfile:42` keeps working. Resolved versions and hashes are unchanged; `uv lock` is a no-op. - Construct `StateDeps(ProverbsState())` per request instead of `dataclasses.replace()`. `replace()` is a shallow copy, so the new deps point at the same state object; it is only safe today because `StateDeps` has one field that the adapter rebinds before every run. Any deps class with a second mutable field would silently share it. |
||
|
|
4c2ba2d161 |
feat(skills): add channels-setup pointing at the hosted onboarding guide
The Channels onboarding workflow is served at https://copilotkit.ai/channels-guide.md, and every other entry point now copies one line that points there (#6357 for the docs surfaces). Coding agents invoked through a skill had no such pointer, so they fell back to setup-slack-channel, which is scoped to Slack, to the provider half, and to an OpenTag checkout. channels-setup is a pointer, not a copy. The workflow stays in one place and is corrected there instead of drifting against the CLI on its own schedule. Verifying the fetch is the substance of the file. The site answers unknown paths with a "Page not found" HTML page under HTTP 200, so a status check proves nothing: an agent that keys on the status code gets a marketing 404 and improvises the workflow from memory. The skill checks the body instead -- markdown, the guide's H1, five Phase headings -- and stops if any fail. Standalone skills are not generated from packages/*/skills, so the slug is registered in RESERVED_LIFECYCLE_SLUGS. Without that entry the sync treats the directory as an orphan and deletes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
397c8ce2e4 |
docs: route every Channels entry point at one onboarding skill (#6357)
## Visual Docs `/slack` — the featured panel from #6356, now carrying the one-line pointer. | Desktop screenshot | Mobile screenshot | |---|---| |  |  | | Desktop scroll | Mobile scroll | |---|---| | <video src="https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-desktop-scroll-7f14af56.webm" controls width="400"></video> | <video src="https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-mobile-scroll-7f14af56.webm" controls width="240"></video> | Docs `/teams` — same component, Teams wording, proving the frontend switch. | Desktop screenshot | Desktop scroll | |---|---| |  |  | <sub>Artifacts are hosted on a CopilotKit/website release tag rather than a new tag in this repo, to keep clear of the monorepo's release automation.</sub> --- ## Summary Builds on #6356 (merged in here, its featured-panel treatment kept) and finishes the job it started. #6356 made the Channels starter prompts **discoverable**. This makes them **correct**, and makes every other road say the same thing. The onboarding workflow was written out as prose on six surfaces across three repos: the Channels overview page (Slack and Teams), the docs landing activation strip, copilotkit.ai/channels, and the channels-sdk README twice. They drifted apart, and each went stale against the CLI on its own schedule. That drift is what the dogfooding feedback was reporting: - **"It didn't tell me which skills to install, so it dropped me into a list of god knows how many."** Neither copy button named a skill. - **"`npx copilotkit project select` didn't work — I had an old CLI."** Only some surfaces pinned `@latest`. - **An agent halted on `copilotkit channels`.** No such command exists in the published CLI — verified against 4.5.1, whose `--help` has no `channels` entry. - **"Copy this prompt is super easy to miss."** The strongest prompts were behind an accordion; the two visible buttons had invisible payloads. ## What changed - **`<ChannelsStartPrompt />`** — one shared entry point, frontend-aware, wearing #6356's featured treatment: accent panel, terminal mark, eyebrow, prominent copy action. - **Both overview accordions are gone.** The accordion existed because the payload was twenty lines. The payload is now one sentence, so hiding it behind "Open & copy prompt" costs a click and buys nothing. - **The activation strip emits the same pointer**, rendered on screen rather than living only in a clipboard payload. - **Both surfaces send `promptCopied` with a `surface` property**, so the funnel can answer which road people take. The website emits its own event name with the same property for the same reason. The prompt every surface now emits: ``` Run `npx copilotkit@latest skills install --skill setup-slack-channel -y`, then follow that skill to build your first CopilotKit Channels agent and connect it to Slack using CopilotKit's built-in agent. ``` ## Notes for reviewers - **`-y` respects `--skill`** — verified empirically against `copilotkit@4.5.1`: exactly one skill installs, no picker. The installer detects the coding agent it runs inside, so `--agent` is omitted. - **The panel renders exactly what the button copies.** Two earlier shapes were wrong in instructive ways: a full monospace paragraph wrapped like a rendering bug, and a code block with the ask underneath read as a shell command with a footnote — which made a button labelled "Copy prompt" look like it was lying. - **The `featured` Accordion variant from #6356 stays in `mdx-components.tsx`** as a shared opt-in capability, but its only two consumers were the accordions removed here, so it is currently unused. Worth a call: keep it, or drop it in a follow-up. - **`setup-slack-channel` is the slug the combined onboarding prompt will occupy** once it lands from channels-sdk. Its frontmatter is currently scoped hard to the Slack provider half, so it needs rescoping before this reads correctly for Teams. Companion PRs: CopilotKit/website#444 and the channels-sdk README. ## Validation - `npx vitest run` in `showcase/shell-docs` — 54 files, 372 tests passed - `npx tsc --noEmit` — clean - pre-commit lint and commitlint hooks passed - Rendered and reviewed `/slack` locally; confirmed the copied string matches the rendered one ## Update — impressions and a copy-only prompt Since the artifacts above were first posted: - **Impression event.** `docs.channels_activation_viewed` fires once per surface on first intersection at 50%. Both docs entry points previously emitted a copy event and nothing else, so the copy count had no denominator. `surface` values now live in a shared `CHANNELS_ACTIVATION_SURFACES` map, keeping the landing strip, the overview panel, and copilotkit.ai/channels separable inside one funnel. - **Guarded on `typeof IntersectionObserver`.** An impression is never worth breaking a render for, and this repo's jsdom tests do not define it. - **The prompt is no longer rendered.** Per review, the copy button carries the payload. The panel leads with the copy action directly under the heading — the prompt is not on the page, so the button is the point of the panel rather than trailing furniture — and the supporting line no longer says "paste this" next to nothing. Screenshots and videos above were re-captured after all of it. ## Validation (current) - `npx vitest run` in `showcase/shell-docs` — 55 files, 376 tests passed - `npx tsc --noEmit` — clean - pre-commit lint and commitlint hooks passed - Reviewed `/slack` and `/teams` at 1440×900 and iPhone 13 ## Companion PRs - CopilotKit/channels-sdk#15 — **merged** - CopilotKit/website#444 — open --- ## Update — addresses both review blockers ### Blocking issue: the skill did not support the promised workflow — resolved by removing the skill from the path The pointer is now a fetch of one hosted file, not a skill install: ``` Read https://copilotkit.ai/channels-guide.md and help the user build their first channel ``` The guide lives at `public/channels-guide.md` in CopilotKit/website and owns the whole workflow. It asks the developer which platform and which agent framework they want, which is why the pointer passes neither. That removes the mismatch at its root rather than narrowing the copy around it. Naming the picker's channel and backend meant these pages promised coverage on `setup-slack-channel`'s behalf, and that skill is scoped to Slack, to the provider half, and to an OpenTag checkout — so Teams pointed at a workflow that does not exist and the picker implied nineteen backends it never claimed. A pointer that names nothing cannot overpromise. `CHANNELS_ONBOARDING_SKILL`, its install command, and the per-selection prompt builders are gone; one constant serves every surface. ### Visual regression: disclosure restored, treatment re-tokenised | Collapsed | Expanded | |---|---| |  |  | | Mobile | Teams variant | |---|---| |  |  | - **The expandable preview is back.** The panel is the shared featured `<Accordion>` again — reused, not re-implemented — so the overview stays compact and a reader can expand to read the exact prompt before copying. - **Whitespace.** Collapsed height went from ~380px to ~110px with `p-4`, matching every other docs panel. The page's own introduction is visible without scrolling. - **Colour.** You were right, and my "it is unchanged from #6356" reply missed the point. `copilotkit-ui-theme` names *"purple accent bar or stripe"* as a known wrong direction, and `copilotkit-branding` requires accent to be restrained with gradients behind content rather than as the contrast layer — the saturated `--accent` tile plus accent-mixed gradient was both at once. It now matches the `OpsPlatformCTA` idiom: `--bg-elevated`, `--border`, `--shadow-control`, accent only on a 20px glyph and the hover state. Verified light and dark. One deliberate deviation: I dropped the "READY-TO-USE STARTER PROMPT" eyebrow on the low-noise rule, since the title says the same thing. Easy to restore if you want it. ### Behavioural bug: fixed `copyPrompt` shared one `try` with `posthog.capture`, so a throwing client reported "Copy blocked" for a prompt already on the clipboard. Capture now sits behind the same isolated helper `ChannelsActivationStrip` uses. Two regression tests: capture throwing after a resolved write still shows "Copied"; a rejected write still shows "Copy blocked" and emits no copy event. ### Lockfile churn: dropped Reverted to `origin/main`. It came from an `npm install` in the review worktree; no dependency changed. ### Also added `docs.channels_activation_prompt_expanded` — the disclosure is a funnel step neither `viewed` nor `promptCopied` can see, so someone who never opened the panel was indistinguishable from someone who opened it and left. ## Validation (current) - `npx vitest run` in `showcase/shell-docs` — 55 files, 376 tests passed - `npx tsc --noEmit` — clean; pre-commit lint and commitlint passed - Reviewed `/slack` and `/teams`, collapsed and expanded, light and dark, at 1440×900 and iPhone 13 |
||
|
|
d92652dcdd |
chore: release monorepo v1.66.1 (#6365)
## Release monorepo v1.66.1 **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.66.1` 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.66.1` - Creates git tag `monorepo/v1.66.1` - 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.66.1 |
||
|
|
c69f7e96a5 | chore: release monorepo v1.66.1 | ||
|
|
a1d27ab017 |
feat(shell-docs): strip both Channels surfaces to one copy action
**Root page.** The channel and agent-backend pickers are gone, and the copy action moves beside the supporting sentence, under the heading. The pickers had stopped earning their place: the guide asks which platform and framework the developer wants, so choosing here asked the same question twice and changed nothing about what got copied. `ActivationSelect` and its option plumbing go with them. The setup-guide link becomes one aside alongside OpenTag rather than a per-selection route. **Per-framework pages.** The accordion is gone. It existed to keep a twenty-line prompt out of the way; the payload is now a single action, so a disclosure cost a click and revealed nothing. The panel keeps the in-content idiom it shares with `OpsPlatformCTA` — neutral surface, `--border`, accent on a small glyph — and the button reads "Copy prompt" like every other surface. `docs.channels_activation_prompt_expanded` retires with the disclosure that was its only trigger. |
||
|
|
d8eeb77aef |
chore: release channels v0.7.1 (#6361)
## Release channels v0.7.1 **Scope:** `channels` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `channels` packages to `0.7.1` 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 `channels` packages to npm at version `0.7.1` - Creates git tag `channels/v0.7.1` - 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.channels/v0.7.1 |
||
|
|
f8f9e2a721 | chore: release channels v0.7.1 | ||
|
|
dfe728d833 | fix(channels): retry gateway drain joins | ||
|
|
82eaecdfda | fix(channels): retry transient gateway activation | ||
|
|
503fc7c593 |
fix(channels): back off prolonged outage logs
Keep drop, give-up, and recovery logs immediate. Space repeated still-down reminders during long gateway outages. |
||
|
|
335b12a96a |
feat(shell-docs): point the Channels prompt at the hosted guide
Replaces the skill-install pointer with a fetch of one hosted file:
Read https://copilotkit.ai/channels-guide.md and help the user build
their first channel
The guide lives at `public/channels-guide.md` on the marketing site and owns the
whole workflow. It asks the developer which platform and which agent framework
they want, which is why this pointer passes neither.
That is what resolves the review's blocking issue rather than papering over it.
Interpolating the picker's channel and backend meant these pages promised
coverage on a skill's behalf — and the skill it named is scoped to Slack, to the
provider half, and to an OpenTag checkout, so the Teams road pointed at a
workflow that does not exist and the backend picker implied nineteen it never
claimed. A pointer that names nothing cannot overpromise, and the guide handles
selection itself.
Consequently `CHANNELS_ONBOARDING_SKILL`, its install command, and the
per-selection prompt builders are gone: one constant serves every surface.
Supporting copy that said the prompt "installs the onboarding skill" or offered
a "tailored" prompt is corrected — neither is true now.
|
||
|
|
ecbf51028f |
feat(runtime): split Channel status into transport and provider legs (refs OSS-739)
`status().overall === "online"` proved only that the runtime reached the Gateway with a valid project API key. It said nothing about whether a Slack/Teams app was bound to the Channel, so a Channel with no provider at all reported `online` — and every version of our onboarding guidance used that value to certify end-to-end success. `setup_required` had no producer. The manager set it only when the activation engine threw a `SETUP_REQUIRED` error, and the engine stopped doing that at the 2026-07-29 realtime-boundary cutover. In published @copilotkit/channels-intelligence@0.7.0 the string survives in exactly one file, a shipped test. The 15 doc comments describing the state outlived the mechanism, which is why nobody noticed for a week; they are corrected here, with a note not to describe the state again without a path that can emit it. The Gateway now reports per-Channel provider attachment on the control join reply. This half consumes it: - `connectRealtimeGateway` captures the join reply (it was discarded) and exposes `providerStates()`. Phoenix's `Push.resend` preserves `recHooks`, so the hook re-fires on every auto-rejoin and a Channel provisioned while the runtime was disconnected is picked up with no extra plumbing. - The launcher and the manager's handle view delegate it as a GETTER, not a captured snapshot, for that same reason. - `status()` gains `detail`, reporting `transport` and `provider` separately so a caller can assert the leg it cares about. `channels` keeps its shape — turning its values into objects would break the CLI's channels-report and the starter channel-host — but its values are now the fold of the two legs, which is what makes `overall` honest. `unknown` is the load-bearing case. An older Gateway, a Gateway whose lookup failed, a handle without the seam, a Channel the Gateway did not mention, an unrecognised state, and a throwing getter all yield `unknown`, which keeps the transport-derived status — exactly today's behaviour. Only a positively reported absence downgrades a Channel, so no existing deployment turns amber on upgrade. The 41 pre-existing channel-manager tests pass unchanged, which is that guarantee. Verified the new tests fail without the fix by mutating the fold, not just that they pass with it. |
||
|
|
afef22d316 |
feat(shell-docs): restore the prompt disclosure and put it on the docs palette
Two changes from review. **The disclosure is back.** The panel is the shared featured `<Accordion>` again — reused, not restyled — so the overview stays compact when collapsed and a reader can expand to read the exact prompt before copying it. Copy-only made the payload opaque, which was the problem #6356 set out to fix. The component still owns the Slack/Teams switch and the analytics; the container is markup. **The treatment is now token-only.** The featured variant carried a saturated `--accent` tile and an accent-mixed gradient. `copilotkit-ui-theme` names a purple accent bar or stripe as a known wrong direction, and `copilotkit-branding` scopes accent to restrained, atmospheric use with gradients behind content rather than as the contrast layer — the old treatment was both at once, on a docs `--accent` that resolves to violet. It now matches the in-content panel idiom already in `OpsPlatformCTA`: `--bg-elevated`, `--border`, `--shadow-control`, and accent carried only by a small glyph and the hover state. Padding drops to `p-4` like every other docs panel, so a collapsed prompt no longer pushes the page's own introduction below the fold. Verified in light and dark. Also adds `docs.channels_activation_prompt_expanded`. The disclosure is where a funnel loses people and neither `viewed` nor `promptCopied` can see it: someone who never opened the panel is indistinguishable from someone who opened it and walked away. The prompt inside wraps instead of scrolling. The docs' usual code block scrolls horizontally, which is right for code and wrong here — it hid half the prompt behind the overflow, defeating the point of letting people read it first. |
||
|
|
bc80efb17f |
Reskinnable demo: a health-system knowledge desk with grounded retrieval and long-running approval gates (#6321)
Adds a skin to `examples/showcases/reskinnable-demo`: **Keel** (`/keel`), the internal knowledge and operations desk of Harbor Point Health, a fictional nine-hospital health system. The product is one sentence: **an answer you can trust, that turns into work that actually happens.** ## Why this skin The shipped skins bracket the `Skin` contract — `airline` is in-memory with four optional slots, `banking` is REST-backed with nearly all of them, and #6302's `logistics` proves a server-enforced authority gate. A further skin earns its place only by demonstrating something none of them do. Keel introduces **two substrates the app has never had**: 1. **Grounded retrieval.** A server-side `search_knowledge` tool over a nine-document policy corpus, returning cited passages. Every existing tool in this app pushes client state *up* to the agent; this inverts the flow — the agent reaches for something the user has not seen and brings it back. 2. **Long-running stateful process.** Runs whose steps advance on a 900ms ticker and halt at human approval gates. Banking's HITL is a single approval; airline's mutations are instantaneous. Neither has duration — state that changes while you are not looking. It is also the first skin with **parameterized routes** (`knowledge/<docId>`, `runs/<runId>`), so `resolvePage` actually destructures the segment array rather than using a flat lookup. ## The demo arc | Beat | What happens | | --- | --- | | *"What's our policy on giving a contractor access to patient records?"* | `search_knowledge` → grounded answer + `showSources` citing POL-114 and POL-302 | | Click a citation | Routes to `/keel/knowledge/phi-access-policy#minimum-necessary`, scrolls, highlights | | *"Set that up for Priya Raman"* | `showPlaybook` → `startRun` HITL plan preview (6 steps · 1 gate · 5 roles) | | Run ticks | `RUN-1045` advances, then halts **blocked** on Privacy Officer sign-off | | Switch persona → *"approve it"* | `approveStep` card carrying the policy citation as the reason the gate exists | **The citation resolving into the real app is the load-bearing beat.** Most AI-knowledge demos show a footnote; few make the footnote drive the app. ## Two details that are load-bearing, not stylistic **`showSources` cannot render a fabricated citation.** It accepts only `(docId, sectionId)` pairs — never citation text — and resolves the ref, heading, and snippet from the real corpus client-side. A citation the model invents fails to resolve and is dropped, rather than rendering as a convincing fake. Grounding is structural here, not prompt-dependent. **The ticker cannot thrash the agent's context.** A 900ms timer plus a `useAgentContext` readable is a token-cost hazard. The readables memoize on a `summaryKey` derived only from `(runId, status, currentStepId)` tuples — never from elapsed time — so context is rewritten on meaningful transitions and never on a raw tick. The interval is also cleared whenever nothing is running. ## What it exercises - **In-memory substrate** — seed-backed `useData` with a pure reducer (`tick` / `startRun` / `approveStep` / `rejectStep` / `cancelRun`) and a stale-approval race guard. - **Identity triad** — `RuntimeProviders` (persona above the provider) + `useRuntimeProperties` + a server-safe `identifyUser`, scoping memory per persona. A gate is actionable only when its `approverRole` matches the active persona; otherwise it renders "waiting on <role>". - **a2ui canvas** — an ops report from a deterministic op-builder; no figures travel in the ops, every number binds client-side. - **OGUI sandbox** — four functions projecting to allowlisted DTOs. - **Theme** — deep pine on warm neutrals at `--radius: 0.5rem`, the tightest in the app, with `--brand-violet` re-valued as the amber awaiting-approval accent (the shared vocabulary has no warning token, and a skin may not invent token names). Pure token re-valuing; no component edits, no `globals.css` change. Dark-capable via the `--nw-dark-capable` opt-in. Deliberately omits `chatHeaderActions` and `onSuggestionSelect` (they exist in banking only to serve its PDF-attachment beat) and `Providers` (nothing needs to mount below the provider). ## The most valuable file in the diff `src/skins/keel/data/seed.test.ts` asserts that **every one of the 23 playbook-step `policyRef`s resolves to a real `docId` + `sectionId`** in the corpus. That invariant is what fuses the two substrates — a dangling reference means an approval card cites a document that does not exist. It has already earned its keep: it caught a citation to `POL-208 §documentation` that did not exist (`documentation` belongs to POL-121), which had been written into the design spec and propagated into the implementation plan before any code was generated. That test was also **mutation-tested** rather than trusted: deliberately breaking one `policyRef` turns it red, so its green result reflects a real check and not one of its fallback paths silently no-opping. ## Verification Keel's own suite: **16 test files / 105 tests** (`npx vitest run src/skins/keel`). Whole app after merging `main`: **38 files / 215 tests** — the larger number includes logistics' 65 tests, which arrived with the merge and are not this PR's. `pnpm lint` 0 issues · `npx tsc --noEmit -p .` exit 0 · `pnpm build` compiles. `main` is merged in (resolving the append-only registry conflict predicted below), so the branch is mergeable and all four skins — banking, airline, logistics, keel — are registered under the same id in both the client and server registries. **No CI job exercises this app, so green checks here are not evidence.** `.github/workflows/static_quality.yml` carries `paths-ignore: ["examples/**"]` on both its `push` and `pull_request` triggers, and no workflow in the repo references `reskinnable-demo` at all. The passing checks on this PR are unrelated legacy example demos plus binary/config checks. The local gates above are the only real signal — worth knowing before reading a green tick as coverage. **Not verified: the chat agent itself.** No API key was available, so gen-UI, both HITL cards, the citation deep-link, the ticker's live advance, persona switching, dark mode, and OGUI are unexercised at runtime. A live smoke test of those paths is the main thing a reviewer should add — it is also where the `pointer-events-auto` fix and the context-churn guard would actually be observed working. ## Review pass The skin went through a four-round unbiased review (16 reviewers per round, non-leading prompt) plus a fix cycle per finding. Findings landed as six thematic commits on top of the feature commits: | Commit | Closes | | --- | --- | | `fix(keel): make engine mutations honest and single-path` | Lost updates from non-functional `setRuns`, a stale render-time read, gates that could enter `awaiting_approval` without `startedAt`, a rejector mislabelled as approver, and a mutation reporting success for a commit that did not happen | | `fix(keel): make retrieval deterministic and citations grounded` | Locale-dependent tiebreak ordering (breaking the documented byte-identical guarantee), order-dependent transitive synonym expansion, phrase synonyms matching across word boundaries, duplicate citation keys, and a second citation into the same document silently failing to re-scroll | | `fix(keel): guard prototype access on untrusted lookup keys` | `/keel/constructor` and friends resolving `Object.prototype` members past a `?? null` guard and being handed to React as a component; same class in the persona lookup, where the key arrives from client-forwarded properties | | `fix(keel): pin date/time formatting and fix in-chat interactivity` | Hydration mismatches from ambient-locale formatting, and policy links dead in chat because CopilotKit paints `useComponent` renders with `pointer-events: none` | | `fix(keel): anchor seeded runs to a per-call now` | Seed timestamps anchored to fixed calendar dates while the app compares them to `Date.now()` — a ~6.7-day cycle time poisoning the median KPI, and the mid-flight ticking run arriving already resolved on remount | | `fix(keel): harden report surface, catalog, and remaining chrome` | Duplicate a2ui component ids colliding React keys, and a failed report surface latching so the canvas never self-healed | Each fix carries a red-green proof (test written, source broken to confirm red, restored to confirm green). Two were structural rather than per-instance: the prototype-safe lookup uses a `Map` so inherited keys cannot exist, and a single `ChatSurface` wrapper now carries `pointer-events-auto` so exactly one such literal remains in the skin. **Caveat worth stating plainly: the final six fixes did not themselves get an unbiased review round.** The review was stopped deliberately after round four. Earlier rounds are the calibration — round one's 16 fixes introduced 3 regressions (~19%), round three's introduced zero — so the risk is real but bounded, and it is concentrated in these six commits rather than spread across the diff. ## Relationship to #6302 **Resolved: #6302 (`logistics` / Meridian) has merged, and keel landed second.** The predicted conflict materialised exactly as described — both PRs append to the same two registry lines — and is resolved keep-both in the merge commit on this branch. Worth noting that until that merge, GitHub created *no* CI runs for this PR at all: it does not evaluate `pull_request` workflows for a branch whose mergeable state is `CONFLICTING`, because it cannot compute the merge ref those events run against. A conflicting PR therefore looks like "CI hasn't started" rather than "CI failed". Worth deciding before either merges whether these ship as two skins or Keel's retrieval half folds into Meridian. Keel adds grounded retrieval, duration, and parameterized routes; it also re-proves approval gates, maximal contract coverage, and an a2ui canvas that Meridian already demonstrates. Keel's canvas (`canvas-surface.tsx` + `ops-report.ts`) was deliberately built as cleanly separable scope for exactly this reason — nothing imports it except the agent and the skin object. ## Known follow-ups (non-blocking) - In the `adverse-event` playbook, both the `rca-decision` and `close` steps cite `POL-208 §root-cause-analysis`. POL-208 has no section that fits "Document and close" better, so this is the least-bad mapping rather than a clean one; a new corpus section would resolve it properly. - `seed.test.ts`'s live-corpus check retains two fallback paths (`catch { return }` and `if (!docs) return`) that would let it pass silently if the corpus import ever broke. It genuinely runs today (verified by mutation), but the escape hatches are a latent hazard worth removing now that the corpus is guaranteed present. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
4d5fd3a25e |
fix(shell-docs): stop a throwing analytics client from faking a copy failure
The clipboard write and the capture call shared one try block, so a PostHog client that threw reported "Copy blocked" for a prompt already sitting on the clipboard. Only the write decides what the reader is told; capture moves behind the same isolated helper the activation strip already uses, and the impression observer uses it too instead of its own inline catch. Two regression tests: capture throwing after a resolved write still shows "Copied", and a rejected write still shows "Copy blocked" without emitting a copy event. Also drops 16 lines of `dev: true` lockfile churn picked up from an `npm install` in the review worktree — no dependency actually changed. |
||
|
|
0b1b37e0af |
Merge origin/main into feat/reskinnable-demo-keel-skin
Resolves the append-only registry conflict this PR's description predicted: #6302 (logistics / Meridian) landed first and took the same two lines, so keel takes the trivial conflict. Both edits are additive, so the resolution is keep-both in both registries — logistics first (it landed first), keel second. Why the merge was necessary rather than optional: GitHub does not create pull_request workflow runs for a PR whose mergeable state is CONFLICTING, because it cannot compute the merge ref those events run against. The push of the review fixes therefore produced ZERO CI runs — verified over 12 minutes of polling the Actions API by head SHA, and corroborated by Actions being healthy repo-wide at the same time. Not a timing artifact, and not a path filter: test_e2e-legacy-v1 and auto_merge_showcases both trigger on pull_request with paths: examples/**, and synchronize is a default activity type. Scope of the drift: main is 127 commits ahead of the merge-base, but within reskinnable-demo it added only logistics' own files (48 skin + 17 API route + 2 reskin-skill docs) plus +7 append-only lines across exactly the two registry files. No globals.css change, no skin-contract.ts change, no skins-config.ts change — so the shared token vocabulary and the frozen Skin contract are untouched, and keel's isolation claim still holds against a four-skin registry. All four skins are now registered under the same id in both registries (banking, airline, logistics, keel), which is the invariant the two-registry split exists to maintain. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
c01b637bdc |
fix(keel): harden report surface, catalog, and remaining chrome
Remaining fixes across the ops-report builder, the a2ui canvas surface, the catalog, agent wiring, suggestions, and the knowledge/playbooks pages: correct the report surface output and its catalog registration so the canvas renders the intended report, and tighten the surrounding chrome so these entry points behave consistently. Tests cover the report builder and canvas surface. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
28adff5dbf |
fix(keel): anchor seeded runs to a per-call now
Seed data used a fixed or module-load timestamp, so seeded run timelines drifted out of a plausible window as time passed and could not be reproduced deterministically in tests. This anchors every seeded run relative to a `now` passed in at seed time, keeping the demo's timeline coherent whenever it is generated and making the seed testable. A test pins the relative anchoring. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
5bbe483073 |
fix(keel): pin date/time formatting and fix in-chat interactivity
Dates and times were formatted with the runtime's ambient locale and timezone, so server and client rendered different strings and React hydration mismatched; several call sites also built ad-hoc formatters that drifted from one another. This centralizes formatting behind pinned locale/timezone formatters — one per format — so output is stable across environments. It also fixes the ChatSurface pointer-events boundary so interactive controls rendered inside the chat transcript (playbook, approval, and run cards) actually receive clicks. Tests cover the pinned formatters. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
43cce12c53 |
fix(keel): guard prototype access on untrusted lookup keys
URL route segments and client-forwarded runtime properties were used directly as object keys, so a crafted value like "__proto__" or "constructor" could resolve to an inherited Object.prototype member instead of a real entry — misrouting a page or silently matching a bogus user identity. This constrains both lookups to own, real keys so untrusted input can no longer reach the prototype chain. Tests cover the malicious key cases for page resolution and user identification. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
576b3c4df8 |
fix(keel): make retrieval deterministic and citations grounded
Search results depended on the runtime locale for tie-breaking, synonym expansion chained transitively into unrelated terms, and phrase matching hit partial tokens, so the same query could return different or wrong results across environments. Citations could also duplicate and fail to land on their source document. This pins result ordering to a locale-independent comparison, bounds synonym expansion to direct (non-transitive) matches, matches phrases on whole-token boundaries, deduplicates citations, and makes a citation click land on the correct document. Tests lock in the deterministic ordering and citation targeting. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
3eb7d33e4f |
fix(keel): make engine mutations honest and single-path
The data engine could report success for mutations that changed nothing and exposed multiple divergent write paths, so callers could not trust a returned status or reason about a gate from one place. This routes every mutation through one shared commit path, returns values that reflect what actually happened, and tightens the gates so a no-op or a rejected change can no longer be misrepresented as applied. Types and the seed-backed data hook are updated to match, with tests covering the corrected return contract and persona-scoped access. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3 |
||
|
|
22abe9c034 |
feat(shell-docs): lead with the copy action, drop the dangling instruction
The prompt is not on the page, so the button is the whole point of the panel. It now sits directly under the heading instead of off to the right, where it read as trailing furniture. "Copy the prompt and paste it into your coding agent" also had nothing to point at once the text stopped being rendered. The supporting line now says only what the skill does. |
||
|
|
3a2af9ee47 |
feat(shell-docs): add activation impressions, keep the prompt in the clipboard
Two changes. **Impressions.** Both docs entry points emitted a copy event and nothing else, so the copy count had no denominator: a surface nobody scrolls to and a surface everybody ignores were indistinguishable. `docs.channels_activation_viewed` fires once per surface on the first intersection at 50%, from an IntersectionObserver rather than on mount, since both sit below the fold. The `surface` values move into a shared `CHANNELS_ACTIVATION_SURFACES` map so the two docs roads — and copilotkit.ai/channels, which sends its own event name with the same property — stay separable inside one funnel. The observer is guarded on `typeof IntersectionObserver`. An impression is never worth breaking a render for, and this repo's jsdom tests do not define it. **The prompt is no longer rendered.** The panel offers the prompt through the copy button alone. The supporting line says so explicitly rather than saying "paste this" next to nothing. |
||
|
|
55790bbdce |
feat(shell-docs): name the real onboarding skill slug
Points the entry point at setup-slack-channel, the slug the combined onboarding prompt will occupy in skills/ once it lands from channels-sdk. |
||
|
|
e62d950248 |
feat(shell-docs): give the Channels entry point #6356's featured treatment
Adopts the visual language from the featured-Accordion work — accent panel, terminal mark, eyebrow, prominent copy action — for the shared Channels entry point, and drops the disclosure it was attached to. The accordion existed because the payload was twenty lines; the payload is now one sentence, so hiding it behind "Open & copy prompt" costs a click and buys nothing. The panel renders exactly the text the button copies. Two earlier shapes were wrong in instructive ways: a full monospace paragraph wrapped like a rendering bug, and a code block with the ask underneath read as a shell command with a footnote, which made a button labelled "Copy prompt" look like it was lying. Only the command is monospace now; the prose around it wraps like prose. The featured Accordion variant stays in mdx-components as a shared opt-in capability, unused for the moment. |
||
|
|
e9ea40dbb4 |
docs: make Channels setup prompts easier to discover (#6356)
## Summary - add an opt-in featured treatment to the existing docs `Accordion` component - use that treatment for the coding-agent starter prompts at the top of both the Slack and Microsoft Teams Channels overview pages - replace the easily missed accordion label with a branded prompt panel, a terminal icon, platform-specific supporting copy, and a prominent **Open & copy prompt** action - preserve the existing native `details` interaction and code-block copy control inside the expanded prompt - update the Channels documentation regression test for the new featured markup and provider-specific descriptions ## Why The existing “copy this prompt” accordion looked like ordinary supporting content and was easy to skip at the top of the overview pages. This change gives the starter prompt a clear visual hierarchy and action without changing the prompt content or introducing a new navigation concept. The visual treatment deliberately uses a terminal icon rather than a generic sparkle so it reads as a developer workflow for a coding agent. ## User impact People landing on either Channels overview page can now immediately recognize that there is a ready-to-use setup prompt. Slack and Teams retain their own provider-specific instructions, while the interaction remains familiar: 1. Open the featured prompt. 2. Review the provider-specific setup instructions. 3. Use the existing copy control on the prompt code block. Unrelated accordions retain their current appearance because the new treatment is opt-in. ## Implementation notes - reuses the existing CopilotKit docs design tokens for color, spacing, borders, radii, focus states, and dark mode - keeps the native `details`/`summary` semantics and keyboard behavior - adapts the header and action layout at narrower widths - changes the action label to **Close prompt** while expanded - leaves the published Slack and Teams prompt bodies unchanged apart from replacing two em dashes with colons for readability - adds no new dependencies ## Validation - `npm test -- src/lib/__tests__/channels-docs.test.ts --maxWorkers=1 --no-file-parallelism` — 29 tests passed - `npm run typecheck` - `pnpm exec oxfmt --check showcase/shell-docs/src/components/mdx-components.tsx showcase/shell-docs/src/content/docs/channels/index.mdx showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts` - `git diff --check` - pre-commit lint and commit-message hooks passed - Impeccable design detector returned no findings - manually reviewed Slack and Teams in collapsed and expanded states, light and dark themes, and a narrower responsive viewport - confirmed no browser console errors during local review ## Scope This PR is intentionally limited to the shared opt-in accordion treatment, the two Channels overview prompt instances, and their focused regression coverage. It does not restyle existing documentation accordions or alter the underlying setup guidance. |
||
|
|
7f493d2527 |
merge origin/codex/channels-prompt-visual-flare
Keeps the featured Accordion treatment from #6356 as a shared opt-in component. Its two consumers — the Slack and Teams starter prompts on the Channels overview — are replaced here by the shared entry-point component, because the payload those accordions concealed is now one line and there is nothing left to disclose. |
||
|
|
7b8034c6d4 | docs: highlight Channels setup prompts | ||
|
|
db1f877897 |
feat(shell-docs): route every Channels entry point at one onboarding skill
The Channels overview page, the docs landing activation strip, the website's /channels strip, and the channels-sdk README each carried their own copy of the onboarding workflow — six versions across three repos. They drifted, and each went stale against the CLI independently: developers were told to install unnamed skills, run `copilotkit channels` (which the published CLI does not have), and use a bare `npx copilotkit` that a cached older binary shadows. Every surface now emits the same two sentences naming one skill, and the workflow itself lives in that skill. Shipped here: - `buildChannelsActivationPrompt` returns a pointer, not a workflow, built from a single `CHANNELS_ONBOARDING_SKILL` constant. - The overview page's two 20-line prompts — one Slack, one Teams, both hidden in accordions — collapse into the shared `<ChannelsStartPrompt />`. - The prompt text renders on screen instead of living only in a clipboard payload. That is why it was missable: a copy button with an invisible payload reads as decoration. At one line there is nothing left to hide behind an accordion. - Both surfaces emit `promptCopied` with a `surface` property, so the funnel can answer which road people actually take. Tests pin the corrections that drift produced: the skill is named, the install is non-interactive, the CLI is `@latest`, and the overview cannot re-embed a workflow. |
||
|
|
cb9f8183dd | Merge branch 'main' into fix/react-core-activity-contentkey-memo | ||
|
|
76aa8a824b |
docs(reskinnable-demo): correct the reskin skill's layout, tools, theme, and canvas guidance (#6354)
Corrects the repo-local `reskin` skill (`.claude/skills/reskin/`), which in several places teaches guidance that produces a broken or subtly defective skin when followed literally. Docs only — no application source changes. ## Why now The `logistics` skin (#6302) was built by following this skill and then debugged against a live app. Every fix below is a defect that build actually surfaced, or that an audit of the skill against the three shipped skins turned up. Where the skill and `src/skins/logistics/**` disagreed, the skill was the one that was wrong. ## The two that shipped a visibly broken skin **The layout template taught `min-h-screen`.** That is a *minimum*, so on any page taller than the viewport the container grows, the whole document scrolls, and the pinned nav scrolls away with it — and `<main>`'s `overflow-y-auto` goes inert, because its parent is unbounded. Now `h-screen overflow-hidden` on the root plus `h-full` on the `<aside>`. **The template never published `--nw-nav-inset-left` / `--nw-nav-inset-right`.** Every shipped skin does. Without them the shell's floating skin selector docks on top of the new skin's nav. Added, including the cleanup that removes both — a missing cleanup leaks the inset into whatever skin the user switches to next. ## The one that is invisible to every gate **No mention of the dependency array** on `useComponent` / `useFrontendTool` / `useHumanInTheLoop`. Omit it and the render closure captures whatever the data was at registration time — for a REST-backed skin, the empty array from before the first fetch — permanently. This is the nastiest bug in the app because it **compiles, lints, and passes every test**. The symptom is the agent narrating confidently over a broken surface: ``` agent: "PO-88213 trade-offs are on screen." UI: "No shipment matches that reference." ``` Banking already documented the trap in a code comment; the skill never did. Every template registration now closes with its deps array. ## Also fixed - **Meta-utility strip** — new section documenting that the presenter reset / theme toggle / help controls are *skin-authored*, not shell-provided. Covers the `usePresenterReset()` gate, that `ThemeToggle` is a shared component (fair to import) while `useAskCopilot` must be *ported* (a skin may not import from another skin), and that the skin's own `dev/reset` route must gate on `presenterResetEnabled() || NODE_ENV !== "production"` so the button and the endpoint agree. - **`--nw-dark-capable`** — previously unmentioned. A skin that ships a `.dark .theme-<id>` block but omits the flag stays stuck in light, because the shell's reconciler forces it. Documented as an explicit opt-in, and paired with the note that a theme toggle without a dark palette is a dead control. - **a2ui surface ops must be emitted from a server `defineTool`** — previously silent. A client `useFrontendTool` result never produces the in-stream `TOOL_CALL_RESULT` the a2ui middleware needs, so the canvas stays permanently blank. This one cost real debugging time on #6302. - **Parameterized `useComponent` render signature** — receives the schema output *directly*, not wrapped in `{ args }`, unlike HITL / frontend-tool renders. Airline has no parameterized example to learn from, so the skill now shows one and contrasts the two. - **`nav` is not the segment validator** — the skill claimed it was "the source of truth for which segments are valid"; the frozen contract says the opposite and names `resolvePage`. Following the skill got you a 404. - **Airline `useData` contradiction** — SKILL.md listed `data/` among the slots airline omits; airline sets it, and templates.md already said so. - Two nits: the `tools.tsx` template would NPE for a no-data skin (`useSkinData` returns `undefined`), and `skin.tsx` left a dangling `use<Id>Data` import for skins that omit the slot. ## Verification Every code sample was diffed against the corresponding real file in `src/skins/logistics/`, and every API-signature claim against the installed types in `packages/react-core/dist/`. Markdown only, so no build/lint/test surface changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
a8ab77d05e |
docs(reskinnable-demo): correct reskin skill's layout, theme, tools, and canvas guidance
The reskin authoring skill taught several patterns that ship a broken skin when
followed literally. Corrected against the debugged logistics skin (the frozen
contract wins on conflict):
- layout: h-screen overflow-hidden (not min-h-screen) + h-full aside, so the nav
stays pinned and <main> scrolls inside it
- layout: publish --nw-nav-inset-left/right with cleanup, so the floating skin
selector never docks on the nav and the inset does not leak between skins
- layout: document the meta-utility strip (Reset/ThemeToggle/Help) as
skin-authored chrome, with the reset-route gating coupling
- theme: document --nw-dark-capable as the dark-mode opt-in + a .dark .theme-<id>
example
- tools: every useComponent/useFrontendTool/useHumanInTheLoop registration must
close with a deps array, or the closure captures empty pre-fetch data forever
- tools: a parameterized useComponent render receives the schema output directly,
not { args }
- a2ui: a CanvasSurface must be fed by a server defineTool, never a client
useFrontendTool
- contract: nav is display-only; resolvePage is the sole segment validator
- fix airline useData contradiction; NPE-safe no-data tools shape; skin.tsx
dangling-import note
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
86b0015534 |
fix(runtime): preserve realtime gateway handoff continuity (#6316)
## Summary - replay runner events sequentially with immutable IDs, terminal acknowledgements, and bounded retries - preserve accepted runs across planned and prolonged abnormal reconnects without manufacturing terminal errors - watchdog stalled abnormal socket reconnects while keeping pre-join failure behavior bounded - make Slack and Teams delivery settlement safe across deployment handoffs - add the compiled cross-service Kind runner used by the Intelligence integration suite ## Test plan - [x] `NX_DAEMON=false NX_SKIP_NX_CACHE=true pnpm nx test runtime` (1,840 tests) - [x] focused Intelligence runner suite (42 tests) - [x] `pnpm --filter @copilotkit/runtime check-types` - [x] `pnpm --filter @copilotkit/channels-intelligence check-types` - [x] channels-intelligence suite (174 tests) - [x] real cross-service Kind planned and hard-stop lifecycle suite; accepted agent remains un-aborted |
||
|
|
18be1a9ba3 |
Reskinnable demo: a supply-chain control tower skin with a server-enforced authority gate (#6302)
Adds a third skin to `examples/showcases/reskinnable-demo`: **Meridian**
(`/logistics`), a supply-chain control tower built at the **maximal**
end of the frozen `Skin` contract.
## Why a third skin
The two shipped skins bracket the contract — `airline` is in-memory with
four optional slots, `banking` is REST-backed with nearly all of them. A
third earns its place only if it demonstrates something, so this one
runs a **different domain rule** through the same machinery.
Banking's most valuable property isn't the HTTP (its store is a
module-scope object seeded from JSON). It's that a **server-side rule
the client cannot bypass** rejects a write, returns a symptom, and
forces the agent to learn the failure and route around it. Meridian
reproduces that shape with **role-scoped approval authority** instead of
a policy budget.
## The headline beat, verified end-to-end over HTTP
| Attempt | Result |
| --- | --- |
| Rosa (authority $5,000) expedites `PO-88213` at $8,400 | `403
OVER_AUTHORITY` |
| Same request, body claims `costUsd: 1` | `403` — server recomputes;
client cost is never trusted |
| `PATCH {weightKg: 1}` then expedite | `422 FORBIDDEN_FIELD` — pricing
inputs are not writable |
| Approve a **non-justifying** escalation, then expedite | `403` — still
blocked |
| Approve `LINE_DOWN_RISK`, then expedite | `200`, logged at $8,400 |
| Director expedites with no escalation | `200` |
`PATCH` is an **allow-list** (`status`, `etaCurrent`) rather than a
deny-list, precisely so no pricing input becomes a side channel around
the gate. There is a named regression test for that bypass.
## What it exercises
- **REST substrate** — `/api/logistics/v1/*` over a seeded store, with
the authority gate and a two-step escalation recovery under a closed
code catalogue.
- **Identity triad** — `RuntimeProviders` (auth above the provider) +
`useRuntimeProperties` + a server-safe `identifyUser`, scoping runs per
planner.
- **a2ui canvas** — a Decision Brief from a deterministic op-builder; no
figures travel in the ops, every number binds client-side.
- **OGUI sandbox** — five functions, each projecting to an allowlisted
DTO so no internal field reaches iframe-authored JS.
- **Theme** — warm signal-amber on graphite at a tight radius, against
banking's violet and airline's teal. Pure token re-valuing; no component
edits, no `globals.css` change.
Deliberately omits `chatHeaderActions` and `onSuggestionSelect` (they
exist in banking only to serve its PDF-attachment beat) and `useData`
(components read REST directly, like banking).
## Verification
`pnpm test:unit` 22 files / 110 tests · `pnpm lint` 0 issues · `pnpm
build` compiles.
Driven in a real browser: all four routes render; theme applies; the
exception board sorts worst-first on real data; switching skins restores
banking with no theme or nav-inset leak.
**Not verified:** the chat agent itself — no API key was available, so
gen-UI, the HITL cards, `renderBrief` on the canvas, and OGUI are
unexercised at runtime. A live smoke test of those paths is the main
thing a reviewer should add.
## Known follow-ups (non-blocking)
Unused parameter in two private lane helpers · duplicate enum selections
in `buildBriefOps` could emit two components sharing an id ·
`getMitigationOptions` returns its type raw while the other four sandbox
handlers project · `req.json()` unguarded (malformed body → 500 not 400)
· `ModeSplitChart` uses two tones across four modes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
675a5d8568 |
fix(reskinnable-demo): pin the logistics nav to the viewport
The shell root used `min-h-screen`, which is a MINIMUM: on a page taller than the viewport the container grew with the content, so the whole document scrolled and the sidebar scrolled away with it. It also left `<main>`'s own `overflow-y-auto` inert, because an unbounded parent gives it no height to overflow against. Switch the root to `h-screen overflow-hidden` and give the aside `h-full`, mirroring banking's layout. The shell is now exactly one viewport tall, the nav stays pinned, and `<main>` scrolls internally. Measured on /logistics/lanes at a 700px viewport: document scrollHeight 700 (page no longer scrolls), aside top 0 / bottom 700, main scrollHeight 1472 vs clientHeight 700, and the aside stays at top 0 after scrolling main by 700px. |
||
|
|
deffa6c3d6 |
feat(reskinnable-demo): add meta-utility strip and dark mode to logistics skin
Port banking's sidebar-footer utility controls into Meridian: a Reset (presenter-gated), the shared ThemeToggle, and a copilot Help shortcut, placed directly above the existing "On duty" planner switcher. Give the skin a warm- graphite dark palette (--nw-dark-capable + a .dark .theme-logistics block) so the theme toggle is a live control. Port useAskCopilot into the skin (no cross- skin import) and widen the reset endpoint's gate to allow presenter/booth deployments (presenterResetEnabled OR non-production). Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
cf16befb11 |
fix(reskinnable-demo): re-register logistics gen-UI and HITL tools when data loads
Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
e2bd6cec43 |
fix(reskinnable-demo): server-side brief tool, decision records, honest greeting
Applies the final whole-branch review wave for the logistics skin. - renderBrief: move from a client useFrontendTool to a server defineTool on the BuiltInAgent (mirrors banking's render_report). A client frontend-tool result never yields an in-stream TOOL_CALL_RESULT, so the a2ui middleware never emitted the a2ui-surface activity and the brief canvas stayed blank. Tool name kept exactly "renderBrief". build-brief-ops + catalog/definitions confirmed server-safe (plain Zod, no React/.tsx), so agent.ts stays server-safe. - createDecisionRecord: implement the missing tool (globally registered) to log a decision NOT executed through commitMitigation, wiring the previously-dead fileDecision + POST /decisions path. Harden the route: require/resolve plannerId, derive decidedBy/role server-side (never from body), 404 unknown shipmentId, clamp costUsd. Client forwards plannerId only. Prompt + toolLabels updated. Adds a decisions route test (planner-derived identity vs body decoys, 404, 400). - greeting: drop the wrong "six lanes / three shipments" figures (seed has 10 lanes, 4 exception shipments); quantitative claims bind to data, not prose. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
8fef990752 |
feat(reskinnable-demo): register the logistics skin in both registries
Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
74f187db5d |
feat(reskinnable-demo): add logistics agent, suggestions, and OGUI design brief
Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
5db4703b94 |
feat(reskinnable-demo): register logistics gen-UI, HITL, and brief tools
Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
7413152a4a |
feat(reskinnable-demo): add logistics provider stack and per-planner identity
Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
60ad3d353a | feat(reskinnable-demo): expose logistics data to OGUI sandbox via projected DTOs | ||
|
|
244763f93a |
feat(reskinnable-demo): add logistics a2ui decision-brief canvas surface
Co-Authored-By: Claude <noreply@anthropic.com> |