Commit Graph

15656 Commits

Author SHA1 Message Date
Alem Tuzlak ee37df5111 fix(release): write posix paths in the public API manifest
Windows path.relative() used backslashes in provenance paths, so Linux CI failed the committed-manifest check. Normalize to forward slashes and add a regression test.
2026-08-31 14:28:19 +02:00
Alem Tuzlak 2feda707e9 chore(release): add markdown-renderer to the public package list
Enroll @copilotkit/markdown-renderer in the monorepo release scope and refresh the public API manifest. This unblocks the unit CI release-script check.
2026-08-31 14:18:14 +02:00
Alem Tuzlak 1912bbf147 fix(markdown-renderer): sanitize link clicks and align Vue keys
Pass sanitized hrefs to onLinkClick. Use camelCase Vue nodeRenderers keys. Drop CopilotKit Tailwind classes from the Vue package renderer.
2026-08-31 14:18:02 +02:00
github-actions[bot] 561f4c618b style: auto-fix formatting 2026-08-31 12:05:54 +00:00
Alem Tuzlak c1a948c784 fix(react-core): restore the error-toast markdown import path
The v1 error toast imported BasicMarkdownRenderer one folder too short,
so react-core failed to build and every downstream CI job went red.

Also: aria-pressed on the react-router markdown mode buttons, README
entry points, and docs snippets that CodeRabbit flagged as copy-paste
errors.
2026-08-31 14:04:06 +02:00
Alem Tuzlak 4c9baaa2cb docs(markdown): add an agent prompt that migrates to the new renderer
The old prompt only restored Streamdown. The new prompt defaults to the
built-in streaming renderer, plugs Streamdown in through markdownRenderer
only when the app still needs highlighting, math, or diagrams, and
deletes the removed Vue useKatexStyles export.
2026-08-31 13:50:16 +02:00
Alem Tuzlak 366a1c1610 fix(react-core): keep main IntelligenceIndicator CSS after merge
The merge took the whole pre-merge globals.css to drop streamdown
styles, which also overwrote main's newer indicator classes.
Restore main's CSS and only delete the streamdown rules.
2026-08-31 13:20:41 +02:00
Alem Tuzlak 0980a73b63 Merge origin/main into feat/pluggable-markdown-renderer
Keep the pluggable markdown renderer (drop bundled streamdown/katex).
Take main's inspector context, threads-drawer rename, and showcase moves.

--no-verify: this worktree has no node_modules, so lefthook cannot run.
2026-08-31 13:18:28 +02:00
renovate[bot] 37135f0646 chore(deps): update zizmorcore/zizmor-action action to v0.6.3 (#6797)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[zizmorcore/zizmor-action](https://redirect.github.com/zizmorcore/zizmor-action)
| action | patch | `v0.6.2` → `v0.6.3` |

---

### Release Notes

<details>
<summary>zizmorcore/zizmor-action (zizmorcore/zizmor-action)</summary>

###
[`v0.6.3`](https://redirect.github.com/zizmorcore/zizmor-action/compare/v0.6.2...v0.6.3)

[Compare
Source](https://redirect.github.com/zizmorcore/zizmor-action/compare/v0.6.2...v0.6.3)

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/Los_Angeles)

- Branch creation
  - "before 9am every weekday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/CopilotKit/CopilotKit).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40OS4wIiwidXBkYXRlZEluVmVyIjoiNDQuNDkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-08-31 10:29:58 +00:00
renovate[bot] 15f09b224e chore(deps): update zizmorcore/zizmor-action action to v0.6.3 2026-08-31 07:38:15 +00:00
Ran Shemtov 119cc4eb66 feat(showcase): MAF Python A2UI error-recovery demo on agent-framework 1.2.0 (#6738)
## What

Brings the just-released A2UI support and the latest Microsoft Agent
Framework
(Python) into the showcase, and moves the MAF A2UI demos onto the native
subagent / auto-inject technique so they match the langgraph-python
reference
instead of the pre-1.2.0 hand-rolled path.

- **Bump MAF to 1.2.0.** `agent-framework-ag-ui[a2ui]==1.2.0`,
  `agent-framework-openai==1.14.0`, `agent-framework-core==1.15.0`,
`ag-ui-a2ui-toolkit==0.0.4`. 1.2.0 is A2UI's first release; the `[a2ui]`
extra
  pulls the toolkit.

- **New `a2ui-recovery` demo** (the one A2UI demo the integration lacked
vs
langgraph-python). Backend-owned via the adapter's native `enable_a2ui`
(`injectA2UITool: false`), which runs the shared toolkit's
validate/retry
recovery loop in-process. Heal pill recovers a malformed first render;
exhaust
pill hits the attempt cap and surfaces the `a2ui_recovery_exhausted`
fallback.
Adds the agent, route, page, chat, suggestions, a D6 fixture, and an e2e
spec.

- **Migrate `declarative-gen-ui` to native A2UI auto-injection.**
Removed the
  hand-rolled `generate_a2ui` (a raw secondary OpenAI call to
`_design_a2ui_surface`); the agent now binds no A2UI tool and the route
sets
`injectA2UITool: true`, so the adapter's `plan_a2ui_injection`
auto-injects the
native `generate_a2ui` sub-agent. Reworked the D6 fixture to the native
  `render_a2ui` shape.

- **Migrate `beautiful-chat` to native A2UI auto-injection.** Same
change for the
flagship composite: removed its hand-rolled `generate_a2ui`, flipped the
route
to `injectA2UITool: true`. The adapter now auto-injects the native
sub-agent
  alongside the agent's own tools (todos, query, flights).

- **`a2ui-fixed-schema` unchanged** — it is the fixed-schema pattern
(client
  authored schema, agent streams data via a backend `display` tool,
  `injectA2UITool: false`), which langgraph-python does identically. Not
  hand-rolled generation.

- **Docs.** Enriched the MAF A2UI docs page into real "how to A2UI"
content
(dynamic / fixed / recovery) and connected the A2UI demos via
`docs-links`.

## Why native (not the dojo's example agents)

The showcase mirrors langgraph-python's frontend-catalog + auto-inject
pattern,
not the AG-UI dojo's `a2ui_config` example agents. Before 1.2.0 the MAF
adapter
had no native A2UI, so the showcase hand-rolled `generate_a2ui`. 1.2.0
ships the
native path, so these demos now use it and match langgraph 1:1.

## Validation

- `validate-pins` clean (FAIL count + hash unchanged), `validate-parity`
PASS,
  `generate-registry` clean.
- **D6 (full frontend, aimock replay), all green:**
- `a2ui-recovery` — heal paints the recovered surface; exhaust shows the
    hard-failure UI.
- `declarative-gen-ui` — all 4 dashboard pills paint on the auto-inject
path
    (confirms the runtime forwards `injectA2UITool: true` and
    `plan_a2ui_injection` fires).
  - `beautiful-chat` — regression across all 5 features (pie/bar chart,
schedule-meeting, search-flights, toggle-theme): wrapping the multi-tool
    agent in the A2UI planner loop does not break its non-A2UI tools.
- **AG-UI protocol layer** (published 1.2.0 wheel + aimock): recovery
  (heal/exhaust), declarative, and beautiful-chat all stream real
`a2ui_operations` / `a2ui_recovery_exhausted`, RUN_FINISHED, no
RUN_ERROR.

- **Remove the last hand-rolled A2UI (default agent).** The
general-purpose
default agent (`agent.py`, catch-all `/` endpoint) also carried a
hand-rolled
`generate_a2ui`; removed it (the default agent no longer offers A2UI,
matching
langgraph's default agent). Stripped the stale `_design_a2ui_surface`
fixture
residue and refreshed the e2e-spec comments that described the old
mechanism.

After this PR the MAF-python integration has **zero hand-rolled A2UI
anywhere
except the fixed-schema demo** (which is the intended fixed-schema
pattern,
identical to langgraph). The shared `tools/generate_a2ui.py` module is
intentionally left intact — it is symlinked by other integrations (ag2,
agno, …)
that have not migrated; MAF-python simply no longer imports it.

## Pre-existing, out of scope

Full D6 for ms-agent-python is 37/40. The 3 red cells — `multimodal`,
`voice`,
`hitl-approve-deny` — are **not** touched by this PR: their agents are
byte-identical to main and never used `generate_a2ui`. `multimodal` is
the known
shared-CopilotKit frontend bug (`runStartCount=0`, the run never
starts);
`voice`/`hitl-approve-deny` complete the run but their text does not
settle inside
the probe's tight budget. All A2UI and default-agent cells pass.
2026-08-31 09:37:00 +02:00
Ben Taylor 77c8ac1768 docs: add governed action approval UI pattern (#6277)
## Summary
- Adds a Human-in-the-Loop guide for governed side-effect actions
- Shows a vendor-neutral action envelope with summary, tool, reference,
verdict, and arguments
- Covers allow, deny, and require_approval handling with useInterrupt
and useHumanInTheLoop examples

## Validation
- Parsed human-in-the-loop meta.json and verified governed-actions is
present
- Checked the new MDX frontmatter and required approval terms/hooks
- Confirmed the new guide contains no OSuite/osuite branding
2026-08-30 23:54:05 -05:00
Ben Taylor 1a4e2c9a51 fix(examples): store Teams app icons in Git LFS (#6456)
## Summary

- normalize `examples/teams/appPackage/color.png` and `outline.png` into
Git LFS pointers
- preserve the original PNG contents and dimensions
- leave the `examples/integrations/a2a-a2ui/agent/images/` symlinks
untouched

## Root cause

The two PNGs match the repository's Git LFS attributes but were
committed as raw Git blobs. Git therefore continually cleans the working
files into LFS pointers and reports the worktree as dirty, which can
also block rebases.

## Validation

- verified both committed blobs are valid Git LFS pointers
- verified both LFS objects can be fetched from a fresh checkout of the
fork
- verified SHA-256 hashes match the original PNG contents:
- `color.png`:
`ee46987787ab5dfff4792e2df112a8d5422046296ddb1cb83fa1a85cf546d19f`
- `outline.png`:
`e653c3c3e4a700a8a46ee349463bba2169c368be803b5f48c2ae5c7ac4a7e452`
- verified image dimensions remain 192×192 and 32×32
- `MICROSOFT_APP_ID=00000000-0000-0000-0000-000000000000 NX_DAEMON=false
pnpm nx run teams-example:package --skip-nx-cache`
- independently reran previously flaky Nx test targets successfully:
  - `@copilotkit/web-inspector:test` (373 tests)
  - `@copilotkit/channels-slack:test` (389 tests)
  - `@copilotkit/vue:test` (1074 tests)
- checked against the latest upstream `main`; the affected paths are
unchanged and merge cleanly

Fixes #6420
2026-08-30 21:35:18 -05:00
Ben Taylor d4bcaaffb0 docs: fix typo "Retreive" -> "Retrieve" in LangGraph tutorial (#6358)
Fixes a typo in the AI travel app LangGraph tutorial: the step heading
"Retreive API keys" should read "Retrieve API keys".
2026-08-30 21:32:04 -05:00
Ben Taylor 69441ccfa4 fix(react-core): render every v1 tool call (#6682)
## What does this PR do?

Fixes the v1 compatibility render path so an assistant message can
render every tool call instead of only `toolCalls[0]`.

The returned lazy renderer now:

- matches each tool call with its corresponding tool result;
- renders all registered tool-call renderers in one fragment;
- removes `null` render results and returns `null` when no tool has a
renderer.

Keeping the fragment behind the existing lazy-renderer callback
preserves the exported `useLazyToolRenderer` return signature. Filtering
before returning also avoids attaching empty generative UI, so
caller-provided subcomponents are not suppressed when no renderer is
registered.

Regression tests cover multiple tool calls, per-call result matching,
the all-unhandled case, and a mixed handled/unhandled message.

## Related PRs and Issues

- Fixes #2946

## Verification

- `pnpm nx run @copilotkit/react-core:test` (133 files, 1,530 Vitest
tests plus 47 script tests)
- `pnpm nx run @copilotkit/react-core:check-types`
- `pnpm exec oxfmt --check
packages/react-core/src/v1-deprecated/hooks/use-lazy-tool-renderer.tsx
packages/react-core/src/v1-deprecated/hooks/__tests__/use-lazy-tool-renderer.test.tsx`

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] Documentation is unchanged because this restores existing v1
behavior without changing the public API
- [x] "Allow edits by maintainers" is checked
2026-08-30 16:59:05 -05:00
Ben Taylor b0077511bd docs(built-in-agent): mention Novita in OpenAI-compatible providers list (#6502)
## What does this PR do?

Adds Novita to the list of OpenAI-compatible providers documented for
the built-in agent's model selection, alongside OpenRouter, Ollama,
Together, and Groq. Novita exposes an OpenAI-compatible `/openai/v1`
endpoint, so it works through the existing `createOpenAI({ baseURL })`
pattern already documented for those providers.

A follow-up commit adds a callout clarifying that Novita only implements
the Chat Completions endpoint, not Responses — so readers should call
`provider.chat(model)` rather than the bare `provider(model)` form shown
in the adjacent OpenRouter example, which routes through Responses and
would error on Novita's endpoint.

## Related PRs and Issues

- None

## Verification

- `npm run typecheck` — pass
- `npm run test` — 55/55 files, 374/374 tests pass
- `npm run build` — Next.js production build succeeded, 222/222 static
pages generated
- Live verification: called Novita's OpenAI-compatible endpoint via
`createOpenAI({ baseURL }).chat(model)` with model
`deepseek/deepseek-v4-pro-0813` — HTTP 200, finish reason `stop`, usage
populated

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [ ] "Allow edits by maintainers" is checked
2026-08-30 15:06:48 -05:00
Ben Taylor 478a2aac95 fix(examples): MCP Apps widgets send ui/notifications/size-changed so the host resizes the iframe (#6700)
## What

The MCP Apps example widgets report their intrinsic size with
`ui/notifications/size-change`, but both the host and the ext-apps spec
use
`ui/notifications/size-changed`:

- Host: `MCPAppsActivityRenderer` only handles `case
"ui/notifications/size-changed"` and
  reads `{ width, height }` from it to size the iframe.
- Spec: `@modelcontextprotocol/ext-apps` defines
`McpUiSizeChangedNotification` with
  `method: "ui/notifications/size-changed"` (`App.sendSizeChanged`).

Because the names differ by one letter, the host never receives the size
and the widget
iframe stays at its initial height instead of growing to fit its
content.

## Fix

Rename the notification to the spec name in the affected widgets. The
payload is unchanged
(`{ width, height }`), which is exactly what the host reads, so this is
a one-line change per
widget on the sender side only.

No host change: in JSON-RPC a notification (no `id`) must not be
answered, so the host
silently ignoring the old name is correct behavior; the bug is purely
that the widgets sent
the wrong method name.

## Affected widgets

- `examples/showcases/mcp-apps/mcp-server/apps`: flights, hotels,
kanban, trading
- `examples/showcases/generative-ui-playground/mcp-server/apps`:
calculator, flights, hotels,
  kanban, todo, trading

## Testing

Reproduced against a local run of `examples/showcases/mcp-apps` (Next
frontend + MCP server):
before, the widget iframe rendered at its initial height; after the
rename the host receives
`size-changed` and the iframe resizes to the widget content.

## A note from the contributors

From the team at MCP Apps Builders - part of our ongoing series to round
out MCP Apps host
support in CopilotKit. Opening as a draft for review.
2026-08-30 15:04:00 -05:00
Ben Taylor 9909ca44d6 fix(react-core): make agentMetadata.nodeName match the node where the interrupt originates (#6488)
## Problem

`useAgentNodeName` must update React consumers when AG-UI node events
arrive, and `useLangGraphInterrupt.enabled()` must receive the node
where an interrupt actually occurred.

Current `main` includes the basic ref-to-state reactivity fix from
[ffd1580](https://github.com/CopilotKit/CopilotKit/commit/ffd15801d6d),
but that commit explicitly leaves #1426 open: a later `RUN_FINISHED` can
still replace the interrupting node with `"end"`, and v1 consumers can
still be hidden behind `useCoAgent`'s memoized return value.

## What remains in this PR

Rebased onto current `main` (`e9387e0`) after the v1 source migration,
this PR contains only the remaining behavior:

- Preserve the last active node when `RUN_FINISHED` reports `outcome:
"interrupt"`.
- Preserve it for the legacy `on_interrupt` custom-event flow as well.
- Continue transitioning successful and failed runs to `"end"`; reset
new runs and agent switches to `"start"`.
- Add `nodeName` to the `useCoAgent` return-value memo dependencies so
v1 consumers receive the reactive update.
- Share `INTERRUPT_EVENT_NAME` between the hook and interrupt
implementation.

The public hook signatures and AG-UI protocol remain unchanged.

## Preview workflow

- Disabled pkg-pr-new's generated all-package StackBlitz template;
package preview install URLs remain available.

## Changes

- `packages/react-core/src/v1-deprecated/hooks/use-agent-nodename.ts`
- `packages/react-core/src/v1-deprecated/hooks/use-coagent.ts`
-
`packages/react-core/src/v1-deprecated/hooks/__tests__/use-agent-nodename.test.tsx`
- `packages/react-core/src/v2/types/interrupt.ts`
- `packages/react-core/src/v2/hooks/use-interrupt.tsx`
- `.github/workflows/publish-commit.yml`

## Verification

- Full React Core Vitest suite: **131 files, 1520 tests passed**.
- Preview workflow: Nx formatting and YAML parsing passed.
- `nx run @copilotkit/react-core:check-types --skipNxCache`: passed,
including all 33 dependency tasks.
- `git diff --check origin/main...HEAD`: passed.
- The composite React Core test target then reaches the existing
Windows-only script baseline: 8 path-normalization failures plus 2
symlink-permission failures. These are outside this PR's files; the
complete Vitest suite passes before that script stage.

## Scope

This intentionally does not change the AG-UI event protocol, runtime
event ordering, HITL workflow, v1/v2 compatibility layer, or the
separate node tracking in `use-coagent-state-render-bridge.tsx`.

Fixes #1426
2026-08-30 10:05:27 -05:00
Ben Taylor 8870481474 fix(release): preserve breaking change footers (#6745)
## What does this PR do?

Preserves Conventional Commit bodies while collecting release changes so
breaking-change migration guidance can reach both raw and AI-generated
release notes.

The change:

- parses `git log` with explicit field and record separators, including
multiline bodies without splitting commits;
- extracts both `BREAKING CHANGE:` and `BREAKING-CHANGE:` footers and
keeps their continuation lines;
- recognizes only the Conventional Commit `!:` marker instead of
arbitrary exclamation marks;
- shares the raw release-note renderer between the release preparation
script and focused tests;
- adds a real temporary-Git-history regression test plus unit coverage
for footer-only, `!:`-only, trailer, multiline, and empty-body cases.

The implementation is intentionally limited to `scripts/release/`.

Validation completed:

- `pnpm exec vitest run scripts/release` — 14 files, 161 tests passed
- `pnpm run build`
- full test suite, with all initially environment-sensitive projects
rerun successfully
- `pnpm run check:packages`
- `pnpm run lint` — no errors
- `pnpm run check-format`
- `pnpm run release:prepare:dry`
- `bash scripts/release/verify-release-scope-dropdowns.sh`
- targeted TypeScript and oxlint checks for all six changed files

## Related PRs and Issues

- Fixes https://github.com/CopilotKit/CopilotKit/issues/6479
- Clean, release-only follow-up to
https://github.com/CopilotKit/CopilotKit/pull/6632

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation (not applicable; internal release tooling with
regression coverage)
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-08-30 09:59:26 -05:00
guomengjia618-dot 4b53e86716 fix(ci): stop publishing the unusable all-package sandbox 2026-08-30 12:14:59 +08:00
Ben Taylor 9fcaed7690 fix(sdk-python): strip orphan OpenAI Responses function_call content blocks (#6781)
## What does this PR do?

`after_model` strips intercepted frontend `tool_calls` off the last
AIMessage, but under `responses/v1` output the equivalent
`function_call` **content blocks** stay behind in `message.content`. If
the run is cancelled mid-turn, that partial turn persists without a
`ToolMessage`. On replay, `langchain-openai` serializes the orphaned
block into the Responses API `input` with no matching
`function_call_output`, and OpenAI rejects the request:

```
400 - "No tool output found for function call call_..."
```

Because the poisoned message replays with the thread history, the thread
then fails **permanently** — every subsequent turn 400s, not just the
one that raced the cancellation.

The existing sanitizer (`_fix_messages_for_bedrock`) only strips
unanswered Anthropic `tool_use` blocks. This PR extends the same three
strip sites to OpenAI Responses `function_call` blocks, keyed on
`call_id` (`block["id"]` on those is the item id, not the call id):

1. blocks whose `call_id` is missing from `msg.tool_calls`
2. all of them when `tool_calls` is empty (e.g. after_model intercepted
everything)
3. the ones belonging to unanswered (non-adjacent) `tool_calls`

This is a re-land of the fix from #4473 (closed unmerged, tracked as
OSS-71); the defect is still present on current `main`.

## Side-effect analysis — orphan-handoff contract

The sanitizer runs request-scoped inside
`wrap_model_call`/`awrap_model_call`, in the same pass that already
strips Anthropic `tool_use` blocks on `main`; it writes nothing to the
checkpoint and does not change `after_agent`'s restore behavior:

- the intercepted frontend call is still restored with its synthetic
`forwarded_to_frontend` result before the sanitizer runs, so an
**answered** `function_call` block survives the wrap
(`test_next_model_call_keeps_answered_function_call_blocks_on_restore`);
- only blocks with no matching answered tool call are stripped — the
poisoned-replay shape is covered end-to-end through
`wrap_model_call`/`awrap_model_call`
(`test_replayed_cancelled_turn_strips_orphan_function_call_blocks`, sync
+ async).

## Related PRs and Issues

Fixes #6676
Re-lands #4473

## Checklist

- [x] Tests pass: `pytest tests/test_copilotkit_lg_middleware.py` → 91
passed (4 regression tests: the two unit shapes plus the two
request-scoped wrap paths above, sync + async)
- [x] Full local suite: `pytest tests/` → 232 passed, 11 skipped (the 4
`test_intercepted_tool_call_events.py` failures reproduce on pristine
`main` in the same local environment — dependency drift, not from this
branch)
- [x] `ruff format --check` clean on the changed files; no new `ruff
check` findings
- [x] `__tests__` / devSK: N/A (Python SDK)
2026-08-29 21:20:46 -05:00
Ben Taylor 2fd9cf72c1 docs(shell-docs): fix broken Action Handlers link in a2ui fixed-schema page (#6764)
The "Advanced — Action Handlers" link pointed to
`./advanced#action-handlers`, but there is no `advanced.mdx` page in
`docs/generative-ui/a2ui/`. The "Action handlers (reference)" section
lives in this same file, so the link now points to it directly with
`#action-handlers-reference`.
2026-08-29 21:15:33 -05:00
Ben Taylor 57fb0e7180 docs(example): remove dead links to non-existent docs files in agentcore README (#6763)
The `examples/integrations/agentcore` README references
`docs/LOCAL_DEVELOPMENT.md` and `docs/LOCAL_DOCKER_TESTING.md`, but the
example has no `docs/` directory and neither file exists anywhere in the
example. The local-development workflow is already fully documented
inline in the "Local Development" section, so these references are dead
links.

Verified: `docs/` is absent from `examples/integrations/agentcore/`, and
a repo-wide search finds no `LOCAL_DEVELOPMENT.md` /
`LOCAL_DOCKER_TESTING.md`.
2026-08-29 21:15:02 -05:00
Ben Taylor a68fd0b253 fix(showcase/harness): re-auth on 403 from an expired PocketBase token (#6466)
## Root cause

The harness's PocketBase client
(`showcase/harness/src/storage/pb-client.ts`) re-authenticated its
superuser token **only on HTTP 401**. But when the superuser/admin auth
token's ~14-day TTL expires, PocketBase does **not** return 401 — it
treats the request as an unauthenticated *guest* and returns:

```
HTTP 403 {"code":403,"message":"Only admins can perform this action.","data":{}}
```

on every write. Because 403 was never treated as an auth-expiry signal,
the expired token was never refreshed, so **all `status` writes failed
permanently** until the process restarted. `classifyWriterError` maps
403 → `pb_permission` (a terminal reason), so the failure looked like a
permission problem rather than an expired session. This is what blanked
the dashboard for ~46h.

## The fix

In `request()`, treat a 403 as the same stale-session signal as a 401 —
**but only when the request actually carried an `Authorization` header**
(`sentAuth`). A 403 on a request that sent no token is a genuine
guest-forbidden result that re-auth cannot fix, so it is left to
surface.

- The retry stays bounded by `MAX_AUTH_RETRIES` (1). A 403 that
**persists after a fresh, successful re-auth** is a real permission
error and falls through to the caller (still classified `pb_permission`)
— never an infinite re-auth loop.
- No change to the 401 path, the retry envelope, or any other status
class.

```
(res.status === 401 || (res.status === 403 && sentAuth)) &&
authRetries < MAX_AUTH_RETRIES && attempts < maxAttempts
```

## Local red-green proof (real PocketBase, real client — not a fake)

Stood up a live **PocketBase v0.22.21** (the pinned version) locally,
created an admin + a superuser-gated `status` collection, and set
`adminAuthToken.duration = 5` (5s — the server's minimum). A temporary
driver drove the **real `createPbClient`** against it: write #1 caches a
token, sleep 6.5s so the cached token **genuinely expires**, then write
#2.

First confirmed the raw failure surface — an expired admin token on a
write:

```
EXPIRED-token write status + body:
{"code":403,"message":"Only admins can perform this action.","data":{}}
HTTP 403
```

### RED (unmodified code)

```
[driver] write#1 OK id=setjh0ca1s09s14 — token now cached
[driver] sleeping 6.5s for the cached admin token to expire...
CVDIAG component=pb-client:create:status ... status=error error=status=403 {"code":403,"message":"Only admins can perform this action.","data":{}}
[driver] RED: write#2 FAILED after expiry: Error: pb create failed: 403 {"code":403,"message":"Only admins can perform this action.","data":{}}
EXIT=1
```

The expired token 403s, **no re-auth occurs**, the write stays failed.

### GREEN (with this fix)

```
[driver] write#1 OK id=tkl59dt5d3xt11g — token now cached
[driver] sleeping 6.5s for the cached admin token to expire...
[driver] GREEN: write#2 SUCCEEDED after expiry id=uns9y2dgysynpwz
EXIT=0
```

Same repro, same expired token: the 403 now triggers re-auth, the write
is retried once and **succeeds**.

## Regression tests

Added three tests to `pb-client.test.ts`:

1. `re-auths on 403 (expired superuser token treated as guest) then
retries the write` — 403-with-token → re-auth → retry succeeds (2 auths,
2 writes).
2. `caps 403 re-auth at 1 — a 403 that persists after a fresh auth
surfaces (no infinite loop)` — bounded; the persistent 403 surfaces (2
auths, 2 writes, then throws).
3. `does NOT re-auth on 403 when no credentials were sent (genuine
guest-forbidden)` — no token → no re-auth, no retry (0 auths, 1 write).

**Mutation check:** reverting the fix (403 branch removed) makes tests 1
and 2 fail while test 3 still passes — the tests are structurally able
to detect the fix.

## Code-review hardening (Tier-3 cr-loop)

A full-breadth review of the re-auth branch surfaced two additional
load-bearing issues in the exact code this PR modifies; both fixed here
with their own red-green + individual mutation checks:

- **Drain the response body on the re-auth path.** The 401/403 re-auth
branch did `continue` without draining the prior failed response —
unlike the 429/5xx branches, which call `drainBody()` — leaking a
half-consumed socket on every token refresh (F2.3 socket-reuse
discipline). `drainBody` was hoisted above the branch and invoked before
the retry.
- RED: `failed401.bodyUsed` = `false` (undrained). GREEN: body drained
after the fix.
- **Bound the re-auth gate by `attempts < maxAttempts`.** The re-auth
gate checked only `authRetries`, not `attempts` (the 429/5xx gates check
both), so a token expiring on the final attempt could fire a 4th
`fetchImpl`, exceeding the documented `maxAttempts = 3` envelope. Added
the guard for consistency.
- RED: `expected 4 to be 3` (4th fetch fired). GREEN: `writeCount ===
3`.

Full `pb-client.test.ts` suite: **35 passed**. CI green.

## Follow-ups (out of scope for this PR — pre-existing, tracked
separately)

The review confirmed the fix is sound and found no defect in it, but
flagged pre-existing issues in the same file that predate this change
and belong in their own PRs:

- **Observability regression (HF13-B1):** `create()`'s CVDIAG "every
record write failure is greppable" log is unreachable for
retry-exhausted 429/5xx writes, because `request()` now throws
`PbHttpError` before `create()`'s `!res.ok` block runs. (403 writes are
unaffected — they reach the log.)
- **Auth re-auth stampede:** `ensureAuth()` has no single-flight guard,
so at token expiry every concurrent writer re-auths independently.
Fixing this (coalesce concurrent re-auths behind one shared in-flight
promise) benefits both the 401 and 403 paths.
- **401 `sentAuth` symmetry (trivial):** the 401 re-auth path lacks the
`sentAuth` guard the new 403 path has, wasting one bounded attempt when
no credentials are configured.
- **`deleteByFilter` off-by-one:** the iteration cap throws on a
fully-successful delete of exactly a multiple-of-200 ≥ 20000 rows.
- **Inert `RETRY_AFTER_MAX_MS` cap + its mutation-blind test.**
2026-08-29 16:08:16 -05:00
Ben Taylor 415aa9af74 docs: fix typo in coagent troubleshooting (#6787)
Fix duplicate the/typo n -> in.
2026-08-29 16:05:43 -05:00
Ben Taylor 7f3d4e62df docs: fix GitHub branding and hyphenation in troubleshooting (#6786)
Fix GitHub branding (Github -> GitHub) and hyphenation open-source.
2026-08-29 16:05:37 -05:00
Ben Taylor 6067e87316 docs: fix heading spacing in CONTRIBUTING (#6785)
Fix missing space after numbered headings.
2026-08-29 16:05:31 -05:00
Ben Taylor 1433cd86f6 docs: fix hyphenation in CONTRIBUTING (#6784)
Fix hyphenation: open source -> open-source when used as adjective.
2026-08-29 16:05:23 -05:00
Ben Taylor 40beb51634 fix(channels): echo the originating interrupt back as command.interruptEvent (#6353)
## Problem

`thread.resume()` sends `forwardedProps.command = { resume }` and
nothing else.

LangGraph needs nothing more — it correlates a resume by `thread_id` —
so this gap has been invisible. Bridges that correlate by ids carried
**inside the interrupt** cannot work at all. `@ag-ui/mastra` gates its
resume on `command.interruptEvent` and needs `{toolCallId, runId}` out
of it:

```js
let o = input.forwardedProps?.command;
if (!o?.interruptEvent && Array.isArray(input.resume)) { /* standard path */ }
if (o?.resume === false && o?.interruptEvent) { /* cancelled */ }
if (o?.resume != null && o?.interruptEvent) { /* resume */ }
```

With no `interruptEvent`, **every branch is skipped**. The resume is not
an error — it is a silent no-op: the run proceeds as an ordinary new
turn, no `RUN_ERROR`, no tool result, and the suspended tool never
completes. That is the entire Mastra HITL story through Channels today:
the picker appears, the click lands, nothing resumes.

## Change

`Thread` retains the captured interrupt value when the interrupt fires,
and `resume` echoes it back.

- **Opaque.** Channels neither parses nor reshapes the value — it hands
back exactly what the agent sent, so no framework specifics leak in.
This mirrors what `useInterrupt` in `react-core` already does for legacy
interrupts.
- **Durable, not in-memory.** The click that resumes can arrive in a
later run, possibly after a restart — which is the entire reason this
path exists rather than `awaitChoice`.
- **One-use.** Read back with `kv.consume` (atomic take-and-delete),
matching the one-use continuation the resume already claims, so a
replayed click cannot resurrect a spent resume.
- **Retention tied to `actionRetentionMs`**, so the value and the button
that consumes it expire together. Configure a longer action retention
and the value can lapse first; the resume then omits the field, which is
the pre-existing behaviour rather than a new failure.
- **Best-effort store touches.** An agent that needs no correlation data
must not lose its interrupt to a store hiccup.

## Why this is additive

The field is **omitted** when no interrupt value was captured, so the
serialized command is byte-identical for agents that carry no
correlation data, and a normal (non-interrupt) run still writes nothing
at all. LangGraph ignores the extra field.

`interrupt-event-echo.test.tsx` covers all four properties — echo,
durability, one-use, omission. The echo test fails without the fix
(verified by reverting it).

## Verification

- `channels-core`: 268/268 tests, typecheck clean, `oxfmt` clean,
`oxlint` 0 errors.
- Verified end to end against a real Mastra agent driven through Slack:
two full cycles (approve, then decline), each producing a matching
`SUSPENDING` → `RESUMED` pair agent-side. Before the fix the same rig
produced suspends with zero resumes.
- The LangGraph interrupt path was re-checked against the same harness
and is unchanged.

## Reviewer note

One existing assertion in `hitl-continuation.test.tsx` moves from
counting `kv.set` / `kv.consume` calls to matching them by **key**. The
invariant it protects is "an interrupt turn writes exactly one action
snapshot, and one resume consumes it once" — both still asserted, now
stated directly rather than via a total that conflates action state with
unrelated bookkeeping. Flagging it because it is the one pre-existing
test this change edits.

## Out of scope

Channels ignores the standard AG-UI interrupt path (`RUN_FINISHED` with
`outcome.type === "interrupt"`, resumed via a top-level `resume` array).
Mastra emits it today alongside the legacy event, and it carries
interrupt ids and expiry the legacy event lacks. Worth a follow-up.
2026-08-29 14:29:29 -05:00
Ben Taylor 10587a8cd6 chore(channels): drop references to canceled OSS tickets in code comments (#6297)
## What

Three comments in the channels packages point at Linear tickets that
were **canceled on 2026-07-01** (superseded by OSS-401's consolidated
realtime foundation). They read as live tracking pointers when nothing
actually tracks the work.

| File | Reference | Ticket status |
|---|---|---|
| `packages/channels-core/src/codec.ts` | `TODO(OSS-363)` | Canceled |
| `packages/channels-intelligence/src/runtime.ts` | `TODO(OSS-377)` |
Canceled |
| `packages/channels-slack/src/ingress-normalize.ts` | `(OSS-362)`
provenance note | Canceled |

## How

The engineering intent is **preserved** in each case and re-marked as
`TODO (untracked)` with a note that the original ticket was canceled —
so a reader re-files rather than chasing a dead ticket. The `OSS-362`
mention was a bare provenance parenthetical carrying no intent, so it is
simply dropped.

`codec.ts` additionally records that the TODO's stated precondition is
**already met**: the pure Slack ingress mapping (mention stripping,
stable event-id derivation, real-user filtering, field extraction) now
lives in `channels-slack/src/ingress-normalize.ts`. The remaining work
is hoisting `normalizeIngress` onto the generic `PlatformCodec` — worth
knowing for whoever picks it up.

## Why this came up

While grounding a Channels x AG-UI gap analysis, several `OSS-###`
comments turned out to reference closed tickets — three canceled, two
already done. The done ones (OSS-450 Teams managed, OSS-491 delivery
terminal-signal) had already been cleaned out of `main` by subsequent
work; these three canceled ones are what remain.

## Scope / notes

- **Comment-only. No behavior change.** `oxfmt --check` and `oxlint`
both clean.
- Out of scope, flagged for a possible follow-up: `OSS-406`
(`channels-intelligence/src/index.ts`) and `OSS-599`
(`runtime/.../channel-manager.ts`, 5 references) are also canceled, but
sit outside the channels packages surveyed here. Other referenced
tickets (`OSS-96/162/476/566/568/621/623/641/670/691`) were not
status-checked.
2026-08-29 14:28:25 -05:00
Ben Taylor 55d321dd2f chore: add CodeRabbit configuration (#6077)
Tunes the already-installed CodeRabbit reviewer for this repo. Today it
runs on defaults; this makes it repo-aware and cuts noise.

## What this changes
- **`profile: chill`** + **`auto_review.ignore_title_keywords`** (skip
the `fix(showcase)`/release automation PRs) + **`path_filters`**
(exclude lockfiles, dist, snapshots, fixtures, aimock wiring, the
vendored openclaw demo) — keeps review budget on real contributor code.
- **Enables `gitleaks` / `semgrep` / `actionlint`** — secret + SAST +
workflow linting, complementing the existing `zizmor` / fork-PR-alert.
- **`path_instructions`** that encode repo conventions so they're
flagged inline on every PR:
- additions under the retired `packages/v1|v2/` or `docs/content/` trees
- the `showcase/shell-docs/src/content/ag-ui/` **downstream-mirror**
rule (changes belong upstream in ag-ui-protocol/ag-ui)
  - this repo does **not** use changesets (`.changeset/` debris)
- LangGraph routing: `goto END` on a frontend/HITL tool call is the
intended handoff, not a dropped tool
- verify reference-parity claims by **direction against the actual
reference**, not the PR description

## Notes
- Advisory only — no behavior is auto-merge/auto-close.
- CodeRabbit validates its own config, so it should confirm the schema
on this PR.
- An equivalent config for `ag-ui-protocol/ag-ui` is coming as a
separate PR (different retired paths; ag-ui docs are canonical, not a
mirror).

Part of the OSS backlog-triage automation plan (Phase 1).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-29 10:22:24 -05:00
Mark 17ed176736 chore: RuleTester coverage for no-single-arg-zod-record lint rule (#5032)
## Summary

Follow-up to #5025 addressing @marthakelly's [review
suggestion](https://github.com/CopilotKit/CopilotKit/pull/5025#discussion_r3307285578):
add oxlint `RuleTester` coverage for the
`copilotkit/no-single-arg-zod-record` rule. Since the underlying Zod 4
incompatibility is **type-level** (no runtime test can catch a
regression), this lint rule is the real safety net, so it's worth
testing directly.

## Cases (via `oxlint/plugins-dev` `RuleTester`)
**valid**
- two-arg `z.record(z.string(), z.unknown())` — no false positive
- two-arg with `.optional()` chain
- single-arg `.record()` on a non-`z` object (`cache.record(entry)`) —
confirms the rule is scoped to the `z` alias and won't over-fire

**invalid**
- single-arg `z.record(z.unknown())` → fires + autofix output
`z.record(z.string(), z.unknown())`
- chained `z.record(z.unknown()).optional()` → fixes the inner call
- `z.record(...spread)` → reports **without** a fix (`output: null`)

## Node version gate (important)
oxlint's `RuleTester` requires **Node ≥ 22** (it throws at parse time on
older runtimes). The CI unit matrix includes a **Node 20** job, so the
cases are gated to skip below Node 22 — verified locally: **6/6 pass on
Node 22**, **skips cleanly on Node 20**. The lint rule itself is still
exercised on every Node version through the `oxlint` job; only these
RuleTester unit tests are gated.

Also extends the `react-ui` vitest `include` to pick up co-located
`oxlint-rules/**/*.test.mjs`.

## Test plan
- [x] `vitest run` on the new file under Node 22 → 6 passed
- [x] same under Node 20 → 1 skipped (gate works; Node 20 CI job stays
green)
- [x] `oxlint` clean on the new test + config

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-29 07:54:58 -07:00
Rainer Hahnekamp dc6617c103 fix: ng version of the demo (#6659)
**exact `22.0.0`**, matching the library:

- `examples/v2/angular/demo/package.json`
- `examples/v2/angular/storybook/package.json`
2026-08-29 16:46:07 +02:00
Ben Taylor 564b35929f fix(telemetry): install registry outside parent workspace (#6621)
## Problem

Telemetry fragment workflows install the nested oss-path-to-production
checkout through CopilotKit's parent pnpm workspace. The checkout's own
dependencies, including Node type declarations, are therefore not
installed and reconciliation fails during TypeScript compilation.

## Why

pnpm discovers the parent workspace from p2p, reports the 70 CopilotKit
workspace projects, and ignores the external registry's lockfile.

## Fix

Run install and reconciliation with the ignore-workspace option in both
telemetry workflows, forcing pnpm to use the checked-out registry's
package metadata and lockfile. Verified with a clean
oss-path-to-production checkout.
2026-08-29 09:36:57 -05:00
1553126902 7b7e5cc510 test(sdk-python): cover orphan function_call stripping through the wrap path
Add two request-scoped regressions for #6676 beyond the unit tests:

- test_replayed_cancelled_turn_strips_orphan_function_call_blocks: the
  poisoned-checkpoint shape (run cancelled mid-turn, function_call blocks
  persisted with no ToolMessages and no intercept state) driven through
  wrap_model_call/awrap_model_call — the model must receive a history
  with no orphaned blocks and no unanswered tool_calls, which is what
  keeps langchain-openai from re-emitting them as Responses input items
  without a matching function_call_output.
- test_next_model_call_keeps_answered_function_call_blocks_on_restore:
  the legitimate resume path — restored frontend call with its synthetic
  result plus the answered backend call keep their function_call blocks,
  proving the sanitizer does not over-strip the orphan-handoff contract.
2026-08-29 22:22:48 +08:00
Tyler Slaton a9614495b8 fix(telemetry): install registry outside parent workspace 2026-08-29 08:58:08 -05:00
一片一片 8b5c6abfd6 Merge branch 'main' into docs/fix-agentcore-readme-dead-links 2026-08-29 18:43:49 +08:00
一片一片 3555b4a48e Merge branch 'main' into docs/fix-a2ui-action-handlers-link 2026-08-29 18:43:17 +08:00
Murat Sari bb80fff40a fix(angular): align demo and Storybook versions
The Angular demo and Storybook use Angular 22.1, while @copilotkit/angular is developed against Angular 22.0. Both consumers map @copilotkit/angular directly to its source files.

That causes TypeScript to load Angular types from two dependency contexts. Angular signal types contain unique-symbol brands, so signals originating from Angular 22.0 are incompatible with otherwise equivalent signals from Angular 22.1.

Pin the demo and Storybook to Angular 22.0 so the source-linked library and its consumers share the same Angular type identity. Update pnpm-lock.yaml to keep frozen installs reproducible.

The existing unit workflow intentionally selects only packages/**, while the packed-package test does not exercise these source-linked monorepo consumers.
2026-08-29 11:45:32 +02:00
deepshekhardas 6ff101e7c0 docs: fix typo in coagent troubleshooting 2026-08-29 11:29:05 +05:30
deepshekhardas 93248cf95f docs: fix GitHub branding and hyphenation in troubleshooting 2026-08-29 11:28:28 +05:30
deepshekhardas 220ac3236a docs: fix heading spacing in CONTRIBUTING 2026-08-29 11:27:54 +05:30
deepshekhardas d77ef77c02 docs: fix hyphenation in CONTRIBUTING 2026-08-29 11:27:02 +05:30
Ben Taylor b995233a4d test(shared): add unit tests for conditions engine (#6735)
Add comprehensive unit tests for the xecuteConditions rule engine in
packages/shared/src/utils/conditions.ts, which previously had no test
coverage. Covers all 12 comparison/existence rules, logical AND/OR/NOT
nesting, implicit AND across multiple conditions, and dot-path
resolution (including missing-path and whole-value fallback). Uses the
existing vitest setup in the shared package.
2026-08-28 23:23:22 -05:00
Ben Taylor 0056b6ac5e docs(examples/travel): fix broken search.py link in hook README (#6734)
The lib/hooks/README.md in the travel example links to
../../agent/travel/search.py, but the file is at
xamples/v1/travel/agent/src/search.py. Fix the relative path to
../../agent/src/search.py.
2026-08-28 22:31:35 -05:00
Ben Taylor 1d27fa4b2b docs: add missing question mark in What is CopilotKit heading (#6753)
docs: add missing question mark for heading.
2026-08-28 22:30:11 -05:00
Ben Taylor db6d885021 docs(shell-docs): fix broken Fixed Schema (Streaming) link (#6733)
The A2UI overview pages for both the DeepAgents and LangGraph
integrations link to ./fixed-schema-streaming, but that file does not
exist (only dvanced, dynamic-schema, ixed-schema, index, styling
exist). Point both links to the existing ./fixed-schema page.
2026-08-28 22:12:45 -05:00
gdut4140 1ef61717b8 docs(shell-docs): point Action Handlers link at the LangGraph advanced page 2026-08-29 11:06:54 +08:00
gdut4140 6f986a9a99 docs(example): remove dead LOCAL_DEVELOPMENT.md references in agentcore docker + validate script 2026-08-29 11:06:48 +08:00
hata33 963f753f53 fix(sdk-python): strip orphan OpenAI Responses function_call content blocks
after_model strips intercepted frontend tool_calls from the AIMessage but
leaves the equivalent responses/v1 function_call content blocks in
message.content. When a run is cancelled mid-turn the partial turn persists
without a ToolMessage, and on replay langchain-openai serializes the
orphaned block into the Responses API input with no matching
function_call_output, so OpenAI rejects every subsequent turn with
400 "No tool output found for function call call_...".

Extend the checkpoint message sanitizer to treat function_call blocks
like the existing Anthropic tool_use handling: strip blocks whose call_id
is missing from msg.tool_calls, strip all of them when tool_calls is
empty, and strip the ones belonging to unanswered tool_calls.

Fixes #6676
2026-08-29 10:58:40 +08:00