@cat-factory/binary-generators 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Savin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # @cat-factory/binary-generators
2
+
3
+ The **generative binary integrations** the platform ships, plus the seam a deployment writes its
4
+ own with. These are the metered vendor APIs a [binary-output step](https://github.com/kibertoad/cat-factory/blob/main/docs/initiatives/binary-output-foundational-storage.md)
5
+ calls to _produce_ its artifacts, as opposed to the foundational service the artifacts are then
6
+ _stored_ through.
7
+
8
+ | id | What it makes | Modalities |
9
+ | ------------- | ---------------------------------------------------------------------- | ----------------------------------- |
10
+ | `nano-banana` | Images with legible text in them, and conversational editing, up to 4K | `image` (`image/png`, `image/jpeg`) |
11
+
12
+ ## Why the platform ships one at all
13
+
14
+ `defaultBinaryGeneratorRegistry()` is still empty, and the reasoning that kept it empty still
15
+ holds: no image API is one every organisation runs, and every one of them is metered. What changed
16
+ is the Media task type. It ships a generating agent kind, a preset and the storage underneath, and
17
+ its step selected nothing, so the platform's most demonstrable capability was reachable only by a
18
+ deployment that first wrote an integration, an OpenAPI document and a credential declaration.
19
+
20
+ Metered is answered by the credential rather than by the registry. With no key resolved the agent
21
+ is told the integration is unavailable and reports it as the reason an artifact is missing, so a
22
+ deployment that ignores this package pays nothing and sees one extra row in a picker.
23
+
24
+ ## Using it
25
+
26
+ Every facade defaults its registry to the shipped set, so nothing is needed to get `nano-banana`:
27
+
28
+ ```ts
29
+ await start({/* … */}) // binaryGeneratorRegistry defaults to binaryGeneratorRegistryWithBuiltins()
30
+ ```
31
+
32
+ An **injected** registry REPLACES the default rather than merging with it, so a deployment adding
33
+ its own integrations starts from the built-ins:
34
+
35
+ ```ts
36
+ import { binaryGeneratorRegistryWithBuiltins } from '@cat-factory/node-server'
37
+
38
+ const binaryGeneratorRegistry = binaryGeneratorRegistryWithBuiltins()
39
+ binaryGeneratorRegistry.registerAll(myIntegrations)
40
+ await start({ binaryGeneratorRegistry })
41
+ ```
42
+
43
+ That matters more here than for the gate or prompt-fragment registries, because the shipped
44
+ `pl_media` preset SELECTS `nano-banana` by id: a registry without it refuses that pipeline's runs
45
+ at admission (`binary_output_generator_invalid`) rather than degrading quietly. Dropping the
46
+ shipped integration is a legitimate choice; editing that preset's step is the other half of it.
47
+
48
+ Register **once per deployment**, on the process that owns the registry. In mothership mode that is
49
+ the mothership: a node resolves integrations over `/internal/binary-generators` and consults no
50
+ registry of its own, so the set the pipeline builder offers and the set admission resolves are one
51
+ set however far behind the node's build has drifted.
52
+
53
+ On the **Worker** the registry is also held process-wide
54
+ (`infrastructure/binaryGenerators.ts`), the same way the deployment's binary artifact stores are
55
+ and for the same reason: that runtime builds a container per entry point, and a binary-output
56
+ step's dispatch brief is composed on the durable path, which takes no options at all.
57
+ `createWorker({ overrides: { binaryGeneratorRegistry } })` registers on your behalf, so a
58
+ deployment using the documented seam needs to know none of that. What made this one easy to miss
59
+ is that an override-less build resolves the SHIPPED set rather than nothing, so an unregistered
60
+ integration is absent from a brief that otherwise looks populated.
61
+
62
+ ## The credential
63
+
64
+ `nano-banana` authenticates with `GEMINI_API_KEY`, an API key from
65
+ [Google AI Studio](https://aistudio.google.com/apikey), sent as the `x-goog-api-key` header. The
66
+ name is the vendor's own, so an agent reaching for `google-genai` finds the value where the library
67
+ looks.
68
+
69
+ Set it either as a workspace **capability credential** (subject `binary-generator`, id
70
+ `nano-banana`, key `GEMINI_API_KEY`) or in the environment the deployment dispatches runs from. The
71
+ value travels on the job body alone and is injected into the agent's process for that job; it is
72
+ never written to the checkout, the prompt or a log.
73
+
74
+ Do **not** add it to `docs/environment-variables.md`: every variable documented there is reserved
75
+ by `check-reserved-env-keys.mjs`, which would make this key unresolvable.
76
+
77
+ Billing must be enabled on the Google project. None of these models has a free tier, so an
78
+ unbilled key gets `FAILED_PRECONDITION` on the first call, which the agent reports as the reason
79
+ nothing was generated.
80
+
81
+ ## Writing your own
82
+
83
+ `defineBinaryGenerator` is exported for exactly this, and re-exported from each facade so a
84
+ deployment reaches it from the package it already depends on. It runs the platform's OWN
85
+ registration rules at import (the definition schema, plus `binaryGeneratorDetailIssues`: the
86
+ endpoint policy, the contract-set rules, the media-type classifier, the harness check and the
87
+ capability-versus-accepted-values pairing), so a definition that would fail a boot fails a test
88
+ instead:
89
+
90
+ ```ts
91
+ import { defineBinaryGenerator, openApiContract } from '@cat-factory/binary-generators'
92
+
93
+ export const acmeImages = defineBinaryGenerator({
94
+ id: 'acme-images',
95
+ name: 'Acme Images',
96
+ summary: 'One line the picker and the agent brief show.',
97
+ description: 'What it is good at, what it is NOT for, and its cost profile.',
98
+ modalities: ['image'],
99
+ mediaTypes: ['image/png'],
100
+ capabilities: ['seed', 'exact-size'],
101
+ endpoint: 'https://api.acme.example',
102
+ credentials: [{ key: 'ACME_IMAGE_API_KEY', usage: 'Authorization: Bearer <value>' }],
103
+ contracts: [
104
+ openApiContract({ contractId: 'acme-api', title: 'Acme API', document: ACME_OPENAPI }),
105
+ ],
106
+ guidance: 'Operating notes folded into the agent’s brief verbatim.',
107
+ })
108
+ ```
109
+
110
+ Two rules the seam cannot check for you, both pinned per integration in the tests here:
111
+
112
+ - **A capability is a claim about the endpoint's REQUEST.** Declare one only where the registered
113
+ contract carries the parameter, or a step is admitted and the agent is left holding an option it
114
+ cannot send. `nano-banana.test.ts` names each capability's evidence in the document.
115
+ - **An `accepts` set is a REFUSAL.** State one only where the endpoint genuinely enumerates its
116
+ values, and read it off the contract rather than restating it, so the two cannot drift.
117
+
118
+ Everything that does not gate an option ("good at pixel art", "cheap below 2K", "rate limited to
119
+ five a second") stays prose in `description` and `guidance`, where a sentence can say what it means.
@@ -0,0 +1,3 @@
1
+ /** The catalog-facing OpenAPI document for Nano Banana on `https://generativelanguage.googleapis.com`. */
2
+ export declare const NANO_BANANA_OPENAPI: Record<string, unknown>;
3
+ //# sourceMappingURL=nano-banana.openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nano-banana.openapi.d.ts","sourceRoot":"","sources":["../../src/contracts/nano-banana.openapi.ts"],"names":[],"mappings":"AAoDA,0GAA0G;AAC1G,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAyYxB,CAAA"}
@@ -0,0 +1,416 @@
1
+ // Google's Gemini Interactions API, narrowed to Nano Banana image generation, as an OpenAPI 3.1
2
+ // document.
3
+ //
4
+ // Authored here because Google publishes this surface as prose, curl snippets and SDK reference
5
+ // pages rather than as a machine-readable spec a registry can hand to an agent. The document is
6
+ // what a run actually reads: the registry renders it into `.cat-context/` beside the brief, and
7
+ // the agent writes its request from this and nothing else.
8
+ //
9
+ // WHAT IS IN IT is one operation, and that is not a small integration written small: it is the
10
+ // whole of what a step needs. Generation here is SYNCHRONOUS, so the response to
11
+ // `POST /v1beta/interactions` carries the image bytes inline as base64. There is no task to poll,
12
+ // no signed URL to race, and correspondingly no second endpoint.
13
+ //
14
+ // The Interactions API is a great deal more than image generation, and the rest is deliberately
15
+ // absent: text and audio and video generation, function calling, code execution, computer use,
16
+ // the Deep Research and Antigravity agents, file search, streaming, `background` execution with
17
+ // its `GET`/`DELETE`/`cancel` lifecycle, and stored multi-turn interactions
18
+ // (`previous_interaction_id`). Grounding tools (Google Search, Google Maps, URL context) are the
19
+ // omission worth naming twice, because they are the one that bills: 5,000 free searches a month
20
+ // across all Gemini 3.x usage on the key, then $14 per 1,000. A step asked to draw a scout has no
21
+ // business searching the web to do it, and an interface an agent is handed is an interface it
22
+ // will use.
23
+ //
24
+ // MODEL NAMES ARE BAKED IN HERE, which wants its reason because a vendor's model list is usually
25
+ // the first thing a document like this gets wrong. `model` is a REQUIRED enum field on every
26
+ // request, there is no discovery operation registered, and the three image models are a cost
27
+ // ladder rather than a price list, so an agent that cannot name one cannot make a request at all.
28
+ //
29
+ // `satisfies OpenAPIV3_1.Document` is what makes "authored in TypeScript" worth anything: a typo'd
30
+ // `$ref` or a parameter with no `in` is a compile error here rather than a garbled context file at
31
+ // dispatch. The ANNOTATION stays `Record<string, unknown>` so `openapi-types` does not reach
32
+ // consumers through this package's `.d.ts`: the checking is ours, and the shipped shape is what
33
+ // `openApiContract` takes.
34
+ //
35
+ // WHAT NO TEST HERE CAN HOLD, listed because an unlisted unchecked fact is one nobody re-checks.
36
+ // The type system holds this document's shape and `openapi-documents.test.ts` holds its internal
37
+ // references, but every NAME and NUMBER below was transcribed from Google's docs and will go stale
38
+ // without failing anything:
39
+ //
40
+ // - the three image model ids, and the resolutions each supports (Lite is 1K only);
41
+ // - 14 reference images, and the per-model split between objects, characters and style refs;
42
+ // - the ten aspect ratios and the four `image_size` values;
43
+ // - the per-image prices in the generator's description, and the $60 / $120 per 1M output
44
+ // token rates behind them;
45
+ // - SynthID watermarking of every generated image.
46
+ //
47
+ // A vendor that changes one of these ships an integration that still compiles, still passes, and
48
+ // is wrong at dispatch. Re-read them against https://ai.google.dev/gemini-api/docs/image-generation
49
+ // when touching this file.
50
+ /** The catalog-facing OpenAPI document for Nano Banana on `https://generativelanguage.googleapis.com`. */
51
+ export const NANO_BANANA_OPENAPI = {
52
+ openapi: '3.1.0',
53
+ info: {
54
+ title: 'Gemini Interactions API (Nano Banana image generation)',
55
+ version: 'v1beta',
56
+ description: 'Text-to-image and reference-guided image generation and editing, through the one ' +
57
+ '`interactions` endpoint. Every request carries the `x-goog-api-key` header. Generation is ' +
58
+ 'SYNCHRONOUS: the response carries the finished image inline as base64, so there is no ' +
59
+ 'task, no poll and no expiring link. A request the model declines is not an error status: ' +
60
+ 'it answers 200 with a text block explaining the refusal and no image block at all.',
61
+ },
62
+ servers: [{ url: 'https://generativelanguage.googleapis.com' }],
63
+ security: [{ googleApiKey: [] }],
64
+ paths: {
65
+ '/v1beta/interactions': {
66
+ post: {
67
+ operationId: 'createImageInteraction',
68
+ summary: 'Generate or edit an image and receive the bytes inline',
69
+ description: 'One call, one image. Send the brief as a `text` block and any reference images as ' +
70
+ '`image` blocks in the same `input` array; the response carries the result as base64 ' +
71
+ 'in `output_image`, and `usage` reports what it cost in tokens. There is no balance ' +
72
+ 'read on this API and no dry run, so the request that discovers an exhausted quota is ' +
73
+ 'a real one.',
74
+ requestBody: {
75
+ required: true,
76
+ content: {
77
+ 'application/json': {
78
+ schema: { $ref: '#/components/schemas/ImageInteractionRequest' },
79
+ },
80
+ },
81
+ },
82
+ responses: {
83
+ '200': {
84
+ description: 'The interaction completed. It carries an image when the model produced one and ' +
85
+ 'text alone when it declined, so check for the image rather than assuming it.',
86
+ content: {
87
+ 'application/json': { schema: { $ref: '#/components/schemas/Interaction' } },
88
+ },
89
+ },
90
+ '400': { $ref: '#/components/responses/BadRequest' },
91
+ '403': { $ref: '#/components/responses/PermissionDenied' },
92
+ '429': { $ref: '#/components/responses/RateLimited' },
93
+ '500': { $ref: '#/components/responses/ServerError' },
94
+ '503': { $ref: '#/components/responses/Unavailable' },
95
+ },
96
+ },
97
+ },
98
+ },
99
+ components: {
100
+ securitySchemes: {
101
+ googleApiKey: {
102
+ type: 'apiKey',
103
+ in: 'header',
104
+ name: 'x-goog-api-key',
105
+ description: 'An API key from Google AI Studio. Send it as this header rather than as a `?key=` ' +
106
+ 'query parameter, which puts the credential in logs and referrers.',
107
+ },
108
+ },
109
+ responses: {
110
+ BadRequest: {
111
+ description: 'The request was rejected. `INVALID_ARGUMENT` is a malformed body, and also what an ' +
112
+ 'invalid API key returns, so read `error.message` rather than assuming the body is at ' +
113
+ 'fault. `FAILED_PRECONDITION` is billing not enabled on the project, which every image ' +
114
+ 'model requires since none has a free tier.',
115
+ content: { 'application/json': { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } },
116
+ },
117
+ PermissionDenied: {
118
+ description: '`PERMISSION_DENIED`: the key is valid but not entitled to this model. A rewrite of ' +
119
+ 'the request will not fix it; report it as a gap.',
120
+ content: { 'application/json': { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } },
121
+ },
122
+ RateLimited: {
123
+ description: '`RESOURCE_EXHAUSTED`, a rate limit on any of four dimensions: requests per minute, ' +
124
+ 'input tokens per minute, requests per day, and IMAGES per minute, which is the one ' +
125
+ 'peculiar to these models. Back off exponentially; the daily one does not clear by ' +
126
+ 'waiting a minute.',
127
+ content: { 'application/json': { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } },
128
+ },
129
+ ServerError: {
130
+ description: '`INTERNAL`: a fault on Google’s side. Retry with backoff.',
131
+ content: { 'application/json': { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } },
132
+ },
133
+ Unavailable: {
134
+ description: '`UNAVAILABLE`: the model is temporarily overloaded. Retry with backoff rather than ' +
135
+ 'switching models mid-set, since a different model draws a different picture.',
136
+ content: { 'application/json': { schema: { $ref: '#/components/schemas/ErrorEnvelope' } } },
137
+ },
138
+ },
139
+ schemas: {
140
+ ImageInteractionRequest: {
141
+ type: 'object',
142
+ required: ['model', 'input'],
143
+ properties: {
144
+ model: { $ref: '#/components/schemas/ImageModel' },
145
+ input: {
146
+ type: 'array',
147
+ description: 'The prompt, as an ordered list of blocks. One `text` block is the brief; each ' +
148
+ '`image` block is a reference the model composes from or edits, up to 14 of them ' +
149
+ 'on one request (the pro model reads them as roughly six objects, five characters ' +
150
+ 'and three style references).',
151
+ items: { $ref: '#/components/schemas/InputBlock' },
152
+ },
153
+ response_format: { $ref: '#/components/schemas/ImageResponseFormat' },
154
+ generation_config: { $ref: '#/components/schemas/GenerationConfig' },
155
+ },
156
+ },
157
+ ImageModel: {
158
+ type: 'string',
159
+ enum: ['gemini-3.1-flash-image', 'gemini-3.1-flash-lite-image', 'gemini-3-pro-image'],
160
+ description: 'Which image model draws it: a cost ladder, not a feature list. ' +
161
+ '`gemini-3.1-flash-image` (Nano Banana 2) is the default working model and reaches 4K. ' +
162
+ '`gemini-3.1-flash-lite-image` is the cheapest and is 1K ONLY, so pairing it with a ' +
163
+ 'larger `image_size` is a request that cannot be served. `gemini-3-pro-image` (Nano ' +
164
+ 'Banana Pro) is the premium model: the best text rendering and identity consistency, ' +
165
+ 'roughly twice the price, and the one to reach for when the image has to carry legible ' +
166
+ 'words. `gemini-2.5-flash-image` (the original Nano Banana) is legacy and not offered ' +
167
+ 'here.',
168
+ },
169
+ InputBlock: {
170
+ description: 'One block of the prompt: the written brief, or a reference image.',
171
+ oneOf: [
172
+ { $ref: '#/components/schemas/TextInput' },
173
+ { $ref: '#/components/schemas/ImageInput' },
174
+ ],
175
+ },
176
+ TextInput: {
177
+ type: 'object',
178
+ required: ['type', 'text'],
179
+ properties: {
180
+ type: { type: 'string', const: 'text' },
181
+ text: {
182
+ type: 'string',
183
+ description: 'What the image IS: subject, framing, materials, lighting, and the words to render ' +
184
+ 'if any. Describe the picture rather than listing keywords; these models read a ' +
185
+ 'paragraph better than a tag soup, and they render text in the brief literally.',
186
+ },
187
+ },
188
+ },
189
+ ImageInput: {
190
+ type: 'object',
191
+ required: ['type'],
192
+ properties: {
193
+ type: { type: 'string', const: 'image' },
194
+ data: {
195
+ type: 'string',
196
+ description: 'The reference image as RAW base64, with no `data:` prefix. Send bytes this way ' +
197
+ 'rather than by `uri`, since a bearer-gated storage link is not something Google’s ' +
198
+ 'network can fetch.',
199
+ },
200
+ uri: {
201
+ type: 'string',
202
+ description: 'A Files API URI for an image already uploaded to this project. Not a general ' +
203
+ 'URL fetcher.',
204
+ },
205
+ mime_type: {
206
+ type: 'string',
207
+ enum: ['image/png', 'image/jpeg', 'image/webp', 'image/heic', 'image/heif'],
208
+ description: 'The format of `data`. Declare it; do not leave it to be sniffed.',
209
+ },
210
+ },
211
+ },
212
+ ImageResponseFormat: {
213
+ type: 'object',
214
+ description: 'What to produce. Omitting it entirely yields a 1:1 1K PNG, which is rarely the size ' +
215
+ 'the deliverable actually wants.',
216
+ properties: {
217
+ type: {
218
+ type: 'string',
219
+ const: 'image',
220
+ description: 'Ask for an image. Without it the model may answer with text.',
221
+ },
222
+ mime_type: {
223
+ type: 'string',
224
+ enum: ['image/png', 'image/jpeg'],
225
+ description: 'The container. `image/png` for flat colour, transparency, or anything a later ' +
226
+ 'step will cut up; `image/jpeg` only for a finished photographic image, since its ' +
227
+ 'artefacts survive every edit after it.',
228
+ },
229
+ aspect_ratio: {
230
+ type: 'string',
231
+ enum: ['1:1', '3:2', '2:3', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9'],
232
+ description: 'The shape. This is a CLOSED list, not a free-form ratio: a deliverable whose ' +
233
+ 'required proportions are not on it is generated at the nearest ratio and cropped ' +
234
+ 'afterwards, never requested directly.',
235
+ },
236
+ image_size: {
237
+ type: 'string',
238
+ enum: ['512px', '1K', '2K', '4K'],
239
+ description: 'The resolution, and the price. The `K` is capitalised and the values are exact ' +
240
+ 'strings. `gemini-3.1-flash-lite-image` serves `1K` alone; the other two serve all ' +
241
+ 'four.',
242
+ },
243
+ },
244
+ },
245
+ GenerationConfig: {
246
+ type: 'object',
247
+ properties: {
248
+ thinking_level: {
249
+ type: 'string',
250
+ enum: ['minimal', 'low', 'medium', 'high'],
251
+ description: 'How much the model reasons before drawing. `minimal` is the default and is right ' +
252
+ 'for a brief that already says what the picture is; `high` earns its keep on ' +
253
+ 'composition-heavy work and on images with a lot of text in them. The interim ' +
254
+ 'images it draws while thinking are not charged, but the thinking TOKENS are.',
255
+ },
256
+ seed: {
257
+ type: 'integer',
258
+ description: 'Reuse to iterate on one composition while the prompt changes. It does not hold a ' +
259
+ 'character; reference images do.',
260
+ },
261
+ },
262
+ },
263
+ Interaction: {
264
+ type: 'object',
265
+ description: 'One completed generation. The image is inline; nothing here expires.',
266
+ properties: {
267
+ id: { type: 'string', description: 'The interaction id, for support and for logs.' },
268
+ object: { type: 'string', description: 'Always `interaction`.' },
269
+ model: { $ref: '#/components/schemas/ImageModel' },
270
+ status: {
271
+ type: 'string',
272
+ enum: [
273
+ 'completed',
274
+ 'in_progress',
275
+ 'queued',
276
+ 'requires_action',
277
+ 'incomplete',
278
+ 'failed',
279
+ 'cancelled',
280
+ 'budget_exceeded',
281
+ ],
282
+ description: 'For a plain image request this is `completed`. The asynchronous states belong to ' +
283
+ '`background` interactions, which this integration does not use.',
284
+ },
285
+ output_image: {
286
+ $ref: '#/components/schemas/OutputImage',
287
+ },
288
+ output_text: {
289
+ type: 'string',
290
+ description: 'The model’s text, concatenated. On a successful generation it is commentary; when ' +
291
+ 'there is NO `output_image` it is the refusal, and it is the only thing that says ' +
292
+ 'why.',
293
+ },
294
+ steps: {
295
+ type: 'array',
296
+ description: 'The full interleaved transcript. `output_image` is the last image out of it, ' +
297
+ 'which is what a single-image request wants; read this only when a request ' +
298
+ 'produced several.',
299
+ items: { $ref: '#/components/schemas/Step' },
300
+ },
301
+ usage: { $ref: '#/components/schemas/Usage' },
302
+ created: { type: 'string', description: 'ISO 8601 creation timestamp.' },
303
+ },
304
+ },
305
+ Step: {
306
+ type: 'object',
307
+ properties: {
308
+ id: { type: 'string' },
309
+ type: {
310
+ type: 'string',
311
+ enum: ['model_output', 'user_input', 'function_call'],
312
+ description: 'Generated images are on the `model_output` steps.',
313
+ },
314
+ content: {
315
+ type: 'array',
316
+ description: 'The blocks of this step, in order.',
317
+ items: { $ref: '#/components/schemas/OutputContent' },
318
+ },
319
+ },
320
+ },
321
+ OutputContent: {
322
+ description: 'One block of a step’s output.',
323
+ oneOf: [
324
+ { $ref: '#/components/schemas/OutputImage' },
325
+ { $ref: '#/components/schemas/OutputText' },
326
+ ],
327
+ },
328
+ OutputImage: {
329
+ type: 'object',
330
+ description: 'A generated image, inline. Every one carries an invisible SynthID watermark, which ' +
331
+ 'survives ordinary edits and is not something to try to remove.',
332
+ properties: {
333
+ type: { type: 'string', const: 'image' },
334
+ data: {
335
+ type: 'string',
336
+ description: 'The image as RAW base64 with no `data:` prefix. DECODE IT before storing: ' +
337
+ 'storing the base64 text is a corrupt asset that looks like a successful run.',
338
+ },
339
+ mime_type: {
340
+ type: 'string',
341
+ enum: ['image/png', 'image/jpeg'],
342
+ description: 'What the bytes actually are. Store this as the asset’s content type rather than ' +
343
+ 'the one you asked for.',
344
+ },
345
+ },
346
+ },
347
+ OutputText: {
348
+ type: 'object',
349
+ properties: {
350
+ type: { type: 'string', const: 'text' },
351
+ text: { type: 'string' },
352
+ },
353
+ },
354
+ Usage: {
355
+ type: 'object',
356
+ description: 'What the interaction cost, in tokens. This is the only accounting the API offers, ' +
357
+ 'there is no balance endpoint anywhere on this surface.',
358
+ properties: {
359
+ total_input_tokens: { type: 'integer', description: 'Prompt and reference images.' },
360
+ total_output_tokens: {
361
+ type: 'integer',
362
+ description: 'The image, billed at the model’s image-output rate. Roughly 1,120 tokens for a 1K ' +
363
+ 'image on the flash model and 2,520 for a 4K one, so resolution IS the bill.',
364
+ },
365
+ total_thought_tokens: {
366
+ type: 'integer',
367
+ description: 'Reasoning, when `thinking_level` asked for any. Billed.',
368
+ },
369
+ total_tokens: { type: 'integer' },
370
+ output_tokens_by_modality: {
371
+ type: 'array',
372
+ description: 'The output split by kind, which is how the image half is isolated.',
373
+ items: { $ref: '#/components/schemas/ModalityTokens' },
374
+ },
375
+ },
376
+ },
377
+ ModalityTokens: {
378
+ type: 'object',
379
+ properties: {
380
+ modality: {
381
+ type: 'string',
382
+ description: 'e.g. `TEXT`, `IMAGE`.',
383
+ },
384
+ token_count: { type: 'integer' },
385
+ },
386
+ },
387
+ ErrorEnvelope: {
388
+ type: 'object',
389
+ description: 'Google’s standard error wrapper. Every failure on this API arrives in it.',
390
+ properties: {
391
+ error: {
392
+ type: 'object',
393
+ properties: {
394
+ code: { type: 'integer', description: 'The HTTP status, repeated.' },
395
+ message: {
396
+ type: 'string',
397
+ description: 'The human-readable reason. Read it, since the status alone is ambiguous.',
398
+ },
399
+ status: {
400
+ type: 'string',
401
+ description: 'The canonical code, e.g. `INVALID_ARGUMENT`, `PERMISSION_DENIED`, ' +
402
+ '`RESOURCE_EXHAUSTED`, `FAILED_PRECONDITION`, `INTERNAL`, `UNAVAILABLE`.',
403
+ },
404
+ details: {
405
+ type: 'array',
406
+ description: 'Structured detail, when the service attaches any.',
407
+ items: { type: 'object', additionalProperties: true },
408
+ },
409
+ },
410
+ },
411
+ },
412
+ },
413
+ },
414
+ },
415
+ };
416
+ //# sourceMappingURL=nano-banana.openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nano-banana.openapi.js","sourceRoot":"","sources":["../../src/contracts/nano-banana.openapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import type { BinaryGeneratorCapability, BinaryGeneratorCredential, BinaryGeneratorDefinition, UploadApiContract } from '@cat-factory/contracts';
2
+ /**
3
+ * A validated integration definition: structurally a {@link BinaryGeneratorDefinition} (exactly
4
+ * what `BinaryGeneratorRegistry.register` takes), with the four optional COLLECTIONS settled so
5
+ * consumers never branch on `undefined`.
6
+ *
7
+ * `accepts` is the one optional field left unsettled, and the asymmetry is deliberate: it is a
8
+ * record of sets rather than a collection, and the platform reads an absent entry as "not stated"
9
+ * while refusing an empty array inside one. Settling it would turn a definition that declares no
10
+ * value sets into one that declares empty ones, which is the difference between "unjudged" and
11
+ * "refuses everything".
12
+ */
13
+ export interface BinaryGeneratorEntry extends BinaryGeneratorDefinition {
14
+ mediaTypes: string[];
15
+ capabilities: BinaryGeneratorCapability[];
16
+ contracts: UploadApiContract[];
17
+ credentials: BinaryGeneratorCredential[];
18
+ }
19
+ /** What {@link defineBinaryGenerator} accepts, before defaults are settled. */
20
+ export interface BinaryGeneratorInput {
21
+ /** Lower-kebab slug a step names in `stepOptions.binaryOutput.generatorIds`. */
22
+ id: string;
23
+ name: string;
24
+ /** One line; what the picker and the agent's brief show. */
25
+ summary: string;
26
+ /** What it is good at, what it is NOT for, and its cost profile. */
27
+ description: string;
28
+ /** The content types it produces. At least one. */
29
+ modalities: readonly BinaryGeneratorDefinition['modalities'][number][];
30
+ /** The concrete formats it emits, when the integration pins them down. */
31
+ mediaTypes?: readonly string[];
32
+ /**
33
+ * What it can be ASKED FOR while generating: the per-step generation options a step may point
34
+ * at it. Omitted (or empty) means "only the coarse facts are known": every option requirement
35
+ * against it is reported as unverifiable rather than refused, which is what lets an integration
36
+ * registered before this axis existed go on running unchanged.
37
+ */
38
+ capabilities?: readonly BinaryGeneratorCapability[];
39
+ /**
40
+ * For the options whose domain is a CLOSED SET, which values this endpoint takes. The axis
41
+ * beside {@link capabilities} rather than part of it: that one says the request can CARRY the
42
+ * value, this says the endpoint will take the one being asked for.
43
+ *
44
+ * Omitted per option means "not stated", and it is the right answer for an endpoint that renders
45
+ * anything inside limits no list can enumerate: those belong in `guidance`. A set is a REFUSAL,
46
+ * so declare one only where the endpoint genuinely has one.
47
+ */
48
+ accepts?: BinaryGeneratorDefinition['accepts'];
49
+ /** The API's base URL. `https` (or loopback) only: the credentials ride this request. */
50
+ endpoint?: string;
51
+ /** Operating notes folded into the agent's brief verbatim. */
52
+ guidance?: string;
53
+ /**
54
+ * The credentials it authenticates with, by NAME. A list because a vendor account is not always
55
+ * one string, and injection names must be distinct, which the schema refuses case-insensitively.
56
+ */
57
+ credentials?: readonly BinaryGeneratorCredential[];
58
+ /** API contract documents, injected as `.cat-context/` files beside the brief. */
59
+ contracts?: readonly UploadApiContract[];
60
+ }
61
+ /**
62
+ * Validate an integration definition and freeze it.
63
+ *
64
+ * Two checks, and they are the SAME two the engine makes at boot rather than an approximation of
65
+ * them. `binaryGeneratorDefinitionSchema` is the shape every write path shares, and
66
+ * `binaryGeneratorDetailIssues` is the rule set a parse structurally cannot make (a cleartext
67
+ * endpoint, a contract set that reads as garbage to the agent handed it, a media type whose
68
+ * modality contradicts the declared ones, a harness with no generation tool, an accepted-value set
69
+ * with no capability behind it). Calling them rather than restating them is the whole point of the
70
+ * seam: a rule the platform adds fails this definition on the version bump instead of on the boot
71
+ * that follows a deploy.
72
+ *
73
+ * What it does NOT check is the one thing no schema could: whether a declared capability is one the
74
+ * registered CONTRACT can actually be asked for. That is a claim about a vendor's request body, so
75
+ * it is pinned per integration in a test beside the document.
76
+ */
77
+ export declare function defineBinaryGenerator(input: BinaryGeneratorInput): BinaryGeneratorEntry;
78
+ /**
79
+ * Turn an OpenAPI document written as a TypeScript object into the uploadable contract shape.
80
+ *
81
+ * The document stays a VALUE in source rather than a string, and this is the one place it becomes
82
+ * the text the registry stores: a typo'd `operationId` or a parameter with no `in` is then a
83
+ * compile error at the document, where `satisfies OpenAPIV3_1.Document` can see it.
84
+ *
85
+ * `document` is deliberately structural here rather than typed against `openapi-types`, which
86
+ * would put that package in every consumer's type resolution for a helper that only serializes.
87
+ * Two-space indentation because an agent reads the body verbatim.
88
+ */
89
+ export declare function openApiContract(params: {
90
+ contractId: string;
91
+ title: string;
92
+ document: Record<string, unknown>;
93
+ }): UploadApiContract;
94
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,iBAAiB,EAClB,MAAM,wBAAwB,CAAA;AAoB/B;;;;;;;;;;GAUG;AACH,MAAM,WAAW,oBAAqB,SAAQ,yBAAyB;IACrE,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,YAAY,EAAE,yBAAyB,EAAE,CAAA;IACzC,SAAS,EAAE,iBAAiB,EAAE,CAAA;IAC9B,WAAW,EAAE,yBAAyB,EAAE,CAAA;CACzC;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAA;IACnB,mDAAmD;IACnD,UAAU,EAAE,SAAS,yBAAyB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,EAAE,CAAA;IACtE,0EAA0E;IAC1E,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;IACnD;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAA;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAClD,kFAAkF;IAClF,SAAS,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAA;CACzC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,GAAG,oBAAoB,CA6BvF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC,GAAG,iBAAiB,CAOpB"}
package/dist/define.js ADDED
@@ -0,0 +1,69 @@
1
+ import { binaryGeneratorDefinitionSchema } from '@cat-factory/contracts';
2
+ import { binaryGeneratorDetailIssues } from '@cat-factory/kernel';
3
+ import * as v from 'valibot';
4
+ /**
5
+ * Validate an integration definition and freeze it.
6
+ *
7
+ * Two checks, and they are the SAME two the engine makes at boot rather than an approximation of
8
+ * them. `binaryGeneratorDefinitionSchema` is the shape every write path shares, and
9
+ * `binaryGeneratorDetailIssues` is the rule set a parse structurally cannot make (a cleartext
10
+ * endpoint, a contract set that reads as garbage to the agent handed it, a media type whose
11
+ * modality contradicts the declared ones, a harness with no generation tool, an accepted-value set
12
+ * with no capability behind it). Calling them rather than restating them is the whole point of the
13
+ * seam: a rule the platform adds fails this definition on the version bump instead of on the boot
14
+ * that follows a deploy.
15
+ *
16
+ * What it does NOT check is the one thing no schema could: whether a declared capability is one the
17
+ * registered CONTRACT can actually be asked for. That is a claim about a vendor's request body, so
18
+ * it is pinned per integration in a test beside the document.
19
+ */
20
+ export function defineBinaryGenerator(input) {
21
+ const parsed = v.parse(binaryGeneratorDefinitionSchema, {
22
+ ...input,
23
+ modalities: [...input.modalities],
24
+ mediaTypes: [...(input.mediaTypes ?? [])],
25
+ capabilities: [...(input.capabilities ?? [])],
26
+ credentials: (input.credentials ?? []).map((credential) => ({ ...credential })),
27
+ contracts: (input.contracts ?? []).map((contract) => ({ ...contract })),
28
+ });
29
+ const issues = binaryGeneratorDetailIssues(parsed);
30
+ if (issues.length > 0) {
31
+ throw new Error(`Binary generator '${parsed.id}': ${issues.map((i) => i.message).join('; ')}`);
32
+ }
33
+ return Object.freeze({
34
+ ...parsed,
35
+ mediaTypes: parsed.mediaTypes ?? [],
36
+ // Settled to an array like the two beside it, and settling it changes NOTHING about how it is
37
+ // read: the coverage rule treats an empty capability list and an absent one as the same
38
+ // documented state ("only the coarse facts are known"), and the registry's own projection
39
+ // settles it to `[]` on the way to the engine either way.
40
+ capabilities: parsed.capabilities ?? [],
41
+ contracts: parsed.contracts ?? [],
42
+ // Settled for the same reason `contracts` is: every reader here loops rather than branching on
43
+ // one optional object, and a loop over `undefined` is the one shape that does not degrade
44
+ // quietly. `accepts` is deliberately NOT settled beside these, since an empty object and an
45
+ // absent one are the same fact there while an empty ARRAY inside one is refused, so
46
+ // manufacturing one would invent a refusal nobody declared.
47
+ credentials: parsed.credentials ?? [],
48
+ });
49
+ }
50
+ /**
51
+ * Turn an OpenAPI document written as a TypeScript object into the uploadable contract shape.
52
+ *
53
+ * The document stays a VALUE in source rather than a string, and this is the one place it becomes
54
+ * the text the registry stores: a typo'd `operationId` or a parameter with no `in` is then a
55
+ * compile error at the document, where `satisfies OpenAPIV3_1.Document` can see it.
56
+ *
57
+ * `document` is deliberately structural here rather than typed against `openapi-types`, which
58
+ * would put that package in every consumer's type resolution for a helper that only serializes.
59
+ * Two-space indentation because an agent reads the body verbatim.
60
+ */
61
+ export function openApiContract(params) {
62
+ return {
63
+ contractId: params.contractId,
64
+ format: 'openapi',
65
+ title: params.title,
66
+ body: `${JSON.stringify(params.document, null, 2)}\n`,
67
+ };
68
+ }
69
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AA8E5B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA2B;IAC/D,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,+BAA+B,EAAE;QACtD,GAAG,KAAK;QACR,UAAU,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;QACjC,UAAU,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QACzC,YAAY,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QAC7C,WAAW,EAAE,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC;QAC/E,SAAS,EAAE,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;KACxE,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChG,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,MAAM;QACT,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE;QACnC,8FAA8F;QAC9F,wFAAwF;QACxF,0FAA0F;QAC1F,0DAA0D;QAC1D,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;QACvC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;QACjC,+FAA+F;QAC/F,0FAA0F;QAC1F,4FAA4F;QAC5F,oFAAoF;QACpF,4DAA4D;QAC5D,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,EAAE;KACtC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,MAI/B;IACC,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI;KACtD,CAAA;AACH,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The environment variable the Gemini API key is delivered as.
3
+ *
4
+ * Both what the deployment's `ToolSecretResolver` is asked for and the variable name the agent
5
+ * reads inside its run container, which is why it is a POSIX identifier rather than the header it
6
+ * ends up in (`x-goog-api-key`).
7
+ *
8
+ * Named for the VENDOR rather than for this definition's id: `GEMINI_API_KEY` is the variable
9
+ * Google's own `google-genai` client libraries read from the environment, so an agent that reaches
10
+ * for a library instead of writing the request by hand finds the value where the library looks.
11
+ * That is also why this definition needs no `envName`: the lookup key and the injected name are
12
+ * already the same string.
13
+ *
14
+ * It sits outside `isReservedPlatformEnvKey`'s set, which the tests assert rather than assume. The
15
+ * platform's own Google models are reached through OpenRouter (`OPENROUTER_*`), so no family here
16
+ * covers this name; if one ever does, the lookup resolves nothing and "no credential" looks exactly
17
+ * like an unset variable. Never document it in `docs/environment-variables.md` either: every
18
+ * variable named there is reserved by `check-reserved-env-keys.mjs`, which would retire this key.
19
+ *
20
+ * A workspace sets it as a capability credential (`binary-generator` subject, key `GEMINI_API_KEY`)
21
+ * or the deployment sets it in the environment its runs dispatch from.
22
+ */
23
+ export declare const NANO_BANANA_CREDENTIAL_KEY = "GEMINI_API_KEY";
24
+ /**
25
+ * The registry id a step names in `stepOptions.binaryOutput.generatorIds`, re-exported from
26
+ * `@cat-factory/contracts` rather than spelled here.
27
+ *
28
+ * The id has two authors that must agree: this definition, and the built-in `pl_media` preset in
29
+ * kernel's seed catalog, which selects it. Kernel cannot import this package (this package depends
30
+ * on kernel), so the shared layer below both owns the string and each side reads it.
31
+ */
32
+ export { NANO_BANANA_GENERATOR_ID } from '@cat-factory/contracts';
33
+ /**
34
+ * `nano-banana`: Google's Gemini image models, and the platform's FIRST shipped generative binary
35
+ * integration.
36
+ *
37
+ * Three facts about where it sits.
38
+ *
39
+ * **It is the only one the platform ships**, which is a change of position rather than a first
40
+ * step down a list. The registry stayed empty on the argument that no image generator is one every
41
+ * organisation runs and every one of them is metered. Both halves are still true and neither one
42
+ * decides this: what shipping the Media task type showed is that a step selecting NOTHING leaves
43
+ * the deployment's most demonstrable capability reachable only by a deployment that first writes
44
+ * an integration, an OpenAPI document and a credential declaration. Metered is handled by the
45
+ * credential (unset means the agent is told the integration is unavailable and reports the gap),
46
+ * so the cost of shipping one is a picker entry a deployment can ignore, against a task type that
47
+ * generates nothing out of the box.
48
+ *
49
+ * **It is SYNCHRONOUS**, which is unusual for this class of API and is most of what its guidance
50
+ * spends its words on. No task id, no polling loop, no signed URL with minutes on it: the bytes
51
+ * come back on the create call. The failure mode that replaces them is quieter than any of those,
52
+ * because a refused prompt answers 200 with text and no image at all.
53
+ *
54
+ * **It emits `image/png` and `image/jpeg`**, the two values `response_format.mime_type` takes and
55
+ * the whole of what this API returns. A step needing a vector or an animated GIF is refused for
56
+ * holding this alone, correctly, and pointed at whatever the deployment registered beside it.
57
+ *
58
+ * @see https://ai.google.dev/gemini-api/docs/image-generation
59
+ */
60
+ export declare const nanoBananaGenerator: import("../define.js").BinaryGeneratorEntry;
61
+ //# sourceMappingURL=nano-banana.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nano-banana.d.ts","sourceRoot":"","sources":["../../src/generators/nano-banana.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,0BAA0B,mBAAmB,CAAA;AAE1D;;;;;;;GAOG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,mBAAmB,6CA8G9B,CAAA"}
@@ -0,0 +1,173 @@
1
+ import { NANO_BANANA_GENERATOR_ID } from '@cat-factory/contracts';
2
+ import { NANO_BANANA_OPENAPI } from '../contracts/nano-banana.openapi.js';
3
+ import { defineBinaryGenerator, openApiContract } from '../define.js';
4
+ /**
5
+ * The environment variable the Gemini API key is delivered as.
6
+ *
7
+ * Both what the deployment's `ToolSecretResolver` is asked for and the variable name the agent
8
+ * reads inside its run container, which is why it is a POSIX identifier rather than the header it
9
+ * ends up in (`x-goog-api-key`).
10
+ *
11
+ * Named for the VENDOR rather than for this definition's id: `GEMINI_API_KEY` is the variable
12
+ * Google's own `google-genai` client libraries read from the environment, so an agent that reaches
13
+ * for a library instead of writing the request by hand finds the value where the library looks.
14
+ * That is also why this definition needs no `envName`: the lookup key and the injected name are
15
+ * already the same string.
16
+ *
17
+ * It sits outside `isReservedPlatformEnvKey`'s set, which the tests assert rather than assume. The
18
+ * platform's own Google models are reached through OpenRouter (`OPENROUTER_*`), so no family here
19
+ * covers this name; if one ever does, the lookup resolves nothing and "no credential" looks exactly
20
+ * like an unset variable. Never document it in `docs/environment-variables.md` either: every
21
+ * variable named there is reserved by `check-reserved-env-keys.mjs`, which would retire this key.
22
+ *
23
+ * A workspace sets it as a capability credential (`binary-generator` subject, key `GEMINI_API_KEY`)
24
+ * or the deployment sets it in the environment its runs dispatch from.
25
+ */
26
+ export const NANO_BANANA_CREDENTIAL_KEY = 'GEMINI_API_KEY';
27
+ /**
28
+ * The registry id a step names in `stepOptions.binaryOutput.generatorIds`, re-exported from
29
+ * `@cat-factory/contracts` rather than spelled here.
30
+ *
31
+ * The id has two authors that must agree: this definition, and the built-in `pl_media` preset in
32
+ * kernel's seed catalog, which selects it. Kernel cannot import this package (this package depends
33
+ * on kernel), so the shared layer below both owns the string and each side reads it.
34
+ */
35
+ export { NANO_BANANA_GENERATOR_ID } from '@cat-factory/contracts';
36
+ /**
37
+ * `nano-banana`: Google's Gemini image models, and the platform's FIRST shipped generative binary
38
+ * integration.
39
+ *
40
+ * Three facts about where it sits.
41
+ *
42
+ * **It is the only one the platform ships**, which is a change of position rather than a first
43
+ * step down a list. The registry stayed empty on the argument that no image generator is one every
44
+ * organisation runs and every one of them is metered. Both halves are still true and neither one
45
+ * decides this: what shipping the Media task type showed is that a step selecting NOTHING leaves
46
+ * the deployment's most demonstrable capability reachable only by a deployment that first writes
47
+ * an integration, an OpenAPI document and a credential declaration. Metered is handled by the
48
+ * credential (unset means the agent is told the integration is unavailable and reports the gap),
49
+ * so the cost of shipping one is a picker entry a deployment can ignore, against a task type that
50
+ * generates nothing out of the box.
51
+ *
52
+ * **It is SYNCHRONOUS**, which is unusual for this class of API and is most of what its guidance
53
+ * spends its words on. No task id, no polling loop, no signed URL with minutes on it: the bytes
54
+ * come back on the create call. The failure mode that replaces them is quieter than any of those,
55
+ * because a refused prompt answers 200 with text and no image at all.
56
+ *
57
+ * **It emits `image/png` and `image/jpeg`**, the two values `response_format.mime_type` takes and
58
+ * the whole of what this API returns. A step needing a vector or an animated GIF is refused for
59
+ * holding this alone, correctly, and pointed at whatever the deployment registered beside it.
60
+ *
61
+ * @see https://ai.google.dev/gemini-api/docs/image-generation
62
+ */
63
+ export const nanoBananaGenerator = defineBinaryGenerator({
64
+ id: NANO_BANANA_GENERATOR_ID,
65
+ name: 'Nano Banana',
66
+ summary: 'Google’s Gemini image models: conversational generation and reference-guided editing up to 4K, with strong in-image text rendering, returned synchronously as inline bytes and charged per image.',
67
+ modalities: ['image'],
68
+ // The two values `response_format.mime_type` takes. Declare only what a step may REQUIRE: both
69
+ // of these can be asked for by name, so both are safe to state. An integration whose container
70
+ // is whatever the response happens to carry declares the one it always produces and no more.
71
+ mediaTypes: ['image/png', 'image/jpeg'],
72
+ // What a step may ASK this integration for. One operation carries all of it, where most image
73
+ // APIs split the same work across two calls or lack a piece of it.
74
+ //
75
+ // - `reference-image` / `multi-reference`: `image` blocks in `input`, up to fourteen on one
76
+ // request.
77
+ // - `instruction-edit`: the same operation, an `image` block plus a `text` block saying what to
78
+ // change. Editing is not a separate endpoint here, which is what makes "the same character,
79
+ // now facing left" a request rather than a re-roll.
80
+ // - `seed`: `generation_config.seed`.
81
+ // - `aspect-ratio`: `response_format.aspect_ratio`, a CLOSED list of ten, stated as an `accepts`
82
+ // set below so a ratio outside it is refused by name rather than generated at the nearest one
83
+ // and silently cropped.
84
+ //
85
+ // Absent, and each for a reason the endpoint states rather than a preference:
86
+ //
87
+ // - `mask-edit`: no mask parameter. Conversational editing rewrites the whole picture, which is
88
+ // a different act from repainting a named region.
89
+ // - `negative-prompt`: no such field; what to keep out goes in the brief.
90
+ // - `candidate-batch`: one interaction returns one image. Several candidates are several
91
+ // requests, each charged, which is what the comparison surface does with it.
92
+ // - `exact-size`: `image_size` is a resolution LADDER (`512px`, `1K`, `2K`, `4K`) rather than a
93
+ // width and a height, so this endpoint cannot be handed dimensions at all. A step needing
94
+ // 96x96 is refused here rather than served a 1K render downscaled by whoever wrote the call.
95
+ // - `upscale`: the same ladder, for the same reason: it is not a multiple of a native size.
96
+ // - `transparent-background`: PNG is offered, alpha is not a request parameter.
97
+ // - `tileable`: no parameter.
98
+ capabilities: ['reference-image', 'multi-reference', 'instruction-edit', 'seed', 'aspect-ratio'],
99
+ // The enum is declared VERBATIM rather than pre-reduced, and `21:9` is why that is safe: it
100
+ // reduces to `7:3`, the platform compares in lowest terms at both ends, so a step asking for
101
+ // `7:3` is served. Reducing the set by hand would gain nothing and lose the correspondence to the
102
+ // document beside it, which is what the tests check.
103
+ //
104
+ // No `outputSizes` beside it, deliberately: this endpoint takes no dimensions, so it declares
105
+ // neither `exact-size` nor a size set.
106
+ accepts: {
107
+ aspectRatios: ['1:1', '3:2', '2:3', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9'],
108
+ },
109
+ endpoint: 'https://generativelanguage.googleapis.com',
110
+ credentials: [
111
+ {
112
+ key: NANO_BANANA_CREDENTIAL_KEY,
113
+ usage: 'x-goog-api-key: <value> (an API key from Google AI Studio)',
114
+ required: true,
115
+ },
116
+ ],
117
+ contracts: [
118
+ openApiContract({
119
+ contractId: 'nano-banana-api',
120
+ title: 'Gemini Interactions API (Nano Banana image generation and editing)',
121
+ document: NANO_BANANA_OPENAPI,
122
+ }),
123
+ ],
124
+ description: `Generates and EDITS images from a written brief and up to fourteen reference images, and hands the bytes back on the same call. Two things make it worth reaching for: it renders long, legible text inside a picture better than most image APIs (signage, UI mockups, labelled diagrams, a title card that has to say the actual title), and it edits conversationally, so "the same character, now facing left, keep the armour" is a request rather than a re-roll.
125
+
126
+ ## Which model
127
+
128
+ | Model | Resolutions | ~Cost / image | Use it for |
129
+ | --- | --- | --- | --- |
130
+ | **\`gemini-3.1-flash-lite-image\`** | 1K only | $0.034 | Drafting and thumbnails, where several cheap tries beat one considered one. |
131
+ | **\`gemini-3.1-flash-image\`** | 512px, 1K, 2K, 4K | $0.045-0.151 | The default. Everything that is not specifically a typography or identity problem. |
132
+ | **\`gemini-3-pro-image\`** | 1K, 2K, 4K | $0.134-0.24 | The image you keep when it has words in it, or when a character has to survive into a set. |
133
+
134
+ Cost is per IMAGE and rises with resolution, so the size is the budget. \`image_size\` is a closed set of four strings rather than a pixel count, so a deliverable's real dimensions are reached by generating the nearest one and resizing.
135
+
136
+ ## Getting the same thing twice
137
+
138
+ Reference images, and a conversation. Up to fourteen ride a single request (the pro model reads them as roughly six objects, five characters and three style references), which is how a character, a prop or a palette survives into a second generation. Reusing a \`seed\` holds a composition while the prompt changes; it does not hold a subject.
139
+
140
+ ## What it is not for
141
+
142
+ - **Not pixel art.** This returns a smooth render, and shrinking one to 32px is a different asset that costs the same. Sprites, tilesets and item icons want an integration that draws on a pixel grid at native size, which a deployment registers beside this one.
143
+ - **Not a vector, and not an animation.** It emits \`image/png\` and \`image/jpeg\` and nothing else, so a step declaring \`image/svg+xml\` or \`image/gif\` is refused for holding this alone.
144
+ - **Not a deliverable at an exact pixel size.** \`image_size\` is a ladder of four buckets, so a step whose requirement IS 96x96 is refused rather than handed a downscale nobody chose.
145
+ - **Not a masked editor.** Editing here rewrites the whole picture from a sentence, so a step whose work is "fix this corner and touch nothing else" is refused at admission rather than handed a whole-image regeneration that looks like a fix.
146
+ - **Not 3D, audio, video or documents.** It declares \`image\` alone. A step that must also deliver a mesh needs an integration that makes one selected too, and admission refuses the step rather than letting it discover the gap at the end of a paid run.
147
+ - **Not a general Gemini client.** The registered contract is one operation. Text generation, function calling, code execution, the research agents, streaming and stored multi-turn interactions are all on this endpoint and none is registered. Grounding with Google Search is the pointed omission: it bills separately once the monthly free searches are gone, and a step asked to draw a scout has no business searching the web to do it.
148
+ - **Not unwatermarked.** Every image carries an invisible SynthID watermark. It is a fact to know about the deliverable, not a problem to route around.
149
+ - **Not free, and not free-tier.** None of these models has one, so a project without billing enabled gets \`FAILED_PRECONDITION\` on the first call. There is no balance read and no dry run on this API at all: the request that discovers an exhausted quota is a real one.
150
+ - **Not a source of art direction.** It renders the brief it is given. What the subject looks like comes from the task and the step's context services, not from here.`,
151
+ guidance: `Base URL \`https://generativelanguage.googleapis.com\`. Send \`x-goog-api-key: $${NANO_BANANA_CREDENTIAL_KEY}\` as a HEADER on every request, not as a \`?key=\` query parameter, which puts the credential in logs. If that variable is unset the platform could not provide the credential: do NOT call the API and do not invent a key; report the gap as the reason the artifact is missing.
152
+
153
+ **This one is synchronous.** \`POST /v1beta/interactions\` returns the finished image inline. There is no task id, no polling loop, no expiring link, and nothing to recover if a response is lost, so a dropped connection is a charge with no artifact: treat one request as one attempt.
154
+
155
+ **The loop that works**
156
+
157
+ 1. Pick the model from the job, not from habit: \`gemini-3.1-flash-lite-image\` to draft, \`gemini-3.1-flash-image\` by default, \`gemini-3-pro-image\` when the image carries text or a character has to stay recognisable. The lite model serves \`1K\` alone, so asking it for \`4K\` is a request that cannot be served.
158
+ 2. Size the request from the TARGET. \`image_size\` takes \`512px\`, \`1K\`, \`2K\` or \`4K\` and \`aspect_ratio\` takes one of ten fixed ratios, so pick the smallest bucket that covers what the step asked for and resize the bytes afterwards rather than expecting an exact pixel count.
159
+ 3. Send the brief as one \`text\` block describing the PICTURE: subject, framing, materials, lighting, and the words to render if any. A paragraph beats a keyword list here, and text in the brief is rendered literally.
160
+ 4. Set \`response_format\` deliberately: \`"type": "image"\` so the model does not answer in prose, and \`mime_type\` \`image/png\` for flat colour, transparency or anything a later step cuts up. \`image/jpeg\` only for a finished photographic image.
161
+ 5. Leave \`thinking_level\` at \`minimal\` for a brief that already says what the picture is; raise it to \`high\` for dense composition or a lot of in-image text. The interim images it draws are not charged, but the thinking tokens are.
162
+ 6. **Check that you got an image.** A declined request is not an error status: it answers 200 with \`output_text\` explaining and NO \`output_image\`. Treat that as terminal and REWRITE the brief, since resubmitting the same words buys the same refusal at the same price.
163
+ 7. Decode \`output_image.data\` from raw base64 (there is no \`data:\` prefix) and store the bytes, using \`output_image.mime_type\` as the content type rather than the one you asked for. Read \`usage.total_output_tokens\` for what it cost; it is the only accounting this API gives.
164
+
165
+ **Where this step's generation options land on this API.** The brief lists them once, in the platform's words; these are the fields that carry them here, and all of them ride the ONE request. Reference images are \`image\` blocks in \`input\`, up to fourteen, as raw base64 \`data\` rather than a \`uri\`. An instruction edit is an \`image\` block plus the \`text\` block saying what to change; there is no separate edit operation to switch to. A fixed seed is \`generation_config.seed\`. An aspect ratio is \`response_format.aspect_ratio\`, whose ten values are the whole of the list. A negative prompt, a masked edit, a transparent background and a tiling texture have no parameter here at all: report any of them as unmet rather than writing them into the brief and hoping.
166
+
167
+ **Holding a set together.** Send the first image back as an \`image\` block in the next request's \`input\`, alongside the text that says what to change: that is the edit path, and it is how a turnaround or a variant stays the same character. Send bytes as base64 \`data\` rather than \`uri\`, because Google fetches a \`uri\` from its own network and cannot read a link that needs the platform's credentials.
168
+
169
+ **Storing what you generated.** Bytes go to the step's configured storage service, never to the repository and never inline in a report, and the storage contract in your context says how. Declare the real content type (\`image/png\` or \`image/jpeg\`) rather than \`application/octet-stream\`.
170
+
171
+ **Failures worth telling apart.** They all arrive as \`{"error": {"code", "message", "status"}}\`, and the status matters more than the HTTP code. \`INVALID_ARGUMENT\` (400) is a malformed body AND what an invalid key returns, so read the message. \`FAILED_PRECONDITION\` (400) is billing not enabled, which is a gap to report rather than a retry. \`PERMISSION_DENIED\` (403) is a key with no entitlement to that model. \`RESOURCE_EXHAUSTED\` (429) is a rate limit on one of four dimensions (requests, input tokens, requests per DAY, and images per minute, which is the one peculiar to these models), so back off exponentially and do not assume a minute clears it. \`INTERNAL\` (500) and \`UNAVAILABLE\` (503) are transient: retry with backoff rather than switching models, since a different model draws a different picture.`,
172
+ });
173
+ //# sourceMappingURL=nano-banana.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nano-banana.js","sourceRoot":"","sources":["../../src/generators/nano-banana.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAErE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,gBAAgB,CAAA;AAE1D;;;;;;;GAOG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;IACvD,EAAE,EAAE,wBAAwB;IAC5B,IAAI,EAAE,aAAa;IACnB,OAAO,EACL,mMAAmM;IACrM,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,+FAA+F;IAC/F,+FAA+F;IAC/F,6FAA6F;IAC7F,UAAU,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC;IACvC,8FAA8F;IAC9F,mEAAmE;IACnE,EAAE;IACF,6FAA6F;IAC7F,cAAc;IACd,iGAAiG;IACjG,+FAA+F;IAC/F,uDAAuD;IACvD,uCAAuC;IACvC,kGAAkG;IAClG,iGAAiG;IACjG,2BAA2B;IAC3B,EAAE;IACF,8EAA8E;IAC9E,EAAE;IACF,iGAAiG;IACjG,qDAAqD;IACrD,2EAA2E;IAC3E,0FAA0F;IAC1F,gFAAgF;IAChF,iGAAiG;IACjG,6FAA6F;IAC7F,gGAAgG;IAChG,6FAA6F;IAC7F,iFAAiF;IACjF,+BAA+B;IAC/B,YAAY,EAAE,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,EAAE,cAAc,CAAC;IAChG,4FAA4F;IAC5F,6FAA6F;IAC7F,kGAAkG;IAClG,qDAAqD;IACrD,EAAE;IACF,8FAA8F;IAC9F,uCAAuC;IACvC,OAAO,EAAE;QACP,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KACxF;IACD,QAAQ,EAAE,2CAA2C;IACrD,WAAW,EAAE;QACX;YACE,GAAG,EAAE,0BAA0B;YAC/B,KAAK,EAAE,4DAA4D;YACnE,QAAQ,EAAE,IAAI;SACf;KACF;IACD,SAAS,EAAE;QACT,eAAe,CAAC;YACd,UAAU,EAAE,iBAAiB;YAC7B,KAAK,EAAE,oEAAoE;YAC3E,QAAQ,EAAE,mBAAmB;SAC9B,CAAC;KACH;IACD,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;uKA0BwJ;IACrK,QAAQ,EAAE,mFAAmF,0BAA0B;;;;;;;;;;;;;;;;;;;;yzBAoBgsB;CACxzB,CAAC,CAAA"}
@@ -0,0 +1,45 @@
1
+ import type { BinaryGeneratorRegistry } from '@cat-factory/kernel';
2
+ import type { BinaryGeneratorEntry } from './define.js';
3
+ export { type BinaryGeneratorEntry, type BinaryGeneratorInput, defineBinaryGenerator, openApiContract, } from './define.js';
4
+ export { NANO_BANANA_CREDENTIAL_KEY, NANO_BANANA_GENERATOR_ID, nanoBananaGenerator, } from './generators/nano-banana.js';
5
+ export { NANO_BANANA_OPENAPI } from './contracts/nano-banana.openapi.js';
6
+ /**
7
+ * The integrations the platform ships, in registration order.
8
+ *
9
+ * Exported as data so a deployment can register a SUBSET (or none) onto its own registry instance
10
+ * without reaching into this package's internals, and so a test can assert over the shipped set
11
+ * rather than over whatever a helper happened to install.
12
+ */
13
+ export declare const BUILTIN_BINARY_GENERATORS: readonly BinaryGeneratorEntry[];
14
+ /**
15
+ * Install the built-in integrations onto an app-owned registry instance, and return it.
16
+ *
17
+ * Idempotent by id: registering twice replaces rather than duplicates, so an entry point a test
18
+ * also drives is safe to call again. Registration ORDER is what makes a deployment's own
19
+ * definition of a shipped id an override, so the built-ins go first.
20
+ */
21
+ export declare function registerBuiltinBinaryGenerators(registry: BinaryGeneratorRegistry, generators?: readonly BinaryGeneratorEntry[]): BinaryGeneratorRegistry;
22
+ /**
23
+ * A fresh {@link BinaryGeneratorRegistry} carrying the shipped integrations.
24
+ *
25
+ * What each facade defaults to when a deployment injects no registry of its own, exactly as
26
+ * `gateRegistryWithBuiltins()` and `promptFragmentRegistryWithBuiltins()` are. An INJECTED registry
27
+ * replaces this one rather than merging with it, so a deployment that wants its own integrations
28
+ * AND the shipped ones starts from this call and registers onto the same instance:
29
+ *
30
+ * const binaryGeneratorRegistry = binaryGeneratorRegistryWithBuiltins()
31
+ * binaryGeneratorRegistry.registerAll(myIntegrations)
32
+ * startLocal({ binaryGeneratorRegistry })
33
+ *
34
+ * That matters more here than for the other two registries, because the shipped `pl_media` preset
35
+ * SELECTS `nano-banana` by id: a registry without it refuses that pipeline's runs at admission
36
+ * (`binary_output_generator_invalid`) rather than degrading, which is the correct disposition for
37
+ * a step naming an integration nobody registered and a loud one to meet on upgrade.
38
+ *
39
+ * Registered ONCE per deployment, on the process that owns the registry. In mothership mode that
40
+ * is the mothership: a node resolves integrations over `/internal/binary-generators` and consults
41
+ * no registry of its own, so the set the pipeline builder offers and the set admission resolves
42
+ * are one set however far behind the node's build has drifted.
43
+ */
44
+ export declare function binaryGeneratorRegistryWithBuiltins(): BinaryGeneratorRegistry;
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAElE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AA2BvD,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAExE;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,SAAS,oBAAoB,EAA0B,CAAA;AAE/F;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,uBAAuB,EACjC,UAAU,GAAE,SAAS,oBAAoB,EAA8B,GACtE,uBAAuB,CAGzB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mCAAmC,IAAI,uBAAuB,CAE7E"}
package/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ import { defaultBinaryGeneratorRegistry } from '@cat-factory/kernel';
2
+ import { nanoBananaGenerator } from './generators/nano-banana.js';
3
+ // ---------------------------------------------------------------------------
4
+ // The built-in generative binary integrations, authored ENTIRELY through the public
5
+ // binary-generator seam: this package depends on @cat-factory/kernel + @cat-factory/contracts and
6
+ // never on the engine. It is the same dogfood `@cat-factory/gates` and
7
+ // `@cat-factory/prompt-fragments` are: if the platform's own image integration can be expressed as
8
+ // an external package, so can a deployment's, and the seam cannot rot for consumers only because
9
+ // every boot exercises it.
10
+ //
11
+ // The registry itself stays EMPTY by default (`defaultBinaryGeneratorRegistry()`). A facade
12
+ // chooses: `binaryGeneratorRegistryWithBuiltins()` is what the shipped runtimes default to, and a
13
+ // deployment that wants a different set news its own instance. The two are opposite deployments
14
+ // and both are legitimate, which is why neither is baked into the registry class.
15
+ //
16
+ // WHY THE PLATFORM SHIPS ONE AT ALL. The registry shipped empty on the argument that no image
17
+ // generator is one every organisation runs and that every one of them is metered. Both halves
18
+ // are still true. What changed is that the Media task type shipped a generating agent, a preset
19
+ // and the storage under it, and left the selection blank: the platform's most demonstrable
20
+ // capability was the one nothing shipped could exercise, and closing that gap took a deployment
21
+ // writing an integration, an OpenAPI document and a credential declaration first. Metered is
22
+ // answered by the credential rather than by the registry: with no key resolved the agent is told
23
+ // the integration is unavailable and reports it as the reason an artifact is missing, so a
24
+ // deployment that ignores this entry pays nothing and sees one extra row in a picker.
25
+ // ---------------------------------------------------------------------------
26
+ export { defineBinaryGenerator, openApiContract, } from './define.js';
27
+ export { NANO_BANANA_CREDENTIAL_KEY, NANO_BANANA_GENERATOR_ID, nanoBananaGenerator, } from './generators/nano-banana.js';
28
+ export { NANO_BANANA_OPENAPI } from './contracts/nano-banana.openapi.js';
29
+ /**
30
+ * The integrations the platform ships, in registration order.
31
+ *
32
+ * Exported as data so a deployment can register a SUBSET (or none) onto its own registry instance
33
+ * without reaching into this package's internals, and so a test can assert over the shipped set
34
+ * rather than over whatever a helper happened to install.
35
+ */
36
+ export const BUILTIN_BINARY_GENERATORS = [nanoBananaGenerator];
37
+ /**
38
+ * Install the built-in integrations onto an app-owned registry instance, and return it.
39
+ *
40
+ * Idempotent by id: registering twice replaces rather than duplicates, so an entry point a test
41
+ * also drives is safe to call again. Registration ORDER is what makes a deployment's own
42
+ * definition of a shipped id an override, so the built-ins go first.
43
+ */
44
+ export function registerBuiltinBinaryGenerators(registry, generators = BUILTIN_BINARY_GENERATORS) {
45
+ registry.registerAll(generators);
46
+ return registry;
47
+ }
48
+ /**
49
+ * A fresh {@link BinaryGeneratorRegistry} carrying the shipped integrations.
50
+ *
51
+ * What each facade defaults to when a deployment injects no registry of its own, exactly as
52
+ * `gateRegistryWithBuiltins()` and `promptFragmentRegistryWithBuiltins()` are. An INJECTED registry
53
+ * replaces this one rather than merging with it, so a deployment that wants its own integrations
54
+ * AND the shipped ones starts from this call and registers onto the same instance:
55
+ *
56
+ * const binaryGeneratorRegistry = binaryGeneratorRegistryWithBuiltins()
57
+ * binaryGeneratorRegistry.registerAll(myIntegrations)
58
+ * startLocal({ binaryGeneratorRegistry })
59
+ *
60
+ * That matters more here than for the other two registries, because the shipped `pl_media` preset
61
+ * SELECTS `nano-banana` by id: a registry without it refuses that pipeline's runs at admission
62
+ * (`binary_output_generator_invalid`) rather than degrading, which is the correct disposition for
63
+ * a step naming an integration nobody registered and a loud one to meet on upgrade.
64
+ *
65
+ * Registered ONCE per deployment, on the process that owns the registry. In mothership mode that
66
+ * is the mothership: a node resolves integrations over `/internal/binary-generators` and consults
67
+ * no registry of its own, so the set the pipeline builder offers and the set admission resolves
68
+ * are one set however far behind the node's build has drifted.
69
+ */
70
+ export function binaryGeneratorRegistryWithBuiltins() {
71
+ return registerBuiltinBinaryGenerators(defaultBinaryGeneratorRegistry());
72
+ }
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAA;AAEpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,8EAA8E;AAC9E,oFAAoF;AACpF,kGAAkG;AAClG,uEAAuE;AACvE,mGAAmG;AACnG,iGAAiG;AACjG,2BAA2B;AAC3B,EAAE;AACF,4FAA4F;AAC5F,kGAAkG;AAClG,gGAAgG;AAChG,kFAAkF;AAClF,EAAE;AACF,8FAA8F;AAC9F,8FAA8F;AAC9F,gGAAgG;AAChG,2FAA2F;AAC3F,gGAAgG;AAChG,6FAA6F;AAC7F,iGAAiG;AACjG,2FAA2F;AAC3F,sFAAsF;AACtF,8EAA8E;AAE9E,OAAO,EAGL,qBAAqB,EACrB,eAAe,GAChB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAExE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAoC,CAAC,mBAAmB,CAAC,CAAA;AAE/F;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAC7C,QAAiC,EACjC,UAAU,GAAoC,yBAAyB;IAEvE,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;IAChC,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mCAAmC;IACjD,OAAO,+BAA+B,CAAC,8BAA8B,EAAE,CAAC,CAAA;AAC1E,CAAC"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@cat-factory/binary-generators",
3
+ "version": "0.2.0",
4
+ "description": "The built-in generative binary integrations (Nano Banana, Google's Gemini image models) plus the authoring seam a deployment defines its own with, written entirely through the public BinaryGeneratorRegistry seam: depends only on @cat-factory/kernel + @cat-factory/contracts, never the engine.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/kibertoad/cat-factory.git",
8
+ "directory": "backend/packages/binary-generators"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "type": "module",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "dependencies": {
27
+ "valibot": "^1.4.2",
28
+ "@cat-factory/contracts": "0.317.0",
29
+ "@cat-factory/kernel": "0.305.0"
30
+ },
31
+ "devDependencies": {
32
+ "openapi-types": "^12.1.3",
33
+ "typescript": "7.0.2",
34
+ "vitest": "^4.1.10",
35
+ "@cat-factory/orchestration": "0.276.0",
36
+ "@cat-factory/agents": "0.133.1"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -b tsconfig.build.json",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit",
41
+ "test": "vitest",
42
+ "test:run": "vitest run"
43
+ }
44
+ }