Commit Graph

12110 Commits

Author SHA1 Message Date
Mike Ryan 469070e501 fix(examples): update threads license command 2026-06-18 16:10:09 -07:00
Jordan Ritter e621016fba fix(cvdiag): applyByteCap bounds caller fixed fields so the tier cap is provably enforced (M1 CR convergence lever C2)
applyByteCap's "hard guarantee" docstring was false: it trimmed only the
metadata bag (Steps 1-3), so caller-supplied variable-length STRING fields
that the caller controls — `slug`, `demo`, `parent_span_id`, `trace_id` —
were never trimmed. A 5000-char `demo`/`slug` enqueued ~5552B over the 2048B
default cap while stamped `_truncated:true` as if bounded (spec §7 R5-F3
violation).

Add Step 4: after the metadata bag is exhausted (dropped to {}), if the
envelope is STILL over cap the excess can only be in those four caller string
fields. Clamp them longest-first to a short prefix + `…[clamped]` marker until
under cap. Minted ids (`test_id`, `span_id`) are left intact — they are
fixed-width by construction and already bounded; `trace_id` mirrors `test_id`
(fixed-width UUIDv7) so it is included only for completeness so the
post-condition holds even for a hand-built oversized `trace_id`. Format-
constrained / enum / numeric fields (`boundary`, `layer`, `outcome`, `ts`,
`mono_ns`, `duration_ms`, `schema_version`, 9-key `edge_headers`) are bounded
by construction and never touched. Post-condition: serializedSize(env) <= cap
for ANY realistic envelope.

Fix `_truncated` semantics: stamp it only when trimming ACTUALLY occurred (a
field was modified), per its documented meaning, rather than on bare over-cap
detection. A detection that finds nothing trimmable until Step 4 ends with
Step 4 trimming and stamping.

applyByteCap is private; its sole caller is buildEnvelope (which both emit()
and flush() route through). Pure instrumentation preserved — never throws.

Adds a durable bytecap-invariant matrix test over pathological shapes (huge
slug/demo/parent_span_id, huge nested + scalar-heavy metadata, default + debug
tiers) asserting serializedSize(env) <= BYTE_CAP_BY_TIER[tier] for every case.
2026-06-18 16:02:03 -07:00
Jordan Ritter 01509609bd fix(cvdiag): complete secret-scrub — recurse nested metadata values, base64url keys, multi-@ userinfo (M1 CR convergence lever C1)
The §6 PII guarantee was incomplete in three ways that three CR rounds kept
re-finding in the same class. This completes the scrub.

1. Nested-value scrub. `validateMetadata` scrubbed ONLY top-level string
   values; secrets buried in allow-listed array/object values (e.g.
   `backend.error.caught.stack_brief`, `aimock.match.decision.reject_reasons`)
   leaked. New `scrubDeep(value)` in scrub.ts (leaf module; called from
   schema.ts which already imports from ./scrub.js — no cycle) applies
   `scrubSecrets` to every string LEAF at any depth, preserving structure and
   non-string leaves. validateMetadata deep-scrubs object/array values on a
   `structuredClone` (cycle-preserving; no caller mutation), falling back to
   in-place scrub on the rare unclonable input (correctness > no-mutation).

   Walker cycle-safety: ITERATIVE (explicit work stack, never the call stack)
   with a WeakSet visited-guard — a self-referential/cyclic or 10k-deep object
   is visited once and never re-entered, so no stack-overflow/hang on untrusted
   metadata. Only plain objects (Object.prototype/null proto) and arrays are
   descended; Date/RegExp/etc. are left as-is.

2. base64url key alphabet. SK_KEY_REGEX
   `/sk-(?:[A-Za-z0-9_-]*[A-Za-z0-9]{12,})[A-Za-z0-9_-]*/g` redacts modern keys
   whose body uses base64url incl. `_`/`-` (sk-ant-api03-AbCd_Ef-…, sk-proj-…),
   with a ≥12-char contiguous-alnum entropy gate (shortest real tail
   `0123456789xyzAB` = 15) so prose ("ask-me-later" → me/later; "task_list_items"
   uses `sk_` not `sk-`) is NOT redacted. Legacy `sk-<16+ alnum>` still matches.

3. multi-@ URL userinfo. URL_USERINFO_REGEX `/([a-z][a-z0-9+.-]*:\/\/)[^/\s]*@/gi`
   redacts the FULL authority up to the LAST `@` before the host/path; excluding
   `/` keeps it from crossing into the path. Fixes `https://a@b@c.com/x` leaking
   `b@c.com`; user:pass@ and bare-token tok@ still work.

scrubSecrets signature + SCRUB_REPLACEMENT unchanged. Call sites of the deep
scrub: schema.ts `validateMetadata` (object/array branch) → scrubDeep; emit.ts
buildEnvelope routes data-plane metadata through validateMetadata unchanged.

Red→green: scrub-coverage corpus (schema.test.ts) + nested array-of-objects
sweep & cyclic-input safety (emit.test.ts) observed RED (base64url key
unredacted; multi-@ tail leaked; nested secret survived envelope JSON), GREEN
after fix. Full cvdiag suite 119/119 (pb-writer live-PB test skipped sans
PB_BIN). tsc clean; oxfmt + oxlint 0 errors on touched files.
2026-06-18 16:02:03 -07:00
Ben Taylor c673a7622e chore(examples): bump @copilotkit/* deps to 1.61.0 across integration starters (#5553)
Bumps the `examples/integrations` starter projects to the **1.61.0**
release.

## What changed
1. **`package.json` (20 files, 44 specifiers)** — all `@copilotkit/*`
deps `1.60.1 → 1.61.0`: `react-core`, `runtime`, `react-ui`,
`a2ui-renderer`, `sdk-js`.
2. **`package-lock.json` (20 files)** — regenerated now that `1.61.0` is
published. All `@copilotkit/*` resolve to `1.61.0`, and the transitive
`@copilotkit/license-verifier` moves `0.4.2 → 0.5.0` (the range
`runtime@1.61.0` now ships, `~0.5.0`).

Starters: a2a-a2ui, a2a-middleware, adk, agent-spec, agentcore (frontend
+ infra-cdk runtime lambda), agno, crewai-crews, crewai-flows,
langgraph-fastapi, langgraph-js (app + agent), langgraph-python,
llamaindex, mastra, mcp-apps, ms-agent-framework-dotnet,
ms-agent-framework-python, pydantic-ai, strands-python.

## Notes
- `examples/integrations/*` are **not** pnpm-workspace members, so the
root lockfile is unaffected; each starter ships its own
`package-lock.json`.
- Lockfiles regenerated with `npm install --package-lock-only` (three
starters needed `--ignore-scripts` to skip env-specific lifecycle hooks:
pip / dotnet / build steps — no dependency impact).
- The generated `mastra/.next/standalone/.../package.json` build
artifact is intentionally **not** touched.

Closes ENT-939
2026-06-18 17:54:12 -05:00
Benjamin Taylor 07981f146a chore(examples): regenerate integration starter lockfiles for 1.61.0
1.61.0 is now published. Regenerates all 20 starter package-lock.json files
so @copilotkit/* resolves to 1.61.0 and the transitive
@copilotkit/license-verifier moves 0.4.2 -> 0.5.0 (shipped by runtime@1.61.0).

ENT-939
2026-06-18 17:47:55 -05:00
Jordan Ritter 4ae564f2f9 fix(cvdiag): applyByteCap must not mutate caller metadata or overload _metadata_dropped for size drops (M1 CR B2)
buildEnvelope's accounting branch aliased args.metadata into the
envelope; applyByteCap's in-place trims (Steps 1/2) then mutated the
caller's object as a side effect of pure instrumentation. Shallow-clone
the accounting bag before it enters the envelope (data-plane events
already get a fresh `survivor` from validateMetadata, so only the
accounting branch needed the clone).

applyByteCap Step 3's full-bag size-drop stamped _metadata_dropped, but
that flag is the §6 PII closed-world signal (set by buildEnvelope when
validateMetadata drops unknown keys); overloading it for a SIZE drop
pollutes PB drift queries. A size-drop is already observable via the
_truncated flag stamped earlier, so Step 3 now drops the bag without
touching _metadata_dropped. The genuine PII stamp is unchanged.

Call sites (both private, in-module only):
  buildEnvelope: emit() (emit.ts:309), flush() accounting (emit.ts:514).
  applyByteCap:  buildEnvelope() (emit.ts:388).
No external callers.
2026-06-18 15:45:28 -07:00
Jordan Ritter fd99348fe3 fix(cvdiag): scrub modern hyphenated sk-proj-/sk-ant- API keys (M1 CR B3)
The old SK_KEY_REGEX /sk-[A-Za-z0-9]{16,}/ stopped its character class at
the first hyphen, so modern hyphenated-prefix keys leaked into PocketBase:
the `proj` (4) / `ant` (3) / `api03` (5) prefix segments never reached the
required 16-char run, so `sk-proj-…` and `sk-ant-api03-…` never matched and
were stored verbatim — defeating the §6 secret-scrub guarantee for the two
most common real-world key formats.

New pattern: /sk-(?:[A-Za-z0-9]+-)*[A-Za-z0-9]{16,}/g
  `sk-` then zero-or-more hyphen-terminated alnum SEGMENTS (the prefix words)
  followed by a ≥16-char alnum entropy TAIL.

Why it does not over-redact ordinary hyphenated prose: the mandatory 16+
alphanumeric tail is the entropy gate. Words like "ask-me-later" or
"task-list" have no 16+ contiguous alnum run after an `sk-`, so they never
match (negative test asserts the phrase is returned unchanged). The legacy
`sk-<16+ alnum>` form is preserved as the zero-segment case.

SCRUB_REPLACEMENT and scrubSecrets structure unchanged. Four new tests
(sk-proj-, sk-ant-api03-, legacy sk-<16+>, negative over-redaction);
RED on the two hyphenated cases pre-fix, GREEN after. Full cvdiag suite
110/110.
2026-06-18 15:45:27 -07:00
Jordan Ritter ce2c5daa7f fix(cvdiag): flush() leaves queue intact when no PB writer is configured (M1 CR B1)
Move the `pbWriter === undefined` early-return to the TOP of flush(), before
the queue.splice and the drop-accounting block. Previously, with no writer,
flush() spliced the queue into `batch` and discarded it on the trailing
early-return while resetting droppedSinceFlush to 0 — silently losing all
queued telemetry every flush window. Under { autoFlush: true } with no writer
this was continuous data loss, contradicting the documented pbWriter contract
("When absent, events stay queued").

flush() callers: the startBackgroundFlush() interval timer and any manual
flush() call. With a writer present, the A3 drop-accounting + reset-only-
after-landing behavior is unchanged.
2026-06-18 15:45:27 -07:00
Jordan Ritter f4b735169b fix(cvdiag): flush() builds queue_dropped accounting event directly into batch, never loses drop count (M1 CR A3)
flush() previously zeroed droppedSinceFlush BEFORE confirming the accounting
emit() landed, then recovered the just-enqueued event via a fragile second
splice of the whole queue (which APPENDS despite the "prepend" docstring). If
that emit() returned null (validateEnvelope failure / exception) the drop count
AND the cvdiag.queue_dropped record were permanently lost.

Fix: extract the envelope-construction + validateEnvelope + applyByteCap logic
of emit() into a private buildEnvelope() helper (no enqueue side effect). flush()
now calls buildEnvelope() for the accounting event and pushes the result DIRECTLY
into the outgoing batch (no queue round-trip / re-splice). droppedSinceFlush is
reset to 0 ONLY after the accounting envelope is in the batch, so a null/throw
retains the count for the next flush. Docstring updated to "append" (event order
in a best-effort batch is not load-bearing — the classifier re-sorts by mono_ns/ts).

Call sites of the extracted buildEnvelope(): emit() (enqueues result, still
resolves/returns CvdiagEnvelope|null) and flush() (appends to batch, still
resolves/never-rejects). Public method signatures unchanged.

Red-green: src/cvdiag/emit.test.ts. Loss-path test forces the accounting build
to return null and asserts the drop count is retained (lands on the next flush);
RED on original = 0 queue_dropped events land (count silently lost), GREEN after
fix = exactly 1 lands with _dropped_count preserved.
2026-06-18 15:34:03 -07:00
Jordan Ritter 0ca29a4fc4 fix(cvdiag): applyByteCap enforces tier byte cap for scalar/short-string-heavy envelopes (M1 CR A2)
applyByteCap previously stamped _truncated:true and trimmed only >64-char
string values / nested objects in metadata. An envelope over the tier byte
cap due to many short-string or numeric/boolean metadata values (none
reachable by that pass) was stamped truncated yet still enqueued OVER cap,
violating spec §7 R5-F3 ("the whole serialized envelope must fit the tier
byte cap").

Fix escalates while still over cap: (1) legacy >64-char/object trim, then
(2) clamp ALL string values progressively, then (3) drop metadata to {}
(stamping _metadata_dropped). Fixed scalar envelope fields are bounded by
construction, so an empty metadata bag is the hard guarantee the enqueued
row fits any realistic cap. Method stays pure-instrumentation: it never
throws.

Call sites: applyByteCap is private; emit() (emit.ts:361) is the sole caller
and already wraps it in try/catch. Post-condition holds: on return the
serialized envelope is <= cap OR metadata is already {}.
2026-06-18 15:32:52 -07:00
Jordan Ritter b9ff5a49fd fix(cvdiag): scrub secrets in metadata values + bare-token URL userinfo (M1 CR A1)
validateMetadata kept surviving metadata VALUES verbatim and never scrubbed
them, so Bearer/sk-/URL-userinfo secrets in free-text fields (e.g.
*.message_scrubbed, probe.*.url) reached PocketBase unscrubbed — violating
spec §6 (the field is literally named message_scrubbed). Now every SURVIVING
string metadata value is run through scrubSecrets; non-string values are left
untouched.

Also tighten URL_USERINFO_REGEX: the old pattern required a user:password@
colon, so a bare-token userinfo (scheme://token@, e.g. https://ghp_xxx@host)
was NOT redacted. The new pattern matches any non-/, non-@, non-ws run before
@, covering BOTH the colon and no-colon forms; the $1[REDACTED]@ replacement
shape is unchanged and the / exclusion confines the match to the authority.

Import-cycle avoidance (approach a): moved scrubSecrets + its regex constants
into a new leaf module scrub.ts that has NO intra-cvdiag imports. Applying the
scrub inside validateMetadata required schema.ts to call scrubSecrets, but
edge-headers.ts already imports EDGE_HEADER_KEYS from schema.ts, so importing
scrubSecrets from edge-headers into schema would form a schema -> edge-headers
-> schema cycle. The leaf module is imported by both; edge-headers re-exports
the symbols so the historical public surface is preserved.

scrubSecrets call-site enumeration (all still resolve):
  - src/probes/drivers/d4-chat-roundtrip.ts (imports from ./cvdiag/index.js;
    index.ts does `export * from ./edge-headers.js`, which re-exports scrub.ts)
  - src/cvdiag/raw-byte-capture.ts (imports from ./edge-headers.js — re-export)
  - src/cvdiag/index.ts (`export *` from edge-headers — re-export intact)
2026-06-18 15:32:52 -07:00
Jerel Velarde 4d2b1584f4 docs(cookbook): flatten showcase paths to examples/showcases/oracle-agent-memory layout 2026-06-18 15:30:27 -07:00
Martha Kelly Schumann 7eb40b0524 docs(showcase): fix shared randomUUID imports (#5456)
## Summary
- fix Showcase docs snippets that import `randomUUID` from non-existent
`@copilotkit/shared/v2`
- use the published `@copilotkit/shared` entrypoint instead
- move the fix to the publishing Showcase docs source under
`showcase/shell-docs`

## Linear
- FAC-65

## Verification
- `rg -n "@copilotkit/shared/v2" showcase/shell-docs/src/content`
returns no matches
- `pnpm validate:model-names`
- `npm ci --ignore-scripts` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs`
- `git diff --check`
- pre-commit passed after refreshing root dependencies with `pnpm
install`
2026-06-18 15:21:56 -07:00
Martha Kelly Schumann 7f73f2c4ac docs(showcase): fix Deep Agents state streaming schema (#5556)
## Summary
- wrap the Deep Agents TypeScript `observed_steps` custom state field in
`zodState`
- clarify that `step_progress_tool.steps` intentionally maps into the
`observed_steps` state field
- move the fix to the publishing Showcase docs source under
`showcase/shell-docs`

## Why
The QA report called out a mismatch between `steps` and
`observed_steps`. That mapping is intentional via `stateItem`; the
actual issue is that the custom state field was declared as plain Zod.
In LangGraph JS, fields that are not wrapped with `zodState` can be
filtered out of the AG-UI state snapshot, so the tool call can happen
while the UI never receives the rendered state.

## Verification
- `pnpm validate:model-names`
- `npm ci --ignore-scripts` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs`
- `git diff --check`
2026-06-18 15:21:45 -07:00
Jordan Ritter 8aecedcc6e chore(cvdiag): merge --ab-report subcommand wiring into integration 2026-06-18 15:11:59 -07:00
Jordan Ritter 8b86e9a877 feat(cvdiag): wire --ab-report subcommand into cvdiag CLI 2026-06-18 15:11:38 -07:00
Mike Ryan e183d2fa43 chore: release monorepo v1.61.0 (#5560)
## Summary
- bump monorepo package versions to 1.61.0
- keep plugin metadata versions aligned with the runtime package

## Validation
- pnpm check:plugin-skills
- pre-commit hook ran package test, publint, and attw targets
v1.61.0
2026-06-18 15:09:20 -07:00
Jerel Velarde 09c192ea37 docs(cookbook): live demo embed + point source links at examples/showcases/oracle-agent-memory 2026-06-18 15:09:06 -07:00
Jordan Ritter 02e8e205f1 chore(cvdiag): merge L3-A internal-routing A/B into integration 2026-06-18 15:06:49 -07:00
Jordan Ritter 1248461eba feat(cvdiag): Railway-internal routing A/B — HMAC guard + dual-run + ab-report (L3-A) 2026-06-18 15:06:13 -07:00
Martha Kelly Schumann f8beaf787f Merge branch 'main' into docs/FAC-77-state-streaming-schema 2026-06-18 15:04:23 -07:00
Mike Ryan a296c57ef4 fix: sync plugin metadata before release PR creation 2026-06-18 15:03:10 -07:00
Jordan Ritter 1ccd782b58 chore(cvdiag): merge L2-B cvdiag CLI into integration 2026-06-18 15:01:33 -07:00
Jordan Ritter ca84419656 feat(cvdiag): showcase cvdiag CLI — timeline/classify/replay/purge (L2-B) 2026-06-18 15:01:04 -07:00
Mike Ryan b094156537 chore: release monorepo v1.61.0 2026-06-18 15:00:19 -07:00
github-actions[bot] 20cd06f62e style: auto-fix formatting 2026-06-18 21:55:16 +00:00
Jordan Ritter 1994d930e5 chore(cvdiag): merge L2-C raw-byte capture pipeline into integration 2026-06-18 14:54:33 -07:00
Sam Julien 4c311ac3fb chore(docs): add shell-docs symlink 2026-06-18 14:54:25 -07:00
Jordan Ritter 73ad95a3db feat(cvdiag): raw-byte capture pipeline (decode→scrub→html-strip→cap, DEBUG-only) (L2-C) 2026-06-18 14:54:02 -07:00
Jordan Ritter 08a7b3cdff chore(cvdiag): merge L2-A flap classifier into integration 2026-06-18 14:51:27 -07:00
Jordan Ritter e7bf4fa3fd feat(cvdiag): full 8-class flap classifier + synthetic ground-truth suite (L2-A) 2026-06-18 14:50:47 -07:00
Martha Schumann 72007b1d6a docs(showcase): move shared import fix to shell docs 2026-06-18 14:49:58 -07:00
Jordan Ritter e56ae02eab chore(cvdiag): merge L2-D CI perf-regression gate into integration 2026-06-18 14:48:23 -07:00
Jordan Ritter 07915f3c1c feat(cvdiag): CI perf-regression gate for emit cost (100µs median / 250µs p99) (L2-D) 2026-06-18 14:47:33 -07:00
Martha Schumann c6028bbdab Merge remote-tracking branch 'origin/main' into docs/FAC-65-shared-v2-imports 2026-06-18 14:44:08 -07:00
Martha Schumann 597690fd3a Merge remote-tracking branch 'origin/docs/FAC-77-state-streaming-schema' into docs/FAC-77-state-streaming-schema 2026-06-18 14:43:22 -07:00
Martha Schumann bdaba9aeca docs(showcase): move state streaming fix to shell docs 2026-06-18 14:42:54 -07:00
Mike Ryan f5c72d246e chore(deps): bump @copilotkit/license-verifier to ~0.5.0 (#5552)
## What

Bumps `@copilotkit/license-verifier` from `0.4.2` to `0.5.0`:
- `packages/runtime/package.json`: `~0.4.2` → `~0.5.0`
- `packages/shared/package.json`: `~0.4.2` → `~0.5.0`
- root `package.json` `pnpm.overrides`: `~0.4.2` → `~0.5.0` (this
override was the effective version gate — without bumping it the
lockfile stayed pinned at 0.4.2)
- `pnpm-lock.yaml`: regenerated, now resolves `0.5.0`
- `.npmrc`: added `@copilotkit/license-verifier` to
`minimum-release-age-exclude[]` (same treatment as `@ag-ui/langgraph`)
so the freshly-published 0.5.0 could be locked before clearing the 24h
`minimum-release-age` guard

## Status

0.5.0 is published and the lockfile resolves it cleanly. The earlier
"blocked on publish" state is resolved. Ready to come out of draft
pending CI.

Linear: ENT-938
2026-06-18 14:41:30 -07:00
Jordan Ritter 03f33fca48 chore(cvdiag): merge L1-D3 Python backend instrumentation into integration 2026-06-18 14:41:10 -07:00
Jordan Ritter a74f423ef4 chore(cvdiag): merge L1-D2 Python backend instrumentation into integration 2026-06-18 14:41:09 -07:00
Jordan Ritter 0bfdc75dff chore(cvdiag): merge L1-D1 Python backend instrumentation into integration 2026-06-18 14:41:09 -07:00
Martha Schumann c1e2566c58 Merge remote-tracking branch 'origin/main' into docs/FAC-77-state-streaming-schema 2026-06-18 14:40:55 -07:00
Jordan Ritter 24de3d1710 feat(cvdiag): backend instrumentation for google-adk + langroid + langgraph-fastapi (L1-D3) 2026-06-18 14:40:32 -07:00
Jordan Ritter 84369d5887 feat(cvdiag): backend instrumentation for llamaindex + ms-agent-python + claude-sdk-python (L1-D1) 2026-06-18 14:37:47 -07:00
Jordan Ritter 9d66761064 feat(cvdiag): backend instrumentation for pydantic-ai + crewai-crews + agno (L1-D2) 2026-06-18 14:37:10 -07:00
Austin Merrick 4dab6d38f8 fix(shell-docs): align callout accent colors to brand
Fumadocs' default callout palette (generic blue/amber/green) renders the
docs <Callout> accent and left bar off-brand against the purple-anchored
theme — and on the main docs route the info/success tokens weren't emitted
at all, falling back to the near-white muted color.

Define --color-fd-info/warning/success as plain :root custom properties
(not @theme tokens, which Tailwind v4 tree-shakes when no utility class
references them — the Callout reads them only via inline var()). Map info
-> brand accent (purple), warning -> the existing docs --warning orange,
success -> brand mint (new --success token, mint/800 light, mint/400 dark).
All theme-aware; error stays mapped to --destructive via shadcn.css.
2026-06-18 14:35:43 -07:00
Benjamin Taylor b71583c41e Merge remote-tracking branch 'origin/main' into chore/ent-938-bump-license-verifier 2026-06-18 16:35:20 -05:00
Benjamin Taylor 73b6713b69 Merge remote-tracking branch 'origin/main' into chore/ent-938-bump-license-verifier
# Conflicts:
#	.npmrc
#	packages/shared/package.json
#	pnpm-lock.yaml
2026-06-18 16:32:30 -05:00
Sam Julien cd40146d82 fix(react-core): honor selfManagedAgents in CopilotKit wrapper validation (#5551)
## Summary

Fixes #5417. The v1 `<CopilotKit>` wrapper's `validateProps` threw
`ConfigurationError: Missing required prop: 'runtimeUrl' or
'publicApiKey' or 'publicLicenseKey'` whenever neither `runtimeUrl` nor
a public key was supplied — without considering self-managed agents.
This rejected the documented self-managed-agent setup, even though the
underlying v2 `CopilotKitProvider` accepts it via its `hasLocalAgents`
gate.

- **Fix:** `validateProps` now mirrors the provider's `hasLocalAgents`
check, so `selfManagedAgents` and `agents__unsafe_dev_only` satisfy the
requirement without a `runtimeUrl` or Cloud key.
- **Test:** new rendering test pins the behavior — still throws when
nothing is configured, no longer throws when local agents are supplied.
- **Docs:** the showcase error-reference "v1 behaves differently"
callout claimed the wrapper throws unconditionally and rejects
`selfManagedAgents` (both now false); corrected, and dropped the "(v2
only)" label on the self-managed example.

## Test plan

- [x] `nx test react-core` — 1284 passing, 0 failing
- [x] New test fails before the fix (red) and passes after (green)
- [x] No new type errors introduced (pre-existing `tsc` noise unchanged
vs `main`)
2026-06-18 14:32:14 -07:00
Jordan Ritter 6d72936fcb chore(cvdiag): merge L1-I backend instrumentation into integration 2026-06-18 14:30:51 -07:00