Commit Graph

11982 Commits

Author SHA1 Message Date
Mike Ryan 0e5bf3c582 chore: sync with origin/main 2026-06-23 18:14:16 -07:00
Mike Ryan 927e265c36 chore: sync with origin/main 2026-06-23 18:12:31 -07:00
Jordan Ritter b7d9e1f0e1 chore(showcase): remove redundant harness-legacy service from SSOT and fixtures (#5658)
## Why

The pool-fleet migration is **complete**. The control-plane harness plus
the prod workers (deployed 2026-06-19, `HARNESS_ROLE=worker`, pool count
2) are live in both envs and cover every probe dimension the interim
`harness-legacy` fleet-migration bridge was holding live.
`harness-legacy` is now dead config.

This PR is the **code-side cleanup only**. The live Railway
`harness-legacy` service is torn down separately (see follow-up below).

## What changed

- **SSOT** (`showcase/scripts/railway-envs.ts`): removed the entire
`harness-legacy` entry and the now-dead `key === "harness-legacy"`
special-case branch in `computePromoteClosure`. Updated stale doc
comments that referenced legacy.
- **Stale `harness-workers` comment fixed**: it claimed the worker is
"STAGING-ONLY". The prod worker is in fact live on Railway (deployed
2026-06-19, `HARNESS_ROLE=worker`, pool count 2). Comment now states
workers run in BOTH envs, while flagging that this SSOT entry still
models only the staging instance — see ambiguity note below.
- **Generated artifact**: regenerated `railway-envs.generated.json` (41
-> 40 services).
- **Golden snapshot** (`__tests__/fixtures/railway-envs.golden.json` +
its test header): dropped the `harness-legacy` block.
- **Fixtures / expectation sets**: removed `harness-legacy` from the
`GATE_IGNORED` sets in `__tests__/verify-railway-image-refs.test.ts`,
the `envsFor` assertion + the `computePromoteClosure` exclusion test in
`railway-envs.test.ts`, the promote-notify fixtures (`success.json`,
`partial.json`, `total-failure.json`), and the `redeploy-env.ts` doc
comments. Updated service-count assertions (41 -> 40) in
`railway-envs.test.ts`, `__tests__/verify-railway-image-refs.test.ts`,
and `__tests__/emit-railway-envs-json.test.ts`.
- **Ruby spec**
(`bin/spec/test_promote_single_service_fleet_invariants.rb`): renamed
the synthetic prod-only fixture name from `harness-legacy` to
`deprecated-prod-only-svc` (a fabricated example name with no SSOT
coupling; renamed to avoid implying the SSOT still carries legacy).

## Red-green proof

This is a behavior change to the SSOT. A new assertion (`railway-envs
SSOT > does NOT contain harness-legacy`) was added and observed FAILING
with the entry still present, then PASSING after removal.

**RED** (assertion added, SSOT entry still present):
```
 FAIL  railway-envs.test.ts > railway-envs SSOT > does NOT contain harness-legacy (fleet-migration bridge retired)
AssertionError: expected [ Array(41) ] to not include 'harness-legacy'
 ❯ railway-envs.test.ts:137:23
    136|     const names = listServiceNames();
    137|     expect(names).not.toContain("harness-legacy");
       |                       ^

 Test Files  1 failed (1)
      Tests  1 failed | 103 skipped (104)
```

**GREEN** (SSOT entry removed + fixtures updated):
```
 Test Files  4 passed (4)
      Tests  171 passed (171)
```
(`railway-envs.test.ts`, `__tests__/verify-railway-image-refs.test.ts`,
`__tests__/railway-envs.golden.test.ts`,
`__tests__/emit-railway-envs-json.test.ts`)

Ruby spec:
```
5 runs, 42 assertions, 0 failures, 0 errors, 0 skips
```

## Pre-push gates

- Formatter (`oxfmt --check`): green
- Lint (`oxlint`): 0 errors (2 pre-existing warnings in
`redeploy-env.ts`, unrelated)
- Typecheck (`tsc -p scripts/tsconfig.json`): green for changed files (1
pre-existing unrelated error in `generate-search-index.ts`, present on
the clean base)
- Tests: full `scripts` suite 2086 passed / 12 skipped. (Two test files
hit a flaky `/tmp/...-generated-data.lock` EEXIST parallel-mkdir race;
they pass when run serially and do not touch `harness-legacy`.)

## Follow-up infra step (NOT in this PR)

- [ ] Delete the live Railway `harness-legacy` service (id
`11279eba-97eb-417e-82a5-7cb4254eb147`) from **both** staging and prod
environments. Manual infra step handled separately by the orchestrator.

## Ambiguity note (prod-worker SSOT entry)

The `harness-workers` SSOT entry currently declares **only** a `staging`
env — there is no `prod` env entry, even though a prod worker is live on
Railway. Per task scope I did **not** invent the prod `serviceInstance`
config (it isn't verifiable from the repo), so I only corrected the
comment to stop asserting "staging-only" and left the entry shape
unchanged. Backfilling a real `prod` env entry is a separate follow-up.

---

## Update: README count sync + code review

Added commit `5859ae4` (`docs(showcase): sync promote-notify README
counts...`): the harness-legacy removal dropped each promote-notify
fixture by one service, so `test-fixtures/promote-notify/README.md` was
updated 29/26/29 → **28/25/28** (counts derived from the fixtures via
`jq`, not hand-set).

**Code review:** 11-agent CR round + 11-agent confirmation round, both
converged to **0 mandatory (bucket a) findings**; Procedure 3 bucket-(c)
promotion audit returned **PROMOTE_TO_A = 0**. The harness-legacy
removal is clean — no dangling SSOT references, generated JSON 41→40
exact, no behavior change.

### Follow-up backlog (NOT this PR — surfaced by CR)

_Distinct-subject (candidate spin-off PRs):_
- **prototype-key hardening** — `verify-railway-image-refs.ts:268`
`findUntrackedServices` uses bare `SERVICES[name]` instead of
`Object.hasOwn`; a Railway service named `constructor`/`toString` is
silently treated as tracked → Railway↔SSOT drift false-negative (real
bug, 2 reviewers).
- **harness-workers prod-backfill** — SSOT declares only a staging env
though a prod worker is live; promote-notify fixtures encode
harness-workers as a promote outcome vs SSOT-skipped;
`serviceId===prodInstanceId` smell.
- **promote-notify fixture fleet completeness** — fixtures model a
28-service fleet, omit the 12 `starter-*` services (live fleet = 40).

_Pre-existing subject-neutral nits:_ `emit-railway-envs-json.ts`
`--check` uncurated-crash edge; `serviceEnvPairs()` unconsumed export;
orphaned `promote-notify/validate.sh`; stale "starter-* staging-only"
rationale in the Ruby fleet spec; truncation-suffix/`failed_count`
README contract with no fixture coverage; unguarded `sibling` nil in the
Ruby spec.
2026-06-23 18:05:51 -07:00
Jordan Ritter ec646bbf4f Merge remote-tracking branch 'origin/main' into chore/remove-harness-legacy-ssot
# Conflicts:
#	showcase/scripts/railway-envs.generated.json
#	showcase/scripts/railway-envs.ts
2026-06-23 17:56:18 -07:00
Mike Ryan c0536e4e7f fix(inspector): remove superseded owned thread stores 2026-06-23 17:48:10 -07:00
Jordan Ritter eaa3a0a3fd fix(showcase): honest promote-notify message + durable healthcheckPath tracking (#5657)
## Summary

Two related fixes to the showcase promote pipeline:

**Promote-notify Slack message — say what actually happened.**
Previously a partial promote rendered a message that read as if
*everything* failed, with no indication of which services promoted vs.
failed. Now the message:
- announces `🚂 Promoting showcase → prod (N): <names>` with a legible
count
- on a partial/failed outcome leads with `Promoted:` / `Failed:` (one
header each, then bullets) so you can see exactly which services
succeeded and which didn't
- reports real wall-clock elapsed (integer-coerced; degrades to omitting
the phrase if metadata is unparseable)
- drops the constant `verify-prod:` legend line that preceded the
meaningful data

**Durable healthcheckPath tracking — stop the silent prod drift.** The
promote pin path and the provisioning path could leave a service's
Railway `healthcheckPath` diverged from intent (this is what left aimock
answering on the wrong path in prod). Now:
- `healthcheckPath` is tracked per-service/per-env in the SSOT
(`railway-envs`)
- the promote pin re-asserts it (omit-when-absent — never sends `null`)
- `deploy-to-railway` provisioning routes through `isTrackedService` /
`resolveProvisionHealthcheck`, so a tracked-null service correctly
*omits* the healthcheck while an untracked one keeps the `/api/health`
default

## Test plan
- [x] `test_promote_healthcheck_reassert.rb` — pin re-asserts SSOT
healthcheck, omits when absent, never null (3 runs / 11 assertions)
- [x] `deploy-to-railway.healthcheck.test.ts` + `railway-envs.test.ts` +
`emit-railway-envs-json.test.ts` (121 tests)
- [x] snapshot-ivar lint green (2 runs / 32 assertions)
- [x] notify renderer dry-run mirrors success / partial / total-failure
shapes
2026-06-23 17:44:44 -07:00
Mike Ryan d5d8b6b75d fix(inspector): use unregistered thread store payload 2026-06-23 17:38:04 -07:00
Mike Ryan 4f7ba3c16a fix(threads): resolve runtime and inspector edge cases 2026-06-23 17:29:45 -07:00
Jordan Ritter 5859ae4057 docs(showcase): sync promote-notify README counts after harness-legacy removal
PR #5658 dropped harness-legacy from the fixtures but the README still
showed pre-removal counts, contradicting the edited fixtures.
2026-06-23 17:19:29 -07:00
Mike Ryan 05ca99aef3 test(inspector): avoid inline telemetry import type 2026-06-23 17:10:26 -07:00
Mike Ryan f5d91758ac fix(inspector): align thread recency telemetry 2026-06-23 17:09:44 -07:00
Jordan Ritter 0809c25dac chore(showcase): remove redundant harness-legacy service from SSOT and fixtures
The pool-fleet migration is complete: the control-plane harness plus the
prod workers (deployed 2026-06-19, HARNESS_ROLE=worker, pool count 2) now
cover every probe dimension the interim `harness-legacy` fleet-migration
bridge was holding live, so `harness-legacy` is dead config.

This is the code-side cleanup only:
- Remove the `harness-legacy` entry from the railway-envs SSOT and the
  now-dead `key === "harness-legacy"` special-case in computePromoteClosure.
- Regenerate railway-envs.generated.json (41 -> 40 services).
- Drop harness-legacy from the golden snapshot, the gateIgnore expectation
  sets, the promote-notify fixtures, and the redeploy-env doc comments;
  update the service-count assertions (41 -> 40).
- Fix the stale "STAGING-ONLY" harness-workers comment: prod workers are
  live on Railway, though this SSOT entry still models the staging
  instance only (no prod env backfilled here yet).

The live Railway `harness-legacy` service is torn down separately as a
follow-up infra step.
2026-06-23 17:05:26 -07:00
Jordan Ritter 59e18693eb feat(showcase): honest promote-notify message + durable healthcheckPath SSOT tracking
Promote-notify Slack message: name the promoted AND failed services (one
Failed: header + bullets), legible "(N): <names>" count, real wall-clock
elapsed (integer-coerced), and drop the constant verify-prod legend line.

Durable healthcheckPath: track it per-service/env in the SSOT (railway-envs),
re-assert it on the promote pin path (omit-when-absent, never null), and route
deploy-to-railway provisioning through isTrackedService/resolveProvisionHealthcheck
so a tracked-null service omits the healthcheck while an untracked one keeps the
/api/health default — fixing the silent prod-healthcheck drift that refused aimock.

Tests: ruby pin-reassert spec + deploy-to-railway healthcheck spec + emit/golden/accessor.
2026-06-23 16:56:16 -07:00
Mike Ryan 2480902489 fix(inspector): keep credential reloads authoritative 2026-06-23 16:53:23 -07:00
Mike Ryan 1bfbe843f3 fix(inspector): reset thread details on credential changes 2026-06-23 16:42:15 -07:00
Mike Ryan 74a0a30fe1 fix(threads): refresh credential changes across clients 2026-06-23 16:31:25 -07:00
Mike Ryan 5a4126ddfd fix(threads): refresh credentialed thread contexts 2026-06-23 16:15:42 -07:00
Mike Ryan 244f8a3d7b fix(threads): complete inspector runtime context 2026-06-23 15:56:14 -07:00
Tyler Slaton 2ea3e66702 fix: repair check-types across all packages and gate it in CI (#5365)
## What this does

Follow-up to #5356. CI never ran `check-types`, so type errors piled up
silently across the monorepo. #5356 fixed shared, a2ui-renderer, and
angular. This PR fixes every remaining package and adds a CI gate so it
cannot happen again.

Almost all of the diff is mechanical type repair: type annotations,
`import type` splits, casts, ambient declarations, and tsconfig
module-resolution bumps. The sections below call out the parts that are
not purely mechanical so review can focus there.

## Where to focus your review

These are the only changes with runtime or public-API impact. Everything
else is type-level and behavior-preserving.

1. **License gating wired to /info** (shared, react-core, vue,
react-native). Behavioral change, details below.
2. **react-native bug fix**: a `catch` binding referenced by
`TypeError.cause` had been lint-stripped and is restored, plus a `{
cause }` is now attached to a parse error.
3. **runtime `Schema` passthrough**: a type-only cap on `tool()` schema
inference. Runtime behavior is unchanged; it only stops tsc from blowing
past 8 GB.
4. **New public type export**: `Anchor` from web-inspector, consumed by
react-core's `inspectorDefaultAnchor`.
5. **New CI job**, see the CI section.

## License context wired to /info

Follow-up to @MikeRyanDev's review on #5356 (wire the inert license
machinery to /info). `createLicenseContextValue` now takes the
server-reported license status instead of a hardwired null.
`checkFeature()` returns false only when the runtime reports `expired`
or `invalid`, and fails open otherwise (null, none, expiring, and valid
all keep features on). The React, Vue, and React Native providers feed
the status they already track from /info. New tests cover the gating in
shared and react-core. Per-feature data is not in /info yet, so gating
is uniform across features.

## Type fixes by package

- **core** (392 errors): bundler module resolution (matches the tsdown
build and the vue package), strict-mode and AG-UI drift in sources and
tests, added `@types/phoenix`.
- **react-core** (255): component, hook, and test drift (now-private
`activeRunCompletionPromise`, RunFinished outcome union, slot prop
types, StandardSchema variance), ambient declarations for `katex` CSS
and the react-markdown JSX namespace, plus the `Anchor` export.
- **runtime** (393, previously hidden behind an OOM crash):
BuiltInAgentConfiguration narrowing, AI SDK v6 and AG-UI drift, the
UserMessage attachment migration, OpenAI v4/v5 surface changes, and the
`Schema` passthrough noted above.
- **react-native** (23): provider/core bridge types, RN 0.85 drift, and
the `catch`/`cause` bug fix noted above.
- **smaller fixes**: runtime-client-gql (UserMessage image narrowing,
pinned `types`), web-inspector (nodenext import extensions, RunFinished
narrowing), react-textarea (es2023 lib for `toReversed`), react-ui,
voice, agentcore-runner, sqlite-runner (one-liners), sdk-js (bundler
resolution plus the missing `@standard-schema/spec` dev dep),
examples/v2/node (drop stale node10 resolution overrides).

## CI

New `check-types` job in `static_quality.yml`: run graphql codegen, then
`nx run-many -t check-types --parallel=1` with a 12 GB heap. The runtime
typecheck alone peaks near 10 GB and takes about 5 minutes on a 16 GB
runner, so it runs serially.

## Testing

- `nx run-many -t check-types` passes for all 24 projects locally and in
CI.
- All package test suites pass: core 431, react-core 1271, runtime 1523,
vue 1001, react-native 246, runtime-client-gql 129, plus the smaller
packages.
- Lockfile diff is only the two added type-only dev dependencies
(`@types/phoenix`, `@standard-schema/spec`).

## Follow-ups (not in this PR)

- react-core's built `.d.mts` files emit extensionless relative imports,
which silently degrade consumer types to `any` under `skipLibCheck`
(found while fixing react-native).
- The AI SDK x zod type-instantiation cost in runtime deserves a real
fix (40M instantiations); trace data available.
- `@ai-sdk/anthropic`'s new `authToken` setting is not forwarded by the
Anthropic adapter.
- `MCPClientProvider.tools()` has diverged from AI SDK v6's `ToolSet`
typing.
2026-06-23 15:41:18 -07:00
Mike Ryan 137a78233d fix(threads): address confirmation lifecycle gaps 2026-06-23 15:39:32 -07:00
Tyler Slaton 6d9397c42e Preserve quickstart CTA styling in shell docs reference pages (#5654)
## Summary
- Add a dedicated `shell-docs-primary-cta` style for the hero quickstart
link so it keeps the primary CTA color inside reference content.
- Cover the new class usage in the hero and framework overview tests.

## Testing
- Updated unit tests to verify the quickstart CTA class and the matching
global CSS override.
- Updated unit tests to verify the framework overview markup includes
the primary CTA class.
2026-06-23 15:27:06 -07:00
Austin Merrick 4ba201b5c4 fix: repair check-types across all packages and gate it in CI
Repairs TypeScript check-types across the monorepo and adds a CI gate so
regressions are caught going forward:

- core: bundler module resolution and strict-mode fixes
- sdk-js: bundler module resolution; keep codegen, formatter, packaging working
- react-core: fixes across components, hooks, and tests
- react-native: restore catch binding referenced by TypeError cause
- runtime: repair check-types and bound AI SDK schema inference
- web-inspector: nodenext import extensions, export Anchor
- remaining packages and node example: assorted check-types repairs
- deps: add missing type-only devDependencies
- license context driven from /info licenseStatus
- ci: run check-types in the static quality workflow

Squashed from 12 commits for a single, easily-revertable change.
2026-06-23 15:26:47 -07:00
Tyler Slaton 307a52c8eb Preserve quickstart CTA styling in reference content 2026-06-23 15:26:36 -07:00
Jordan Ritter 3b9696265d fix(showcase): recover ag2 + llamaindex staging from crash-loops (#5642)
## Summary

Both the **AG2** and **LlamaIndex** showcase integrations were 100% red
on the staging matrix (every cell `BE ×`, 0 green all day). Root cause
was **not** per-cell logic — each framework's Next.js frontend was
crash-looping under matrix load, driven by an agent-side hot loop. This
PR fixes both root-cause loops.

### LlamaIndex — `sse-missing` (commit forwarding request-time tools)
- **Root cause:** the llama-index AG-UI adapter never forwarded
`RunAgentInput.tools`, so page-injected tools (`toggleTheme`,
`pieChart`, `barChart`, `scheduleTime`, MCP, `generateSandboxedUi`) were
invisible to the LLM → `RUN_ERROR` (404 `no_fixture_match` →
`openai.NotFoundError`) → no `RUN_FINISHED` → `sse-missing`. The retry
storm OOM-crashed the frontend (Node fatal, `next-server` v15.5.18).
- **Fix:** new `RequestAwareAGUIChatWorkflow` (`_request_tools.py`) that
forwards request tools, re-roles the tool-result on an LLM-bound copy
only, and skips duplicate frontend-tool chunk emission (bare snapshot
already carries them). Applied to beautiful_chat / mcp_apps /
open_gen_ui / open_gen_ui_advanced routers; added the `search_flights`
backend tool to beautiful_chat for langgraph-python parity.
- **Red→green:** beautiful-chat **0/5 → 5/5** (toggle-theme,
schedule-meeting, search-flights, pie-chart, bar-chart), freshly
re-confirmed via `--d6 --rebuild`; `mcp-apps` red→green.

### AG2 — `generate_a2ui` empty-arg validation loop
- **Root cause:** the declarative route pointed `HttpAgent` at the root
catch-all agent instead of the dedicated `/declarative-gen-ui` mount,
and `generate_a2ui` required a `context` arg the model emits as `{}` →
pydantic `context Field required` → infinite retry. **630 loop
iterations** observed; the flood starved the ag2 frontend (502s).
- **Fix:** dedicated mount + `injectA2UITool:false` + no-arg
`generate_a2ui` (matches langgraph-python / google-adk) + regenerated
aimock fixture to 4-pill parity + ported the stale ag2
renderers/definitions.
- **Red→green:** **630 → 0** validation errors, `runsFinished=1`, clean
`generate_a2ui → TOOL_CALL_RESULT → RUN_FINISHED`.

## Known remaining (pre-existing, separate, NOT regressions)
- **ag2 `gen-ui-declarative`:** run completes but the a2ui surface
components don't paint (autogen↔AG-UI bridge `TOOL_CALL_RESULT` →
A2UIMiddleware surface-conversion gap). Tracked separately.
- **llamaindex `open-gen-ui`:** `sse-missing` resolved; residual
sandboxed-UI iframe render gap is **pre-existing** (red since
2026-06-01, fc=509 — predates this crash).

## Test plan
- [x] LlamaIndex beautiful-chat `--d6 --rebuild`: 5/5 green (fresh,
isolated)
- [x] LlamaIndex mcp-apps: red→green
- [x] AG2 declarative hot-loop: 630→0 validation errors, runsFinished=1
- [x] Pyright clean on both integrations (verified in-Docker with real
deps)
- [x] oxfmt clean
- [ ] Post-deploy: re-run D6 matrix on staging to confirm ag2 +
llamaindex column recovery

## Deploy note
Staging is **image-sourced, main-only** (the "Showcase: Build & Push"
workflow builds GHCR images and redeploys on push to `main`). This
branch will NOT auto-deploy. To validate on staging before merge: `gh
workflow run showcase_build.yml --ref fix/llamaindex-sse-request-tools
-f service=ag2` (and `service=llamaindex`).
2026-06-23 15:21:11 -07:00
Mike Ryan ca78e0101f fix(threads): harden thread store lifecycle 2026-06-23 15:18:59 -07:00
Jordan Ritter 9f2b859450 fix(showcase): stop ag2 generate_a2ui empty-arg validation loop
The ag2 declarative-gen-ui route pointed its HttpAgent at the root
catch-all mount (agents/agent.py) instead of the dedicated
/declarative-gen-ui mount (a2ui_dynamic.py), and generate_a2ui declared
a required context arg that the model emits as {}. pydantic rejected
every call with "context Field required" and AG2 retried without bound —
a 630-iteration hot loop per pill that flooded logs and starved the
frontend.

Fix: route to the dedicated mount with injectA2UITool:false (the
dedicated agent owns generate_a2ui and emits a2ui_operations itself);
make generate_a2ui a no-arg tool matching the D6 fixtures and the
langgraph-python gold standard, with a constant inner system prompt
(per-pill distinctness comes from the captured user message). Regenerated
the gen-ui-declarative fixture and ported the LP definitions/renderers
catalog (all 7 driver testids) for parity. Eliminates the validation
loop: runsFinished=1, zero validation errors.
2026-06-23 15:12:09 -07:00
Jordan Ritter 7cb43581aa fix(showcase): forward request-time tools in llamaindex AG-UI adapter
The LlamaIndex AG-UI adapter never forwarded RunAgentInput.tools, so
page-injected frontend tools (useFrontendTool / useComponent /
useHumanInTheLoop) were invisible to the LLM — runs the model couldn't
satisfy ended in RUN_ERROR (no RUN_FINISHED), which the harness reported
as sse-missing.

New RequestAwareAGUIChatWorkflow re-implements the chat step as the
upstream body plus three additions: forward RunAgentInput.tools as no-op
FunctionTool stubs carrying the verbatim injected JSON schema; re-role
tool-result messages to role="tool" on the LLM-bound message copy only
(so the hasToolResult second-leg fixture matches) without mutating
stored/snapshot history; and for frontend tool calls dispatch the
ToolCallEvent but skip the duplicate TOOL_CALL_CHUNK (the bare snapshot
already delivers the call). beautiful-chat 0/5 -> 5/5; mcp-apps fixed.
2026-06-23 15:12:09 -07:00
Jordan Ritter eded5f5325 fix(showcase): skip non-probe-eligible services in staging precondition instead of crashing (#5650)
## Summary

A `service=all` promote runs `verify-staging-precondition` →
`verify-deploy.ts --env staging --services <all>`, which
**hard-crashes** (exit 2) the moment it's handed a service with
`probe.staging=false` (`resolveProbeTargets` throws `not
probe-eligible`). That failed the whole-fleet precondition. This adds an
opt-in `--skip-ineligible` flag (wired into the
`verify-staging-precondition` step **only**) that skips a
non-probe-eligible service with a clear status line instead of throwing;
eligible services are still probed normally, and an explicit
single-service probe / unknown name still hard-fails (strict default
preserved; `verify-prod` untouched).

After #5641 (starters always-on), the only remaining
`probe.staging=false` services are **`harness-workers`** and
**`harness-legacy`** — empirically confirmed to still crash a
`service=all` precondition, so this fix is still needed (the starters
that originally triggered it are now eligible and probed normally).

## Verification
- Red-green (vitest, real surface): a service list containing
`harness-workers` crashes WITHOUT the flag, and is SKIPPED
(`harness-workers (skipped — not probe-eligible for staging,
probe.staging=false)`) WITH it.
- verify-deploy suite: 124/124 pass. `tsc --noEmit` clean on changed
files. `actionlint` on the workflow: exit 0.

## Test plan
- [x] vitest verify-deploy suite green (124)
- [ ] CI green

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-23 15:09:20 -07:00
Mike Ryan 0494f86771 fix(threads): preserve same-agent thread stores 2026-06-23 14:53:46 -07:00
Jordan Ritter 160ba5a4aa fix(showcase): skip non-probe-eligible services in staging precondition instead of crashing 2026-06-23 14:51:16 -07:00
Jordan Ritter 7a00e2373d feat(showcase): make starter fleet always-on + verify-deploy starter driver (#5641)
## Summary

The 12 `starter-*` services were deliberately `sleepApplication=true`
(sleepable), `probe.staging=false` (held out of the verify-deploy
staging matrix). That class-difference is the root of three recurring
symptoms: the `service=all` promote false-fails them (SLEEPING / no
running-digest), the deployed-starter smoke test 404s a cold container,
and they're excluded from staging validation. Decision: **bring them
into the normal managed-fleet flow — always-on + staging-probed.**

- **`verify-deploy.drivers.starter.ts` (new)** — real `probeStarter`
baseline driver (deployment SUCCESS + HTTP 200 on `/`, mirroring
`probeShell`); replaces the fail-loud `case "starter"` stub.
- **`railway-envs.ts`** — `staging.probe: false→true` for all 12
starters (prod already true); stale "staging probe OFF" comments
rewritten. `railway-envs.generated.json` regenerated (in-sync via
`--check`).
- **`provision-starter-fleet.ts`** — `sleepApplication: true→false` +
comments/logs; test updated.

## ⚠️ Deploy ordering (must hold)
This PR flips `probe.staging=true`, which routes starters into the
staging matrix. It must **not** merge until the live services are
flipped always-on, or a `service=all` promote would probe still-sleeping
starters and fail. Sequence: **(1) live-flip 24 instances
`sleepApplication=false` + redeploy (12 staging + 12 prod), (2) verify
awake + serving `/`, (3) merge this PR.**

## Verification
- Red-green: starter driver (stub→probe `/`), SSOT golden (probe.staging
true), provisioner (sleep false) — all RED→GREEN.
- `showcase/scripts`: 2092 tests pass; `tsc --noEmit` clean on changed
files.

## Test plan
- [x] vitest suites green (2092)
- [ ] CR
- [ ] live 24-instance always-on flip + redeploy (staging + prod)
- [ ] CI green
- [ ] post-flip: `service=all` dry-run shows starters pass staging probe

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-23 14:43:55 -07:00
Mike Ryan 9d6ea812ef fix(threads): address injectThreads review findings 2026-06-23 14:42:43 -07:00
Tyler Slaton b07647eede fix(showcase): built-in-agent shared-state demo now re-renders on UI writes (#5643)
## Summary

The built-in-agent `shared-state-read-write` demo wrote agent state via
**direct property assignment** (`agent.state = {...}`). That sets the
value but does **not** fire `onStateChanged`, so components subscribed
via `useAgent` never re-render off a UI write. Switched the seed + title
write to **`agent.setState(...)`**, which updates state *and* notifies
subscribers.

This was the lone divergence: every other framework demo — including the
`langgraph-python` gold standard — already uses `agent.setState(...)`,
and `docs/shared-state.mdx` documents writes going through `setState`.

```diff
-    agent.state = { ...defaultRecipe } as unknown as typeof agent.state;
+    agent.setState({ ...defaultRecipe } as unknown as typeof agent.state);
...
-    agent.state = { ...(agent.state as object), title: next } as unknown as typeof agent.state;
+    agent.setState({ ...(agent.state as object), title: next } as unknown as typeof agent.state);
```

## Why

`@ag-ui/client`'s `AbstractAgent.setState()` both assigns `this.state`
and notifies all subscribers (`onStateChanged`), which is what drives
the `useAgent` re-render. Direct `agent.state = {...}` is a plain
property write — it skips the notification, so any component relying on
the hook to reflect the change silently goes stale.

## Test plan

- `showcase/bin/showcase test built-in-agent --d5 --verbose --cycle
--isolate` → green after rebuild from the changed source.
- lefthook pre-commit (lint-fix, package checks) + commitlint pass.

## Follow-up (not in this PR)

The built-in-agent demo is recipe-shaped (title/ingredients/steps) while
the other read-write demos are preferences/notes + `DemoLayout`. Per the
showcase 1:1 convention it should mirror `langgraph-python` — larger
change (+ fixture realignment), flagging separately.
2026-06-23 14:42:18 -07:00
Sam Julien 7e4284d35f docs(git): require branching from up-to-date origin/main (#5648)
## Summary

Adds a rule to `.claude/docs/git.md`: fetch and branch off `origin/main`
before starting work, rather than whatever the local `main` happens to
be.

```sh
git fetch origin && git switch -c <branch-name> origin/main
# worktree equivalent:
git fetch origin && git worktree add -b <branch-name> ../<dir> origin/main
```

Branching off a stale local `main` ("behind origin/main by N commits")
bases the PR on an old commit and invites avoidable merge conflicts. The
rule also documents the recovery path for an already-stale branch: `git
fetch origin && git rebase origin/main`.

Docs-only change to agent guidance; no code impact.
2026-06-23 14:32:27 -07:00
Nathan 🔶 Tarbert c84e2166bc docs(git): require branching from up-to-date origin/main
Add a rule to .claude/docs/git.md to fetch and branch off origin/main
(not stale local main) before starting work, with the worktree
equivalent and the rebase recovery command for an already-stale branch.
Prevents PRs from being based on an old commit.
2026-06-23 17:22:07 -04:00
Nathan 🔶 Tarbert 97d1de4682 fix(showcase): use agent.setState in built-in-agent shared-state demo
The built-in-agent shared-state-read-write demo wrote agent state via
direct property assignment (`agent.state = {...}`), which sets the value
but does not fire `onStateChanged` — so subscribed components never
re-render off a UI write. Every other framework demo (including the
langgraph-python gold standard) already uses `agent.setState(...)`, which
both updates state and notifies subscribers. This was the lone divergence.

Switch the seed and the title write to `agent.setState(...)` so the demo
matches the documented pattern (docs/shared-state.mdx says writes go
through `setState`) and the rest of the showcase.

Verified: built-in-agent D5 e2e-deep suite green after rebuild.
2026-06-23 17:21:49 -04:00
Tyler Slaton 006c62592e chore: release monorepo v1.61.1 (#5645)
## Release monorepo v1.61.1

**Scope:** `monorepo` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.61.1`
   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 `monorepo` packages to npm at version `1.61.1`
   - Creates git tag `monorepo/v1.61.1`
   - 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.
v1.61.1
2026-06-23 14:21:47 -07:00
Ben Taylor f52960d279 fix(runtime): set telemetry license token for v2 SSE runtimes (#5640)
## Problem

Self-hosted users on the v2 SSE runtime never get a `telemetry_id` on
their runtime telemetry events — even with a license token configured.
The root cause is in `packages/runtime/src/v2/runtime/core/runtime.ts`:

- `CopilotIntelligenceRuntime` **did** call
`telemetry.setLicenseToken(...)` in its constructor.
- `BaseCopilotRuntime` and `CopilotSseRuntime` **did not**.

`telemetry_id` is derived only inside `setLicenseToken`
(`parseAndWarnTelemetryId`). If it's never called, every event the
runtime emits is sent anonymously. SSE-mode handlers (`handle-connect`,
`handle-run`, `sse-response`) all emit `oss.runtime.*` events through
the shared telemetry singleton, so all of them went out unattributed for
SSE users.

## Fix

Hoist the license-token resolution (`options.licenseToken ??
COPILOTKIT_LICENSE_TOKEN`) and the `telemetry.setLicenseToken` call
**into `BaseCopilotRuntime`'s constructor**, so SSE and Intelligence
runtimes attribute telemetry identically. The now-redundant duplicate is
removed from `CopilotIntelligenceRuntime` (its `licenseChecker` stays).
The v1 `CopilotRuntime` already set the token and is unchanged.

## Test coverage — every construction path into the endpoints

The token is set at construction time and all endpoints share one
telemetry singleton, so covering every runtime construction path covers
every endpoint.

- **`runtime-license-telemetry.test.ts`** — `CopilotSseRuntime` and
`CopilotIntelligenceRuntime` (direct) + the `CopilotRuntime` shim (both
SSE and Intelligence delegates), each across `{explicit option,
COPILOTKIT_LICENSE_TOKEN fallback, none}`. Asserts the token is set
**exactly once** (guards against a double-set after the hoist).
- **`sse-license-telemetry.integration.test.ts`** — end-to-end: an SSE
runtime built with a license token forwards it all the way to
`lambdaClient.send` on a real Express endpoint request.
- **`copilot-runtime-license-telemetry.test.ts`** — regression guard for
the v1 `CopilotRuntime` path (already worked, previously untested —
exactly the kind of untested path that let this gap appear).

The new tests are **red before the fix** (the SSE-path assertions fail)
and green after.

## Verification

- New + existing v2 runtime suite: **770/770 pass**, no regressions.
- Lint: 0 new warnings. Build: green (types clean). Format: clean
(oxfmt).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-23 16:19:36 -05:00
Mike Ryan fd3d434d22 fix(core): tighten thread store typing 2026-06-23 14:16:21 -07:00
Benjamin Taylor c2d38f4b6a refactor(runtime): resolve license token once in base; add env-fallback integration test
Addresses PR review feedback:
- Resolve the license token once (option ?? COPILOTKIT_LICENSE_TOKEN) into a
  protected readonly field on BaseCopilotRuntime, and have
  CopilotIntelligenceRuntime's licenseChecker reuse it. Collapses the duplicated
  resolution and structurally enforces that telemetry attribution and feature
  gating can never disagree, instead of relying on a "keep in sync" comment.
- Add an integration test for the env-var-only path (no licenseToken option) —
  the exact self-hosted scenario this PR targets — proving the env-resolved
  token reaches lambdaClient.send through a real request. Kept in its own file
  so the process-wide telemetry singleton (last-write-wins) can't false-pass it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:11:26 -05:00
Benjamin Taylor 7fb1600da5 test(runtime): integration-cover license token → sink for all endpoints/modes
Adds genuine end-to-end coverage beyond the SSE-via-Express case:
- SSE via the Hono adapter
- SSE via the framework-agnostic fetch handler (what node + custom adapters wrap)
- Intelligence mode end-to-end (real CopilotIntelligenceRuntime, WS runner stubbed)

Each constructs a real runtime (so the base-class setLicenseToken runs), drives a
real request through the adapter, and asserts the token reaches lambdaClient.send
on oss.runtime.copilot_request_created.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:11:26 -05:00
Benjamin Taylor b5a435f0c4 fix(runtime): set telemetry license token for v2 SSE runtimes
Only CopilotIntelligenceRuntime called telemetry.setLicenseToken in its
constructor; BaseCopilotRuntime and CopilotSseRuntime did not. As a result,
self-hosted SSE users got anonymous runtime telemetry (no telemetry_id) even
with a license token configured — and those events were additionally throttled
to the 5% anonymous sample rate, leaving runtime telemetry_id stuck at ~1%.

Hoist the licenseToken resolution (option ?? COPILOTKIT_LICENSE_TOKEN env
fallback) and telemetry.setLicenseToken call into BaseCopilotRuntime so SSE and
Intelligence runtimes attribute telemetry identically. Remove the now-redundant
duplicate from CopilotIntelligenceRuntime (its licenseChecker stays).

Tests cover every construction path into the endpoints:
- runtime-license-telemetry.test.ts: SSE/Intelligence direct + CopilotRuntime
  shim (both delegates) x {explicit option, env fallback, none}; asserts the
  token is set exactly once (guards against a double-set after the hoist).
- sse-license-telemetry.integration.test.ts: end-to-end proof the token rides
  to lambdaClient.send through a real Express endpoint request.
- copilot-runtime-license-telemetry.test.ts: regression guard for the v1
  CopilotRuntime path (already worked, previously untested).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:11:26 -05:00
tylerslaton 410d34001d chore: release monorepo v1.61.1 2026-06-23 21:00:28 +00:00
Mike Ryan 7d6a95b939 Merge branch 'blitz/angular-inject-threads/B4' into blitz/angular-inject-threads/integration 2026-06-23 13:58:11 -07:00
Mike Ryan ca15b8d106 docs(angular): document injectThreads 2026-06-23 13:58:03 -07:00
Tyler Slaton 56615da485 ci: reduce release delay, fix flakey test, use devops bot token (#5644)
## Summary
- Mint a GitHub App token for the stable release workflow and reuse it
for PR creation and follow-up API calls
- Disable lefthook during automation commits so release PR generation
does not depend on local developer hooks
- Relax the CopilotChat perf regression test to assert correctness
without a hard 5s wall-clock check

## Testing
- Unit/UI test updated to allow longer async rendering while still
verifying 100 messages render successfully
- Not run (not requested)
2026-06-23 13:56:33 -07:00
Mike Ryan 5c2f4f93cc Merge branch 'blitz/angular-inject-threads/B3' into blitz/angular-inject-threads/integration 2026-06-23 13:54:06 -07:00
Mike Ryan 322d32c9d5 feat(angular): add injectThreads API 2026-06-23 13:53:39 -07:00
Mike Ryan 8796db57e0 feat(react-core): align useThreads with shared thread store 2026-06-23 13:53:19 -07:00
Tyler Slaton 83b21df925 Stabilize release PR token and chat perf test 2026-06-23 13:51:35 -07:00