Commit Graph

15385 Commits

Author SHA1 Message Date
Martha Kelly Schumann 68fbe97d87 chore(sdk-python): release 0.1.96 for FAC-124 (#6728)
## Summary

- publish the current Python SDK lifecycle repair as
`copilotkit==0.1.96`
- unblock the LangGraph Python starter from consuming the fixed
intercepted frontend/MCP tool-call bridge
- keep the starter pin unchanged until PyPI serves this exact version

## Why this is separate

FAC-124 requires an ordered delivery sequence. The current lifecycle fix
and its streaming/non-streaming regression tests are already on `main`,
but PyPI still serves `0.1.95` and the starter is pinned to `0.1.94`.
Merging this release gate publishes `0.1.96`; the follow-up PR can then
safely update the starter lock, add the bounded Excalidraw labeling
contract, and add starter-level deterministic coverage.

Refs FAC-124.

## Validation

- `./scripts/release/detect-py-version-changes.sh` →
`should_publish=true` for `0.1.96`
- `poetry check --lock`
- `poetry build` produced the `0.1.96` wheel and sdist
- `poetry run python -m pytest -q
tests/test_intercepted_tool_call_events.py` → 8 passed
- `git diff --check`
python-sdk/v0.1.96
2026-08-26 15:27:12 -07:00
copilotkit-qa-bot[bot] 4c5402a0aa chore(sdk-python): release 0.1.96 2026-08-26 15:12:51 -07:00
Martha Kelly Schumann a09e0d1d14 docs(agno): configure frontend-tool session storage (#6726)
## Summary

- configure the Agno showcase agent with writable SQLite session storage
so paused frontend-tool and HITL runs can resume
- document the database requirement on all four affected Agno routes
without changing shared framework-neutral content
- add runtime-agent, rendered visual docs, LLM-text, dependency, and
framework-isolation regression coverage

## Validation

- Agno Python tests: 33 passed
- Agno persistence regression: passed
- shell-docs persistence tests: 5 passed
- shell-docs typecheck and production build: passed
- Ruff and git diff checks: passed
- Agno 2.7.2 characterization: the configured database passes the
missing-database guard and reaches session lookup

## Probe note

The direct D6 probe was blocked before Agno started by the existing
showcase shared-module staging error at showcase/integrations/ag2/tools.
No Agno service or changed code was reached.

Fixes
[FAC-125](https://linear.app/copilotkit/issue/FAC-125/agno-frontend-tools-fail-without-database-configured).
2026-08-26 15:07:51 -07:00
copilotkit-qa-bot[bot] 17c59fd719 collect Agno persistence regression in CI 2026-08-26 14:56:06 -07:00
copilotkit-qa-bot[bot] 301fa3cc19 Merge remote-tracking branch 'origin/main' into codex/fac-125-agno-db-docs 2026-08-26 14:47:03 -07:00
Martha Kelly Schumann 54567d1d12 fix(docs): send Intelligence CTAs through onboarding (#6727)
## Bug

[Sam
reported](https://copilotkit.slack.com/archives/C0BR0V2P4QJ/p1787777419496249)
two live Docs CTA failures:

- The navbar signup CTA returned users to Docs after authentication.
- Other Intelligence CTAs could send signed-in users to Pricing.

## Fix

- Send every Docs Intelligence signup CTA to the matching environment at
`/sign-in?post_auth_redirect=ready`.
- Remove the Docs `redirect_url` that explicitly caused the return to
Docs.
- Share the URL builder across the navbar and in-page CTAs so their
destinations stay aligned.
- Preserve existing UTM attribution and custom Talk to an Engineer
destinations.

## Validation

- 18 focused tests passed.
- Shell Docs TypeScript check passed.
- Focused lint and formatting checks passed.

No visual changes.
2026-08-26 14:09:53 -07:00
copilotkit-qa-bot[bot] 235ac1cf8f fix(docs): correct Intelligence CTA redirects 2026-08-26 14:01:28 -07:00
copilotkit-qa-bot[bot] 3c314c6d84 rerun CI after automated formatting 2026-08-26 13:48:06 -07:00
github-actions[bot] 53b2bfb520 style: auto-fix formatting 2026-08-26 20:46:46 +00:00
copilotkit-qa-bot[bot] 09f0482152 document Agno frontend-tool session storage 2026-08-26 13:43:11 -07:00
Martha Kelly Schumann e432fd161b fix(ms-agent-python): migrate starter to stable APIs (#6723)
## Summary

- migrate the Microsoft Agent Framework Python starter from retired beta
APIs to the current stable Agent, tool, AG-UI, and OpenAI client
contracts
- preserve both Azure AD (`az login`/`DefaultAzureCredential`) and Azure
API-key authentication
- update all authored Microsoft Agent Python setup examples and
integration guidance
- prevent duplicate frontend/backend HITL tool registration exposed by
the stable runtime
- add stable API, auth-path, configuration-error, and rendered-doc
regression coverage

## Validation

- Python stable API unit tests: 4 passed
- keyless Azure client initialization smoke
- rendered docs regression matrix: 8 passed
- shell docs TypeScript typecheck
- Ruff, Oxfmt, Oxlint, and git diff checks
- Dockerized starter smoke: 4 passed (health, agent, interaction, chat)

Fixes
[FAC-127](https://linear.app/copilotkit/issue/FAC-127/microsoft-agent-python-docs-import-non-existent-agent-frameworkazure).

Supersedes the closed, unmerged earlier attempt in #6099.
2026-08-26 13:25:24 -07:00
copilotkit-qa-bot[bot] 70f15d6ead Merge remote-tracking branch 'origin/main' into codex/fac-127-ms-agent-stable-apis 2026-08-26 12:56:16 -07:00
copilotkit-qa-bot[bot] 0b854047e2 fix(ms-agent-python): preserve Azure AD auth 2026-08-26 12:55:46 -07:00
Martha Kelly Schumann 45faf9f65a docs(google-adk): define state-streaming stop callback (#6611)
## Summary

- document the Google ADK `stop_on_terminal_text` registration required
for state streaming
- keep internal callback commentary out of generated public docs and
link the canonical implementation instead
- cover both visual and LLM documentation output, including regression
checks for leaked internals

## Validation

- `npx vitest run src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts`
- `npx vitest run src/lib/__tests__/llm-text.test.ts -t "renders the
Google ADK termination setup"`
- `npx oxfmt --check src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts
src/lib/__tests__/llm-text.test.ts`
- `npx oxlint src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts
src/lib/__tests__/llm-text.test.ts`
- `npm run typecheck`
- `git diff --check`
2026-08-26 12:48:01 -07:00
copilotkit-qa-bot[bot] 8a01027e7b Merge remote-tracking branch 'origin/main' into codex/fac-72-google-adk-stop-callback 2026-08-26 12:39:51 -07:00
Ben Taylor d02f7e699e docs(react-core): state the blast radius of key-remount and the provisional agent (refs OSS-979) (#6710)
## Why

An Intelligence integration lost a request-to-row correlation map
partway through a user interaction — no error, no warning. It surfaced
as "our response routing is flaky". OSS-979 filed it as
`CopilotKitProvider` remounting its children.

The provider does nothing of the kind. It renders `{children}`
unconditionally at `CopilotKitProvider.tsx:952` — unkeyed, no early
return, and there is no `Suspense` boundary anywhere in `v2`. Nothing in
the SDK silently re-points the active thread either; every mutation path
(`setActiveThreadId`, `startNewThread`, the drawer row click, the
inspector override) is caller-driven.

The remount was app-side, and it was app-side because this skill told it
to be:

- `references/threads.md:98` teaches `useThreads()` → select →
`<CopilotChat key={activeId}>`, and that recipe is only reachable once
Intelligence is wired.
- `references/switching-agents.md:123` teaches "`key={activeAgent}`
forces remount so thread state doesn't leak" without saying what else
that discards.
- `examples/showcases/reskinnable-demo/src/app/[skin]/layout.tsx:223`
models `<SubagentActivityProvider key={threadId}>` above `{children}`,
commented "Remounting is deliberate".

Follow all three and you key a layout-level provider on a thread id that
changes asynchronously after mount. Everything below it dies
mid-interaction.

Two properties made it invisible:

- Durable threads exist only in Intelligence mode, so with a plain SSE
runtime `useThreads` returns nothing, the selected thread never changes,
and the remount never fires. It appears the moment Intelligence is
wired.
- Whether state survives depends on whether the user acted before the
thread list resolved.

## What changed

Docs only — no library change. Both traps now carry their blast radius,
in the four places an agent actually reads:

| File | Change |
|---|---|
| `SKILL.md` | Two invariants in the load-once section, so they land
before any reference is opened |
| `references/threads.md` | New HIGH entry on keying above app state;
note that `activeId` in the switcher recipe settles asynchronously |
| `references/switching-agents.md` | Existing HIGH entry now states the
blast radius and cross-links the threads trap |
| `references/switching-agents-recipes.md` | Key rule amended — keep it
on `<CopilotChat>`, nowhere higher |
| `references/agent-access.md` | The second route to the same symptom:
`useAgent` swaps a provisional stand-in for the real agent when `/info`
resolves, so an effect keyed on `agent` re-runs once, mid-interaction.
Adds an `isReady` pattern and a HIGH entry |

`isReady` appeared in **zero** shipped skills before this — it was
documented only in `showcase/shell-docs/.../useAgent.mdx` and in JSDoc.
Same shape as OSS-888, where the root cause was the shipped skill rather
than the library.

Also corrects a factual error: the skill claimed `useAgent` returns `{
agent }` only. It returns `{ agent, isReady }`.

The 10-file diff is 5 source files under `packages/react-core/skills/`
plus their 5 mirrors under `skills/`, regenerated with `pnpm
sync:plugin-skills`.

## Verification

- `pnpm check:plugin-skills` — mirror in sync
- `pnpm exec vitest run scripts/__tests__/sync-plugin-skills.test.ts` —
12 passed
- `oxfmt --check` — clean over both skill trees
- Full pre-commit suite green, including `test-and-check-packages`
(`test`, `publint`, `attw` across 2 projects and 20 dependent tasks)

## Not in scope

Whether the run's app keyed on `threadId` or on `agent` is not
settleable from the repo — its source is not in any checkout, and there
is no `2026-08-25` strands run report under
`tools/one-prompt-development/evaluation/runs` on any branch. Both
variants produce the reported symptom and this covers both, so a
first-hand repro is a separate task. The `reskinnable-demo` layout is
left as-is deliberately: it is a legitimate use of the pattern, and it
is now the worked example the guidance warns about.

Scoping detail in the OSS-979 comment.

refs OSS-979

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-26 14:38:23 -05:00
Maxim 9a103a0348 chore(examples): put the AgentCore example on uv projects (#6672)
## What

Converts `examples/integrations/agentcore` from unlocked
`requirements.txt` files to uv projects (`pyproject.toml` + `uv.lock`),
matching every other Python integration example in this repo.

## Why

The example already used uv as an *installer* — the agent images are
built from `ghcr.io/astral-sh/uv` and ran `uv pip install -r
requirements.txt` — but nothing was locked, so each image build
re-resolved transitive dependencies against whatever PyPI had that day.

That had already drifted into a hard break. `langgraph==1.0.10rc1`
resolved alongside a `langgraph-prebuilt` that reads `ExecutionInfo` off
`langgraph.runtime`, which 1.0.x does not export, so the LangGraph agent
raised `ImportError` at container start.

## The migration

- Both agents get a `pyproject.toml` + `uv.lock`; the Dockerfiles
install with `uv sync --locked` and run out of `/app/.venv`.
- LangGraph agent moves to `langgraph==1.1.6` / `langchain==1.2.15`, the
pair used by `examples/integrations/langgraph-python`, which resolves
the import failure. `langchain` was previously `>=0.3.0` while the code
uses the 1.x `create_agent` API.
- Four packages that shipped code imports directly were declared nowhere
and survived only as transitives: `boto3` (both agents, via
`agents/utils/ssm.py`), `PyJWT` (langgraph, via `agents/utils/auth.py`),
`langchain-core` (langgraph, via `tools/todos.py`), `botocore` (root,
via `scripts/utils.py`). All now declared; no resolved version changed.
- `aws-opentelemetry-distro` moves from a loose second `uv pip install`
into the locked set.
- The example root gets a project for the `scripts/` helpers. Their
`requirements.txt` listed the dependencies but nothing installed it, so
`uv run scripts/test-agent.py` — the command that script's own docstring
gives — failed on a missing `requests`.
- Deploy and local-dev scripts call `uv run` instead of bare `python3`;
preflight checks for `uv` rather than `python3`.

## Terraform

The docker-mode image hash read `patterns/<pattern>/requirements.txt`,
`patterns/utils`, a root-level `gateway/` and `tools/`, and a root
`pyproject.toml` — none of which exist here. `filesha256` on a missing
file is a plan-time error. Repointed at `agents/<pattern>` and
`agents/utils`, now hashing `pyproject.toml` and `uv.lock`.

The hash then had to exclude the virtualenv the migration creates:
`fileset(pattern_dir, "**/*.py")` saw 3114 files instead of 5, and the
computed hash differed depending on whether a developer had ever run the
agent locally — feeding `replace_triggered_by` and forcing a runtime
replacement. Measured: `5f9a98ef…` with a venv present vs `bacab1e1…`
without, on identical committed sources. The fix filters `.venv/` and
`venv/` and produces the clean-checkout digest in both cases.

## Review

Five review rounds plus a promotion audit, and nine defects this
migration introduced were found and fixed before merge. Each fix was
verified by running the thing, not by reading it:

- The image hash sweeping the local virtualenv (above).
- The undeclared direct dependencies (above).
- `scripts/test-agent.py` piped the child's output and never drained it,
deadlocking the agent, while the startup-timeout branch blocked forever
on a read — the only channel carrying uv's lockfile-drift error.
- `--local` treated any listener on port 8080 as the agent, so a foreign
process produced "Agent started successfully" while the real child died.
Adopting a running process is now explicit opt-in.
- The documented invocation contract had drifted across the two READMEs
and both scripts' usage text; a single reconciliation pass now owns all
of them, and every documented command was executed to confirm it works.
- The Terraform README named the CloudFormation deploy script, which
cannot read a Terraform deployment.
- Two `.env.example` entries parsed as their own trailing comment text
under Docker Compose.
- The build context shipped 898 MB of local-only artifacts (measured);
now 11 kB.
- Assorted comment and message inaccuracies, including a container env
block whose comment attributed a runtime-critical variable to uv.

## Repository hygiene

Two fixes the pre-merge gates surfaced rather than the review rounds:

- `oxfmt` on the example README and `tofu fmt` on the Terraform locals
file. The README break was introduced by this branch; the Terraform one
predates it but sits in a file this branch edits.
- The Terraform ignore rules were anchored to the top level, so the
provider cache `terraform init` writes beside every *nested* module was
fully stageable — a measured 834 MB one `git add -A` from being
committed. Switched to unanchored patterns, matching what this branch
already did for the virtualenv layout. Verified with `git check-ignore`
that the nested cache and lock file are now covered, tfstate and tfvars
still are, and no tracked file is caught by the wider patterns.

## Not addressed

Roughly 90 further findings are real but pre-existing and belong to four
follow-up subjects, listed in full in the review ledger:

- **Terraform module repair** — `terraform validate` fails on five
undeclared resources, so this module cannot plan at all. Zip mode
references an entry point and a packager directory that do not exist.
- **test-agent AG-UI correctness** — the request body fails
`RunAgentInput.model_validate` on both agents, and the response decoder
parses a pre-AG-UI format, so failures print nothing and exit 0.
- **README accuracy** — a `docs/` directory that does not exist, a wrong
Node floor, a teardown block whose second command never runs.
- **Deploy-script hardening** —
`infra-terraform/scripts/deploy-frontend.py` requires a Terraform output
nothing declares, so it exits 1 every run; the README now says so rather
than presenting it as a working path.

## Verification

Both images built for `linux/arm64` and the agent module imported inside
each, after every fix cycle. `uv lock --check` clean on all three
projects. Every documented command executed from its documented
directory. The virtualenv hash filter, the build-context reduction, the
pipe deadlock, the port-adoption fix and the env-file parsing were each
verified by measurement with before/after output. No repo CI job builds
or lints this example, so these local runs are the only coverage that
exists.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-26 21:36:43 +02:00
Ben Taylor 9e488f7a51 test(examples): gate the starters' Intelligence wiring block on one shape (closes OSS-982) (#6716)
**Merge order: #6711#6718 → this PR.**

#6718 rewrites `identifyUser` in the same 22 blocks and is a sibling of
this branch, not stacked on it, so the two overlap on the same lines.
Landing the gate last means it ratchets on the finished shape and avoids
a conflict. If this PR goes first instead, #6718 goes red until it moves
all 22 sites together — which is the gate working, but noisier.

Stacked on #6711 — merge that first. This branch descends from it, so
the diff below carries its commit too; GitHub drops those once #6711
lands.

Basing this PR on `main` rather than on #6711's branch is deliberate: 17
of 35 workflows filter `pull_request: branches: [main]`, including the
one this PR extends, so a PR based on the 981 branch would not run the
check it adds.

## Problem

The marked block that wires managed Intelligence is the region a hosted
reader copies verbatim, and nothing checked it. Both gaps are
deliberate, not accidental:

- `examples/integrations/_parity/manifest.json` lists
`src/app/api/copilotkit/**` under `allowedDivergence` for every instance
it tracks. What parity does hold byte-identical is the demo frontend: 54
verbatim paths of example canvas, todo columns and charts.
- No `examples/integrations/*/docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`. The wiring is a ternary on that variable, so
all 13 smoke-tested starters take the else arm. The `intelligence:` arm
has never executed in CI, in any starter.

The cost was already visible. The block's code was byte-identical in 21
of 22 starters, but its warning comment had drifted into five variants
and the two `ms-agent-framework-*` starters shipped the `demo-user` stub
with no warning at all. Comment drift is harmless by itself; it is the
tracer showing nothing held the region still, and it is how the
localhost default of #6711 survived in all 22 copies at once.

## Change

`scripts/validate-intelligence-wiring-block.ts` greps the opening
marker, compares every site against the north-star starter, and fails on
the first line that differs. Two normalisations keep it usable:

- The block is dedented, because `agentcore` nests it two levels deeper
— its runtime is a Lambda handler, not a Next.js route.
- The else arm's runner name is masked, because `agentcore` runs
`AgentCoreRunner` in front of a Bedrock AgentCore session where an
in-process runner has nothing to run. `EXPECTED_RUNNER` holds that one
exception, so a runner swapped in by accident still fails.

Everything else, comment text included, must match to the byte. Then the
warning is unified at all 22 sites on the fullest existing wording,
which also says the id must exist in Intelligence or thread operations
can fail.

It compares against the north star rather than a literal kept in the
script, so improving the block means editing `langgraph-python` and
running the other 21 to match.

## What it does and does not guarantee

It is a shape gate, not a content gate: 22 identically wrong copies
still pass. What it guarantees is that a fix reaches all of them or
none. The check passes on day one — 21 of 22 already matched on code —
so it is a ratchet, not a migration.

## Verification

Mutating a real starter three ways, each caught:

| Mutation | Reported as |
| --- | --- |
| Dropped one comment line | `line 15 differs from the north star`, exit
1 |
| \`InMemoryAgentRunner\` → \`SomeOtherRunner\` | `else arm uses
SomeOtherRunner; expected InMemoryAgentRunner` |
| Reintroduced \`?? \"http://localhost:4201\"\` | `line 6 differs`, both
sides shown |

The third matters: the #6711 regression is now caught at a second site,
independent of the env-name validator.

Commands run, all exit 0:

- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-wiring-block.test.ts
scripts/__tests__/validate-intelligence-env-names.test.ts` — 30 passed
- `pnpm check:intelligence-wiring-block` — `All 22 Intelligence wiring
sites match langgraph-python.`
- `pnpm check:intelligence-env-names` — unaffected, still canonical
- `pnpm parity:verify`
- `oxfmt --check`, `oxlint`, and `tsc --noEmit --strict` on the new pair

Two tests guard the gate against going vacuous: one asserts at least 22
marker files are discovered, so an empty violation list cannot pass on
an empty file list.

Not run locally: the lefthook pre-commit suite, which fails
environmentally in a worktree without per-package installs (`sh: vite:
command not found`). This diff touches no package source.

## Not covered

Enrolling the `intelligence:` arm in the smoke path. It needs a license
token in CI secrets and an endpoint reachable from the compose network —
a different size of job, tracked separately.
2026-08-26 14:36:24 -05:00
copilotkit-qa-bot[bot] b0908711ab Merge remote-tracking branch 'origin/main' into codex/fac-72-google-adk-stop-callback 2026-08-26 12:30:08 -07:00
copilotkit-qa-bot[bot] 2ba50f1476 docs(google-adk): keep callback setup concise 2026-08-26 12:29:46 -07:00
Maxim 888a70e169 Merge branch 'main' into chore/agentcore-uv 2026-08-26 21:28:39 +02:00
Ben Taylor 4b73ce3c83 fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981) (#6711)
## What does this PR do?

`CopilotKitIntelligence` is built to be correct when the caller says
nothing: omitting `apiUrl`/`wsUrl` resolves to
`https://api.intelligence.copilotkit.ai` and
`wss://realtime.intelligence.copilotkit.ai`, and its docstring says so
outright — *"leaving both unset is always correct against it."*

Every starter's runtime route defeated that default:

```ts
apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201",
wsUrl:
  process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401",
```

With the variables unset — the correct configuration for a managed user
— the `??` supplies localhost and the runtime aims at a local stack that
is not running. This is the artifact `copilotkit init` clones, so it is
the first thing a new managed user runs.

The starter's own `.env.example` already warns about exactly this
failure, two files away:

> `INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` point at a
self-hosted or local Intelligence deployment only — leave them unset
when using managed Intelligence, or the channel host and runtime will
try to reach a local stack that usually is not running.

So the documentation was right and the code contradicted it.
`channel-host.mts`, in the same directories, already had the correct
shape.

### The fix

**22 runtime wiring sites** (20 route handlers, `adk-angular/server.ts`,
and the AgentCore Lambda) now use the conditional spread these starters
already use in `channel-host.mts`, so a self-hosted override still works
and the managed default applies when absent:

```ts
...(process.env.INTELLIGENCE_API_URL
  ? { apiUrl: process.env.INTELLIGENCE_API_URL }
  : {}),
```

No hosted URL is written into the examples — the library already owns
them, so this is a deletion.

**3 `.env.example` files** (`agent-spec`, `llamaindex`, `mcp-apps`) set
the same values *uncommented*. Two do it directly beneath a comment
telling the reader to leave them unset, and an `.env.example` is copied
to `.env`, so these were the remaining route to a localhost value once
the code default was gone. Commented out to match the other nineteen
starters; `agent-spec` had no explanation at all and gets the standard
one.

**A guard**, added to the existing
`scripts/validate-intelligence-env-names.ts` rather than a new script —
it already polices the canonical Intelligence key name and the two dead
hosts, and its workflow is deliberately unfiltered so it sees every
README, example and skill. Two rules: `managedUrlFallback` (a `??`/`||`
default on either variable) and `managedUrlEnvFileAssignment` (an
uncommented env-example assignment). The rule is the *pattern*, not the
literal, so a staging host substituted for localhost fails the same way.

Five files legitimately want a local target and are allowlisted with
their reasons: the `playwright.config.ts` and `.env.example` of the
banking and reskinnable-demo showcases (own vendored compose ports
7050/7053 and 7250/7253, own seeded org keys) and
`agentcore/docker/.env.example` (the documented local development
stack). Resolving those to the managed hosts would aim an offline test
suite at production.

`scripts/__tests__` has no general runner, so the workflow runs this
test file explicitly, following the `plugin-skills-check.yml` precedent
— otherwise a rule that silently stopped matching would leave the check
passing on an empty result.

## Related PRs and Issues

- Closes OSS-981.
- Supersedes the canceled ENT-922, whose blocker ("do not invent hosted
URLs; rewrite once the managed env contract is final") no longer
applies: the contract shipped as `MANAGED_INTELLIGENCE_API_URL` /
`MANAGED_INTELLIGENCE_WS_URL`, and the fix removes a fallback rather
than adding a URL.
- ENT-949 shipped a warning for this class of mistake, but
`warnOnPartialHostOverride` only fires on a *partial* override — both
values defaulting to localhost together is not partial, so nothing
warned.

## Verification

- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-env-names.test.ts` — 13 passed.
Written first: the rules were red before they existed, then reported
**48 violations across 24 files** for the code rule and **10 across 5**
for the env-file rule; the fixes took both to green.
- `pnpm check:intelligence-env-names` — exit 0.
- `oxfmt --check` and `oxlint` over all 25 touched files — clean.
- The spread typechecks under `strict` + `exactOptionalPropertyTypes`,
the setting that would reject `apiUrl: string | undefined`.
- The marked wiring block stays byte-identical across 21 of 22 starters
(`agentcore` differs only in its runner), and no `localhost` remains
inside any marked block.
- Not run locally: the 13 starter Next builds. `test_smoke-starter.yml`
typechecks the route handlers in CI on this PR.

### Out of scope

`agentcore/docker/docker-compose.yml` keeps its
`${INTELLIGENCE_API_URL:-http://localhost:4201}`: it is compose
substitution in the documented local-dev stack, not shipped runtime
code. Separately that default cannot work anyway — inside the bridge
container `localhost` is the container's own loopback — but that is a
different bug.

## 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
- [x] "Allow edits by maintainers" is checked

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-26 14:28:28 -05:00
Maxim f8e13e675c Remove the banking showcase in favor of reskinnable-demo (#6683)
## What

Sunsets `examples/showcases/banking`. It is superseded by
`examples/showcases/reskinnable-demo`, which ships the same banking
experience as one of its runtime-swappable skins (alongside airline) on
top of a shared shell. Keeping both means maintaining two copies of the
same demo.

160 files deleted, plus the five places that pointed at the app:

| File | Change |
| --- | --- |
| `pnpm-workspace.yaml` | Drops the workspace entry. Also fixes the
adjacent NOTE, which attributed the canary AG-UI pin to "banking's
agent" when it is reskinnable-demo's own Python deep agent that needs
it. |
| `pnpm-lock.yaml` | Regenerated (−919/+12). The 12 additions are
peer-suffix re-keying caused by removing the importer — banking pinned a
different `eslint`/`vitest` peer combination. No dependency version
changes. |
| `examples/README.md` | The banking row becomes a reskinnable-demo row,
so the successor is listed and the showcase count is unchanged. |
| `showcase/shell-docs/src/content/docs/faq.mdx` | The "Banking
Assistant" link retargets to reskinnable-demo instead of 404ing. |
| `.github/config-allowlist.txt` | Drops the deleted `next.config.mjs`.
|

Note that banking was a real pnpm workspace member using `workspace:*`
deps, unlike reskinnable-demo, which sits deliberately outside the
workspace with its own lockfile. That is why the root lockfile has to be
regenerated here.

## Deliberately not changed

- `scripts/migrate-demos.sh` and `scripts/archive-demo-repos.sh` still
name `examples/showcases/banking`. Those are the already-executed
one-shot manifests for the repo consolidation; the path is a historical
record in them, not a live reference.
- `reskinnable-demo`'s `.env.example` and `docker-compose.yml` still
explain their +200 port offset in terms of banking's stack. The offset
stays real, and "was cloned from banking" stays true.
- The `banking` mentions in `test_reskinnable-demo.yml` refer to
reskinnable-demo's **banking skin**, not this app.

## Verification

- `.github/scripts/check-config-allowlist.sh` passes.
- A full `pnpm install` agrees with the regenerated lockfile (no further
diff).
- The lockfile-only regen and the full install produce identical output.

The `test-and-check-packages` pre-commit hook fires on any
`pnpm-lock.yaml` change, so it ran `test,publint,attw` across all 25
packages. Four suites failed locally — `sqlite-runner`, `web-inspector`,
`vue`, `react-core` — in a worktree installed with `--ignore-scripts`,
which skips `better-sqlite3`'s native build. This change touches no
package source, so CI is the gate on those; please confirm they are
green here before merging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-26 21:27:16 +02:00
copilotkit-qa-bot[bot] 7d3be75754 Merge remote-tracking branch 'origin/main' into codex/fac-72-google-adk-stop-callback 2026-08-26 12:25:50 -07:00
copilotkit-qa-bot[bot] 7a24a2d855 fix(ms-agent-python): migrate starter to stable APIs 2026-08-26 12:09:08 -07:00
Ben Taylor 61a67e716b fix(runtime): keep thread naming task after transcript (#6722)
## Summary

- place the thread-title task after the embedded conversation transcript
- explicitly tell the reused agent not to answer the conversation
- add a regression test that locks the prompt ordering

## Why

LangGraph starter agents can interpret the final embedded `user:` line
as the active request when the transcript comes last. They answer the
conversation instead of returning title JSON, causing retries and
eventual `Untitled` thread names.

## Validation

- reproduced with the latest LangGraph starter and
`@copilotkit/runtime@1.69.2`
- original prompt: 0/12 direct calls returned title JSON; 4/4 targeted
threads fell back to `Untitled`
- reordered prompt: 12/12 direct calls returned title JSON; 4/4 targeted
threads received generated titles
- runtime thread-name unit suite: 28/28 passing
- pre-commit affected package checks passing under the repository Node
22 toolchain
2026-08-26 13:44:47 -05:00
Martha Kelly Schumann 7bb8610030 fix(react-core): honor predictive state tool arguments (#6608)
## Summary

- honor the predictive-state `tool_argument` source field instead of
indexing tool arguments by destination `state_key`
- preserve the agent's existing state and combine every matching
same-tool configuration into one replacement-safe `setState` call
- preserve the SDK contract by storing the complete tool arguments when
`tool_argument` is omitted
- tolerate incremental JSON strings while ignoring unrelated tools and
malformed `PredictState` configuration
- merge current `main` and keep the final diff limited to the
PredictState implementation and regression tests in React Core's
`v1-deprecated` source boundary

## Validation

- predictive-state regression tests: 8/8 passed
- full React Core suite: 1,532 passed, 2 skipped; script tests: 47/47
passed
- dependency-aware React Core type-check: passed
- Web Inspector type-check from current `main`: passed
- React Core build: passed
- Python predictive-state control tests: 5/5 passed
- focused lint: 0 errors; formatting and `git diff --check`: passed

The final PR diff is limited to two React Core files.

FAC-122 is the canonical report; this also covers its existing
duplicates FAC-90 and FAC-107.

Linear: FAC-122
2026-08-26 11:39:00 -07:00
Maximiliano Korp eeb01fc33f fix(runtime): keep thread naming task after transcript 2026-08-26 11:28:30 -07:00
copilotkit-qa-bot[bot] 1ddc8d5454 Merge remote-tracking branch 'origin/main' into codex/fac-72-google-adk-stop-callback 2026-08-26 11:11:28 -07:00
copilotkit-qa-bot[bot] ad8f3f0a1b Merge remote-tracking branch 'origin/main' into codex/fac-122-predict-state-tool-argument 2026-08-26 11:00:48 -07:00
Benjamin Taylor 314f1ca55d fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981)
CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.

Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.

Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 12:56:36 -05:00
Tyler Slaton d917fef833 fix(web-inspector): restore Playground surface styling (#6721)
## Problem

The Inspector Playground rendered without its intended surface styling
in the React demo, leaving its header, welcome state, composer, and dark
mode looking flat and unfinished.

## Why

The Playground rules lived in `CpkThreadInspector.styles`, but the
Playground itself renders inside `WebInspectorElement`'s separate Shadow
DOM. Those selectors therefore could not reach the rendered Playground.

## Fix

- Move the Playground animation, responsive, and surface rules into the
owning Web Inspector Shadow DOM.
- Add explicit light/dark surface treatments for the header, welcome
state, and composer.
- Add a regression test that verifies the styles live in
`WebInspectorElement.styles`.
- Repair the current-main Inspector verification baseline for eager
message loading and strict indexed access.

Verified with the React demo and the root OpenAI API key, including a
successful Playground response. The Inspector package build, typecheck,
and all 608 tests pass.
2026-08-26 10:53:16 -07:00
copilotkit-qa-bot[bot] b04b47c932 fix(react-core): preserve predictive state updates 2026-08-26 10:44:39 -07:00
Tyler Slaton d44178a8f0 fix(web-inspector): match Playground composer surface 2026-08-26 19:31:36 +02:00
Tyler Slaton 76f3e9fff4 fix(web-inspector): repair Inspector verification baseline 2026-08-26 19:23:38 +02:00
Tyler Slaton edbbdbdf80 fix(web-inspector): restore Playground surface styling 2026-08-26 19:22:45 +02:00
copilotkit-qa-bot[bot] 6d6f59ee77 Merge main into FAC-122 predictive state fix
# Conflicts:
#	packages/react-core/src/v1-deprecated/components/__tests__/CopilotListeners.predictState.test.ts
2026-08-26 10:14:07 -07:00
Martha Kelly Schumann e1d31696f7 Fix blank Slack manifest usage hints (#6604)
## Summary
- omit blank optional `usage_hint` values from both distributed Slack
app manifests
- preserve the meaningful hints on `/agent` and `/preview`
- parse and validate both YAML and JSON manifests in a focused
regression suite
- assert their slash-command definitions stay in sync

## Validation
- `pnpm exec vitest run app/slack-app-manifest.test.ts` (7 tests)
- `pnpm exec nx run slack-example:test` (70 tests)
- `pnpm exec nx run slack-example:check-types`
- affected-package pre-commit matrix
- `git diff --check`

## Linear
-
[FAC-128](https://linear.app/copilotkit/issue/FAC-128/slack-example-manifest-yaml-rejects-due-to-empty-usage-hint-fields)
2026-08-26 10:07:58 -07:00
copilotkit-qa-bot[bot] e21af7f5c2 Merge main into FAC-128 Slack manifest fix
# Conflicts:
#	pnpm-lock.yaml
2026-08-26 09:53:09 -07:00
Alem Tuzlak 1a92a179f7 fix(web-inspector): polish inspector chrome, threads, and dark mode (#6713)
Open Inspector in dark mode and check Agent → Current Messages. Row
lines must be dark gray, not light.

This PR fixes Inspector chrome that was hard to read in dark mode. It
also shows live user turns on the Threads Messages tab, puts Collapse
under the Intelligence card, and sizes the collapsed sidebar.

## Testing

1. **Commands run.** `pnpm exec vitest run` on `thread-detail`,
`view-in-app`, `web-inspector`, and `inspector-navigation` in
`packages/web-inspector`. 128 tests passed after a 200ms wait on the
icon-rail agent flyout. The repo pre-commit Nx graph failed because
`.worktrees/` defines duplicate projects. That hook was skipped.

2. **Manual test.**
   1. Run `examples/v2/react-router` and open Inspector.
   2. Switch to dark mode.
3. Open Agent and look at Current Messages. Dividers must match the dark
card, not light gray.
4. Collapse the sidebar. The agent icon must match the other 36px tabs
and stay centered.
5. Open Settings → Privacy. There must be no extra line above Anonymous
usage analytics.

3. **How this PR makes testing easy.** Updated tests in
`thread-detail.spec.ts`, `view-in-app.spec.ts`, `web-inspector.spec.ts`,
and `inspector-navigation.spec.ts`. The react-router example publishes
app context for the Context tab.

## Risk / rollback

Low. UI and Inspector-only. Revert the PR to undo.
2026-08-26 18:40:01 +02:00
Alem Tuzlak 5a188d6783 fix(web-inspector): animate launcher hover scale and color 2026-08-26 09:25:41 -07:00
Alem Tuzlak 93861b428d fix(web-inspector): polish inspector chrome, threads, and dark mode 2026-08-26 09:25:41 -07:00
Tyler Slaton 51dc79268f feat(web-inspector): preview launcher HUD on load (#6715)
## Summary

Automatically preview the existing launcher HUD on every Inspector
mount, then fade it away after the feature statuses resolve.

## Why

The closed Inspector launcher did not draw attention to the feature
availability and enablement links already exposed on hover.

## How

- open the HUD after a short page-settle delay
- stagger the four rows and connected checks with compositor-only motion
- let hover or focus take control without an automatic close
- respect reduced motion and clean up mount timers
- cover the lifecycle with focused tests
2026-08-26 09:25:26 -07:00
Benjamin Taylor 84dd86f2ed test(examples): gate the starters' Intelligence wiring block on one shape (closes OSS-982)
The marked block that wires managed Intelligence is the region a hosted reader
copies verbatim, and nothing checked it. Both gaps were deliberate: the parity
manifest lists `src/app/api/copilotkit/**` under `allowedDivergence` for every
instance it tracks, and no `docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`, so every smoke-tested starter takes the else arm and
the `intelligence:` arm has never run in CI.

The cost was already visible. The block's code was byte-identical in 21 of 22
starters, but its warning comment had drifted into five variants and the two
`ms-agent-framework-*` starters shipped the `demo-user` stub with no warning at
all. That drift is how the localhost default of OSS-981 survived in all 22
copies at once.

Add `scripts/validate-intelligence-wiring-block.ts`, which greps the opening
marker, compares every site against the north-star starter, and fails on the
first line that differs. Two normalisations keep it usable: the block is
dedented, because `agentcore` nests it deeper, and the else arm's runner name is
masked, because `agentcore` runs `AgentCoreRunner` in front of a Bedrock session
where an in-process runner has nothing to run. Everything else, comment text
included, must match to the byte.

Then unify the warning at all 22 sites on the fullest wording, which also says
the id must exist in Intelligence or thread operations can fail.

The check passes on day one, so it is a ratchet rather than a migration. It is a
shape gate, not a content gate: 22 identically wrong copies still pass. What it
guarantees is that a fix reaches all of them or none.

Not covered: enrolling the `intelligence:` arm in the smoke path. That needs a
license token in CI and a reachable endpoint from the compose network, and is
tracked separately.
2026-08-26 11:16:00 -05:00
Tyler Slaton e8a5e00e8b feat(web-inspector): preview launcher HUD on load 2026-08-26 17:41:32 +02:00
Benjamin Taylor 8483f434f7 fix(examples): stop overriding the managed Intelligence URL defaults (closes OSS-981)
CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.

Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.

Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:59:23 -05:00
David McKay 21ac364918 fix(runtime): stop reporting every connect failure as a 404 (closes OSS-971) (#6708)
## Summary

The Intelligence connect handler classified a handful of platform
rejections and flattened everything else into `404 "Connect plan not
available"`, writing the real cause only to server-side stderr.

So a 500 from app-api, a socket timeout, a connection reset and a bug in
our own code all produced the same misleading answer, and the only way
to learn what actually happened was to read the runtime container's
logs.

## Why it matters

This came out of a production incident on a self-hosted deployment.
Redis filled, app-api returned 500 on a join-code write, and the
operator saw a 404 naming a "connect plan" that had nothing to do with
the failure. The platform was up and the request was retryable; the
reported status communicated neither, and pointed the investigation at a
missing thread instead. Diagnosing it took about a day, almost all of it
spent on the wrong hypothesis.

## What changes

| Case | Before | After |
| --- | --- | --- |
| Platform 400/401/403/404/409 | rejection + message | unchanged |
| Platform 500, 503, any other status | `404` "Connect plan not
available" | that status, with the message |
| No status: timeout, ECONNRESET, DNS, our own bug | `404` "Connect plan
not available" | `502`, with the message |

`502` for the no-status case says "the thing behind me is unreachable",
which is true and actionable. `404` asserts the thread does not exist,
which sends the caller to investigate the wrong thing entirely.

Every branch now returns the underlying message rather than burying it
in a log line the caller cannot see.

## Verification

- `handle-connect.test.ts` 20 passed, including three new cases:
platform 500 passes through, platform 503 passes through, and an
unreachable platform reports 502. The existing genuine-404 case is
asserted unchanged, so widening the pass-through does not blur a real
not-found with an unreachable platform.
- Red/green checked: reverting the handler while keeping the tests fails
3 of 20.
- `packages/runtime` v2 suite goes 1270 to 1273 passing, the three
additions. The 13 test files that fail to collect and the 26 `tsc`
errors are identical with and without this change; they are unbuilt
workspace deps in a fresh worktree, not related to it.
- Full lefthook pre-commit suite passes.

## Not addressed here

`handlers/shared/sse-response.ts` has the same shape: it returns `200`
and `text/event-stream` before the run starts, so a later throw closes
the stream with zero events and no error frame. From the client that is
indistinguishable from a silent no-op send. Fixing it means changing the
streaming contract, so it wants its own PR.

Related, same family of bug, different file: #6697.
2026-08-26 07:58:51 -07:00
Benjamin Taylor 6669b3a487 docs(react-core): state the blast radius of key-remount and the provisional agent (refs OSS-979)
An Intelligence integration lost a request-to-row correlation map partway
through a user interaction, with no error and no warning. It surfaced as
"our response routing is flaky". OSS-979 filed it as CopilotKitProvider
remounting its children.

The provider does nothing of the kind. It renders `{children}`
unconditionally, unkeyed, with no early return and no Suspense boundary
anywhere in v2. The remount was app-side, and it was app-side because this
skill told it to be:

* `references/threads.md` teaches `useThreads()` -> select ->
  `<CopilotChat key={activeId}>`, and that recipe is only reachable once
  Intelligence is wired.
* `references/switching-agents.md` teaches "`key={activeAgent}` forces
  remount so thread state doesn't leak" without saying what else that
  discards.
* `examples/showcases/reskinnable-demo/src/app/[skin]/layout.tsx:223`
  models `<SubagentActivityProvider key={threadId}>` above `{children}`,
  commented "Remounting is deliberate".

Follow all three and you key a layout-level provider on a thread id that
changes asynchronously after mount. Everything below it dies
mid-interaction.

Two properties made it invisible. Durable threads exist only in
Intelligence mode, so in OSS-only development the selected thread never
changes and the remount never fires. And whether state survives depends on
whether the user acted before the thread list resolved.

Both traps now carry their blast radius, in the four places an agent
actually reads:

* `SKILL.md` -- two invariants in the load-once section, so they land
  before any reference is opened.
* `references/threads.md` -- a HIGH entry on keying above app state, plus a
  note that `activeId` in the switcher recipe settles asynchronously.
* `references/switching-agents.md` and `switching-agents-recipes.md` --
  keep the `key` on `<CopilotChat>`, never on a wrapper or a layout
  provider.
* `references/agent-access.md` -- the second route to the same symptom.
  `useAgent` swaps a provisional stand-in for the real agent when `/info`
  resolves, so an effect keyed on `agent` re-runs once, mid-interaction.
  Adds an `isReady` pattern and a HIGH entry. `isReady` appeared in zero
  shipped skills before this; it was documented only in shell-docs and in
  JSDoc.

Also corrects a factual error: the skill claimed `useAgent` returns
`{ agent }` only. It returns `{ agent, isReady }`.

No library change. The provider behaves correctly; the guidance did not
describe what it costs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:58:28 -05:00
David McKay b4145f42fc fix(runtime): stop reporting every connect failure as a 404 (closes OSS-971)
The Intelligence connect handler classified a handful of platform rejections and
flattened everything else into HTTP 404 "Connect plan not available", writing the
real cause only to server-side stderr.

That made every unrecognised failure look like a missing thread. A 500 from
app-api, a socket timeout, a connection reset and a bug in our own code all
produced the same misleading answer, and the only way to find out what actually
happened was to read the runtime container's logs.

It cost a customer a day. Their Redis filled, app-api returned 500 on a
join-code write, and their engineer saw a 404 naming a "connect plan" that had
nothing to do with the failure. The platform was up and the request was
retryable; the reported status said neither.

Now:

  - a status we already special-case (400, 401, 403, 404, 409) still reports as
    a rejection with its message, unchanged;
  - any other status from the platform passes through as itself, so a 503 stays
    a 503 and the caller knows to retry; and
  - an error carrying no status never reached the platform, so it reports 502
    rather than 404, which says "the thing behind me is unreachable" instead of
    asserting the thread does not exist.

Every branch now returns the underlying message rather than burying it in a log
line the caller cannot see.

The SSE run path in handlers/shared/sse-response.ts has the same shape and is not
addressed here: it returns 200 and text/event-stream before the run starts, so a
later throw closes the stream with no events and no error frame. That needs its
own change to the streaming contract.
2026-08-26 07:55:31 -07:00
Alem Tuzlak b5d037545f fix(examples): add missing zod for the OpenRouter demo-server (#6704)
The Angular OpenRouter demo-server crashed on chat because
`@ai-sdk/openai` imports `zod/v4` and the server did not declare `zod`.
This PR adds that dependency so pnpm can put `zod` next to the server on
Windows.

## What does this PR do?

Adds `zod` to `examples/v2/angular/demo-server`. That is the OpenRouter
`createOpenAI` path. The React demo already had this dependency.

## Related PRs and Issues

- Inspector test path: local `main` plus the React Router example also
has `zod` linked so chat can run while this PR is open.

## Checklist

- [ ] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)

## Testing

1. **Commands run.** I did not run the monorepo test suite. This change
is one private example `package.json` plus the lockfile importer line. I
did confirm Node can resolve `zod/v4` from `@ai-sdk/openai` after `zod`
is linked into the example `node_modules`.
2. **Manual test.**
1. On current `main` (before this change), run
`examples/v2/angular/demo-server` with `OPENROUTER_API_KEY` set. Send a
chat message. The process fails with `Cannot find module 'zod/v4'`.
   2. Check out this branch and run `pnpm install`.
3. Start the demo-server again and send a chat message. The OpenRouter
call starts. No `zod/v4` error.
3. **How this PR makes testing easy.** The OpenRouter example now
declares the same `zod` peer the React demo already had.

## Risk / rollback

Low. The change is a private example dependency. Revert the PR to undo
it.
2026-08-26 12:38:26 +02:00