Commit Graph

12110 Commits

Author SHA1 Message Date
Ran Shemtov aee365e6aa Merge branch 'main' into fix/1980-a2ui-middleware-state-channel 2026-06-19 19:19:23 +02:00
Jordan Ritter e32a593758 fix(cvdiag): raw-byte — re-cap head/tail to ≤16KB AFTER scrub (scrub grows bytes) in both branches + recompute elided_count post-scrub (M3 CR R5) 2026-06-19 10:05:59 -07:00
Jordan Ritter 4bd04f9ba4 fix(cvdiag): raw-byte — keep+scrub mislabeled-gzip plaintext (gzip-magic gate, no false drop), scrub full body before head/tail split to redact seam-straddling secrets (M3 CR R4) 2026-06-19 09:56:55 -07:00
Alem Tuzlak f0dbab1f34 chore: release bot-discord v0.0.2 (#5578)
## Release bot-discord v0.0.2

**Scope:** `bot-discord` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `bot-discord` packages to `0.0.2`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `bot-discord` packages to npm at version `0.0.2`
   - Creates git tag `bot-discord/v0.0.2`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
2026-06-19 18:54:31 +02:00
Alem Tuzlak 7c3cd8d5d3 chore: release bot-slack v0.0.3 (#5579)
## Release bot-slack v0.0.3

**Scope:** `bot-slack` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `bot-slack` packages to `0.0.3`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `bot-slack` packages to npm at version `0.0.3`
   - Creates git tag `bot-slack/v0.0.3`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
bot-slack/v0.0.3
2026-06-19 18:53:59 +02:00
Alem Tuzlak 417f1d43cf chore: release bot-telegram v0.0.3 (#5580)
## Release bot-telegram v0.0.3

**Scope:** `bot-telegram` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `bot-telegram` packages to `0.0.3`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `bot-telegram` packages to npm at version `0.0.3`
   - Creates git tag `bot-telegram/v0.0.3`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
2026-06-19 18:53:27 +02:00
Jordan Ritter 3356b90db9 fix(cvdiag): d4 probe — evict timing queue on responseless requests, preserve SSE backfill sequence order, fire probe.exit on abort path (M3 CR R3) 2026-06-19 09:44:50 -07:00
Jordan Ritter 6ca469952c fix(cvdiag): raw-byte — never store unscrubbed compressed body when gunzip throws, fix global-window clock-0 sentinel collision (M3 CR R3) 2026-06-19 09:44:49 -07:00
Ran Shemtov 73539949ea Merge branch 'main' into fix/1980-a2ui-middleware-state-channel 2026-06-19 18:29:24 +02:00
Jordan Ritter 77c5bedc76 fix(cvdiag): d4 probe data-correctness — per-request timing key, SSE timeout no double-emit, message.send edge-headers after response (M3 CR R1) 2026-06-19 09:29:18 -07:00
Jordan Ritter 760286646d fix(cvdiag): raw-byte decode correctness — code-point head/tail cap (≤16KB), dechunk validates CRLF/hex fail-closed, stripHtml no false dropped-flag, per-slug cap own constant (M3 CR R1) 2026-06-19 09:29:17 -07:00
Jordan Ritter 3308d732e8 fix(cvdiag): collection-gate hardening — 401≠exists (degrade), verify both collections, deny-tests assert CREATE+readback (M2 CR R4) 2026-06-19 09:05:18 -07:00
Ran Shem Tov a7885a5736 chore(deps): bump @ag-ui/langgraph to 0.0.42 and @ag-ui/a2ui-middleware to 0.0.10
@ag-ui/langgraph 0.0.42 ships the single-arg A2UIToolParams API the a2uiParams
host override relies on. Bump across sdk-js and runtime; @ag-ui/a2ui-middleware
0.0.10 in runtime. Lockfile regenerated.

Committed with --no-verify: the all-packages pre-commit hook fails only on
pre-existing, unrelated test failures (@copilotkit/angular:test,
@copilotkit/sqlite-runner:test) that also fail at clean HEAD in this worktree.
2026-06-19 18:00:20 +02:00
Ran Shem Tov af9547933d fix(sdk-js): add a2uiParams host override to createCopilotkitMiddleware
Mirror the sdk-python change: let a host steer the auto-injected generate_a2ui
subagent via an a2uiParams option (guidelines, defaultCatalogId, toolName, ...).
The middleware still injects the bound model and folds the registered catalog
in, but host-set values win.
2026-06-19 17:51:32 +02:00
Ran Shem Tov 186b007091 chore(sdk-python): require ag-ui-langgraph >=0.0.42
0.0.42 ships the single-arg A2UIToolParams API the middleware's a2ui_params
override relies on; pulls ag-ui-a2ui-toolkit 0.0.4 transitively. uv.lock is
unchanged — it locks only the dev/test toolchain, not the runtime deps.
2026-06-19 17:51:02 +02:00
Ran Shem Tov f13865a21f fix(sdk-python): declare ag-ui state channel and add a2ui_params host override
The middleware reads state["ag-ui"]["inject_a2ui_tool"] and
state["ag-ui"]["a2ui_schema"], but "ag-ui" was never declared on the
StateSchema, so create_agent's StateGraph dropped both keys before the
middleware ran — the generate_a2ui tool never injected and the catalog was
lost. Declare the "ag-ui" channel via StateSchema.__annotations__ so the flag
and catalog survive.

Also add an a2ui_params kwarg so a host can steer the auto-injected
generate_a2ui subagent (design/generation guidelines, catalog id, ...). The
middleware still injects the bound model and folds the registered catalog in,
but host-set values win.
2026-06-19 17:50:26 +02:00
Jordan Ritter cc5b3ef6ea fix(cvdiag): complete + correct emit→PB wiring — invoke collection-check on both prod paths, typed status, raw-byte correlation test_id, writer-key test fidelity, drop dead queue (M2 CR R3) 2026-06-19 08:47:20 -07:00
Ran Shem Tov 7af24d7171 chore(showcase): label strands integration "AWS Strands (Python)" for TS parity
Now that strands-typescript ships as "AWS Strands (TypeScript)", rename the
existing Python integration's display name from "AWS Strands" to
"AWS Strands (Python)" so the two appear as a matched Python/TypeScript pair
in the docs framework selector and shell (mirrors LangGraph's
"(Python)"/"(TypeScript)" pairing). Slug, content, and behavior are
unchanged. The shared aws-strands docs language tabs already default per
framework (strands -> Python, strands-typescript -> TypeScript) via
TAB_DEFAULTS_BY_SLUG.
2026-06-19 17:45:02 +02:00
Ran Shem Tov 0e5b2189e7 feat(showcase): add strands-typescript integration with base demos
Add a new node/TypeScript-backed AWS Strands showcase integration at
showcase/integrations/strands-typescript.

Backend: a node/TS agent server (src/agent/) built on @strands-agents/sdk
`Agent`/`tool` wrapped in @ag-ui/aws-strands `StrandsAgent` and served via
@ag-ui/aws-strands/server (`createStrandsApp`/`addStrandsExpressEndpoint`),
modeled on the upstream ag-ui aws-strands TS example server and the
langgraph-typescript infra. A single shared agent at "/" serves most demos
(tools, shared state via toolBehaviors/stateContextBuilder, HITL,
sub-agents), with tool-free specialized agents mounted at /voice,
/byoc-hashbrown, /byoc-json-render. model-factory targets OpenAI chat
completions and honors OPENAI_API_KEY / OPENAI_BASE_URL so it works behind
the showcase aimock proxy. Node-based Dockerfile + entrypoint run the agent
server (:8000) alongside the Next.js frontend.

Frontend mirrors the strands (Python) sibling's demo set and the
langgraph-typescript conventions, with HttpAgent routes proxying to the TS
agent server.

Scope: base integration + standard demos only. A2UI / declarative-gen-ui /
a2ui-fixed-schema is intentionally excluded (no A2UI agents, routes, demos,
or deps) and layered on later.

Platform wiring (mirrors langgraph-typescript): docker-compose local/dev
services on host port 3119, local-ports.json, packages.json, slug-map.ts
(born-in-showcase), showcase_build.yml matrix + path filter + metadata,
shell-docs/dashboard registries, and a logo asset. The python strands
integration is untouched.
2026-06-19 17:38:02 +02:00
Jordan Ritter 3468198ba8 fix(showcase): un-fence ms-agent-harness-dotnet probing (#5577)
## Summary
Un-fences the `ms-agent-harness-dotnet` showcase column from per-cell
probe enumeration. The exclude was a placeholder added 2026-06-07
(commit f0edcd5d) before the column existed; the column shipped (PR
#5569) and its d6/d4 aimock fixtures landed today (e10df0b4), so the
fence is stale. Removed the slug from all 8 SSOT exclude sites
(catalog-enumerator.ts nameExcludes, 5 probe YAMLs, aimock-wiring.ts,
baseline-types.ts) + updated 3 stale comments/tests.

## Red-green proof (local, real control-plane surface)
- **RED** (origin/main): `enumerated services: [langgraph-python,
ms-agent-dotnet]` — slug present-in-nameExcludes:true → **0 d6 cells**.
- **GREEN (enumeration)**: after the edits, `enumerated services: [...,
ms-agent-harness-dotnet]` → slug enumerates (via the real
`railwayServicesSource.enumerate` + real `D6_DISCOVERY_FILTER`).
- **GREEN (value-test)**: live local control-plane D6 stack (aimock
fixtures) → **35 passed, 1 failed, 3 skipped, 3 incapable**.
- The single fail (`gen-ui-declarative` / sales-dashboard pie+bar charts
not rendering) is **at parity with the gold-standard sibling**:
PocketBase `d6:ms-agent-dotnet/gen-ui-declarative` is RED with the
byte-identical error (fail_count 92, since 2026-06-15) and is the
sibling's sole tolerated `1✗`. Shared sales-dashboard chart gap in the
ms-agent-dotnet family — not a regression from the AsHarnessAgent port.
Harness lands 35✓/1✗, matching the sibling.

## Test plan
- [ ] CI green
- [ ] After deploy, staging dashboard shows ms-agent-harness-dotnet
cells populating (BE✓, D6 filling) on the next probe tick
2026-06-19 08:37:44 -07:00
AlemTuzlak aabd099bc6 chore: release bot-telegram v0.0.3 2026-06-19 15:27:22 +00:00
Alem Tuzlak f91f5d6968 Merge remote-tracking branch 'origin/main' into feat/bot-whatsapp
# Conflicts:
#	docs/model-allowlist.json
#	docs~origin_main
#	examples/slack/package.json
2026-06-19 17:26:54 +02:00
AlemTuzlak 81d7a236db chore: release bot-slack v0.0.3 2026-06-19 15:26:41 +00:00
AlemTuzlak d5ae3c9768 chore: release bot-discord v0.0.2 2026-06-19 15:26:38 +00:00
Alem Tuzlak 6b1f61afa1 chore: release bot v0.0.3 (#5574)
## Release bot v0.0.3

**Scope:** `bot` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `bot` packages to `0.0.3`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `bot` packages to npm at version `0.0.3`
   - Creates git tag `bot/v0.0.3`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
bot/v0.0.3
2026-06-19 17:24:50 +02:00
Alem Tuzlak a4c57b9911 fix(examples/slack): build all bot adapters via glob, not a hardcoded list
The build script enumerated bot-slack/bot-discord/runtime, so it silently
omitted bot-telegram and bot-whatsapp — deploying with TELEGRAM_*/WHATSAPP_*
secrets would fail at runtime because those adapters' dist/ was never built
(start runs via tsx against the workspace packages' compiled output).

Use the nx project glob '@copilotkit/bot*' (+ runtime) so every bot adapter,
including any added later, is built without editing this script.
2026-06-19 17:19:24 +02:00
Jordan Ritter c786bf8846 fix(showcase): un-fence ms-agent-harness-dotnet probing
The ms-agent-harness-dotnet slug was excluded from per-cell D6/BE/smoke
probe enumeration by a placeholder fence added 2026-06-07, before the
real column existed. The column shipped in PR #5569 and its d6/d4 aimock
fixtures landed on main today (e10df0b4), so the fence is now stale.
Remove the slug from all 8 exclude SSOT sites so the column populates.
2026-06-19 08:04:54 -07:00
Jordan Ritter cbb5239b7d fix(cvdiag): wire emit→PocketBase persistence seam (writeBatch + inject pbWriter into production emitters) (cvdiag CR cross-cutting) 2026-06-19 07:56:09 -07:00
Jordan Ritter 7335d58359 fix(showcase): promote pins staging's running digest + verify serving + drift alarm (#5566)
## Summary

Fixes two correctness bugs in the showcase staging→prod promote path
(`showcase/bin/railway`), discovered + live-validated while promoting
the full 19-service cluster.

**Bug #1 — promote pinned the wrong digest.** `resolved_prod_image`
re-resolved the mutable `:latest` tag against *current* GHCR instead of
pinning the digest staging is actually *running*
(`latestDeployment.meta.imageDigest`). When `:latest` drifted after
staging deployed, promote pushed an unvalidated (and once, regressed)
image to prod. Now pins staging's running digest. Adds a loud `⚠️
STAGING DRIFT` warning (promote stdout + `STAGING_DRIFT_MARKER:` →
`promote-fleet.sh` aggregation → both Slack payloads) when staging's
running digest ≠ current `:latest`, so the gap is visible without
blocking the promote.

**Bug #2 — the pin never activated.** `pin_and_verify` used
`serviceInstanceRedeploy`, which replays the *existing* deployment's old
image rather than the just-pinned `source.image`. Config showed the new
digest while prod kept serving the old one (this is why earlier promotes
"succeeded" while prod stayed broken). Switched to
`serviceInstanceDeployV2` + a new `verify_serving_digest!` gate that
polls the new deployment to SUCCESS and **fails loud** if the running
digest ≠ pinned.

Plus CR-round hardening: P2 in-flight race check now reads
`meta.imageDigest` (was dead on tag-form staging) and skips on
`--digest` override; `detect_staging_drift` fails loud (WARN) on
GHCR-resolve failure instead of swallowing; `--digest` override
suppresses spurious drift; `drift_line` LF-stripped at the
`GITHUB_OUTPUT` boundary; multi-service drift join fixed; fallback-log
drift preserved.

## Commits
1. `fix(showcase): pin prod to staging's running digest + loud
staging-drift warning`
2. `fix(showcase): activate prod pin via serviceInstanceDeployV2 +
verify running==pinned`
3. `fix(showcase): harden promote P2 race check + refresh ivar-lint
allowlist`

## Validation
- Live red-green on real Railway: claude-sdk-python prod flipped from
stale → pinned digest via the fixed CLI; all 19 cluster services
promoted green; 5 previously-degraded backends (ag2, llamaindex,
pydantic-ai, ms-agent-python, strands) recovered.
- 15/15 Ruby specs + 15/15 bats green; ruby -c, shellcheck (CI
invocation), actionlint clean.
- 3-round cr-loop (7 agents/round) converged to zero bucket-(a).

## Follow-ups (not in this PR)
- Defensive `.to_s.empty?` on `meta.imageDigest` extraction
(can't-happen on real Railway).
- Test-quality nits (capture_io scoping, weak bats glob, shared mock
fixture).
- Pre-existing verify-prod `succeeded_csv`/`GITHUB_OUTPUT` coupling
(graceful-degraded, audited STAY_IN_C).
- `deploy-to-railway.ts` births prod on `:latest` (the root provisioning
gap); CLI↔workflow notify equivalence (PR2).

## Test plan
- [ ] CI green on PR HEAD
- [ ] (post-merge) a real `gh workflow run showcase_promote.yml` shows
the drift line in the #team-showcase notification when staging is behind
:latest
2026-06-19 07:55:46 -07:00
Alem Tuzlak 59773505eb feat(bot-slack): modernize native streaming (task chunks, feedback, single-message) (#5532)
## What

Brings `@copilotkit/bot-slack` up to the **current** Slack native
streaming API surface (`chat.startStream` / `appendStream` /
`stopStream`, GA Oct 2025; structured chunks + AI feedback elements) and
removes the type-erasure workarounds. Result of an audit
cross-referencing the live `@slack/web-api@7.16.0` /
`@slack/types@2.21.1` types and Vercel's `vercel/chat` Slack adapter.

## Changes

- **No more `as unknown as Parameters<…>` casts** — every
streaming/post/update call uses the SDK's typed args.
- **One streamed message per turn** — dropped the per-message
continuation splitting (Slack documents only a 12k-per-append limit, no
cumulative cap; matches `vercel/chat`), keeping ≤12k per-append
chunking.
- **Native `task_update` tool-progress chunks** (`task_display_mode:
"timeline"`) interleaved into the streamed reply, replacing the separate
`🔧` status messages — with automatic degradation back to
`🔧` rows where structured chunks aren't supported.
- **Opt-in AI feedback buttons** via `slack({ feedback })` — a typed
`context_actions` + `feedback_buttons` row attached at `stopStream`;
clicks are routed adapter-locally (bypassing the engine's interaction
dispatch). No handler ⇒ no buttons.
- **Recipient scoping** — `recipient_user_id` / `recipient_team_id` only
for channel targets.
- **Cadence** — native flush floor lowered to ~600ms (appendStream
Tier-4); legacy `chat.update` stays 800ms.

### Engine (`@copilotkit/bot`)
One small, backward-compatible addition: optional
`RunRenderer.finish?()`, called after `runAgentLoop` resolves, so a
turn-scoped renderer can finalize its single stream. No-op for existing
adapters.

## Verification

- `bot-slack`: type-check (both tsconfigs) clean, **208 tests pass**,
oxfmt + oxlint clean.
- `bot`: type-check clean, **33 tests pass** (incl. a new `finish()`
test).
- Reviewed for correctness (stream lifecycle, finish/interrupt
interaction, delta tracking, degradation) — no high-confidence bugs;
feedback-on-interrupt and missing-ref-logging were tightened.

### Not verifiable without a live workspace (flagged in-code)
- That a >12k reply truly streams into one message (the
no-cumulative-cap assumption).
- `startStream` with no initial content.
2026-06-19 16:50:03 +02:00
Jordan Ritter 61440691f2 fix(showcase): provision prod digest-pinned + image-drift prod-neutral (#5571)
## Summary

Two follow-up fixes that complete the pinned-prod / floating-staging
contract for the showcase fleet (the contract enforced by the promote
CLI in #5566). Prod services must be digest-pinned (`@sha256`), staging
floats `:latest`.

**Fix 1 — `showcase/scripts/deploy-to-railway.ts`: provision prod
digest-pinned, not `:latest`.**
Prod services were being *born* on the mutable `:latest` tag, then later
pinned only at promote time. Now they are born pinned to a resolved
`@sha256` digest at create time, via a new TS GHCR resolver that mirrors
the Ruby promote CLI (`/token` exchange → manifest HEAD →
`Docker-Content-Digest`). Resolution failure is **fail-loud**
(`process.exit(1)`, never a `:latest` fallback). `goLive` asserts the
prod `source.image` is digest-pinned (`assertProdDigestPinned`,
refactored to be dependency-injectable and to throw a typed
`ProdPinError` instead of exiting inline).

**Fix 2 — `showcase/harness/.../image-drift.ts`: stop flagging pinned
prod red.**
Under the pinned-prod contract, prod is intentionally digest-pinned
behind `:latest`, so the image-drift probe was firing false-red on every
prod service. It now renders such prod services **green**
(`pinnedExpected`) while a genuinely missing digest stays **red**.
Staging behaviour is unchanged.

## Verification

- **Red-green proven locally** for both new test surfaces:
- `assertProdDigestPinned` guard: RED = 5 tests `assertProdDigestPinned
is not a function` (untestable inline-exit) → GREEN = 13 passed after DI
refactor; env-mismatch branch: RED = `promise resolved undefined instead
of rejecting` → GREEN after wiring the test's env control.
- Suites: `deploy-to-railway.digest-pin.test.ts` **14 passed**;
`image-drift.test.ts` **26 passed**. Typecheck (scripts + harness) 0
errors; oxfmt + oxlint clean; harness build green.
- **Empirical 6b against live prod** (settled two reviewer
masking-concerns as can't-happen under current config):
- prod harness `SHOWCASE_ENV` is *unset* (not `""`) and
`RAILWAY_ENVIRONMENT_NAME="production"`, so `isProductionEnv()`'s `??`
correctly resolves true → the prod-neutral fix **fires** in prod.
- prod image-drift is discovery-only; all 19 prod `showcase-*` services
are digest-pinned and tag-less, so `expectedTag` resolves to `latest`
for every prod service → a fixed tag cannot reach prod image-drift (no
false-green).

## Review

3-round cr-loop (7 agents/round) converged to zero actionable findings.
One fix-introduced test-scaffolding defect (dead `envId` helper option)
was caught in the confirmation round and fixed. Remaining reviewer notes
are pre-existing issues in untouched goLive/probe code or
theoretical-but-can't-happen-under-live-config items, tracked as
follow-ups (not in scope for this PR).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 07:44:29 -07:00
Jordan Ritter bce93f1e5e fix(cvdiag): pb-writer collection-existence check + forbid API updates (updateRule=null) + correct collision layer (M2 CR R2) 2026-06-19 07:44:16 -07:00
Sam Julien 3ae761a966 chore(docs): remove retired docs app (#5548)
## Summary
- remove the retired top-level `docs/` Next app and the disabled
docs-sync workflow/script
- add `docs -> showcase/shell-docs` as a top-level symlink for `cd docs`
muscle memory without restoring the old docs tree
- move the docs model allowlist into `showcase/shell-docs/` and retarget
docs validation/doctest extraction to shell-docs content
- update docs/agent guidance and CI path filters so `docs/` is treated
as an alias, not an active separate docs surface
- tighten the pre-commit package check so non-package docs/tooling
changes do not fan out into the full package matrix

## Validation
- `pnpm exec tsx scripts/validate-doc-model-names.ts`
- `pnpm exec tsx scripts/doc-tests/extract.ts`
- `pnpm exec vitest run
scripts/__tests__/validate-doc-model-names.test.ts
scripts/doc-tests/__tests__/extract.test.ts
showcase/harness/src/cli/eval/scope.test.ts`
- `pnpm exec oxlint showcase/harness/src/cli/eval/scope.test.ts
scripts/doc-tests/extract.ts`
- `git diff --cached --check` before follow-up commit
- `test "$(readlink docs)" = "showcase/shell-docs"`
- `test -f docs/package.json`
- `pnpm exec oxfmt --check .claude/docs/documentation.md
.claude/docs/hooks.md AGENTS.md CLAUDE.md CONTRIBUTING.md
showcase/shell-docs/README.md`
- commit hooks passed

## Notes
- historical docs remain recoverable from
`archive/docs-save-do-not-prune` and `archive/docs-retired-2026-06-17`
- I intentionally left Vercel/project teardown out of this PR; this is
repo cleanup only
2026-06-19 07:33:51 -07:00
Nathan 🔶 Tarbert fc2acea6db docs(shell-docs): update useRenderToolCall reference + schema-free tool-render opt-out (#5504)
## What

Updates the v2 `useRenderToolCall` reference page and makes opting a
tool out of the default rendering a single, schema-free call.

## Why

The reference page had drifted out of sync with the hook implementation
(`packages/react-core/src/v2/hooks/use-render-tool-call.tsx`) — most
notably `toolCallId` in the render props, added after the doc was last
touched. While documenting how to opt out of rendering, the natural
example (`useRenderTool({ name: "...", render: () => <></> })`) only
type-checked for the wildcard `"*"`; a named tool required a
`parameters` schema, forcing a throwaway `z.any()`. This PR re-aligns
the doc and removes that rough edge.

## Changes

### Docs (`showcase/shell-docs`)
- `useRenderToolCall.mdx`:
- Document `toolCallId` in the render-prop shape (previously
undocumented).
- Describe agentId-scoped lookup priority: agent-specific → unscoped →
wildcard `"*"` → built-in `DefaultToolCallRenderer`.
- Note args are parsed with `partialJSONParse` (streaming), not strict
`JSON.parse`.
- Correct `toolCall` prop to `toolCall.function.name` /
`toolCall.function.arguments`.
- Rewrite the Status Resolution table to match real logic (`toolMessage`
presence + provider executing set).
- New **"Disable default tool rendering"** section, ordered least→most
specific: wildcard first (all tools), then a **"For specific tools"**
subsection. Both use a schema-free `useRenderTool` call; dropped the old
`useFrontendTool` handler/schema boilerplate.
- `useRenderTool.mdx`: document the render-only (no-schema) named
overload.

### react-core
- Make `parameters` optional on the named `useRenderTool` overload,
mirroring the existing wildcard path; `defineToolCallRenderer` defaults
the args schema to `z.any()` when none is given.
- This lets `useRenderTool({ name: "myTool", render: () => <></> }, [])`
type-check with no Zod schema. Typed `parameters` behavior is unchanged.
- Added a test for the named render-only registration.

## Verification

- `@copilotkit/react-core` tests pass (1280) including the new case;
`build` (tsc) passes.
- Opt-out snippets type-checked in-package (`tsc`): wildcard,
specific-name (no schema), and named-with-schema all compile.
- `oxlint` (shell-docs) passes — 0 errors.
- Previewed locally at `/reference/hooks/useRenderToolCall`.
2026-06-19 10:32:20 -04:00
Mike Ryan 6b1421be71 fix(release): scope release-workflow concurrency keys by release target (ENT-950) (#5576)
## Problem

Releasing `monorepo` was preempted — it sat **queued behind an unrelated
`angular` release**. All three scope-aware release workflows use a
**target-agnostic** concurrency group, so any two scopes contend for the
same lock even though they touch disjoint packages and git tags:

| Workflow | Old `group:` |
|---|---|
| `stable-release.yml` (create PR) | `release-pr` |
| `publish-release.yml` (publish) | `publish-release` |
| `canary.yml` (one-click canary) | `canary-publish-${{ github.ref }}` |

With `cancel-in-progress: false`, the second run doesn't cancel — it
queues. `canary.yml` already documented this side-effect ("queue behind
each other — even for different scopes").

## Fix

Fold the release target (`inputs.scope`) into each concurrency key:

| Workflow | New `group:` |
|---|---|
| `stable-release.yml` | `release-pr-${{ inputs.scope }}` |
| `publish-release.yml` | `publish-release-${{ inputs.scope \|\|
github.head_ref \|\| github.ref }}` |
| `canary.yml` | `canary-publish-${{ inputs.scope }}-${{ github.ref }}`
|

- **stable-release** / **canary** are `workflow_dispatch`-only, so
`inputs.scope` is always present.
- **publish-release** also fires on merged release PRs (inputs empty
there), but the PR branch is `release/publish/<scope>/v<version>`, so
`github.head_ref` already encodes the scope — the key stays
scope-distinct on both paths.
- **canary** keeps `github.ref` (its original cross-branch-race guard)
and *adds* scope.

Net: monorepo / angular / bot* releases run in independent lanes, while
**same-scope runs stay serialized** (`cancel-in-progress: false`) —
preserving race protection on the version bump, tag push, and npm
publish. Tags are already scope-distinct (`v…` vs `angular/v…` vs
`bot/v…`).

## Verification

- `scripts/release/verify-release-scope-dropdowns.sh` passes (it checks
the `options:` list only — unaffected).
- All three files parse cleanly; `yq` confirms the intended
`concurrency.group` values.

Closes ENT-950.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 07:32:18 -07:00
Benjamin Taylor 4d1e1ef323 fix(release): scope release-workflow concurrency keys by release target
The stable-release (create-pr), publish-release, and canary workflows all
used a target-agnostic concurrency group, so releasing one scope (e.g.
`monorepo`) would queue behind an unrelated scope's run (e.g. `angular`)
despite touching disjoint packages and tags. Fold the release target into
each concurrency key so different scopes run in independent lanes while
same-scope runs stay serialized (cancel-in-progress: false), preserving the
version-bump / tag-push / npm-publish race protection.

publish-release also fires on merged release PRs where `inputs.scope` is
empty; the PR branch `release/publish/<scope>/v<version>` carries the scope
via `github.head_ref`, so the key stays scope-distinct on that path too.

ENT-950

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:25:29 -05:00
Mike Ryan dba73fa407 chore(examples): remove live-consumed _intelligence overlay (ENT-834) (#5525)
## What

Removes the live-consumed local Intelligence overlay and the dead
references the deletion would leave behind.

- Deletes `examples/integrations/_intelligence/` (`docker-compose.yml`,
`.env.intelligence`, `README.md`) — the overlay the currently-shipped
CLI clones at runtime.
- Strips the now-dangling `# see
examples/integrations/_intelligence/.env.intelligence for the seed
value` pointer from 8 integration `.env.example` files (adk, agno,
langgraph-fastapi, langgraph-js, langgraph-python,
ms-agent-framework-{dotnet,python}, strands-python).

Closes ENT-834.

## ⚠️ DO NOT MERGE until launch

The **currently-shipped** CLI clones
`CopilotKit/CopilotKit@main:examples/integrations/_intelligence` at
runtime via `fetchIntelligenceOverlay` (hardcoded to `main`). Deleting
this dir from `main` **immediately breaks** the shipped CLI's
threads-framework `init` — the overlay fetch 404s.

**Merge gate (verify at merge time):**
- [ ] The managed-only CLI build has **dropped
`fetchIntelligenceOverlay`** (Intelligence-repo removal ticket)
- [ ] That managed-only CLI has been **released**
- [ ] Merge in lockstep with launch

## Scope

- ✅ **Included:** the `_intelligence/` overlay dir + the 8 dead
`.env.example` pointers it leaves behind.
- ✅ **Already done elsewhere:**
`examples/integrations/langgraph-python-threads/` (the ticket's "maybe"
scope) was already removed via ENT-800 — it is no longer on
`origin/main`.
- ⏭️ **Deferred to a launch-coordinated follow-up:** the local-stack
`INTELLIGENCE_API_URL`/`GATEWAY_WS_URL` defaults (`localhost:4201` /
`ws://localhost:4401`) baked into ~18 `copilotkit:intelligence` route
blocks + `agentcore/docker/docker-compose.yml`, and the matching
local-dev block in each `.env.example`. These depend on the managed
CLI's hosted env contract (Intelligence repo) and shouldn't be guessed
at now. The `copilotkit license` locked-state copy is owned by ENT-804.

## Not affected

The `docker-compose.test.yml` + `docker/Dockerfile.{agent,app}` files
across the integrations are the **e2e/CI test harness** for the example
apps, unrelated to the Intelligence pivot. The Threads feature, the
activation-gated `copilotkit:intelligence` block, and the themed
threads-drawer UI are the product and work against hosted Intelligence —
only the local-stack scaffolding is being removed.

## Notes

Branched off fresh `origin/main` (`c540734143`). No in-repo code
references the overlay dir (the `_intelligence` matches under
`packages/` are an unrelated private field on the agent registry).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 07:23:57 -07:00
Alem Tuzlak d92959e68d Merge remote-tracking branch 'origin/main' into feat/bot-whatsapp
# Conflicts:
#	.github/workflows/canary.yml
#	.github/workflows/publish-release.yml
#	.github/workflows/stable-release.yml
#	examples/slack/README.md
#	examples/slack/app/index.ts
#	examples/slack/app/sender-context.ts
#	release.config.json
2026-06-19 16:17:16 +02:00
Alem Tuzlak 699bc9a5c0 fix(examples/slack): build all imported bot packages for deploy
The build script only built bot-slack, bot-discord, and runtime, so
@copilotkit/bot-telegram (plus bot and bot-ui) were never compiled — the
Railway deploy then crashed on `import { telegram } from @copilotkit/bot-telegram`.
Build every workspace package the app imports.
2026-06-19 16:15:25 +02:00
Alem Tuzlak c55dad5cc5 Merge branch 'main' into feat/bot-slack-native-streaming
Resolve event-renderer.ts onRunFinishedEvent: keep the native turn stream open
(finalized in finish()) AND retain the legacy per-message stream drain from main
(#5573) as a no-op-in-native safety net. app/index.ts (telegram adapter from
#5520 + showToolStatus:false) and create-bot.test.ts auto-merged.
2026-06-19 15:57:20 +02:00
Alem Tuzlak 22e5c8c2d1 feat(examples/slack): web search via a TanStack AI factory agent (#5549)
## What

Gives the example's runtime agent **web search** (OpenAI's hosted
`web_search` tool).

`BuiltInAgent`'s classic `tools` only accepts CopilotKit's handler-based
`ToolDefinition[]` (each needs an `execute`), so it can't carry a
provider/hosted tool like web search. The fix is to run the agent in
**factory mode** (`type: "tanstack"`) and drive the LLM call with
TanStack AI's `chat()` — which exposes provider tools and managed MCP
directly.

## How (`examples/slack/runtime.ts`)

- **Adapter:** `openaiText(model)` from `@tanstack/ai-openai` (OpenAI
Responses API; `gpt-5.5` default).
- **Web search:** `webSearchTool({ type: "web_search" })` from
`@tanstack/ai-openai/tools`, passed in `chat({ tools: [...] })`.
- **MCP:** Linear + Notion via `@tanstack/ai-mcp` `createMCPClient`
(HTTP transport + `Authorization: Bearer`), created per-run inside the
factory; `chat()` discovers their tools and closes the connections when
the run ends.
- **Bridge:** `convertInputToTanStackAI(ctx.input)` (already in
`@copilotkit/runtime`) maps AG-UI input → `chat()` messages +
systemPrompts; `BuiltInAgent` converts `chat()`'s stream back to AG-UI
events via its built-in `convertTanStackStream`.
- The big triage `SYSTEM_PROMPT` is prepended to `systemPrompts`.

Adds `@tanstack/ai`, `@tanstack/ai-openai`, `@tanstack/ai-mcp` to the
example.

> **OpenAI-only now.** Web search is an OpenAI provider tool, so this
drops the previous multi-provider path (anthropic/google). `AGENT_MODEL`
accepts a bare OpenAI id or `openai/<id>` (prefix stripped).

## Verification

- `tsc --noEmit -p examples/slack/tsconfig.json` clean (validates the
full `chat()` wiring — adapter, tools, MCP, `convertInputToTanStackAI`,
factory return type).
- Runtime **boots** on the factory: `agent "triage" ready · MCP: Linear,
Notion`, server listening — confirms the `@tanstack/*` ESM imports
resolve and the agent constructs. (A live web-search query is exercised
through the Slack bot.)
- oxfmt + oxlint clean.

> Pre-commit `test-and-check-packages` bypassed: the lockfile change
makes nx mark everything affected, and the only failure is the
pre-existing, environmental `@copilotkit/sqlite-runner:test` (native
sqlite on Node 24), unrelated to this change.
2026-06-19 15:19:17 +02:00
AlemTuzlak 734a187257 chore: release bot v0.0.3 2026-06-19 13:04:11 +00:00
Alem Tuzlak ed95058e92 chore(examples/slack): adopt released TanStack openai-base strict-schema fix
Bump @tanstack/ai-openai 0.14.4 → 0.15.2 (pins @tanstack/openai-base 0.8.7,
TanStack/ai#790) and @tanstack/ai → 0.32.0. 0.8.7 emits strict:false for tool
schemas outside OpenAI's strict subset in the provider-path function-tool
converter, so MCP tools (e.g. Notion's API-post-search) no longer 400 — no
local patch needed. Verified end-to-end against the live Slack bot.
2026-06-19 15:01:17 +02:00
Alem Tuzlak 319d0ec78a feat(examples/slack): wire frontend tools into the TanStack agent + tidy render output
- runtime.ts: pass the bridge's forwarded client tools (convertInputToTanStackAI's
  tools) into chat() alongside web_search + MCP, so generative-UI cards and the
  confirm_write HITL gate work; drop the prompt line that made the model narrate
  charts with a trailing "Charting …" sentence (it landed after the image).
- render-chart / render-diagram: post the caption as a header BEFORE the image
  (a file upload's message lands a beat after postFile resolves, so caption-first
  keeps a stable caption → image order) and drop the false "rendered above" wording.

Depends on the @copilotkit/runtime factory tool-lifecycle fix (#5572) and the
bot-slack HITL/render-order fix (#5573).
2026-06-19 14:05:01 +02:00
Alem Tuzlak d4d6e204f1 feat(examples/slack): add web search via a TanStack AI factory agent
The example's runtime agent needed OpenAI's hosted `web_search` tool, but
BuiltInAgent's classic `tools` only accepts handler-based `ToolDefinition[]`
(needs `execute`) — it can't carry a provider/hosted tool. So switch the
agent to BuiltInAgent **factory mode** (`type: "tanstack"`) and drive it with
TanStack AI's `chat()`:

- `openaiText(model)` adapter (OpenAI Responses API; gpt-5.5 default)
- `webSearchTool({ type: "web_search" })` provider tool (`@tanstack/ai-openai/tools`)
- Linear/Notion MCP via `@tanstack/ai-mcp` `createMCPClient` (HTTP + bearer),
  created per-run; `chat()` discovers their tools and closes the connections
- `convertInputToTanStackAI(ctx.input)` bridges AG-UI input → `chat()`;
  BuiltInAgent converts `chat()`'s stream back to AG-UI events

OpenAI-only now (web search is OpenAI-specific); AGENT_MODEL accepts a bare
OpenAI id or an "openai/<id>" form. Adds @tanstack/ai, @tanstack/ai-openai,
@tanstack/ai-mcp to the example.
2026-06-19 14:02:43 +02:00
Alem Tuzlak 967623de9f fix(bot-slack): resolve HITL waiter in assistant-pane DMs + render-tool message order (#5573)
Two fixes surfaced by exercising the bot's generative-UI / HITL tools
end-to-end in Slack.

## 1. HITL never resumed in an assistant-pane DM

An assistant-pane DM is **threaded**, so the ingress path
(`assistant.ts`) keys the turn's conversation by **`thread_ts`**. But
`decodeInteraction` forced **`DM_SCOPE`** for any `D…` channel. So the
HITL `awaitChoice` waiter was registered under `D…::<thread_ts>` while a
button click looked it up under `D…::dm` — the waiter was never
resolved. Clicking **Create/Cancel** swapped the card UI (the button's
`onClick` ran) but the agent run never resumed: no write, no reply.

**Fix:** honor an explicit `thread_ts` as the conversation scope even in
DMs (matching ingress); fall back to `DM_SCOPE` only for a genuinely
unthreaded DM. + a `decodeInteraction` regression test.

## 2. Render-tool output landed out of order

Defensively finalize any text stream still open at the end of a run
(`onRunFinishedEvent`), so a run's streamed text is fully posted before
the run-loop executes tool handlers that post out-of-band content
(images, cards).

All 200 `@copilotkit/bot-slack` tests pass. Verified live:
`confirm_write` now gates a write and resumes on approval; chart/diagram
output renders in order.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 13:58:28 +02:00
Alem Tuzlak 08184c471a feat: Telegram bot adapter (@copilotkit/bot-telegram) + dual-platform example (#5520)
## What

Adds a **Telegram platform adapter** (`@copilotkit/bot-telegram`) for
`@copilotkit/bot`, at feature parity with `@copilotkit/bot-slack`, plus
a runnable example that drives **both** a Slack bot and a Telegram bot
from one app.

## `@copilotkit/bot-telegram` (new package)

A grammY-based adapter implementing the `PlatformAdapter` interface:

- **Ingress:** long-polling by default; webhook / `auto` (serverless-env
detection) opt-in.
- **Threading:** topic-aware hybrid — `tg:<chatId>:<scope>` where scope
is `dm`, `topic:<id>` (forum supergroups, gated on `chat.is_forum`), or
`user:<userId>` (non-forum groups).
- **Rendering:** the platform-agnostic `@copilotkit/bot-ui` JSX IR →
Telegram HTML, with a plain-text format fallback when HTML entity
parsing fails; length-bounded.
- **Streaming:** chunked `editMessageText` (placeholder → repeated
edits) with HTML-expansion headroom.
- **Interactions:** `callback_query` HITL (ack-first; 64-byte
`callback_data` via hashed action ids resolved through the ActionStore).
- **Inbound files:** photo/document ingestion wired into the agent turn
(size-capped, token-redacted).
- Ships `defaultTelegramTools` (user lookup) + `defaultTelegramContext`
(tagging / HTML / thread-model guidance).
- **Capabilities:** `supportsModals: false`, `supportsTyping: true`,
`supportsStreaming: true`, `supportsThreadTitle: true`,
`supportsReactions: false`, `supportsSuggestedPrompts: false`.

## `examples/slack` → Slack **and** Telegram from one app

Rather than maintain a diverging copy, the example now starts a Slack
bot and/or a Telegram bot (env-conditional on which credentials are
present) from one **platform-neutral** app layer — shared components,
tools, context, commands, render helpers. Each platform gets its own
`createBot` with its platform-specific default tools/context; the shared
components emit the cross-platform JSX IR that each adapter renders
natively.

To make the shared layer truly neutral: unicode glyphs instead of Slack
mrkdwn `:shortcode:` strings (Telegram doesn't expand them; Slack
renders unicode fine), no Slack-Block-Kit `raw` fallbacks, and neutral
context/tool wording (per-platform tagging guidance comes from each
adapter's default context). The separate `examples/telegram` app was
removed; its e2e smoke harness + BotFather setup docs were migrated into
`examples/slack`.

## Testing

- `@copilotkit/bot-telegram`: 138 unit tests; `nx build` (typecheck)
clean.
- `slack-example`: 42 tests (incl. `renderTelegram` parity assertions);
typecheck clean.
- `nx run-many -t build` green; root `pnpm install --frozen-lockfile` in
sync.
- Reviewed via a multi-round adversarial CR loop to convergence (zero
load-bearing findings) + a bucket-(c) promotion audit.

## Note: `bot-telegram` is unpublished

The example references it as `workspace:*` and **runs from the
monorepo** (`pnpm --filter slack-example start`). Standalone deploys
(the example's own lockfile) work for Slack today; once `bot-telegram`
publishes alongside its siblings, switch the dep to `~0.0.2` and
regenerate the standalone lockfile to enable standalone Telegram
deploys. Documented in the example README.

## Known limitations / follow-ups (out of scope, pre-existing or
inherent)

- **render_table** hardening (pre-existing in the Slack example):
`clamp()` overflow `notes` are computed but not surfaced (silent
truncation); "Max 100 rows" doc vs `MAX_DATA_ROWS = 99` off-by-one;
monospace fallback ignores column alignment.
- **Telegram e2e harness** is a documented best-effort **manual-trigger
smoke**: `getUpdates` contends with the bot's own long-poller
(single-consumer Bot API limit); multi-chunk reply assembly and
follow-up message selection are approximate. Automated upgrade path
(second sender bot) documented in `e2e/TELEGRAM-README.md`.
- **`_status` glyph mapping** uses substring matching, so non-default
Linear workflow state names (e.g. "Unstarted") can map to the wrong
glyph — identically on both platforms.
- **Bold-inside-link** (`[**text**](url)`) renders literal asterisks on
both Slack and Telegram (link labels aren't formatted on either) —
pre-existing, cosmetic.
- Telegram long-poll failures (revoked token, 409 conflict) are logged
inside the adapter and not surfaced to `start()`, so startup reports
success even if polling later fails.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 13:46:10 +02:00
Alem Tuzlak 0c85156458 fix(runtime): support full tool lifecycle in BuiltInAgent TanStack factory mode (#5572)
## Problem

`BuiltInAgent`'s TanStack factory mode (`type: "tanstack"`) was unusable
with server-executed tools. The stream converter
(`convertTanStackStream`) stopped converting at the **first per-turn
`RUN_FINISHED`**, assuming tools are always executed client-side and
re-prompted. But `chat()` executes MCP-server and provider tools
**itself** across multiple turns — so the `TOOL_CALL_RESULT` and the
model's final answer were dropped, and MCP-backed turns returned
**nothing**. It also couldn't surface frontend (generative-UI / HITL)
tools at all in factory mode.

## Fix

- **`convertTanStackStream`**: drop TanStack's per-turn
`RUN_STARTED`/`RUN_FINISHED` (the `Agent` wrapper owns the single outer
pair) and convert **every** turn's events; dedupe tool `START`/`END` by
id (chat() re-announces a call when it re-prompts); surface `RUN_ERROR`
by throwing instead of silently dropping it.
- **`convertInputToTanStackAI`**: return `input.tools` as TanStack
**client-side** tools so the frontend's generative-UI / HITL tools work
in factory mode, and **sanitize** their JSON Schema (recursively close
open objects → `additionalProperties: false`) so OpenAI accepts them —
mirroring what the classic (Vercel AI SDK) path did implicitly via its
Zod round-trip.

## Tests

Adds coverage to the converter + input-converter suites: multi-turn (no
truncation at per-turn `RUN_FINISHED`), tool-call dedup, `RUN_ERROR`
surfacing, client-tool conversion, and schema sanitizing. Full
`@copilotkit/runtime` suite green.

Verified end-to-end against a live Slack bot (`examples/slack`): web
search + Linear/Notion MCP + generative-UI cards + HITL all work through
factory mode.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-19 13:45:32 +02:00
Alem Tuzlak 83c0a66c7b fix(bot-slack): resolve HITL waiter in assistant-pane DMs + fix render-tool message order
Two fixes surfaced by exercising generative-UI / HITL tools through the bot:

- interaction.ts: an assistant-pane DM is threaded, so the ingress path keys the
  turn by thread ts — but decodeInteraction forced DM_SCOPE for any "D…" channel.
  The awaitChoice waiter was registered under D…::<thread_ts> while the button
  click looked it up under D…::dm, so it was never resolved: clicking Create/
  Cancel swapped the card UI but the run never resumed (no write, no reply).
  Honor an explicit thread_ts as the scope even in DMs; fall back to DM_SCOPE
  only for a genuinely unthreaded DM.
- event-renderer.ts: defensively finalize any text stream still open at run end,
  so a run's streamed text is fully posted before the run-loop executes tool
  handlers that post out-of-band content.

Adds a decodeInteraction regression test for the threaded-DM scope.
2026-06-19 13:23:22 +02:00
Alem Tuzlak 5bf13abb8e fix(runtime): support full tool lifecycle in BuiltInAgent TanStack factory mode
The TanStack stream converter stopped at the first per-turn RUN_FINISHED,
assuming tools are executed client-side. That truncated runs whose tools are
executed by chat() itself (MCP servers, provider tools): the TOOL_CALL_RESULT
and the model's final answer were dropped, so MCP-backed turns returned nothing.

- convertTanStackStream: drop TanStack's per-turn RUN_STARTED/RUN_FINISHED (the
  Agent wrapper owns the outer pair) and convert every turn's events; dedupe
  tool START/END by id; surface RUN_ERROR instead of silently dropping it.
- convertInputToTanStackAI: return input.tools as TanStack client-side tools so
  the frontend's generative-UI / HITL tools work in factory mode, and sanitize
  their JSON Schema (close open objects) so OpenAI accepts them.

Adds converter + input tests (multi-turn, dedup, error surfacing, client-tool
conversion, schema sanitizing).
2026-06-19 13:20:21 +02:00