2330 Commits

Author SHA1 Message Date
Benjamin Taylor c9f2119de3 refactor(channels-intelligence): make realtime scope org/channel ids optional (OSS-473) 2026-07-15 07:15:00 -05:00
Benjamin Taylor 229f3f1460 refactor(channels): rename Bot/createBot public API to Channel/createChannel across the workspace (OSS-473) 2026-07-15 07:14:59 -05:00
Alem Tuzlak 3adea157a4 test(channels-intelligence): cover getMessages mapping + address review
Addresses review on #5969:
- Add getMessages tests: string content, content-part array (text parts joined,
  non-text contributes ""), and role→isBot/user derivation; plus the no-getHistory
  transport case returning [].
- Log on the unexpected getMessages catch (matches conversationStore's seeding
  path) instead of degrading to [] silently — HttpDeliverySource already swallows
  its own fetch failures, so this outer catch only fires on a real throw.
- Fold the duplicated role boolean (isBot computed once, drives both fields).
2026-07-15 11:44:32 +02:00
Maxim 8efdc49e6e fix(runtime): validate recall limit as positive integer and trim query in parseRecallBody
parseRecallBody under-validated two inputs of the /memories/recall endpoint:

- `limit`: the `typeof limit !== "number"` guard let NaN, Infinity, -5, and
  3.5 through. Infinity/NaN then JSON-serialize to null (silently corrupting
  the forwarded platform request); negatives/fractions are nonsensical.
  Now require a finite positive integer (Number.isInteger && > 0) when
  provided, else 400.
- `query`: the `length === 0` guard missed whitespace-only queries ("   "),
  forwarding a useless query to the platform (reachable from the inspector
  recall box). Now trim and reject when empty, forwarding the trimmed value.

`scope` validation is unchanged; docstring updated to match.

Added behavioral tests asserting 400 for limit = Infinity/NaN/-1/2.5 and for
a whitespace-only query, plus a valid { query, limit } case that passes 200
with the trimmed query forwarded.

Call sites: parseRecallBody is called only by handleRecallMemories (same file).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 17:03:45 -07:00
Maxim c2184f0729 test(core): lock in NUL separator in capabilityKey against tool-identity collision
The reported bug (capabilityKey joining agentId+name with a SPACE) is NOT
present: run-handler.ts already joins with a NUL byte (0x00), matching the
"NUL-separated" docstring. The space appearance was a rendering artifact —
a NUL byte displays as an invisible/space-like gap in editors and grep.

This adds the mandatory covering regression test so a future edit back to a
space separator cannot regress silently. It registers two agent-scoped tools
whose (name, agentId) pairs collide under a space separator but are distinct
under NUL — A {name:"b c", agentId:"a"} and B {name:"c", agentId:"a b"}, both
"a b c" with a space — disables only A, and asserts B stays enabled in
isToolEnabled and buildFrontendTools. Red/green verified: fails against a space
separator, passes against the shipped NUL separator.

capabilityKey callers (setToolEnabled/isToolEnabled/buildFrontendTools) are all
internal and all rely on this collision-free identity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 16:43:39 -07:00
Maxim 7f7e39b4b7 feat(web-inspector): wire memory-store recall into memory view recall UI (OSS memory B3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:59:59 -07:00
Maxim a8727a4936 feat(web-inspector): recall section, relevance bars, scope badges in CpkMemoryList (OSS memory B3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:58:45 -07:00
Maxim 379c80e1c9 feat(web-inspector): add pure recall relevance helpers + tests (OSS memory B3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:57:14 -07:00
Maxim 7308f34055 feat(web-inspector): rename Learning tab to Memory (OSS memory B3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:55:10 -07:00
Maxim 6c36e0f7bc Merge slot B0t6 into integration 2026-07-14 14:21:23 -07:00
Maxim 742e5c908d Merge slot A3 into integration 2026-07-14 14:21:22 -07:00
Maxim 895e2dd2d8 feat(react-core): enforce A2UI catalog component toggling on both context and render paths
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:19:28 -07:00
Maxim 23a56400bc chore(web-inspector): regenerate Tailwind CSS for Capabilities tab utilities (A3) 2026-07-14 14:17:45 -07:00
Maxim 0b14899dbb feat(web-inspector): mark fired capabilities with an active dot (A3 task 7, optional) 2026-07-14 14:17:14 -07:00
Maxim d785693062 test(web-inspector): cover Capabilities view render + toggles (A3 task 6) 2026-07-14 14:16:01 -07:00
Maxim eb31768a00 feat(core): open project_meta realtime channel and surface project memories
Parse optional projectJoinToken/projectJoinCode from /memories/subscribe.
When present, open a second project_meta:memories:<code> realtime channel
alongside the user channel, feeding the same id-keyed reducer. Both channels
share the same session stamp so the reducer's superseded-context guard drops
stale deltas from both identically (the D4 landmine). Absent project creds ->
user-only, silent degrade, realtimeStatus unaffected.

Remove the user-scope snapshot filter now that project rows stay in sync via
the project channel, and invert the snapshot test to expect project rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 14:13:50 -07:00
Maxim 557ba14ebd feat(web-inspector): implement Capabilities view + toggles (A3 task 5) 2026-07-14 14:13:32 -07:00
Maxim 1300afd067 feat(web-inspector): dispatch Capabilities view from renderMainContent (A3 task 4) 2026-07-14 14:12:37 -07:00
Maxim ad7c052af2 feat(web-inspector): add Capabilities nav item gated on tools/catalog (A3 task 3) 2026-07-14 14:11:40 -07:00
Maxim 201d4477c0 feat(web-inspector): add capability row model + pure helper with tests (A3 task 2) 2026-07-14 14:10:57 -07:00
Maxim 6ac2a9e032 feat(web-inspector): add Capabilities tab label + MenuKey (A3 task 1) 2026-07-14 14:09:01 -07:00
Maxim 597b9e0725 Merge slot B1b into integration 2026-07-14 13:57:18 -07:00
Maxim a78de81eaa Merge slot B2 into integration 2026-07-14 13:57:18 -07:00
Maxim 9084f29cd5 feat(core): expose catalogComponents + enable/disable via RunHandler delegation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 13:55:43 -07:00
Maxim 227059ad48 feat(core): add catalog-component state + methods to RunHandler
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 13:55:43 -07:00
Maxim 491675037b feat(core): add recall() to the memory store with score projection
Adds recall(query, opts?) to the MemoryStore (POST /memories/recall,
hybrid RAG), a score? field on Memory, the recallResponseToMemory
projector, MEMORIES_RECALL_PATH, and the MEMORY_RECALL_FAILED error
code. The user-scope list filter is intentionally retained (its
removal is relocated to a later slot co-landing with the project
realtime channel).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 13:52:30 -07:00
Maxim d62df4b3d1 feat(runtime): thread project join creds through /memories/subscribe proxy
Extend the memory subscribe passthrough so the response carries the
optional project-scoped realtime credentials { projectJoinToken?,
projectJoinCode? } alongside the existing user { joinToken, joinCode }.

- Widen SubscribeToMemoriesResponse with optional projectJoinToken /
  projectJoinCode; the platform client passes them through verbatim.
- handleSubscribeToMemories forwards both fields only when present and
  omits them when absent (silent-degrade contract; client then opens
  only the user channel). Field names match Intelligence B0a verbatim.
- Tests: handler forwards project creds when present and omits when
  absent (no present-with-undefined keys); client passes them through.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 13:51:47 -07:00
Maxim 3568dc1326 Merge slot B1a into integration 2026-07-14 13:41:37 -07:00
Maxim 70d1c3ae9f Merge slot A2r into integration 2026-07-14 13:41:37 -07:00
Maxim f01bba7378 chore: WIP preserve (round 2, UNVERIFIED — verify+squash pending) 2026-07-14 13:00:02 -07:00
Maxim 5db6ded719 chore: WIP preserve (round 2, UNVERIFIED — verify+squash pending) 2026-07-14 13:00:02 -07:00
Maxim bd21ce8365 chore: WIP preserve (round 2, UNVERIFIED — verify+squash pending) 2026-07-14 13:00:01 -07:00
Alem Tuzlak ecb91c8770 fix(channels-intelligence): teams thread history via getHistory + getMessages
getHistory was Slack-only: it keyed off threadTs and returned [] for any route
without one, so a Teams route ({adapter:'teams', tenantId, conversationId})
never hit /api/channels/history. That starved BOTH history paths on Teams --
agent.messages seeding (conversationStore.getOrCreate) and the read_thread tool
-- so the agent always reported "no earlier messages" / "no image in thread".

- getHistory is now adapter-aware (mirrors conversationKeyFromReplyTarget's
  per-adapter switch): Slack keys off teamId/channel/threadTs; Teams sends
  adapter=teams + tenantId + conversationId, matching app-api's
  teams:{tenantId}:{conversationId} thread_key. The /api/channels/history route
  already accepts this shape. Slack query order is unchanged.
- Add getMessages to the adapter so thread.getMessages() (the read_thread tool)
  reads reconstructed history via the transport and maps it to ThreadMessage[].
  Without it Thread.getMessages() returns [] and thread-reading tools see
  nothing even when history exists.

Adds tests for the Teams getHistory query and the missing-id short-circuit.
2026-07-14 19:35:08 +02:00
Benjamin Taylor 2f72e5d720 fix(core): don't surface a user-initiated Stop as an agent error (#5966)
Follow-up to #5812/#5885. When the user presses Stop, an agent may emit a
terminal RUN_ERROR (often code "abort") as its cancellation signal. The client's
onRunErrorEvent surfaced that unconditionally via emitAgentError →
AGENT_RUN_ERROR_EVENT → the default chat error banner ("This operation was
aborted"), even though a user stop is expected, not a failure.

Suppress the error emission in onRunErrorEvent when the run was user-aborted,
mirroring the local-abort suppression already on the runAgent/connectAgent
paths. Prefer the client's own _runAbortController.signal.aborted (robust) over
the agent-supplied code, with code === "abort" as a secondary signal. Normal
RUN_ERRORs are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 09:14:29 -05:00
Ben Taylor 855446e1ab fix(runtime): stop finalizeRunEvents emitting events after a terminal (#5812) (#5885)
## Summary

Pressing **Stop** while an assistant message is streaming
(CopilotRuntime + `HttpAgent` proxy) crashed the chat with:

```
Cannot send event type 'TEXT_MESSAGE_END': The run has already errored with 'RUN_ERROR'. No further events can be sent.
```

Root cause: `finalizeRunEvents` appended a trailing `TEXT_MESSAGE_END`
**after** the `RUN_ERROR` that the aborted agent had already emitted.

Fixes #5812.

## Root cause

When the upstream agent (e.g. pydantic-ai's `AGUIAdapter`) is aborted
mid-stream it emits a live `RUN_ERROR` while a text message is still
open — it does **not** close the message first. All runners
(`in-memory`, `intelligence`, `sqlite`) stream `finalizeRunEvents`'
output *after* everything the agent already emitted, so the appended
closer landed past the terminal:

| | outgoing event order |
|---|---|
| **Before** | `… TEXT_MESSAGE_CONTENT → RUN_ERROR → TEXT_MESSAGE_END` ❌
verifier throws |
| **After** | `… TEXT_MESSAGE_CONTENT → RUN_ERROR` ✅ terminal closes the
message client-side |

Per the AG-UI invariant: at most one terminal event per run, and no
sub-events after it. I confirmed against the real `@ag-ui/client`
`verifyEvents` (the verifier the browser runs) that a terminal arriving
with a message still open is valid — the terminal implicitly closes it.

## Fix

`finalizeRunEvents` (in `@copilotkit/shared`) now returns early and
appends **nothing** when the stream already contains a terminal event
(`RUN_FINISHED` or `RUN_ERROR`). The abrupt-end path (no terminal →
close open streams + synthesize a terminal, in the correct order) is
unchanged. No API/signature change; the in-memory, intelligence, and
sqlite runners all inherit the fix.

## Testing

RED→GREEN verified — each new/updated assertion was confirmed to fail
against the pre-fix code:

- **`finalize-events.test.ts`** — terminal-present appends nothing
(parametrized over `RUN_FINISHED` and `RUN_ERROR`) + a named #5812 case.
- **`in-memory-runner.test.ts`** — end-to-end mid-stream-stop
regression: a fake `HttpAgent`-style agent is stopped between
`TEXT_MESSAGE_START` and `TEXT_MESSAGE_END`; asserts no events follow
`RUN_ERROR` **and** that the collected stream passes `verifyEvents`
(before the fix this threw the exact browser error).
- **`intelligence-runner.test.ts`** — corrected a pre-existing assertion
that had encoded the buggy post-terminal `TEXT_MESSAGE_END`.

Green: full `@copilotkit/runtime` suite, `@copilotkit/sqlite-runner`,
`@copilotkit/shared`, `check-types`, `oxlint` (0 errors), and build.

## Reviewer notes

- The behavior change is a single early-return in `finalize-events.ts`;
the `terminalEventMissing` guards simplify away because they're only
reachable when no terminal exists.
- Diff is +204/−55 across 4 files, the bulk of it tests.
2026-07-14 09:03:15 -05:00
Ben Taylor c266142354 fix(react-core): scope streamdown table action controls in packaged CSS (#5775) (#5944)
Fixes #5775.

## Problem

After #5099 scoped the streamdown markdown/table styles under
`[data-copilotkit] [data-streamdown="…"]`, the **table action controls**
(copy / download) are still unstyled for hosts that import
`@copilotkit/react-core/v2/styles.css` but don't also ship streamdown's
raw Tailwind utilities. streamdown renders the controls row, per-button
wrappers, trigger buttons, dropdown popovers and menu items with
unprefixed utilities (`flex`, `items-center`, `justify-end`, `gap-1`,
`cursor-pointer`, `p-1`, …) and **no stable `data-streamdown`
attribute**, so CopilotKit's packaged CSS didn't cover them — the
controls rendered as vertically stacked plain icons instead of a
right-aligned row.

## Fix

Add scoped fallback selectors under `[data-copilotkit]
[data-streamdown="table-wrapper"]`, targeting the controls chrome
**structurally** (since it has no `data-streamdown` hook):

- controls row → `> div:first-child:not(:last-child)` (flex,
right-aligned, gap)
- per-button wrapper → `… > div` (relative, positions the popover)
- trigger buttons → `… > div > button` (matches the code-block
copy/download button styling)
- dropdown popover → `… > div > div`
- popover menu items → `… > div > div > button`

The controls row is `table-wrapper`'s first child **only when controls
are enabled**; `:not(:last-child)` leaves a control-less table (whose
single child is the scroll container, already styled by #5099)
untouched.

## Verification

- **Compiles.** Built `globals.css` through the Tailwind v4 CLI — every
`@apply` resolves (e.g. `bg-background` → `var(--background)`,
`shadow-lg` → the shadow vars, `min-w-[120px]` → `min-width:120px`) and
all five rules emit with correct values.
- **Selectors match the real DOM.** A new DOM test
(`streamdown-table-controls.test.tsx`) renders a real `<Streamdown>`
table and asserts the controls row is `table-wrapper`'s first-non-only
child, carries no `data-streamdown` attribute, and contains the trigger
buttons under `> div > button` — i.e. the scoped selectors target real
elements. This also guards against streamdown markup drift.
- **Selector presence** guarded by `streamdown-styles.test.ts`
(whitespace-robust).
- Full `styles/__tests__` suite green; `oxlint`/`oxfmt` clean.

## Note (out of scope, discovered while fixing)

At runtime in streamdown `1.6.11` the `<table>` element is stamped
`data-streamdown="table-wrapper"` (not `"table"`): `MarkdownTable`
passes `data-streamdown="table-wrapper"` as a prop that leaks through
`...rest` onto the `<table>`, overriding the intended `"table"`. So
#5099's `[data-streamdown="table"]` selector currently matches nothing,
and the table also matches the `table-wrapper` rules. The controls fix
here is unaffected (its child-combinator selectors don't match the
table's `thead`/`tbody` children), but the `[data-streamdown="table"]`
selector is worth a separate follow-up / upstream report.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-13 17:23:33 -05:00
Benjamin Taylor 5b7eccf464 fix(react-core): drop stale MCP follow-ups instead of re-homing them (OSS review)
Addresses two P1s on #5940: the re-home path (a) injected unscoped frontend
tools/readable context into the background run — `buildFrontendTools`/
`getContextForAgent` include entries with no agentId, so the thread-A run
received thread-B's live context and could execute global frontend handlers
against B — and (b) lost continuity across multiple queued follow-ups (fresh
per-item proxy + enqueue-time snapshot, so run 2 never saw run 1's result).

Both stem from *running* the stale follow-up. Switch to skip-stale: when the
shared agent's threadId no longer matches the thread the follow-up was enqueued
for, drop it (with a warning) rather than run it against the now-foreground
thread. This removes the proxy/registerProxiedAgent machinery entirely and
resolves both P1s by construction. The MCP app still gets its ui/message ack at
enqueue time; only the optional agent turn on an abandoned thread is skipped.

Removes the re-home unit/integration tests; the e2e regression test (no
cross-thread run after a switch) and simplified unit tests cover the behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 17:01:30 -05:00
Benjamin Taylor 3be17829cc test(react-core): integration coverage for MCP follow-up re-home path (#5819)
Drives the real CopilotKitCore + RunHandler + ProxiedCopilotRuntimeAgent via
registerProxiedAgent against a mocked transport (mirrors
proxied-runtime-transport.test.ts). Asserts the re-homed run reaches the runtime
addressed to the ORIGINAL threadId (not the foreground one), carries the captured
message, runs on an isolated instance whose events never reach the shared agent,
and unregisters the transient proxy after. Closes the delegate/replay-lifecycle
gap the mocked-host unit tests couldn't reach — in CI, no live runtime needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 17:01:30 -05:00
Benjamin Taylor a1dd26f0b4 fix(react-core): scope queued MCP ui/message follow-ups to enqueue-time thread (#5819)
Queued MCP app ui/message follow-up work executed against whatever thread the
shared registry agent pointed at when the queue drained. If the host switched
threads while the follow-up was queued (agent busy), the run — and its streamed
events — leaked into the now-foreground thread.

Capture the thread context at enqueue and route the follow-up through
ɵrunMcpFollowUp: same thread runs live on the shared agent (unchanged); a
changed thread re-homes the run onto an isolated registerProxiedAgent sibling
pinned to the original thread (own event stream, persists + reconciles on
return); a changed thread on a non-runtime agent drops the follow-up rather
than leaking it.

Regression from 762370a4e5 (revert of per-thread activity-renderer clone
routing, #3630); uses the sanctioned registerProxiedAgent primitive (#4629)
instead of reintroducing implicit clones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 17:01:30 -05:00
github-actions[bot] b693416bb6 style: auto-fix formatting 2026-07-13 16:58:26 -05:00
Benjamin Taylor 2882cdad92 fix(react-core): scope streamdown table action controls in packaged CSS (#5775)
After #5099 the table wrapper/cells are styled via [data-streamdown] selectors,
but the copy/download controls row, button wrappers, trigger buttons, dropdown
popovers and menu items render with raw Tailwind utilities and no stable
data-streamdown attribute — so hosts that import @copilotkit/react-core/v2
styles without shipping streamdown's own utilities saw them unstyled (icons
stacked vertically instead of a right-aligned row).

Add scoped fallback selectors under [data-copilotkit] [data-streamdown=
"table-wrapper"], targeting the controls chrome structurally. The controls row
is the first child ONLY when controls are enabled, so :not(:last-child) leaves a
control-less table (single child = the scroll container) untouched.

Verified against streamdown 1.6.11's actual rendered DOM (a new DOM test guards
that the structure the selectors assume still holds) and by compiling the CSS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 16:58:26 -05:00
Benjamin Taylor 690108bfd6 test(react-core): strengthen #5801 provider coverage to actually catch ctor-fetch
Adversarial review found the original provider tests passed even with the fix
removed: a single committed mount yields exactly one /info whether the ctor or
an effect fires it, and the ctor's fetch is several microtasks deep so ordering
can't distinguish it — only the multi-instance (discarded-render) case differs,
which Testing Library can't reproduce.

- Add CopilotKitProvider.deferWiring.test.tsx (mocked core): asserts the provider
  constructs with `deferInitialConnection: true` and calls `connect()` from an
  effect. This FAILS if the deferral wiring is dropped (verified).
- Keep the two real-core tests as normal-mount regression guards (one /info on
  mount; idempotent under StrictMode) and document that the multi-instance proof
  lives in core-defer-runtime-connection.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 16:21:22 -05:00
github-actions[bot] 6ab7e9d3d6 style: auto-fix formatting 2026-07-13 16:21:22 -05:00
Benjamin Taylor ce2d52397d fix(core,react-core): defer runtime /info out of the core constructor (#5801)
CopilotKitProvider constructs the core during React's render phase, and React
can start-and-discard renders (concurrent rendering / Suspense / StrictMode).
Because the constructor fired the `/info` request synchronously, every discarded
-and-recreated core issued its own request — a single page load was observed
firing 70-80 `/info` requests instead of one.

Separate construction (pure) from connection (network I/O):

- core: `deferInitialConnection` lets the constructor record the runtime config
  (so `runtimeUrl` stays available synchronously to hooks) WITHOUT starting the
  `/info` fetch. `connect()` starts the single connection and is idempotent
  (bails unless status is Disconnected), so a double-invoked mount effect
  collapses to one request. `updateRuntimeConnection` also gains an in-flight
  guard keyed by url+transport so concurrent same-target calls de-dupe.
- react-core: the provider constructs with `deferInitialConnection: true` and
  calls `copilotkit.connect()` from its commit-phase mount effect — so renders
  discarded before commit never fetch.

Backward compatible: without `deferInitialConnection` the constructor still
connects (Vue/Angular/vanilla unaffected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 16:21:22 -05:00
Aria Zhao 96390cac64 fix(react-core): forward refs through DropdownMenuTrigger and TooltipTrigger
Wrap the DropdownMenuTrigger and TooltipTrigger UI primitives in
React.forwardRef so they forward the ref to their underlying Radix
primitive. Radix `asChild` slots pass a ref to their child; as plain
function components these triggers dropped it, producing the React 18.3
"Function components cannot be given refs" warning on every render of
CopilotChatInput's AddMenuButton.

Fixes #5744
2026-07-13 16:07:50 +00:00
github-actions[bot] 1b057bf9df style: auto-fix formatting 2026-07-13 08:16:05 -05:00
Benjamin Taylor 6b6ee2e3c7 docs(channels-teams): document HITL button action envelope + contract test
Authoritative wire shape for the Action.Submit button + the Message activity Teams
delivers on click (activity.value carries { ckActionId, value }, text empty; not
Action.Execute). Round-trip contract test locks emit↔decode.
2026-07-13 08:16:05 -05:00
Benjamin Taylor 14a2276818 fix(channels-teams): fail-loud final send in TeamsMessageStream
finish() now performs the final send outside the error-swallowing throttle path
and rejects on transport failure, so a consumer never marks a turn delivered when
the last post/update didn't land. Mid-stream edits stay tolerant (log + retry).
2026-07-13 08:16:05 -05:00
enekesabel 4f29932ca8 fix(vue): keep attachment sources structuredClone-safe 2026-07-13 11:35:07 +02:00
Tyler Slaton 87db1b01e7 chore: release channels-whatsapp v0.0.2 (#5924)
## Release channels-whatsapp v0.0.2

**Scope:** `channels-whatsapp` | **Bump:** `patch`

---

### How this release process works

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

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

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

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

### Before merging

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

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
2026-07-10 15:56:51 -07:00