Commit Graph

185 Commits

Author SHA1 Message Date
Maximiliano Korp 4f29570bd6 fix(channels-intelligence): recover from error-only websocket failures 2026-08-10 10:15:28 -07:00
Austin Merrick b32b5539cc feat: add Inspector navigation, usage, and locked Threads (refs ENT-1173) (#6275)
## What does this PR do?

Adds the CopilotKit consumer side of ENT-1173 across Shared, Runtime,
Core, Web Inspector, and the existing Shell Docs pages.

- Defines and parses optional trusted Inspector metadata for identity,
plan, license, action, usage, and expiry. Runtime proxies it through a
private, failure-isolated route, and Core refreshes it without changing
connection state.
- Groups Inspector navigation into Threads, Agents, and Learning.
Threads renders finite, unlimited, unknown, overage, and expiring usage
states plus matching trusted plan or license actions.
- Keeps explicit `threadEndpoints` as the only authority for Thread
requests. Locked or absent capability states make no list, subscription,
detail, message, event, or state calls.
- Keeps the zero-thread video, three example Threads, detail tabs, and
guided tour in empty and locked states. General Intelligence remains the
default onboarding path; only trusted `team_self_hosted` metadata uses
self-hosted onboarding.
- Gives an active license with missing Runtime routes a short **Finish
setting up Rich Threads** state. Users can copy a safe coding-agent
prompt or open the public Runtime setup guide. The same copy control
appears in that guide, and raw Markdown/LLM views include the full
prompt.
- Keeps finite usage green below 90%, orange from 90% to the limit, and
red at or above the limit. At 90%, a trusted plan action changes from
**Manage Your Plan** to a purple **Upgrade Your Plan** without changing
its trusted URL, action kind, or telemetry contract.
- Adds a deterministic 33-state loopback lab for CopilotKit developers.
It has no production route or export, is absent from public docs and
package metadata, and is excluded from the npm tarball.

`Expiring Soon` is display-only; this PR does not enable the thread
culler. Managed Enterprise receives no manage-plan action, and Team
Self-Hosted receives no hosted plan action. Optional metadata and the
additive expiry field remain compatible across mixed producer, Runtime,
Core, and Inspector versions.

A small Channels test-only change updates fetch mocks for current
TypeScript types. It changes no Slack or Teams docs or runtime behavior.

## Related PRs and issues

- Refs
[ENT-1173](https://linear.app/copilotkit/issue/ENT-1173/ship-plg-ready-inspector-navigation-metadata-and-locked-threads)
- Producer:
[CopilotKit/Intelligence#696](https://github.com/CopilotKit/Intelligence/pull/696)

## Validation

- `@copilotkit/web-inspector`: 20 files and 372 tests passed; typecheck
and production build passed.
- Shell Docs: 57 files and 383 tests passed; lint, typecheck, and
production build passed. The build generated all 222 static pages.
- Browser checks cover the copy-prompt flow, unchanged white **Manage
Your Plan**, purple **Upgrade Your Plan**, orange 4,500/5,000 usage, and
red 5,000/5,000 usage.
- Independent review found no Critical or Important issues.
- The broader Runtime, React Native, Channels, package-quality,
compatibility, and Node-version checks from the prior pushed head remain
green.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] I updated the relevant documentation
- [ ] "Allow edits by maintainers" is checked
2026-08-07 14:08:23 -07:00
Tyler Slaton 0cf899095b fix(channels): take the legacy path when a dropped stream start settles as applied
The gateway settles a direct-message slack.stream.start provider failure
as applied with capabilityError and no provider reference. Parse the
start result inside the fallback try so that shape reaches the legacy
create instead of hard-failing the stream body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 15:44:06 -07:00
Alem Tuzlak 85d30dd327 fix(channels): keep managed Slack DM replies visible 2026-08-05 15:44:06 -07:00
Ben Taylor 9a86e21d48 feat(runtime): split Channel status into transport and provider legs (refs OSS-739) (#6360)
**Half 2 of 2 for OSS-739.** Gateway half ships first:
CopilotKit/Intelligence#746.

## Why

`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 Channels
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 `SETUP_REQUIRED`, and the engine stopped doing
that at the 2026-07-29 realtime-boundary cutover (`8f166577ce`). 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.

## Change

- `connectRealtimeGateway` captures the control join reply (it was
**discarded**) and exposes `providerStates()`. Phoenix's `Push.resend`
preserves `recHooks`, so the hook re-fires on every auto-rejoin — 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:

```ts
status() → {
  overall: "setup_required",
  channels: { support: "setup_required" },
  detail: { support: { status: "setup_required", transport: "online", provider: "not_attached" } },
}
```

`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.

- The stale `setup_required` doc comments are corrected, with a note not
to describe the state again without a path that can emit it.

## Back-compat: `unknown` is load-bearing

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.

## Testing

- `channel-manager-provider-leg.test.ts` — 14, incl. the regression test
that never existed ("reports setup_required for a joined Channel with no
provider attached") and one case per degradation path
- `realtime-gateway-provider-states.test.ts` — 8 parser cases
- `realtime-gateway.test.ts` — +2 proving the wiring end-to-end through
real Phoenix framing, not just the parser
- Full suites: runtime **1874/1874**, channels-intelligence **192/192**;
`check-types` and `build` clean for both packages

**I mutation-tested the fold** — neutralising it fails 5 tests including
the linchpin — so these assert behaviour rather than passing vacuously.

Worth noting: two type errors (`ChannelsHandle` in `runtime.ts`, a
session mock) were invisible to vitest, which transpiles without
typechecking. The pre-commit build gate caught them.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-05 16:06:40 -05:00
Benjamin Taylor 9280e71346 refactor(channels): trim redundant provider-leg tests and fix a wrong comment
Self-review of the previous commit.

Corrects a factual error I introduced: the `SETUP_REQUIRED` note claimed such a
Channel "has no transport at all (the launcher never returned a handle)". False
for the `hasDirectAdapter` branch, which starts the developer-owned transport and
assigns a synthetic handle — so there IS a running transport there. Dropped the
wrong reasoning and shortened the note to the part that holds: the misattribution
is cosmetic on a path with no producer, and a future producer should report
through the `providerStates` seam.

Removes three tests that did not earn their place:

- "calls the seam ON the session" — redundant. `ProviderStateGateway.providerStates`
  reads `this`, so the two remaining tests already fail if the launcher ever used
  a detached reference. It died on the same mutation as the first test, for the
  same reason.
- "omits providerStates for a session without the seam" — survived the mutation
  that removes the forward, so it guarded nothing.
- the channel-level-error rejoin case — same `Push.resend` hook as the transport
  drop, so it re-proved one mechanism at ~1s extra wall-clock. Kept the drop
  case: it asserts a genuinely fresh socket, which is the "provisioned while the
  runtime was disconnected" story the design claim is about.
- "keeps the last reported states while a rejoin has not yet succeeded" — pinned
  behaviour with no observable consequence, since the transport leg dominates the
  fold while offline.

Also trims the drift-guard comment: why a guard was NOT added belongs in the PR
discussion, not permanently in source.

Re-mutation-tested after trimming: removing the forward kills both remaining seam
tests; making `providerStates` a snapshot kills the rejoin test while the other
41 gateway tests pass.

Verified: channels-intelligence 195/195, runtime 1874/1874, build + oxfmt clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:27:46 -05:00
Benjamin Taylor c71d9ac58b fix(channels): forward the provider seam through the exported launcher helper
Addresses review on #6360.

`startChannelsWithGatewayControl` is public so callers can compose over a
session they manage themselves, but it forwarded only `onClose` and
`onStateChange` — not `providerStates`. A handle without the provider seam makes
`ChannelManager.providerLeg` fall back to `unknown`, which keeps the
transport-derived status and reports `online` for a Channel with no provider
bound: the exact false green OSS-739 removes, still reachable through a public
export. The guard is widened too, so a session exposing only `providerStates`
is no longer dropped on the fall-through path.

Tests the reconnect claim that makes `providerStates` a getter rather than a
snapshot. Nothing exercised it: the gateway tests covered only the initial join
reply, and the manager-side rejoin test proves the manager re-reads on each
`status()` call, not that the session's value ever changes. The fake socket's
join reply can now vary per join, so a drop -> rejoin carrying a different
`channels` map asserts the refresh over real Phoenix framing — via both rejoin
paths (channel-level error on a live socket, and a full transport drop onto a
fresh socket), plus the case where a rejoin has not yet succeeded and the last
known states must persist.

Mutation-tested both: removing the forward kills 3 of 4 seam tests, and making
`providerStates` a captured snapshot kills both rejoin tests while all 3
pre-existing provider-state tests still pass — which is the gap itself.

Docs corrected against their real mechanisms:

- `attached`/`unhealthy`/`not_attached` now state the gateway's actual rule
  (adapter `status == "active"` is part of the predicate; a configured adapter in
  `error` is `unhealthy` with no failed health check), plus the best-of adapter
  fold that keeps a Slack-only Channel `attached`.
- `ready()` no longer promises it rejects on `error`. It awaits activation, so
  it can resolve while `status().overall === "error"` from an `unhealthy`
  provider. Says that instead.
- Notes the legacy `SETUP_REQUIRED` path reports a provider condition on the
  transport leg (dead, cosmetic, left rather than guessed at), and why the
  provider-state set is duplicated across the duck-typed package seam.

Verified: channels-intelligence 199/199, runtime 1874/1874, both builds clean,
oxfmt/oxlint clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:16:31 -05:00
Austin Merrick 9b604378aa test(channels): update fetch mocks for current types 2026-08-05 11:55:50 -07:00
Tyler Slaton 7e30957976 fix(channels): reconnect after clean gateway close 2026-08-04 13:54:36 -07:00
Mike Ryan 80370b5ecb fix(channels): preserve provider diagnostics 2026-08-04 12:33:17 -07:00
Tyler Slaton dfe728d833 fix(channels): retry gateway drain joins 2026-08-04 07:34:36 -07:00
Tyler Slaton 82eaecdfda fix(channels): retry transient gateway activation 2026-08-04 07:34:36 -07:00
Benjamin Taylor 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.
2026-08-04 08:21:30 -05:00
Maximiliano Korp 8e83dfa6db fix: downgrade in-flight Slack appends 2026-08-03 15:58:22 -07:00
Maximiliano Korp e6c740fd2e fix: preserve gateway delivery compatibility 2026-08-03 15:58:22 -07:00
Maximiliano Korp 5cc22a5a1f fix(runtime): preserve gateway handoff continuity 2026-08-03 15:58:19 -07:00
Mike Ryan 979a8ee990 feat(channels-slack): support data visualization blocks 2026-08-03 11:29:12 -07:00
Tyler Slaton 7f37c3395e fix(channels-intelligence): restore inbound trigger files (#6332)
## 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.
2026-08-03 10:43:41 -07:00
Tyler Slaton 65150a683b fix(channels-intelligence): repair all transcript consumers 2026-08-03 09:26:04 -07:00
Mike Ryan b88f9b7e4f feat(channels): complete native JSX contracts 2026-08-03 09:23:15 -07:00
Mike Ryan f8145b05b7 feat(channels): add native Slack and Teams JSX 2026-08-03 09:23:15 -07:00
Tyler Slaton 6c2c289d85 fix(channels-intelligence): restore prepared trigger files 2026-08-03 08:52:57 -07:00
Tyler Slaton 42d25ade8a fix(channels): accept namespaced app user IDs 2026-08-01 19:19:42 -04:00
Mike Ryan 17e7f33876 fix(channels): reconcile identity stack integration 2026-08-01 09:26:39 -07:00
Mike Ryan 561bf19fa6 feat(channels): add explicit identity and memory grants 2026-08-01 09:19:13 -07:00
Tyler Slaton 5ceb53799b fix(channels): complete managed provider parity 2026-08-01 11:10:28 -04:00
Tyler Slaton a0e516d403 fix(channels): delegate Teams typing to Gateway 2026-08-01 00:00:04 -04:00
Tyler Slaton aaf9d61a66 fix(channels): enforce provider delivery semantics 2026-07-31 23:49:51 -04:00
Tyler Slaton 183e83ff58 fix(channels): preserve stream cleanup errors 2026-07-31 23:12:04 -04:00
Tyler Slaton 1712be5ae9 feat(channels): complete managed Teams SDK parity 2026-07-31 23:11:06 -04:00
Tyler Slaton 871cd20f2a feat(channels): add managed welcome lifecycle 2026-07-31 18:26:41 -04:00
Tyler Slaton d247050646 feat(channels): declare Slack and Teams together 2026-07-31 18:23:28 -04:00
Mike Ryan eb6c8df0ad fix(channels): start Slack streams with first text 2026-07-31 10:26:41 -07:00
Mike Ryan 29d2721775 fix(channels): contain terminal delivery failures 2026-07-30 23:20:48 -07:00
Mike Ryan 552268d47d fix: keep participant metadata out of assistant history 2026-07-30 22:50:27 -07:00
Mike Ryan 81d192ad22 fix(channels): suppress exact provider self output 2026-07-30 19:51:08 -07:00
Mike Ryan 7259bae438 feat(channels): bound pending delivery capacity 2026-07-30 19:51:08 -07:00
Mike Ryan 9dc343ddf8 feat(channels): confirm managed file delivery 2026-07-30 19:51:08 -07:00
Mike Ryan ddfa6f3453 feat(channels): supersede pre-output runs 2026-07-30 19:51:08 -07:00
Mike Ryan 2a00a5a16a feat(channels): render native Slack status 2026-07-30 19:51:08 -07:00
Mike Ryan bff87d1428 feat(channels): handle transcript failures by surface 2026-07-30 19:51:07 -07:00
Mike Ryan f7437daad3 feat(channels): charge deliveries on first work 2026-07-30 19:51:07 -07:00
Mike Ryan f1eef91fed feat(channels): authorize delivery thread access 2026-07-30 19:51:07 -07:00
Mike Ryan b376c901f6 feat(channels): load delivery-scoped transcripts 2026-07-30 19:51:07 -07:00
Mike Ryan bf8abc3cef feat(channels): add V5 message operation routing 2026-07-30 19:51:07 -07:00
Benjamin Taylor eb9643a2f2 fix(channels): assert per-turn isolation by symptom, drop dead concurrency error
Strengthen the shared-instance-factory test to assert the user-visible defect
rather than object identity. Two overlapping turns through
`agent: (id) => shared` both read the one shared `messages` array, so each run
is prompted with the other user's question too. Against main's source the test
now reports exactly that:

  expected [ 'first+second', 'first+second' ] to deeply equal [ 'first', 'second' ]

The symptom is asserted before the mechanism so a regression names the defect
instead of posing an object-identity puzzle.

This also settles what the original "only the first mention gets a reply" report
was: overlapping mentions dropped by the old `onLockConflict: drop` default plus
the managed per-thread exclusive gate, both already fixed in #6256. Both turns
do reply here; what was left was cross-contaminated context, not a dead turn.

Remove `ChannelAgentConcurrencyError`, unthrown since #6256. Its doc claimed it
was kept so older importers would not break, but it was never re-exported from
`channels-intelligence`'s entrypoint — `git log -S` over index.ts confirms it was
never reachable from outside the package, whose only export is `.`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:56:33 -05:00
Mike Ryan e4a105a1c0 docs(channels): describe gateway delivery 2026-07-30 12:23:35 -07:00
Mike Ryan 7694cbcdd9 fix(channels): complete ordered delivery outcomes 2026-07-30 12:23:22 -07:00
Mike Ryan 02e7eaedb3 feat(channels): prioritize final provider delivery 2026-07-30 11:56:17 -07:00
Mike Ryan 69b23e95b7 fix(channels): name managed post results as assets 2026-07-30 11:56:00 -07:00