mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
7baed27370
`src/headless.ts` re-exported five runtime values inside `export type { … }`
blocks. A type-only re-export strips the runtime binding, so the symbol is
unimportable and — for the enums — the field it types cannot be compared
against at all, because an enum-typed field rejects a bare string literal.
`RenderToolProps["status"]` is `ToolCallStatus`, so a consumer of this PR's own
render-prop contract had no working way to branch on `status`.
Determined empirically, not by reading: a throwaway probe imported all 32
re-exported symbols as values under the package's real tsconfig. The 27 that
are genuine types reported TS2693 ("only refers to a type"); five did not, and
those five are the ones moved. Declaration sites confirm each:
ToolCallStatus packages/core/src/types.ts:14 export enum
CopilotKitCoreErrorCode packages/core/src/core/core.ts:99 export enum
CopilotKitCoreRuntimeConnectionStatus packages/core/src/core/core.ts:320 export enum
UseAgentUpdate packages/react-core/src/v2/hooks/use-agent.tsx:13
export enum
AbstractAgent @ag-ui/client declare abstract class
Nothing else changed kind: Suggestion, FrontendTool, Message, ToolCall,
ToolMessage, AgentCapabilities, ResumeStatus, Interrupt, ResumeEntry, the
Interrupt*/RenderTool*/Thread*/CopilotChat* prop and config types,
ReactFrontendTool, ReactHumanInTheLoop, ReactToolCallRenderer and
CopilotKitContextValue are all genuine types and stay `export type`.
`src/index.ts` does `export * from "./headless"`, which republishes values and
types alike, so both published entry points are fixed. Verified in the built
output: all five appear without a `type` prefix in dist/headless.d.mts and
dist/index.d.mts, and as runtime bindings in headless.mjs, index.mjs and
index.cjs.
Negative control: with the pre-fix headless.ts the same probe produced ten
TS1362 errors ("cannot be used as a value because it was exported using
'export type'") across both entries; with the fix, zero.
This makes the already-merged docs on this branch true. The RN reference pages
write `import { ToolCallStatus } from "@copilotkit/react-native"` and
`status === ToolCallStatus.Executing` (useRenderTool.mdx:170, useFrontendTool.mdx:129,
useHumanInTheLoop.mdx:86) and `import { useAgent, UseAgentUpdate }` with
`updates: [UseAgentUpdate.OnMessagesChanged]` (useAgent.mdx:234). None of those
imports resolved before this commit.
AbstractAgent is a deliberate inclusion, not scope creep: it is a runtime class
and the AG-UI extension point consumers subclass, and @ag-ui/client is a
dependency of this package rather than a peer, so a consumer cannot reliably
import it from there directly. It carries no bundle cost — the headless entry
already imports @ag-ui/client transitively through
@copilotkit/react-core/v2/headless, and esbuild tree-shakes an unused
re-export, so scripts/measure-headless.mjs still reports 92.7 kB gzip.
Forced test change, in scope only because the fix causes it: the value
re-export makes headless.ts the first runtime importer of @copilotkit/core in
this package's graph, so `import "../index"` now evaluates real core, which
named-imports RUNTIME_MODE_SSE and friends from @copilotkit/shared.
headless-integration.test.tsx replaced that module wholesale with a two-key
factory, so the import threw. Fixed by spreading importOriginal() instead of
replacing — the form vitest's own error message prescribes — leaving
createLicenseContextValue the only stubbed member. No assertion, case or
coverage changed.
Call-Site Enumeration (Procedure 2 step 8) — `grep -rn` per symbol across
packages/ plus every importer of @copilotkit/react-native in the repo. Every
site holds, because type -> value is a widening: an `import type` of a value
export is still legal.
ToolCallStatus
packages/react-native/src/headless.ts:94 — the changed export. Holds.
No other site in packages/ or examples/ names it. Nothing imported it
before, which is the bug.
CopilotKitCoreRuntimeConnectionStatus
packages/react-native/src/headless.ts:95 — the changed export. Holds.
No other site.
CopilotKitCoreErrorCode
packages/react-native/src/headless.ts:96 — the changed export. Holds.
CopilotKitProvider.tsx:12,37 / CopilotChat.tsx:13,93 / CopilotPopup.tsx:26,223
— all `import type … from "@copilotkit/core"`, used only in a `code:` field
position. They import from core directly, not through this entry, and a
type position is unaffected by the re-export kind. Hold.
UseAgentUpdate
packages/react-native/src/headless.ts:65 — the changed export. Holds.
packages/react-core/src/v2/headless.ts:41 — already a value export there,
with a comment giving this exact reason; this commit makes RN agree with it
rather than diverge. Holds.
AbstractAgent
packages/react-native/src/headless.ts:107 — the changed export. Holds.
packages/react-native/src/__mocks__/test-copilotkit.tsx:22,41,42 — already
imports the class as a VALUE from @ag-ui/client and subclasses it, i.e. it
had to bypass this entry to do what the entry now permits. Unchanged and
still passing. Holds.
packages/react-core/src/v2/**, packages/channels-telegram/** — all import
from @ag-ui/client directly; none route through @copilotkit/react-native.
Hold.
Importers of @copilotkit/react-native outside the package
examples/v2/react-native/demo/{App.tsx,src/ChatScreen.tsx,index.js} — import
CopilotKitProvider, useAgent, useCopilotKit, useFrontendTool and the
polyfills entry. None of the five symbols appears anywhere in the demo, so
nothing to break; the demo is now able to import them. Holds.
Surface guards
src/__tests__/headless-entry-surface.test.ts — its `not.toHaveProperty`
denylist covers the chat/attachment exports and the two removed registry
symbols; none of the five is listed, and its bare-specifier bans
(@gorhom/bottom-sheet, expo-*, shiki/mermaid/katex/a2ui-renderer, non-headless
react-core entries) are unaffected by adding @copilotkit/core and
@ag-ui/client edges. Passes unchanged.
Verification: `pnpm nx run @copilotkit/react-native:check-types` succeeds
(tsc --noEmit, 0 errors). `npx vitest run --reporter=dot` — 22 files, 253
tests, all passing. `npx oxfmt --check` clean; `npx oxlint` 0 errors and 2
warnings, both pre-existing in the touched test file (no-shadow on a mocked
`React`, no-this-in-sfc).
Note on a pre-existing flake: headless-entry-surface.test.ts hits the 5000ms
default testTimeout on `await import("../headless")` when the machine is loaded.
Measured 6 serial runs each way on the same box — pre-fix headless.ts failed
4 of 6, post-fix 3 of 6, identical timeout signature — so it predates this
change and is load-induced, not caused by it. Every run above used
`--testTimeout=60000`; raising that default (or making those assertions
static) is worth a follow-up, and is not this commit's to make.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
133 lines
5.3 KiB
TypeScript
133 lines
5.3 KiB
TypeScript
/**
|
|
* @copilotkit/react-native/headless
|
|
*
|
|
* Lean entry for headless React Native consumers: just the provider and the
|
|
* platform-agnostic agent/tool hooks, with NONE of the prebuilt chat UI.
|
|
*
|
|
* The default barrel (`@copilotkit/react-native`) re-exports the prebuilt chat
|
|
* components (`CopilotChat` / `CopilotModal` / `CopilotSidebar` / `CopilotPopup`,
|
|
* which import `@gorhom/bottom-sheet`) and `useAttachments` (which imports
|
|
* `expo-document-picker` + `expo-file-system`). Those are declared as optional
|
|
* peer dependencies, but a static re-export still forces Metro to resolve them
|
|
* at bundle time — so a consumer that only uses `CopilotKitProvider` + `useAgent`
|
|
* + `useFrontendTool` (a fully custom UI) had to install every chat/attachment
|
|
* native dep or stub them in `metro.config.js`, or the release bundle fails with
|
|
* `Unable to resolve module expo-document-picker`.
|
|
*
|
|
* Import from here to skip that stack entirely:
|
|
* ```tsx
|
|
* import { CopilotKitProvider, useAgent, useFrontendTool } from "@copilotkit/react-native/headless";
|
|
* ```
|
|
*
|
|
* Mirrors `@copilotkit/react-core/v2/headless` (issue #4893 / PR #5883), which
|
|
* this entry builds on. The default barrel re-exports everything here plus the
|
|
* chat UI, so existing imports from `@copilotkit/react-native` are unchanged.
|
|
*/
|
|
|
|
// Auto-install polyfills so consumers don't need a manual import.
|
|
// Must run before any CopilotKit code that relies on ReadableStream / fetch streaming.
|
|
import "./polyfills";
|
|
|
|
// React Native provider (no web deps, no bottom-sheet, no expo native modules)
|
|
export { CopilotKitProvider } from "./CopilotKitProvider";
|
|
export type { CopilotKitNativeProviderProps } from "./CopilotKitProvider";
|
|
|
|
// Provider props alias (mirrors web's CopilotKitProviderProps)
|
|
export type { CopilotKitNativeProviderProps as CopilotKitProviderProps } from "./CopilotKitProvider";
|
|
|
|
// Re-export context and hooks from react-core (platform-agnostic)
|
|
export {
|
|
useCopilotKit,
|
|
useLicenseContext,
|
|
CopilotKitContext,
|
|
type CopilotKitContextValue,
|
|
} from "@copilotkit/react-core/v2/context";
|
|
|
|
// Re-export hooks that work without web deps
|
|
// These consume the CopilotKitContext which our provider sets
|
|
export {
|
|
useAgent,
|
|
useFrontendTool,
|
|
useComponent,
|
|
useHumanInTheLoop,
|
|
useInterrupt,
|
|
useSuggestions,
|
|
useConfigureSuggestions,
|
|
useAgentContext,
|
|
useThreads,
|
|
useCapabilities,
|
|
defineToolCallRenderer,
|
|
CopilotChatDefaultLabels,
|
|
// Runtime enum, not a type: it is passed BY VALUE to useAgent's `updates`
|
|
// option (`updates: [UseAgentUpdate.OnMessagesChanged]`). `export type` would
|
|
// strip the runtime binding and leave consumers unable to name a member.
|
|
// react-core's own headless entry exports it as a value for the same reason.
|
|
UseAgentUpdate,
|
|
type UseInterruptConfig,
|
|
type AgentContextInput,
|
|
type JsonSerializable,
|
|
type Thread,
|
|
type UseThreadsInput,
|
|
type UseThreadsResult,
|
|
type CopilotChatLabels,
|
|
type CopilotChatConfigurationValue,
|
|
type InterruptEvent,
|
|
type InterruptHandlerProps,
|
|
type InterruptRenderProps,
|
|
type Interrupt,
|
|
type ResumeEntry,
|
|
type ResumeStatus,
|
|
type ReactFrontendTool,
|
|
type ReactHumanInTheLoop,
|
|
type RenderToolInProgressProps,
|
|
type RenderToolExecutingProps,
|
|
type RenderToolCompleteProps,
|
|
} from "@copilotkit/react-core/v2/headless";
|
|
|
|
// Re-export core runtime ENUMS as values. Each of these is `export enum` in
|
|
// @copilotkit/core, and each is the type of a prop or field a consumer has to
|
|
// branch on — `status === ToolCallStatus.Executing`,
|
|
// `status === CopilotKitCoreRuntimeConnectionStatus.Connected`. An enum-typed
|
|
// field rejects a bare string literal, so a type-only re-export leaves the
|
|
// member names unreachable and the field impossible to compare against.
|
|
export {
|
|
ToolCallStatus,
|
|
CopilotKitCoreRuntimeConnectionStatus,
|
|
CopilotKitCoreErrorCode,
|
|
} from "@copilotkit/core";
|
|
|
|
// Re-export core types commonly needed
|
|
export type { Suggestion, FrontendTool } from "@copilotkit/core";
|
|
|
|
// AbstractAgent is a runtime CLASS, not a type: it is the AG-UI extension point
|
|
// consumers subclass (`class MyAgent extends AbstractAgent`) and test with
|
|
// `instanceof`. @ag-ui/client is a dependency of this package rather than a peer,
|
|
// so a consumer cannot reliably import the class from there directly — a
|
|
// type-only re-export left the extension point unreachable from RN.
|
|
export { AbstractAgent } from "@ag-ui/client";
|
|
|
|
// Re-export AG-UI types for consumer convenience (matches web SDK surface)
|
|
export type {
|
|
Message,
|
|
AssistantMessage as AssistantMessageType,
|
|
ToolCall,
|
|
ToolMessage,
|
|
AgentCapabilities,
|
|
} from "@ag-ui/client";
|
|
|
|
// Render tool registration (React Native flavour: RN-typed render function,
|
|
// registered into react-core's canonical registry — no local registry).
|
|
export { useRenderTool } from "./hooks/useRenderTool";
|
|
export type { UseRenderToolOptions } from "./hooks/useRenderTool";
|
|
export type {
|
|
RenderToolProps,
|
|
RenderToolFunction,
|
|
} from "./hooks/render-tool-types";
|
|
|
|
// Render tool consumption. react-core's hook is platform-agnostic — it pulls no
|
|
// DOM and no chat-UI stack, and it returns ReactElement | null, which is exactly
|
|
// what FlatList's renderItem requires. Use it to render a registered component
|
|
// on any surface, chat or not.
|
|
export { useRenderToolCall } from "@copilotkit/react-core/v2/headless";
|
|
export type { ReactToolCallRenderer } from "@copilotkit/react-core/v2/headless";
|