@cleocode/contracts 2026.6.12 → 2026.6.14

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 (216) hide show
  1. package/dist/__tests__/operation-def.test.js +5 -1
  2. package/dist/__tests__/operation-def.test.js.map +1 -1
  3. package/dist/__tests__/provider-def.test.d.ts +12 -0
  4. package/dist/__tests__/provider-def.test.d.ts.map +1 -0
  5. package/dist/__tests__/provider-def.test.js +84 -0
  6. package/dist/__tests__/provider-def.test.js.map +1 -0
  7. package/dist/__tests__/sealed-credential.test.d.ts +19 -0
  8. package/dist/__tests__/sealed-credential.test.d.ts.map +1 -0
  9. package/dist/__tests__/sealed-credential.test.js +74 -0
  10. package/dist/__tests__/sealed-credential.test.js.map +1 -0
  11. package/dist/__tests__/service-provider.test.d.ts +17 -0
  12. package/dist/__tests__/service-provider.test.d.ts.map +1 -0
  13. package/dist/__tests__/service-provider.test.js +108 -0
  14. package/dist/__tests__/service-provider.test.js.map +1 -0
  15. package/dist/__tests__/skill-executor-contract.test.js +3 -0
  16. package/dist/__tests__/skill-executor-contract.test.js.map +1 -1
  17. package/dist/acceptance-gate.d.ts +2 -3
  18. package/dist/acceptance-gate.d.ts.map +1 -1
  19. package/dist/boundary.d.ts +52 -0
  20. package/dist/boundary.d.ts.map +1 -1
  21. package/dist/boundary.js +24 -19
  22. package/dist/boundary.js.map +1 -1
  23. package/dist/conduit.d.ts +171 -0
  24. package/dist/conduit.d.ts.map +1 -1
  25. package/dist/config/index.d.ts +23 -0
  26. package/dist/config/index.d.ts.map +1 -0
  27. package/dist/config/index.js +22 -0
  28. package/dist/config/index.js.map +1 -0
  29. package/dist/config.d.ts +104 -1
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/data-accessor.d.ts +21 -1
  32. package/dist/data-accessor.d.ts.map +1 -1
  33. package/dist/data-accessor.js.map +1 -1
  34. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts +21 -0
  35. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.d.ts.map +1 -0
  36. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js +84 -0
  37. package/dist/dispatch/__tests__/tasks-gateway-crud-ops.test.js.map +1 -0
  38. package/dist/dispatch/identity.d.ts +1 -1
  39. package/dist/dispatch/identity.d.ts.map +1 -1
  40. package/dist/dispatch/identity.js +25 -0
  41. package/dist/dispatch/identity.js.map +1 -1
  42. package/dist/dispatch/operation-def.d.ts +20 -0
  43. package/dist/dispatch/operation-def.d.ts.map +1 -1
  44. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  45. package/dist/dispatch/operations-registry.js +1044 -182
  46. package/dist/dispatch/operations-registry.js.map +1 -1
  47. package/dist/docs/read.d.ts +2 -2
  48. package/dist/index.d.ts +25 -11
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +17 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/lease-ipc/__tests__/freeze.test.d.ts +15 -0
  53. package/dist/lease-ipc/__tests__/freeze.test.d.ts.map +1 -0
  54. package/dist/lease-ipc/__tests__/freeze.test.js +313 -0
  55. package/dist/lease-ipc/__tests__/freeze.test.js.map +1 -0
  56. package/dist/lease-ipc/index.d.ts +17 -0
  57. package/dist/lease-ipc/index.d.ts.map +1 -0
  58. package/dist/lease-ipc/index.js +16 -0
  59. package/dist/lease-ipc/index.js.map +1 -0
  60. package/dist/lease-ipc/messages.d.ts +918 -0
  61. package/dist/lease-ipc/messages.d.ts.map +1 -0
  62. package/dist/lease-ipc/messages.js +476 -0
  63. package/dist/lease-ipc/messages.js.map +1 -0
  64. package/dist/lease-ipc/version.d.ts +41 -0
  65. package/dist/lease-ipc/version.d.ts.map +1 -0
  66. package/dist/lease-ipc/version.js +43 -0
  67. package/dist/lease-ipc/version.js.map +1 -0
  68. package/dist/llm/catalog-schema.d.ts +242 -0
  69. package/dist/llm/catalog-schema.d.ts.map +1 -0
  70. package/dist/llm/catalog-schema.js +153 -0
  71. package/dist/llm/catalog-schema.js.map +1 -0
  72. package/dist/llm/catalog-schema.json +139 -0
  73. package/dist/llm/index.d.ts +15 -0
  74. package/dist/llm/index.d.ts.map +1 -0
  75. package/dist/llm/index.js +14 -0
  76. package/dist/llm/index.js.map +1 -0
  77. package/dist/llm/interfaces.d.ts +23 -0
  78. package/dist/llm/interfaces.d.ts.map +1 -1
  79. package/dist/llm/oauth.d.ts +16 -2
  80. package/dist/llm/oauth.d.ts.map +1 -1
  81. package/dist/llm/onboarding.d.ts +126 -0
  82. package/dist/llm/onboarding.d.ts.map +1 -0
  83. package/dist/llm/onboarding.js +34 -0
  84. package/dist/llm/onboarding.js.map +1 -0
  85. package/dist/llm/provider-def.d.ts +254 -0
  86. package/dist/llm/provider-def.d.ts.map +1 -0
  87. package/dist/llm/provider-def.js +93 -0
  88. package/dist/llm/provider-def.js.map +1 -0
  89. package/dist/llm/sealed-credential.d.ts +164 -0
  90. package/dist/llm/sealed-credential.d.ts.map +1 -0
  91. package/dist/llm/sealed-credential.js +43 -0
  92. package/dist/llm/sealed-credential.js.map +1 -0
  93. package/dist/llm/system-of-use.d.ts +383 -0
  94. package/dist/llm/system-of-use.d.ts.map +1 -0
  95. package/dist/llm/system-of-use.js +238 -0
  96. package/dist/llm/system-of-use.js.map +1 -0
  97. package/dist/llm/system-resolver.d.ts +87 -11
  98. package/dist/llm/system-resolver.d.ts.map +1 -1
  99. package/dist/llm/system-resolver.js +14 -7
  100. package/dist/llm/system-resolver.js.map +1 -1
  101. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts +17 -0
  102. package/dist/operations/__tests__/ensures-schema-registry.test.d.ts.map +1 -0
  103. package/dist/operations/__tests__/ensures-schema-registry.test.js +113 -0
  104. package/dist/operations/__tests__/ensures-schema-registry.test.js.map +1 -0
  105. package/dist/operations/admin.d.ts +124 -1
  106. package/dist/operations/admin.d.ts.map +1 -1
  107. package/dist/operations/ensures-schema-registry.d.ts +172 -0
  108. package/dist/operations/ensures-schema-registry.d.ts.map +1 -0
  109. package/dist/operations/ensures-schema-registry.js +226 -0
  110. package/dist/operations/ensures-schema-registry.js.map +1 -0
  111. package/dist/operations/entities.d.ts +110 -0
  112. package/dist/operations/entities.d.ts.map +1 -0
  113. package/dist/operations/entities.js +695 -0
  114. package/dist/operations/entities.js.map +1 -0
  115. package/dist/operations/llm.d.ts +116 -9
  116. package/dist/operations/llm.d.ts.map +1 -1
  117. package/dist/operations/output-contracts-data.d.ts.map +1 -1
  118. package/dist/operations/output-contracts-data.js +230 -0
  119. package/dist/operations/output-contracts-data.js.map +1 -1
  120. package/dist/operations/service.d.ts +68 -0
  121. package/dist/operations/service.d.ts.map +1 -0
  122. package/dist/operations/service.js +336 -0
  123. package/dist/operations/service.js.map +1 -0
  124. package/dist/operations/tasks.d.ts +109 -0
  125. package/dist/operations/tasks.d.ts.map +1 -1
  126. package/dist/operations/tasks.js.map +1 -1
  127. package/dist/operations/validate.d.ts +1 -12
  128. package/dist/operations/validate.d.ts.map +1 -1
  129. package/dist/operations/validate.js +1 -1
  130. package/dist/playbook.d.ts +116 -0
  131. package/dist/playbook.d.ts.map +1 -1
  132. package/dist/release/plan.d.ts +5 -5
  133. package/dist/session.d.ts +11 -0
  134. package/dist/session.d.ts.map +1 -1
  135. package/dist/session.js.map +1 -1
  136. package/dist/templates/manifest.d.ts +1 -1
  137. package/dist/tools/agent-tools.d.ts +231 -0
  138. package/dist/tools/agent-tools.d.ts.map +1 -0
  139. package/dist/tools/agent-tools.js +25 -0
  140. package/dist/tools/agent-tools.js.map +1 -0
  141. package/dist/tools/atomic.d.ts +19 -0
  142. package/dist/tools/atomic.d.ts.map +1 -1
  143. package/dist/tools/atomic.js +17 -0
  144. package/dist/tools/atomic.js.map +1 -1
  145. package/dist/tools/index.d.ts +2 -0
  146. package/dist/tools/index.d.ts.map +1 -1
  147. package/dist/tools/skill-executor.d.ts +7 -0
  148. package/dist/tools/skill-executor.d.ts.map +1 -1
  149. package/dist/tools/web-tools.d.ts +217 -0
  150. package/dist/tools/web-tools.d.ts.map +1 -0
  151. package/dist/tools/web-tools.js +31 -0
  152. package/dist/tools/web-tools.js.map +1 -0
  153. package/dist/vault/service-provider.d.ts +343 -0
  154. package/dist/vault/service-provider.d.ts.map +1 -0
  155. package/dist/vault/service-provider.js +857 -0
  156. package/dist/vault/service-provider.js.map +1 -0
  157. package/dist/workgraph.d.ts +12 -12
  158. package/package.json +14 -2
  159. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  160. package/src/__tests__/operation-def.test.ts +7 -1
  161. package/src/__tests__/provider-def.test.ts +93 -0
  162. package/src/__tests__/sealed-credential.test.ts +100 -0
  163. package/src/__tests__/service-provider.test.ts +129 -0
  164. package/src/__tests__/skill-executor-contract.test.ts +3 -0
  165. package/src/acceptance-gate.ts +2 -3
  166. package/src/boundary.ts +70 -20
  167. package/src/conduit.ts +200 -0
  168. package/src/config/index.ts +35 -0
  169. package/src/config.ts +106 -1
  170. package/src/data-accessor.ts +22 -1
  171. package/src/dispatch/__tests__/tasks-gateway-crud-ops.test.ts +99 -0
  172. package/src/dispatch/identity.ts +25 -0
  173. package/src/dispatch/operation-def.ts +21 -0
  174. package/src/dispatch/operations-registry.ts +1083 -176
  175. package/src/index.ts +240 -25
  176. package/src/lease-ipc/__tests__/freeze.test.ts +342 -0
  177. package/src/lease-ipc/index.ts +75 -0
  178. package/src/lease-ipc/messages.ts +588 -0
  179. package/src/lease-ipc/version.ts +46 -0
  180. package/src/llm/catalog-schema.json +139 -0
  181. package/src/llm/catalog-schema.ts +184 -0
  182. package/src/llm/index.ts +29 -0
  183. package/src/llm/interfaces.ts +23 -0
  184. package/src/llm/oauth.ts +16 -2
  185. package/src/llm/onboarding.ts +131 -0
  186. package/src/llm/provider-def.ts +291 -0
  187. package/src/llm/sealed-credential.ts +166 -0
  188. package/src/llm/system-of-use.ts +503 -0
  189. package/src/llm/system-resolver.ts +91 -11
  190. package/src/operations/__tests__/ensures-schema-registry.test.ts +169 -0
  191. package/src/operations/admin.ts +144 -0
  192. package/src/operations/ensures-schema-registry.ts +260 -0
  193. package/src/operations/entities.ts +758 -0
  194. package/src/operations/llm.ts +120 -8
  195. package/src/operations/output-contracts-data.ts +264 -0
  196. package/src/operations/service.ts +360 -0
  197. package/src/operations/tasks.ts +119 -0
  198. package/src/operations/validate.ts +1 -14
  199. package/src/playbook.ts +117 -0
  200. package/src/session.ts +11 -0
  201. package/src/tools/agent-tools.ts +270 -0
  202. package/src/tools/atomic.ts +21 -0
  203. package/src/tools/index.ts +46 -0
  204. package/src/tools/skill-executor.ts +7 -0
  205. package/src/tools/web-tools.ts +247 -0
  206. package/src/vault/service-provider.ts +1137 -0
  207. package/dist/tessera.d.ts +0 -32
  208. package/dist/tessera.d.ts.map +0 -1
  209. package/dist/tessera.js +0 -11
  210. package/dist/tessera.js.map +0 -1
  211. package/dist/warp-chain.d.ts +0 -129
  212. package/dist/warp-chain.d.ts.map +0 -1
  213. package/dist/warp-chain.js +0 -13
  214. package/dist/warp-chain.js.map +0 -1
  215. package/src/tessera.ts +0 -35
  216. package/src/warp-chain.ts +0 -185
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Declarative `ProviderDef` contract — the DATA shape of one LLM provider.
3
+ *
4
+ * M3 Provider SSoT (epic T11667 · task T11702). `ProviderDef` is the DECLARATIVE,
5
+ * serializable description of a provider: its identity, aliases, auth methods, wire
6
+ * endpoint (a discriminated union keyed on the `transport` literal), models.dev
7
+ * catalog key, default headers, optional OAuth flow, and a single request-quirk hook
8
+ * signature. It is the contract the `providers` global-DB table (T11703) persists and
9
+ * the alias resolver (T11704) reads.
10
+ *
11
+ * ## ProviderDef vs {@link ProviderProfile} — declarative DATA vs runtime BEHAVIOUR
12
+ *
13
+ * The pre-existing {@link ProviderProfile} (T9262 · T11756) is the RUNTIME provider
14
+ * shape carried in the in-process registry: it bundles live hook FUNCTIONS
15
+ * (`prepareMessages`, `buildExtraBody`, `buildApiKwargsExtras`, `fetchModels`) that
16
+ * cannot be serialized into a DB row. `ProviderDef` is the DECLARATIVE projection of
17
+ * the same provider — only serializable DATA — so it can be a `providers` table row,
18
+ * shipped as a seed, diffed, and resolved without importing provider-specific code.
19
+ * The builtin `ProviderProfile` set is the SEED SOURCE for the builtin `ProviderDef`
20
+ * set (the core mapper derives one from the other); the non-serializable hooks stay
21
+ * on `ProviderProfile`, reduced here to the single declarative {@link RequestQuirk}
22
+ * descriptor that records WHICH quirk a provider needs without embedding a closure.
23
+ *
24
+ * ## Endpoint is a discriminated union (AC5)
25
+ *
26
+ * {@link ProviderEndpoint} is a closed discriminated union keyed on the literal
27
+ * `transport` field — one variant per wire protocol CLEO knows how to speak. No
28
+ * `any`/`unknown` shortcut: every variant carries its own typed fields. This is the
29
+ * declarative analog of {@link ApiMode}; the core transport adapter table (T11767)
30
+ * maps a `transport` value to its `Transport` constructor.
31
+ *
32
+ * ## Contracts purity (Gate 10)
33
+ *
34
+ * This module is TYPES ONLY — interfaces, type aliases, and `as const` literal arrays.
35
+ * It exports NO bodied runtime helper. The builtin-set construction and the seed/upsert
36
+ * logic live in `@cleocode/core` (T11703 · T11704), never here.
37
+ *
38
+ * @module llm/provider-def
39
+ * @task T11702
40
+ * @epic T11667
41
+ * @see ./provider-profile.ts — the RUNTIME provider shape (hooks) this projects from
42
+ * @see ./provider-id.ts — {@link ApiMode} / {@link BuiltinProviderId} the transport mirrors
43
+ * @see ./oauth.ts — {@link ProviderOAuthConfig} the OAuth flow placeholder reuses
44
+ * @see ../../../core/src/store/schema/cleo-global/providers.ts — the persisted table (T11703)
45
+ */
46
+
47
+ import type { StoredAuthTypeWire } from '../operations/llm.js';
48
+ import type { ProviderOAuthConfig } from './oauth.js';
49
+ import type { ProviderId } from './provider-id.js';
50
+
51
+ /**
52
+ * Wire-transport discriminant for a {@link ProviderEndpoint} variant.
53
+ *
54
+ * Closed union — one literal per protocol CLEO has a transport for. The
55
+ * declarative analog of {@link import('./provider-id.js').ApiMode}:
56
+ *
57
+ * - `openai-completions` — OpenAI `/chat/completions` shape (OpenAI, OpenRouter,
58
+ * Moonshot, Kimi, xAI, Gemini-via-OpenAI-compat) — `ApiMode 'chat_completions'`.
59
+ * - `openai-responses` — OpenAI Responses API (Codex backend, xAI-responses) —
60
+ * `ApiMode 'codex_responses'`.
61
+ * - `anthropic-messages` — native Anthropic Messages API (prompt-cache + thinking) —
62
+ * `ApiMode 'anthropic_messages'`.
63
+ * - `aisdk` — routed through the Vercel AI-SDK provider factory (the data-driven
64
+ * `apiMode → provider` adapter, T11767) rather than a bespoke CLEO transport.
65
+ *
66
+ * @task T11702
67
+ */
68
+ export const PROVIDER_TRANSPORTS = [
69
+ 'openai-completions',
70
+ 'openai-responses',
71
+ 'anthropic-messages',
72
+ 'aisdk',
73
+ ] as const;
74
+
75
+ /** TypeScript union derived from {@link PROVIDER_TRANSPORTS}. */
76
+ export type ProviderTransport = (typeof PROVIDER_TRANSPORTS)[number];
77
+
78
+ /**
79
+ * Endpoint for the `openai-completions` transport — the OpenAI
80
+ * `/chat/completions` wire shape (OpenAI, OpenRouter, Moonshot, Kimi, xAI,
81
+ * Gemini-via-compat). `baseUrl` has NO trailing slash.
82
+ *
83
+ * @task T11702
84
+ */
85
+ export interface OpenAICompletionsEndpoint {
86
+ /** Discriminant — the OpenAI chat-completions wire protocol. */
87
+ readonly transport: 'openai-completions';
88
+ /** Base URL for the provider's API — no trailing slash. */
89
+ readonly baseUrl: string;
90
+ }
91
+
92
+ /**
93
+ * Endpoint for the `openai-responses` transport — the OpenAI Responses API
94
+ * (Codex backend, xAI-responses). `baseUrl` has NO trailing slash.
95
+ *
96
+ * @task T11702
97
+ */
98
+ export interface OpenAIResponsesEndpoint {
99
+ /** Discriminant — the OpenAI Responses wire protocol. */
100
+ readonly transport: 'openai-responses';
101
+ /** Base URL for the Responses endpoint — no trailing slash. */
102
+ readonly baseUrl: string;
103
+ }
104
+
105
+ /**
106
+ * Endpoint for the `anthropic-messages` transport — the native Anthropic
107
+ * Messages API (full prompt-cache + extended thinking). `baseUrl` has NO
108
+ * trailing slash.
109
+ *
110
+ * @task T11702
111
+ */
112
+ export interface AnthropicMessagesEndpoint {
113
+ /** Discriminant — the native Anthropic Messages wire protocol. */
114
+ readonly transport: 'anthropic-messages';
115
+ /** Base URL for the Anthropic API — no trailing slash. */
116
+ readonly baseUrl: string;
117
+ }
118
+
119
+ /**
120
+ * Endpoint for the `aisdk` transport — the provider is reached through the
121
+ * Vercel AI-SDK provider factory (the data-driven adapter table, T11767) keyed
122
+ * by `aiSdkProvider`, rather than a bespoke CLEO transport.
123
+ *
124
+ * @task T11702
125
+ */
126
+ export interface AiSdkEndpoint {
127
+ /** Discriminant — routed through the AI-SDK provider factory. */
128
+ readonly transport: 'aisdk';
129
+ /** Base URL passed to the AI-SDK provider factory — no trailing slash. */
130
+ readonly baseUrl: string;
131
+ /**
132
+ * The AI-SDK provider-factory key (e.g. `openai-compatible` | `google` |
133
+ * `bedrock`) the adapter table maps to a `create*` factory. NEVER an inline
134
+ * client construction — the factory call is the LLM chokepoint (T11783).
135
+ */
136
+ readonly aiSdkProvider: string;
137
+ }
138
+
139
+ /**
140
+ * A provider's wire endpoint — a discriminated union keyed on the `transport`
141
+ * literal (AC5). Exactly one variant per {@link ProviderTransport}; each carries
142
+ * its own typed fields, with no `any`/`unknown` shortcut.
143
+ *
144
+ * @task T11702
145
+ */
146
+ export type ProviderEndpoint =
147
+ | OpenAICompletionsEndpoint
148
+ | OpenAIResponsesEndpoint
149
+ | AnthropicMessagesEndpoint
150
+ | AiSdkEndpoint;
151
+
152
+ /**
153
+ * Known request-quirk kinds — the DECLARATIVE record of WHICH provider-specific
154
+ * request adjustment a provider needs, WITHOUT embedding the closure.
155
+ *
156
+ * The live closures stay on {@link ProviderProfile} (`buildExtraBody` /
157
+ * `buildApiKwargsExtras` / `prepareMessages`); a `ProviderDef` row records only
158
+ * the quirk's stable kind so the table/seed stays serializable. The runtime maps
159
+ * a {@link RequestQuirk} `kind` back to its hook implementation.
160
+ *
161
+ * - `grok-conv-id` — inject the process-scoped `x-grok-conv-id` header (xAI).
162
+ * - `gemini-thinking-config` — inject `thinking_config` in `extra_body` (Gemini).
163
+ * - `kimi-reasoning-effort` — top-level `reasoning_effort` kwarg (Kimi Code).
164
+ * - `moonshot-schema-sanitize` — shallow tool-schema sanitization (Moonshot).
165
+ * - `openrouter-pareto` — inject the Pareto price-router plugin (OpenRouter).
166
+ *
167
+ * @task T11702
168
+ */
169
+ export const REQUEST_QUIRK_KINDS = [
170
+ 'grok-conv-id',
171
+ 'gemini-thinking-config',
172
+ 'kimi-reasoning-effort',
173
+ 'moonshot-schema-sanitize',
174
+ 'openrouter-pareto',
175
+ ] as const;
176
+
177
+ /** TypeScript union derived from {@link REQUEST_QUIRK_KINDS}. */
178
+ export type RequestQuirkKind = (typeof REQUEST_QUIRK_KINDS)[number];
179
+
180
+ /**
181
+ * A single declarative request-quirk descriptor (AC4 — the "request-quirk hook
182
+ * signature"). Records the quirk's stable {@link RequestQuirkKind} so a
183
+ * `ProviderDef` row stays serializable; the runtime resolves the `kind` to its
184
+ * hook implementation on {@link ProviderProfile}. The signature the hook MUST
185
+ * satisfy is `(model: string) => Record<string, unknown>` (the declarative shape
186
+ * of `buildExtraBody` / `buildApiKwargsExtras`), captured here only as the
187
+ * documented contract — never as an embedded closure.
188
+ *
189
+ * @task T11702
190
+ */
191
+ export interface RequestQuirk {
192
+ /** The stable quirk kind this provider needs. */
193
+ readonly kind: RequestQuirkKind;
194
+ }
195
+
196
+ /**
197
+ * OAuth flow placeholder embedded on a {@link ProviderDef} (AC4).
198
+ *
199
+ * For M3 this is the existing {@link ProviderOAuthConfig} shape (PKCE / device-code
200
+ * endpoints + client id), aliased so the M4 vault work (T10409) can widen it without
201
+ * a contract rename. A `ProviderDef` carries `oauth` only when the provider supports
202
+ * an OAuth login flow (anthropic, openai, kimi-code); it is omitted otherwise.
203
+ *
204
+ * @task T11702
205
+ */
206
+ export type OAuthFlowDef = ProviderOAuthConfig;
207
+
208
+ /**
209
+ * Declarative description of one LLM provider — the serializable DATA SSoT.
210
+ *
211
+ * The contract persisted as a `providers` table row (T11703) and read by the alias
212
+ * resolver (T11704). Derived from the runtime {@link ProviderProfile} set: identity
213
+ * + aliases + auth + endpoint + catalog key + headers + optional OAuth + the
214
+ * declarative request quirks. Carries NO closures — only serializable DATA.
215
+ *
216
+ * @task T11702
217
+ */
218
+ export interface ProviderDef {
219
+ /**
220
+ * Canonical provider id (lower-cased). For builtins this is a
221
+ * {@link BuiltinProviderId}; plugin providers may use any non-empty string.
222
+ */
223
+ readonly id: ProviderId;
224
+
225
+ /** Human-readable display name shown in pickers and diagnostic output. */
226
+ readonly displayName: string;
227
+
228
+ /**
229
+ * Alternate names that resolve to this provider (case-insensitive). The SINGLE
230
+ * source the alias resolver (T11704) reads — `codex`/`chatgpt` → `openai`,
231
+ * `claude` → `anthropic`, `google` → `gemini`, … An alias colliding with another
232
+ * provider's primary `id` is a resolution error (T11704 AC2).
233
+ */
234
+ readonly aliases: ReadonlyArray<string>;
235
+
236
+ /**
237
+ * Authentication mechanisms this provider supports (`api_key` | `oauth` |
238
+ * `aws_sdk`). The {@link StoredAuthTypeWire} wire enum.
239
+ */
240
+ readonly authMethods: ReadonlyArray<StoredAuthTypeWire>;
241
+
242
+ /**
243
+ * The provider's wire endpoint — a discriminated union keyed on `transport`
244
+ * (AC5). A provider that speaks multiple protocols (e.g. xAI completions +
245
+ * responses) carries the multiple variants in {@link altEndpoints}.
246
+ */
247
+ readonly endpoint: ProviderEndpoint;
248
+
249
+ /**
250
+ * Additional wire endpoints this provider also speaks, beyond the primary
251
+ * {@link endpoint}. Collapses the prior per-ApiMode profile duplication into ONE
252
+ * row (T11703 AC4 — xAI's completions + responses become one `ProviderDef`).
253
+ * Empty/omitted for single-protocol providers.
254
+ */
255
+ readonly altEndpoints?: ReadonlyArray<ProviderEndpoint>;
256
+
257
+ /**
258
+ * The models.dev catalog provider key joining this provider to the
259
+ * `models_catalog` table (T11733). Usually equals {@link id}; carried distinctly
260
+ * because a provider id and its catalog key can differ (e.g. `kimi-code` →
261
+ * `moonshot`).
262
+ */
263
+ readonly modelsDevId: string;
264
+
265
+ /**
266
+ * HTTP headers sent with every request to this provider, merged into the
267
+ * client's `defaultHeaders` (e.g. `{ 'anthropic-version': '2023-06-01' }`).
268
+ * Omitted when the provider needs no pinned headers.
269
+ */
270
+ readonly defaultHeaders?: Readonly<Record<string, string>>;
271
+
272
+ /**
273
+ * Environment variable names that may supply this provider's API key (e.g.
274
+ * `['ANTHROPIC_API_KEY']`). Resolver chains may check these before the vault.
275
+ * Omitted when the provider has no env-var credential path.
276
+ */
277
+ readonly envVars?: ReadonlyArray<string>;
278
+
279
+ /**
280
+ * OAuth flow placeholder (AC4) — present only when the provider supports an
281
+ * OAuth login flow (anthropic, openai, kimi-code). See {@link OAuthFlowDef}.
282
+ */
283
+ readonly oauth?: OAuthFlowDef;
284
+
285
+ /**
286
+ * Declarative request quirks this provider needs (AC4) — the stable
287
+ * {@link RequestQuirk} descriptors WITHOUT the closures (those stay on
288
+ * {@link ProviderProfile}). Empty/omitted when the provider needs no quirk.
289
+ */
290
+ readonly requestQuirks?: ReadonlyArray<RequestQuirk>;
291
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Sealed credential handle — the E10 on-demand-decrypt contract (T11752).
3
+ *
4
+ * ## Why a sealed handle exists
5
+ *
6
+ * Until E10, a resolved credential carried the secret **inline** as a bare
7
+ * `apiKey: string` field on `CredentialResultWire` / `ResolvedLLM`. That
8
+ * plaintext token traveled up the entire resolution call stack — through
9
+ * `resolveLLMForRole` → `resolveLLMForSystem` → consumers — where it could be
10
+ * accidentally logged, serialized into a LAFS envelope, or copied into a
11
+ * diagnostic. Every layer that merely *routed* the credential to the wire still
12
+ * had structural read access to the plaintext.
13
+ *
14
+ * A {@link SealedCredential} inverts that. The resolver returns an **opaque
15
+ * handle** that names the credential (`provider` + `account`) and exposes a
16
+ * single asynchronous {@link SealedCredential.fetch} method. The plaintext token
17
+ * is materialized ONLY when `fetch()` is invoked — and by E10 design that call
18
+ * happens at exactly two trust boundaries:
19
+ *
20
+ * 1. the wire — `transportForProvider` / `session-factory.ts` immediately
21
+ * before building the provider transport / auth headers, or
22
+ * 2. daemon worker-injection — the daemon brokering a short-lived credential
23
+ * into a spawned agent.
24
+ *
25
+ * Between the resolver and those boundaries, NO layer holds a plaintext secret;
26
+ * it holds a handle whose `fetch()` is the sole decryption chokepoint.
27
+ *
28
+ * ## Types-only (Gate 10 contracts purity)
29
+ *
30
+ * This module is **types-only**. {@link SealedCredential.fetch} is a
31
+ * *function-typed field* on an interface — a declaration of shape, NOT a bodied
32
+ * runtime function — so it satisfies the contracts-purity gate
33
+ * (`lint-no-runtime-in-contracts`). The concrete implementation of `fetch()`
34
+ * (the actual `crypto/credentials.ts` decrypt + the vault/daemon round-trip)
35
+ * lives in `@cleocode/core` — never in contracts.
36
+ *
37
+ * @module llm/sealed-credential
38
+ * @task T11752
39
+ * @epic T11746
40
+ * @see ADR-072 §Type lock-in
41
+ */
42
+
43
+ import type { ProviderId } from './provider-id.js';
44
+
45
+ /**
46
+ * The plaintext secret materialized by {@link SealedCredential.fetch}.
47
+ *
48
+ * ## Branded so it cannot be silently created or widened
49
+ *
50
+ * `DecryptedToken` is a *branded* string: an opaque nominal type produced ONLY
51
+ * by the core decrypt chokepoint inside {@link SealedCredential.fetch}. The
52
+ * `readonly __decryptedToken` brand is a phantom marker — it is NEVER assigned a
53
+ * runtime value (there is no runtime field; this is purely a compile-time
54
+ * nominal tag), so a plain `string` cannot be passed where a `DecryptedToken`
55
+ * is expected, and a `DecryptedToken` is not assignable to an arbitrary
56
+ * `string` sink without an explicit, auditable read of `.value`.
57
+ *
58
+ * ## NEVER serialized
59
+ *
60
+ * A `DecryptedToken` MUST NEVER be:
61
+ * - returned up the resolver call stack (it exists only transiently inside
62
+ * the `fetch()` consumer at the wire / daemon worker-injection boundary),
63
+ * - placed on any envelope, `NormalizedResponse.providerData`, log line,
64
+ * diagnostic, or `cleo llm whoami` output,
65
+ * - persisted, cached across calls, or held beyond the single transport
66
+ * construction / auth-header build that consumed it.
67
+ *
68
+ * The CI chokepoint lint (`lint-llm-chokepoint`) plus the E10 redaction tests
69
+ * (T11754) enforce that no bare secret string crosses the resolver boundary.
70
+ *
71
+ * Because the brand is phantom (compile-time only) and there is no enumerable
72
+ * runtime field, `JSON.stringify(token)` over the value field yields only the
73
+ * underlying string — which is precisely why callers MUST treat the value as
74
+ * write-once-to-the-wire and never route it into a serializer.
75
+ *
76
+ * @task T11752
77
+ */
78
+ export type DecryptedToken = {
79
+ /**
80
+ * Phantom brand marker — compile-time only, never materialized at runtime.
81
+ * Prevents a plain `string` from being assigned where a {@link DecryptedToken}
82
+ * is required (nominal typing). Reading this field is meaningless at runtime;
83
+ * it exists solely to make the type opaque.
84
+ *
85
+ * @internal
86
+ */
87
+ readonly __decryptedToken: 'DecryptedToken';
88
+ /**
89
+ * The materialized plaintext secret (API key or OAuth bearer token).
90
+ *
91
+ * SECURITY: read this ONLY at the wire (`transportForProvider`) or daemon
92
+ * worker-injection, build the auth header, and discard. NEVER log, serialize,
93
+ * or return it up the stack. Marked `readonly` so a consumer cannot mutate it
94
+ * in place.
95
+ */
96
+ readonly value: string;
97
+ };
98
+
99
+ /**
100
+ * An opaque, sealed reference to a credential whose plaintext is fetched on
101
+ * demand at the wire — never carried inline up the resolver stack.
102
+ *
103
+ * The resolver returns this *instead of* a bare `apiKey: string` (E10 ·
104
+ * T11753). Consumers route the handle to the transport boundary unchanged;
105
+ * only the boundary calls {@link fetch} to materialize the
106
+ * {@link DecryptedToken}, build the provider auth headers, and discard the
107
+ * plaintext.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * // At the wire boundary (session-factory.ts) — the ONLY place fetch() runs.
112
+ * // Prefer `authHeadersFromSealed` (core), which invokes fetch() internally so
113
+ * // the plaintext is never bound to a caller-visible variable:
114
+ * const headers = await authHeadersFromSealed(sealed, authType);
115
+ * // …or, for diagnostics, name the credential WITHOUT materializing it:
116
+ * log.info({ provider: sealed.provider, preview: sealed.tokenPreview });
117
+ * // The full token is NEVER returned, logged, or serialized.
118
+ * ```
119
+ *
120
+ * @task T11752
121
+ */
122
+ export interface SealedCredential {
123
+ /** Provider this credential targets (e.g. `'anthropic'`, `'openai'`). */
124
+ readonly provider: ProviderId;
125
+ /**
126
+ * Account / store-label identifier this handle resolves against (e.g.
127
+ * `'default'`, `'work'`). Names *which* credential the handle points at
128
+ * without revealing its secret — safe to log and serialize.
129
+ */
130
+ readonly account: string;
131
+ /**
132
+ * Non-secret, redacted preview of the underlying credential (e.g. `'…6789'`
133
+ * or `'oat-…6789'` for OAuth). The SOLE token-derived value safe to place on
134
+ * a log line, LAFS envelope, or `cleo llm whoami` diagnostic.
135
+ *
136
+ * SECURITY: this is computed ONCE at seal time from at most the last 4 token
137
+ * characters via the core redaction chokepoint (`tokenPreviewOf`) — it can
138
+ * never be reversed into the plaintext. Diagnostics that need to *name* a
139
+ * credential without materializing it MUST read this field, never call
140
+ * {@link fetch}. The E10 redaction test (T11754 · AC3) asserts that the
141
+ * preview, not the full token, is the only credential-derived string that
142
+ * crosses any logging/diagnostic boundary.
143
+ *
144
+ * @task T11754
145
+ */
146
+ readonly tokenPreview: string;
147
+ /**
148
+ * Materialize the plaintext secret on demand.
149
+ *
150
+ * SECURITY INVARIANT: the returned {@link DecryptedToken} is the ONLY point
151
+ * at which the secret exists in plaintext. Implementations (in
152
+ * `@cleocode/core`) run the `crypto/credentials.ts` decrypt INSIDE this
153
+ * method and MUST be invoked ONLY at the wire (`transportForProvider` /
154
+ * `session-factory.ts`) or daemon worker-injection — never to surface a key
155
+ * up the resolution stack. The result MUST NOT be cached, persisted, logged,
156
+ * or serialized by any caller.
157
+ *
158
+ * This is a *function-typed field on an interface* (a type, not a bodied
159
+ * function), so the declaration is contracts-purity-safe (Gate 10): the body
160
+ * lives in core.
161
+ *
162
+ * @returns The materialized plaintext token, valid only for immediate
163
+ * wire/daemon use.
164
+ */
165
+ readonly fetch: () => Promise<DecryptedToken>;
166
+ }