[pull] main from CopilotKit:main#50
Merged
Merged
Conversation
…ity, history/files, delete (OSS-476)
The realtime-gateway transport had drifted from the HTTP transport: it built a
text-only ingress envelope that coerced commands/reactions/interactions into
empty turns, keyed conversations per-turn (breaking threaded follow-ups),
dropped the provider actor identity, and implemented none of
fetchFile/getHistory/uploadFile (so the realtime path silently ran with no
history and no file support). It also had no `delete` render kind. This brings
the realtime path to full parity with direct.
- claim-mapping: extract the claim→ingress mapping (ClaimedDelivery,
conversationKeyFromReplyTarget, mapDeliveryToEnvelope) into a shared module
both transports use, so they cannot drift again. Add the provider `actor` to
the claim turn and map it to `env.user` (fixes identity on BOTH paths).
- realtime transport: build the envelope via the shared mapper — real kind
discrimination, thread-stable conversationKey, actor→user — instead of the
text-only inline build. Fail-closed: an unmodeled reply-target/kind is
dropped+logged, not crashed.
- file/history: extract fetchFile/getHistory/uploadFile into a shared
IntelligenceFileHistoryClient (HTTP-only — the gateway never relays bytes or
history). The realtime transport gains them when configured with
`appApiBaseUrl` + `apiKey` (threaded through the launcher); absent that, the
methods stay undefined and the adapter degrades exactly as before.
- delete render kind: add `{ kind: "delete"; ref }` to ChannelRenderEvent
(mirrors the frozen Intelligence contract) and route thread.delete through a
render frame when a render sink is wired (OSS-420), like post/update/file.
Tests: shared-mapper unit tests (actor→user, kind discrimination,
conversationKey, unmodeled-adapter throw); realtime tests for non-text kind +
identity + thread-stable key and file/history capability toggling; a
delete-render-frame test. Full suite + check-types + build green.
…ort (OSS-476) Pre-merge 7-agent CR of #5983 surfaced several realtime-transport defects (all in channels-intelligence). Fixes: - Poison-payload re-lease loop: an unmappable delivery (unmodeled reply-target adapter / unknown input.kind) with a valid lease was logged + dropped, so app-api re-leased the identical payload forever. It now fails NON-retryable (dead-letter), mirroring the HTTP path. nack() gains a `retryable` param. - Double-terminal-signal race: realtime ack()/nack() deleted delivery state AFTER the wire push, so a per-turn-timeout nack could race a late dispatch ack and emit BOTH fail + complete_requested. Now delete-before-push (XOR). - Concurrent dispatch: deliveries were handled fire-and-forget; now processed serially (parity with the HTTP runLoop) so an in-flight redelivery can't reset the shared per-turn seq counter or run two turns of one conversation at once. - actor.displayName was carried through the claim mapper then dropped in dispatchTo (`{ id }` only) — now forwarded to handlers (OSS-476 identity). - fetchFile enforced MAX_INBOUND_FILE_BYTES only against declared content-length; the actual read was unbounded. Now streams and aborts past the cap. - getHistory returned the FULL history for limit<=0 (slice(-0)) and hydrated file bytes for over-returned messages it then discarded. Now caps to the most recent `limit` BEFORE hydrating; limit<=0 -> []. - stream() posted an empty text frame for an empty stream; now skips the post. - HTTP withTimeout/defaultSleep timers now unref() (parity with realtime). - Corrected the inaccurate thread_started comment in claim-mapping.ts. Deferred to OSS-491 (delivery-terminal-signal contract, needs app-api coordination): an empty turn (reaction/command that posts nothing) has no valid completion signal (acceptedThrough requires >=1) and redelivers; and the run_error swallow on the HTTP-fallback render path. Tests: poison non-retryable, single-terminal XOR, serial dispatch, displayName forwarding, getHistory limit<=0 + cap. channels-intelligence 169 tests + check-types green.
…giene (OSS-476)
From the 7-agent CR confirmation round:
- RealtimeGatewayTransport.stop() now halts intake (a `stopped` guard in
handleDeliveryAvailable — the session exposes no `off` to detach the
DELIVERY_AVAILABLE listener) and DRAINS the in-flight delivery (awaits the
serial `processing` chain) before clearing state, so a turn settling at stop
time still sends its terminal signal instead of silently no-oping and
redelivering. Mirrors HttpDeliverySource.stop().
- Realtime nack() truncates the reason to 500 chars (parity with HTTP).
- getMessages drops empty content parts before join(" ") so a read_thread
transcript isn't corrupted with doubled/leading/trailing spaces (a test had
enshrined "part one part two").
- Removed dead imports (buildContentParts, AgentContentPart, ChannelFileRef)
left in http-transports.ts after the file/history extraction.
Deferred (delivery-contract / design; need coordination — folded into OSS-491):
timeout-nack can redeliver a still-running turnId (overlap); realtime push()
no-state fallback vs HTTP fail-loud (documented intentional — parity Q); the
empty-turn completion signal.
Tests: stop() drains in-flight + ignores post-stop deliveries; getMessages
assertion corrected. channels-intelligence 170 tests + check-types green.
…OSS-476)
Trivial items from the CR confirmation rounds (no behavior change to production
paths):
- intelligence-adapter.test.ts: `delete source.getHistory` was inert (getHistory
is a prototype method; delete only removes own props), so the "no getHistory"
branch was never actually exercised. Shadow with an own `undefined` instead so
the `source?.getHistory?.()` short-circuit is genuinely tested.
- in-memory-transports.ts: mirror the production `limit <= 0 -> []` guard in
InMemoryDeliverySource.getHistory (was `slice(-limit)` → returns ALL for 0).
- realtime ack(): log the empty-turn (no accepted frames) drop so the OSS-491
redelivery pile-up is diagnosable (every other drop path here logs).
- http-transports.ts: remove two orphaned JSDoc comments dangling over
ClaimResponse (leftovers from the file/history extraction).
- intelligence-adapter.ts: correct an inaccurate op-id comment (mintOp is the
${turnId}:${seq} source; the render path keys on ${turnId}:${slot}:${seq}).
channels-intelligence 170 tests + check-types green.
…tivation + map user name (OSS-476)
- [P1] History/files were absent on the NORMAL managed path: defaultActivateChannel
never forwarded the app-api HTTP URL, so the transport (which installs
fetchFile/getHistory/uploadFile only when appApiBaseUrl is set) ran without
them for Channels started by the CopilotRuntime handler — only manual
low-level launcher callers got file/history. Thread intelligence.ɵgetApiUrl()
through ChannelActivationConfig.apiUrl → defaultActivateChannel →
startChannelsOverRealtimeGateway({ appApiBaseUrl }). The launcher + transport
already accepted it.
- [P2] Managed turns exposed the provider profile under a non-public `displayName`
field, leaving PlatformUser.name undefined. Map env.user.displayName -> name
(parity with the direct Slack adapter, which populates `name`).
Tests: deriver returns apiUrl; defaultActivateChannel forwards appApiBaseUrl to
the launcher opts; onMessage sees message.user.name. channel-activation-config +
channels-intelligence (170) green; runtime build type-checks. (channel-manager.test
executes in CI — local vitest hits the known optional-peer-dep resolution flake.)
…r RED+GREEN harness) Faithful node:22-slim repro: fd1 through the same awk process-substitution as entrypoint.sh, a Railway-capped drain reader, a uvicorn+CVDIAG-shaped flood, and the static no-log /api/health as victim. RED wedges (200->502, CPU->0, heartbeat frozen); the FIXED lane stays 200 throughout. run.sh asserts the outcome (exit 3/4/5 on a false result, proven). watchdog.sh runs the entrypoint public-guard loop verbatim and needle-anchors it against entrypoint.sh.
…gate (default ON) Shared cvdiag_bootstrap: gate the per-LLM-call breadcrumb capture handler and the emit_cvdiag stdout write behind CVDIAG_LOG_STDOUT (default ON so every other integration is byte-for-byte unchanged; opt-out per service). Enqueue to the non-blocking PocketBase sink BEFORE the stdout write so a wedged fd1 cannot cost the durable breadcrumb. No sampling; full fidelity to PB. Red-green unit tests incl. a hostile-stdout durability test.
… alerting watchdog (claude-sdk-python) Self-activate CVDIAG_LOG_STDOUT=0 when CVDIAG_PB_URL is wired (safe: only silences stdout when PB is receiving; explicit override preserved). Extend the watchdog to poll the public $PORT /api/health and, on sustained failure, POST a loud #oss-alerts Slack alert BEFORE kill-restart; add the same alert to the agent-\:8000 branch (no silent recovery). Add uvicorn --no-access-log to cut the access-log flood. Together these keep the shared log stream under the 500/sec cap so the pipe never backs up.
…ity, history/files, delete (#5983) ## What & why The Realtime Gateway transport had drifted from the HTTP polling transport, so managed channels running over the gateway behaved differently from direct. This brings the realtime path to full parity. Before, the realtime transport: - built a **text-only** ingress envelope — commands/reactions/interactions were coerced into empty turns; - keyed conversations **per-turn** (`conversationKey = turn.id`), so threaded follow-ups didn't share agent/session state; - **dropped the provider actor** — `env.user` was never populated (on *both* transports, in fact); - implemented **none** of `fetchFile`/`getHistory`/`uploadFile`, so the realtime path silently ran with no history and no file support; - had **no `delete` render kind**, so `thread.delete` couldn't render over the gateway. ## Changes - **`claim-mapping.ts` (new, shared):** extract the claim→ingress mapping — `ClaimedDelivery`, `conversationKeyFromReplyTarget`, `mapDeliveryToEnvelope` — into one module both transports import, so they can't drift again (drift *was* the bug). Adds the provider `actor` to the claim turn and maps it to `env.user` — fixing identity on **both** paths. - **realtime transport:** builds the envelope via the shared mapper (real kind discrimination, thread-stable `conversationKey`, actor→user) instead of the inline text-only build. Fail-closed: an unmodeled reply-target/kind is dropped+logged, not crashed. - **`intelligence-file-history.ts` (new, shared):** extract `fetchFile`/`getHistory`/`uploadFile` into `IntelligenceFileHistoryClient`. These are **HTTP-only** — the gateway relays the render-event stream but never file bytes or history. The realtime transport gains them when configured with `appApiBaseUrl` + `apiKey` (threaded through the launcher); without those, the methods stay `undefined` and the adapter degrades exactly as before. - **`delete` render kind:** add `{ kind: "delete"; ref }` to `ChannelRenderEvent` (mirrors the frozen Intelligence contract) and route `thread.delete` through a render frame when a render sink is wired, like post/update/file. ## Tests - `claim-mapping.test.ts`: actor→user, kind discrimination (command/reaction/interaction/text), thread-stable conversationKey (slack/teams), unmodeled-adapter throw. - `render-events.test.ts`: realtime non-text kind + identity + thread-stable key; file/history capability toggling on config; a delete-render-frame test. - Full suite (160) + `check-types` + `build` green. ## Companion This is the CopilotKit SDK half of the managed-transport parity work; the Intelligence-side half (gateway `render_event/1` validator for file/delete, DB CHECK, Connector Outbox `chat.delete`, actor on the claim) landed separately. Together they close the direct-vs-managed parity matrix. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - Extract the platform-neutral Channels foundation into `@copilotkit/channels-core`. - Make `@copilotkit/channels` the batteries-included consumer entry point, with adapter and UI subpaths. - Release the umbrella, core, UI, and all six adapters as one shared `channels` version scope. - Verify the packed consumer contract and migrate the Slack and Teams examples to the umbrella. ## Why Consumers should be able to install one Channels package without making the runtime or selective integrations depend on every platform SDK. Shipping the complete Channels family together prevents adapter/core version drift and makes the umbrella's exact dependency set release as a compatible unit. ## How - Move shared bot/runtime primitives into `channels-core` and invert adapter/runtime dependencies. - Add exact workspace dependencies and export subpaths from the umbrella package. - Consolidate the existing release configuration and all three release workflow selectors into one shared `channels` scope containing all nine packages. - Use `@copilotkit/channels` as the version source: the next minor release resolves to `0.2.0` and bumps every Channels package together. - Publish scoped packages in dependency order for both stable and canary releases: UI, core, adapters, then the umbrella. - For a stable Channels release, publish the UI/core/adapters first, run the registry-backed packed-consumer verifier against those newly published exact versions, then publish the umbrella. - Generate Channels release notes from `channels/v*` tags rather than the monorepo `v*` tags. - Verify builds, type checks, tests, package artifacts, examples, and release workflow scope synchronization locally. ### First stable release sequence 1. Bootstrap the currently unpublished `@copilotkit/channels-core` package on npm and configure npm trusted publishing for every Channels package against this repository's `release / publish` workflow and `npm` environment. 2. Create and merge the `channels` minor release PR. The stable workflow publishes the Channels family in the staged order above and validates the packed umbrella from the registry before the umbrella is released. 3. Create and merge a subsequent `monorepo` release PR so the published `@copilotkit/runtime` switches from the historical umbrella dependency to `@copilotkit/channels-core`.
## Release channels v0.2.0 **Scope:** `channels` | **Bump:** `minor` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `channels` packages to `0.2.0` 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.2.0` - Creates git tag `channels/v0.2.0` - 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.
## Problem The Channels v0.2.0 release publishes its dependency packages, then fails before publishing the umbrella package because pnpm rejects the freshly published dependencies under the 24-hour minimum release age policy. ## Why The registry-backed verifier installs the packed umbrella in an isolated temporary consumer without carrying a Channels-family release-age exemption into that consumer. ## Fix Generate a pnpm workspace config for the isolated consumer that exempts every Channels package while retaining the maturity gate for unrelated dependencies. Add regression coverage for the complete family. Verified with all 109 release-script tests, the registry-backed Channels verifier, explicit lint/typecheck/format checks, and the full Nx package build.
## Release monorepo v1.63.0 **Scope:** `monorepo` | **Bump:** `minor` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `monorepo` packages to `1.63.0` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `monorepo` packages to npm at version `1.63.0` - Creates git tag `monorepo/v1.63.0` - 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.
…oop wedge (#5987) ## What happened The `claude-sdk-python` showcase column went fully red on **staging** (37 cells, `BE ✗` cascading) while prod and the TypeScript sibling stayed green on the same image. It wasn't 37 bugs — it was one wedged replica. **Root cause:** a D6 fan-out pushed the container's combined log volume past Railway's ~500 logs/sec drain cap. `entrypoint.sh` pipes both processes' stdout through an `awk` process-substitution, so Next.js's `fd1` is a **synchronous pipe**; when Railway stopped draining, the pipe filled and Next.js's next `console.log` blocked in `write(2)`, **freezing the event loop**. Even the static `/api/health` went 502, CPU→0, memory flat, process alive — so `restartPolicyType: ON_FAILURE` never fired and `numReplicas: 1` meant one wedge reds the whole column. Load-triggered, not a code/env diff. Full analysis (root cause + mitigation trade-offs): Notion → *Showcase stdout-backpressure wedge* under Plans / Proposals. ## The fix (two coordinated MUSTs) Showcase's promise is preserved throughout — real D6 traffic, live page, and full CVDIAG diagnostics all intact. Nothing is sampled or dropped. - **MUST-1 — take the flood off stdout, losslessly.** Route CVDIAG's per-LLM-call breadcrumb off stdout behind a new `CVDIAG_LOG_STDOUT` gate (**default ON**, so every other integration is byte-for-byte unchanged); the non-blocking PocketBase sink still receives every envelope at full fidelity — which is the path `cvdiag classify` and the dashboard already read from. `emit_cvdiag` now enqueues to PB **before** the stdout write so a wedged fd1 can't cost the durable breadcrumb. `entrypoint.sh` self-activates `CVDIAG_LOG_STDOUT=0` only when `CVDIAG_PB_URL` is wired (safe: never silences stdout when PB isn't receiving). Plus uvicorn `--no-access-log` to drop the access-log noise. - **MUST-2 — detect and recover the hang, loudly.** Extend the watchdog to poll the public `$PORT /api/health` and, on sustained failure, POST a `#oss-alerts` Slack alert **before** kill-restarting — and add the same alert to the agent-`:8000` branch. No silent recovery: every wedge pages. ## Red → green proof A docker `node:22-slim` repro (`showcase/tests/repro/stdout-wedge/`) reproduces the real topology (same `awk` pipe, Railway-capped drain reader, uvicorn+CVDIAG-shaped flood, static no-log `/api/health` victim): - **RED** (unmitigated): `/api/health` 200 → 502/timeout the instant the flood crosses the cap; heartbeat frozen; CPU parked at 0. `200=6 / WEDGE=11`. - **GREEN** (flood cut below cap): health stays fast-200 across the whole window; heartbeat advances. `200=16 / WEDGE=0`. - `run.sh` asserts the outcome and **exits non-zero on a false result** (a deliberately-forced false-GREEN exits 5, was exit 0 pre-fix). - MUST-2: the actual `entrypoint.sh` watchdog loop, run verbatim against a genuinely wedged port, detects → POSTs the captured alert → `kill -9`s the real Next.js PID. `watchdog.sh` needle-anchors the public probe/kill/alert against `entrypoint.sh` (mutating the probe fails the test). - Unit: `test_cvdiag_log_stdout_gate.py` — 5/5 incl. a hostile-stdout durability test (RED: enqueue starved; GREEN: enqueue preserved). Full `_shared` suite 19 passed / 2 skipped. ## Review Tier-3 cr-loop (shared source + deploy config + kill path): 7-agent review → adversarial per-finding verification → 5 mandatory fixes (all red-green'd, isolated worktrees) → 7-agent confirmation round converged to **zero mandatory findings** → bucket-(c) promotion audit `PROMOTE_TO_A: 0`. ##⚠️ Not merge-ready yet — draft on purpose - [ ] **Staging branch-deploy validation** — local can't exercise the one runtime unknown: that the container actually reaches `showcase-pocketbase.railway.internal:8090` and lands a `cvdiag_events` row. Must confirm on a staging deploy of this branch before merge. - [ ] **Railway env wiring (out-of-band, human-gated):** set on `showcase-claude-sdk-python` (staging + prod) — `CVDIAG_BACKEND_EMITTER=1`, `CVDIAG_PB_URL=http://showcase-pocketbase.railway.internal:8090`, `CVDIAG_WRITER_KEY` (op:// in DevOps `showcase`), `SLACK_WEBHOOK_OSS_ALERTS`. Without these MUST-1/MUST-2 stay inert (safe: default is current behavior). - [ ] Green CI. ## Follow-up (non-blocking, fail-safe) Repro-harness polish (none false-GREEN-capable): GREEN heartbeat assertion is timing-fragile under a raised start-delay (false-RED only); `run.sh` local lane doesn't forward all tunables; `watchdog.sh` webhook-assert race + helper-death flake; reader has no close handler on the local lane. Pre-existing (bucket c): `OPENAI_API_KEY` warning is mislabeled; Next.js has no readiness gate; `_SETUP_DONE` degrade-latch. 🤖 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 : )