@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,226 @@
1
+ /**
2
+ * Cohere's API error envelope. It is a BARE object with ONE key — exactly as
3
+ * @ai-sdk/cohere@4.0.35's `cohereErrorDataSchema` transcribes it:
4
+ * `cohereErrorDataSchema = z.object({ message: z.string() })`
5
+ * There is no `error` wrapper, no `type`, and no `code`. A twin that emitted OpenAI's
6
+ * `{ error: { message, type } }` here would be caught by that schema on every failure path.
7
+ */
8
+ export type CohereApiError = {
9
+ message: string;
10
+ };
11
+ /** A v2 chat message as the caller sends it. `content` is a string OR a content-part array. */
12
+ export type CohereMessageV2 = {
13
+ role: 'user' | 'assistant' | 'system' | 'tool';
14
+ content?: string | Array<Record<string, unknown>> | null;
15
+ tool_calls?: CohereToolCallV2[] | null;
16
+ tool_plan?: string | null;
17
+ tool_call_id?: string | null;
18
+ };
19
+ /**
20
+ * `ToolCallV2`. The vendor's own serializer declares `function?: ToolCallV2Function.Raw | null`,
21
+ * with `name` and `arguments` optional INSIDE it — but this type deliberately declares all three
22
+ * REQUIRED, because that is what the twin EMITS and what a caller may rely on.
23
+ *
24
+ * The narrowing is the right way round: @ai-sdk/cohere's `cohereChatResponseSchema` requires
25
+ * `function.name` and `function.arguments` on every tool call, and its `tool-call-start` chunk
26
+ * schema requires them too — so a twin that emitted the vendor's loosest legal shape would fail
27
+ * the stricter of its two clients. Narrower on OUTPUT, never on input. (§9 round 1, finding 9
28
+ * caught the comment here saying the opposite of the code.)
29
+ */
30
+ export type CohereToolCallV2 = {
31
+ id: string;
32
+ type: 'function';
33
+ function: {
34
+ name: string;
35
+ arguments: string;
36
+ };
37
+ };
38
+ /**
39
+ * The reason a chat request finished. Cohere's values are UPPER-CASE and are its own set — this
40
+ * is the single most conspicuous place an OpenAI-shaped copy goes wrong (`stop` / `tool_calls`
41
+ * are NOT Cohere values). Source: `api/types/ChatFinishReason.d.ts`.
42
+ */
43
+ export type CohereFinishReason = 'COMPLETE' | 'STOP_SEQUENCE' | 'MAX_TOKENS' | 'TOOL_CALL' | 'ERROR' | 'TIMEOUT';
44
+ export declare const COHERE_FINISH_REASONS: readonly CohereFinishReason[];
45
+ /** `Usage` — TWO nested counters, `billed_units` and `tokens`, not a flat `prompt_tokens` trio. */
46
+ export type CohereUsage = {
47
+ billed_units: {
48
+ input_tokens: number;
49
+ output_tokens: number;
50
+ };
51
+ tokens: {
52
+ input_tokens: number;
53
+ output_tokens: number;
54
+ };
55
+ };
56
+ /** A content item on an assistant response message. */
57
+ export type CohereAssistantContentItem = {
58
+ type: 'text';
59
+ text: string;
60
+ } | {
61
+ type: 'thinking';
62
+ thinking: string;
63
+ };
64
+ /** `AssistantMessageResponse`. */
65
+ export type CohereAssistantMessage = {
66
+ role: 'assistant';
67
+ content?: CohereAssistantContentItem[];
68
+ tool_plan?: string;
69
+ tool_calls?: CohereToolCallV2[];
70
+ };
71
+ /** The unary `POST /v2/chat` envelope — NO `choices`, NO `object`, NO `created`. */
72
+ export type CohereChatV2Response = {
73
+ id: string;
74
+ finish_reason: CohereFinishReason;
75
+ message: CohereAssistantMessage;
76
+ usage: CohereUsage;
77
+ };
78
+ /**
79
+ * The v2 streaming event `type` values, in the order `cohereChatChunkSchema`'s discriminated
80
+ * union declares them. The twin emits a subset; the SET is the vendor's.
81
+ */
82
+ export type CohereStreamEventType = 'message-start' | 'content-start' | 'content-delta' | 'content-end' | 'tool-plan-delta' | 'tool-call-start' | 'tool-call-delta' | 'tool-call-end' | 'citation-start' | 'citation-end' | 'message-end' | 'debug';
83
+ /**
84
+ * v1's OWN finish-reason set. It is NOT the v2 `ChatFinishReason` set, which is the sort of
85
+ * conflation that looks harmless until a value crosses over: `NonStreamedChatResponse.finish_reason`
86
+ * is `FinishReason` — all EIGHT of `COMPLETE | STOP_SEQUENCE | ERROR | ERROR_TOXIC | ERROR_LIMIT |
87
+ * USER_CANCEL | MAX_TOKENS | TIMEOUT` (`api/types/FinishReason.d.ts`), with **no `TOOL_CALL`** and
88
+ * with three members v2 does not have. Typing the v1 body with the v2 union invited `TOOL_CALL`
89
+ * onto a response the vendor's union rejects (§9 round 1, finding 9) — and the first correction
90
+ * then transcribed only six of the eight, which §9 round two caught: this const is public API, so a
91
+ * consumer validating a v1 body against a short set rejects two legal vendor values.
92
+ * The twin only ever emits `COMPLETE` on v1, which is valid in this set, in the v2 set, and in the
93
+ * narrower `ChatStreamEndEventFinishReason` the v1 stream terminator uses.
94
+ */
95
+ export type CohereV1FinishReason = 'COMPLETE' | 'STOP_SEQUENCE' | 'ERROR' | 'ERROR_TOXIC' | 'ERROR_LIMIT' | 'USER_CANCEL' | 'MAX_TOKENS' | 'TIMEOUT';
96
+ export declare const COHERE_V1_FINISH_REASONS: readonly CohereV1FinishReason[];
97
+ /**
98
+ * `NonStreamedChatResponse`. v1 answers a FLAT `text` string with `meta` (not `usage`), and takes
99
+ * a single `message` string plus `chat_history` — it is a different protocol from v2, sharing
100
+ * only the host. Modeling it as "v2 with a prefix" is the trap.
101
+ */
102
+ export type CohereChatV1Response = {
103
+ text: string;
104
+ generation_id: string;
105
+ response_id: string;
106
+ finish_reason: CohereV1FinishReason;
107
+ chat_history: Array<{
108
+ role: string;
109
+ message: string;
110
+ }>;
111
+ meta: CohereApiMeta;
112
+ };
113
+ export type CohereApiMeta = {
114
+ api_version: {
115
+ version: string;
116
+ };
117
+ billed_units: Partial<{
118
+ input_tokens: number;
119
+ output_tokens: number;
120
+ search_units: number;
121
+ classifications: number;
122
+ images: number;
123
+ image_tokens: number;
124
+ pages: number;
125
+ }>;
126
+ tokens?: {
127
+ input_tokens?: number;
128
+ output_tokens?: number;
129
+ };
130
+ warnings?: string[];
131
+ };
132
+ /** The six `EmbeddingType` values Cohere accepts. `float` is the default and the only one
133
+ * @ai-sdk/cohere requests. */
134
+ export declare const COHERE_EMBEDDING_TYPES: readonly ["float", "int8", "uint8", "binary", "ubinary", "base64"];
135
+ export type CohereEmbeddingType = (typeof COHERE_EMBEDDING_TYPES)[number];
136
+ /** The five `EmbedInputType` values. v2 embed REQUIRES one; v1 does not. */
137
+ export declare const COHERE_INPUT_TYPES: readonly ["search_document", "search_query", "classification", "clustering", "image"];
138
+ export type CohereInputType = (typeof COHERE_INPUT_TYPES)[number];
139
+ /** `V2EmbedRequestTruncate` / `EmbedRequestTruncate` — UPPER-CASE, unlike most vendors' enums. */
140
+ export declare const COHERE_TRUNCATE: readonly ["NONE", "START", "END"];
141
+ /** `EmbedByTypeResponse` — v2 keys the vectors BY TYPE under `embeddings`. */
142
+ export type CohereEmbedV2Response = {
143
+ id: string;
144
+ embeddings: Partial<Record<CohereEmbeddingType, number[][] | string[]>>;
145
+ texts: string[];
146
+ response_type: 'embeddings_by_type';
147
+ meta: CohereApiMeta;
148
+ };
149
+ /** `EmbedFloatsResponse` — v1's DEFAULT answer puts a flat `number[][]` at `embeddings`. The two
150
+ * shapes are not interchangeable, and the v1 caller only gets the by-type shape when it asks for
151
+ * `embedding_types`. */
152
+ export type CohereEmbedV1FloatsResponse = {
153
+ id: string;
154
+ embeddings: number[][];
155
+ texts: string[];
156
+ response_type: 'embeddings_floats';
157
+ meta: CohereApiMeta;
158
+ };
159
+ export type CohereRerankResult = {
160
+ index: number;
161
+ relevance_score: number;
162
+ document?: {
163
+ text: string;
164
+ };
165
+ };
166
+ export type CohereRerankResponse = {
167
+ id: string;
168
+ results: CohereRerankResult[];
169
+ meta: CohereApiMeta;
170
+ };
171
+ export type CohereClassification = {
172
+ id: string;
173
+ input: string;
174
+ prediction: string;
175
+ predictions: string[];
176
+ confidence: number;
177
+ confidences: number[];
178
+ labels: Record<string, {
179
+ confidence: number;
180
+ }>;
181
+ classification_type: 'single-label' | 'multi-label';
182
+ };
183
+ export type CohereClassifyResponse = {
184
+ id: string;
185
+ classifications: CohereClassification[];
186
+ meta: CohereApiMeta;
187
+ };
188
+ export type CohereTokenizeResponse = {
189
+ tokens: number[];
190
+ token_strings: string[];
191
+ meta: CohereApiMeta;
192
+ };
193
+ export type CohereDetokenizeResponse = {
194
+ text: string;
195
+ meta: CohereApiMeta;
196
+ };
197
+ export type CohereCheckApiKeyResponse = {
198
+ valid: boolean;
199
+ organization_id: string;
200
+ owner_id: string;
201
+ };
202
+ /** `CompatibleEndpoint` — the closed set of endpoint names a model card may list. */
203
+ export declare const COHERE_ENDPOINTS: readonly ["chat", "embed", "classify", "summarize", "rerank", "rate", "generate"];
204
+ export type CohereEndpoint = (typeof COHERE_ENDPOINTS)[number];
205
+ export type CohereModelCard = {
206
+ name: string;
207
+ endpoints: CohereEndpoint[];
208
+ finetuned: boolean;
209
+ context_length: number;
210
+ tokenizer_url: string | null;
211
+ default_endpoints: CohereEndpoint[];
212
+ is_deprecated?: boolean;
213
+ };
214
+ /**
215
+ * A single Server-Sent Event the streaming path emits (collected in tests, socketed by the
216
+ * server). `data` is the JSON payload; the terminator is signalled with `done: true` — cohere-ai's
217
+ * `core.Stream` is constructed with `eventShape: { type: 'sse', streamTerminator: '[DONE]' }`, so
218
+ * `data: [DONE]` is what ends a v2 chat stream.
219
+ */
220
+ export type SseEvent = {
221
+ data?: Record<string, unknown>;
222
+ done?: boolean;
223
+ };
224
+ /** A sink the streaming path writes events into (an injected collector in tests / a real HTTP SSE
225
+ * writer in the server). NO real sockets or setTimeout in the handler. */
226
+ export type SseSink = (event: SseEvent) => void;
@@ -0,0 +1,40 @@
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
+ export const COHERE_FINISH_REASONS = ['COMPLETE', 'STOP_SEQUENCE', 'MAX_TOKENS', 'TOOL_CALL', 'ERROR', 'TIMEOUT'];
29
+ export const COHERE_V1_FINISH_REASONS = ['COMPLETE', 'STOP_SEQUENCE', 'ERROR', 'ERROR_TOXIC', 'ERROR_LIMIT', 'USER_CANCEL', 'MAX_TOKENS', 'TIMEOUT'];
30
+ // ── Embed ───────────────────────────────────────────────────────────────────────────────
31
+ /** The six `EmbeddingType` values Cohere accepts. `float` is the default and the only one
32
+ * @ai-sdk/cohere requests. */
33
+ export const COHERE_EMBEDDING_TYPES = ['float', 'int8', 'uint8', 'binary', 'ubinary', 'base64'];
34
+ /** The five `EmbedInputType` values. v2 embed REQUIRES one; v1 does not. */
35
+ export const COHERE_INPUT_TYPES = ['search_document', 'search_query', 'classification', 'clustering', 'image'];
36
+ /** `V2EmbedRequestTruncate` / `EmbedRequestTruncate` — UPPER-CASE, unlike most vendors' enums. */
37
+ export const COHERE_TRUNCATE = ['NONE', 'START', 'END'];
38
+ // ── Models ──────────────────────────────────────────────────────────────────────────────
39
+ /** `CompatibleEndpoint` — the closed set of endpoint names a model card may list. */
40
+ export const COHERE_ENDPOINTS = ['chat', 'embed', 'classify', 'summarize', 'rerank', 'rate', 'generate'];
@@ -0,0 +1,15 @@
1
+ export { handleCohereTwinRequest, buildChatV2, streamChatV2, COHERE_ROUTER_SURFACE, COHERE_CLOSED_SETS } from './cohere-twin.js';
2
+ export type { CohereRequest, CohereResponseEnvelope, ChatV2Args } from './cohere-twin.js';
3
+ export { createCohereTwinFetch, createCohereTwinServer, type CohereTwinFetchOptions } from './cohere-server.js';
4
+ export { cohereScenarioAdapter, createCohereScenarioEngine, loadCohereScenarioDocument, realizeCohereRespond } from './cohere-scenario.js';
5
+ export type { CohereScenarioEngine, CohereScenarioRequest, CohereScenarioRespond, ScenarioToolCall, ScriptedResult } from './cohere-scenario.js';
6
+ export { COHERE_MODELS, COHERE_ENDPOINTS, findModel, modelServes } from './cohere-models.js';
7
+ export { base64Embedding, classifyText, COHERE_OUTPUT_DIMENSIONS, cohereId, contentToText, countInputTokens, embedDimensions, estimateTokens, fnv1a, lastUserText, preferredTokenId, pseudoEmbedding, quantizeEmbedding, rerankScore, segmentText, stubAssistantText, stubToolArguments, stubToolCall, stubToolPlan, stubV1Text, toolCallId, toolNames, } from './cohere-stub.js';
8
+ export { COHERE_EMBEDDING_TYPES, COHERE_ENDPOINTS as COHERE_COMPATIBLE_ENDPOINTS, COHERE_FINISH_REASONS, COHERE_INPUT_TYPES, COHERE_TRUNCATE, COHERE_V1_FINISH_REASONS, } from './cohere-types.js';
9
+ export type { CohereApiError, CohereApiMeta, CohereAssistantContentItem, CohereAssistantMessage, CohereChatV1Response, CohereChatV2Response, CohereClassifyResponse, CohereEmbedV1FloatsResponse, CohereEmbedV2Response, CohereEmbeddingType, CohereFinishReason, CohereInputType, CohereMessageV2, CohereModelCard, CohereRerankResponse, CohereToolCallV2, CohereUsage, CohereV1FinishReason, SseEvent, SseSink, } from './cohere-types.js';
10
+ export { COHERE_API_BASE, cohereRequestForAction, fullSyncCohere, liveCohereExecute, mapConnector, mapDataset, mapEmbedJob, pollTimestamp, pullCohereState, pushCohereAction, pushPendingCohereActions, syncCohereFromReal, unpushableReason, } from './cohere-connector.js';
11
+ export type { CohereExecute, LiveCohereOptions } from './cohere-connector.js';
12
+ export { COHERE_BUDGET_CEILING, COHERE_BUDGET_MAX_RETRY_AFTER_S, COHERE_BUDGET_WINDOW_MS, COHERE_CALL_WEIGHTS, COHERE_RATE_BUDGET, CohereBudget, CohereBudgetError, cohereBudgetPath, cohereCallWeight, } from './cohere-budget.js';
13
+ export type { CohereBudgetErrorKind, CohereBudgetOptions, CohereBudgetReservation, CohereBudgetSnapshot } from './cohere-budget.js';
14
+ import type { TwinPack } from '@volter/world-core';
15
+ export declare const pack: TwinPack;
@@ -0,0 +1,84 @@
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.js";
16
+ export { createCohereTwinFetch, createCohereTwinServer } from "./cohere-server.js";
17
+ export { cohereScenarioAdapter, createCohereScenarioEngine, loadCohereScenarioDocument, realizeCohereRespond } from "./cohere-scenario.js";
18
+ export { COHERE_MODELS, COHERE_ENDPOINTS, findModel, modelServes } from "./cohere-models.js";
19
+ export { base64Embedding, classifyText, COHERE_OUTPUT_DIMENSIONS, cohereId, contentToText, countInputTokens, embedDimensions, estimateTokens, fnv1a, lastUserText, preferredTokenId, pseudoEmbedding, quantizeEmbedding, rerankScore, segmentText, stubAssistantText, stubToolArguments, stubToolCall, stubToolPlan, stubV1Text, toolCallId, toolNames, } from "./cohere-stub.js";
20
+ export { COHERE_EMBEDDING_TYPES, COHERE_ENDPOINTS as COHERE_COMPATIBLE_ENDPOINTS, COHERE_FINISH_REASONS, COHERE_INPUT_TYPES, COHERE_TRUNCATE, COHERE_V1_FINISH_REASONS, } from "./cohere-types.js";
21
+ export { COHERE_API_BASE, cohereRequestForAction, fullSyncCohere, liveCohereExecute, mapConnector, mapDataset, mapEmbedJob, pollTimestamp, pullCohereState, pushCohereAction, pushPendingCohereActions, syncCohereFromReal, unpushableReason, } from "./cohere-connector.js";
22
+ // The client-side rate budget — the fail-closed backstop `liveCohereExecute` routes every live
23
+ // request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
24
+ // here is Cohere's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
25
+ // bindings. Exported so an operator can inspect spend (`snapshot`) and so a caller can catch
26
+ // `CohereBudgetError` by type; there is deliberately no export that disables the guard.
27
+ export { COHERE_BUDGET_CEILING, COHERE_BUDGET_MAX_RETRY_AFTER_S, COHERE_BUDGET_WINDOW_MS, COHERE_CALL_WEIGHTS, COHERE_RATE_BUDGET, CohereBudget, CohereBudgetError, cohereBudgetPath, cohereCallWeight, } from "./cohere-budget.js";
28
+ import { COHERE_RATE_BUDGET as RATE_BUDGET } from "./cohere-budget.js";
29
+ export const pack = {
30
+ vendor: 'cohere',
31
+ // The SAME object cohere-budget.ts declares at module load — one source of truth, so
32
+ // registering the pack and importing the connector can never arm two different ceilings.
33
+ rateBudget: RATE_BUDGET,
34
+ transport: 'rest',
35
+ archetype: 'generative',
36
+ bin: 'world-cohere',
37
+ // The subject types this twin projects. `token` is the LEARNED TOKENIZER VOCABULARY that
38
+ // `/v1/tokenize` observes and `/v1/detokenize` folds — twin-local state with no vendor
39
+ // endpoint, which is why the connector refuses to push it type-wide.
40
+ resources: ['dataset', 'connector', 'embed_job', 'token'],
41
+ 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',
42
+ 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.',
43
+ // cohere-ai addresses the same origin with `v1/…` and `v2/…` paths (`core.url.join(environment,
44
+ // "v2/chat")`); @ai-sdk/cohere's default baseURL is 'https://api.cohere.com/v2'. The common
45
+ // prefix both share is the origin root, so the browser-routing prefix is '/'.
46
+ browserRouting: { apiPathPrefix: '/', loaderHost: 'https://api.cohere.com' },
47
+ // ADOPTION — how an app repo betrays that it talks to this vendor (§7 point 11), declared HERE
48
+ // rather than in the central SDK_TWINS / ENV_STEM_VENDORS maps (declaring a fact in both homes
49
+ // throws at module init).
50
+ // • sdks — the TWO official npm clients of the surface this pack models: Cohere's own
51
+ // Fern-generated client and the Vercel AI SDK provider. `cohere-ai` was on covers.ts's
52
+ // UNKNOWN_DEP_KNOWN_SERVICES list (the curated "well-known client with NO twin pack" set)
53
+ // until this pack was born; that entry is deleted in the same commit, because
54
+ // scripts/packless-claims.test.ts is RED on any name both packless-listed and pack-claimed.
55
+ // • envStems — the two SDKs read DIFFERENT variables and both are claimed: cohere-ai's
56
+ // `BearerAuthProvider` falls back to `CO_API_KEY` (`const ENV_TOKEN = "CO_API_KEY"`), while
57
+ // @ai-sdk/cohere calls `loadApiKey({ environmentVariableName: 'COHERE_API_KEY' })`. Claiming
58
+ // only one would let an app configured for the other read as uncovered. The stems are
59
+ // written bare because the overlay lowercases and the detector strips non-alphanumerics.
60
+ // • NO `scopes` entry: Cohere owns no npm scope. `@ai-sdk/` belongs to Vercel and is shared by
61
+ // every provider, so claiming it would attribute openai/anthropic/mistral traffic here.
62
+ adoption: {
63
+ // Cohere's official Python SDK (`cohere-ai` on npm, `cohere` on PyPI).
64
+ pypi: ['cohere'],
65
+ sdks: ['cohere-ai', '@ai-sdk/cohere'],
66
+ envStems: ['COHERE', 'CO'],
67
+ },
68
+ // INTERCEPTION (§7 point 8). ONE host: `CohereEnvironment` in cohere-ai@8.1.0's
69
+ // environments.d.ts declares exactly `Production: "https://api.cohere.com"` and nothing else —
70
+ // no regional or staging siblings — and @ai-sdk/cohere's default baseURL is that host plus
71
+ // `/v2`. The Bedrock/SageMaker clients the SDK also ships (`AwsClient`, `BedrockClient`,
72
+ // `SagemakerClient`) address AWS hosts, which are a different vendor's plane and deliberately
73
+ // NOT claimed here.
74
+ hosts: [{ host: 'api.cohere.com' }],
75
+ // WORLD WIRING (§7 point 12) — the ruling is NONE, and it is grounded, not skipped.
76
+ // NEITHER SDK reads a base-URL environment variable. cohere-ai takes the override as a
77
+ // CONSTRUCTOR option (`baseUrl` / `environment`, resolved through `core.Supplier.get`) and the
78
+ // only env var anywhere in its source is `CO_API_KEY` (plus the AWS credential names its Bedrock
79
+ // client uses); @ai-sdk/cohere takes `options.baseURL` and reads only `COHERE_API_KEY`.
80
+ // Inventing a `COHERE_BASE_URL` would make `covers` report the world covered — any app-read env
81
+ // counts — while the app, reading no such var, still talked to the real vendor. Interception is
82
+ // therefore host-based, via the `hosts` entry above, which is the truth.
83
+ 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.',
84
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@volter/twin-cohere",
3
+ "version": "0.1.0",
4
+ "description": "Local Cohere twin — a faithful, stateful local Cohere API your real cohere-ai / @ai-sdk/cohere client talks to unmodified. Both protocol versions are modeled (v1 chat/embed/rerank/classify/tokenize + v2 chat/embed/rerank), with deterministic stubs for model output and vendor-faithful envelopes, streaming, errors and refusals. Built on @volter/world-core.",
5
+ "author": "Volter (https://github.com/volter-ai)",
6
+ "license": "Apache-2.0",
7
+ "files": [
8
+ "src",
9
+ "defaults",
10
+ "README.md",
11
+ "LICENSE",
12
+ "!**/*.test.ts",
13
+ "!**/*.test.tsx",
14
+ "dist"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/volter-ai/twin.git",
19
+ "directory": "packages/twin/cohere"
20
+ },
21
+ "homepage": "https://github.com/volter-ai/twin/tree/main/packages/twin/cohere#readme",
22
+ "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/src/index.d.ts",
26
+ "default": "./dist/src/index.js"
27
+ }
28
+ },
29
+ "bin": {
30
+ "world-cohere": "dist/src/cli.js"
31
+ },
32
+ "scripts": {
33
+ "test": "bun test src/*.test.ts",
34
+ "typecheck": "tsc --noEmit",
35
+ "build": "node ../../../scripts/publish/build.mjs",
36
+ "prepack": "node ../../../scripts/publish/prepare-publish.mjs prepack",
37
+ "postpack": "node ../../../scripts/publish/prepare-publish.mjs postpack"
38
+ },
39
+ "peerDependencies": {
40
+ "@volter/world-core": "2.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@ai-sdk/cohere": "^4.0.35",
44
+ "@types/bun": "^1.2.20",
45
+ "@types/node": "^24.0.0",
46
+ "@volter/world-core": "2.0.0",
47
+ "@volter/world-tooling": "0.1.0",
48
+ "cohere-ai": "^8.1.0",
49
+ "typescript": "^5.9.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=22.3"
53
+ },
54
+ "keywords": [
55
+ "twin",
56
+ "local",
57
+ "mock",
58
+ "mirror",
59
+ "simulator",
60
+ "fixtures",
61
+ "testing",
62
+ "sdk",
63
+ "api",
64
+ "localstack",
65
+ "cohere",
66
+ "command",
67
+ "rerank",
68
+ "embed",
69
+ "llm"
70
+ ]
71
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { keepProcessAlive } from '@volter/world-core/lifecycle';
3
+ // world-cohere CLI: serve the Cohere API twin or run conformance. Cohere is an API-first vendor —
4
+ // its dashboard is incidental tooling for keys, billing and usage, not where the work happens
5
+ // (docs/contributing/architecture.md C1b) — so this pack ships no mirror and there is no `mirror` command.
6
+ import { hasFlag, optionValue } from '@volter/world-core/args';
7
+ import { createCohereTwinServer } from './cohere-server.ts';
8
+
9
+ const [cmd, ...rest] = process.argv.slice(2);
10
+ const port = Number(optionValue(rest, '--port', '0')) || undefined;
11
+ const root = optionValue(rest, '--root') || undefined;
12
+ const readOnly = hasFlag(rest, '--read-only'); // a twin accepts writes unless started read-only
13
+ const scenario = optionValue(rest, '--scenario') || undefined; // scripted chat turns (JSON handlers file)
14
+
15
+ if (cmd === 'serve') {
16
+ const s = await createCohereTwinServer({ readOnly, ...(root ? { root } : {}), ...(port ? { port } : {}), ...(scenario ? { scenarioPath: scenario } : {}) });
17
+ process.stdout.write(`cohere twin (v1 + v2; model output is a deterministic stub)${readOnly ? ' [read-only]' : ''}${scenario ? ` [scenario: ${scenario}]` : ''} at http://127.0.0.1:${s.port}\n`);
18
+ await keepProcessAlive();
19
+ } else if (cmd === 'conformance') {
20
+ // dev-only; lazy so the bin runs without @volter/world-tooling in the runtime graph
21
+ const { checkCohereConformance } = await import('./cohere-conformance.ts');
22
+ const report = await checkCohereConformance({ ...(root ? { root } : {}) });
23
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
24
+ if (!report.ok) process.exitCode = 1;
25
+ } else {
26
+ // NON-ZERO on an unknown command. `world-cohere bogus` exiting 0 makes a typo in a world's boot
27
+ // script look like a successful start (§9 round two, MINOR 10).
28
+ process.stdout.write('Usage: world-cohere serve|conformance [--port N] [--root DIR] [--read-only] [--scenario FILE]\n');
29
+ process.exitCode = 1;
30
+ }