[pull] main from CopilotKit:main - #468
Merged
Merged
Conversation
## Release channels v0.6.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.6.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.6.1` - Creates git tag `channels/v0.6.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.
0.6.1 carries one change: createChannel's clone check now warns instead of throwing when `clone()` drops subclass state (#6322). On 0.6.0 a starter hosting a Channel through @ag-ui/langgraph refuses every turn, because LangGraphAgent's clone() leaves `emittedToolCallStartIds` and `eventsStreamActive` behind -- both per-run scratch that is re-initialized before anything reads it, so dropping them was never the problem. The starters are the surface where that failure is user-visible, so they should not sit on the release that has it. No @copilotkit/* bump rides along, and none is needed. The fix lives entirely in @copilotkit/channels-core, and every path to it is a caret range: runtime@1.65.0 asks for channels-core ^0.6.0, and channels-intelligence@0.6.0 (which runtime does pin exactly) asks for ^0.6.0 as well. Both resolve onto the same 0.6.1, so the runtime's channel path picks up the fix without a new runtime release. Verified from the regenerated locks rather than assumed: each of the 15 resolves exactly one channels-core, at 0.6.1, with no second copy nested under runtime. Lockfiles were regenerated with --package-lock-only; the diffs contain @copilotkit/channels* lines and nothing else, so no unrelated dependency floated forward in the process.
…ollowups # Conflicts: # showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pnpm/action-setup](https://redirect.github.com/pnpm/action-setup) | action | patch | `v6.0.9` → `v6.0.10` | | [zizmorcore/zizmor-action](https://redirect.github.com/zizmorcore/zizmor-action) | action | patch | `v0.6.1` → `v0.6.2` | --- ### Release Notes <details> <summary>pnpm/action-setup (pnpm/action-setup)</summary> ### [`v6.0.10`](https://redirect.github.com/pnpm/action-setup/compare/v6.0.9...v6.0.10) [Compare Source](https://redirect.github.com/pnpm/action-setup/compare/v6.0.9...v6.0.10) </details> <details> <summary>zizmorcore/zizmor-action (zizmorcore/zizmor-action)</summary> ### [`v0.6.2`](https://redirect.github.com/zizmorcore/zizmor-action/releases/tag/v0.6.2) [Compare Source](https://redirect.github.com/zizmorcore/zizmor-action/compare/v0.6.1...v0.6.2) zizmor 1.29.0 is now the default version. </details> --- ### Configuration 📅 **Schedule**: (in timezone America/Los_Angeles) - Branch creation - "before 9am every weekday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/CopilotKit/CopilotKit). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->
## What Bumps `@copilotkit/channels` from `0.6.0` to `0.6.1` in all 15 integration starters that host a Channel, with lockfiles regenerated to match. Nothing else changes — `@copilotkit/*` stays on `1.65.0`. `examples/slack` and `examples/teams` are untouched: they consume the workspace copy (`workspace:*`), so they already track the fix. ## Why `channels@0.6.1` carries exactly one change — [#6322](#6322), where `createChannel`'s clone check warns instead of throwing when `clone()` drops subclass state. On `0.6.0`, a starter hosting a Channel through `@ag-ui/langgraph` **refuses every turn**, because `LangGraphAgent.clone()` leaves `emittedToolCallStartIds` and `eventsStreamActive` behind — both per-run scratch that is re-initialized before anything reads it. The starters are where that failure is user-visible, so they shouldn't sit on the release that has it. ## No runtime release needed The question came up whether `@copilotkit/runtime` has to be re-released so the pins don't conflict. It does not. The fix lives entirely in `@copilotkit/channels-core`, and every path to it is a caret range: | consumer | asks for | resolves to | | --- | --- | --- | | `@copilotkit/channels@0.6.1` (umbrella) | `channels-core` exactly `0.6.1` | 0.6.1 | | `@copilotkit/runtime@1.65.0` | `channels-core: ^0.6.0` | 0.6.1 | | `@copilotkit/channels-intelligence@0.6.0` (runtime's one exact channels pin) | `channels-core: ^0.6.0` | 0.6.1 | The umbrella does not depend on `channels-intelligence`, so nothing pulls a second copy of it in alongside the runtime. Everything dedupes onto one `channels-core@0.6.1`, which is what makes the runtime's own channel path pick up the fix without a release of its own. ## Testing **1. Every regenerated lock resolves exactly one `channels-core`, at 0.6.1** — verified rather than reasoned, since a second nested copy is the failure mode that would have forced a runtime release: ``` a2a-middleware: node_modules/@copilotkit/channels-core@0.6.1 adk: node_modules/@copilotkit/channels-core@0.6.1 claude-sdk-python: node_modules/@copilotkit/channels-core@0.6.1 agno: node_modules/@copilotkit/channels-core@0.6.1 claude-sdk-typescript: node_modules/@copilotkit/channels-core@0.6.1 langgraph-python: node_modules/@copilotkit/channels-core@0.6.1 crewai-flows: node_modules/@copilotkit/channels-core@0.6.1 langgraph-js: node_modules/@copilotkit/channels-core@0.6.1 mastra: node_modules/@copilotkit/channels-core@0.6.1 llamaindex: node_modules/@copilotkit/channels-core@0.6.1 ms-agent-framework-dotnet: node_modules/@copilotkit/channels-core@0.6.1 pydantic-ai: node_modules/@copilotkit/channels-core@0.6.1 mcp-apps: node_modules/@copilotkit/channels-core@0.6.1 ms-agent-framework-python: node_modules/@copilotkit/channels-core@0.6.1 strands-python: node_modules/@copilotkit/channels-core@0.6.1 ``` **2. The full `@copilotkit/*` resolution in the reference starter (`langgraph-python`)** — `channels-intelligence` correctly stays at `0.6.0` (runtime's exact pin) while consuming the 0.6.1 core: ``` 1.65.0 @copilotkit/a2ui-renderer 0.6.1 @copilotkit/channels 0.6.1 @copilotkit/channels-core 0.6.1 @copilotkit/channels-discord 0.6.0 @copilotkit/channels-intelligence 0.6.1 @copilotkit/channels-slack 0.6.1 @copilotkit/channels-teams 0.6.1 @copilotkit/channels-telegram 0.6.1 @copilotkit/channels-ui 0.6.1 @copilotkit/channels-whatsapp 1.65.0 @copilotkit/core 1.65.0 @copilotkit/react-core 1.65.0 @copilotkit/runtime 1.65.0 @copilotkit/shared 1.65.0 @copilotkit/web-components ``` **3. Reference starter installs and typechecks its channel host** — real `npm install`, not lock-only: ``` > copilotkit-langgraph-template@0.1.0 typecheck:channel > tsc -p tsconfig.channel.json --noEmit EXIT=0 installed core: 0.6.1 | intelligence: 0.6.0 ``` **4. The published 0.6.1 tarball actually carries the fix** — checked the installed dist, not just the version number, so a mis-built release would not pass as fixed: ``` node_modules/@copilotkit/channels-core/dist/create-channel.js 74: warnOnCloneDroppedOwnFields(prototype, cloned); 132: function warnOnCloneDroppedOwnFields(prototype, cloned) { 139: console.warn(`createChannel: ${name}.clone() dropped ${dropped.join(", ")}. ` + ``` `console.warn`, not `throw` — the 0.6.0 behaviour is gone. **5. No unrelated dependency drifted.** Filtering every non-`@copilotkit/channels*` line out of a regenerated lock diff leaves nothing: ``` $ git diff examples/integrations/adk/package-lock.json | grep -E "^[+-]" \ | grep -vE "integrity|resolved|@copilotkit/channels" | grep -E '"[a-z]' | sort | uniq -c 8 + "version": "0.6.1", 8 - "version": "0.6.0", ``` **Not tested:** no starter was run end-to-end against a live Channel here — the change is a version pin, and the behaviour it unblocks was verified in #6322. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - add disabled, branded coming-soon entries for Discord, WhatsApp, Telegram, and SMS in the Channels picker - add a reusable Channels activation strip to the Shell Docs landing page with verified channel/backend guide routing, accessible selection controls, prompt copying, OpenTag guidance, and analytics - replace the global course banner with a Channels announcement linking to `/channels` - add focused coverage for route resolution, selection behavior, clipboard feedback, keyboard navigation, telemetry, and banner content ## Validation - `npm exec -- oxfmt --check src/components/banners.tsx src/components/__tests__/banners.test.tsx` - `npm run lint` (no errors; existing warnings only) - `npm run typecheck` - `npm test -- --maxWorkers=2` — 54 files, 362 tests passed - `npm run build` - verified the activation flow and `/channels` destination locally at desktop and mobile widths
…ailure Slack installs an app when it creates one from a manifest, and that install grants two scopes: channels:history and chat:write. The manifest's declared scopes reach the app's configuration but not the grant, which is what Slack's "you've changed the permission scopes" banner reports. One Reinstall to Workspace raises the grant to the full set. Measured against a real workspace. A token copied before that reinstall passes every check we have. auth.test succeeds, so attaching stores it and reports the adapter healthy. chat:write is present, so the bot can post. app_mentions:read is absent, so Slack never delivers app_mention and no handler ever runs — an online, structurally deaf Channel. The channels skill already documents an "online but silent" failure caused by a version disagreement, which logs a rejected delivery. This one logs nothing at all, because Slack never sends anything to reject, so it gets its own section next to it and the verify checklist now says "reinstalled" rather than "installed". Intelligence refuses a short token at paste time now, so the section also says to read that error as this problem caught early. examples/slack said "Install to Workspace → copy the xoxb- bot token", which is both the wrong button label and the wrong order. Its manifest declares even more scopes than the managed one, so the gap there is larger.
…ailure (#6330) ## What Documents a Slack setup failure that passes every check we have, in the two places that would have prevented it. - `skills/copilotkit-channels` gets a new section under the existing silent-failure guidance, and its verify checklist now says the app must be **reinstalled**, not installed. - `examples/slack/README.md` had the wrong button label and the wrong order: *"OAuth & Permissions → **Install to Workspace** → copy the `xoxb-` bot token."* No code changes. ## Why Measured against a real workspace: creating a Slack app from a manifest **installs it**, and that install grants exactly two scopes — `channels:history` and `chat:write`. The manifest's declared scopes reach the app's configuration but not the grant, which is what Slack's yellow *"you've changed the permission scopes"* banner is reporting. One **Reinstall to Workspace → Allow** raises the grant to the full declared set. A bot token copied before that reinstall is the trap, because nothing catches it: - `auth.test` succeeds, so the credential stores and the adapter reports healthy. - `chat:write` is present, so the bot can post — it does not look broken. - `app_mentions:read` is absent, so Slack never delivers `app_mention`, and no handler ever runs. The result is a Channel that is genuinely online and structurally deaf. The channels skill already documented an "online but silent" failure whose cause is a version disagreement between the installed `@copilotkit/channels-*` packages and the Intelligence deployment. That one is identifiable by a rejected-delivery log line. This one logs **nothing at all**, because Slack never sends anything to reject — so it needed its own section rather than a footnote on that one, and the distinguishing signal (a log line vs. no log line) is stated explicitly. `examples/slack` is the self-hosted adapter path rather than managed Channels, so the skill deliberately does not cover it — but the Slack behaviour is identical, and that manifest declares more scopes than the managed one (`users:read.email`, `team:read`, `chat:write.public`, plus a user token), so the gap there is larger. ## Notes for review - The two-scope measurement was taken against the managed Intelligence manifest. The mechanism is Slack's create-from-manifest flow, which is the same flow `examples/slack` uses, so I expect the same grant there — but the README wording says "only a couple of the scopes the manifest declares" rather than naming a count, since I have not measured that manifest specifically. - Intelligence now refuses a short-scoped token when it is pasted (`CHANNEL_ADAPTER_SLACK_TOKEN_SCOPES_INCOMPLETE`), naming the absent scopes. The skill says to read that error as this problem caught early, and notes that a Channel attached before that check existed can still be sitting in this state — only a rotation clears it. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…annels Ports the setup-slack-channel skill from CopilotKit/channels-sdk into the skill set that `copilotkit skills install` distributes, so the Slack setup flow is reachable from the CLI rather than only from a channels-sdk checkout. The skill is copied unchanged. It stays browser-first and makes no reference to the `copilotkit channels` commands: per Ben Taylor on 2026-08-03, those defer to the web app until they have more real-world usage. Both frontmatter descriptions are narrowed so the two skills stop matching the same request. copilotkit-channels now owns the code half — declaring, wiring, and customising a Channel — and assumes the provider app exists. setup-slack-channel owns the provider half for a first-time Slack setup. Each names the other's territory and hands off. Refs CopilotKit/channels-sdk#10
## Summary Document the draft-first Microsoft Teams setup flow across Channels docs, skills, and the Teams adapter README. ## Why Intelligence now offers a resumable Fast CLI path and a Guided manual path while keeping custom branding artifacts local and separating provider completion from runtime health. ## How - Describe the fully scoped provisioning and resume contract. - Replace Azure Bot and manifest-editing guidance with Teams Developer Portal plus Entra. - Teach both setup skills the local-only artifact and Team-installation boundaries. - Update documentation contract tests for the new path.
## Summary - add `defineChannelComponent` so an agent can call a server-rendered JSX component as a typed tool - add native JSX namespaces for Slack Block Kit and Teams Adaptive Cards - use one provider codec for both direct adapters and Intelligence-managed delivery - recover interactive handlers by stable JSX key after a process restart - generate and audit the native component catalog against the provider catalogs This keeps native provider UI in the existing Channels render and delivery path. It does not add a second renderer, transport, or action system. ## Review order 1. **Component tool contract:** `packages/channels-core/src/channel-component.ts`, `create-channel.ts`, and `thread.ts` - Standard Schema validates agent arguments before render. - Render receives the source platform and run `AbortSignal`. - The rendered UI posts as a separate provider message; the tool returns a short acknowledgement. 2. **Native IR:** `packages/channels-ui/src/native.ts` and `render.ts` - Native nodes carry a provider tag. - Traversal follows named slots such as Slack `accessory` and Teams `actions`, not only `children`. 3. **Slack:** `packages/channels-slack/src/native*.ts`, `render.ts`, and `interaction.ts` - `Slack.Block`, `Slack.Element`, and `Slack.Object` map to Block Kit field names. - Direct and managed Slack share the same codec and fallback-text rules. 4. **Teams:** `packages/channels-teams/src/native*.ts`, `render/index.ts`, and `interaction.ts` - `Teams.AdaptiveCard` is the explicit root. - The serializer computes the minimum Adaptive Card version from every type and property used. 5. **Recovery and managed parity:** `packages/channels-core/src/action-*.ts` and `packages/channels-intelligence/src/delivery-adapter.ts` - Stable JSX keys, the source platform, and the action value are stored in the action snapshot. - CopilotKit/Intelligence#729 asserts the final Slack Web API and Bot Framework request bodies. ## Data flow ```text agent tool call -> Standard Schema validation -> async JSX render -> portable or provider-native Channel IR -> Slack or Teams codec -> direct adapter or Intelligence-managed delivery -> provider API ``` ```text provider interaction -> provider callback decoder -> hot ActionRegistry lookup -> ActionStore snapshot fallback -> component re-render -> stable keyed handler ``` ## Public API An agent-rendered component uses the same JSX vocabulary as `thread.post`: ```tsx const Approval = defineChannelComponent({ name: "show_approval", description: "Post an approval request.", parameters: z.object({ title: z.string() }), render: ({ title }, { platform }) => ( <Card title={`${title} (${platform})`}> <Button key="approve" value="approve" onClick={approve}> Approve </Button> </Card> ), }); createChannel({ name: "approvals", components: [Approval], }); ``` Use native JSX only when the portable vocabulary does not expose a provider feature: ```tsx await thread.post( <Slack.Block.Section text={<Slack.Object.MarkdownText text="*Deploy ready*" />} accessory={ <Slack.Element.Button key="approve" text={<Slack.Object.PlainText text="Approve" />} value={{ decision: "approve" }} onClick={({ action }) => approve(action.value)} /> } />, ); ``` ```tsx await thread.post( <Teams.AdaptiveCard fallbackText="Deploy approval"> <Teams.TextBlock text="Deploy ready" wrap /> <Teams.ActionSet> <Teams.Action.Submit key="approve" title="Approve" value={{ decision: "approve" }} onSubmit={({ action }) => approve(action.value)} /> </Teams.ActionSet> </Teams.AdaptiveCard>, ); ``` ## Guardrails - Native nodes from one provider fail if rendered for another provider. - Slack rejects missing required fields, invalid top-level nodes, and more than 50 blocks. - Teams rejects invalid roots and explicit versions below the minimum required version. - Interactive nodes in agent-rendered components require stable, unique JSX keys. - `Slack.Raw` and `Teams.Raw` accept reviewed provider JSON but do not bind callbacks. The generated catalog is in `packages/channels/native-catalogs.md`. The package READMEs contain the full Slack, Teams, and component-tool usage notes. ## Test map | Contract | Main coverage | | --- | --- | | component tool schema, render context, post, and acknowledgement | `packages/channels-core/src/channel-component.test.ts` | | native IR, provider tags, and named-slot traversal | `packages/channels-ui/src/native.test.tsx` | | Slack catalog, serialization, validation, and callbacks | `packages/channels-slack/src/native-*.test.*` | | Teams catalog, versioning, serialization, and callbacks | `packages/channels-teams/src/native-*.test.*` | | keyed cold recovery and reaction recovery | `packages/channels-core/src/*recovery.test.*` | | managed codec parity | `packages/channels-intelligence/src/delivery-provider-elements.test.ts` and CopilotKit/Intelligence#729 | ## Validation - `pnpm nx run-many -t test,check-types,build --projects=@copilotkit/channels-ui,@copilotkit/channels-core,@copilotkit/channels-slack,@copilotkit/channels-teams,@copilotkit/channels-intelligence,@copilotkit/channels` - `pnpm verify:channels-umbrella` - `pnpm check:channel-native-catalogs` - `pnpm audit:channel-native-catalogs` - pre-commit tests, publint, and API Extractor checks for 27 affected projects - all PR checks pass on `b88f9b7e4f247939d65603d96df26090baad916a`
## Problem Channel agents lose inbound files whenever the current-trigger transcript omits attachments. The currently deployed Intelligence path always omits those files because normalized_payload never contains their handles. ## Why The delivery adapter seeds the current inbound turn from the transcript, and core then deduplicates the explicit prepared input. Files present only on the prepared delivery therefore never reach either the agent-history consumer or channel.getMessages during version skew. ## Fix Restore a missing current-trigger transcript file list from the prepared delivery inside ClaimedChannelDelivery.getTranscript(), where the result is shared and memoized for both consumers. Existing transcript files are preserved, so the Intelligence fix and this fallback cannot duplicate attachments. Either PR independently repairs the agent path. Coverage proves both an omitted transcript and an already-correct transcript hydrate the image for getMessages and agent seeding.
`bun-version: latest` let a Bun release change module-resolution behaviour between runs. 1.3.14 is the version the recent passing and failing runs both resolved, so pin it and make the job reproducible.
eventsource maps its `bun` export condition to the ESM build, and Bun resolves
`bun` before `require`. So a CJS require("eventsource") under Bun receives an
async ESM module and throws "require() async module ... is unsupported". The
package ships a real CJS build behind `require`, but Bun never reaches it.
Two CJS consumers in our graph hit this: the MCP SDK's own dist/cjs/client/sse.js,
and @ag-ui/mcp-apps-middleware, which requires that SDK path unconditionally at
module load. It surfaced as an intermittent failure of the runtime bun
integration job -- intermittent because it is a load-order race, where the run
only passes if the ESM graph happens to evaluate eventsource first.
Dropping the `bun` key makes Bun fall through to `import` for ESM consumers
(same file as before) and `require` for CJS consumers (the CJS build they need).
Takes the bun integration test from 5/20 to 20/20 locally. A version bump is not
an alternative: eventsource 4.1.0 still ships the same mapping.
Module-graph hygiene, not a behaviour fix -- the preceding eventsource patch is what fixes the bun failure. The SDK's SSE transport was imported at the top of the agent module but is only constructed inside the `type === "sse"` branch ~1300 lines below, so `eventsource` was pulled into the module graph of every non-SSE path, including every test that merely touches the agent module. Move it to an `await import()` at the point of use. `transport` and `mcpClient` gain explicit annotations so they keep real types instead of the bare `let` declarations they had before.
Resolves the one conflict in skills/copilotkit-channels/SKILL.md, where main (#6320) rewrote the same frontmatter description this branch narrowed. The two sides disagreed on Teams. This branch scoped the skill to the code half and said it "assumes the provider app already exists"; main established that Teams provider setup *is* this skill's job, because the CLI or dashboard wizard performs it. Kept the branch's code-half framing and the handoff to setup-slack-channel, but scoped that handoff to first-time Slack app creation only, so it no longer contradicts main's Teams sections or the Slack provider troubleshooting that stays in this file. Took main's version 1.1.0. Also registers setup-slack-channel in RESERVED_LIFECYCLE_SLUGS. Standalone skills have no packages/*/skills source, so without an entry the sync script treats the directory as an orphan: `pnpm check:plugin-skills` fails, and `pnpm sync:plugin-skills` deletes all 8 files of the new skill. Verified both behaviours against the pre-fix script before fixing. The paired size assertion in the unit test moves 9 -> 10.
…e is offerable Co-Authored-By: Claude <noreply@anthropic.com>
…d authority gate Co-Authored-By: Claude <noreply@anthropic.com>
…ot bypass the authority gate Co-Authored-By: Claude <noreply@anthropic.com>
…t chrome Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…er data Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…identity Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…sign brief Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…st 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>
…en data loads Co-Authored-By: Claude <noreply@anthropic.com>
…tics 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>
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.
…orced 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)
## 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
…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>
…me, 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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )