@ggui-ai/project-config 0.1.0-rc.3 → 0.2.0-alpha.3

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/README.md CHANGED
@@ -59,7 +59,7 @@ if (path) {
59
59
 
60
60
  `protocol` MUST match `PROTOCOL_VERSION` exported by `@ggui-ai/protocol` in the version installed.
61
61
 
62
- Optional blocks: `storage` (sessions / vectors / threads via `memory` or `sqlite`), `theme`, `primitives`, `blueprints`, `mcpMounts`, and `operator`. See the exported Zod schema for the full, authoritative shape.
62
+ Optional blocks: `storage` (renders / vectors / threads via `memory` or `sqlite`), `theme`, `primitives`, `blueprints`, `mcpMounts`, and `operator`. See the exported Zod schema for the full, authoritative shape.
63
63
 
64
64
  ## License
65
65
 
package/dist/schema.d.ts CHANGED
@@ -123,9 +123,9 @@ declare const StorageSurfaceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
123
123
  path: z.ZodString;
124
124
  }, z.core.$strict>], "driver">;
125
125
  declare const StorageSchema: z.ZodObject<{
126
- /** Session persistence (events + stack + identity). Absent =
127
- * in-memory (sessions reset on restart). */
128
- sessions: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
126
+ /** Render persistence (events + identity). Absent = in-memory
127
+ * (renders reset on restart). */
128
+ renders: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
129
129
  driver: z.ZodLiteral<"memory">;
130
130
  }, z.core.$strict>, z.ZodObject<{
131
131
  driver: z.ZodLiteral<"sqlite">;
@@ -150,7 +150,7 @@ declare const StorageSchema: z.ZodObject<{
150
150
  * deployment Portal's self-hosted flow talks to). Pair with
151
151
  * `driver: 'sqlite'` + a `path` for durable local storage; the
152
152
  * `@ggui-ai/mcp-server-core/sqlite#SqliteThreadStore` reference
153
- * impl consumes the same file-per-surface layout the sessions/
153
+ * impl consumes the same file-per-surface layout the renders/
154
154
  * vectors drivers already use. */
155
155
  threads: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
156
156
  driver: z.ZodLiteral<"memory">;
@@ -163,13 +163,56 @@ declare const StorageSchema: z.ZodObject<{
163
163
  }, z.core.$strict>], "driver">>;
164
164
  }, z.core.$strict>;
165
165
  /**
166
- * Public type alias for one storage surface's config (sessions or
166
+ * Public type alias for one storage surface's config (renders or
167
167
  * vectors). Callers instantiating adapters from a parsed manifest
168
168
  * pattern-match on `driver`.
169
169
  */
170
170
  export type StorageSurfaceConfig = z.infer<typeof StorageSurfaceSchema>;
171
171
  /** Public type alias for the full storage block. */
172
172
  export type StorageConfig = z.infer<typeof StorageSchema>;
173
+ /**
174
+ * Explicit LLM-route selection for UI generation. The operator
175
+ * declares which `(provider, model)` the OSS `ggui serve` runs
176
+ * against — eliminates the surprise where exporting `OPENAI_API_KEY`
177
+ * silently picks the OpenAI default model.
178
+ *
179
+ * `model` accepts EITHER serialization:
180
+ * - **Canonical** — `provider:model` (e.g. `anthropic:claude-haiku-4-5-20251001`)
181
+ * - **LiteLLM compat** — `provider/model` (e.g. `anthropic/claude-haiku-4-5`)
182
+ *
183
+ * Parsed at schema-load time via `parseAnyLlmRoute` and surfaced
184
+ * as a typed `LlmRoute` for downstream consumers (zero string
185
+ * threading through the dispatch path).
186
+ */
187
+ declare const GenerationSchema: z.ZodObject<{
188
+ /**
189
+ * Typed LLM route — `(provider, model)` parsed at the schema
190
+ * boundary into a discriminated `LlmRoute`. See
191
+ * `docs/principles/model-string-convention.md`.
192
+ */
193
+ model: z.ZodPipe<z.ZodString, z.ZodTransform<{
194
+ provider: "anthropic";
195
+ model: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6" | "claude-opus-4-7" | "claude-opus-4-6";
196
+ } | {
197
+ provider: "openai";
198
+ model: "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
199
+ } | {
200
+ provider: "google";
201
+ model: "gemini-3.5-flash" | "gemini-flash-latest" | "gemini-3.1-flash-lite" | "gemini-3.1-pro-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3-flash-preview";
202
+ } | {
203
+ provider: "bedrock";
204
+ model: (string & {}) | "us.anthropic.claude-haiku-4-5-20251001-v1:0" | "eu.anthropic.claude-haiku-4-5-20251001-v1:0" | "apac.anthropic.claude-haiku-4-5-20251001-v1:0" | "global.anthropic.claude-haiku-4-5-20251001-v1:0" | "us.anthropic.claude-sonnet-4-6" | "eu.anthropic.claude-sonnet-4-6" | "apac.anthropic.claude-sonnet-4-6" | "us.anthropic.claude-opus-4-7" | "eu.anthropic.claude-opus-4-7" | "apac.anthropic.claude-opus-4-7" | "global.anthropic.claude-opus-4-7" | "us.anthropic.claude-opus-4-6-v1";
205
+ } | {
206
+ provider: "openrouter";
207
+ model: (string & {}) | "anthropic/claude-haiku-4.5" | "anthropic/claude-sonnet-4.6" | "anthropic/claude-opus-4.7" | "openai/gpt-5.5" | "openai/gpt-5.5-pro" | "openai/gpt-5.4-mini" | "openai/gpt-5.4-nano" | "google/gemini-3.5-flash" | "google/gemini-3.1-pro-preview" | "google/gemini-3.1-flash-lite" | "x-ai/grok-4.3" | "meta-llama/llama-4-maverick" | "meta-llama/llama-3.3-70b-instruct" | "deepseek/deepseek-v4-pro" | "deepseek/deepseek-r1-0528" | "qwen/qwen3.7-max" | "qwen/qwen3-coder" | "mistralai/mistral-large-2512" | "openai/gpt-oss-120b";
208
+ }, string>>;
209
+ }, z.core.$strict>;
210
+ /**
211
+ * Public type alias for the parsed generation block. `model` is the
212
+ * typed `LlmRoute`, NOT the raw string — schema-side transform fires
213
+ * at parse time so downstream consumers never see the string form.
214
+ */
215
+ export type GenerationConfig = z.infer<typeof GenerationSchema>;
173
216
  /**
174
217
  * Public type alias for the resolved theme selection (post-parse).
175
218
  *
@@ -302,17 +345,26 @@ export declare const GguiJsonV1: z.ZodObject<{
302
345
  */
303
346
  publicEnv: z.ZodOptional<z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodString>>>;
304
347
  /**
305
- * MCP-Apps presentation mode (canvas slice). `'canvas'` makes
306
- * `ggui_new_session` mint a session-scoped iframe
307
- * (`ui://ggui/session/<sessionId>`) and route subsequent pushes
308
- * through the session WebSocket channel instead of returning a
309
- * per-push `ui://ggui/render/<shortCode>` resource. `'inline'`
310
- * (default when omitted) preserves the per-push iframe behavior
311
- * that flagship MCP-Apps hosts already expect.
348
+ * App-default display-mode hint stamped on every `ggui_render` from
349
+ * this app via `_meta.ui.displayMode`. Spec-aligned with MCP App
350
+ * display literals (`'inline' | 'fullscreen' | 'pip'`).
351
+ *
352
+ * Honored by hosts as a PRESENTATION preference — `'fullscreen'`
353
+ * says "render as a main view"; `'inline'` says "stack vertically in
354
+ * the chat log"; `'pip'` says "render as picture-in-picture overlay".
355
+ * The wire mechanism is identical regardless of mode (every push
356
+ * stamps its own `_meta.ui.resourceUri` and every iframe goes
357
+ * through the same runtime mount path); display mode controls ONLY
358
+ * how the host arranges the iframes it mounts. Agents can override
359
+ * per push via `ggui_render.input.displayMode`.
360
+ *
361
+ * Absent ⇒ no per-push hint stamped (host falls back to its own
362
+ * default, typically `'inline'`).
312
363
  */
313
- defaultMcpAppsMode: z.ZodOptional<z.ZodEnum<{
364
+ defaultDisplayMode: z.ZodOptional<z.ZodEnum<{
314
365
  inline: "inline";
315
- canvas: "canvas";
366
+ fullscreen: "fullscreen";
367
+ pip: "pip";
316
368
  }>>;
317
369
  }, z.core.$strict>;
318
370
  /** Operator identity — who's running this server. Optional;
@@ -398,9 +450,9 @@ export declare const GguiJsonV1: z.ZodObject<{
398
450
  * — no surface silently creates a file on disk. See
399
451
  * {@link StorageConfig}. */
400
452
  storage: z.ZodOptional<z.ZodObject<{
401
- /** Session persistence (events + stack + identity). Absent =
402
- * in-memory (sessions reset on restart). */
403
- sessions: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
453
+ /** Render persistence (events + identity). Absent = in-memory
454
+ * (renders reset on restart). */
455
+ renders: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
404
456
  driver: z.ZodLiteral<"memory">;
405
457
  }, z.core.$strict>, z.ZodObject<{
406
458
  driver: z.ZodLiteral<"sqlite">;
@@ -425,7 +477,7 @@ export declare const GguiJsonV1: z.ZodObject<{
425
477
  * deployment Portal's self-hosted flow talks to). Pair with
426
478
  * `driver: 'sqlite'` + a `path` for durable local storage; the
427
479
  * `@ggui-ai/mcp-server-core/sqlite#SqliteThreadStore` reference
428
- * impl consumes the same file-per-surface layout the sessions/
480
+ * impl consumes the same file-per-surface layout the renders/
429
481
  * vectors drivers already use. */
430
482
  threads: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
431
483
  driver: z.ZodLiteral<"memory">;
@@ -437,6 +489,40 @@ export declare const GguiJsonV1: z.ZodObject<{
437
489
  path: z.ZodString;
438
490
  }, z.core.$strict>], "driver">>;
439
491
  }, z.core.$strict>>;
492
+ /**
493
+ * Explicit UI-generation LLM route. Absent + a boot-time provider
494
+ * key resolves → `ggui serve` hard-fails with an actionable error
495
+ * pointing here. Absent + no key → graceful boot (the
496
+ * Connect-Claude card flow handles per-user keys at request time).
497
+ * Present → use this route verbatim; the schema-side
498
+ * `parseAnyLlmRoute` transform yields a typed `LlmRoute` so the
499
+ * dispatch path never sees a string.
500
+ *
501
+ * See {@link GenerationConfig}.
502
+ */
503
+ generation: z.ZodOptional<z.ZodObject<{
504
+ /**
505
+ * Typed LLM route — `(provider, model)` parsed at the schema
506
+ * boundary into a discriminated `LlmRoute`. See
507
+ * `docs/principles/model-string-convention.md`.
508
+ */
509
+ model: z.ZodPipe<z.ZodString, z.ZodTransform<{
510
+ provider: "anthropic";
511
+ model: "claude-haiku-4-5-20251001" | "claude-sonnet-4-6" | "claude-opus-4-7" | "claude-opus-4-6";
512
+ } | {
513
+ provider: "openai";
514
+ model: "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
515
+ } | {
516
+ provider: "google";
517
+ model: "gemini-3.5-flash" | "gemini-flash-latest" | "gemini-3.1-flash-lite" | "gemini-3.1-pro-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3-flash-preview";
518
+ } | {
519
+ provider: "bedrock";
520
+ model: (string & {}) | "us.anthropic.claude-haiku-4-5-20251001-v1:0" | "eu.anthropic.claude-haiku-4-5-20251001-v1:0" | "apac.anthropic.claude-haiku-4-5-20251001-v1:0" | "global.anthropic.claude-haiku-4-5-20251001-v1:0" | "us.anthropic.claude-sonnet-4-6" | "eu.anthropic.claude-sonnet-4-6" | "apac.anthropic.claude-sonnet-4-6" | "us.anthropic.claude-opus-4-7" | "eu.anthropic.claude-opus-4-7" | "apac.anthropic.claude-opus-4-7" | "global.anthropic.claude-opus-4-7" | "us.anthropic.claude-opus-4-6-v1";
521
+ } | {
522
+ provider: "openrouter";
523
+ model: (string & {}) | "anthropic/claude-haiku-4.5" | "anthropic/claude-sonnet-4.6" | "anthropic/claude-opus-4.7" | "openai/gpt-5.5" | "openai/gpt-5.5-pro" | "openai/gpt-5.4-mini" | "openai/gpt-5.4-nano" | "google/gemini-3.5-flash" | "google/gemini-3.1-pro-preview" | "google/gemini-3.1-flash-lite" | "x-ai/grok-4.3" | "meta-llama/llama-4-maverick" | "meta-llama/llama-3.3-70b-instruct" | "deepseek/deepseek-v4-pro" | "deepseek/deepseek-r1-0528" | "qwen/qwen3.7-max" | "qwen/qwen3-coder" | "mistralai/mistral-large-2512" | "openai/gpt-oss-120b";
524
+ }, string>>;
525
+ }, z.core.$strict>>;
440
526
  /**
441
527
  * Default plugin-registry URL for this app.
442
528
  *
@@ -468,7 +554,7 @@ export declare const GguiJsonV1: z.ZodObject<{
468
554
  /**
469
555
  * Local MCP tool mounts — the operator-facing seam for declaring
470
556
  * additional tool surfaces that aggregate onto `/mcp` alongside
471
- * ggui-native tools (`ggui_push`, etc.). Each entry is a module
557
+ * ggui-native tools (`ggui_render`, etc.). Each entry is a module
472
558
  * specifier:
473
559
  *
474
560
  * - Relative paths (`./path/to/mount.js`, `../local/mcp.mjs`)
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyGxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,cAAc;IAClB;sDACkD;;IAElD;8CAC0C;;IAI1C;;uDAEmD;;IAEnD;;;8CAG0C;;kBAI1C,CAAC;AAEH,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AA+H5D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;oBAI5B,CAAC;AAqEH;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB;;;;IAZxB;;oDAEgD;;8BAahD,CAAC;AAEH,QAAA,MAAM,aAAa;IACjB;gDAC4C;;;;;QAnB5C;;wDAEgD;;;IAmBhD;gDAC4C;;;;;QAtB5C;;wDAEgD;;;IAsBhD;;;;;;sCAMkC;;;;;QA9BlC;;wDAEgD;;;kBA8BhD,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU;IACrB;uEACmE;;IAOnE,iDAAiD;;IAKjD,oBAAoB;;;;QA9XpB;;;;;;;;;;;;;;;WAeG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAIH;;;;;;;;;;;;;;;;;WAiBG;;QAEH;;;;;;;;WAQG;;;;;;IAqVH;;;kDAG8C;;QA/T9C;0DACkD;;QAElD;kDAC0C;;QAI1C;;2DAEmD;;QAEnD;;;kDAG0C;;;IAqT1C,+DAA+D;;QAhS/D;sDAC8C;;;IAoS9C;qEACiE;;;;;IAQjE;;;;;;;;;;;;;;;;;;OAkBG;;;;;;;;;;;;;;;IAKH;0DACsD;;QAhRtD;;;;;kDAK0C;;;IAgR1C;;;;gCAI4B;;QA5H5B;oDAC4C;;;;;YAnB5C;;4DAEgD;;;QAmBhD;oDAC4C;;;;;YAtB5C;;4DAEgD;;;QAsBhD;;;;;;0CAMkC;;;;;YA9BlC;;4DAEgD;;;;IAiJhD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;;IAQH;;;;;;;;;;;;;;;;;;;;;;;;6EAwByE;;kBASzE,CAAC;AAEH,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAEzC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmHxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,QAAA,MAAM,cAAc;IAClB;sDACkD;;IAElD;8CAC0C;;IAI1C;;uDAEmD;;IAEnD;;;8CAG0C;;kBAI1C,CAAC;AAEH,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AA+H5D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;oBAI5B,CAAC;AAqEH;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB;;;;IAZxB;;oDAEgD;;8BAahD,CAAC;AAEH,QAAA,MAAM,aAAa;IACjB;qCACiC;;;;;QAnBjC;;wDAEgD;;;IAmBhD;gDAC4C;;;;;QAtB5C;;wDAEgD;;;IAsBhD;;;;;;sCAMkC;;;;;QA9BlC;;wDAEgD;;;kBA8BhD,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,gBAAgB;IACpB;;;;OAIG;;;;;;;;;;;;;;;;;kBAoBH,CAAC;AAEH;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU;IACrB;uEACmE;;IAOnE,iDAAiD;;IAKjD,oBAAoB;;;;QAtbpB;;;;;;;;;;;;;;;WAeG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAIH;;;;;;;;;;;;;;;;;WAiBG;;QAEH;;;;;;;;;;;;;;;;WAgBG;;;;;;;IAqYH;;;kDAG8C;;QA/W9C;0DACkD;;QAElD;kDAC0C;;QAI1C;;2DAEmD;;QAEnD;;;kDAG0C;;;IAqW1C,+DAA+D;;QAhV/D;sDAC8C;;;IAoV9C;qEACiE;;;;;IAQjE;;;;;;;;;;;;;;;;;;OAkBG;;;;;;;;;;;;;;;IAKH;0DACsD;;QAhUtD;;;;;kDAK0C;;;IAgU1C;;;;gCAI4B;;QA5K5B;yCACiC;;;;;YAnBjC;;4DAEgD;;;QAmBhD;oDAC4C;;;;;YAtB5C;;4DAEgD;;;QAsBhD;;;;;;0CAMkC;;;;;YA9BlC;;4DAEgD;;;;IAiMhD;;;;;;;;;;OAUG;;QAlJH;;;;WAIG;;;;;;;;;;;;;;;;;;IAmJH;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;;IAQH;;;;;;;;;;;;;;;;;;;;;;;;6EAwByE;;kBASzE,CAAC;AAEH,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,UAAU,CAAC,SAAS,CAAC,CAEzC"}
package/dist/schema.js CHANGED
@@ -41,7 +41,7 @@
41
41
  * schema change, not a drive-by.
42
42
  */
43
43
  import { z } from 'zod';
44
- import { appPublicEnvSchema, strictGadgetDescriptorSchema, } from '@ggui-ai/protocol';
44
+ import { appPublicEnvSchema, parseAnyLlmRoute, strictGadgetDescriptorSchema, } from '@ggui-ai/protocol';
45
45
  /**
46
46
  * App slug — URL-safe identifier. 2–64 chars, lowercase
47
47
  * alphanumeric + hyphens, no leading/trailing hyphen. Used as a
@@ -123,15 +123,23 @@ const AppSchema = z.strictObject({
123
123
  */
124
124
  publicEnv: appPublicEnvSchema.optional(),
125
125
  /**
126
- * MCP-Apps presentation mode (canvas slice). `'canvas'` makes
127
- * `ggui_new_session` mint a session-scoped iframe
128
- * (`ui://ggui/session/<sessionId>`) and route subsequent pushes
129
- * through the session WebSocket channel instead of returning a
130
- * per-push `ui://ggui/render/<shortCode>` resource. `'inline'`
131
- * (default when omitted) preserves the per-push iframe behavior
132
- * that flagship MCP-Apps hosts already expect.
126
+ * App-default display-mode hint stamped on every `ggui_render` from
127
+ * this app via `_meta.ui.displayMode`. Spec-aligned with MCP App
128
+ * display literals (`'inline' | 'fullscreen' | 'pip'`).
129
+ *
130
+ * Honored by hosts as a PRESENTATION preference — `'fullscreen'`
131
+ * says "render as a main view"; `'inline'` says "stack vertically in
132
+ * the chat log"; `'pip'` says "render as picture-in-picture overlay".
133
+ * The wire mechanism is identical regardless of mode (every push
134
+ * stamps its own `_meta.ui.resourceUri` and every iframe goes
135
+ * through the same runtime mount path); display mode controls ONLY
136
+ * how the host arranges the iframes it mounts. Agents can override
137
+ * per push via `ggui_render.input.displayMode`.
138
+ *
139
+ * Absent ⇒ no per-push hint stamped (host falls back to its own
140
+ * default, typically `'inline'`).
133
141
  */
134
- defaultMcpAppsMode: z.enum(['inline', 'canvas']).optional(),
142
+ defaultDisplayMode: z.enum(['inline', 'fullscreen', 'pip']).optional(),
135
143
  });
136
144
  /**
137
145
  * Operator identity — who's running this server. Surfaced on the
@@ -318,9 +326,9 @@ export const ThemeConfigSchema = z.union([
318
326
  * adapters; no surface ever silently creates a file on disk without
319
327
  * the operator declaring it here.
320
328
  *
321
- * ## Why `{sessions, vectors}` together
329
+ * ## Why `{renders, vectors}` together
322
330
  *
323
- * Sessions + vectors are the two substantial persistence surfaces
331
+ * Renders + vectors are the two substantial persistence surfaces
324
332
  * the OSS server honors today. Shipping them in the same block from
325
333
  * v1 avoids a second schema churn when vectors or a future store
326
334
  * (e.g. threads) lands — there's one `storage` shape and each known
@@ -333,14 +341,14 @@ export const ThemeConfigSchema = z.union([
333
341
  * Explicit driver makes the choice visible at a glance — both to
334
342
  * the operator reading the file and to future adapters that land
335
343
  * against the same interfaces (`postgres`, `redis`, `libsql`, …).
336
- * A flat `sessions: "./path.sqlite"` shape would bake sqlite into
344
+ * A flat `renders: "./path.sqlite"` shape would bake sqlite into
337
345
  * the schema and force a breaking redesign the first time another
338
346
  * driver landed.
339
347
  *
340
348
  * ## Why per-surface `path` instead of a shared root
341
349
  *
342
- * Sessions + vectors can (and typically should) live in separate
343
- * files: session data is small + hot-path; vector data is large +
350
+ * Renders + vectors can (and typically should) live in separate
351
+ * files: render data is small + hot-path; vector data is large +
344
352
  * append-heavy. Coupling them into one file conflates two different
345
353
  * IO profiles. Operators who want co-located storage can point both
346
354
  * paths at the same file — but the schema doesn't force that.
@@ -360,7 +368,7 @@ export const ThemeConfigSchema = z.union([
360
368
  *
361
369
  * ## Strictness
362
370
  *
363
- * Root `storage` is strict — unknown surface keys (`sesions: …`,
371
+ * Root `storage` is strict — unknown surface keys (`rendrs: …`,
364
372
  * `vectros: …`, `blueprints: …`) fail parse. Per-surface shapes
365
373
  * are discriminated on `driver`; each variant is strict so typos
366
374
  * like `{driver: 'sqlite', paht: './x'}` fail immediately.
@@ -386,9 +394,9 @@ const StorageSurfaceSchema = z.discriminatedUnion('driver', [
386
394
  StorageSqliteSchema,
387
395
  ]);
388
396
  const StorageSchema = z.strictObject({
389
- /** Session persistence (events + stack + identity). Absent =
390
- * in-memory (sessions reset on restart). */
391
- sessions: StorageSurfaceSchema.optional(),
397
+ /** Render persistence (events + identity). Absent = in-memory
398
+ * (renders reset on restart). */
399
+ renders: StorageSurfaceSchema.optional(),
392
400
  /** Vector index persistence (RAG, blueprint embeddings). Absent =
393
401
  * in-memory (index rebuilds on restart). */
394
402
  vectors: StorageSurfaceSchema.optional(),
@@ -397,10 +405,49 @@ const StorageSchema = z.strictObject({
397
405
  * deployment Portal's self-hosted flow talks to). Pair with
398
406
  * `driver: 'sqlite'` + a `path` for durable local storage; the
399
407
  * `@ggui-ai/mcp-server-core/sqlite#SqliteThreadStore` reference
400
- * impl consumes the same file-per-surface layout the sessions/
408
+ * impl consumes the same file-per-surface layout the renders/
401
409
  * vectors drivers already use. */
402
410
  threads: StorageSurfaceSchema.optional(),
403
411
  });
412
+ /**
413
+ * Explicit LLM-route selection for UI generation. The operator
414
+ * declares which `(provider, model)` the OSS `ggui serve` runs
415
+ * against — eliminates the surprise where exporting `OPENAI_API_KEY`
416
+ * silently picks the OpenAI default model.
417
+ *
418
+ * `model` accepts EITHER serialization:
419
+ * - **Canonical** — `provider:model` (e.g. `anthropic:claude-haiku-4-5-20251001`)
420
+ * - **LiteLLM compat** — `provider/model` (e.g. `anthropic/claude-haiku-4-5`)
421
+ *
422
+ * Parsed at schema-load time via `parseAnyLlmRoute` and surfaced
423
+ * as a typed `LlmRoute` for downstream consumers (zero string
424
+ * threading through the dispatch path).
425
+ */
426
+ const GenerationSchema = z.strictObject({
427
+ /**
428
+ * Typed LLM route — `(provider, model)` parsed at the schema
429
+ * boundary into a discriminated `LlmRoute`. See
430
+ * `docs/principles/model-string-convention.md`.
431
+ */
432
+ model: z
433
+ .string()
434
+ .min(1, 'generation.model must not be empty')
435
+ .transform((raw, ctx) => {
436
+ const parsed = parseAnyLlmRoute(raw);
437
+ if (!parsed) {
438
+ ctx.addIssue({
439
+ code: 'custom',
440
+ message: `generation.model "${raw}" is not a recognized LlmRoute. ` +
441
+ `Use the canonical form "provider:model" (e.g. ` +
442
+ `"anthropic:claude-haiku-4-5-20251001") or the LiteLLM ` +
443
+ `form "provider/model" (e.g. "anthropic/claude-haiku-4-5"). ` +
444
+ `See docs/principles/model-string-convention.md.`,
445
+ });
446
+ return z.NEVER;
447
+ }
448
+ return parsed;
449
+ }),
450
+ });
404
451
  /**
405
452
  * The authoritative v1 schema. `z.infer<typeof GguiJsonV1>` yields
406
453
  * the static TypeScript type. A valid document round-trips cleanly:
@@ -466,7 +513,19 @@ export const GguiJsonV1 = z.strictObject({
466
513
  * default). Present means the operator has opted in per surface
467
514
  * — no surface silently creates a file on disk. See
468
515
  * {@link StorageConfig}. */
469
- storage: StorageSchema.optional().describe('Per-surface storage adapter declarations. Absent = every persistence surface (sessions, threads, vectors, kv) stays in-memory (zero-config OSS default). Present = opt-in per surface; no surface silently creates a file on disk.'),
516
+ storage: StorageSchema.optional().describe('Per-surface storage adapter declarations. Absent = every persistence surface (renders, threads, vectors, kv) stays in-memory (zero-config OSS default). Present = opt-in per surface; no surface silently creates a file on disk.'),
517
+ /**
518
+ * Explicit UI-generation LLM route. Absent + a boot-time provider
519
+ * key resolves → `ggui serve` hard-fails with an actionable error
520
+ * pointing here. Absent + no key → graceful boot (the
521
+ * Connect-Claude card flow handles per-user keys at request time).
522
+ * Present → use this route verbatim; the schema-side
523
+ * `parseAnyLlmRoute` transform yields a typed `LlmRoute` so the
524
+ * dispatch path never sees a string.
525
+ *
526
+ * See {@link GenerationConfig}.
527
+ */
528
+ generation: GenerationSchema.optional().describe('Explicit UI-generation LLM route. `model` accepts canonical `provider:model` or LiteLLM `provider/model` form. Absent + a boot key resolves → server hard-fails (operator must pick a model). Absent + no key → graceful per-user fallback flow.'),
470
529
  /**
471
530
  * Default plugin-registry URL for this app.
472
531
  *
@@ -501,7 +560,7 @@ export const GguiJsonV1 = z.strictObject({
501
560
  /**
502
561
  * Local MCP tool mounts — the operator-facing seam for declaring
503
562
  * additional tool surfaces that aggregate onto `/mcp` alongside
504
- * ggui-native tools (`ggui_push`, etc.). Each entry is a module
563
+ * ggui-native tools (`ggui_render`, etc.). Each entry is a module
505
564
  * specifier:
506
565
  *
507
566
  * - Relative paths (`./path/to/mount.js`, `../local/mcp.mjs`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/project-config",
3
- "version": "0.1.0-rc.3",
3
+ "version": "0.2.0-alpha.3",
4
4
  "description": "Schema and loader for `ggui.json` — the portable app/agent manifest for the ggui protocol. Vendor-neutral. Consumed by the `ggui` CLI and any host that reconstitutes an agent from the manifest.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -33,8 +33,8 @@
33
33
  "dependencies": {
34
34
  "tinyglobby": "^0.2.16",
35
35
  "zod": "^4.3.6",
36
- "@ggui-ai/protocol": "0.1.0-rc.3",
37
- "@ggui-ai/design": "0.1.0-rc.3"
36
+ "@ggui-ai/design": "0.2.0-alpha.3",
37
+ "@ggui-ai/protocol": "0.2.0-alpha.3"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^24.0.0",