Commit Graph

4085 Commits

Author SHA1 Message Date
github-actions[bot] 39d86aa872 style: auto-fix formatting 2026-04-27 16:24:06 -07:00
Sam Julien d1cd9f06a8 feat(shell-docs): collapse framework landing into the docs landing shell
Switching frameworks now lands on the same shell as / does, just with
the framework already URL-active. The /<framework> route renders the
docs-landing hero, CLI command, and utility cards (Concepts / API
Reference / Generative UI), then DocsLandingNext's "Continue with X"
branch with Quickstart / Browse {framework} docs / Switch framework
pointers. Replaces the previous bespoke FrameworkLandingPage that
showed a "You're viewing docs scoped to" panel and a hardcoded 4-card
grid (chat-ui / tool-rendering / frontend-tools / human-in-the-loop)
that drifted from the docs landing.

DocsLandingNext now prefers the URL-active framework over the stored
preference so /<framework> can render the "Continue with X" branch
during SSR with no mount-flicker — the previous storedFramework-only
gating caused a brief picker render before the localStorage effect
caught up.

Delete the orphaned per-framework index.mdx files. They were the
backing for the old sidebar Introduction entry (already removed) and
the URL /<framework>/index, which Next.js's optional catch-all
collapses to the bare framework path anyway. Their FrameworkOverview
content (banner video, init command, supportedFeatures grid) was a
legacy marketing-style landing that doesn't fit the new docs-routing
shape.
2026-04-27 16:23:40 -07:00
Sam Julien 4ab807c334 fix(shell-docs): drop redundant Introduction sidebar entry from framework sections
Each integration's meta.json opened with a "Getting Started" section
containing index (rendered as "Introduction") and quickstart. The
override filter already drops sections and root-equivalent pages, so
quickstart was always filtered (root quickstart.mdx exists), but
index slipped through and rendered as an Introduction link to
/<framework>/index — duplicating /<framework> with the marketing-style
FrameworkOverview content. Drop the entire opening section from every
framework's meta.json so the framework block in the sidebar starts at
the first framework-unique topic.

Also flatten empty-title wrapper groups in buildFrameworkOverridesNav.
buildNavTree clears the title on a spread-derived group when the
preceding section has the same name (so the renderer doesn't double-
print "Generative UI"). The override filter then drops those section
headers, leaving titleless containers that only added an extra indent
step around their children. Inline the children at the wrapper's
level instead, raising "Your Components" up where "Generative UI"
used to sit.
2026-04-27 16:23:40 -07:00
Sam Julien 7d1e806799 feat(shell-docs): hide sidebar Quickstart entry until a framework is active
The root /quickstart page is a routing shim — it has no real content,
just a pointer to the per-framework quickstarts. Showing it in the
sidebar on / leads users into a dead-end click ("This page is a
routing shim..."). Hide the entry until a framework is active in the
URL or stored in localStorage; once either is set, SidebarLink prepends
it and the click lands on real per-framework content.
2026-04-27 16:23:40 -07:00
Sam Julien 4711c7de06 fix(shell-docs): per-framework quickstart wins over routing shim
The framework-scoped route resolves root MDX before per-framework
overrides, which is correct for most pages — root content rendered
with framework-specific snippets is the primary path. But the new
root quickstart.mdx is a routing shim that exists only so the sidebar
entry has a backing page; real quickstart content lives per-framework
at integrations/<framework>/quickstart.mdx. Special-case the
quickstart slug so the override always wins for framework-scoped URLs.

While here, add the missing crewai-crews → crewai-flows docs-folder
mapping. The registry slug was renamed but the docs folder kept the
older name, so /crewai-crews/quickstart couldn't find the override.
2026-04-27 16:23:40 -07:00
Sam Julien b5632286db feat(shell-docs): restore categorized framework picker on docs landing
The merged landing's "no framework selected" branch previously showed a
flat IntegrationGrid. Swap it back to the categorized picker (Popular /
Agent Frameworks / Provider SDKs / …) so the docs root mirrors the same
shape users saw before the merge — the affordance to pick a backend is
unchanged from their perspective.

Also re-add the "Quickstart" sidebar entry so every framework has it.
The page itself is a routing shim; SidebarLink prepends the stored
framework, and direct hits to /quickstart 308 to /.

Extract FRAMEWORK_CATEGORY_ORDER into lib/framework-categories so the
client component can pull the constant without dragging fs-using
helpers from lib/docs-render through the bundle. docs-render
re-exports the constant so existing server-side imports keep working.
2026-04-27 16:23:40 -07:00
Sam Julien 8adbebd30b feat(shell-docs): merge docs landing + /quickstart picker into one page
The docs landing at `/` and the standalone `/quickstart` picker did
overlapping jobs and confused first-time users. "Quickstart" as a
top-level URL was misleading: clicking it from the shell landing or
the docs overview's top-card list led to a 23-line picker rather than
the actual quickstart guide (which lives at `/<framework>/quickstart`,
117–527 lines per framework).

Merge them. `/` becomes the single docs-side landing:

  - Always shown: hero + product positioning, the `npx copilotkit@latest
    create` CLI command (moved up from /quickstart), and three utility
    cards (Concepts, API Reference, Generative UI).
  - Conditional below: a new `DocsLandingNext` client component that
    branches on `storedFramework`. Null → "Pick your agent framework"
    + the integrations grid (the picker). Set → "Continue with
    {name}" + three pointer cards (quickstart for that framework,
    framework landing, switch frameworks).

The standalone `/quickstart` page is deleted; `/quickstart` 301-redirects
to `/`. `/<framework>/quickstart` is unchanged — that's the canonical
per-framework quickstart guide.

Side effects of the merge:

  - The unscoped catch-all's `DocsOverview` was rewritten to drop the
    former two-step "Pick a backend / Or jump into a topic" panels.
    The framework picker is now in `DocsLandingNext` (only when there's
    no stored choice); the topic cards (DOCS_SECTIONS) were a flatter
    duplicate of the new JTBD sidebar and got deleted entirely.
  - `content/docs/index.mdx` was the file that rendered at `/index`
    (reachable from a sidebar entry under Get Started). With the
    merged `/` landing serving as the canonical overview, the
    `/index` URL stops being meaningful — `index.mdx` is deleted and
    the entry is removed from the top-level `meta.json`.
  - `DocsLandingNext` is registered in the MDX components map so
    future pages can drop it inline if useful, but its primary use is
    inside `DocsOverview` itself.

Pairs with PDX-46 (BIA-as-default). Once BIA defaults are in, the
"no framework" branch of `DocsLandingNext` becomes rare and the page
reads as the "what's next" experience for nearly every visitor.

Closes PDX-58.
2026-04-27 16:23:40 -07:00
Sam Julien 3fe20fd9bd fix(shell-docs): rename gen-UI subgroup Open → Open-Ended
The concepts/three-types-of-gen-ui page and the canonical
learn/generative-ui/index page both use "Open-Ended" (with the
suffix) as the third gen-UI category alongside Controlled and
Declarative. The sidebar subsection had been shipped as just "Open"
in the JTBD reorg, leaving readers with three different namings
across the site ("Open" in nav, "Open-Ended" in concept page,
"Open-Ended" / "Fully Generated" in the learn page).

Align the sidebar to the canonical name. The other two subsections
("Controlled" and "Declarative") were already consistent.
2026-04-27 16:23:40 -07:00
Sam Julien b1131f21f5 fix(shell-docs): tighten docs overview to a doc-router shape
Two changes to index.mdx that align it with the new JTBD sidebar
instead of competing with it:

- Drop the lower "Explore by feature" 6-card section. Every entry
  there (Chat UI, Headless UI, Generative UI, Backend & Runtime,
  Programmatic Control, API Reference) was a flatter version of a
  sidebar section the JTBD reorg introduced. The overview pitching
  the same content twice made the page heavier without adding routing
  value.
- Swap one of the 4 top cards (was: Chat UI → /prebuilt-components)
  for Concepts → /concepts/architecture so the conceptual entry
  point is reachable from the docs front door, not just the sidebar
  subgroup.

The overview is now: 4 top cards (Quickstart, API Reference, Concepts,
Generative UI), the LandingCodeShowcase, and the IntegrationGrid.
Tighter and clearly a doc-router rather than a product-pitch redux.
2026-04-27 16:23:40 -07:00
Sam Julien 2ee45ceead fix(shell-docs): update mergeFrameworkNav anchor for JTBD section names
The merge anchor was hardcoded to "app control" with fallbacks to
"threads" and "backend". After the JTBD reorg renamed "App Control"
→ "Give Your App Agent Powers" and "Backend" → "Agents & Backends",
none of the candidates matched and every framework-scoped sidebar fell
back to append-at-end, relegating the framework section ("Built-in
Agent", "LangGraph (Python)", etc.) to the bottom of the nav.

Restore the original placement by adding the new section names to the
anchor candidate list: "give your app agent powers" (the renamed App
Control) and "agents & backends" (the renamed Backend) take priority,
with the old names kept as fallbacks for resilience. The match is
case-insensitive and stops at the first hit. Verified the framework
section now lands between Give Your App Agent Powers and Agents &
Backends for /built-in-agent/* and /langgraph-python/*.
2026-04-27 16:23:40 -07:00
Sam Julien 3600c1f6cc chore(shell-docs): strip narrative comments from redirects block
The redirect entries are self-describing — source, destination, and
permanent flag say everything the runtime needs. The block comments
were rationale that belongs in commit messages, not in a config file
that future readers scan for shape.
2026-04-27 16:23:40 -07:00
Sam Julien 133264f615 fix(shell-docs): 301 /concepts/oss-vs-cloud → /concepts/oss-vs-enterprise
The previous commit renamed the file but didn't add a redirect, so
any open browser tab or external link to /concepts/oss-vs-cloud now
404s — surfacing as a "sidebar disappeared" experience because the
404 page doesn't render the docs shell. Add the 301 redirect alongside
the migrate-to-* and frontend-actions ones.
2026-04-27 16:23:40 -07:00
Sam Julien fed518552b fix(shell-docs): rename concepts/oss-vs-cloud → oss-vs-enterprise
"Cloud" implied a single deployment shape (managed-only). "Enterprise"
is the accurate name for the layer — same Intelligence Platform runs
hosted at Copilot Cloud or self-hosted via the copilot-intelligence
Helm chart. The page already used "Enterprise" terminology in the
body; this aligns the slug, page title, and meta entry to match.

File renamed via git mv (history preserved). search-index.json paths
update on the next build (gitignored, regenerated).
2026-04-27 16:23:40 -07:00
Sam Julien db0dfe5820 feat(shell-docs): fill out the three concept pages with real content
Replaces the TODO placeholders left by the JTBD restructure with
written explanation-layer content sourced from existing repo material
(learn/architecture, learn/generative-ui, learn/intelligence-platform,
ag-ui/concepts/architecture, premium/overview) and from two CopilotKit
blog posts ("AG-UI Is Redefining the Agent–User Interaction Layer"
and "The Three Types of Generative UI: Static, Declarative and Fully
Generated").

- concepts/architecture.mdx: the three-layer stack (frontend, runtime,
  agent), AG-UI as the protocol bridge between runtime and agent, the
  request flow at a glance. Reuses the existing architecture-diagram
  PNG via ImageZoom and links out to the deeper learn/* coverage.
- concepts/three-types-of-gen-ui.mdx: a freedom-vs-control framing of
  Controlled / Declarative / Open-Ended generative UI with one concrete
  example per type and explicit "when to pick" guidance. Notes the
  alias terminology (Static, Fully Generated) so readers can match the
  blog and the learn/ pages. Cross-links to the implementation pages
  for each type.
- concepts/oss-vs-cloud.mdx: clear OSS-vs-Enterprise feature split with
  a capability-by-capability table, hosted-vs-self-hosted explanation,
  and a decision rubric ("if you want CopilotKit to *be* your
  conversation persistence layer, that's where Enterprise begins").

All three keep the explanation-layer scope (Diátaxis): they orient
and explain, then point the reader at the canonical detailed pages
for implementation specifics. None of them duplicate how-to content.
2026-04-27 16:23:40 -07:00
Sam Julien 5fbb586a71 fix(shell-docs): drop redundant What is CopilotKit concept page
The concepts/what-is-copilotkit.mdx placeholder duplicated the
overview rendered at /index.mdx — same intent ("orient the reader"),
same target audience. Keep the index page as the single what-is
entry point and remove the concept-page slot.
2026-04-27 16:23:40 -07:00
Sam Julien f9d0da3b59 fix(shell-docs): suppress duplicate group title when section header already labels it
The current meta convention pairs a `---X---` section header with a
`...x` spread that resolves to a sub-meta whose `title` is also "X".
buildNavTree emitted both a section node and a group node with the
same title, and the renderers showed each one — so users saw "BUILD
GENERATIVE UI" (section, uppercase tracking-widest) followed by
"Build Generative UI" (group, regular case) for the same content.
The doubling was already on the live site for one section
("Generative UI") and didn't get pushback there, but the JTBD reorg
makes it more visible: every spread-backed section ends up doubled
(Migrate, Other, etc.).

Fix in two halves, kept narrow:

- buildNavTree: when the previously-pushed node is a section header
  with the same title that the spread's group would carry, set the
  group's title to empty string. The group still wraps its children
  for indentation/nesting; only the redundant inner label is dropped.
- All three nav renderers (renderNavItem in docs-page-view.tsx,
  OverviewNavItem in app/[[...slug]]/page.tsx, RenderNav in
  app/[framework]/[[...slug]]/page.tsx) gain a `{node.title && ...}`
  guard so an empty string doesn't render an empty <div>.

Sections without a matching spread (Get Started → index/quickstart/
coding-agents listed inline) and spreads without a preceding section
header (legacy framework-tree work, override merges) are unaffected.
A spread whose sub-meta title differs from the section header (e.g.
section "Observe & Operate" + ...troubleshooting whose title is
"Troubleshooting") still renders both labels, which is correct —
they're meaningfully different and the inner one acts as a
sub-grouping label.
2026-04-27 16:23:40 -07:00
Sam Julien c119768195 feat(shell-docs): JTBD information-architecture restructure
Restructures the top-level sidebar from feature-primitive sections
(Basics / App Control / Backend / Generative UI / Platform / Premium
Features / Troubleshooting) into 9 jobs-to-be-done sections that match
how readers describe what they want to build. Decisions captured in
the IA Restructure plan.

Section structure:

  1. Get Started — index, quickstart, coding-agents, + concepts subtree
     (placeholders for what-is-copilotkit, architecture,
     three-types-of-gen-ui, oss-vs-cloud)
  2. Build Chat UIs — prebuilt-components, custom-look-and-feel,
     multimodal-attachments
  3. Build Generative UI — nested Controlled / Declarative / Open
     subgroups (tool-based, tool-rendering, state-rendering, reasoning,
     your-components / a2ui + dynamic-schema + fixed-schema / mcp-apps)
  4. Give Your App Agent Powers — frontend-tools, shared-state,
     multi-agent/subagents, programmatic-control
  5. Agents & Backends — built-in-agent (new), copilot-runtime,
     custom-agent, ag-ui, runtime-server-adapter
  6. Observe & Operate — inspector, vs-code-extension, +
     troubleshooting subgroup (event-inspector, hook-explorer,
     debug-mode, observability-connectors, common-issues,
     error-debugging)
  7. Enterprise — premium/overview, premium/observability,
     premium/self-hosting, threads (merged from former Threads section
     per product call: threads + persistence are part of Enterprise)
  8. Migrate — v2, 1.10.X, 1.8.2 (moved out of Troubleshooting and
     renamed to drop the redundant 'migrate-to-' prefix)
  9. Other — contributing, telemetry (unchanged)

  Reference stays as a separate root-true tree, not surfaced through the
  top-level meta. Its v1/v2 treatment is a follow-up decision.

Page moves and new files:

  - Migration guides moved from troubleshooting/migrate-to-{v2,1.10.X,
    1.8.2}.mdx to migrate/{v2,1.10.X,1.8.2}.mdx with 301 redirects from
    the old URLs in next.config.ts. Migrate gets its own first-class
    section with a lucide/RefreshCw icon — separate from
    Troubleshooting per IA-analysis pillar ("I'm upgrading deliberately"
    ≠ "something broke").
  - 4 concept placeholders + index under content/docs/concepts/. Each
    has frontmatter (title, description, icon, doc_type: explanation)
    and a TODO body referencing where the content will come from
    (existing repo material + blog posts). Slot exists; content
    backfills later.
  - New built-in-agent.mdx at root — 1-page summary that places BIA in
    Agents & Backends as a peer to copilot-runtime / custom-agent / etc,
    with a link out to the full BIA-scoped docs at /built-in-agent.

Other meta moves:

  - programmatic-control: Basics → Give Your App Agent Powers
  - inspector: Basics → Observe & Operate
  - vs-code-extension: Platform → Observe & Operate
  - multimodal-attachments: Platform → Build Chat UIs
  - runtime-server-adapter: Platform → Agents & Backends
  - The 3 migration pages: Troubleshooting → Migrate (with file moves)

Removed dead code: the `...unselected` line in top-level meta.json
was a no-op (unselected/meta.json has `root: true` so the spread
produced nothing). Stripped during the rewrite.

Multiple new visible labels are intentional. The renderer renders
sections (uppercase tracking-widest) and groups (medium-weight regular
case) at the same level when a section spreads a sub-meta with a
matching title — same pattern that's been live for 'Generative UI'
through every prior PR. The Build Generative UI nesting (Controlled /
Declarative / Open) is a deliberate three-types-of-gen-UI subgrouping
inside the section, surfaced via subsection headers in
generative-ui/meta.json's pages array.
2026-04-27 16:23:40 -07:00
Jordan Ritter 4320408805 fix(showcase-ops): use bare probe_id for run-history lookup (#4338)
## Summary

- Fix probe run history always returning empty — the invoker writes PB
rows with bare `cfg.id` (e.g. `e2e-deep`) but the HTTP handler queried
with the scheduler entry ID including the `probe:` prefix (e.g.
`probe:e2e-deep`), so they never matched

## Test plan

- [x] 1320 tests pass
- [x] Typecheck clean
- [ ] CI green
- [ ] After deploy, click probe:e2e-deep row on dashboard and confirm
"Recent Runs" populates
2026-04-27 15:49:40 -07:00
Jordan Ritter 1b89648f02 fix(showcase-ops): use bare probe_id for run-history lookup
The invoker writes PB rows with bare cfg.id (e.g. 'e2e-deep') but
the HTTP handler passed the scheduler entry id with 'probe:' prefix
(e.g. 'probe:e2e-deep') to writer.recent(). The IDs never matched,
so run history always returned empty.
2026-04-27 15:49:19 -07:00
Sam Julien 1a5ee31dc4 chore(showcase/starters): regenerate mastra starter for region marker drift 2026-04-27 15:09:50 -07:00
Sam Julien ac727e72ed docs(showcase/mastra): runtime-inject-tool sibling file for declarative-gen-ui
Completes the declarative-gen-ui cell for mastra (5/5 regions). The
runtime-inject-tool snippet teaches the TS-runtime injectA2UITool flag —
mastra's actual route bundles generateA2uiTool into weatherAgent so it
doesn't follow this pattern, but the docs page is teaching the runtime
feature itself, so a docs-only sibling file matches the established
pattern (see chat-component.snippet.tsx in agentic-chat).
2026-04-27 15:09:50 -07:00
github-actions[bot] 4dc2eea5ed style: auto-fix formatting 2026-04-27 15:09:50 -07:00
Sam Julien 3d68634670 docs(showcase/mastra): add region markers for shell-docs snippet coverage
Adds the @region[name] / @endregion[name] markers the shell-docs <Snippet>
mechanism needs to resolve mastra-scoped code blocks. Brings 10 cells from
"missing snippet" warnings to clean rendered code in the docs.

Two patterns:

  1. In-place markers (12 of the 14 file edits) — wrap existing demo source
     where it already reads as a clean teaching example. Comment-only,
     stripped from rendered output, zero runtime change.

  2. Sibling <region>.snippet.tsx files (2 new files) — for cases where
     the production demo carries QA hooks (frontend tools, render tools,
     agent context) that aren't relevant to the docs page being taught.
     Lives in the demo folder so the bundler picks it up; not imported,
     not rendered. Used for agentic-chat/chat-component (mastra Chat is
     QA-laden) and tool-rendering/render-flight-tool + catchall-renderer
     (mastra demo only renders weather, not flights or catchall).

Cells advanced to ready: prebuilt-popup, prebuilt-sidebar, frontend-tools,
chat-customization-css, agentic-chat-reasoning, chat-slots, agentic-chat,
tool-rendering, headless-complete, plus partial coverage on declarative-gen-ui
(provider-a2ui-prop only; runtime-inject-tool is missing on every framework
and needs a cross-cutting authoring round on langgraph-python first).

Out of scope: a2ui-fixed-schema and the runtime-inject-tool region on
declarative-gen-ui (cross-cutting — reference framework lacks them too);
shared-state-*, subagents, gen-ui-*, mcp-apps, open-gen-ui*, interrupt-headless
(stub or no-folder on mastra — engineering work).
2026-04-27 15:09:50 -07:00
github-actions[bot] aab249262f style: auto-fix formatting 2026-04-27 22:04:51 +00:00
Jordan Ritter f1ee30f243 fix: CR round 2 — scope envelope regression, rejected-promise side-row 2026-04-27 15:02:08 -07:00
Jordan Ritter 6b4dc68de4 fix: CR round 1 — hitl-steps allowlist, Semaphore guards, YAML accuracy, response envelope 2026-04-27 15:02:08 -07:00
Jordan Ritter 5c31e171cd docs: add showcase-ops memory budget note to RAILWAY.md
Documents the D5 e2e-deep Chromium memory envelope (4 services x 2
features = 8 concurrent contexts, ~2.4GB peak) and the two tuning
knobs operators should adjust if OOM occurs.
2026-04-27 15:02:07 -07:00
Jordan Ritter bb698a5819 test: add integration tests for bounded-parallel execution and feature-type filtering
Semaphore unit tests verify the counting semaphore gates concurrent
access correctly at limit=1, limit=2, and limit>=N. Feature parallelism
test confirms FEATURE_CONCURRENCY=2 produces peak context count of 2
when 4 features run. Feature-type filtering tests cover the driver-level
ctx.featureTypes filter (filtered-by-trigger note, all-filtered short-
circuit) and the HTTP handler validation (empty array, empty strings,
non-array, combined slugs+featureTypes, rate-limit non-consumption on
400). Graceful degradation test verifies correct aggregate results
regardless of concurrency level.

Exports Semaphore class and FEATURE_CONCURRENCY constant from
e2e-deep.ts for direct unit testing.
2026-04-27 15:02:07 -07:00
Jordan Ritter fba6132552 feat: apply featureTypes filter in e2e-deep driver before execution loop
When ctx.featureTypes is set from the trigger layer, partition runnable
features into kept and filtered sets before the execution loop begins.
Filtered features are recorded as skipped with a "filtered-by-trigger"
note so dashboards can distinguish trigger-scoped runs from no-script
skips. Logging emits the filter and excluded count when active.
2026-04-27 15:02:07 -07:00
Jordan Ritter 26df046389 feat: parse and validate featureTypes filter in trigger HTTP handler
Accept optional featureTypes in the trigger request body filter. Validates
that featureTypes is a non-empty array of non-empty strings when present,
returning 400 with a descriptive error otherwise. Forwards the validated
featureTypes into TriggerOptions alongside existing slug filtering.
2026-04-27 15:02:07 -07:00
Jordan Ritter 8e8745a382 feat: add bounded-parallel feature execution to e2e-deep driver
Replace the sequential for-loop over runnable features with a
semaphore-gated Promise.allSettled pool capped at
FEATURE_CONCURRENCY=2. Each feature already creates its own
independent browser context, so there is no shared mutable state
between concurrent runs. Results are collected from settled promises
to preserve input ordering. Setting FEATURE_CONCURRENCY=1 degrades
to identical sequential behaviour.
2026-04-27 15:02:07 -07:00
Jordan Ritter 5a8a25f871 feat: thread featureTypes filter through probe type system
Add optional featureTypes?: string[] to ProbeContext, TriggerOptions,
and InvokerTriggerOptions. The invoker extracts the field from trigger
opts and forwards it into the ProbeContext passed to each driver, so
drivers like e2e-deep can restrict their run scope to specific feature
types without changes to the scheduler or HTTP layer.
2026-04-27 15:02:07 -07:00
Jordan Ritter 920e61b392 chore: bump e2e-deep max_concurrency to 4 and timeout to 180s
Production memory headroom supports 4 parallel chromium contexts.
Higher concurrency cuts tail latency roughly in half; tighter timeout
matches the reduced per-worker wall-clock budget.
2026-04-27 15:02:06 -07:00
Jordan Ritter 48623f4faf fix(showcase): post-merge CI fixes for google-adk parity (#4318)
1. Add missing @json-render/core and @json-render/react deps to
   google-adk package.json (Docker build failed on byoc-json-render
   demo imports)
2. Update catalog test expectations to match actual generated counts
   (wired 482, crewai-crews 28, LGP 37 wired + 2 unshipped)
3. Regenerate google-adk starter from template to eliminate drift
   detected by the starter consistency check
2026-04-27 13:16:02 -07:00
github-actions[bot] b7dce5c1b4 style: auto-fix formatting 2026-04-27 19:54:21 +00:00
Alem Tuzlak 3daa7dbba0 fix: align google-adk starter with packages — route rewrite, deps, sample_agent
Rewrite starter route.ts to use HttpAgent (matching packages pattern),
fix default port to 8123, add full 37-agent list including sample_agent,
add missing @ag-ui/client dependency, register sample_agent in registry.
2026-04-27 12:52:53 -07:00
Alem Tuzlak 9c5a0ac850 fix: correct OGUI instructions, sandbox regex, and ternary precedence
Update open-gen-ui agent instructions to emit HTML directly instead
of fenced blocks (the runtime's generateSandboxedUi tool handles
rendering). Tighten sandbox evaluateExpression regex to reject **
exponentiation. Fix ternary operator precedence in 3 agent files
where string concatenation bound tighter than the conditional.
2026-04-27 12:52:53 -07:00
Alem Tuzlak ba5bf65881 fix: handle promise rejections in google-adk demo pages
Replace swallowed .catch(() => {}) with console.error logging in
headless-simple, headless-complete, and shared-state-read demos.
2026-04-27 12:52:53 -07:00
Alem Tuzlak f282a1674e fix: harden google-adk route error handling and add auth catch-all
Remove stack trace leaks from error responses, replace @ts-ignore
with @ts-expect-error, hoist CopilotRuntime to module scope, and
add [[...slug]] catch-all for the V2 auth endpoint so sub-route
requests don't 404.
2026-04-27 12:52:53 -07:00
Alem Tuzlak d8a208297d refactor(google-adk): track strip-prior-block in inject callbacks
Round 5 review surfaced a fragility in the empty-write guard. The
`if not new_text` short-circuit added in Round 4 correctly avoids
clobbering the LlmAgent static `instruction=` field when there's
nothing to inject — but if a prior callback DID embed a block in the
existing Content and the user has since cleared state, the early
return leaves the stale block in place because we never write the
stripped result back.

In practice this is unreachable: `_INSTRUCTION` is non-empty for all
three agents, so `original_text` after stripping always retains at
least the static instruction. But the explicit `stripped_prior_block`
flag makes the invariant honest if anyone ever wires up an LlmAgent
with an empty `instruction=` and removes the implicit dependency on
the static instruction being non-empty.
2026-04-27 12:52:53 -07:00
Alem Tuzlak ebb9247ccb style(google-adk): drop unnecessary {} fallback in state spreads
eslint-plugin-unicorn no-useless-fallback-in-spread — spreading
undefined into an object literal is a no-op, so '?? {}' is redundant.
Functionally identical, silences the lint warning.
2026-04-27 12:52:53 -07:00
Alem Tuzlak 5d3269c113 fix(google-adk): preserve static instruction + persisted notes
Two related issues surfaced in Round 4 review of the ADK showcase:

1. The before-model callbacks in agent_config_agent,
   readonly_state_agent_context_agent, and shared_state_read_write_agent
   guarded the empty-write branch with `if not new_text and original is
   None`. That missed the case where `original` is a non-None Content
   with empty text — possible after stripping a prior block that was
   the entire Content. In that branch the callback wrote
   Content(text=""), clobbering the LlmAgent's static `instruction=`.
   Tightened the guard to `if not new_text: return None`.

2. The shared-state-read-write seed effect spread the observed agent
   state but unconditionally assigned `notes: []`, wiping any
   backend-persisted notes when the user landed without persisted
   preferences. Falls back to `agentState?.notes ?? []` instead.
2026-04-27 12:52:52 -07:00
Alem Tuzlak d1d5a936b1 fix(showcase/google-adk): CR Round 3 — splice prior block, fail-loud guards, sanitize errors, spread state writes
Round 3 confirmation found bugs Round 2's fixes left behind in the same
files. All fixes target subject-scoped + load-bearing concerns.

bucket (a) findings:

A1 (strip-prior-block content loss): all three before-model-callback
agents (\`agent_config_agent\`, \`readonly_state_agent_context_agent\`,
\`shared_state_read_write_agent\`) sliced
\`original_text[end_idx + len(END_MARKER):]\` to remove the prior
injected block. That dropped content from index 0 to sig_idx as well —
fine for the steady-state pattern where our block is at position 0,
but if any other middleware ever prepends content to system_instruction
between turns, that prefix would be silently lost. Splice instead:
\`original_text[:sig_idx] + original_text[end_idx + len(END_MARKER):]\`.

A2 (empty-block + empty-original still rewrites system_instruction):
when both \`block\` is None and \`original\` is None, the callbacks were
still writing \`Content(role="system", parts=[Part(text="")])\`,
overwriting whatever the LlmAgent's \`instruction=\` field would have
contributed. Added an early return for that case so the static
instruction passes through.

A3 (sub-agent error message exposes raw exception text):
\`_invoke_sub_agent\` was raising
\`_SubAgentError(f"sub-agent call failed: {exc.__class__.__name__}: {exc}")\`.
\`{exc}\` for genai HTTP errors can include URLs, request IDs, partial
credentials, and quota details. The error string lands in
\`state["delegations"][i].result\` and ships to the public Railway
deploy. Sanitized to surface only \`exc.__class__.__name__\` to clients;
\`logger.exception\` keeps the full trace + str(exc) server-side.

A4 (agent.setState seed/handler effects clobber unrelated state keys):
\`agent.setState({ config: INITIAL_CONFIG })\` and the matching prefs
seed/handlers replaced the whole state object rather than merging. If
the runtime adds any other keys (e.g. \`copilotkit\` for the read-only
context middleware, future framework additions), they were wiped on
every config write. Now spread the observed state on every write.

A5 (\`_build_prefs_block\` missing the empty-recognized-keys guard): a
truthy prefs dict containing only unrecognized keys (schema drift /
forward-extension UI prop) would still emit a block consisting only of
\`[SIG, "Tailor every response to these preferences. ADDR_USER_BY_NAME"]\`,
i.e. directives without directives. Added the same
\`if len(lines) == 1: return None\` guard already present in
\`agent_config_agent._format_config\` and
\`readonly_state_agent_context_agent._format_context\`.

Bucket (b) defensive items applied:

- subagents \`_invoke_sub_agent\` parts extraction: dropped the redundant
  \`if content is not None\` ternary; \`getattr(None, "parts", None)\`
  already returns the default.

Starter regenerated.

Call-site enumeration:
- splice path inside \`_inject_config\`/\`_inject_context\`/
  \`_inject_preferences\`: signature unchanged; only callers are the
  matching LlmAgent before_model_callback bindings.
- \`_SubAgentError\` constructor message format changed (still str
  argument): consumers stringify it via str(exc); behaviour unchanged
  apart from the redacted exception body.
- \`agent.setState\` payloads in agent-config + shared-state-read-write
  pages: consumed only by the matching runtime route per page; the
  spread shape is a superset of the prior shape, so backend callbacks
  see the same fields plus any pre-existing keys (which they already
  read defensively).
- \`_build_prefs_block\` early-return: only consumer is
  \`_inject_preferences\` in the same module; new return path is None,
  same as the existing \`not prefs\` early return.
2026-04-27 12:52:52 -07:00
Alem Tuzlak 6323ccf205 fix(showcase/google-adk): CR Round 2 — fix bugs introduced by Round 1 + remaining bucket (a) cleanups
Round 2 (confirmation) surfaced bugs in code my Round 1 fixes touched.

bucket (a) findings:

- subagents_agent._update_delegation: dropped the synthetic
  \`sub_agent: "unknown"\` fallback I added in Round 1. The frontend's
  \`Delegation.sub_agent\` union is
  \`research_agent | writing_agent | critique_agent\`; an "unknown" value
  fell through SUB_AGENT_BADGE_CLASS / SUB_AGENT_LABEL maps and rendered
  as \`undefined\` text + className. Now we log loudly and skip — no row
  beats a malformed row.

- subagents_agent._invoke_sub_agent: cleaned up the
  \`getattr(content, "parts", None) or [] if content is not None else []\`
  ternary. \`getattr(None, ...)\` already returns the default, so the
  outer conditional was dead.

- agent_config_agent._format_config: added \`if len(lines) == 1: return None\`
  guard before appending CONFIG_END_MARKER. A truthy config dict with no
  recognized keys (schema drift / future-extension forwardedProps) was
  emitting a meaningless [SIGNATURE, END_MARKER] block telling the LLM
  to "Honour every directive above" with zero directives. Mirrors the
  same guard in readonly_state_agent_context_agent._format_context.

- delegation-log.tsx: added STATUS_BADGE_CLASS + STATUS_LABEL maps and
  a status-derived border-color so running/completed/failed entries are
  visually distinguishable. Without this, my Round 1 backend fix that
  actually emits all three states had no UI signal — every entry was
  the same muted gray. Now: amber spinner + "running…" while the
  sub-agent works, emerald on completion, red on failure.

- hitl-in-app/page.tsx: added an unmount cleanup that rejects every
  still-pending approval (with reason "user navigated away before
  responding") so the agent's tool-call promises never orphan when
  the user leaves the page mid-approval. Also added \`key={head.id}\`
  on ApprovalDialog so any future local state in the dialog component
  is per-request rather than leaking from one approval to the next as
  the head rotates.

- tool-rendering-custom-catchall: aligned the renderer's status type
  with what the runtime + page actually emit. The runtime's
  DefaultRenderProps status enum is
  \`"inProgress" | "executing" | "complete"\` and the page collapses both
  pre-completion variants to "executing"; there is no "incomplete"
  state today. The renderer's \`"incomplete"\` branch was unreachable
  dead code from a copy-paste of an older type. Narrowed the prop type
  to \`"executing" | "complete"\` and dropped the dead branch.

- manifest.yaml highlight arrays: 7 demos with dedicated runtime
  routes (a2ui-fixed-schema, declarative-gen-ui, byoc-json-render,
  byoc-hashbrown, auth, open-gen-ui, open-gen-ui-advanced) were
  pointing at the unified \`src/app/api/copilotkit/route.ts\` instead
  of their own dedicated route file. Documentation drift the showcase
  tooling uses to render source listings — pointed each at its real
  route. Also added \`src/app/demos/auth/demo-token.ts\` to the auth
  highlight list so the bundled demo-token note is visible alongside
  the page.

bucket (b) cheap items applied:

- AgentSpec.predict_state: \`Iterable\` → \`Sequence\`. A one-shot
  generator passed by mistake would empty after the first request;
  Sequence forces a re-iterable container. Module-level lists already
  satisfy this contract.

Starter regenerated to keep drift-check green.

Call-site enumeration:
- \`STATUS_BADGE_CLASS\`, \`STATUS_LABEL\`, \`ENTRY_BORDER_BY_STATUS\`
  (new): private to \`delegation-log.tsx\`.
- \`pendingQueueRef\` (new): private to hitl-in-app/page.tsx unmount cleanup.
- \`_update_delegation\` signature unchanged; only the missing-entry
  branch behaviour changed (no longer appends).
- \`AgentSpec.predict_state\` widened constraint
  (Iterable→Sequence): only consumed in agent_server.py's
  ADKAgent construction; passing a list still satisfies Sequence.
- CustomCatchallRendererProps.status narrowed
  (executing|complete|incomplete → executing|complete): only
  consumer is the page in the same demo folder; it now passes a
  matching union.
2026-04-27 12:52:52 -07:00
Alem Tuzlak 8804c5d0e0 fix(showcase/google-adk): wire OGUI runtime + boost streaming + state-hydration race + cheap (b) cleanups
CR Round 1 remaining bucket (a) findings + cheap (b) items:

A1 (streaming claim alignment): \`shared_state_streaming_agent.py\` no
longer carries an empty \`GenerateContentConfig()\` with comments that
claimed it enabled per-token streaming. Per-token streaming is driven
by (i) \`PredictStateMapping(stream_tool_call=True)\` set on the
ADKAgent middleware and (ii) the new
\`streaming_function_call_arguments=True\` kwarg added to the AgentSpec
+ agent_server.py wiring. The kwarg degrades gracefully on google-adk
< 1.24.0 with a UserWarning per the upstream contract.

A8 (useEffect race): both \`agent-config/page.tsx\` and
\`shared-state-read-write/page.tsx\` previously seeded INITIAL_* with
empty deps on mount, which fired BEFORE the runtime hydrated agent.state
on a page reload — overwriting backend-persisted config/preferences with
defaults. Replaced with a \`seededRef.current\` flag that gates the seed
to a single emission AFTER agent.state has been observed at least once.
Effect now correctly lists \`agent\` + \`agentState\` deps (no eslint
disable).

A9 (notes-card stable key): \`shared-state-read-write/notes-card.tsx\`
\`key={i}\` for plain-string lists used pure positional identity.
Replaced with \`key={i}::\${note}\` so DOM identity tracks both
position and content — agent rewrites of the notes list no longer
attach scroll/selection state to the wrong row.

A10 (hitl-in-app concurrent approvals): replaced
\`useState<ApprovalRequest | null>\` with a queue. Gemini's parallel
tool-call paths can fire \`request_approval\` more than once before the
first resolves; the previous single-slot state orphaned the first
promise (the agent waited forever). Queue head renders, every approval
resolves in arrival order, removal is filter-by-id-safe under
concurrent resolves.

A13 (OGUI runtime config): \`copilotkit-ogui/route.ts\` was missing
\`openGenerativeUI: { agents: [...] }\` on the CopilotRuntime. Without
it the OpenGenerativeUIMiddleware never engaged, so the
\`generateSandboxedUi\` tool calls from \`open_gen_ui\` /
\`open_gen_ui_advanced\` never converted to activity events and the
sandboxed iframe stayed empty. Verified the langgraph-python sibling
configures it the same way.

A14 (booked_schema parity): every sibling integration loads
\`BOOKED_SCHEMA = a2ui.load_schema(...)\` for parity even though the
A2UI Python SDK does not yet accept \`action_handlers\` for swap-on-
click; google-adk's \`a2ui_fixed_agent.py\` was the only package to
ship the JSON without loading it. Added the load + manifest highlight
entries.

A15 (handleClearNotes stale closure): same file as A9. Both handlers
now read latest agent state via the closure-fresh \`agentState\`
rather than the destructured \`preferences\` / \`notes\` snapshots that
went stale under rapid concurrent edits.

Bucket (b) cheap items applied:
- \`agent_server.py\`: \`reload=True\` → env-gated via UVICORN_RELOAD,
  so production runs aren't burning CPU on file-watching.
- \`agent_server.py\` /agents/state docstring: now explicitly names the
  FIRST registered agent (\`_simple_chat\`-shared demos work; bespoke-
  LlmAgent demos store sessions in their own ADKAgent's service that
  /agents/state doesn't reach — STATE_DELTA via useAgent still works).
- \`registry.py\`: stale "Already-implemented demos" header dropped;
  \`_SIMPLE_CHAT_INSTRUCTION\` rewritten to be tool-agnostic so the
  hint matches every demo that shares this agent (frontend-tools,
  headless-simple's \`show_card\`, etc.).
- \`tool-rendering-custom-catchall/page.tsx\`: \`: any\` cast on render
  args replaced with inferred types + an explicit status-narrow at the
  call site.
- \`voice/page.tsx\`: empty \`input={{}}\` prop removed — was overriding
  CopilotChat's default input slot with no override at all.
- \`byoc-json-render\` page+route: agent name renamed to
  \`byoc-json-render-demo\` (kebab + \`-demo\` suffix) to match
  sibling \`byoc-hashbrown-demo\` / \`a2ui-fixed-schema\` /
  \`auth-demo\` convention.

Starter regenerated to keep drift-check green.

Call-site enumeration:
- \`AgentSpec.streaming_function_call_arguments\` (new field): only
  consumed in agent_server.py's \`ADKAgent(...)\` construction.
- \`SHARED_STATE_STREAMING_PREDICT_STATE\`: re-exported, signature
  unchanged.
- \`AGENT_REGISTRY\` shared-state-streaming entry now passes
  streaming_function_call_arguments=True; no other callers.
- \`byoc-json-render-demo\` agent string: only consumed in the
  matching dedicated route + the corresponding page.tsx; both
  updated together.
2026-04-27 12:52:52 -07:00
Alem Tuzlak 0d86bab891 docs(showcase/google-adk): correct stale path + wiring claims in demo doc-comments
CR Round 1 bucket (a) finding A12: doc-comments referenced files and
wiring patterns that don't match the actual code in this package.

- a2ui-fixed-schema/page.tsx: \`src/agents/a2ui_fixed.py\` →
  \`src/agents/a2ui_fixed_agent.py\` (actual filename)
- declarative-gen-ui/page.tsx: \`src/agents/a2ui_dynamic.py\` →
  \`src/agents/declarative_gen_ui_agent.py, which re-exports the
  generate_a2ui tool defined in src/agents/main.py\`
- auth/demo-token.ts: header referenced \`use-demo-auth.ts\` (no such
  file in this package); now points at \`./page.tsx\` and adds a
  reminder that the constant ships into the browser bundle.
- open-gen-ui/page.tsx: rewrote to describe the actual two-pronged
  wiring — \`openGenerativeUI\` on the runtime AND on the provider —
  rather than just naming the runtime; the prior single-source claim
  obscured the activity-renderer activation that lives on \`<CopilotKit>\`.
- byoc-hashbrown/page.tsx: removed \"Wave 4a\" internal sequencing
  label; \"langgraph-python agent\" → \"Google ADK byoc_agent\".
- byoc-json-render/page.tsx: \"the LangGraph agent\" → \"the Google
  ADK byoc_agent\"; \"Wave 4a's hashbrown demo\" → \"the
  byoc-hashbrown demo\".

Comments-only change. No behavioural impact.

Call-site enumeration: doc-comments are leaf changes; no symbols
added, removed, or renamed; no call sites to enumerate.
2026-04-27 12:52:39 -07:00
Alem Tuzlak 1bf78426f0 fix(showcase/google-adk): inject-callbacks fail-loud + strip-stale-block on bad state + agent-config docstring
CR Round 1 bucket (a) findings A6, A7, A11:

A6 (silent no-op on malformed state): all three before-model-callback
agents (agent_config, readonly_state_agent_context, shared_state_read_write)
imported `logging.getLogger(__name__)` but never called the logger when
the frontend sent a non-dict / non-list payload. Schema drift was
indistinguishable from "user hasn't set anything yet". Now log
\`logger.warning(...)\` with the observed type so the regression surfaces
server-side.

A7 (readonly-context: early returns leave stale prior block): when
\`state["copilotkit"]\` or \`state["copilotkit"]["context"]\` was the
wrong shape, \`_inject_context\` returned without reaching the
strip-prior-block path — any context block injected on a previous turn
would stay embedded in \`system_instruction\` indefinitely. Now coerce
malformed state to \`raw_entries = []\` so the strip path always runs.

A11 (agent_config docstring stale): top-of-file docstring listed only
\`tone/expertise/response_length\` but the impl + frontend also handle
\`language\`. Updated.

Also extracted the trailing-sentence end markers into module-level
constants (\`CONFIG_END_MARKER\`, \`CONTEXT_END_MARKER\`,
\`PREFS_END_MARKER\`) so the strip logic and the \`_format_*\` builders
can't drift apart from a one-place edit. Added a warning log when a
signature is present but the end marker is missing — previously the
strip silently no-op'd and prior blocks would stack on subsequent turns.

Call-site enumeration:
- \`CONFIG_END_MARKER\`, \`CONTEXT_END_MARKER\`, \`PREFS_END_MARKER\`
  (new): private to their respective modules; only consumed by the
  builder + strip pair within each file.
- \`_format_config\`, \`_format_context\`, \`_build_prefs_block\` (logic
  changed): private to their modules; unchanged signature.
- \`_inject_config\`, \`_inject_context\`, \`_inject_preferences\` (logic
  changed): wired only into LlmAgent's \`before_model_callback\`;
  signature unchanged.
2026-04-27 12:52:39 -07:00
Alem Tuzlak 532eb85f63 fix(showcase/google-adk): subagents fail-loud + emit running state + defensive content access
CR Round 1 bucket (a) findings A2-A5:

A2 (errors recorded as completed): \`_record_delegation\` always set
\`status: "completed"\` even when \`_invoke_sub_agent\` returned a
\`(sub-agent error: …)\` string. The frontend's "failed" badge style was
defined but never reached. Replaced with a typed \`_SubAgentError\` plus
a \`_delegate\` flow that appends a "running" entry, invokes, then
flips to "completed"/"failed" with the actual error message.

A3 (never emits running): the Delegation type union has
\`"running"|"completed"|"failed"\` but only "completed" was ever written.
Now appended at delegation start; demo's "live delegation log" claim is
truthful.

A4 (AttributeError on safety-blocked responses):
\`getattr(candidates[0].content, "parts", None)\` directly accessed
\`.content\` — None content (safety-blocked, empty parts) raises
AttributeError on the inner getattr. Now guards content via getattr
first.

A5 (exception list too narrow): \`except (genai_errors.APIError,
ValueError)\` let httpx.ConnectError, asyncio.TimeoutError, RuntimeError
propagate up and crash the supervisor's tool turn. Catching the broad
\`Exception\` is appropriate here because the user-visible surface is a
structured \`{status: "failed", error}\` dict — server-side traceback is
preserved via \`raise … from exc\`.

Tool return shape changed from \`{"result": str}\` to
\`{"status": "completed"|"failed", result?, error?}\`. Supervisor
instruction updated to describe the new shape so the LLM can handle
sub-agent failures explicitly instead of fabricating a reply.

Call-site enumeration:
- \`_SubAgentError\`, \`_append_delegation\`, \`_update_delegation\`,
  \`_delegate\`: new private helpers, no external callers
- \`_record_delegation\` (removed): was private, no callers outside this
  module
- \`research_agent\`/\`writing_agent\`/\`critique_agent\` (return shape):
  only consumed by the LLM via tool-calling. Frontend reads
  \`state["delegations"]\` directly; its Delegation type already has
  the full status union.
- \`subagents_root_agent\` re-export: unchanged; registry import still
  resolves.
2026-04-27 12:52:39 -07:00
Alem Tuzlak efc17c677d fix(showcase/google-adk): add docs-links entries for all 27 new features
Without per-feature override entries, the dashboard's DocsRow falls
through to the global feature probe in shared/feature-registry.json.
For features whose canonical docs path doesn't exist on
docs.copilotkit.ai (e.g. an ADK-scoped page for shared-state-streaming
where only the langgraph-* equivalent is published) the probe returned
notfound and the docs/shell columns rendered as red ✗.

Mirror the pre-existing 8 entries' pattern: each new feature gets either
\`og_docs_url\` pointing at the real /adk/... page where one exists, or
\`og_docs_url: null\` for features without a canonical ADK page (which
the cell renders as an honest gray "opt-out" dot rather than red ✗).
\`shell_docs_path\` stays null for every feature — there's no
google-adk-scoped docs tree under shell-docs (PR #4085).
2026-04-27 12:52:39 -07:00
Alem Tuzlak 4b46cb4953 test(showcase/scripts): update catalog metadata expectations for google-adk parity
\`generate-catalog.test.ts\` hard-codes the expected wired/stub/unshipped
counts. Adding ~27 demos to google-adk shifted wired 526 → 552, stub
8 → 9, unshipped 203 → 176. Verified via
\`pnpm exec tsx generate-registry.ts\` against the fresh manifest.
2026-04-27 12:52:34 -07:00