@volter/twin-cohere 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +224 -0
  2. package/defaults/handlers.json +26 -0
  3. package/dist/defaults/handlers.json +26 -0
  4. package/dist/src/cli.d.ts +2 -0
  5. package/dist/src/cli.js +31 -0
  6. package/dist/src/cohere-budget.d.ts +55 -0
  7. package/dist/src/cohere-budget.js +171 -0
  8. package/dist/src/cohere-capabilities.d.ts +14 -0
  9. package/dist/src/cohere-capabilities.js +1852 -0
  10. package/dist/src/cohere-conformance.d.ts +17 -0
  11. package/dist/src/cohere-conformance.js +464 -0
  12. package/dist/src/cohere-connector.d.ts +150 -0
  13. package/dist/src/cohere-connector.js +625 -0
  14. package/dist/src/cohere-models.d.ts +21 -0
  15. package/dist/src/cohere-models.js +73 -0
  16. package/dist/src/cohere-scenario.d.ts +57 -0
  17. package/dist/src/cohere-scenario.js +176 -0
  18. package/dist/src/cohere-server.d.ts +16 -0
  19. package/dist/src/cohere-server.js +184 -0
  20. package/dist/src/cohere-stub.d.ts +119 -0
  21. package/dist/src/cohere-stub.js +321 -0
  22. package/dist/src/cohere-twin.d.ts +82 -0
  23. package/dist/src/cohere-twin.js +1243 -0
  24. package/dist/src/cohere-types.d.ts +226 -0
  25. package/dist/src/cohere-types.js +40 -0
  26. package/dist/src/index.d.ts +15 -0
  27. package/dist/src/index.js +84 -0
  28. package/package.json +71 -0
  29. package/src/cli.ts +30 -0
  30. package/src/cohere-budget.ts +197 -0
  31. package/src/cohere-capabilities.ts +1855 -0
  32. package/src/cohere-conformance.ts +489 -0
  33. package/src/cohere-connector.ts +709 -0
  34. package/src/cohere-models.ts +79 -0
  35. package/src/cohere-scenario.ts +194 -0
  36. package/src/cohere-server.ts +195 -0
  37. package/src/cohere-stub.ts +337 -0
  38. package/src/cohere-twin.ts +1290 -0
  39. package/src/cohere-types.ts +231 -0
  40. package/src/index.ts +159 -0
@@ -0,0 +1,231 @@
1
+ // Shared wire-shape types for the Cohere API surface. These mirror the real vendor JSON shapes
2
+ // (not an SDK's internal types — the twin never imports a vendor SDK at runtime; the SDKs are
3
+ // exercised only in *.test.ts). Kept minimal but faithful.
4
+ //
5
+ // COHERE SPEAKS ITS OWN DIALECT. It is NOT OpenAI-compatible and copying an OpenAI-shaped pack's
6
+ // permissiveness would be exactly the bug: Cohere's chat response has no `choices` array, its
7
+ // finish reasons are UPPER-CASE (`COMPLETE`/`TOOL_CALL`/`MAX_TOKENS`), its usage lives under
8
+ // `usage.billed_units`/`usage.tokens` (v2) or `meta.billed_units` (v1), and its error envelope is
9
+ // a BARE `{ "message": "…" }` with no `error` wrapper, no `type`, no `code`.
10
+ //
11
+ // PROVENANCE for every shape below: the two SDKs this pack is built against, read from their
12
+ // published npm tarballs during this build (2026-08-31):
13
+ // • cohere-ai@8.1.0 — the Fern-generated client. The WIRE keys come from its
14
+ // `serialization/**/*.d.ts` `interface Raw` blocks (Fern's serializers map snake_case wire
15
+ // keys onto camelCase TS properties, so the TS types alone would be MISLEADING here):
16
+ // `V2ChatResponse`, `AssistantMessageResponse`, `Usage`, `UsageTokens`, `UsageBilledUnits`,
17
+ // `ChatFinishReason`, `V2ChatStreamResponse` (+ its twelve event members),
18
+ // `EmbedByTypeResponse`, `EmbedByTypeResponseEmbeddings`, `EmbedFloatsResponse`,
19
+ // `V2RerankResponse`, `V2RerankResponseResultsItem`, `ClassifyResponse`, `TokenizeResponse`,
20
+ // `DetokenizeResponse`, `CheckApiKeyResponse`, `ListModelsResponse`, `GetModelResponse`,
21
+ // `ApiMeta`, `ApiMetaBilledUnits`, `ApiMetaApiVersion`, `NonStreamedChatResponse`, `Dataset`,
22
+ // `Connector`, `EmbedJob`, `ToolCallV2`.
23
+ // • @ai-sdk/cohere@4.0.35 — the SECOND independent transcription of the same wire, and the
24
+ // STRICTER one for chat/embed/rerank: `cohereChatResponseSchema`, `cohereChatChunkSchema`
25
+ // (a `z.discriminatedUnion("type", …)` over the stream events), `cohereUsageSchema`,
26
+ // `cohereTextEmbeddingResponseSchema`, `cohereRerankingResponseSchema` and
27
+ // `cohereErrorDataSchema`. Where the two disagree in strictness the twin satisfies BOTH.
28
+
29
+ // ── Errors ──────────────────────────────────────────────────────────────────────────────
30
+ /**
31
+ * Cohere's API error envelope. It is a BARE object with ONE key — exactly as
32
+ * @ai-sdk/cohere@4.0.35's `cohereErrorDataSchema` transcribes it:
33
+ * `cohereErrorDataSchema = z.object({ message: z.string() })`
34
+ * There is no `error` wrapper, no `type`, and no `code`. A twin that emitted OpenAI's
35
+ * `{ error: { message, type } }` here would be caught by that schema on every failure path.
36
+ */
37
+ export type CohereApiError = { message: string };
38
+
39
+ // ── Chat v2 (`POST /v2/chat`) ───────────────────────────────────────────────────────────
40
+ /** A v2 chat message as the caller sends it. `content` is a string OR a content-part array. */
41
+ export type CohereMessageV2 = {
42
+ role: 'user' | 'assistant' | 'system' | 'tool';
43
+ content?: string | Array<Record<string, unknown>> | null;
44
+ tool_calls?: CohereToolCallV2[] | null;
45
+ tool_plan?: string | null;
46
+ tool_call_id?: string | null;
47
+ };
48
+
49
+ /**
50
+ * `ToolCallV2`. The vendor's own serializer declares `function?: ToolCallV2Function.Raw | null`,
51
+ * with `name` and `arguments` optional INSIDE it — but this type deliberately declares all three
52
+ * REQUIRED, because that is what the twin EMITS and what a caller may rely on.
53
+ *
54
+ * The narrowing is the right way round: @ai-sdk/cohere's `cohereChatResponseSchema` requires
55
+ * `function.name` and `function.arguments` on every tool call, and its `tool-call-start` chunk
56
+ * schema requires them too — so a twin that emitted the vendor's loosest legal shape would fail
57
+ * the stricter of its two clients. Narrower on OUTPUT, never on input. (§9 round 1, finding 9
58
+ * caught the comment here saying the opposite of the code.)
59
+ */
60
+ export type CohereToolCallV2 = {
61
+ id: string;
62
+ type: 'function';
63
+ function: { name: string; arguments: string };
64
+ };
65
+
66
+ /**
67
+ * The reason a chat request finished. Cohere's values are UPPER-CASE and are its own set — this
68
+ * is the single most conspicuous place an OpenAI-shaped copy goes wrong (`stop` / `tool_calls`
69
+ * are NOT Cohere values). Source: `api/types/ChatFinishReason.d.ts`.
70
+ */
71
+ export type CohereFinishReason = 'COMPLETE' | 'STOP_SEQUENCE' | 'MAX_TOKENS' | 'TOOL_CALL' | 'ERROR' | 'TIMEOUT';
72
+
73
+ export const COHERE_FINISH_REASONS: readonly CohereFinishReason[] = ['COMPLETE', 'STOP_SEQUENCE', 'MAX_TOKENS', 'TOOL_CALL', 'ERROR', 'TIMEOUT'];
74
+
75
+ /** `Usage` — TWO nested counters, `billed_units` and `tokens`, not a flat `prompt_tokens` trio. */
76
+ export type CohereUsage = {
77
+ billed_units: { input_tokens: number; output_tokens: number };
78
+ tokens: { input_tokens: number; output_tokens: number };
79
+ };
80
+
81
+ /** A content item on an assistant response message. */
82
+ export type CohereAssistantContentItem =
83
+ | { type: 'text'; text: string }
84
+ | { type: 'thinking'; thinking: string };
85
+
86
+ /** `AssistantMessageResponse`. */
87
+ export type CohereAssistantMessage = {
88
+ role: 'assistant';
89
+ content?: CohereAssistantContentItem[];
90
+ tool_plan?: string;
91
+ tool_calls?: CohereToolCallV2[];
92
+ };
93
+
94
+ /** The unary `POST /v2/chat` envelope — NO `choices`, NO `object`, NO `created`. */
95
+ export type CohereChatV2Response = {
96
+ id: string;
97
+ finish_reason: CohereFinishReason;
98
+ message: CohereAssistantMessage;
99
+ usage: CohereUsage;
100
+ };
101
+
102
+ /**
103
+ * The v2 streaming event `type` values, in the order `cohereChatChunkSchema`'s discriminated
104
+ * union declares them. The twin emits a subset; the SET is the vendor's.
105
+ */
106
+ export type CohereStreamEventType =
107
+ | 'message-start' | 'content-start' | 'content-delta' | 'content-end'
108
+ | 'tool-plan-delta' | 'tool-call-start' | 'tool-call-delta' | 'tool-call-end'
109
+ | 'citation-start' | 'citation-end' | 'message-end' | 'debug';
110
+
111
+ // ── Chat v1 (`POST /v1/chat`) — a DIFFERENT envelope, not a versioned alias ──────────────
112
+ /**
113
+ * v1's OWN finish-reason set. It is NOT the v2 `ChatFinishReason` set, which is the sort of
114
+ * conflation that looks harmless until a value crosses over: `NonStreamedChatResponse.finish_reason`
115
+ * is `FinishReason` — all EIGHT of `COMPLETE | STOP_SEQUENCE | ERROR | ERROR_TOXIC | ERROR_LIMIT |
116
+ * USER_CANCEL | MAX_TOKENS | TIMEOUT` (`api/types/FinishReason.d.ts`), with **no `TOOL_CALL`** and
117
+ * with three members v2 does not have. Typing the v1 body with the v2 union invited `TOOL_CALL`
118
+ * onto a response the vendor's union rejects (§9 round 1, finding 9) — and the first correction
119
+ * then transcribed only six of the eight, which §9 round two caught: this const is public API, so a
120
+ * consumer validating a v1 body against a short set rejects two legal vendor values.
121
+ * The twin only ever emits `COMPLETE` on v1, which is valid in this set, in the v2 set, and in the
122
+ * narrower `ChatStreamEndEventFinishReason` the v1 stream terminator uses.
123
+ */
124
+ export type CohereV1FinishReason = 'COMPLETE' | 'STOP_SEQUENCE' | 'ERROR' | 'ERROR_TOXIC' | 'ERROR_LIMIT' | 'USER_CANCEL' | 'MAX_TOKENS' | 'TIMEOUT';
125
+
126
+ export const COHERE_V1_FINISH_REASONS: readonly CohereV1FinishReason[] = ['COMPLETE', 'STOP_SEQUENCE', 'ERROR', 'ERROR_TOXIC', 'ERROR_LIMIT', 'USER_CANCEL', 'MAX_TOKENS', 'TIMEOUT'];
127
+
128
+ /**
129
+ * `NonStreamedChatResponse`. v1 answers a FLAT `text` string with `meta` (not `usage`), and takes
130
+ * a single `message` string plus `chat_history` — it is a different protocol from v2, sharing
131
+ * only the host. Modeling it as "v2 with a prefix" is the trap.
132
+ */
133
+ export type CohereChatV1Response = {
134
+ text: string;
135
+ generation_id: string;
136
+ response_id: string;
137
+ finish_reason: CohereV1FinishReason;
138
+ chat_history: Array<{ role: string; message: string }>;
139
+ meta: CohereApiMeta;
140
+ };
141
+
142
+ // ── `ApiMeta` — the v1 metering envelope ────────────────────────────────────────────────
143
+ export type CohereApiMeta = {
144
+ api_version: { version: string };
145
+ billed_units: Partial<{ input_tokens: number; output_tokens: number; search_units: number; classifications: number; images: number; image_tokens: number; pages: number }>;
146
+ tokens?: { input_tokens?: number; output_tokens?: number };
147
+ warnings?: string[];
148
+ };
149
+
150
+ // ── Embed ───────────────────────────────────────────────────────────────────────────────
151
+ /** The six `EmbeddingType` values Cohere accepts. `float` is the default and the only one
152
+ * @ai-sdk/cohere requests. */
153
+ export const COHERE_EMBEDDING_TYPES = ['float', 'int8', 'uint8', 'binary', 'ubinary', 'base64'] as const;
154
+ export type CohereEmbeddingType = (typeof COHERE_EMBEDDING_TYPES)[number];
155
+
156
+ /** The five `EmbedInputType` values. v2 embed REQUIRES one; v1 does not. */
157
+ export const COHERE_INPUT_TYPES = ['search_document', 'search_query', 'classification', 'clustering', 'image'] as const;
158
+ export type CohereInputType = (typeof COHERE_INPUT_TYPES)[number];
159
+
160
+ /** `V2EmbedRequestTruncate` / `EmbedRequestTruncate` — UPPER-CASE, unlike most vendors' enums. */
161
+ export const COHERE_TRUNCATE = ['NONE', 'START', 'END'] as const;
162
+
163
+ /** `EmbedByTypeResponse` — v2 keys the vectors BY TYPE under `embeddings`. */
164
+ export type CohereEmbedV2Response = {
165
+ id: string;
166
+ embeddings: Partial<Record<CohereEmbeddingType, number[][] | string[]>>;
167
+ texts: string[];
168
+ response_type: 'embeddings_by_type';
169
+ meta: CohereApiMeta;
170
+ };
171
+
172
+ /** `EmbedFloatsResponse` — v1's DEFAULT answer puts a flat `number[][]` at `embeddings`. The two
173
+ * shapes are not interchangeable, and the v1 caller only gets the by-type shape when it asks for
174
+ * `embedding_types`. */
175
+ export type CohereEmbedV1FloatsResponse = {
176
+ id: string;
177
+ embeddings: number[][];
178
+ texts: string[];
179
+ response_type: 'embeddings_floats';
180
+ meta: CohereApiMeta;
181
+ };
182
+
183
+ // ── Rerank ──────────────────────────────────────────────────────────────────────────────
184
+ export type CohereRerankResult = { index: number; relevance_score: number; document?: { text: string } };
185
+ export type CohereRerankResponse = { id: string; results: CohereRerankResult[]; meta: CohereApiMeta };
186
+
187
+ // ── Classify ────────────────────────────────────────────────────────────────────────────
188
+ export type CohereClassification = {
189
+ id: string;
190
+ input: string;
191
+ prediction: string;
192
+ predictions: string[];
193
+ confidence: number;
194
+ confidences: number[];
195
+ labels: Record<string, { confidence: number }>;
196
+ classification_type: 'single-label' | 'multi-label';
197
+ };
198
+ export type CohereClassifyResponse = { id: string; classifications: CohereClassification[]; meta: CohereApiMeta };
199
+
200
+ // ── Tokenize / detokenize / check-api-key ───────────────────────────────────────────────
201
+ export type CohereTokenizeResponse = { tokens: number[]; token_strings: string[]; meta: CohereApiMeta };
202
+ export type CohereDetokenizeResponse = { text: string; meta: CohereApiMeta };
203
+ export type CohereCheckApiKeyResponse = { valid: boolean; organization_id: string; owner_id: string };
204
+
205
+ // ── Models ──────────────────────────────────────────────────────────────────────────────
206
+ /** `CompatibleEndpoint` — the closed set of endpoint names a model card may list. */
207
+ export const COHERE_ENDPOINTS = ['chat', 'embed', 'classify', 'summarize', 'rerank', 'rate', 'generate'] as const;
208
+ export type CohereEndpoint = (typeof COHERE_ENDPOINTS)[number];
209
+
210
+ export type CohereModelCard = {
211
+ name: string;
212
+ endpoints: CohereEndpoint[];
213
+ finetuned: boolean;
214
+ context_length: number;
215
+ tokenizer_url: string | null;
216
+ default_endpoints: CohereEndpoint[];
217
+ is_deprecated?: boolean;
218
+ };
219
+
220
+ // ── Streaming ───────────────────────────────────────────────────────────────────────────
221
+ /**
222
+ * A single Server-Sent Event the streaming path emits (collected in tests, socketed by the
223
+ * server). `data` is the JSON payload; the terminator is signalled with `done: true` — cohere-ai's
224
+ * `core.Stream` is constructed with `eventShape: { type: 'sse', streamTerminator: '[DONE]' }`, so
225
+ * `data: [DONE]` is what ends a v2 chat stream.
226
+ */
227
+ export type SseEvent = { data?: Record<string, unknown>; done?: boolean };
228
+
229
+ /** A sink the streaming path writes events into (an injected collector in tests / a real HTTP SSE
230
+ * writer in the server). NO real sockets or setTimeout in the handler. */
231
+ export type SseSink = (event: SseEvent) => void;
package/src/index.ts ADDED
@@ -0,0 +1,159 @@
1
+ // @volter/twin-cohere — the Cohere twin (one vendor, one package), built on the shared
2
+ // @volter/world-core kernel. Cohere's API is TWO protocols on one host: the v1 surface
3
+ // (`/v1/chat` with a single `message` string, `/v1/embed` answering flat float vectors,
4
+ // `/v1/rerank`, `/v1/classify`, `/v1/tokenize`, `/v1/models`, datasets, connectors, embed jobs)
5
+ // and the v2 surface (`/v2/chat` with structured `messages[]`, `/v2/embed` keyed by embedding
6
+ // type, `/v2/rerank`). Both are modeled — vendor-faithfully, and as the DIFFERENT protocols they
7
+ // are rather than as a versioned alias. (Cohere is an API-first vendor: its dashboard is
8
+ // incidental tooling for keys, billing and usage, not where the work happens —
9
+ // docs/contributing/architecture.md C1b — so this pack ships no mirror.)
10
+ //
11
+ // THE HONEST CARVE-OUT: the twin cannot run the model, so every generative endpoint returns a
12
+ // DETERMINISTIC STUB (clearly labeled `[twin-stub:<model>]`), never pretending to be real
13
+ // inference. Everything around it — the wire protocol — is faithful. (Conformance + capability
14
+ // tooling live in @volter/world-tooling, a dev dependency — NOT re-exported here, per E2.)
15
+ export { handleCohereTwinRequest, buildChatV2, streamChatV2, COHERE_ROUTER_SURFACE, COHERE_CLOSED_SETS } from './cohere-twin.ts';
16
+ export type { CohereRequest, CohereResponseEnvelope, ChatV2Args } from './cohere-twin.ts';
17
+ export { createCohereTwinFetch, createCohereTwinServer, type CohereTwinFetchOptions } from './cohere-server.ts';
18
+ export { cohereScenarioAdapter, createCohereScenarioEngine, loadCohereScenarioDocument, realizeCohereRespond } from './cohere-scenario.ts';
19
+ export type { CohereScenarioEngine, CohereScenarioRequest, CohereScenarioRespond, ScenarioToolCall, ScriptedResult } from './cohere-scenario.ts';
20
+ export { COHERE_MODELS, COHERE_ENDPOINTS, findModel, modelServes } from './cohere-models.ts';
21
+ export {
22
+ base64Embedding,
23
+ classifyText,
24
+ COHERE_OUTPUT_DIMENSIONS,
25
+ cohereId,
26
+ contentToText,
27
+ countInputTokens,
28
+ embedDimensions,
29
+ estimateTokens,
30
+ fnv1a,
31
+ lastUserText,
32
+ preferredTokenId,
33
+ pseudoEmbedding,
34
+ quantizeEmbedding,
35
+ rerankScore,
36
+ segmentText,
37
+ stubAssistantText,
38
+ stubToolArguments,
39
+ stubToolCall,
40
+ stubToolPlan,
41
+ stubV1Text,
42
+ toolCallId,
43
+ toolNames,
44
+ } from './cohere-stub.ts';
45
+ export {
46
+ COHERE_EMBEDDING_TYPES,
47
+ COHERE_ENDPOINTS as COHERE_COMPATIBLE_ENDPOINTS,
48
+ COHERE_FINISH_REASONS,
49
+ COHERE_INPUT_TYPES,
50
+ COHERE_TRUNCATE,
51
+ COHERE_V1_FINISH_REASONS,
52
+ } from './cohere-types.ts';
53
+ export type {
54
+ CohereApiError, CohereApiMeta, CohereAssistantContentItem, CohereAssistantMessage,
55
+ CohereChatV1Response, CohereChatV2Response, CohereClassifyResponse, CohereEmbedV1FloatsResponse,
56
+ CohereEmbedV2Response, CohereEmbeddingType, CohereFinishReason, CohereInputType,
57
+ CohereMessageV2, CohereModelCard, CohereRerankResponse, CohereToolCallV2, CohereUsage,
58
+ CohereV1FinishReason,
59
+ SseEvent, SseSink,
60
+ } from './cohere-types.ts';
61
+ export {
62
+ COHERE_API_BASE,
63
+ cohereRequestForAction,
64
+ fullSyncCohere,
65
+ liveCohereExecute,
66
+ mapConnector,
67
+ mapDataset,
68
+ mapEmbedJob,
69
+ pollTimestamp,
70
+ pullCohereState,
71
+ pushCohereAction,
72
+ pushPendingCohereActions,
73
+ syncCohereFromReal,
74
+ unpushableReason,
75
+ } from './cohere-connector.ts';
76
+ export type { CohereExecute, LiveCohereOptions } from './cohere-connector.ts';
77
+ // The client-side rate budget — the fail-closed backstop `liveCohereExecute` routes every live
78
+ // request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
79
+ // here is Cohere's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
80
+ // bindings. Exported so an operator can inspect spend (`snapshot`) and so a caller can catch
81
+ // `CohereBudgetError` by type; there is deliberately no export that disables the guard.
82
+ export {
83
+ COHERE_BUDGET_CEILING,
84
+ COHERE_BUDGET_MAX_RETRY_AFTER_S,
85
+ COHERE_BUDGET_WINDOW_MS,
86
+ COHERE_CALL_WEIGHTS,
87
+ COHERE_RATE_BUDGET,
88
+ CohereBudget,
89
+ CohereBudgetError,
90
+ cohereBudgetPath,
91
+ cohereCallWeight,
92
+ } from './cohere-budget.ts';
93
+ export type { CohereBudgetErrorKind, CohereBudgetOptions, CohereBudgetReservation, CohereBudgetSnapshot } from './cohere-budget.ts';
94
+
95
+ // Registry descriptor: the pack self-describes so tooling can discover it, and it is the SINGLE
96
+ // HOME for this vendor's world-facing facts. `bun scripts/pack-facts.ts` compiles `adoption` /
97
+ // `hosts` / `endpointEnv` into packages/world-core/generated/pack-facts.json; re-run it
98
+ // after ANY edit below or the drift gate (scripts/pack-facts.test.ts) goes RED.
99
+ import type { TwinPack } from '@volter/world-core';
100
+ import { COHERE_RATE_BUDGET as RATE_BUDGET } from './cohere-budget.ts';
101
+ export const pack: TwinPack = {
102
+ vendor: 'cohere',
103
+ // The SAME object cohere-budget.ts declares at module load — one source of truth, so
104
+ // registering the pack and importing the connector can never arm two different ceilings.
105
+ rateBudget: RATE_BUDGET,
106
+ transport: 'rest',
107
+ archetype: 'generative',
108
+ bin: 'world-cohere',
109
+ // The subject types this twin projects. `token` is the LEARNED TOKENIZER VOCABULARY that
110
+ // `/v1/tokenize` observes and `/v1/detokenize` folds — twin-local state with no vendor
111
+ // endpoint, which is why the connector refuses to push it type-wide.
112
+ resources: ['dataset', 'connector', 'embed_job', 'token'],
113
+ specSource: 'Cohere API v1 + v2, grounded in the two installed SDKs (cohere-ai@8.1.0 — the Fern-generated client, whose serialization/**/*.d.ts `interface Raw` blocks carry the snake_case WIRE keys — and @ai-sdk/cohere@4.0.35, whose zod schemas are the stricter second transcription of chat/embed/rerank); envelope-faithful, model output is a labeled stub',
114
+ description: 'Cohere twin — faithful v1 AND v2 protocol envelopes (chat with SSE and NDJSON streaming, tool calls, UPPER-CASE finish reasons, two-level usage), deterministic embeddings across all six embedding_types, rerank/classify, a learned tokenizer vocabulary, the real model catalog, and stateful datasets / connectors / embed jobs; generative output is a labeled stub.',
115
+ // cohere-ai addresses the same origin with `v1/…` and `v2/…` paths (`core.url.join(environment,
116
+ // "v2/chat")`); @ai-sdk/cohere's default baseURL is 'https://api.cohere.com/v2'. The common
117
+ // prefix both share is the origin root, so the browser-routing prefix is '/'.
118
+ browserRouting: { apiPathPrefix: '/', loaderHost: 'https://api.cohere.com' },
119
+
120
+ // ADOPTION — how an app repo betrays that it talks to this vendor (§7 point 11), declared HERE
121
+ // rather than in the central SDK_TWINS / ENV_STEM_VENDORS maps (declaring a fact in both homes
122
+ // throws at module init).
123
+ // • sdks — the TWO official npm clients of the surface this pack models: Cohere's own
124
+ // Fern-generated client and the Vercel AI SDK provider. `cohere-ai` was on covers.ts's
125
+ // UNKNOWN_DEP_KNOWN_SERVICES list (the curated "well-known client with NO twin pack" set)
126
+ // until this pack was born; that entry is deleted in the same commit, because
127
+ // scripts/packless-claims.test.ts is RED on any name both packless-listed and pack-claimed.
128
+ // • envStems — the two SDKs read DIFFERENT variables and both are claimed: cohere-ai's
129
+ // `BearerAuthProvider` falls back to `CO_API_KEY` (`const ENV_TOKEN = "CO_API_KEY"`), while
130
+ // @ai-sdk/cohere calls `loadApiKey({ environmentVariableName: 'COHERE_API_KEY' })`. Claiming
131
+ // only one would let an app configured for the other read as uncovered. The stems are
132
+ // written bare because the overlay lowercases and the detector strips non-alphanumerics.
133
+ // • NO `scopes` entry: Cohere owns no npm scope. `@ai-sdk/` belongs to Vercel and is shared by
134
+ // every provider, so claiming it would attribute openai/anthropic/mistral traffic here.
135
+ adoption: {
136
+ // Cohere's official Python SDK (`cohere-ai` on npm, `cohere` on PyPI).
137
+ pypi: ['cohere'],
138
+ sdks: ['cohere-ai', '@ai-sdk/cohere'],
139
+ envStems: ['COHERE', 'CO'],
140
+ },
141
+
142
+ // INTERCEPTION (§7 point 8). ONE host: `CohereEnvironment` in cohere-ai@8.1.0's
143
+ // environments.d.ts declares exactly `Production: "https://api.cohere.com"` and nothing else —
144
+ // no regional or staging siblings — and @ai-sdk/cohere's default baseURL is that host plus
145
+ // `/v2`. The Bedrock/SageMaker clients the SDK also ships (`AwsClient`, `BedrockClient`,
146
+ // `SagemakerClient`) address AWS hosts, which are a different vendor's plane and deliberately
147
+ // NOT claimed here.
148
+ hosts: [{ host: 'api.cohere.com' }],
149
+
150
+ // WORLD WIRING (§7 point 12) — the ruling is NONE, and it is grounded, not skipped.
151
+ // NEITHER SDK reads a base-URL environment variable. cohere-ai takes the override as a
152
+ // CONSTRUCTOR option (`baseUrl` / `environment`, resolved through `core.Supplier.get`) and the
153
+ // only env var anywhere in its source is `CO_API_KEY` (plus the AWS credential names its Bedrock
154
+ // client uses); @ai-sdk/cohere takes `options.baseURL` and reads only `COHERE_API_KEY`.
155
+ // Inventing a `COHERE_BASE_URL` would make `covers` report the world covered — any app-read env
156
+ // counts — while the app, reading no such var, still talked to the real vendor. Interception is
157
+ // therefore host-based, via the `hosts` entry above, which is the truth.
158
+ endpointEnvNone: 'neither official SDK reads a base-URL env var: cohere-ai@8.1.0 takes the override as the `baseUrl`/`environment` CONSTRUCTOR option and reads only CO_API_KEY from the environment (auth/BearerAuthProvider.js), and @ai-sdk/cohere@4.0.35 takes `options.baseURL` and reads only COHERE_API_KEY. Interception is host-based on api.cohere.com; inventing a COHERE_BASE_URL nothing reads would make `covers` claim a world it does not cover.',
159
+ };