Commit Graph

140 Commits

Author SHA1 Message Date
Alem Tuzlak 0980a73b63 Merge origin/main into feat/pluggable-markdown-renderer
Keep the pluggable markdown renderer (drop bundled streamdown/katex).
Take main's inspector context, threads-drawer rename, and showcase moves.

--no-verify: this worktree has no node_modules, so lefthook cannot run.
2026-08-31 13:18:28 +02:00
Aswin Kumar 99d349c6cd Merge branch 'main' into fix/angular-hitl-result-envelope 2026-08-29 00:41:13 +05:30
BenTaylorDev bf1bb98765 chore: release angular v0.4.0 2026-08-28 15:03:28 +00:00
Alem Tuzlak d6812e41c8 fix(core): report the runtime connection status from the last actual contact (#6706)
Closes OSS-904.

## The problem

`CopilotKitCore.runtimeConnectionStatus` was set only by the `/info`
handshake, which runs **once on connect**. If the runtime became
unreachable after that, the status stayed `connected` indefinitely —
measured two independent ways against `examples/v2/react/demo` and
recorded in the ticket.

The failure was not lost, it was filed in the wrong drawer: it arrived
as `agent_run_failed`, indistinguishable from an agent bug. So
everything downstream inherited the wrong answer — System Health
reported healthy, the launcher error signal could not fire for the most
common real symptom ("it worked a minute ago"), and a customer `onError`
handler written to separate wiring problems from agent problems got the
wrong classification.

## What this changes

The status now reports **the outcome of the last actual contact with the
runtime**.

A failed runtime request — or silence past a per-request watchdog —
triggers **one** bounded confirmation request. If nothing answers, the
status moves to `error` and the failure is emitted through the existing
wiring error code, so customers already handling startup wiring failures
pick up the mid-session case without changing a line. A subsequent
successful request re-syncs and clears it.

Crucially, **the conversation survives**. The transition does not
discard runtime knowledge, so the agent backing an open chat is the same
instance, its messages stay on screen, and submitting stays possible —
which matters because submitting is what restores the status.

No polling, no heartbeat, no retry loop. Every timer is bound to one
request and dies with it.

## Decisions worth knowing when reviewing

- **Reactive in both directions.** A heartbeat would put permanent
background traffic into every embedding application; a retry loop mostly
races a user who is about to retry anyway. The cost is stated rather
than hidden: while nothing is happening, nothing is detected.
- **Status change is separated from discarding knowledge.** The only
pre-existing code that set `error` also cleared `remoteAgents`. That is
right at startup and destructive mid-session, because conversation state
lives on the agent instance. Four sites now hold this invariant up
together; each carries a comment saying so.
- **The trigger is deliberately permissive, and the check is the
arbiter.** A request that received a successful response never triggers
a check; user cancellation never does; everything else may. Defining the
trigger precisely would mean maintaining a status-code list that is
complete only for the deployment topologies someone thought of.
- **Silence counts.** A server can refuse (fails fast) or hang (accepts
and never answers). A stopped dev server refuses; a container
mid-rollout, a half-switched deploy and a dropped tunnel hang. Only
bounding the check does not help, because no check starts — hence the
per-request watchdog. It observes only and never cancels the request.
- **The rule is stated by destination, not by call site**, so a runtime
route added later inherits the behaviour. Excluded: the Intelligence
realtime endpoint (a different service — reporting its outage as
"runtime unreachable" would be a false diagnosis), endpoints belonging
to the customer, and the stop request.
- **Recovery may prune, under two conditions**: the runtime must have
reported at least one agent, and the agent must carry no conversation
state. An empty list is the signature of a runtime that has not finished
registering.
- **"Answered but refused" keeps the error status and gets a different
message.** An expired token means the app cannot work, so red is right;
telling the reader "unreachable" would send them to check ports and
containers.

## Deliberately not delivered

- Detecting an outage, or a recovery, while the application is idle.
- Recovery by opening the Threads view: every binding withholds thread
requests until the status is already connected, so nothing is sent while
it is red. The thread plumbing still earns its place for *detection*.
- A signal for the Intelligence realtime endpoint failing while the
runtime is healthy — a real gap, and its own ticket.
- Memory and suggestion routes adopting the instrumented fetch.
- A new status value or a new error code.

## Costs this introduces

`error` now means two things — "never connected, no agents" and "lost
mid-session, agents intact". Documented on the enum. And because the
status can now change mid-session at all, an outage costs some churn
that did not exist before: the memory list and the Inspector's thread
list are cleared and refetched, and where the chat owns its run-activity
store it is stopped and restarted. All of it is paid on a user-caused
transition, never while idle.

## Testing

Four independent reviewers audited an earlier revision of this branch;
the ten defects they reproduced are fixed and each is pinned by a test
that was red first. A mutation audit of 110 mutants killed 90; the
surviving holes were closed in the round after.

The connection-health suites carry 72 tests. Request counting is a
first-class assertion throughout, because several decisions are
*absences* — no polling, no retry loop, one check per burst, no traffic
while red — and an absence is only testable by counting. Those tests use
fake timers advancing ten minutes; that boundary is documented where it
lives, since anything slower is invisible to them.

Verified by hand in a browser with the runtime running as its own
process, so the page outlives it: a refusing runtime, a hanging runtime,
recovery, an agent added during an outage, an agent deleted during an
outage. `performance.timeOrigin` was checked throughout to prove the
page never reloaded and the result was not an artefact of a fresh
handshake.

## Follow-ups this leaves behind

Three of these deserve their own ticket. None blocks this PR; all three
are consequences of where its scope was drawn, and they are listed here
so the boundary is explicit rather than implied.

### 1. A signal for the Intelligence realtime endpoint

In Intelligence mode the browser gets its chat events from a **second
service** at its own address; the runtime is only asked for the
credentials. If that service fails while the runtime is healthy, this
change correctly reports the runtime as reachable — and the user
experiences exactly the silence this ticket exists to remove.

It is excluded here on purpose: folding it into the runtime status would
report "runtime unreachable" about a healthy runtime, and a false
diagnosis costs more debugging time than no signal. It needs its own
signal, which is a presentation decision as much as a detection one.

### 2. Memory routes onto the instrumented fetch

The memory store still builds with the global fetch, so its
runtime-bound requests are invisible to connection health. Two costs: a
genuine failure there is a signal we discard, and a success there cannot
restore the status.

The asymmetry is what makes this worth fixing rather than leaving:
memory is the surface most disrupted by a status transition (its list is
cleared and refetched) and currently the one least able to contribute.
The change itself is small — that module already takes its request
function as an injected dependency.

### 3. Consumers should key on what they need, not on the status value

Several consumers treat "status is not connected" as "discard
everything": the memory list, the Inspector's thread list, and the
chat's run-activity store. That was harmless while the status could not
change after page load. It can now, so every outage costs churn that did
not exist before.

This is the same mistake this PR fixes three times *inside* core — a
guard bound to a state instead of to the thing it protects. The
principle was applied internally and not to these consumers. That makes
the churn listed under "Costs" above **deferred rather than inherent**,
and it is the largest of the three follow-ups: three consumers in three
packages, each with its own risk, which is why it was kept out of this
PR.

### Two smaller items

- The launcher error signal on `main` carries a comment stating the
limitation this change removes ("a runtime that dies after the page
loaded … raises nothing … closing that gap means a re-probe in the
core"). It becomes false when this lands and should be corrected then.
- `packages/web-inspector/src/styles/generated.css` is build output
under version control and re-dirties the tree on every build. Unrelated
to this PR, but the Tailwind source glob scans test files, so any prose
comment containing a utility word (`fixed`, `hidden`, `visible`,
`block`) silently changes the committed CSS. Narrowing the glob would
remove the class of problem.

Full specification, including the interview decisions and every
revision: `OSS-904-PRD.md`.
2026-08-28 14:46:15 +02:00
Alem Tuzlak 5686a0669e Merge branch 'main' into lukas/oss-904-runtime-connection-status 2026-08-28 14:06:40 +02:00
Alem Tuzlak 1dfc5cdafa refactor(core): remove OSS-904 design comments 2026-08-28 13:27:37 +02:00
Alem Tuzlak a7191e2a12 fix(core): bound recovery /info hang and tighten OSS-904 comments 2026-08-28 13:08:44 +02:00
Lukas Moschitz 9e29de156d fix(angular): stop the core mock from hiding the Inspector's exports
The spec replaced @copilotkit/core wholesale with a two-export factory. That
held until the Inspector started mounting in these tests — it is enabled by
default in browser frameworks now, and its connectedCallback calls
isInspectorThreadBridgeEnabled, one of seventeen value exports it imports from
core. A missing one throws an uncaught exception, so the run fails while all
49 test files still report passing, which is a confusing way to find out.

The factory now spreads the real module and overrides only CopilotKitCore and
the connection-status enum, which is what these tests actually drive. Listing
the seventeen would have postponed the next occurrence rather than removed it.

Surfaced by the web-inspector work on this branch: angular only runs when
affected, and it becomes affected the moment web-inspector changes — so the
first PR to touch web-inspector after the default-on change was going to hit
this regardless of what it changed.
2026-08-27 16:58:39 +02:00
Alem Tuzlak ed28f3a908 fix(angular): export inspector development-mode token from public API 2026-08-27 13:01:01 +02:00
Alem Tuzlak 2dec983ed8 fix(angular): restore web-inspector workspace dependency 2026-08-27 12:15:03 +02:00
Alem Tuzlak 42d3c92fbd chore: merge origin/main into tyler/default-browser-inspector 2026-08-27 12:09:27 +02:00
Lukas Moschitz 4f432848f5 docs(oss-904): correct the claims about thread requests and the error state
Five comments justified routing thread requests through the instrumented fetch
by saying it lets opening a view restore the status after an outage. It does
not: every binding withholds its thread requests until the status is already
connected, so while it is red nothing is sent. The justification is DETECTION
only, which is what the CopilotChat site already said correctly.

Also:

- Documents both meanings of the Error state and the invariant behind them —
  the status reports the last actual contact with the runtime — on the
  connection-status reference page, which described only the startup meaning.
- Renames RUNTIME_PROBE_TIMEOUT_MS to ɵRUNTIME_PROBE_TIMEOUT_MS. core/index.ts
  re-exports agent-registry wholesale, so a constant whose own doc says
  "exported for tests" was public API of @copilotkit/core.
- Guards the Inspector's read of ɵruntimeFetch the way it guards its four other
  internal core accessors. A newer Inspector against an older pinned core was
  handing the thread store `undefined`, which breaks the Threads view outright
  rather than merely losing detection through it.
- Corrects the stop-request comment, which claimed to be the only runtime
  destination off the seam; the suggestion route's stateless path, the memory
  store and /inspector-metadata are too, just not by design.
- Corrects OSS-904-VERIFY.md, which said scenario 4 had real traffic to work
  with and left it off the not-covered list.
2026-08-26 13:25:13 +02:00
Lukas Moschitz 1b252d1a26 fix(bindings): route thread requests through the instrumented runtime fetch
Thread REST calls go to the runtime, so under the destination rule they
are runtime traffic — but every binding injected the global fetch, so
their outcomes never reached the connection status. The practical effect:
with a dead runtime the Threads view left the status green, and after the
runtime came back, opening the Threads view could not clear it. Only
sending a message could.

React (both the useThreads store and CopilotChat's standalone
run-activity store), Vue, Angular and the Inspector's own owned store now
take `copilotkit.ɵruntimeFetch` instead. It is a pass-through and is
memoized per core, so nothing changes in the healthy case and no extra
request is issued.

Each binding's thread suite asserts the injection at the seam rather than
inferring it, since a regression back to the global fetch is invisible
from the rendered result.
2026-08-26 13:25:12 +02:00
Tyler Slaton b3b339f544 Revert "feat(web-inspector): add Event Snippets and save-as-snippet (#6649)"
This reverts commit ba4260ad66, reversing
changes made to 47c5510b49.
2026-08-26 02:11:19 +02:00
Aswin Kumar 8eb55a118e Merge branch 'main' into fix/angular-hitl-result-envelope 2026-08-25 19:25:45 +05:30
Tyler Slaton 2bb60b67c5 style: apply repository formatting 2026-08-25 11:36:12 +02:00
Tyler Slaton b53fa8f1ea refactor(inspector): consolidate framework integration 2026-08-25 11:25:08 +02:00
Tyler Slaton 39d5598f0e fix(inspector): simplify framework lifecycle mounts 2026-08-25 11:25:07 +02:00
Tyler Slaton 240ea8a575 test(angular): verify inspector configuration before append 2026-08-25 11:25:06 +02:00
Tyler Slaton a5e072301a fix(inspector): restrict defaults to development builds 2026-08-25 11:25:06 +02:00
Tyler Slaton 4691946a8f feat: enable the Inspector by default in browser frameworks 2026-08-25 11:25:04 +02:00
Alem Tuzlak ba4260ad66 feat(web-inspector): add Event Snippets and save-as-snippet (#6649)
Open Inspector Event Snippets on localhost. You can compile, save, and
replay AG-UI events in chat. Chat shows a bookmark icon next to a tool
call, an A2UI block, or generative UI. Click the icon to save that turn
as a snippet.

## What does this PR do?

This PR adds the Inspector Event Snippets pane.

You can:

- Compile a snippet from a recipe (tool-call, reasoning, text, activity,
raw)
- Save snippets in origin-scoped localStorage
(`cpk:inspector:event-snippets`)
- Import and export snippets from the pane header
- Replay a snippet into live chat through Inspector-only Core inject

Each Run remints `messageId`, `parentMessageId`, `toolCallId`, and
`runId`. The second Run of the same snippet is a new turn.

On localhost, chat shows a bookmark icon beside a tool call, A2UI block,
or generative UI. The icon is absolutely positioned. It hangs to the
right when there is room. Otherwise it hangs to the left. The card stays
full chat width.

The React demo adds `sayHello`, `getTime`, `addNumbers`, and a **Call 3
tools** suggestion.

## Related PRs and Issues

- Linear
[OSS-874](https://linear.app/copilotkit/issue/OSS-874/new-features-also-allow-users-to-emit-specific-events-from-the)

## Checklist

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

## Testing

### Commands run

1. Lefthook pre-commit ran `nx` targets `test`, `publint`, and `attw`
for 27 affected projects. All passed.
2. I did not run `pnpm test:pr` (full repo). Lefthook ran the affected
package matrix only.

### Manual test

1. Run `pnpm demo:react` from the repo root.
2. Open http://localhost:3000
3. Open Inspector and select Event Snippets.
4. In chat, click **Call 3 tools**. Confirm three tool cards at full
chat width, with the bookmark hanging outside the card.
5. Click a bookmark, then click Run twice. Chat shows a second turn with
new IDs.

### How this PR makes testing easy

- `packages/web-inspector/src/lib/__tests__/event-snippets.test.ts`
- `packages/core/src/__tests__/inspect-inject.test.ts` (covers two
injects)
- React demo: `examples/v2/react/demo/src/app/page.tsx`

## Linked issues

Linear
[OSS-874](https://linear.app/copilotkit/issue/OSS-874/new-features-also-allow-users-to-emit-specific-events-from-the)

## Risk / rollback

- If ID remint is wrong, a second Run can no-op or duplicate a turn.
- The save icon shows on localhost Inspector (or when `showDevConsole`
is `true`).
- Rollback: revert this PR.

## Public API change

**Before**

Angular has no Inspector service.

```ts
// no CopilotInspector export from @copilotkit/angular
```

**After**

```ts
import { CopilotInspector } from "@copilotkit/angular";

const inspector = inject(CopilotInspector);
inspector.openInspector({
  messageId: "msg-1",
  menu: "event-snippets",
});
```

React and Vue apps that already mount Inspector on localhost need no new
caller code. Chat wires the bookmark through Inspector context.

`@copilotkit/core` exports `ɵinjectInspectorEvents` for Inspector only.
App code must not call it. There is no public Core emit API.
2026-08-25 09:39:37 +02:00
Alem Tuzlak 98968b49c2 fix(web-inspector): address review on event snippets
- Raw recipe: the Events JSON input now calls requestUpdate, so Run and
  Save stop being permanently disabled.
- Tool args recovery: one depth scan replaces the parse-every-prefix loop.
  Truncated args from a streaming tool call now fail at once, not after
  seconds of blocked main thread.
- Chat bookmark: hidden while the tool arguments are incomplete, so a
  partial payload cannot be captured.
- saveEventSnippet: React, Vue, and Angular wrap the body, so a compile or
  storage failure is reported instead of becoming an unhandled rejection.
- Vue and Angular now gate the in-chat affordances on a dev build plus
  localhost, the same as React. showDevConsole: true on a staging URL no
  longer puts a bookmark into a production chat.
2026-08-24 19:10:06 +02:00
Mike Ryan db88826432 chore: rename Enterprise Intelligence product copy 2026-08-24 09:38:15 -07:00
Alem Tuzlak 4bd576d4ea Merge branch 'main' into alem/oss-874-inspector-event-snippets 2026-08-24 15:48:38 +02:00
Benjamin Taylor 6e0f5773f0 Merge remote-tracking branch 'origin/main' into ben1/v2-runtime-migration
# Conflicts:
#	showcase/shell-docs/src/content/snippets/shared/generative-ui/a2ui.mdx
2026-08-21 12:49:37 -05:00
Alem Tuzlak 76c8e23a0b feat(web-inspector): add Event Snippets and save-as-snippet
Developers can compile, save, and replay AG-UI events from Inspector.

Localhost chat can save a live turn as a snippet.
2026-08-21 19:39:18 +02:00
Rainer Hahnekamp 27685a30fb chore(angular): remove obsolete safe navigation wrappers 2026-08-21 15:19:22 +02:00
Rainer Hahnekamp 2247f548b3 chore(angular): compile library at Angular 22 floor 2026-08-21 15:19:21 +02:00
Rainer Hahnekamp d248f0a7f9 chore(angular): remove empty component imports 2026-08-21 15:18:03 +02:00
github-actions[bot] 8b8097508e style: auto-fix formatting 2026-08-21 15:18:02 +02:00
Rainer Hahnekamp 18d008c423 Align Angular 22 support policy and docs 2026-08-21 15:18:02 +02:00
Rainer Hahnekamp 79c235035a Apply Angular 22 framework migrations 2026-08-21 15:18:02 +02:00
Rainer Hahnekamp fea1a13b58 Upgrade Angular toolchains to version 22 2026-08-21 15:18:01 +02:00
Rainer Hahnekamp bf18677145 preserve interrupted run IDs when resuming Angular agents 2026-08-20 15:37:07 +02:00
Rainer Hahnekamp 736742f6f9 test(angular): document unobservable thread changes 2026-08-20 15:37:07 +02:00
Murat Sari ba41a31d7c feat: implement interrupt handling in AgentStore and add injectInterrupt function 2026-08-20 15:37:07 +02:00
Aswin Kumar 53f856afa3 fix(angular): resolve human-in-the-loop results without the bus envelope
HumanInTheLoop.onResult resolved with the whole rxjs bus event
({toolCallId, toolName, result}) instead of the bare result, because
lastValueFrom was not mapped. The bound tool handler returns that promise
directly as the tool's result, and run-handler JSON.stringifies a non-string
return into the tool message content, so agents received
{"toolCallId":...,"toolName":...,"result":{...}} where React sends the bare
value.

A LangGraph agent gating a side effect on a field of the human's answer
therefore reads that field off the envelope, finds nothing, and falls through
silently while the model still reports the action succeeded.

The existing tests asserted the envelope as expected behaviour, which is why
this went unnoticed; they now pin the bare result, plus a case that fails if
the routing keys leak again.

Reported in #6571, which attributes the envelope to @ag-ui/langgraph. It
originates here. The missing ToolMessage.name in that issue is a separate
defect and is not addressed by this change.
2026-08-20 13:51:10 +07:00
Murat Sari 703944880d feat(angular): expose agent capabilities 2026-08-16 22:11:36 +02:00
Murat Sari 5720cd7fdc feat(angular): support fetch credentials 2026-08-16 21:03:52 +02:00
Murat Sari dde84a5804 fix(angular): prevent duplicate OpenGenerativeUI sandboxes 2026-08-13 14:32:40 +02:00
Murat Sari 01c7283210 fix(core): prevent duplicate interrupt tool results (#6201) 2026-08-13 01:18:16 +02:00
Murat Sari cbf79ef52a fix: align Angular 20 support and resolve packed smoke paths 2026-08-11 21:49:36 +02:00
Murat Sari 4d5e3da712 feat(chat): implement input height measurement and adjust scroll view styles 2026-08-06 12:04:54 +02:00
contextablemark bc968ce96b chore: release angular v0.3.1 2026-08-03 20:50:53 +00:00
Mike Ryan ec0e496bc2 fix(showcase): restore Angular runtime parity 2026-07-28 15:25:16 -07:00
MikeRyanDev cb4ab7fd8e chore: release angular v0.3.0 2026-07-23 16:47:06 +00:00
Mike Ryan 053b00e846 fix(angular): match React MCP Apps sandbox 2026-07-23 08:51:34 -07:00
Mike Ryan cd0b5b4061 fix(angular): address SDK review findings 2026-07-23 07:48:25 -07:00
Mike Ryan fec70d086f feat(angular): checkpoint 2 - core and package 2026-07-23 07:14:55 -07:00