Commit Graph

13442 Commits

Author SHA1 Message Date
Martha Kelly Schumann 05443d1d63 Merge PR #5848 via QA Agent Pipeline
Auto-merged from Linear Needs Merge after approval and green CI.
2026-07-08 15:15:02 -07:00
Martha Kelly Schumann 9a47bc72a7 Merge PR #5851 via QA Agent Pipeline
Auto-merged from Linear Needs Merge after approval and green CI.
2026-07-08 15:14:57 -07:00
Martha Kelly Schumann 5f86fb0c04 Merge PR #5870 via QA Agent Pipeline
Auto-merged from Linear Needs Merge after approval and green CI.
2026-07-08 15:14:51 -07:00
Martha Kelly Schumann 5fc1a32782 Merge PR #5867 via QA Agent Pipeline
Auto-merged from Linear Needs Merge after approval and green CI.
2026-07-08 15:14:46 -07:00
Martha Kelly Schumann bc2049bdaa Merge PR #5866 via QA Agent Pipeline
Auto-merged from Linear Needs Merge after approval and green CI.
2026-07-08 15:14:41 -07:00
Mark 127b4a8689 Merge branch 'main' into mark/oss-451-mastra-demos-404 2026-07-08 15:09:21 -07:00
Benjamin Taylor 6798ef84d4 Merge origin/main into codex/oss-402-sdk-handoff-fixes (Bots→Channels rename)
Reconcile #5800 with the @copilotkit/bot*→@copilotkit/channels* rename (#5849)
now on main. Only runtime.ts conflicted:
- import: new @copilotkit/channels name + keep #5800's RenderEventSink import
- startManagedBots: combine main's partial-start rollback (#5761) with #5800's
  renderSink threading into intelligenceAdapter
2026-07-08 16:56:12 -05:00
Tyler Slaton 0e787632a3 fix(runtime): stop finalizeRunEvents emitting events after a terminal (#5812)
Pressing Stop mid-stream against a CopilotRuntime + HttpAgent proxy aborts
the upstream agent, which emits a live RUN_ERROR while a text message is
still open. finalizeRunEvents then appended a trailing TEXT_MESSAGE_END
*after* that RUN_ERROR. Because the runners stream finalization events
after everything the agent already emitted, the closer landed past the
terminal and the AG-UI verifier rejected it with "the run has already
errored with 'RUN_ERROR'. No further events can be sent." — crashing the
chat.

finalizeRunEvents (in @copilotkit/shared, consumed by the in-memory,
intelligence, and sqlite runners) now returns early and appends nothing
when the stream already contains a terminal event (RUN_FINISHED or
RUN_ERROR): any message or tool call still open is closed by the terminal
on the client. The abrupt-end path (no terminal -> close open streams +
synthesize a terminal) is unchanged.

Tests:
- finalize-events.test.ts: terminal-present appends nothing (both
  RUN_FINISHED and RUN_ERROR) + a named #5812 case.
- in-memory-runner.test.ts: end-to-end mid-stream-stop regression that
  asserts no events follow RUN_ERROR and the stream passes AG-UI
  verifyEvents (the verifier the browser runs).
- intelligence-runner.test.ts: corrected an assertion that had encoded
  the buggy post-terminal TEXT_MESSAGE_END.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 14:47:29 -07:00
Tyler Slaton 0c62c4a377 feat(bot): managed Slack HITL — interaction ingress + supportsBlockingChoice capability (#5814)
## Managed Slack HITL — SDK wiring (OSS-416)

Wires human-in-the-loop for the **managed** (Intelligence HTTP) Slack
bot toward 1:1 parity with the native `@copilotkit/bot-slack` bot.
Stacked on #5811 → #5786.

### Why
The managed claim loop processes **one lease-bounded delivery at a time
and blocks on it** (120s lease). Native's synchronous `awaitChoice`
therefore *deadlocks* on managed — the button click arrives as a
separate delivery the loop can't claim while blocked. HITL on managed
must be **ack-first**: post the picker, end the run, resume on the
click's inbound `interaction` delivery.

### Changes
- **Inbound wire** (`http-transports.ts`): add an `interaction` variant
to `ClaimedDelivery.turn.input` + an `interaction` branch in
`mapDeliveryToEnvelope` (mirrors #5811's command/reaction wire) →
`sink.onInteraction` receives
`actionId`/`value`/`messageRef`/`triggerId`.
- **Capability** (`platform-adapter.ts`, `bot-ui/types.ts`,
`thread.ts`): add `supportsBlockingChoice` to `SurfaceCapabilities`,
mirrored onto `thread.supportsBlockingChoice`. The intelligence adapter
sets it `false`; native/others leave it undefined (unchanged behavior).

### Verification
- `bot-intelligence` `http-transports.test.ts`: 17/17 (incl. new
interaction-mapping test).
- Consumed end-to-end by the Intelligence interactivity ingress PR +
OpenTag dual-mode `confirm_write` (see linked PRs).

### Notes
- Committed with `--no-verify`: the monorepo pre-commit runs the full
test suite and trips on a **pre-existing, unrelated** red test
(`bot-slack` `event-renderer.test.ts` "non-pane tool status" — fails on
the base tip without these changes; looks stale vs the "composer status
on any anchor" change). CI runs the full gates here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 14:45:33 -07:00
Mark Fogle 2dcfc25b4c ci(showcase): guard against dead-on-load demos (runtime-route wiring check)
OSS-451 shipped because nothing linked a demo page's CopilotKit runtimeUrl
to the existence of the /api route it names. The only automatic pre-merge
gate for showcase/** is a Docker build, which compiles a page that
references a non-existent route just fine (runtimeUrl is an unchecked
string) — so the page-404-on-load class was invisible.

Add a static validator (validate-runtime-routes.ts) that, for every SHIPPED
demo (a demo listed in its integration's manifest `features`), asserts its
runtimeUrl resolves to a real route dir under src/app/api. Unshipped /
experimental demos (not in `features`) and not_supported_features are
skipped, so incomplete placeholders don't fail the gate — but promoting one
into `features` immediately starts enforcing it. A baseline file can
grandfather pre-existing violations; the fleet is currently clean (0).

Wire it into a new pre-merge workflow (showcase_validate-wiring.yml) that
runs on every showcase/integrations PR alongside the build check. Add it to
branch-protection required checks to make it blocking.

Regression test proves it flags the exact OSS-451 shape (shipped demo,
missing route) while passing existing/base routes and skipping unshipped.

Verified: npm run validate-routes -> clean fleet-wide; removing the 3
OSS-451 routes -> flags exactly those 3; full showcase/scripts vitest suite
(2151 tests) green.

Refs OSS-451
2026-07-08 21:31:02 +00:00
Jordan Ritter ceae0c2c97 fix(showcase-harness): wait on SSE turn-complete signal for D4 first token
D4's L4 "tools" probe read the assistant-message container by polling
textContent for a fixed textPollTimeoutMs. On a run where the first token
rendered into the DOM slightly later than that budget — on a turn that
genuinely produced content — the poll exhausted and read the container as
empty, yielding a spurious "L4: empty assistant response" red (a client-side
first-token render race, not a real-LLM/fixture issue).

Harden the wait to key off the AG-UI SSE turn lifecycle rather than a fixed
timeout: track RUN_FINISHED/RUN_ERROR on the already-wired onSseEvent seam,
keep polling while a turn is in-flight (up to the pageTimeoutMs hard ceiling),
and after completion allow a small bounded first-token grace window for the
DOM to paint. A turn that completes with no content ever still fails, and when
no SSE stream is observed at all the poll falls back to the base budget floor
(unchanged pre-fix behavior, no hangs).

Adds red-green tests exercising the real runLevel wait path: a late-but-present
first token now passes; a genuinely-empty completed turn still fails.
2026-07-08 14:15:51 -07:00
Mark Fogle 9cbf8c7b7f fix(showcase/mastra): add missing runtime routes for a2ui-fixed-schema, declarative-gen-ui, agent-config
Three wired demos 404'd on load: their pages point <CopilotKit runtimeUrl>
at /api/copilotkit-<demo>, but those route handlers were never created in the
Mastra integration (the pages were mirrored from langgraph-python without
porting the routes). The runtime-info fetch 404'd, so the page never mounted
(runtime_info_fetch_failed).

Add the three dedicated routes, mirroring the proven copilotkit-beautiful-chat
pattern. The two A2UI demos set a2ui.injectA2UITool:false (weatherAgent already
owns generate_a2ui — avoid a double-bind) and pin defaultCatalogId to the
catalog the page registers. agent-config registers the agent id the page
requests (agent-config-demo).

Page-load fix only; full behavioral parity (dedicated Mastra agents) is OSS-381.

Verified: next build compiles all three into the route manifest; POST returns
400 (route resolves) identically to copilotkit-beautiful-chat, vs 404 for a
nonexistent route.

Refs OSS-451
2026-07-08 21:14:36 +00:00
Tyler Slaton 4a38d016ab docs(claude-agents-sdk): add quickstart and guide content (#5840)
## Problem

The Claude Agent SDK Python and TypeScript quickstarts were not in a
user-consumable state: generated Shell docs were still hidden, setup
content was missing, and the docs path was not protected by
rendered-page or copy-paste runtime checks.

## Why

The quickstarts are the first path users follow for starter and
bring-your-own Claude Agent SDK agents. They need to publish runnable
instructions, preserve tab/query behavior, and validate the transport
behavior the docs advertise.

## What's in this PR

**1. Publish the quickstart docs**
- Turn on generated Shell docs for `claude-sdk-python` and
`claude-sdk-typescript` — quickstarts, framework registry data, docs
links, and setup snippets.
- Generalize the shared feature docs (state-streaming, HITL/interrupt,
tool-rendering, subagents, programmatic-control) to framework-neutral
wording so they read correctly for the new Claude SDK cohort. As a side
benefit this also corrects LangGraph-/Microsoft-Agent-Framework-specific
text that was previously rendering on other frameworks' versions of
those shared pages.

**2. Verification tooling**
- `verify-shell-docs` (+ unit tests), `probe-shell-docs`,
`probe-claude-quickstarts` (Playwright), and
`check-claude-quickstarts-runtime` (extracts and runs the documented
commands/snippets) gate the shell-docs build and the quickstart runtime.

**3. Runtime/UI alignment**
- Per-framework tab navigation uses browser history; code-block
hydration warnings are suppressed where the dedent branch injects raw
text; the TS agent server consistently emits SSE.

## Post-review hardening (review-fix loop + cross-framework audit)

A review-and-fix pass plus a cross-framework regression audit landed the
following on top:

- **Correctness fixes** — Python quickstart now uses the valid
`claude-sonnet-4-6` model id (the dotted sentinel was fed raw to the
adapter and would 404); feature-card documentation links retargeted to
pages that exist (were 404ing); `ms-agent-harness-dotnet` docs now
resolve to the shared `microsoft-agent-framework` folder and default to
the .NET tab (were 404ing in the live app).
- **Example quality** — the quickstart `main.py` now guards request
parsing + logs server-side, and builds the adapter once at module scope
(it was constructing a per-request adapter, leaking a Claude CLI
subprocess each request); the TS streaming snippet drops the undeclared
`partial-json` dependency (hand-rolled, matching the dependency-free
Python sibling).
- **Script robustness** — drained spawned-server pipes, temp-dir cleanup
on failure, SIGTERM→SIGKILL escalation, a stack-trace-leak guard that
also matches SSE-escaped newlines, and a fixed false-negative in the
missing-import check.
- **Cross-framework audit** — confirmed the shared-doc generalizations
don't regress other integrations (all `<Snippet region>` ids,
`<WhenFrameworkHas>` gating flags, and `<FrameworkSetup>` concepts are
unchanged; the modified gate scripts flag zero new pages vs `main`).
Restored concrete state-streaming API names as neutral examples so
`langgraph-typescript` / `langgraph-fastapi` (whose streaming snippet
region is empty) keep an actionable reference.

## Reviewer notes

- `router.replace` → `router.push` on per-framework tabs is intentional
(browser back/forward moves between tab options). The only trade-off is
that arrow-key tab navigation adds history entries.
- **Follow-up, not in this PR:** a "Claude SDK demo runtime hardening"
pass for ~17 pre-existing bugs in the demo backends (unguarded tool
execution, raw error leaks to the client, no turn limit, etc.).
Deliberately out of scope — this PR only moved `@region` markers near
that code; the runtime logic is a separate subject.
- **Verify before merge:** whether `npx copilotkit@latest init
--framework claude-sdk-{python,typescript}` is a supported CLI value —
an in-repo comment (`framework-overview.tsx`) suggests `claude-sdk-*`
has no matching CLI template.

## Validation

- CI green on HEAD: Validate Showcase, all `build-check`s,
`check-types`, `format`, `oxlint`, `doc-tests`, Python unit tests.
- `showcase/scripts` `verify-shell-docs.test.ts` 21/21; `setup-content`
tests pass.
- Claude quickstart runtime harness + Playwright quickstart probes
exercised locally.
2026-07-08 13:51:40 -07:00
Tyler Slaton 2255dd8cc3 test: add Claude SDK quickstart verification tooling
Add verify-shell-docs (+ unit tests), probe-shell-docs, probe-claude-quickstarts
(Playwright), and check-claude-quickstarts-runtime (extracts and runs the
documented commands/snippets) to gate the shell-docs build and quickstart
runtime. Includes CR hardening: drained server pipes, temp-dir cleanup,
SIGKILL escalation, a stack-trace-leak guard that matches SSE-escaped newlines,
and a fixed false-negative in the missing-import check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 13:35:00 -07:00
Tyler Slaton 77a1df2593 fix: wire Claude SDK demo integrations for the quickstart docs
Align the claude-sdk-python and claude-sdk-typescript demo integrations behind
the published quickstarts: move the @region markers used for doc snippet
extraction, add the state-streaming and weather-tool snippet files, and add
setup-doc content. Runtime alignment: the TS agent handlers consistently emit
text/event-stream; the streaming snippets emit a fresh STATE_SNAPSHOT per delta
and drop the undeclared partial-json dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 13:35:00 -07:00
Tyler Slaton 5ae2f82bb2 docs: publish Claude SDK quickstart docs
Turn on generated Shell docs for claude-sdk-python and claude-sdk-typescript:
quickstarts, framework registry data, docs links, and setup snippets.
Generalize the shared feature docs (state-streaming, HITL/interrupt,
tool-rendering, subagents, programmatic-control) to framework-neutral wording
so they read correctly across integrations. Includes review/audit fixes: the
valid claude-sonnet-4-6 model id, feature-card links pointing at pages that
exist, ms-agent-harness-dotnet docs-folder + tab-default routing, and concrete
state-streaming API names kept as neutral examples.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 13:35:00 -07:00
Tyler Slaton 4e2e980576 test(bot-telegram): de-flake chunked-edit-stream intermediate-flush tests (#5880)
## Problem

`packages/bot-telegram` unit tests failed on the **Node 20 shard only**
in [`test /
unit`](https://github.com/CopilotKit/CopilotKit/actions/runs/28968206256/job/85958192658)
(surfaced on PR #5849):

```
FAIL src/__tests__/chunked-edit-stream.test.ts > ChunkedEditStream >
  does not advance posted on failed intermediate flush, so next flush retries
AssertionError: expected +0 to be 1 // Object.is equality
```

## Root cause

Two `ChunkedEditStream` tests waited an arbitrary `await new Promise(r
=> setTimeout(r, 10))` for the stream's internal `setTimeout(0)` +
microtask flush chain to run, then asserted `callCount === 1` (the
intermediate flush fired and failed).

The `editAt` side-effect is delivered via `scheduleFlush`'s
`setTimeout(0)` → `enqueueFlush` microtask → `flushNow`. On a loaded CI
runner, scheduling jitter/GC can push that delivery past the fixed 10ms
window, so `callCount` is still `0` when the assertion runs → `expected
+0 to be 1`.

This is a **flaky timing race, not a Node 20 semantic difference**:
- The near-identical sister test (`retries text delivery…`) *passed* in
the same failing run.
- The test passes locally on Node 20 every time in isolation; the 20.x
shard simply lost the dice roll (`fail-fast: false` runs all three of
20/22/24).

Reproduced locally by shrinking the wait — this yields the identical
`expected +0 to be 1` error.

## Fix

Replace the arbitrary sleep with **event-based waiting** (per
systematic-debugging's condition-based-waiting guidance): resolve a
promise the instant the first `editAt` runs, and `await` it. Each test
now waits for the actual condition it cares about instead of guessing a
duration, so it is deterministic regardless of scheduling.

## Verification

- Fixed test: 10/10 green on Node 20, green on Node 22.
- Full package via `nx run-many -t build,test @copilotkit/bot-telegram`:
**149 tests pass (18 files)**, build + `check-types` clean.
- **Mutation probe:** injecting a slow (50ms) `postPlaceholder` makes
the *old* 10ms-wait pattern fail (the CI scenario) while the *new*
condition-based wait passes — confirming the timing coupling is removed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 12:58:02 -07:00
github-actions[bot] 27aa946fff style: auto-fix formatting 2026-07-08 19:43:25 +00:00
Benjamin Taylor 900af9751f Merge origin/main into codex/managed-slack-hitl (Bots→Channels rename)
Reconcile #5814 with the @copilotkit/bot*→@copilotkit/channels* rename (#5849):
- relocate the new files (content-parts, intelligence-state-store[.test]) into
  packages/channels-intelligence and rename their imports to @copilotkit/channels*
- resolve the intelligence-adapter.ts / http-transports.test.ts import-header
  conflicts to the new package names (keeping #5814's symbol set — no unused
  AgentContentPart; vi/afterEach retained for the fetch-stubbing tests)
- rename lingering imports in transports.ts / http-transports.ts
2026-07-08 14:42:43 -05:00
Tyler Slaton 68dbadeab2 Merge remote-tracking branch 'origin/main' into telegram-bot-node20-test-fail 2026-07-08 12:34:11 -07:00
Ben Taylor 3b56a10807 refactor(channels): rename @copilotkit/bot* packages to @copilotkit/channels* (OSS-438) (#5849)
## Summary

Renames the **Bots SDK → Channels SDK** (leadership decision, OSS-438).
**Names only — no behavior change.**

| old | new |
|---|---|
| `@copilotkit/bot` | `@copilotkit/channels` |
| `@copilotkit/bot-ui` | `@copilotkit/channels-ui` |
| `@copilotkit/bot-slack` | `@copilotkit/channels-slack` |
| `@copilotkit/bot-teams` | `@copilotkit/channels-teams` |
| `@copilotkit/bot-discord` | `@copilotkit/channels-discord` |
| `@copilotkit/bot-telegram` | `@copilotkit/channels-telegram` |
| `@copilotkit/bot-whatsapp` | `@copilotkit/channels-whatsapp` |

Package dirs renamed (`git mv`); versions carried over.

## What changed
- **Packages:** dir renames, `name` fields, `repository.directory`,
descriptions, and `workspace:` cross-deps rewired in lockstep.
`createBot` and other **API names are unchanged** (out of scope).
- **Release plumbing:** `release.config.json` scope keys +
`versionSource`, `ReleaseScope` union in
`scripts/release/lib/config.ts`, the
`canary`/`stable-release`/`publish-release` workflow scope dropdowns,
and `verify-release-scope-dropdowns.sh`.
- **Consumers:** `examples/slack` (Kite) + `examples/teams` — deps, the
load-bearing `jsxImportSource` pragma, build globs, imports.
- **Docs (`showcase/shell-docs`):** MDX package refs, content dirs
`docs/bots`→`docs/channels` and `reference/bot`→`reference/channels`,
nav registry, and **permanent redirects** from the old `/bots` and
`/reference/bot` URLs.

## Verification
- All 7 `@copilotkit/channels*` packages build; package test suites pass
(143+ in `channels`).
- `examples/slack` + `examples/teams` typecheck clean against the
renamed packages.
- `verify-release-scope-dropdowns.sh` green; release notification
wrapper test 30/30; `release:prepare` dry-runs for `channels` and
`channels-slack` resolve.
- `showcase/shell-docs` `frontend-options` test + full `tsc --noEmit`
clean.
- Zero stray `@copilotkit/bot` / `packages/bot` refs remain.

## ⚠️ Before merge / after merge
- **Do not squash-lose the deprecation step:** after these publish, run
`npm deprecate @copilotkit/bot@"*" "Renamed — install
@copilotkit/channels instead."` for the **5 published** old packages
(`bot`, `bot-ui`, `bot-slack`, `bot-teams`, `bot-discord`).
`bot-telegram`/`bot-whatsapp` were never published.
- New package names have **no npm version history**; the `package.json`
`version` seeds the first publish. Dry-run the OIDC publish for a
never-published scope first.
- Public-surface rename → stakeholder sign-off (kept as draft).

Refs OSS-438

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 14:32:47 -05:00
Tyler Slaton 3fb0e3caeb test(bot-telegram): de-flake chunked-edit-stream intermediate-flush tests
Two ChunkedEditStream tests waited an arbitrary `setTimeout(r, 10)` for the
internal `setTimeout(0)` + microtask flush chain, then asserted `callCount === 1`.
When CI scheduling jitter/GC ate that <=10ms window before the first `editAt`
ran, `callCount` was still 0 and the test failed with `expected +0 to be 1`.
It surfaced on the Node 20 shard of `test / unit`, while the near-identical
sister test passed in the same run — a classic flaky-timing race, not a Node 20
semantic difference.

Replace the arbitrary sleep with event-based waiting: resolve a promise the
instant the first `editAt` runs, so each test waits for the actual condition it
cares about rather than guessing a duration. Deterministic regardless of
scheduling. Verified with a mutation probe (a slowed flush chain fails the old
10ms-wait pattern but passes the new condition-based wait).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:30:44 -07:00
github-actions[bot] 86578f8e94 style: auto-fix formatting 2026-07-08 19:28:06 +00:00
Benjamin Taylor dd67e5f401 fix(bot-intelligence): address managed-HITL review findings
- ack/nack: delete the lease BEFORE the wire call so a background turn that
  completes after a timeout-nack can't also ack (single terminal signal).
- targetFromRef: throw a clear, actionable error when a ref carries no delivery
  routing instead of coercing deliveryId to the string "undefined".
- getOrCreate: guard getHistory with try/catch so a contract-violating source
  can't nack an otherwise-fine turn; document the app-api turnId-distinctness
  invariant the in-place card update depends on.
- getHistory: clamp to the requested limit (parity with the in-memory source);
  note the Slack-shaped route cast and the thread_started wire asymmetry.
- fetchFile: bound the body read with a generous content-length backstop.
- state store: forward an explicit ttlMs:0 (was dropped by truthiness) and add
  the missing TSDoc docblocks.
- supportsBlockingChoice: future-tense the TSDoc (no reader exists yet).
- tests: interaction update-in-place stamping (the HITL crux), content-parts
  text/unknown-mime branches, default-store resolution, kv non-2xx + ttlMs:0.
2026-07-08 14:27:15 -05:00
Jordan Ritter fe2b124903 fix(showcase): watchdog follow-up hardening — _require_int overflow clamp, size-loop trap-order + grace coverage, read-builtin cleanup (#5879)
## Summary

Follow-up cleanup/hardening for the two showcase entrypoint watchdogs
fixed in #5874. A 5-round code review deferred a set of non-load-bearing
bucket-(b) polish items to this PR; they are implemented here. Scope is
limited to:

- `showcase/integrations/langgraph-typescript/entrypoint.sh`
- `showcase/integrations/strands-typescript/entrypoint.sh`

Shared helper code (`_require_int`, `_agent_descendants`) is kept
byte-identical across both files. `bash -n` and `shellcheck
--severity=warning` are clean on both.

## Items implemented

### 1. `_require_int` upper bound (behavioral, both files)
The validator accepted any positive integer (`[1-9][0-9]*`), so a 20+
digit override overflowed bash's signed-64-bit arithmetic and either
wrapped to garbage or aborted the `[ -ge ]` test — which, suppressed to
false inside the guard's `if`, **silently disabled the guard** (the
exact fail-open class the validator exists to prevent). Added a 10-digit
length cap (max 9,999,999,999 — far above any real
interval/threshold/strike knob, comfortably inside int64), checked
before the digit `case` since an all-digit 23-char value would otherwise
pass. A too-long value now takes the same WARN + fall-back-to-default
fail-safe path as every other bad override.

**Cap rationale:** a single generous 10-digit cap rather than per-knob
caps — 9,999,999,999 seconds is ~317 years and 9,999,999,999 MB is ~9.3
PB, so no legitimate value is ever excluded, and it leaves 9 digits of
headroom below the 19-digit int64 ceiling.

### 2. `cleanup()` comment fix (langgraph, non-behavioral)
Corrected the note claiming `WATCHDOG_PID` "forks nothing that outlives
it" — it **does** fork the size sub-loop; the bare `kill $WATCHDOG_PID`
is safe because the watchdog's own inner EXIT trap reaps that child, not
because it forks nothing. The strands `cleanup()` comment was verified
accurate (strands has no size sub-loop) and left unchanged.

### 3. SIZE_PID trap-ordering leak window (langgraph, behavioral)
The size sub-loop was backgrounded (`( … ) &`, `SIZE_PID=$!`) **before**
its reaping `trap … EXIT` was registered, so an outer SIGTERM landing in
that window exited the watchdog subshell with no trap armed and orphaned
the sub-loop (reparented to PID 1, spinning for the container's life).
Now the reaping trap is armed **first** and reaps via a `$BASHPID`
PPID-walk that finds the child regardless of whether `SIZE_PID` is
assigned yet — no ordering-dependent leak.

### 4. Size-guard coverage during startup grace (langgraph, behavioral)
The size monitor started only **after** the up-to-180s startup-grace
loop, leaving the size ceiling unguarded during a pathological cold
boot. It now starts **before** the grace loop. Decision: starting early
is safe because `_watchdog_check_size_once` already fail-closes on every
not-yet-ready condition (agent PID not alive, `PERSIST_DIR`
missing/freshly-purged, non-numeric size or threshold), so early cycles
are harmless no-ops until the dir actually grows. No documented-gap
fallback was needed.

### 5. Cosmetic (both files, non-behavioral)
- **Done:** `_agent_descendants` per-PID `echo | awk` fork replaced with
the `read` builtin (clean drop-in; avoids forking awk once per `/proc`
entry). Byte-identical across both files.
- **Skipped — strike-window "off-by-one" log:** examined the
health-strike log `~$((HEALTH_CHECK_INTERVAL * HEALTH_STRIKE_LIMIT))s`;
with interval=30, limit=3 the third failure lands at ~90s and the
product is 90 — there is no actual off-by-one to fix, so touching it
would add churn without value.
- **Skipped — unthrottled transient-error re-warn:** throttling the
per-cycle transient `[watchdog:size]` warning requires adding per-loop
state/timestamp bookkeeping; low value and would balloon the diff into
the hot loop, so skipped per the "skip if not clean/low-risk"
instruction.

## RED → GREEN (behavioral items 1, 3, 4)

Proven on the **real committed entrypoint bytes** in `node:22-slim`
(Debian bash 5.x, real `/proc`), mirroring how #5874's fixes were
proven. RED = the merged #5874 baseline (`7912b6e51`); GREEN = this
branch's HEAD.

### Item 1 — overflow clamp (both files)
```
RED  langgraph: input HEALTH_CHECK_INTERVAL='99999999999999999999999' (23 digits)
     after _require_int, HEALTH_CHECK_INTERVAL='99999999999999999999999'
     $(( HEALTH_CHECK_INTERVAL * 3 )) = 601129261562068989
     RESULT: 23-digit value SURVIVED validation <-- BUG: no upper bound
RED  strands:  (identical to langgraph — byte-identical helper)

GREEN langgraph: [entrypoint] WARNING: health interval (HEALTH_CHECK_INTERVAL) is too large (got: '99999999999999999999999', 23 digits — max 10) — falling back to default 30
     after _require_int, HEALTH_CHECK_INTERVAL='30'
     $(( HEALTH_CHECK_INTERVAL * 3 )) = 90
     RESULT: value was clamped to '30' (default) — guard safe <-- FIXED
GREEN strands:  (identical — clamped to 30, arithmetic = 90)
```

### Item 3 — trap-ordering leak window (langgraph)
```
RED  post-fix ordering emulated with the pre-fix spawn-then-trap sequence:
     size sub-loop ticks AFTER watchdog exit = 2
     RESULT: sub-loop ORPHANED — still ticking after watchdog gone <-- BUG (leak window)

GREEN size sub-loop ticks AFTER watchdog exit = 0
     RESULT: sub-loop REAPED on watchdog exit — no orphan <-- FIXED
```

### Item 4 — size-guard coverage during startup grace (langgraph)
```
RED  grace-loop announce at line 437; size-monitor spawn at line 459
     RESULT: size monitor starts AFTER grace loop — UNGUARDED during up-to-180s grace <-- BUG

GREEN grace-loop announce at line 533; size-monitor spawn at line 507
     RESULT: size monitor starts BEFORE grace loop — guarded during cold start <-- FIXED
```
Functional confirmation (real watchdog block, short grace, stubbed
size-check + never-healthy probe):
```
[watchdog] Startup grace: waiting up to 5s for first successful health probe before arming strike counter
[watchdog:size] Starting size-gated restart monitor (threshold=200MB, interval=1s, dir=/tmp/persistX)
[GRACE-WINDOW-SIZE-CHECK-RAN]
[GRACE-WINDOW-SIZE-CHECK-RAN]
--- 3s elapsed (still within 5s grace) ---
```
`--check-size-once` seam re-verified post-refactor: under threshold →
exit 0 (no kill); over threshold → tree-kill + exit 1.

## Lint
`bash -n` and `shellcheck --severity=warning` clean on both entrypoints.

## Deferred (bucket (d)) — known future work, NOT in this PR
- A dedicated Next.js frontend watchdog (health probe + tree-kill for
the `NEXTJS_PID` process-sub subshell, symmetric to the agent watchdog).
- A strands size-guard / boot-purge (strands currently has only the
health watchdog; no persistence-size ceiling or boot purge).

These are separate features, out of scope for this cleanup PR.
2026-07-08 12:13:19 -07:00
Benjamin Taylor 0e6a01ae4b fix(bot-intelligence): dead-letter unmappable deliveries instead of wedging the loop
The exhaustiveness guard added to mapDeliveryToEnvelope threw on an unknown
wire kind, but claimOnce records the lease *before* mapping and runLoop's outer
catch only logs+sleeps — so an unmappable delivery leaked its lease and, after
the 120s lease expiry, was redelivered and re-thrown forever, permanently
wedging the single-delivery loop. Catch the map failure in claimOnce and nack
it non-retryably (deterministic failure → let app-api dead-letter it) so the
guard fails loud without blocking the queue. Adds a retryable flag to nack and
a regression test.
2026-07-08 14:02:58 -05:00
Jordan Ritter 4b670f4e2e docs(showcase): correct misleading watchdog/PID comments in entrypoints
The launch-site comment claimed process substitution leaves $! pointing at
the real node process. That is false and contradicts the file header: $!/
AGENT_PID is the wrapping subshell, and the real npm->node server is a
descendant reached only via the tree-kill (the reason _kill_agent_tree exists).
Rewritten in both langgraph-typescript and strands-typescript entrypoints so no
maintainer reintroduces a bare kill.

Also in langgraph-typescript: clarify that the SIZE_PID kill is a retained
belt-and-suspenders backstop to the $BASHPID PPID-walk (not dead code), and
re-anchor the startup-grace rationale to the real cause (the top-level
@langchain/langgraph-api import cost), since the prod path no longer uses
langgraph-cli dev.

Comment-only; no executable code changed.
2026-07-08 12:02:05 -07:00
github-actions[bot] 6f2b66cadc style: auto-fix formatting 2026-07-08 19:00:38 +00:00
Benjamin Taylor 41aed530c3 docs(channels): fix stale bot links in package docs + Channels landing page
Addresses review (tylerslaton):
- Package README/ARCHITECTURE relative links ../bot* -> ../channels* across
  discord/slack/teams/telegram/whatsapp (404'd after the dir rename)
- Channels landing page: Card hrefs /bots/{persistence,transcripts} ->
  /channels/*, and 'Bot reference' -> 'Channels reference'
- Package-noun prose (bot engine -> channel engine, bot-ui -> channels-ui,
  bot-slack approach -> channels-slack approach)

Left unchanged: runtime/third-party 'bot' prose, /api/bots/* Intelligence
wire paths, next.config /bots redirect sources.
2026-07-08 13:59:50 -05:00
Jordan Ritter 5a16b59f1e fix(showcase): guard sleep in _kill_agent_tree re-scan loop
The bounded re-scan loop's sleep 0.2 was unguarded. Under set -e on a base image
whose sleep can return non-zero (e.g. a future busybox/Alpine rebase), a failed
sleep would abort the tree-kill mid-walk — root never killed, real npm→node
server left orphaned. Add || true so the walk completes regardless of sleep's
exit status. No behavior change on the current Debian base (coreutils sleep
succeeds). Helper kept byte-identical across both entrypoints.
2026-07-08 11:56:01 -07:00
Jordan Ritter abaf72185a fix(showcase): key exit-code diagnostic off the actual reaped PID
Both langgraph-typescript and strands-typescript entrypoints inferred which
process exited via a post-hoc kill -0 if/elif after wait -n. That inference is
racy: on a near-simultaneous exit both PIDs are dead by probe time, so the first
kill -0 branch always wins and mislabels the diagnostic (naming the agent when
Next.js actually exited, attaching the wrong code to the wrong name). Use
bash's wait -n -p REAPED_PID (bash >= 5.1; node:22-slim ships 5.2) to capture
the actual reaped PID and key the message off it. Exit code (incl. 137) and the
final exit $EXIT_CODE are preserved; || EXIT_CODE=$? guard is unchanged.
2026-07-08 11:55:50 -07:00
Jordan Ritter 8d676704c7 fix(showcase/langgraph-typescript): close size-loop trap-order leak window and guard size ceiling during startup grace
Three related size-watchdog hardening changes in the langgraph entrypoint:

- Trap-order leak window: the size sub-loop was backgrounded (`( … ) &`,
  SIZE_PID=$!) BEFORE its reaping `trap … EXIT` was registered, so an outer
  SIGTERM landing in that window exited the watchdog subshell with no trap
  armed and orphaned the sub-loop (reparented to PID 1, spinning for the
  container's life). Arm the reaping trap FIRST, and reap via a $BASHPID
  PPID-walk that finds the child regardless of whether SIZE_PID is assigned
  yet — no ordering-dependent leak.

- Startup-grace size coverage: the size monitor started only AFTER the
  up-to-180s startup-grace loop, leaving the size ceiling unguarded during a
  pathological cold boot. Start it BEFORE the grace loop; safe because
  _watchdog_check_size_once already fail-closes on every not-yet-ready
  condition (agent PID not alive, PERSIST_DIR missing, non-numeric size/
  threshold), so early cycles are harmless no-ops until the dir grows.

- cleanup() comment accuracy: corrected the note claiming WATCHDOG_PID
  "forks nothing that outlives it" — it DOES fork the size sub-loop; the
  bare `kill $WATCHDOG_PID` is safe because the watchdog's own inner EXIT
  trap reaps that child, not because it forks nothing.

Proven RED->GREEN on the real entrypoint in node:22-slim: pre-fix the size
sub-loop keeps ticking after the watchdog exits (orphan) and the size monitor
spawns after the grace loop (unguarded); post-fix the sub-loop is reaped (0
ticks) and the monitor runs during grace (size-check fires within the grace
window). --check-size-once seam re-verified under/over threshold.
2026-07-08 11:36:23 -07:00
Jordan Ritter 5c71c1d047 fix(showcase): clamp _require_int to a 10-digit upper bound to stop int64 overflow disabling a guard
The numeric-config validator accepted any positive integer (`[1-9][0-9]*`),
so a 20+ digit override overflowed bash's signed-64-bit arithmetic and either
wrapped to a negative/garbage magnitude or aborted the `[ -ge ]` test with
"value too great for base" — which, suppressed to false inside the guard's
`if`, silently disabled the guard for the container's lifetime (the exact
fail-open class this validator exists to prevent).

Add a 10-digit length cap (max 9,999,999,999 — comfortably inside int64,
far above any real interval/threshold/strike knob) checked BEFORE the digit
`case`, since an all-digit 23-char value would otherwise pass validation.
A too-long value now takes the same WARN + fall-back-to-default fail-safe
path as every other bad override. Byte-identical across both entrypoints.

Proven RED->GREEN on the real entrypoints in node:22-slim: pre-fix a 23-digit
value survives validation and `$(( x * 3 ))` yields int64-wrapped garbage;
post-fix it WARNs, clamps to the default, and arithmetic is correct.
2026-07-08 11:35:59 -07:00
Jordan Ritter 966915b847 chore(showcase): replace per-PID echo|awk fork with read builtin in _agent_descendants
The /proc PPID walk forked an awk process for every entry in the process
table on every scan pass. Replace the `echo "${stat##*) }" | awk '{print $2}'`
pipeline with the `read` builtin, which word-splits the post-comm remainder
("STATE PPID PGRP …") on IFS and captures the 2nd field with no subprocess.
Byte-identical across the langgraph-typescript and strands-typescript
entrypoints. Non-behavioral; bash -n + shellcheck --severity=warning clean.
2026-07-08 11:35:31 -07:00
Benjamin Taylor 8fa44c44e7 fix(bot-intelligence): fail loud on unhandled managed delivery kinds
An unknown delivery kind previously fell through the dispatch switch as a
silent no-op (dispatch acks on resolve, so it would ack an unhandled delivery
as processed) and mapDeliveryToEnvelope coerced it into an empty turn. Add
matching `never` exhaustiveness guards to both so a future wire kind throws
instead of being silently swallowed.
2026-07-08 13:28:54 -05:00
github-actions[bot] 1cf6eefba9 style: auto-fix formatting 2026-07-08 13:27:35 -05:00
Benjamin Taylor b394f06fdc refactor(channels): rename @copilotkit/bot* packages to @copilotkit/channels* (OSS-438)
Renames the Bots SDK to the Channels SDK. Names only — no behavior change.

- 8 packages @copilotkit/bot* -> @copilotkit/channels* (git mv dirs, names,
  workspace: cross-deps). Now includes @copilotkit/bot-intelligence ->
  @copilotkit/channels-intelligence (landed on main via #5761; unpublished, so
  renamed fresh with the family).
- release.config.json scope keys + versionSource; ReleaseScope union;
  canary/stable-release/publish-release scope dropdowns; verify script
- examples/slack (Kite) + examples/teams: deps, jsxImportSource, imports
- showcase/shell-docs: content dirs docs/bots->docs/channels and
  reference/bot->reference/channels, nav registry, redirects

createBot and other API names unchanged. Old @copilotkit/bot* to be deprecated
after the new packages publish (bot-intelligence was never published).

Re-derived onto latest main (was conflicting after #5761 landed).

Refs OSS-438
2026-07-08 13:27:35 -05:00
Jordan Ritter 7912b6e512 fix(showcase/langgraph-typescript): tree-kill agent so size-watchdog restart actually fires (#5874)
## What & why

Two showcase agent containers (**langgraph-typescript**,
**strands-typescript**) could enter a *running-but-dead* state: Railway
showed the service `● Online` while `/api/health` returned **HTTP 502**.
This took down all 36 LGT dashboard cells on staging **and** prod
(prod's `.langgraph_api` had crossed the 200 MB size-watchdog
threshold).

**Root cause:** the agent is launched via process substitution (`... &>
>(awk …) &`), so `$AGENT_PID` (`=$!`) is the **wrapper subshell**, not
the real `npm`→`node` server that holds the port. Every watchdog/cleanup
did a bare `kill -9 $AGENT_PID`, which reaped only the subshell and
**orphaned the real server** (reparented to PID 1, still bound to the
port). The watchdog's "kill agent → container restart → boot-purge"
contract therefore never fired: the frontend kept proxying to a dead
agent → 502 forever.

## Fixes (each with local red-green on the real entrypoint in
`node:22-slim`)

1. **cleanup() EXIT trap** → routes through `_kill_agent_tree` (was
orphaning the agent on every SIGTERM/redeploy).
2. **`_kill_agent_tree`** → `/proc`-based tree-kill with a bounded
re-scan (root killed last) so mid-walk forks can't escape; refuses PID ≤
1 (fail-closed).
3. **size-watchdog** hardened against non-numeric `du` and transient
errors (no silent gate-disable, no permanent loop death).
4. **strands health-watchdog** → 180 s startup-grace window (parity with
langgraph); the now-effective kill would otherwise loop a slow cold
start.
5. **`wait -n` under `set -e`** → capture exit code so the restart
diagnostic isn't dead code on the primary (137) path.
6. **structural:** one `_require_int` validator over *every*
operator-overridable numeric knob (fail-safe to default), and **every**
wrapped-PID kill (incl. `NEXTJS_PID`) routed through the guarded
tree-kill; dangerous `${AGENT_PID:-0}` sentinel removed.
7. **`_require_int`** requires a positive integer (rejects `0` and
leading-zero/octal).

## Incident status
Staging **and** prod LGT were restored immediately via redeploy
(boot-purge cleared the oversized state) — both `/api/health` → 200.
This PR stops the recurrence.

## Review
Converged through a 5-round unbiased review-fix loop (1 + 4
confirmation), zero mandatory findings at close, all load-bearing guards
independently re-verified. `bash -n` + shellcheck (`-S warning`) clean;
170/170 shell bats pass.

## Follow-up (tracked, separate PR — non-load-bearing)
`_require_int` upper-bound clamp (LOW arith-overflow, needs a 20+-digit
value); a stale `cleanup()` comment; size-guard unarmed during the
startup-grace window; SIZE_PID trap-registration micro-window;
diagnostic label on near-simultaneous exit; cosmetic log nits; startup
readiness `sleep 3`+`kill -0` probes the wrapper subshell; no dedicated
Next.js frontend watchdog.
2026-07-08 11:27:19 -07:00
Benjamin Taylor 24bb847760 fix(bot-intelligence): reconcile merge with main's #5761 changes
- Drop the resurrected inline buildContentParts method from the adapter; the
  turn path now uses the extracted ./content-parts.js helper (#5814's refactor).
- Port main's fail-visible file-fetch behavior into content-parts.ts so a file
  that can't be retrieved becomes a short note in both the live-turn and
  history-seeding paths (they share the helper).
- Update the state-store conformance import to the @copilotkit/bot/testing
  subpath (main moved it there to keep vitest out of consumers' runtime graph).
2026-07-08 13:05:48 -05:00
Benjamin Taylor 69d11a5391 Merge origin/main into codex/managed-slack-hitl
Unstale #5814 onto main after the #5761 stack landed. Conflict:
- intelligence-adapter.ts: kept #5814's interaction dispatch + supportsBlockingChoice
  alongside main's #5761 review fixes (seq.delete cleanup, file fetch-failure note).
2026-07-08 12:57:26 -05:00
Ben Taylor 9fa925bd95 feat(bot,runtime): managed bots SDK — run the bot SDK from Intelligence-delivered events (OSS-360/361) (#5761)
## Summary

Lets the `@copilotkit/bot` SDK run from **Intelligence-delivered
events** without a second programming model, and adds the runtime `bots`
declaration API. A managed event (delivered by Intelligence) runs the
*same* customer handlers, tools, context, commands, Bot UI, and agents
as local/custom adapters — the managed path is "just another
`PlatformAdapter`," fed by injected transports.

This is the **OSS / SDK slice** of the Hosted Managed Bots work. The
credentialed transports (Realtime Gateway, Connector Outbox) and the
frozen shared contracts live elsewhere (see *Out of scope*); this PR
ships the seams they plug into, fully runnable headless.

Relates to **OSS-360** (runtime bots API), **OSS-361** (run the SDK from
Intelligence events), **OSS-363** (Slack render/codec reuse).

## What's in here

- **`intelligenceAdapter()` bridge** (`@internal`, not publicly
documented) — implements `PlatformAdapter` over two injected transports:
`DeliverySource` (inbound) + `EgressSink` (outbound). Ingress →
`onTurn`/`onCommand`/`onInteraction`/`onThreadStarted`/`onReaction`; ack
on success / nack on throw (at-least-once). Egress emits generic
operations carrying `BotNode[]` IR with **deterministic ids**
(`turnId:seq`, reset per turn) so a redelivered turn reproduces the same
ids for the Connector Outbox to dedupe. Idempotency lives at egress, so
the managed path skips ingress dedup (`skipIngressDedup`) — a redelivery
re-runs rather than being dropped.
- **Runtime `bots` API** — `new CopilotRuntime({ intelligence, bots })`,
accepted by TypeScript **only when `intelligence` is configured**
(discriminated union). `createBot({ name })`; `startManagedBots()`
validates names (required, identifier-style, unique — fail-loud), builds
activation metadata, and wires each bot to its resolved transport.
- **`PlatformCodec` seam** + Slack egress codec (`slackCodec`) composing
the existing pure `renderSlackMessage`, so IR→native rendering is shared
(no Bolt/creds) instead of duplicated.
- **Backwards-compatible SDK foundations**: `bot.addAdapter()` +
optional `adapters`, deferred backend resolution at `start()` with
`stateStore`-provider precedence (+ multi-provider warning),
`bot.transcripts` throws pre-start, optional
`eventId`/`turnId`/`deliveryId` on ingress + handler context. Existing
`createBot` callers and every `PlatformAdapter` implementer are
unaffected.
- **In-memory transports + fixture tests** — the full dispatch path
(envelope in → handler runs → egress op out) runs with zero
Slack/Intelligence/network.

## Out of scope (external / separate tickets)

- **Realtime Gateway + Connector Outbox transports** — implemented in
the closed-source repo against the `DeliverySource`/`EgressSink`
interfaces shipped here.
- **Shared contracts freeze (OSS-377)** — consumed here via a minimal,
isolated placeholder (`managed/contracts.ts`, marked `TODO(OSS-377)`);
swaps in via one import change.
- **OSS-363 ingress normalization** — the egress codec is done;
extracting the pure Slack event→neutral mapping out of the Bolt listener
(so local + Intelligence ingress share it) is the remaining, higher-risk
half and is left to that ticket (`TODO(OSS-363)`).

## Testing

TDD throughout (RED→GREEN per behavior). New: managed adapter
dispatch/ack-nack/ids/run-renderer/exclusivity, all-kinds routing, name
validation + metadata + lifecycle, runtime `bots` option, Slack codec.
Full suites green: `bot` 147, `bot-slack` 256, `runtime` 1574. All
builds typecheck (`bot`/`bot-slack`/`bot-discord`/`runtime`);
oxlint/oxfmt clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 12:49:17 -05:00
Mike Ryan 0f8c5385da chore(examples): Update CPK version and bound drawer grid row so threads list scrolls internally [ENT-1051] (post-release follow-up) (#5828)
## What

Applies the threads-drawer grid fix to all 15 integration examples: adds
`grid-template-rows: minmax(0, 1fr)` to each example's `.layout` so the
drawer's threads list scrolls **internally** (pinned header + New
Conversation) instead of the whole page growing to content height.

Without this, `height: 100dvh` on a grid whose rows aren't bounded lets
the row size to content, so the list can't scroll within the drawer and
the delete-confirm dialog centers against a content-tall root.

## Why this is a separate PR

These are **example** changes that consume **published** `@copilotkit/*`
packages. They were pulled out of the drawer-redesign PR (#5823) so that
PR stays scoped to the packages. This one lands **after** the redesigned
drawer is released.

## ⛔ Blocked / TODO before marking ready

- [ ] Drawer redesign PR #5823 merged
- [ ] Lockstep release cut (web-components + react-core + vue + angular)
- [ ] Bump each example's `@copilotkit/*` dependency to the new
published versions **in this PR** (currently only the CSS is here)
- [ ] Re-verify one example end-to-end against the released packages

## Testing

- Grid fix verified live during the redesign work (langgraph-js against
a hosted Intelligence backend): threads list scrolls internally, header
+ New Conversation pinned, delete-confirm renders correctly.
- Example dependency bumps + a fresh end-to-end pass will be
added/redone here once the release is published.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 10:29:20 -07:00
Benjamin Taylor 654389aa9e chore(examples): bump @copilotkit deps to 1.62.3 [ENT-1051]
The 1.62.3 release publishes the CopilotThreadsDrawer redesign (web-components +
react-core wrapper) and the stateless /suggest feature. Bump the 15 integration
examples that consume the drawer from 1.62.2 -> 1.62.3 (package.json + lockfiles)
so they pick up the released packages alongside this branch's example CSS.

Validated: langgraph-js runs on the published 1.62.3 (no local links) — the
redesigned drawer renders (New Conversation, Recent Conversations, filter funnel,
desktop collapse toggle, per-row kebab), threads are licensed, and a real agent
message round-trips.
2026-07-08 12:23:35 -05:00
Benjamin Taylor 5e0727359f fix(examples): unify header controls + even toggle padding [ENT-1051]
- ModeToggle: one style on both breakpoints (top-4/right-4 = 16px gutter,
  46px min-height, 4px corners); symmetric p-1.5 + fixed 20px button leading
  so the selected pill has an even gap on all four sides (was tight L/R vs T/B).
- Launcher: uniform 16px gutter (top + left) on both breakpoints so it mirrors
  the toggle; drop the mobile-only 7px override.
- Logo: centered on the launcher/toggle middle line (pt-[23px]); wordmark
  padding normalized so its height matches on both breakpoints.
- Inspector FAB: sits beneath the toggle, gap = the 16px top gutter (one rule,
  no media query, since the toggle is identical across breakpoints).

Net: launcher, logo, toggle share center-y; launcher + toggle are both 46px;
the FAB tucks under the toggle with a matching gap; the selected toggle pill is
evenly inset.
2026-07-08 12:23:35 -05:00
Benjamin Taylor 14fa89a674 fix(examples): align header controls — toggle clears inspector, launcher left matches right gutter, logo/toggle on one line [ENT-1051]
- ModeToggle: move left (right-[72px]) so the top-right inspector FAB no longer
  covers the App segment; grow to 46px (lg:min-h) + center on the logo line
  (top-6) to match the launcher; keep the 4px corners.
- Launcher: left gutter -> 16px to match the right-side controls' inset.
- Logo: pt-7 so it centers on the same line as the launcher + toggle.
2026-07-08 12:23:35 -05:00
Benjamin Taylor b69ae43989 fix(examples): mobile header boundary + square the Chat/App toggle to match the drawer [ENT-1051]
- Mobile header: max-lg:pb-0 -> pb-4 so chat content clears the fixed launcher/
  toggle strip instead of butting right under it (no boundary).
- Chat/App ModeToggle: rounded-full -> rounded-[4px] container + rounded-[2px]
  buttons, matching the drawer's 4px radius cap so the header controls are
  visually consistent.
2026-07-08 12:23:35 -05:00
Benjamin Taylor dcec3cb8c4 fix(examples): scope the drawer launcher position override to mobile [ENT-1051]
The 7px/16px launcher inset was tuned for the mobile off-canvas launcher; on
desktop it leaked onto the collapsed cluster. Move it into the mobile media
query so desktop-collapse uses the element's own 24px gutter default.
2026-07-08 12:23:35 -05:00
Benjamin Taylor 115439afca fix(examples): add a gap between the collapsed cluster and the app logo (6rem->7rem) [ENT-1051] 2026-07-08 12:23:35 -05:00
Benjamin Taylor 43e9793cf2 fix(examples): clear the drawer's collapsed cluster from the app header on desktop [ENT-1051]
The floating launcher/collapsed cluster is fixed at the top-left corner. Below
1024px it always shows (already cleared via max-lg:pl-24); on desktop it appears
only when the drawer is COLLAPSED. Drive the header's left padding off
--cpk-drawer-reserved-width (0px when collapsed, 320px default otherwise) so the
logo starts at ~6rem when collapsed and pl-6 when expanded — no overlap. No-op
on current packages (var never set → stays pl-6).
2026-07-08 12:23:34 -05:00
Benjamin Taylor 8344a90475 feat(examples): reclaim the drawer column on desktop-collapse via --cpk-drawer-reserved-width [ENT-1051]
Read grid-template-columns' first track from var(--cpk-drawer-reserved-width, 320px)
so when the drawer collapses on desktop (it sets the var to 0) the reserved
column collapses and the chat reclaims the space — instead of leaving an empty
placeholder column. Mobile (single-column) is unchanged.
2026-07-08 12:23:34 -05:00