@ggui-ai/protocol 0.1.0-rc.1
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 +201 -0
- package/README.md +46 -0
- package/dist/bridge/invoke-agent.d.ts +65 -0
- package/dist/bridge/invoke-agent.d.ts.map +1 -0
- package/dist/bridge/invoke-agent.js +113 -0
- package/dist/envelope-adapters.d.ts +24 -0
- package/dist/envelope-adapters.d.ts.map +1 -0
- package/dist/envelope-adapters.js +14 -0
- package/dist/envelopes/builders.d.ts +145 -0
- package/dist/envelopes/builders.d.ts.map +1 -0
- package/dist/envelopes/builders.js +113 -0
- package/dist/errors/unknown-permission-name.d.ts +12 -0
- package/dist/errors/unknown-permission-name.d.ts.map +1 -0
- package/dist/errors/unknown-permission-name.js +29 -0
- package/dist/errors/version-mismatch.d.ts +55 -0
- package/dist/errors/version-mismatch.d.ts.map +1 -0
- package/dist/errors/version-mismatch.js +52 -0
- package/dist/gadgets/resolve-contract-gadgets.d.ts +93 -0
- package/dist/gadgets/resolve-contract-gadgets.d.ts.map +1 -0
- package/dist/gadgets/resolve-contract-gadgets.js +119 -0
- package/dist/gadgets/stdlib-gadgets.d.ts +43 -0
- package/dist/gadgets/stdlib-gadgets.d.ts.map +1 -0
- package/dist/gadgets/stdlib-gadgets.js +161 -0
- package/dist/iframe-bridge.d.ts +63 -0
- package/dist/iframe-bridge.d.ts.map +1 -0
- package/dist/iframe-bridge.js +166 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/integrations/mcp-apps.d.ts +1218 -0
- package/dist/integrations/mcp-apps.d.ts.map +1 -0
- package/dist/integrations/mcp-apps.js +427 -0
- package/dist/navigation/index.d.ts +3 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +1 -0
- package/dist/navigation/stack-navigation.d.ts +55 -0
- package/dist/navigation/stack-navigation.d.ts.map +1 -0
- package/dist/navigation/stack-navigation.js +80 -0
- package/dist/recommended-prompts.d.ts +56 -0
- package/dist/recommended-prompts.d.ts.map +1 -0
- package/dist/recommended-prompts.js +55 -0
- package/dist/registry/blueprint-key.d.ts +9 -0
- package/dist/registry/blueprint-key.d.ts.map +1 -0
- package/dist/registry/blueprint-key.js +28 -0
- package/dist/registry/canonicalize-contract.d.ts +35 -0
- package/dist/registry/canonicalize-contract.d.ts.map +1 -0
- package/dist/registry/canonicalize-contract.js +166 -0
- package/dist/registry/summarize-contract.d.ts +46 -0
- package/dist/registry/summarize-contract.d.ts.map +1 -0
- package/dist/registry/summarize-contract.js +63 -0
- package/dist/schema-learning/derive-contract.d.ts +67 -0
- package/dist/schema-learning/derive-contract.d.ts.map +1 -0
- package/dist/schema-learning/derive-contract.js +117 -0
- package/dist/schema-learning/merge.d.ts +32 -0
- package/dist/schema-learning/merge.d.ts.map +1 -0
- package/dist/schema-learning/merge.js +146 -0
- package/dist/schemas/blueprint.d.ts +32 -0
- package/dist/schemas/blueprint.d.ts.map +1 -0
- package/dist/schemas/blueprint.js +92 -0
- package/dist/schemas/data-contract.d.ts +750 -0
- package/dist/schemas/data-contract.d.ts.map +1 -0
- package/dist/schemas/data-contract.js +663 -0
- package/dist/schemas/gadget-name-grammar.d.ts +29 -0
- package/dist/schemas/gadget-name-grammar.d.ts.map +1 -0
- package/dist/schemas/gadget-name-grammar.js +28 -0
- package/dist/schemas/handshake-suggestion.d.ts +46 -0
- package/dist/schemas/handshake-suggestion.d.ts.map +1 -0
- package/dist/schemas/handshake-suggestion.js +107 -0
- package/dist/schemas/invoke.d.ts +337 -0
- package/dist/schemas/invoke.d.ts.map +1 -0
- package/dist/schemas/invoke.js +169 -0
- package/dist/schemas/mcp.d.ts +301 -0
- package/dist/schemas/mcp.d.ts.map +1 -0
- package/dist/schemas/mcp.js +373 -0
- package/dist/schemas/ops-blueprint.d.ts +176 -0
- package/dist/schemas/ops-blueprint.d.ts.map +1 -0
- package/dist/schemas/ops-blueprint.js +259 -0
- package/dist/schemas/sync-check.d.ts +11 -0
- package/dist/schemas/sync-check.d.ts.map +1 -0
- package/dist/schemas/sync-check.js +60 -0
- package/dist/screen-blueprints/define.d.ts +22 -0
- package/dist/screen-blueprints/define.d.ts.map +1 -0
- package/dist/screen-blueprints/define.js +3 -0
- package/dist/screen-blueprints/index.d.ts +4 -0
- package/dist/screen-blueprints/index.d.ts.map +1 -0
- package/dist/screen-blueprints/index.js +3 -0
- package/dist/screen-blueprints/match.d.ts +35 -0
- package/dist/screen-blueprints/match.d.ts.map +1 -0
- package/dist/screen-blueprints/match.js +51 -0
- package/dist/screen-blueprints/types.d.ts +164 -0
- package/dist/screen-blueprints/types.d.ts.map +1 -0
- package/dist/screen-blueprints/types.js +1 -0
- package/dist/stream/stream-parser.d.ts +62 -0
- package/dist/stream/stream-parser.d.ts.map +1 -0
- package/dist/stream/stream-parser.js +199 -0
- package/dist/transport/websocket.d.ts +178 -0
- package/dist/transport/websocket.d.ts.map +1 -0
- package/dist/transport/websocket.js +1 -0
- package/dist/types/app-config.d.ts +61 -0
- package/dist/types/app-config.d.ts.map +1 -0
- package/dist/types/app-config.js +1 -0
- package/dist/types/auth.d.ts +61 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +1 -0
- package/dist/types/blueprint.d.ts +206 -0
- package/dist/types/blueprint.d.ts.map +1 -0
- package/dist/types/blueprint.js +1 -0
- package/dist/types/canvas-lifecycle.d.ts +105 -0
- package/dist/types/canvas-lifecycle.d.ts.map +1 -0
- package/dist/types/canvas-lifecycle.js +38 -0
- package/dist/types/capabilities.d.ts +40 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +19 -0
- package/dist/types/contract-inference.d.ts +401 -0
- package/dist/types/contract-inference.d.ts.map +1 -0
- package/dist/types/contract-inference.js +44 -0
- package/dist/types/credential.d.ts +41 -0
- package/dist/types/credential.d.ts.map +1 -0
- package/dist/types/credential.js +32 -0
- package/dist/types/data-bindings.d.ts +322 -0
- package/dist/types/data-bindings.d.ts.map +1 -0
- package/dist/types/data-bindings.js +29 -0
- package/dist/types/data-contract.d.ts +1296 -0
- package/dist/types/data-contract.d.ts.map +1 -0
- package/dist/types/data-contract.js +111 -0
- package/dist/types/events.d.ts +182 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +8 -0
- package/dist/types/feedback.d.ts +24 -0
- package/dist/types/feedback.d.ts.map +1 -0
- package/dist/types/feedback.js +7 -0
- package/dist/types/gadget.d.ts +121 -0
- package/dist/types/gadget.d.ts.map +1 -0
- package/dist/types/gadget.js +24 -0
- package/dist/types/handshake-suggestion.d.ts +264 -0
- package/dist/types/handshake-suggestion.d.ts.map +1 -0
- package/dist/types/handshake-suggestion.js +70 -0
- package/dist/types/host-context.d.ts +163 -0
- package/dist/types/host-context.d.ts.map +1 -0
- package/dist/types/host-context.js +142 -0
- package/dist/types/interface-context.d.ts +105 -0
- package/dist/types/interface-context.d.ts.map +1 -0
- package/dist/types/interface-context.js +115 -0
- package/dist/types/invoke.d.ts +28 -0
- package/dist/types/invoke.d.ts.map +1 -0
- package/dist/types/invoke.js +7 -0
- package/dist/types/live-channel.d.ts +613 -0
- package/dist/types/live-channel.d.ts.map +1 -0
- package/dist/types/live-channel.js +1 -0
- package/dist/types/llm.d.ts +61 -0
- package/dist/types/llm.d.ts.map +1 -0
- package/dist/types/llm.js +186 -0
- package/dist/types/mcp-proxy.d.ts +67 -0
- package/dist/types/mcp-proxy.d.ts.map +1 -0
- package/dist/types/mcp-proxy.js +46 -0
- package/dist/types/mcp.d.ts +637 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +30 -0
- package/dist/types/openrouter-models.d.ts +22 -0
- package/dist/types/openrouter-models.d.ts.map +1 -0
- package/dist/types/openrouter-models.js +4843 -0
- package/dist/types/region.d.ts +26 -0
- package/dist/types/region.d.ts.map +1 -0
- package/dist/types/region.js +36 -0
- package/dist/types/session.d.ts +419 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +1 -0
- package/dist/types/thread.d.ts +207 -0
- package/dist/types/thread.d.ts.map +1 -0
- package/dist/types/thread.js +57 -0
- package/dist/types/ui-generator.d.ts +100 -0
- package/dist/types/ui-generator.d.ts.map +1 -0
- package/dist/types/ui-generator.js +53 -0
- package/dist/validation/ajv-runtime.d.ts +140 -0
- package/dist/validation/ajv-runtime.d.ts.map +1 -0
- package/dist/validation/ajv-runtime.js +452 -0
- package/dist/validation/content-hash.d.ts +3 -0
- package/dist/validation/content-hash.d.ts.map +1 -0
- package/dist/validation/content-hash.js +21 -0
- package/dist/validation/contract-validator.d.ts +244 -0
- package/dist/validation/contract-validator.d.ts.map +1 -0
- package/dist/validation/contract-validator.js +711 -0
- package/dist/validation/cross-references.d.ts +105 -0
- package/dist/validation/cross-references.d.ts.map +1 -0
- package/dist/validation/cross-references.js +164 -0
- package/dist/validation/hygiene-rules.d.ts +250 -0
- package/dist/validation/hygiene-rules.d.ts.map +1 -0
- package/dist/validation/hygiene-rules.js +564 -0
- package/dist/validation/lint-contract.d.ts +130 -0
- package/dist/validation/lint-contract.d.ts.map +1 -0
- package/dist/validation/lint-contract.js +225 -0
- package/dist/validation/name-invariants.d.ts +117 -0
- package/dist/validation/name-invariants.d.ts.map +1 -0
- package/dist/validation/name-invariants.js +172 -0
- package/dist/validation/reserved-channels.d.ts +156 -0
- package/dist/validation/reserved-channels.d.ts.map +1 -0
- package/dist/validation/reserved-channels.js +356 -0
- package/dist/validation/resolve-stream-channel.d.ts +78 -0
- package/dist/validation/resolve-stream-channel.d.ts.map +1 -0
- package/dist/validation/resolve-stream-channel.js +64 -0
- package/dist/validation/sanitize-error.d.ts +46 -0
- package/dist/validation/sanitize-error.d.ts.map +1 -0
- package/dist/validation/sanitize-error.js +88 -0
- package/dist/validation/schema-compat-invariants.d.ts +140 -0
- package/dist/validation/schema-compat-invariants.d.ts.map +1 -0
- package/dist/validation/schema-compat-invariants.js +220 -0
- package/dist/validation/schema-meta-validation.d.ts +60 -0
- package/dist/validation/schema-meta-validation.d.ts.map +1 -0
- package/dist/validation/schema-meta-validation.js +131 -0
- package/dist/validation/schema-subset.d.ts +165 -0
- package/dist/validation/schema-subset.d.ts.map +1 -0
- package/dist/validation/schema-subset.js +295 -0
- package/dist/validation/ui-security.d.ts +54 -0
- package/dist/validation/ui-security.d.ts.map +1 -0
- package/dist/validation/ui-security.js +138 -0
- package/dist/validation/zod-to-json-schema.d.ts +63 -0
- package/dist/validation/zod-to-json-schema.d.ts.map +1 -0
- package/dist/validation/zod-to-json-schema.js +126 -0
- package/dist/version.d.ts +1458 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +1459 -0
- package/package.json +113 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handshake-suggestion shapes (2026-05-12).
|
|
3
|
+
*
|
|
4
|
+
* The three-step handshake protocol replaces the older `match` + `plan`
|
|
5
|
+
* framing on the handshake output.
|
|
6
|
+
*
|
|
7
|
+
* Step 1 — the agent posts a `BlueprintDraft` (its idea: contract +
|
|
8
|
+
* optional variance + optional generator hint).
|
|
9
|
+
*
|
|
10
|
+
* Step 2 — the server runs `BlueprintSearch` + contract validation in
|
|
11
|
+
* parallel and returns a {@link HandshakeSuggestion}. The suggestion's
|
|
12
|
+
* `origin` enum routes the agent's next decision:
|
|
13
|
+
*
|
|
14
|
+
* - `cache` — search-score crossed the per-app threshold; cached
|
|
15
|
+
* code wins. `blueprintMeta.codeHash` is present.
|
|
16
|
+
* - `agent` — search missed but validation passed; gen pending
|
|
17
|
+
* against the agent's draft. Provisional blueprintId.
|
|
18
|
+
* - `synth` — search missed AND validation failed; synth amended
|
|
19
|
+
* the contract. Provisional blueprintId; `amendments`
|
|
20
|
+
* carries the diff vs the agent's draft.
|
|
21
|
+
*
|
|
22
|
+
* Step 3 — the agent posts a `PushDecision` (accept the suggestion or
|
|
23
|
+
* override with a fresh draft). Accept reuses the provisional
|
|
24
|
+
* `blueprintId`; override mints a fresh one.
|
|
25
|
+
*
|
|
26
|
+
* Locked decisions:
|
|
27
|
+
*
|
|
28
|
+
* - `blueprintMeta` is ALWAYS present on a successful handshake
|
|
29
|
+
* (Option B from §D5). `codeHash` is the only field that's absent
|
|
30
|
+
* on non-cache origins.
|
|
31
|
+
* - `amendments` is populated only on `origin: 'synth'`. On `cache`
|
|
32
|
+
* and `agent` origins it MUST be omitted.
|
|
33
|
+
* - `validationFindings` is populated only when validators ran AND
|
|
34
|
+
* produced findings — on cache hits these surface as a soft
|
|
35
|
+
* warning ("your draft would've had X issue — using cached
|
|
36
|
+
* blueprint instead"); on agent/synth they're carried for
|
|
37
|
+
* telemetry only (synth's amendment already addressed them).
|
|
38
|
+
*/
|
|
39
|
+
import type { Blueprint, BlueprintVariance } from './blueprint.js';
|
|
40
|
+
import type { DataContract, JsonValue } from './data-contract.js';
|
|
41
|
+
/**
|
|
42
|
+
* Where the handshake's `blueprintMeta` came from. Routes the agent's
|
|
43
|
+
* cognitive model:
|
|
44
|
+
*
|
|
45
|
+
* - `cache` — an existing blueprint matched at or above the per-app
|
|
46
|
+
* threshold. `blueprintMeta.codeHash` is present; the
|
|
47
|
+
* paired `ggui_push({decision: {kind: 'accept'}})`
|
|
48
|
+
* short-circuits to cache delivery.
|
|
49
|
+
* - `agent` — no cache hit, but the agent's draft validated cleanly.
|
|
50
|
+
* `codeHash` absent; gen runs on push against the
|
|
51
|
+
* agent's draft contract verbatim.
|
|
52
|
+
* - `synth` — no cache hit AND validation failed. The synth
|
|
53
|
+
* amender produced a new contract; the diff vs the
|
|
54
|
+
* agent's draft is in `amendments.contractDiff`.
|
|
55
|
+
*/
|
|
56
|
+
export type SuggestionOrigin = 'cache' | 'agent' | 'synth';
|
|
57
|
+
/**
|
|
58
|
+
* Agent's draft on the handshake input — what the agent wants to
|
|
59
|
+
* build. The contract is required; variance + generator are optional
|
|
60
|
+
* hints. The server combines this with its own session/app context
|
|
61
|
+
* (cached blueprints, validator outcomes, operator pins) to produce
|
|
62
|
+
* a {@link HandshakeSuggestion}.
|
|
63
|
+
*/
|
|
64
|
+
export interface BlueprintDraft {
|
|
65
|
+
/**
|
|
66
|
+
* Agent-authored DataContract. Drives both the blueprint-search
|
|
67
|
+
* embed/structural axes and the contract validators. The agent is
|
|
68
|
+
* the contract authority; synth amends only when validation fails.
|
|
69
|
+
*/
|
|
70
|
+
readonly contract: DataContract;
|
|
71
|
+
/**
|
|
72
|
+
* Optional variance tags. Carried through to the suggestion's
|
|
73
|
+
* `blueprintMeta.variance` field; if `decision: 'accept'` lands on a
|
|
74
|
+
* fresh-gen path (origin === 'agent' or 'synth'), the persisted
|
|
75
|
+
* Blueprint row inherits these tags.
|
|
76
|
+
*/
|
|
77
|
+
readonly variance?: {
|
|
78
|
+
/** Free-form persona tag (e.g. 'minimalist', 'data-dense'). */
|
|
79
|
+
readonly persona?: string;
|
|
80
|
+
/** Aesthetic tag — promoted to first-class in a future slice. */
|
|
81
|
+
readonly aesthetic?: string;
|
|
82
|
+
/** Small structured signal — JSON-safe. */
|
|
83
|
+
readonly context?: {
|
|
84
|
+
readonly [key: string]: JsonValue | undefined;
|
|
85
|
+
};
|
|
86
|
+
/** Raw style hint / seed prompt. */
|
|
87
|
+
readonly seedPrompt?: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Generator slug hint (e.g. `'ui-gen-advanced-opus-4-7'`). The
|
|
91
|
+
* server resolves the effective generator as:
|
|
92
|
+
*
|
|
93
|
+
* 1. Operator app-pin (`App.pinnedGenerator`) — wins if set.
|
|
94
|
+
* 2. This hint — if registered in the GeneratorRegistry.
|
|
95
|
+
* 3. Registry default (`ui-gen-default-haiku-4-5`).
|
|
96
|
+
*
|
|
97
|
+
* Hint-only; unknown slugs fall through to the registry default.
|
|
98
|
+
*/
|
|
99
|
+
readonly generator?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Blueprint metadata projected onto the handshake response. The agent
|
|
103
|
+
* uses this to decide whether to accept (reuse the provisional id) or
|
|
104
|
+
* override (mint a fresh id with its own new draft).
|
|
105
|
+
*
|
|
106
|
+
* `blueprintId` is PROVISIONAL — it becomes durable iff the paired
|
|
107
|
+
* push sends `decision: 'accept'`. An override discards it.
|
|
108
|
+
*/
|
|
109
|
+
export interface BlueprintMeta {
|
|
110
|
+
/**
|
|
111
|
+
* Provisional blueprint id. Server-minted at handshake-time.
|
|
112
|
+
* Becomes durable when push accepts; discarded on push override.
|
|
113
|
+
*/
|
|
114
|
+
readonly blueprintId: string;
|
|
115
|
+
/** Canonical RFC 8785 (JCS) hash of the suggestion's contract. */
|
|
116
|
+
readonly contractHash: string;
|
|
117
|
+
/**
|
|
118
|
+
* Content hash of the cached code body. Present iff `origin ===
|
|
119
|
+
* 'cache'`. Absent for `agent` / `synth` (gen pending).
|
|
120
|
+
*/
|
|
121
|
+
readonly codeHash?: string;
|
|
122
|
+
/** Slug of the generator that produced (or will produce) the code. */
|
|
123
|
+
readonly generator: string;
|
|
124
|
+
/** Variance tags carried through from the suggestion. */
|
|
125
|
+
readonly variance: BlueprintVariance;
|
|
126
|
+
/**
|
|
127
|
+
* Optional matcher telemetry — why this blueprint was selected.
|
|
128
|
+
* Operator-readable; LLM-readable. E.g. `'contract-hash, persona →
|
|
129
|
+
* score 0.92'`.
|
|
130
|
+
*/
|
|
131
|
+
readonly selectedReason?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Validator finding surfaced on the suggestion. Mirrors
|
|
135
|
+
* `@ggui-ai/protocol/validation/lint-contract`'s `ContractIssue` shape
|
|
136
|
+
* loosely — kept structural here so the suggestion contract doesn't
|
|
137
|
+
* import from the linter module and create a tight cycle.
|
|
138
|
+
*
|
|
139
|
+
* Each finding has a stable `code`, a severity, the dotted-path
|
|
140
|
+
* location, and a human-readable `message`.
|
|
141
|
+
*/
|
|
142
|
+
export interface SuggestionFinding {
|
|
143
|
+
/** Stable error code (e.g. `'CTR_REF_NEXT_STEP'`, `'CTR_DUP_NAME'`). */
|
|
144
|
+
readonly code: string;
|
|
145
|
+
readonly severity: 'error' | 'warn';
|
|
146
|
+
/** Dotted JS-style path into the contract. */
|
|
147
|
+
readonly path: string;
|
|
148
|
+
/** Human-readable violation prose. */
|
|
149
|
+
readonly message: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Synth's amendment — the diff vs the agent's draft. Populated only on
|
|
153
|
+
* `origin: 'synth'`.
|
|
154
|
+
*
|
|
155
|
+
* `contractDiff` is an RFC 6902 JSON-Patch-style array; the diff
|
|
156
|
+
* applied to the agent's draft yields the suggestion's contract.
|
|
157
|
+
* Helpers in `@ggui-ai/protocol/validation/contract-diff` produce and
|
|
158
|
+
* apply the diff.
|
|
159
|
+
*
|
|
160
|
+
* `reasoning` is the synth model's natural-language explanation —
|
|
161
|
+
* "added required `submit` action so the form completion is
|
|
162
|
+
* observable", etc.
|
|
163
|
+
*/
|
|
164
|
+
export interface SuggestionAmendments {
|
|
165
|
+
readonly contractDiff: JsonPatch;
|
|
166
|
+
readonly reasoning: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Minimal RFC 6902 JSON-Patch shape carried in handshake-suggestion
|
|
170
|
+
* amendments. The protocol re-exports this so consumers can apply /
|
|
171
|
+
* inspect patches without an external dependency.
|
|
172
|
+
*
|
|
173
|
+
* Subset support — every emitter MUST honor `add` / `remove` /
|
|
174
|
+
* `replace`; `move` / `copy` / `test` are reserved for future use
|
|
175
|
+
* (consumers MAY reject unrecognized ops).
|
|
176
|
+
*/
|
|
177
|
+
export type JsonPatch = readonly JsonPatchOp[];
|
|
178
|
+
export type JsonPatchOp = {
|
|
179
|
+
readonly op: 'add';
|
|
180
|
+
readonly path: string;
|
|
181
|
+
readonly value: JsonValue;
|
|
182
|
+
} | {
|
|
183
|
+
readonly op: 'remove';
|
|
184
|
+
readonly path: string;
|
|
185
|
+
} | {
|
|
186
|
+
readonly op: 'replace';
|
|
187
|
+
readonly path: string;
|
|
188
|
+
readonly value: JsonValue;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The full handshake suggestion. Produced by the server in step-2 of
|
|
192
|
+
* the three-step handshake; the agent reads this in the response and
|
|
193
|
+
* branches its push decision on `origin` (accept vs override).
|
|
194
|
+
*/
|
|
195
|
+
export interface HandshakeSuggestion {
|
|
196
|
+
/** Routing discriminator — see {@link SuggestionOrigin}. */
|
|
197
|
+
readonly origin: SuggestionOrigin;
|
|
198
|
+
/** Operator-readable + LLM-readable rationale ("contract-hash → score 0.92"). */
|
|
199
|
+
readonly rationale: string;
|
|
200
|
+
/** Provisional blueprint metadata — see {@link BlueprintMeta}. */
|
|
201
|
+
readonly blueprintMeta: BlueprintMeta;
|
|
202
|
+
/**
|
|
203
|
+
* Populated iff `origin === 'synth'`. Carries the JSON-Patch diff
|
|
204
|
+
* vs the agent's draft and the synth model's reasoning.
|
|
205
|
+
*/
|
|
206
|
+
readonly amendments?: SuggestionAmendments;
|
|
207
|
+
/**
|
|
208
|
+
* Populated iff validators ran AND produced findings. On `origin:
|
|
209
|
+
* 'cache'` these surface as a soft warning (the agent's draft
|
|
210
|
+
* WOULD have had issues, but the cached blueprint is being served);
|
|
211
|
+
* on `agent` / `synth` they're absent (agent path's validators
|
|
212
|
+
* passed; synth path's amendments already addressed them).
|
|
213
|
+
*/
|
|
214
|
+
readonly validationFindings?: readonly SuggestionFinding[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Decision discriminator on the push input. Replaces the old
|
|
218
|
+
* `{contract? | contractHash?}` triad with a clearer accept-vs-
|
|
219
|
+
* override branch.
|
|
220
|
+
*
|
|
221
|
+
* - `accept` — use the handshake's `blueprintMeta` verbatim.
|
|
222
|
+
* If `codeHash` is present (origin === 'cache'),
|
|
223
|
+
* delivery is a fast cache fetch. Otherwise gen
|
|
224
|
+
* runs against the suggestion's stored contract.
|
|
225
|
+
* - `override` — mint a fresh blueprintId and run gen against the
|
|
226
|
+
* agent's NEW draft. The provisional id from the
|
|
227
|
+
* handshake is discarded.
|
|
228
|
+
*/
|
|
229
|
+
export type PushDecision = {
|
|
230
|
+
readonly kind: 'accept';
|
|
231
|
+
} | {
|
|
232
|
+
readonly kind: 'override';
|
|
233
|
+
readonly blueprintDraft: BlueprintDraft;
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Build a minimal JSON-Patch RFC 6902 diff between two contracts.
|
|
237
|
+
*
|
|
238
|
+
* Algorithm: shallow walk over the union of top-level keys; for each
|
|
239
|
+
* key, recurse into nested objects, otherwise emit `add` / `remove` /
|
|
240
|
+
* `replace` at the appropriate path. Arrays are diffed as whole values
|
|
241
|
+
* (no LCS) — sufficient for the synth-amendment use case where the
|
|
242
|
+
* synth model rewrites slot/action maps wholesale rather than
|
|
243
|
+
* splicing single array elements.
|
|
244
|
+
*
|
|
245
|
+
* Output is a {@link JsonPatch}; applying it to `before` produces
|
|
246
|
+
* `after` (modulo array-element identity).
|
|
247
|
+
*
|
|
248
|
+
* Pure / deterministic. Exposed so synth implementations don't need
|
|
249
|
+
* to ship their own diff helper.
|
|
250
|
+
*/
|
|
251
|
+
export declare function jsonPatch(before: unknown, after: unknown): JsonPatch;
|
|
252
|
+
/**
|
|
253
|
+
* Top-N alternative blueprints surfaced on the handshake response.
|
|
254
|
+
* Agents can override into one of these (push with `decision:
|
|
255
|
+
* 'override'`) — the alternatives are full {@link Blueprint} rows so
|
|
256
|
+
* the agent inspects everything it needs to decide.
|
|
257
|
+
*
|
|
258
|
+
* Sorted by descending match score; the suggestion's primary
|
|
259
|
+
* `blueprintMeta` is NOT duplicated here (the alternatives are
|
|
260
|
+
* what the search returned EXCLUDING the top result that became the
|
|
261
|
+
* primary).
|
|
262
|
+
*/
|
|
263
|
+
export type SuggestionAlternatives = readonly Blueprint[];
|
|
264
|
+
//# sourceMappingURL=handshake-suggestion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake-suggestion.d.ts","sourceRoot":"","sources":["../../src/types/handshake-suggestion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a minimal JSON-Patch RFC 6902 diff between two contracts.
|
|
3
|
+
*
|
|
4
|
+
* Algorithm: shallow walk over the union of top-level keys; for each
|
|
5
|
+
* key, recurse into nested objects, otherwise emit `add` / `remove` /
|
|
6
|
+
* `replace` at the appropriate path. Arrays are diffed as whole values
|
|
7
|
+
* (no LCS) — sufficient for the synth-amendment use case where the
|
|
8
|
+
* synth model rewrites slot/action maps wholesale rather than
|
|
9
|
+
* splicing single array elements.
|
|
10
|
+
*
|
|
11
|
+
* Output is a {@link JsonPatch}; applying it to `before` produces
|
|
12
|
+
* `after` (modulo array-element identity).
|
|
13
|
+
*
|
|
14
|
+
* Pure / deterministic. Exposed so synth implementations don't need
|
|
15
|
+
* to ship their own diff helper.
|
|
16
|
+
*/
|
|
17
|
+
export function jsonPatch(before, after) {
|
|
18
|
+
const ops = [];
|
|
19
|
+
buildPatchOps(before, after, '', ops);
|
|
20
|
+
return Object.freeze(ops);
|
|
21
|
+
}
|
|
22
|
+
function buildPatchOps(before, after, path, ops) {
|
|
23
|
+
if (before === after)
|
|
24
|
+
return;
|
|
25
|
+
// null / primitive replacements
|
|
26
|
+
if (before === null ||
|
|
27
|
+
after === null ||
|
|
28
|
+
typeof before !== 'object' ||
|
|
29
|
+
typeof after !== 'object') {
|
|
30
|
+
ops.push({ op: 'replace', path, value: after });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const beforeArr = Array.isArray(before);
|
|
34
|
+
const afterArr = Array.isArray(after);
|
|
35
|
+
if (beforeArr !== afterArr) {
|
|
36
|
+
// Whole-value replace when the kind flips (object ↔ array).
|
|
37
|
+
ops.push({ op: 'replace', path, value: after });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (beforeArr && afterArr) {
|
|
41
|
+
// Whole-array replace — sufficient for amendment diffs.
|
|
42
|
+
ops.push({ op: 'replace', path, value: after });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Both are plain objects.
|
|
46
|
+
const beforeObj = before;
|
|
47
|
+
const afterObj = after;
|
|
48
|
+
const keys = new Set([...Object.keys(beforeObj), ...Object.keys(afterObj)]);
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
const childPath = `${path}/${encodeJsonPointerSegment(key)}`;
|
|
51
|
+
const inBefore = Object.prototype.hasOwnProperty.call(beforeObj, key);
|
|
52
|
+
const inAfter = Object.prototype.hasOwnProperty.call(afterObj, key);
|
|
53
|
+
if (!inAfter) {
|
|
54
|
+
ops.push({ op: 'remove', path: childPath });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!inBefore) {
|
|
58
|
+
ops.push({ op: 'add', path: childPath, value: afterObj[key] });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
buildPatchOps(beforeObj[key], afterObj[key], childPath, ops);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Encode a single JSON-Pointer segment per RFC 6901 §4: `~` → `~0`,
|
|
66
|
+
* `/` → `~1`. Other characters pass through unchanged.
|
|
67
|
+
*/
|
|
68
|
+
function encodeJsonPointerSegment(seg) {
|
|
69
|
+
return seg.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
70
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host Context — projected subset of `McpUiHostContext` ggui captures from
|
|
3
|
+
* the MCP Apps `ui/initialize` response and echoes back to session state so
|
|
4
|
+
* the agent can reason about device/host capabilities on subsequent turns.
|
|
5
|
+
*
|
|
6
|
+
* The MCP Apps spec (`@modelcontextprotocol/ext-apps`) defines a rich
|
|
7
|
+
* `McpUiHostContext` (theme, styles, displayMode, availableDisplayModes,
|
|
8
|
+
* containerDimensions, locale, timeZone, userAgent, platform,
|
|
9
|
+
* deviceCapabilities). ggui captures it iframe-side at `ui/initialize` and
|
|
10
|
+
* echoes a TRIMMED projection back over the live channel (via the
|
|
11
|
+
* `host_context_observed` outbound message) so the server can persist it on
|
|
12
|
+
* `SessionRecord.hostContext` and surface it on `ggui_handshake` /
|
|
13
|
+
* `ggui_consume` output for the agent.
|
|
14
|
+
*
|
|
15
|
+
* Why a projection rather than passthrough:
|
|
16
|
+
*
|
|
17
|
+
* - `theme` / `styles` already flow through ggui's separate theming
|
|
18
|
+
* pipeline (`InterfaceContext` + the theme registry). Duplicating
|
|
19
|
+
* creates two sources of truth.
|
|
20
|
+
* - `toolInfo` is host-loop-internal — the agent has its own toolInfo via
|
|
21
|
+
* MCP framing.
|
|
22
|
+
* - `userAgent` is rarely actionable; skip until a concrete use case
|
|
23
|
+
* appears. Easy to add later (additive optional field).
|
|
24
|
+
*
|
|
25
|
+
* What the projection KEEPS:
|
|
26
|
+
*
|
|
27
|
+
* - `availableDisplayModes` / `currentDisplayMode` — drives canvas-mode
|
|
28
|
+
* display-mode escalation policy (see canvas-mode-detail-displaymode.md).
|
|
29
|
+
* - `containerDimensions` — lets the agent reason about layout density
|
|
30
|
+
* and lets the canvas reflow on resize.
|
|
31
|
+
* - `platform` / `deviceCapabilities` — feeds the generator's
|
|
32
|
+
* responsive-UI prompts.
|
|
33
|
+
* - `locale` / `timeZone` — useful for the agent's date/number rendering.
|
|
34
|
+
*
|
|
35
|
+
* Compatibility posture: every field is optional. Hosts that emit a
|
|
36
|
+
* minimal `McpUiHostContext` (spec-permissible) project to an empty
|
|
37
|
+
* object; consumers MUST handle every field as possibly absent.
|
|
38
|
+
*
|
|
39
|
+
* Versioning: the wire wrapper carries `schemaVersion` so future
|
|
40
|
+
* projection widenings can be detected; this module is the canonical
|
|
41
|
+
* shape for the current schema major.
|
|
42
|
+
*/
|
|
43
|
+
import type { JsonValue } from './data-contract';
|
|
44
|
+
/**
|
|
45
|
+
* The three display modes the MCP Apps spec defines. Mirror of
|
|
46
|
+
* `McpUiDisplayMode` from `@modelcontextprotocol/ext-apps` — re-declared
|
|
47
|
+
* here so the protocol package doesn't take a runtime dependency on the
|
|
48
|
+
* SDK (the SDK is consumed in iframe-runtime + system-card; the protocol
|
|
49
|
+
* package stays SDK-free per the layering boundary).
|
|
50
|
+
*
|
|
51
|
+
* Stay in sync with the SDK literal: `'inline' | 'fullscreen' | 'pip'`.
|
|
52
|
+
* If the spec adds a fourth mode, widen here and in
|
|
53
|
+
* `iframe-runtime`'s capability-resolution helpers in lockstep.
|
|
54
|
+
*/
|
|
55
|
+
export type McpUiDisplayMode = 'inline' | 'fullscreen' | 'pip';
|
|
56
|
+
/**
|
|
57
|
+
* Width specification — either fixed `width` or `maxWidth`, never both.
|
|
58
|
+
* Matches the spec's discriminated container-dimension shape.
|
|
59
|
+
*/
|
|
60
|
+
export interface HostContextWidth {
|
|
61
|
+
readonly width?: number;
|
|
62
|
+
readonly maxWidth?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Height specification — either fixed `height` or `maxHeight`, never both.
|
|
66
|
+
*/
|
|
67
|
+
export interface HostContextHeight {
|
|
68
|
+
readonly height?: number;
|
|
69
|
+
readonly maxHeight?: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Iframe / container dimensions reported by the host. Width and height
|
|
73
|
+
* are independently spec'd (one may be fixed, the other max-bounded).
|
|
74
|
+
*/
|
|
75
|
+
export type HostContextContainerDimensions = HostContextWidth & HostContextHeight;
|
|
76
|
+
/**
|
|
77
|
+
* Input capabilities reported by the host. Both `touch` and `hover` may
|
|
78
|
+
* be true (hybrid devices); both may be false (rare, e.g., voice-only
|
|
79
|
+
* hosts).
|
|
80
|
+
*/
|
|
81
|
+
export interface HostContextDeviceCapabilities {
|
|
82
|
+
readonly touch?: boolean;
|
|
83
|
+
readonly hover?: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Trimmed projection of `McpUiHostContext` that ggui captures iframe-side
|
|
87
|
+
* and echoes to session state for agent visibility.
|
|
88
|
+
*
|
|
89
|
+
* Every field is optional. Hosts that emit minimal context project to
|
|
90
|
+
* mostly-empty objects; consumers MUST treat every field as possibly
|
|
91
|
+
* absent and degrade gracefully.
|
|
92
|
+
*
|
|
93
|
+
* Theme + styles intentionally EXCLUDED — they flow through ggui's own
|
|
94
|
+
* theming pipeline (`InterfaceContext`, theme registry). Duplicating
|
|
95
|
+
* here would create two sources of truth.
|
|
96
|
+
*
|
|
97
|
+
* `userAgent` + `toolInfo` intentionally EXCLUDED for v1 — easy to add
|
|
98
|
+
* later if a concrete use case appears.
|
|
99
|
+
*/
|
|
100
|
+
export interface HostContextProjection {
|
|
101
|
+
/** Display modes the host can render this view in. Absent ⇒ assume `['inline']`. */
|
|
102
|
+
readonly availableDisplayModes?: readonly McpUiDisplayMode[];
|
|
103
|
+
/** Current display mode the host is rendering. Absent ⇒ assume `'inline'`. */
|
|
104
|
+
readonly currentDisplayMode?: McpUiDisplayMode;
|
|
105
|
+
/** Iframe container dimensions. Absent ⇒ unknown; use a reasonable default. */
|
|
106
|
+
readonly containerDimensions?: HostContextContainerDimensions;
|
|
107
|
+
/** Host platform classification. */
|
|
108
|
+
readonly platform?: 'web' | 'desktop' | 'mobile';
|
|
109
|
+
/** Touch / hover input capability. */
|
|
110
|
+
readonly deviceCapabilities?: HostContextDeviceCapabilities;
|
|
111
|
+
/** User's BCP-47 locale (e.g., `'en-US'`). */
|
|
112
|
+
readonly locale?: string;
|
|
113
|
+
/** User's IANA timezone (e.g., `'America/Los_Angeles'`). */
|
|
114
|
+
readonly timeZone?: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Live-channel inbound (client → server) payload that delivers the
|
|
118
|
+
* iframe-captured `HostContextProjection` to the server. Server-side
|
|
119
|
+
* handler writes to `SessionRecord.hostContext`; subsequent
|
|
120
|
+
* `ggui_handshake` / `ggui_consume` responses surface the value to the
|
|
121
|
+
* agent via the optional `client.hostContext` field.
|
|
122
|
+
*
|
|
123
|
+
* Emission cadence:
|
|
124
|
+
* - Once after the iframe-runtime's `ui/initialize` resolves (initial
|
|
125
|
+
* capture).
|
|
126
|
+
* - Once per `ui/notifications/host-context-changed` notification
|
|
127
|
+
* received from the host.
|
|
128
|
+
*
|
|
129
|
+
* Idempotent — re-delivery (e.g., after a reconnect) overwrites the
|
|
130
|
+
* stored value; no merge logic.
|
|
131
|
+
*/
|
|
132
|
+
export interface HostContextObservedPayload {
|
|
133
|
+
readonly sessionId: string;
|
|
134
|
+
readonly hostContext: HostContextProjection;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Project a raw `McpUiHostContext` (from the spec SDK or any equivalent
|
|
138
|
+
* shape — accepts `unknown` so callers don't need to drag in the SDK
|
|
139
|
+
* just to call this) into `HostContextProjection`.
|
|
140
|
+
*
|
|
141
|
+
* Defensive: every field crosses a trust boundary. Malformed inputs
|
|
142
|
+
* (wrong types, weird shapes) drop silently to undefined for that
|
|
143
|
+
* field rather than failing the whole projection. The whole capture
|
|
144
|
+
* path is best-effort — never blocks the bootstrap.
|
|
145
|
+
*
|
|
146
|
+
* Returns `undefined` when the input is not an object at all (caller
|
|
147
|
+
* received null / array / primitive from the host). Returns an empty
|
|
148
|
+
* object when the input is an object but no recognized fields are
|
|
149
|
+
* present — the distinction lets callers tell "host emitted context
|
|
150
|
+
* with no recognized fields" from "host emitted no context."
|
|
151
|
+
*/
|
|
152
|
+
export declare function projectHostContext(raw: unknown): HostContextProjection | undefined;
|
|
153
|
+
/**
|
|
154
|
+
* Deep equality check for two projections. Used by the iframe-runtime
|
|
155
|
+
* to suppress no-op re-emissions when a `host-context-changed`
|
|
156
|
+
* notification arrives but no projection-visible field actually changed.
|
|
157
|
+
*
|
|
158
|
+
* JSON-stringify is sufficient because the projection contains only
|
|
159
|
+
* primitives, arrays of primitives, and plain objects of primitives.
|
|
160
|
+
*/
|
|
161
|
+
export declare function hostContextProjectionsEqual(a: HostContextProjection | undefined, b: HostContextProjection | undefined): boolean;
|
|
162
|
+
export type { JsonValue };
|
|
163
|
+
//# sourceMappingURL=host-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-context.d.ts","sourceRoot":"","sources":["../../src/types/host-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAMjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC;AAM/D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;AAMlF;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB;IACpC,oFAAoF;IACpF,QAAQ,CAAC,qBAAqB,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC7D,8EAA8E;IAC9E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IAC/C,+EAA+E;IAC/E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,8BAA8B,CAAC;IAC9D,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,sCAAsC;IACtC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IAC5D,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAMD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC;CAC7C;AAoBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,qBAAqB,GAAG,SAAS,CAsDlF;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,CAAC,EAAE,qBAAqB,GAAG,SAAS,EACpC,CAAC,EAAE,qBAAqB,GAAG,SAAS,GACnC,OAAO,CAIT;AAGD,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host Context — projected subset of `McpUiHostContext` ggui captures from
|
|
3
|
+
* the MCP Apps `ui/initialize` response and echoes back to session state so
|
|
4
|
+
* the agent can reason about device/host capabilities on subsequent turns.
|
|
5
|
+
*
|
|
6
|
+
* The MCP Apps spec (`@modelcontextprotocol/ext-apps`) defines a rich
|
|
7
|
+
* `McpUiHostContext` (theme, styles, displayMode, availableDisplayModes,
|
|
8
|
+
* containerDimensions, locale, timeZone, userAgent, platform,
|
|
9
|
+
* deviceCapabilities). ggui captures it iframe-side at `ui/initialize` and
|
|
10
|
+
* echoes a TRIMMED projection back over the live channel (via the
|
|
11
|
+
* `host_context_observed` outbound message) so the server can persist it on
|
|
12
|
+
* `SessionRecord.hostContext` and surface it on `ggui_handshake` /
|
|
13
|
+
* `ggui_consume` output for the agent.
|
|
14
|
+
*
|
|
15
|
+
* Why a projection rather than passthrough:
|
|
16
|
+
*
|
|
17
|
+
* - `theme` / `styles` already flow through ggui's separate theming
|
|
18
|
+
* pipeline (`InterfaceContext` + the theme registry). Duplicating
|
|
19
|
+
* creates two sources of truth.
|
|
20
|
+
* - `toolInfo` is host-loop-internal — the agent has its own toolInfo via
|
|
21
|
+
* MCP framing.
|
|
22
|
+
* - `userAgent` is rarely actionable; skip until a concrete use case
|
|
23
|
+
* appears. Easy to add later (additive optional field).
|
|
24
|
+
*
|
|
25
|
+
* What the projection KEEPS:
|
|
26
|
+
*
|
|
27
|
+
* - `availableDisplayModes` / `currentDisplayMode` — drives canvas-mode
|
|
28
|
+
* display-mode escalation policy (see canvas-mode-detail-displaymode.md).
|
|
29
|
+
* - `containerDimensions` — lets the agent reason about layout density
|
|
30
|
+
* and lets the canvas reflow on resize.
|
|
31
|
+
* - `platform` / `deviceCapabilities` — feeds the generator's
|
|
32
|
+
* responsive-UI prompts.
|
|
33
|
+
* - `locale` / `timeZone` — useful for the agent's date/number rendering.
|
|
34
|
+
*
|
|
35
|
+
* Compatibility posture: every field is optional. Hosts that emit a
|
|
36
|
+
* minimal `McpUiHostContext` (spec-permissible) project to an empty
|
|
37
|
+
* object; consumers MUST handle every field as possibly absent.
|
|
38
|
+
*
|
|
39
|
+
* Versioning: the wire wrapper carries `schemaVersion` so future
|
|
40
|
+
* projection widenings can be detected; this module is the canonical
|
|
41
|
+
* shape for the current schema major.
|
|
42
|
+
*/
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// Projection helper
|
|
45
|
+
// =============================================================================
|
|
46
|
+
/**
|
|
47
|
+
* Type guard — `value` is a non-null, non-array plain object.
|
|
48
|
+
*/
|
|
49
|
+
function isPlainObject(value) {
|
|
50
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* True iff `value` is a valid `McpUiDisplayMode` literal.
|
|
54
|
+
*/
|
|
55
|
+
function isDisplayMode(value) {
|
|
56
|
+
return value === 'inline' || value === 'fullscreen' || value === 'pip';
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Project a raw `McpUiHostContext` (from the spec SDK or any equivalent
|
|
60
|
+
* shape — accepts `unknown` so callers don't need to drag in the SDK
|
|
61
|
+
* just to call this) into `HostContextProjection`.
|
|
62
|
+
*
|
|
63
|
+
* Defensive: every field crosses a trust boundary. Malformed inputs
|
|
64
|
+
* (wrong types, weird shapes) drop silently to undefined for that
|
|
65
|
+
* field rather than failing the whole projection. The whole capture
|
|
66
|
+
* path is best-effort — never blocks the bootstrap.
|
|
67
|
+
*
|
|
68
|
+
* Returns `undefined` when the input is not an object at all (caller
|
|
69
|
+
* received null / array / primitive from the host). Returns an empty
|
|
70
|
+
* object when the input is an object but no recognized fields are
|
|
71
|
+
* present — the distinction lets callers tell "host emitted context
|
|
72
|
+
* with no recognized fields" from "host emitted no context."
|
|
73
|
+
*/
|
|
74
|
+
export function projectHostContext(raw) {
|
|
75
|
+
if (!isPlainObject(raw))
|
|
76
|
+
return undefined;
|
|
77
|
+
const out = {};
|
|
78
|
+
// displayMode
|
|
79
|
+
if (isDisplayMode(raw.displayMode)) {
|
|
80
|
+
out.currentDisplayMode = raw.displayMode;
|
|
81
|
+
}
|
|
82
|
+
// availableDisplayModes
|
|
83
|
+
if (Array.isArray(raw.availableDisplayModes)) {
|
|
84
|
+
const filtered = raw.availableDisplayModes.filter(isDisplayMode);
|
|
85
|
+
if (filtered.length > 0)
|
|
86
|
+
out.availableDisplayModes = filtered;
|
|
87
|
+
}
|
|
88
|
+
// containerDimensions
|
|
89
|
+
if (isPlainObject(raw.containerDimensions)) {
|
|
90
|
+
const dims = {};
|
|
91
|
+
const cd = raw.containerDimensions;
|
|
92
|
+
if (typeof cd.width === 'number')
|
|
93
|
+
dims.width = cd.width;
|
|
94
|
+
if (typeof cd.maxWidth === 'number')
|
|
95
|
+
dims.maxWidth = cd.maxWidth;
|
|
96
|
+
if (typeof cd.height === 'number')
|
|
97
|
+
dims.height = cd.height;
|
|
98
|
+
if (typeof cd.maxHeight === 'number')
|
|
99
|
+
dims.maxHeight = cd.maxHeight;
|
|
100
|
+
if (Object.keys(dims).length > 0)
|
|
101
|
+
out.containerDimensions = dims;
|
|
102
|
+
}
|
|
103
|
+
// platform
|
|
104
|
+
if (raw.platform === 'web' || raw.platform === 'desktop' || raw.platform === 'mobile') {
|
|
105
|
+
out.platform = raw.platform;
|
|
106
|
+
}
|
|
107
|
+
// deviceCapabilities
|
|
108
|
+
if (isPlainObject(raw.deviceCapabilities)) {
|
|
109
|
+
const dc = {};
|
|
110
|
+
const src = raw.deviceCapabilities;
|
|
111
|
+
if (typeof src.touch === 'boolean')
|
|
112
|
+
dc.touch = src.touch;
|
|
113
|
+
if (typeof src.hover === 'boolean')
|
|
114
|
+
dc.hover = src.hover;
|
|
115
|
+
if (Object.keys(dc).length > 0)
|
|
116
|
+
out.deviceCapabilities = dc;
|
|
117
|
+
}
|
|
118
|
+
// locale
|
|
119
|
+
if (typeof raw.locale === 'string' && raw.locale.length > 0) {
|
|
120
|
+
out.locale = raw.locale;
|
|
121
|
+
}
|
|
122
|
+
// timeZone
|
|
123
|
+
if (typeof raw.timeZone === 'string' && raw.timeZone.length > 0) {
|
|
124
|
+
out.timeZone = raw.timeZone;
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Deep equality check for two projections. Used by the iframe-runtime
|
|
130
|
+
* to suppress no-op re-emissions when a `host-context-changed`
|
|
131
|
+
* notification arrives but no projection-visible field actually changed.
|
|
132
|
+
*
|
|
133
|
+
* JSON-stringify is sufficient because the projection contains only
|
|
134
|
+
* primitives, arrays of primitives, and plain objects of primitives.
|
|
135
|
+
*/
|
|
136
|
+
export function hostContextProjectionsEqual(a, b) {
|
|
137
|
+
if (a === b)
|
|
138
|
+
return true;
|
|
139
|
+
if (a === undefined || b === undefined)
|
|
140
|
+
return false;
|
|
141
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
142
|
+
}
|