Skip to content

[pull] main from CopilotKit:main - #468

Merged
pull[bot] merged 91 commits into
TheTechOddBug:mainfrom
CopilotKit:main
Aug 4, 2026
Merged

[pull] main from CopilotKit:main#468
pull[bot] merged 91 commits into
TheTechOddBug:mainfrom
CopilotKit:main

Conversation

@pull

@pull pull Bot commented Aug 4, 2026

Copy link
Copy Markdown

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 : )

samjulien and others added 30 commits July 31, 2026 09:32
## 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.
mxmzb and others added 27 commits August 4, 2026 01:56
…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>
…identity

Co-Authored-By: Claude <noreply@anthropic.com>
…sign brief

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)
@pull pull Bot locked and limited conversation to collaborators Aug 4, 2026
@pull pull Bot added the ⤵️ pull label Aug 4, 2026
@pull
pull Bot merged commit 76aa8a8 into TheTechOddBug:main Aug 4, 2026
3 of 52 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants