@ggui-ai/protocol 0.13.0 → 0.15.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/dist/envelope-adapters.d.ts +1 -14
- package/dist/envelope-adapters.d.ts.map +1 -1
- package/dist/envelope-adapters.js +17 -1
- package/dist/envelopes/render-refusal.d.ts +49 -0
- package/dist/envelopes/render-refusal.d.ts.map +1 -0
- package/dist/envelopes/render-refusal.js +12 -0
- package/dist/gadgets/resolve-app-gadgets.d.ts +1 -1
- package/dist/gadgets/stdlib-gadgets.d.ts +2 -2
- package/dist/gadgets/stdlib-gadgets.js +1 -1
- package/dist/index.d.ts +67 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/integrations/mcp-apps.d.ts +4 -10
- package/dist/integrations/mcp-apps.d.ts.map +1 -1
- package/dist/schemas/app-theme.d.ts +5 -0
- package/dist/schemas/app-theme.d.ts.map +1 -1
- package/dist/schemas/app-theme.js +23 -0
- package/dist/schemas/blueprint.d.ts +2 -2
- package/dist/schemas/data-contract.d.ts +12 -30
- package/dist/schemas/data-contract.d.ts.map +1 -1
- package/dist/schemas/data-contract.js +6 -27
- package/dist/schemas/interface-context.d.ts +44 -0
- package/dist/schemas/interface-context.d.ts.map +1 -0
- package/dist/schemas/interface-context.js +22 -0
- package/dist/schemas/invoke.d.ts +1 -1
- package/dist/schemas/invoke.js +1 -1
- package/dist/schemas/mcp.d.ts +636 -69
- package/dist/schemas/mcp.d.ts.map +1 -1
- package/dist/schemas/mcp.js +554 -47
- package/dist/schemas/public-env-key.d.ts +26 -0
- package/dist/schemas/public-env-key.d.ts.map +1 -0
- package/dist/schemas/public-env-key.js +25 -0
- package/dist/schemas/render-input-envelope.d.ts +20 -0
- package/dist/schemas/render-input-envelope.d.ts.map +1 -0
- package/dist/schemas/render-input-envelope.js +56 -0
- package/dist/schemas/runtime-telemetry-limits.d.ts +11 -0
- package/dist/schemas/runtime-telemetry-limits.d.ts.map +1 -0
- package/dist/schemas/runtime-telemetry-limits.js +10 -0
- package/dist/transport/websocket.d.ts +5 -5
- package/dist/types/auth.d.ts +1 -1
- package/dist/types/contract-inference.d.ts +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/ggui-session-event.d.ts +4 -5
- package/dist/types/ggui-session-event.d.ts.map +1 -1
- package/dist/types/ggui-session-event.js +0 -37
- package/dist/types/host-context.d.ts +18 -17
- package/dist/types/host-context.d.ts.map +1 -1
- package/dist/types/host-context.js +7 -0
- package/dist/types/interface-context.d.ts +1 -1
- package/dist/types/invoke.d.ts +1 -1
- package/dist/types/lifecycle.d.ts +1 -1
- package/dist/types/live-channel.d.ts +2 -2
- package/dist/types/llm-route.d.ts +2 -2
- package/dist/types/llm-route.d.ts.map +1 -1
- package/dist/types/llm-route.js +11 -3
- package/dist/types/llm.d.ts +397 -7
- package/dist/types/llm.d.ts.map +1 -1
- package/dist/types/llm.js +111 -10
- package/dist/types/mcp.d.ts +31 -111
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/types/mcp.js +7 -0
- package/dist/types/readonly.d.ts +11 -0
- package/dist/types/readonly.d.ts.map +1 -0
- package/dist/types/readonly.js +1 -0
- package/dist/types/refusal-codes.d.ts +340 -0
- package/dist/types/refusal-codes.d.ts.map +1 -0
- package/dist/types/refusal-codes.js +346 -0
- package/dist/types/render.d.ts +9 -8
- package/dist/types/render.d.ts.map +1 -1
- package/dist/types/ui-generator.d.ts +1 -1
- package/dist/validation/ajv-runtime.d.ts +2 -2
- package/dist/validation/contract-validator.d.ts +6 -6
- package/dist/validation/cross-references.d.ts +2 -2
- package/dist/validation/hygiene-rules.d.ts +1 -1
- package/dist/validation/name-invariants.d.ts +2 -2
- package/dist/validation/reserved-channels.d.ts +1 -1
- package/dist/validation/schema-compat-invariants.d.ts +2 -2
- package/dist/validation/schema-meta-validation.d.ts +1 -1
- package/dist/version.d.ts +201 -5
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +200 -4
- package/dist/wire.d.ts +45 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +44 -0
- package/package.json +13 -3
package/dist/schemas/mcp.js
CHANGED
|
@@ -25,24 +25,10 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { z } from 'zod';
|
|
27
27
|
import { blueprintDraftSchema, handshakeSuggestionSchema, } from './handshake-suggestion.js';
|
|
28
|
-
import { dataContractSchema } from './data-contract.js';
|
|
29
|
-
import { blueprintVarianceSchema } from './blueprint.js';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
width: z.number(),
|
|
33
|
-
height: z.number(),
|
|
34
|
-
});
|
|
35
|
-
export const interfaceContextSchema = z.object({
|
|
36
|
-
viewport: viewportSchema,
|
|
37
|
-
platform: z.enum(['web', 'mobile', 'desktop']),
|
|
38
|
-
deviceType: z.enum(['phone', 'tablet', 'desktop']),
|
|
39
|
-
orientation: z.enum(['portrait', 'landscape']),
|
|
40
|
-
devicePixelRatio: z.number().optional(),
|
|
41
|
-
touchPrimary: z.boolean().optional(),
|
|
42
|
-
shellType: z.enum(['chat', 'fullscreen', 'spatial']).optional(),
|
|
43
|
-
colorScheme: z.enum(['light', 'dark']).optional(),
|
|
44
|
-
reducedMotion: z.boolean().optional(),
|
|
45
|
-
}).passthrough();
|
|
28
|
+
import { dataContractSchema, jsonObjectSchema, jsonValueSchema } from './data-contract.js';
|
|
29
|
+
import { blueprintVarianceSchema, blueprintSourceSchema } from './blueprint.js';
|
|
30
|
+
import { MCP_ENDPOINT_REFUSAL_CODES, REFUSAL_RETRIES, RENDER_GATE_REFUSAL_CODES, } from '../types/refusal-codes.js';
|
|
31
|
+
import { RUNTIME_TELEMETRY_MAX_EVENTS } from './runtime-telemetry-limits.js';
|
|
46
32
|
// ── Wired Tool Input Shapes ──
|
|
47
33
|
//
|
|
48
34
|
// Raw zod shapes for the non-lifecycle tools. The SHAPE is the canonical
|
|
@@ -325,9 +311,14 @@ export const renderInputShape = {
|
|
|
325
311
|
/**
|
|
326
312
|
* Typed `infra` envelope. Today carries one field (`model`); future
|
|
327
313
|
* expansion (temperature, max_tokens, provider hints) lands here
|
|
328
|
-
* additively. `model` MUST
|
|
329
|
-
*
|
|
330
|
-
*
|
|
314
|
+
* additively. `model` MUST parse as a model route in either wire form —
|
|
315
|
+
* canonical `provider:model` or LiteLLM `provider/model` (aliases resolve
|
|
316
|
+
* in both). The mechanism is `renderInputEnvelopeSchema`
|
|
317
|
+
* (`schemas/render-input-envelope.ts`), which the render handler parses
|
|
318
|
+
* BEFORE its pre-generation gate; this registered shape stays
|
|
319
|
+
* parser-free by design so a browser bundle never carries the route
|
|
320
|
+
* tables (ggui#818). A bound generator may also accept generator-specific
|
|
321
|
+
* prefixes for alternate transports.
|
|
331
322
|
*
|
|
332
323
|
* Strict — extra keys at `infra.*` are not silently dropped, so a
|
|
333
324
|
* typo (`infra.modelId`) surfaces as a clear zod path instead of a
|
|
@@ -339,7 +330,7 @@ export const renderInputShape = {
|
|
|
339
330
|
.string()
|
|
340
331
|
.min(1)
|
|
341
332
|
.optional()
|
|
342
|
-
.describe('
|
|
333
|
+
.describe('Model route in either wire form — canonical `anthropic:claude-haiku-4-5-20251001` or LiteLLM `anthropic/claude-haiku-4-5` (aliases resolve in both); generator-specific prefixes (e.g. `bedrock/...`) route to that transport. A value that parses in neither form fails the handler input parse at `infra.model`, before any pre-generation gate; `model_not_in_tier` is only ever a well-formed route with no rate row on the effective tier.'),
|
|
343
334
|
})
|
|
344
335
|
.strict()
|
|
345
336
|
.optional(),
|
|
@@ -445,6 +436,177 @@ export const renderErrorSchema = z.object({
|
|
|
445
436
|
.string()
|
|
446
437
|
.describe('Human-readable failure detail — fold into the next attempt or surface to the operator.'),
|
|
447
438
|
});
|
|
439
|
+
/**
|
|
440
|
+
* The three outcomes a `ggui_render` result can report (SPEC §7.1,
|
|
441
|
+
* ggui#786). A reader branches on this rather than guessing from which
|
|
442
|
+
* fields happen to be present:
|
|
443
|
+
*
|
|
444
|
+
* - `rendered` — the render ran and produced an interface. The
|
|
445
|
+
* identity fields (`sessionId`, `action`, `contractHash`,
|
|
446
|
+
* `blueprintId`, `variantKey`, `cache`) are all present.
|
|
447
|
+
* - `failed` — generation RAN and did not produce a component. The
|
|
448
|
+
* error session IS committed, so the identity fields are present
|
|
449
|
+
* and `error` carries the classification. The handshake is
|
|
450
|
+
* consumed.
|
|
451
|
+
* - `refused` — the deployment declined the call BEFORE it did any
|
|
452
|
+
* work: no state read, nothing committed, no spend. (The SDK has
|
|
453
|
+
* already checked the call against the declared `inputSchema` —
|
|
454
|
+
* the claim is nothing READ, not nothing validated.) The identity
|
|
455
|
+
* fields are structurally ABSENT and `refusal` carries the whole
|
|
456
|
+
* story; the handshake is INTACT, so the same id is valid on a
|
|
457
|
+
* retry.
|
|
458
|
+
*
|
|
459
|
+
* Declared HERE rather than in `types/mcp.ts` (which re-exports the
|
|
460
|
+
* inferred type) so `types/render.ts` can reference it without a
|
|
461
|
+
* type-only import cycle through `types/mcp.ts` — same convention as
|
|
462
|
+
* {@link renderErrorCodeSchema}.
|
|
463
|
+
*/
|
|
464
|
+
export const renderOutcomeSchema = z.enum(['rendered', 'failed', 'refused']);
|
|
465
|
+
/**
|
|
466
|
+
* In-result PRE-GENERATION REFUSAL marker (SPEC §7.1's refused arm,
|
|
467
|
+
* ggui#786). Present iff `outcome: 'refused'`.
|
|
468
|
+
*
|
|
469
|
+
* `code` draws from the closed render-gate subset of
|
|
470
|
+
* `PRE_GENERATION_REFUSAL_CODES` — a code that is not registered fails
|
|
471
|
+
* this enum at the transport, loudly. It is a bug, never a wire state.
|
|
472
|
+
*
|
|
473
|
+
* An agent MUST NOT auto-retry an `after-fix` refusal whose registry
|
|
474
|
+
* row names a `fixBy` other than `caller`: the fix belongs to the app's
|
|
475
|
+
* owner, the tenant, or the operator, and retrying does not perform it.
|
|
476
|
+
*/
|
|
477
|
+
/**
|
|
478
|
+
* The refusal projection every surface shares — what a client acts on:
|
|
479
|
+
* the diagnostic, the one recovery step, and how the call becomes
|
|
480
|
+
* possible again. Defined ONCE and spread into each surface's envelope
|
|
481
|
+
* (the render gate's {@link renderRefusalSchema}, the per-app endpoint's
|
|
482
|
+
* {@link transportRefusalSchema}); the `code` enum is per surface,
|
|
483
|
+
* derived from the registry. `fixBy` never travels — it is a registry
|
|
484
|
+
* attribute a client reads by `code`.
|
|
485
|
+
*/
|
|
486
|
+
const refusalProjectionFields = {
|
|
487
|
+
message: z
|
|
488
|
+
.string()
|
|
489
|
+
.describe('Precise diagnostic — what was checked, and against what. Surface it to the operator; do not parse it.'),
|
|
490
|
+
fix: z
|
|
491
|
+
.string()
|
|
492
|
+
.describe('The one recovery step, addressed to the party that can take it. Retry the same call only when that party is the caller.'),
|
|
493
|
+
retry: z
|
|
494
|
+
.enum(REFUSAL_RETRIES)
|
|
495
|
+
.describe("How the call becomes possible again. 'after-fix': a named party acts and the same call then succeeds. 'next-period': time restores it at the next period boundary. 'later': transient — retry after a short delay. 'never': no caller action restores it under this identity."),
|
|
496
|
+
};
|
|
497
|
+
export const renderRefusalSchema = z.object({
|
|
498
|
+
code: z
|
|
499
|
+
.enum(RENDER_GATE_REFUSAL_CODES)
|
|
500
|
+
.describe("Registered refusal state. Look the code up in the protocol's refusal registry for its retry class and which party can act; the accompanying `fix` names the one recovery step."),
|
|
501
|
+
...refusalProjectionFields,
|
|
502
|
+
handshake: z
|
|
503
|
+
.literal('intact')
|
|
504
|
+
.describe('The handshake was NOT consumed — nothing was read. The same handshakeId is valid on a retry.'),
|
|
505
|
+
balanceCentsAtCheck: z
|
|
506
|
+
.number()
|
|
507
|
+
.int()
|
|
508
|
+
.optional()
|
|
509
|
+
.describe('Present only when the refusing check read a balance: its value at the moment of the check.'),
|
|
510
|
+
});
|
|
511
|
+
/**
|
|
512
|
+
* A refusal typed on the per-app MCP endpoint's authorization
|
|
513
|
+
* (ggui#825) — the registry projection WITHOUT the render-only fields:
|
|
514
|
+
* no `handshake` (nothing was handed), no `balanceCentsAtCheck`. Strict:
|
|
515
|
+
* a render-only field here is a bug, never a wire state. `code` draws
|
|
516
|
+
* from {@link MCP_ENDPOINT_REFUSAL_CODES} — today exactly
|
|
517
|
+
* `app_deprovisioned`, the one refusal with a tenant-side fix and
|
|
518
|
+
* therefore the one that MUST be legible where a deleted app and a bad
|
|
519
|
+
* credential would otherwise look alike.
|
|
520
|
+
*/
|
|
521
|
+
export const transportRefusalSchema = z.strictObject({
|
|
522
|
+
code: z
|
|
523
|
+
.enum(MCP_ENDPOINT_REFUSAL_CODES)
|
|
524
|
+
.describe("Registered refusal state on the per-app endpoint. Look the code up in the protocol's refusal registry for its retry class and which party can act."),
|
|
525
|
+
...refusalProjectionFields,
|
|
526
|
+
});
|
|
527
|
+
/**
|
|
528
|
+
* The JSON-RPC error object a per-app MCP endpoint answers with when it
|
|
529
|
+
* refuses a request for a typed reason (ggui#825, codes ruled in
|
|
530
|
+
* ggui#836): HTTP 403, `code` `-32003` (`APP_NOT_FOUND` — the endpoint
|
|
531
|
+
* no longer serves this app, the same reading ggui's embed host gives a
|
|
532
|
+
* proxy 403) and `message` `App not found`, plus `data.refusal`, which
|
|
533
|
+
* makes it legible. `data` is strict: it carries the refusal and nothing
|
|
534
|
+
* else. An authorization failure that is not a registry state answers
|
|
535
|
+
* HTTP 403 with `-32001` (`UNAUTHORIZED`) and NO `data` — the three
|
|
536
|
+
* untyped arms stay indistinguishable among themselves by contract:
|
|
537
|
+
* naming any of them would say which is true. A first-party server
|
|
538
|
+
* never chooses `-32000`: it is the SDK client's `ConnectionClosed`, so
|
|
539
|
+
* a bare 403 and a dropped socket would share a number.
|
|
540
|
+
*/
|
|
541
|
+
export const transportRefusalErrorSchema = z.strictObject({
|
|
542
|
+
code: z.literal(-32003),
|
|
543
|
+
message: z.literal('App not found'),
|
|
544
|
+
data: z.strictObject({ refusal: transportRefusalSchema }),
|
|
545
|
+
});
|
|
546
|
+
/**
|
|
547
|
+
* The COMPLETE structuredContent of a refused tool result — the whole
|
|
548
|
+
* payload, not a slice of it. Strict on purpose: a refusal commits
|
|
549
|
+
* nothing, so ANY other key (a `sessionId`, a `resourceUri`, an
|
|
550
|
+
* `error`) means the projection leaked state that does not exist.
|
|
551
|
+
*
|
|
552
|
+
* Today `ggui_render` is the only tool that carries a refusing gate;
|
|
553
|
+
* {@link renderOutputSchema} delegates its refused arm here rather than
|
|
554
|
+
* restating the rules, so there is exactly ONE declaration of them.
|
|
555
|
+
*
|
|
556
|
+
* NOT reusable verbatim by a second refusing tool, despite the strict
|
|
557
|
+
* shape reading as generic: {@link renderRefusalSchema} REQUIRES
|
|
558
|
+
* `handshake: 'intact'`, and that field is render-only by
|
|
559
|
+
* construction — a mutation consumes no handshake, so it has nothing
|
|
560
|
+
* to report intact. A mutation arm therefore lands WITH its first
|
|
561
|
+
* emitter, sharing the facts this envelope carries (`code`, `message`,
|
|
562
|
+
* `fix`, `retry`, one closed registry) and carrying no `handshake`
|
|
563
|
+
* field at all (ggui#798).
|
|
564
|
+
*/
|
|
565
|
+
export const refusedOutputSchema = z.strictObject({
|
|
566
|
+
outcome: z.literal('refused'),
|
|
567
|
+
refusal: renderRefusalSchema,
|
|
568
|
+
});
|
|
569
|
+
/**
|
|
570
|
+
* The refused arm's presence rule: the WHOLE payload must be
|
|
571
|
+
* {@link refusedOutputSchema}. Delegating here rather than restating
|
|
572
|
+
* the rule inline keeps one declaration of the RENDER refused
|
|
573
|
+
* envelope. A mutation arm cannot delegate to it — that schema
|
|
574
|
+
* REQUIRES `handshake: 'intact'` — so it mints its own with its first
|
|
575
|
+
* emitter, sharing the facts and carrying no `handshake` (ggui#798).
|
|
576
|
+
*/
|
|
577
|
+
function refineRefusedArm(value, ctx) {
|
|
578
|
+
if (refusedOutputSchema.safeParse(value).success)
|
|
579
|
+
return;
|
|
580
|
+
ctx.addIssue({
|
|
581
|
+
code: 'custom',
|
|
582
|
+
message: "outcome 'refused' MUST carry a registered `refusal` and nothing else — a refusal commits nothing, so no identity field and no `error` may appear beside it.",
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Report a field the committed arms (`rendered` / `failed`) require but
|
|
587
|
+
* the payload omits. The fields are optional at the schema level only
|
|
588
|
+
* to make room for the refused arm; demoting them must not weaken the
|
|
589
|
+
* committed arms, which is what this restores.
|
|
590
|
+
*/
|
|
591
|
+
function requireOnCommittedArm(present, field, outcome, ctx) {
|
|
592
|
+
if (present)
|
|
593
|
+
return;
|
|
594
|
+
ctx.addIssue({
|
|
595
|
+
code: 'custom',
|
|
596
|
+
path: [field],
|
|
597
|
+
message: `outcome '${outcome}' MUST carry \`${field}\` — a committed render reports its full identity.`,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/** `refusal` rides the refused arm only. */
|
|
601
|
+
function rejectRefusalOnCommittedArm(refusal, outcome, ctx) {
|
|
602
|
+
if (refusal === undefined)
|
|
603
|
+
return;
|
|
604
|
+
ctx.addIssue({
|
|
605
|
+
code: 'custom',
|
|
606
|
+
path: ['refusal'],
|
|
607
|
+
message: `\`refusal\` is present only on outcome 'refused', not '${outcome}'.`,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
448
610
|
/**
|
|
449
611
|
* Canonical failure codes for a `resources/read` on a render locator
|
|
450
612
|
* (`ui://ggui/render/{sessionId}/{blueprintKey}`). Closed enum.
|
|
@@ -498,9 +660,10 @@ export const resourceReadErrorSchema = z.object({
|
|
|
498
660
|
.describe('Extra diagnostic context for operators. Dropped on NOT_FOUND so a denied read cannot be told apart from a miss.'),
|
|
499
661
|
});
|
|
500
662
|
/**
|
|
501
|
-
* Wire-output shape
|
|
502
|
-
*
|
|
503
|
-
*
|
|
663
|
+
* Wire-output shape. `outcome` is the discriminant and the only
|
|
664
|
+
* unconditionally required field; which of the others exist follows
|
|
665
|
+
* from it, per the THREE OUTCOMES section below — that section is the
|
|
666
|
+
* single summary of this shape, so do not restate it here.
|
|
504
667
|
* The handler carries `shortCode`, `codeReady`, `handshakeId`,
|
|
505
668
|
* `decision`, `contract`, `codeUrl`, `codeHash`
|
|
506
669
|
* on its internal `RenderOutput` TS shape for telemetry / post-classify
|
|
@@ -514,17 +677,38 @@ export const resourceReadErrorSchema = z.object({
|
|
|
514
677
|
* `render-resource/...`). Leaving a dead URL on the wire had the model
|
|
515
678
|
* hallucinating links that resolve nowhere.
|
|
516
679
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
680
|
+
* THREE OUTCOMES (SPEC §7.1, ggui#786). Every result carries
|
|
681
|
+
* {@link renderOutcomeSchema} on `outcome`, and the identity fields are
|
|
682
|
+
* present IFF something was committed — which is why they are optional
|
|
683
|
+
* at the schema level and pinned by the presence refinement below:
|
|
684
|
+
*
|
|
685
|
+
* - `rendered` — identity fields present; `resourceUri` present iff
|
|
686
|
+
* mountable; no `error`, no `refusal`.
|
|
687
|
+
* - `failed` — generation ran and produced nothing. Identity fields
|
|
688
|
+
* present (the error GguiSession IS committed, so `sessionId`
|
|
689
|
+
* remains a live handle into the session channel), `error`
|
|
690
|
+
* present, `resourceUri` absent, no `_meta` on the result. The
|
|
691
|
+
* handshake is consumed.
|
|
692
|
+
* - `refused` — the deployment declined before doing any work.
|
|
693
|
+
* Identity fields ABSENT, `refusal` present, no `error`, no
|
|
694
|
+
* `nextStep`, no `_meta`. Nothing was committed and the handshake
|
|
695
|
+
* is intact. The refused arm's whole envelope is
|
|
696
|
+
* {@link refusedOutputSchema}.
|
|
697
|
+
*
|
|
698
|
+
* The root stays ONE object with a discriminant field rather than a
|
|
699
|
+
* discriminated union: the MCP spec's `Tool.outputSchema` root MUST be
|
|
700
|
+
* a JSON Schema of type `object`, and the SDK registers zod raw shapes.
|
|
701
|
+
* TypeScript narrowing is via the guards at the bottom of this file
|
|
702
|
+
* ({@link isRenderedOutput} / {@link isFailedRenderOutput} /
|
|
703
|
+
* {@link isRefusedRenderOutput}), never a parallel union type.
|
|
522
704
|
*
|
|
523
705
|
* Post-Phase-B the `'compose'` action enum value is gone — there is no
|
|
524
706
|
* stack of N renders to compose against.
|
|
525
707
|
*/
|
|
526
708
|
export const renderOutputSchema = z.object({
|
|
527
|
-
|
|
709
|
+
outcome: renderOutcomeSchema.describe("Which of the three outcomes this result reports. 'rendered': an interface was produced. 'failed': generation ran and produced none — the session is committed and `error` classifies it. 'refused': the deployment declined before doing any work — `refusal` carries the state, nothing was committed, and the handshake is still valid."),
|
|
710
|
+
/** Present iff something was committed — absent on a refusal. */
|
|
711
|
+
sessionId: z.string().optional(),
|
|
528
712
|
/**
|
|
529
713
|
* Spec-canonical MCP-Apps entry-point — same `ui://ggui/render/{id}`
|
|
530
714
|
* URI surfaced on `_meta.ui.resourceUri`. Surfacing it on the LLM-
|
|
@@ -541,17 +725,24 @@ export const renderOutputSchema = z.object({
|
|
|
541
725
|
.string()
|
|
542
726
|
.optional()
|
|
543
727
|
.describe('MCP-Apps mount URI (ui://ggui/render/{id}). Present iff the render is mountable; absent on a failed render.'),
|
|
544
|
-
action: z
|
|
728
|
+
action: z
|
|
729
|
+
.enum(['create', 'reuse', 'update', 'replace', 'declined'])
|
|
730
|
+
.optional(),
|
|
545
731
|
contractHash: z
|
|
546
732
|
.string()
|
|
733
|
+
.optional()
|
|
547
734
|
.describe('Canonical hash of the rendered data contract (shape only — fields, types, specs). Same hash ⟺ same data flow.'),
|
|
548
735
|
blueprintId: z
|
|
549
736
|
.string()
|
|
737
|
+
.optional()
|
|
550
738
|
.describe('Opaque id of the materialised component for this render. On the handshake-decided reuse paths (accept a cache-origin proposal, or a variance re-aim that resolves to an existing variant) it is the stored id — equal ids across renders mean the same stored component. override.contract always generates cold and mints a fresh id, even for an identical contract.'),
|
|
551
739
|
variantKey: z
|
|
552
740
|
.string()
|
|
741
|
+
.optional()
|
|
553
742
|
.describe('Canonical hash of the design-time variance (persona, aesthetic, seed prompt, context). With contractHash it forms the reuse key: the same pair reuses one component; a different variant of the same contract gets its own.'),
|
|
554
|
-
cache: renderCacheMarkerSchema
|
|
743
|
+
cache: renderCacheMarkerSchema
|
|
744
|
+
.optional()
|
|
745
|
+
.describe('Reuse outcome for this render: whether a stored component was served, its similarity, the matched component id, and how many generation calls that avoided.'),
|
|
555
746
|
/**
|
|
556
747
|
* In-result failure marker — present iff the tool result is
|
|
557
748
|
* `isError: true`. The structuredContent stays schema-conformant on
|
|
@@ -561,6 +752,14 @@ export const renderOutputSchema = z.object({
|
|
|
561
752
|
error: renderErrorSchema
|
|
562
753
|
.optional()
|
|
563
754
|
.describe('Present iff the tool result is isError — canonical {code, message} for a failed/rejected generation. Absent on success.'),
|
|
755
|
+
/**
|
|
756
|
+
* In-result PRE-GENERATION REFUSAL marker — present iff
|
|
757
|
+
* `outcome: 'refused'`, and then it is the ONLY field besides
|
|
758
|
+
* `outcome` (see {@link refusedOutputSchema}).
|
|
759
|
+
*/
|
|
760
|
+
refusal: renderRefusalSchema
|
|
761
|
+
.optional()
|
|
762
|
+
.describe('Present iff outcome is refused — the registered state the deployment declined on, plus the one recovery step. Nothing was committed and the handshake is still valid.'),
|
|
564
763
|
/**
|
|
565
764
|
* Wire-shape recovery hint for the next call. Emitted ONLY when the
|
|
566
765
|
* rendered contract has a non-empty `actionSpec` — i.e. the agent will
|
|
@@ -586,6 +785,56 @@ export const renderOutputSchema = z.object({
|
|
|
586
785
|
timeout: z.number(),
|
|
587
786
|
}),
|
|
588
787
|
}).optional().describe('Required-next-call hint — when the rendered contract has actions, points the agent at ggui_consume({sessionId, timeout}) for the inbound action loop. Absent for pure-display renders.'),
|
|
788
|
+
}).superRefine((value, ctx) => {
|
|
789
|
+
// Present-iff-committed. NOTE for implementors: this refinement is
|
|
790
|
+
// attached to the COMPOSED schema. A consumer that decomposes the
|
|
791
|
+
// schema to its raw shape and rebuilds it (`z.object(schema.shape)`,
|
|
792
|
+
// which is how the MCP SDK registers a tool's outputSchema) loses
|
|
793
|
+
// these rules — which is why the transport validates a refused
|
|
794
|
+
// payload against `refusedOutputSchema` directly. See SPEC §7.1.
|
|
795
|
+
if (value.outcome === 'refused') {
|
|
796
|
+
refineRefusedArm(value, ctx);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
requireOnCommittedArm(value.sessionId !== undefined, 'sessionId', value.outcome, ctx);
|
|
800
|
+
requireOnCommittedArm(value.action !== undefined, 'action', value.outcome, ctx);
|
|
801
|
+
requireOnCommittedArm(value.contractHash !== undefined, 'contractHash', value.outcome, ctx);
|
|
802
|
+
requireOnCommittedArm(value.blueprintId !== undefined, 'blueprintId', value.outcome, ctx);
|
|
803
|
+
requireOnCommittedArm(value.variantKey !== undefined, 'variantKey', value.outcome, ctx);
|
|
804
|
+
requireOnCommittedArm(value.cache !== undefined, 'cache', value.outcome, ctx);
|
|
805
|
+
rejectRefusalOnCommittedArm(value.refusal, value.outcome, ctx);
|
|
806
|
+
// `resourceUri` present IFF `rendered` — the mountability rule, the
|
|
807
|
+
// one presence rule the six identity fields do not cover. A `failed`
|
|
808
|
+
// render commits an error GguiSession but exposes no mount, so a URI
|
|
809
|
+
// beside it points a host at a render that does not exist.
|
|
810
|
+
if (value.outcome === 'rendered' && value.resourceUri === undefined) {
|
|
811
|
+
ctx.addIssue({
|
|
812
|
+
code: 'custom',
|
|
813
|
+
path: ['resourceUri'],
|
|
814
|
+
message: "outcome 'rendered' MUST carry `resourceUri` — a rendered result is mountable, and the URI is how a host mounts it.",
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
if (value.outcome === 'failed' && value.resourceUri !== undefined) {
|
|
818
|
+
ctx.addIssue({
|
|
819
|
+
code: 'custom',
|
|
820
|
+
path: ['resourceUri'],
|
|
821
|
+
message: "`resourceUri` is present only on outcome 'rendered' — a failed render exposes no mount affordance.",
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
if (value.outcome === 'failed' && value.error === undefined) {
|
|
825
|
+
ctx.addIssue({
|
|
826
|
+
code: 'custom',
|
|
827
|
+
path: ['error'],
|
|
828
|
+
message: "outcome 'failed' MUST carry `error` — the classification is what distinguishes it from a render that succeeded.",
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
if (value.outcome === 'rendered' && value.error !== undefined) {
|
|
832
|
+
ctx.addIssue({
|
|
833
|
+
code: 'custom',
|
|
834
|
+
path: ['error'],
|
|
835
|
+
message: "`error` is present only on outcome 'failed', not 'rendered'.",
|
|
836
|
+
});
|
|
837
|
+
}
|
|
589
838
|
});
|
|
590
839
|
/**
|
|
591
840
|
* `ggui_update` — refresh the rendered UI with new state.
|
|
@@ -654,9 +903,22 @@ export const amendInputSchema = z.discriminatedUnion('kind', [
|
|
|
654
903
|
}).strict(),
|
|
655
904
|
]);
|
|
656
905
|
/**
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
906
|
+
* Agent-facing description of the schema-attestation hash, shared by
|
|
907
|
+
* both mutation tools (ggui#560). It is not a comment: `.describe()`
|
|
908
|
+
* ships as JSON-Schema `description` in the tool declaration, so this
|
|
909
|
+
* string is what an agent reads out of `tools/list` when it has to
|
|
910
|
+
* decide whether a hash mismatch means the contract moved under it.
|
|
911
|
+
* Declared once so the two tools cannot say different things about the
|
|
912
|
+
* same field.
|
|
913
|
+
*/
|
|
914
|
+
const PROPS_SCHEMA_HASH_DESCRIPTION = 'sha256 (lowercase hex) over the RFC 8785 canonical form of the enforced props schema this mutation was validated against — the same schema the paired handshake disclosed. Present when the session declares a propsSpec. Equal to the handshake propsSchemaHash by the session-continuity guarantee; a mismatch means the contract changed under you.';
|
|
915
|
+
/** Agent-facing description of the grammar-profile classifier. Same rationale as {@link PROPS_SCHEMA_HASH_DESCRIPTION}. */
|
|
916
|
+
const PROPS_SCHEMA_PROFILE_DESCRIPTION = "Grammar profile of the enforced props schema: 'grammar-safe' or 'full'. Present with propsSchemaHash; treat unrecognized values as 'full'.";
|
|
917
|
+
/**
|
|
918
|
+
* Wire-output shape — minimal acknowledgement. This schema IS the
|
|
919
|
+
* handler's declared output: `ggui_update` registers `.shape` and its
|
|
920
|
+
* return type is `GguiUpdateOutput` (`z.infer` of this schema), so
|
|
921
|
+
* there is no second declaration to keep in step (ggui#798).
|
|
660
922
|
*
|
|
661
923
|
* Every REAL update (`updated: true`) mints a new history record and
|
|
662
924
|
* its result carries the `ai.ggui/render` slice as a FULL bootable
|
|
@@ -666,6 +928,15 @@ export const amendInputSchema = z.discriminatedUnion('kind', [
|
|
|
666
928
|
* history when its higher-epoch `props_update` frame lands; the
|
|
667
929
|
* in-place repaint over the live-channel ladder (WS / SSE / polling /
|
|
668
930
|
* bridge-pull) is `ggui_amend`'s job.
|
|
931
|
+
*
|
|
932
|
+
* NO refusal arm (ggui#786): the pre-generation refusal envelope rides
|
|
933
|
+
* `ggui_render` only. There is no pre-state POLICY gate on the
|
|
934
|
+
* mutation tools today — nothing on this path can decline a call for
|
|
935
|
+
* deployment-policy reasons (a contract error still rejects a
|
|
936
|
+
* malformed mutation before any store read; that is a parse result,
|
|
937
|
+
* not a refusal) — and `handshake: 'intact'` would assert something
|
|
938
|
+
* meaningless on a tool that consumes no handshake. The arm lands with its first
|
|
939
|
+
* emitter (ggui#798), not as a mechanical port of the render one.
|
|
669
940
|
*/
|
|
670
941
|
export const updateOutputSchema = z.object({
|
|
671
942
|
sessionId: z.string(),
|
|
@@ -676,8 +947,7 @@ export const updateOutputSchema = z.object({
|
|
|
676
947
|
* no-op (`updated: false`) no record is minted and this is the bare
|
|
677
948
|
* live-head URI. Mirrored on the LLM-visible structuredContent so
|
|
678
949
|
* SDKs that strip `_meta` from tool_results can still reach the
|
|
679
|
-
* mount URI.
|
|
680
|
-
* this export and the handler's inline schema must not drift.
|
|
950
|
+
* mount URI.
|
|
681
951
|
*/
|
|
682
952
|
resourceUri: z.string(),
|
|
683
953
|
/**
|
|
@@ -707,9 +977,15 @@ export const updateOutputSchema = z.object({
|
|
|
707
977
|
* session-continuity guarantee, so a mismatch is the observable form
|
|
708
978
|
* of a contract changing mid-session.
|
|
709
979
|
*/
|
|
710
|
-
propsSchemaHash: z
|
|
980
|
+
propsSchemaHash: z
|
|
981
|
+
.string()
|
|
982
|
+
.optional()
|
|
983
|
+
.describe(PROPS_SCHEMA_HASH_DESCRIPTION),
|
|
711
984
|
/** Present with `propsSchemaHash`; same profile classifier as the handshake's. */
|
|
712
|
-
propsSchemaProfile: z
|
|
985
|
+
propsSchemaProfile: z
|
|
986
|
+
.string()
|
|
987
|
+
.optional()
|
|
988
|
+
.describe(PROPS_SCHEMA_PROFILE_DESCRIPTION),
|
|
713
989
|
});
|
|
714
990
|
/**
|
|
715
991
|
* `ggui_amend` wire output (#483) — acknowledgement only. `resourceUri`
|
|
@@ -717,17 +993,49 @@ export const updateOutputSchema = z.object({
|
|
|
717
993
|
* mints a record, so there is no pinned URI to return and no epoch
|
|
718
994
|
* field — the history number is untouched by construction). The
|
|
719
995
|
* mounted card receives the new props over the live channels.
|
|
996
|
+
*
|
|
997
|
+
* As with {@link updateOutputSchema}, this schema IS the handler's
|
|
998
|
+
* declared output — `ggui_amend` registers `.shape` and returns
|
|
999
|
+
* `GguiAmendOutput` (ggui#798).
|
|
720
1000
|
*/
|
|
721
1001
|
export const amendOutputSchema = z.object({
|
|
722
1002
|
sessionId: z.string(),
|
|
723
1003
|
updated: z.boolean(),
|
|
1004
|
+
/**
|
|
1005
|
+
* The BARE live-head URI — amend targets the mounted card and never
|
|
1006
|
+
* mints a record, so there is no pinned URI to return and no epoch
|
|
1007
|
+
* field (the history number is untouched by construction).
|
|
1008
|
+
*
|
|
1009
|
+
* NORMATIVE re-anchor reference (SPEC §7.1.2.1, ggui#652 /
|
|
1010
|
+
* guuey#535): together with `sessionId` this is the durable record
|
|
1011
|
+
* that an in-place repaint touched this session at this turn. A
|
|
1012
|
+
* host's persistence layer MAY consume it as a locator-only
|
|
1013
|
+
* re-anchor — `resources/read`-resolvable, stable for the session's
|
|
1014
|
+
* lifetime — so a restored transcript re-positions the card at its
|
|
1015
|
+
* latest referencing turn and rehydrates CURRENT state instead of a
|
|
1016
|
+
* stale earlier snapshot. It rides `structuredContent` (LLM-visible,
|
|
1017
|
+
* same rationale as ggui_update's resourceUri: consumers that strip
|
|
1018
|
+
* `_meta` still reach it) and MUST NOT move to a result `_meta`
|
|
1019
|
+
* slice — any result `_meta` on this tool makes view-minting hosts
|
|
1020
|
+
* break the in-place semantics.
|
|
1021
|
+
*
|
|
1022
|
+
* Both this field and `sessionId` are REQUIRED, which is what makes
|
|
1023
|
+
* SPEC §7.1.2.1's "structurally guaranteed" true rather than a
|
|
1024
|
+
* convention.
|
|
1025
|
+
*/
|
|
724
1026
|
resourceUri: z.string(),
|
|
725
1027
|
/** Same no-op feedback channel as ggui_update's `warning`. */
|
|
726
1028
|
warning: z.string().optional(),
|
|
727
1029
|
/** Schema attestation (ggui#560) — same semantics as ggui_update's. */
|
|
728
|
-
propsSchemaHash: z
|
|
1030
|
+
propsSchemaHash: z
|
|
1031
|
+
.string()
|
|
1032
|
+
.optional()
|
|
1033
|
+
.describe(PROPS_SCHEMA_HASH_DESCRIPTION),
|
|
729
1034
|
/** Present with `propsSchemaHash`. */
|
|
730
|
-
propsSchemaProfile: z
|
|
1035
|
+
propsSchemaProfile: z
|
|
1036
|
+
.string()
|
|
1037
|
+
.optional()
|
|
1038
|
+
.describe(PROPS_SCHEMA_PROFILE_DESCRIPTION),
|
|
731
1039
|
});
|
|
732
1040
|
/**
|
|
733
1041
|
* `ggui_runtime_declare_tool_catalog` — the host runtime declares its
|
|
@@ -898,11 +1206,6 @@ export const runtimePullOutputSchema = z.union([
|
|
|
898
1206
|
runtimePullEventsPageSchema,
|
|
899
1207
|
runtimePullHorizonSchema,
|
|
900
1208
|
]);
|
|
901
|
-
/**
|
|
902
|
-
* Per-batch event cap on `ggui_runtime_telemetry` — a bounded
|
|
903
|
-
* fire-and-forget diagnostic channel, never a data plane.
|
|
904
|
-
*/
|
|
905
|
-
export const RUNTIME_TELEMETRY_MAX_EVENTS = 40;
|
|
906
1209
|
/**
|
|
907
1210
|
* `ggui_runtime_telemetry` input — the iframe runtime's transport
|
|
908
1211
|
* self-report (`_meta.ui.visibility: ['app']`, view-callable only).
|
|
@@ -951,3 +1254,207 @@ export const runtimeTelemetryInputShape = {
|
|
|
951
1254
|
export const runtimeTelemetryInputSchema = z.object(runtimeTelemetryInputShape);
|
|
952
1255
|
/** `ggui_runtime_telemetry` output — bare acknowledgement. */
|
|
953
1256
|
export const runtimeTelemetryOutputSchema = z.object({ ok: z.literal(true) });
|
|
1257
|
+
/** A render that produced an interface — identity fields present. */
|
|
1258
|
+
export function isRenderedOutput(output) {
|
|
1259
|
+
return output.outcome === 'rendered';
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* A generation that RAN and produced nothing. The error session is
|
|
1263
|
+
* committed, so `sessionId` is a live handle and `error` classifies it.
|
|
1264
|
+
*/
|
|
1265
|
+
export function isFailedRenderOutput(output) {
|
|
1266
|
+
return output.outcome === 'failed';
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* A PRE-GENERATION refusal — nothing read and nothing committed, so
|
|
1270
|
+
* every identity field is absent and `refusal` carries the state. (Not
|
|
1271
|
+
* "nothing parsed": the SDK has already checked the call against the
|
|
1272
|
+
* tool's declared `inputSchema` by the time a gate can refuse it.)
|
|
1273
|
+
*/
|
|
1274
|
+
export function isRefusedRenderOutput(output) {
|
|
1275
|
+
return output.outcome === 'refused';
|
|
1276
|
+
}
|
|
1277
|
+
// ============================================================================
|
|
1278
|
+
// Tool output schemas the protocol owns (#817 part C). A handler registers
|
|
1279
|
+
// `<schema>.shape` as its MCP `outputSchema` and derives its output type from
|
|
1280
|
+
// the schema — never a parallel interface. No `.readonly()` here: zod 4
|
|
1281
|
+
// projects it as `readOnly` into the advertised JSON Schema, and the wire is
|
|
1282
|
+
// mutable JSON; readonly is applied at the seam (`DeepReadonly` on the
|
|
1283
|
+
// derived types). Every shape is closed: the transport strip-parses against
|
|
1284
|
+
// `.shape`, so the shape IS the wire.
|
|
1285
|
+
// ============================================================================
|
|
1286
|
+
/** Display modes an MCP Apps host can render a view in (ext-apps vocabulary). */
|
|
1287
|
+
export const mcpUiDisplayModeSchema = z.enum(['inline', 'fullscreen', 'pip']);
|
|
1288
|
+
/**
|
|
1289
|
+
* The host-context projection the iframe-runtime observes and `ggui_consume`
|
|
1290
|
+
* echoes (`client.hostContext`). Every field optional: a host that never
|
|
1291
|
+
* reports one leaves it absent — absent ⇒ the documented default.
|
|
1292
|
+
*/
|
|
1293
|
+
export const hostContextProjectionSchema = z.object({
|
|
1294
|
+
availableDisplayModes: z.array(mcpUiDisplayModeSchema).optional(),
|
|
1295
|
+
currentDisplayMode: mcpUiDisplayModeSchema.optional(),
|
|
1296
|
+
containerDimensions: z
|
|
1297
|
+
.object({
|
|
1298
|
+
width: z.number().optional(),
|
|
1299
|
+
maxWidth: z.number().optional(),
|
|
1300
|
+
height: z.number().optional(),
|
|
1301
|
+
maxHeight: z.number().optional(),
|
|
1302
|
+
})
|
|
1303
|
+
.optional(),
|
|
1304
|
+
platform: z.enum(['web', 'desktop', 'mobile']).optional(),
|
|
1305
|
+
deviceCapabilities: z
|
|
1306
|
+
.object({ touch: z.boolean().optional(), hover: z.boolean().optional() })
|
|
1307
|
+
.optional(),
|
|
1308
|
+
locale: z.string().optional(),
|
|
1309
|
+
timeZone: z.string().optional(),
|
|
1310
|
+
});
|
|
1311
|
+
/** `ggui_consume`'s `client` slice — what the runtime observed about its host. */
|
|
1312
|
+
export const clientObservationsSchema = z.object({
|
|
1313
|
+
hostContext: hostContextProjectionSchema.optional(),
|
|
1314
|
+
});
|
|
1315
|
+
/** One row of `ggui_list_sessions` — eight closed keys; nothing passes through. */
|
|
1316
|
+
export const gguiSessionSummaryWireSchema = z.object({
|
|
1317
|
+
sessionId: z.string(),
|
|
1318
|
+
hostName: z.string().optional(),
|
|
1319
|
+
hostSessionId: z.string().optional(),
|
|
1320
|
+
createdAt: z.string(),
|
|
1321
|
+
lastActivityAt: z.string(),
|
|
1322
|
+
status: z.string(),
|
|
1323
|
+
wsToken: z.string().optional(),
|
|
1324
|
+
wsTokenExpiresAt: z.string().optional(),
|
|
1325
|
+
});
|
|
1326
|
+
/**
|
|
1327
|
+
* The two states a GguiSession is in on the wire — the pair the consume
|
|
1328
|
+
* loop exits on (`expired`). Owned here (ggui#817 part C2); the type is
|
|
1329
|
+
* derived, never a second list.
|
|
1330
|
+
*/
|
|
1331
|
+
export const gguiSessionStatusSchema = z.enum(['active', 'expired']);
|
|
1332
|
+
/**
|
|
1333
|
+
* One drained row of `ggui_consume` — a user action that reached the
|
|
1334
|
+
* pipe (ggui#817 part C2). Closed on the wire: an unknown key is
|
|
1335
|
+
* stripped at the transport, a missing key refuses the row at the seam
|
|
1336
|
+
* (`parsePendingEnvelope`), so a malformed pipe entry never ships to an
|
|
1337
|
+
* agent typed as a good one.
|
|
1338
|
+
*/
|
|
1339
|
+
export const consumeEventEntrySchema = z.object({
|
|
1340
|
+
type: z.literal('action'),
|
|
1341
|
+
sessionId: z.string().min(1),
|
|
1342
|
+
intent: z.string(),
|
|
1343
|
+
actionData: jsonValueSchema.nullable(),
|
|
1344
|
+
uiContext: jsonObjectSchema,
|
|
1345
|
+
actionId: z.string(),
|
|
1346
|
+
firedAt: z.string(),
|
|
1347
|
+
});
|
|
1348
|
+
/**
|
|
1349
|
+
* `ggui_consume`'s output — the drained rows, the session's state, and the
|
|
1350
|
+
* client's observations when the host sent any (ggui#817 part C2). The
|
|
1351
|
+
* handler registers `.shape`; `tools/list` therefore advertises the entry
|
|
1352
|
+
* vocabulary and the status enum instead of a free-form record and a free
|
|
1353
|
+
* string.
|
|
1354
|
+
*/
|
|
1355
|
+
export const gguiConsumeOutputSchema = z.object({
|
|
1356
|
+
events: z.array(consumeEventEntrySchema),
|
|
1357
|
+
status: gguiSessionStatusSchema,
|
|
1358
|
+
client: clientObservationsSchema.optional(),
|
|
1359
|
+
});
|
|
1360
|
+
/** `ggui_list_sessions`' output — the closed summary rows (ggui#817 part C2). */
|
|
1361
|
+
export const gguiListSessionsOutputSchema = z.object({
|
|
1362
|
+
sessions: z.array(gguiSessionSummaryWireSchema),
|
|
1363
|
+
});
|
|
1364
|
+
/**
|
|
1365
|
+
* `ggui_emit`'s output (ggui#817 part C2): `accepted` at the boundary, and
|
|
1366
|
+
* `seq` when the server keeps a stream buffer — seq-aware implementations
|
|
1367
|
+
* stamp and return it so replay cursors can be built from the ack.
|
|
1368
|
+
*/
|
|
1369
|
+
export const gguiEmitOutputSchema = z.object({
|
|
1370
|
+
accepted: z.boolean(),
|
|
1371
|
+
seq: z
|
|
1372
|
+
.number()
|
|
1373
|
+
.int()
|
|
1374
|
+
.nonnegative()
|
|
1375
|
+
.optional()
|
|
1376
|
+
.describe('Session-scoped monotonic outbound sequence assigned to this delivery. Present when the server keeps a stream buffer.'),
|
|
1377
|
+
});
|
|
1378
|
+
/**
|
|
1379
|
+
* `ggui_get_session`'s wire: the store row's six base fields plus the mount
|
|
1380
|
+
* variant — for EVERY session. An MCP-Apps mount is locator-only on the
|
|
1381
|
+
* render object, but its store row carries the base fields, so the
|
|
1382
|
+
* projection reads them from the row and the wire never fails on that
|
|
1383
|
+
* variant. The locator itself is not on this wire (MCP-Apps resources have
|
|
1384
|
+
* their own paths).
|
|
1385
|
+
* `contextSnapshot` rides when a component (`render`) mount's row has one —
|
|
1386
|
+
* never on an mcpApps mount.
|
|
1387
|
+
*/
|
|
1388
|
+
export const gguiGetSessionOutputSchema = z.object({
|
|
1389
|
+
variant: z.enum(['render', 'mcpApps']),
|
|
1390
|
+
id: z.string(),
|
|
1391
|
+
appId: z.string(),
|
|
1392
|
+
eventSequence: z.number().int().nonnegative(),
|
|
1393
|
+
createdAt: z.number().int().nonnegative(),
|
|
1394
|
+
lastActivityAt: z.number().int().nonnegative(),
|
|
1395
|
+
expiresAt: z.number().int().nonnegative(),
|
|
1396
|
+
/**
|
|
1397
|
+
* The last-known value of every declared contextSpec slot, as
|
|
1398
|
+
* `ggui_runtime_sync_context` wrote it onto the row — the read path a
|
|
1399
|
+
* raw MCP client (no widget-context mirror) has for contextSpec values.
|
|
1400
|
+
* Present iff the row carries one — component (`render`) mounts only; an
|
|
1401
|
+
* mcpApps mount never carries one; never an empty placeholder.
|
|
1402
|
+
*/
|
|
1403
|
+
contextSnapshot: jsonObjectSchema.optional(),
|
|
1404
|
+
});
|
|
1405
|
+
/** The three sequential gates of `ggui_protocol_validate_blueprint`. */
|
|
1406
|
+
export const blueprintValidationTierSchema = z.enum(['compile', 'selfCheck', 'runtime']);
|
|
1407
|
+
export const blueprintValidationIssueSchema = z.object({
|
|
1408
|
+
tier: blueprintValidationTierSchema,
|
|
1409
|
+
code: z.string(),
|
|
1410
|
+
message: z.string(),
|
|
1411
|
+
fix: z.string().optional(),
|
|
1412
|
+
});
|
|
1413
|
+
/** `ggui_protocol_validate_blueprint`'s result envelope: `failedAt` names the tier that stopped, or null. */
|
|
1414
|
+
export const blueprintValidationResultSchema = z.object({
|
|
1415
|
+
valid: z.boolean(),
|
|
1416
|
+
failedAt: blueprintValidationTierSchema.nullable(),
|
|
1417
|
+
errors: z.array(blueprintValidationIssueSchema),
|
|
1418
|
+
warnings: z.array(blueprintValidationIssueSchema),
|
|
1419
|
+
});
|
|
1420
|
+
/** A provider row — what `ggui_list_featured_blueprints` returns per blueprint. */
|
|
1421
|
+
export const blueprintEntryWireSchema = z.object({
|
|
1422
|
+
id: z.string(),
|
|
1423
|
+
name: z.string(),
|
|
1424
|
+
description: z.string().optional(),
|
|
1425
|
+
source: blueprintSourceSchema,
|
|
1426
|
+
updatedAt: z.string(),
|
|
1427
|
+
tags: z.array(z.string()).optional(),
|
|
1428
|
+
});
|
|
1429
|
+
export const gguiListFeaturedBlueprintsOutputSchema = z.object({
|
|
1430
|
+
blueprints: z.array(blueprintEntryWireSchema),
|
|
1431
|
+
total: z.number().int().nonnegative(),
|
|
1432
|
+
});
|
|
1433
|
+
/** One `ggui_search_blueprints` hit — a scored row plus the registry-only keys. */
|
|
1434
|
+
export const gguiSearchBlueprintsResultSchema = z.object({
|
|
1435
|
+
id: z.string(),
|
|
1436
|
+
name: z.string(),
|
|
1437
|
+
description: z.string(),
|
|
1438
|
+
category: z.string(),
|
|
1439
|
+
props: z.array(z.object({ name: z.string(), type: z.string(), required: z.boolean(), description: z.string() })),
|
|
1440
|
+
callbacks: z.array(z.string()),
|
|
1441
|
+
featured: z.boolean(),
|
|
1442
|
+
relevance: z.literal('match'),
|
|
1443
|
+
score: z.number(),
|
|
1444
|
+
origin: z.literal('registry').optional(),
|
|
1445
|
+
artifactId: z.string().optional(),
|
|
1446
|
+
version: z.string().optional(),
|
|
1447
|
+
mcpTools: z.array(z.object({ server: z.string().optional(), tool: z.string() })).optional(),
|
|
1448
|
+
scopeVerification: z.enum(['verified', 'unverified']).optional(),
|
|
1449
|
+
});
|
|
1450
|
+
export const gguiSearchBlueprintsOutputSchema = z.object({
|
|
1451
|
+
results: z.array(gguiSearchBlueprintsResultSchema),
|
|
1452
|
+
total: z.number(),
|
|
1453
|
+
query: z.string(),
|
|
1454
|
+
degradedSources: z
|
|
1455
|
+
.array(z.object({
|
|
1456
|
+
source: z.literal('registry'),
|
|
1457
|
+
reason: z.enum(['unreachable', 'timeout', 'invalid_response']),
|
|
1458
|
+
}))
|
|
1459
|
+
.optional(),
|
|
1460
|
+
});
|