@guuey/mcp-apps-host 0.26.0 → 0.28.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.
@@ -105,9 +105,26 @@ export declare function toolResultUiResource(block: Extract<AgBlock, {
105
105
  */
106
106
  export declare function uiLocator(uiData: JsonValue | undefined): string | undefined;
107
107
  /**
108
- * The `ui://` locator a `tool-result` block carries, from EITHER channel it
108
+ * The `ui://` locator a `tool-result` block carries, from ANY channel it
109
109
  * can arrive on — the single seam every locator reader goes through.
110
110
  *
111
+ * `_meta.ui.resourceUri` is read FIRST. Under AgJSON draft.4 (core 0.7.0) a
112
+ * kept-open result's later `tool.done` replaces the payload as a unit, so a
113
+ * final that omits `uiData` / `structuredContent` CLEARS them on the fold,
114
+ * while `_meta` (a descriptor) survives an omitting final. The view's
115
+ * identity lives there by the spec's own rule. Reading the payload channels
116
+ * alone would take a folded card dark. Under 0.6.x both channels were kept,
117
+ * so this order gives the same answer on either version.
118
+ *
119
+ * Producers differ in what they put on `_meta.ui`. A server that only MARKS
120
+ * a result as UI (`_meta.ui: {}`) and carries the locator in
121
+ * `structuredContent` names no locator here, so the payload order decides:
122
+ * the same answer as before. A server that states `_meta.ui.resourceUri`
123
+ * gets that one, even when its payload names another, because the spec makes
124
+ * `_meta.ui` the durable identity. Note that a kept-open result (`more:
125
+ * true`) is what exercises the clearing; a producer that never keeps results
126
+ * open never folds an omitting final.
127
+ *
111
128
  * AgJSON §2.1 routes a tool result's `structuredContent` by its `_meta.ui`
112
129
  * sibling: WITH the sibling it is surface data and the normalizer stamps
113
130
  * `uiData`; WITHOUT it, it is model-channel data and lands in
@@ -115,10 +132,16 @@ export declare function uiLocator(uiData: JsonValue | undefined): string | undef
115
132
  * posture; any plain-locator MCP server) therefore delivers a locator that
116
133
  * is byte-identical in shape but lives one field over — reading `uiData`
117
134
  * alone renders NOTHING for it (dark, not "expired"), and the persistence
118
- * projector minted no placeholder row (the read plane 404s). `uiData` wins
119
- * when both carry one (guuey#209 route-A finding).
135
+ * projector minted no placeholder row (the read plane 404s). Between the
136
+ * payload channels `uiData` wins when both carry one (guuey#209 route-A
137
+ * finding). The pod's `blockLocator` (nocode-runtime live-cards.ts) and the
138
+ * persisted door's `snapshotCarriesLocator` (publicApi ui-resource.ts) apply
139
+ * the same order, so live and persisted guards recognize one locator set.
120
140
  */
121
141
  export declare function toolResultLocator(block: {
142
+ _meta?: {
143
+ [key: string]: JsonValue;
144
+ };
122
145
  uiData?: JsonValue;
123
146
  structuredContent?: JsonValue;
124
147
  }): string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"block-ui.d.ts","sourceRoot":"","sources":["../src/block-ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,CAAC,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAExF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAU5F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAK5F;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,GAAG,SAAS,GACzB,oBAAoB,GAAG,SAAS,CAMlC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAyBlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,GAC/C,oBAAoB,GAAG,SAAS,CAUlC;AAED;;;;;;;;;;;GAWG;AACH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,iBAAiB,CAAC,EAAE,SAAS,CAAC;CAC/B,GAAG,MAAM,GAAG,SAAS,CAErB;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAU5F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAU/E"}
1
+ {"version":3,"file":"block-ui.d.ts","sourceRoot":"","sources":["../src/block-ui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,CAAC,IAAI;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAExF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAU5F;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAK5F;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,GAAG,SAAS,GACzB,oBAAoB,GAAG,SAAS,CAMlC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAyBlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,GAC/C,oBAAoB,GAAG,SAAS,CAUlC;AAED;;;;;;;;;;;GAWG;AACH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACrC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,iBAAiB,CAAC,EAAE,SAAS,CAAC;CAC/B,GAAG,MAAM,GAAG,SAAS,CAErB;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAU5F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAU/E"}
package/dist/block-ui.js CHANGED
@@ -141,9 +141,26 @@ export function uiLocator(uiData) {
141
141
  return typeof uri === "string" && uri.startsWith("ui://") ? uri : undefined;
142
142
  }
143
143
  /**
144
- * The `ui://` locator a `tool-result` block carries, from EITHER channel it
144
+ * The `ui://` locator a `tool-result` block carries, from ANY channel it
145
145
  * can arrive on — the single seam every locator reader goes through.
146
146
  *
147
+ * `_meta.ui.resourceUri` is read FIRST. Under AgJSON draft.4 (core 0.7.0) a
148
+ * kept-open result's later `tool.done` replaces the payload as a unit, so a
149
+ * final that omits `uiData` / `structuredContent` CLEARS them on the fold,
150
+ * while `_meta` (a descriptor) survives an omitting final. The view's
151
+ * identity lives there by the spec's own rule. Reading the payload channels
152
+ * alone would take a folded card dark. Under 0.6.x both channels were kept,
153
+ * so this order gives the same answer on either version.
154
+ *
155
+ * Producers differ in what they put on `_meta.ui`. A server that only MARKS
156
+ * a result as UI (`_meta.ui: {}`) and carries the locator in
157
+ * `structuredContent` names no locator here, so the payload order decides:
158
+ * the same answer as before. A server that states `_meta.ui.resourceUri`
159
+ * gets that one, even when its payload names another, because the spec makes
160
+ * `_meta.ui` the durable identity. Note that a kept-open result (`more:
161
+ * true`) is what exercises the clearing; a producer that never keeps results
162
+ * open never folds an omitting final.
163
+ *
147
164
  * AgJSON §2.1 routes a tool result's `structuredContent` by its `_meta.ui`
148
165
  * sibling: WITH the sibling it is surface data and the normalizer stamps
149
166
  * `uiData`; WITHOUT it, it is model-channel data and lands in
@@ -151,11 +168,14 @@ export function uiLocator(uiData) {
151
168
  * posture; any plain-locator MCP server) therefore delivers a locator that
152
169
  * is byte-identical in shape but lives one field over — reading `uiData`
153
170
  * alone renders NOTHING for it (dark, not "expired"), and the persistence
154
- * projector minted no placeholder row (the read plane 404s). `uiData` wins
155
- * when both carry one (guuey#209 route-A finding).
171
+ * projector minted no placeholder row (the read plane 404s). Between the
172
+ * payload channels `uiData` wins when both carry one (guuey#209 route-A
173
+ * finding). The pod's `blockLocator` (nocode-runtime live-cards.ts) and the
174
+ * persisted door's `snapshotCarriesLocator` (publicApi ui-resource.ts) apply
175
+ * the same order, so live and persisted guards recognize one locator set.
156
176
  */
157
177
  export function toolResultLocator(block) {
158
- return uiLocator(block.uiData) ?? uiLocator(block.structuredContent);
178
+ return uiLocator(block._meta?.ui) ?? uiLocator(block.uiData) ?? uiLocator(block.structuredContent);
159
179
  }
160
180
  export function snapshotUiResource(cardSnapshot) {
161
181
  if (!isJsonObject(cardSnapshot))
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { createMcpUiResourceReader, declaredResourceCsp, uiResourceChannel, type
10
10
  export type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps";
11
11
  export { asToolCallResult, createMcpUiActionRelay, unavailableToolCallResult, UI_ACTION_TOOLS, UI_ACTION_UNAVAILABLE_TEXT, UI_SEMANTIC_ACTION_TOOLS, type CreateMcpUiActionRelayDeps, type McpToolCallContent, type McpToolCallResult, type McpToolStructuredContent, type UiActionRequest, } from "./action.js";
12
12
  export { initializeResult, initialViewHostState, resourceReadResponse, RESOURCES_READ_METHOD, teardownMessage, toolCallResponse, TOOLS_CALL_METHOD, viewHostElapsed, viewHostReceive, diagnoseCspViolation, type CspViolationLike, type ViewCspDiagnosis, type ViewCspOrigins, type ViewHostBehavior, type ViewHostEffect, type ViewHostOutbound, type ViewHostPhase, type ViewHostInfo, type ViewHostState, type ViewHostTransition, type ViewRequestId, } from "./view-host-protocol.js";
13
- export { attachViewHost, viewDocumentHtml, type AttachViewHostConfig, type ViewCspEvents, type ViewFrameLike, type ViewHostEvents, } from "./view-host.js";
13
+ export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS, type AttachViewHostConfig, type UserMessageDelivery, type ViewCspEvents, type ViewFrameLike, type ViewHostEvents, } from "./view-host.js";
14
14
  export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, type SandboxPageDeliveryConfig, } from "./sandbox-page.js";
15
15
  export { toolResultRenderRefusal, type PreGenerationRefusal } from "./render-refusal.js";
16
16
  export { EMPTY_HOST_STYLE_VARIABLES, announcedHostStyleVariables, hostStyleVariablesRecord, type McpUiStyleVariableKey, type McpUiStyles, } from "./host-style-variables.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAIrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIzF,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,GACjB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAC;AAIrB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,uBAAuB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIzF,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,GACjB,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ export { ACTION_STAGED_MSG, isStageableAction, stagedActionText, withActionStagi
9
9
  export { createMcpUiResourceReader, declaredResourceCsp, uiResourceChannel, } from "./reader.js";
10
10
  export { asToolCallResult, createMcpUiActionRelay, unavailableToolCallResult, UI_ACTION_TOOLS, UI_ACTION_UNAVAILABLE_TEXT, UI_SEMANTIC_ACTION_TOOLS, } from "./action.js";
11
11
  export { initializeResult, initialViewHostState, resourceReadResponse, RESOURCES_READ_METHOD, teardownMessage, toolCallResponse, TOOLS_CALL_METHOD, viewHostElapsed, viewHostReceive, diagnoseCspViolation, } from "./view-host-protocol.js";
12
- export { attachViewHost, viewDocumentHtml, } from "./view-host.js";
12
+ export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS, } from "./view-host.js";
13
13
  export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, } from "./sandbox-page.js";
14
14
  // guuey#836 — the typed read of a ggui_render PRE-GENERATION refusal
15
15
  // (`@ggui-ai/protocol` 0.14.0's third `outcome`). Hosts face it with words
package/dist/react.d.ts CHANGED
@@ -37,8 +37,8 @@ import { type AttachViewHostConfig } from "./view-host.js";
37
37
  import type { ViewCspDiagnosis, ViewHostPhase } from "./view-host-protocol.js";
38
38
  import type { ResolvedViewMount } from "./card-mount.js";
39
39
  import { type ViewHostStrings } from "./view-strings.js";
40
- export { attachViewHost, viewDocumentHtml } from "./view-host.js";
41
- export type { AttachViewHostConfig, ViewCspEvents, ViewFrameLike, ViewHostEvents } from "./view-host.js";
40
+ export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS } from "./view-host.js";
41
+ export type { AttachViewHostConfig, UserMessageDelivery, ViewCspEvents, ViewFrameLike, ViewHostEvents } from "./view-host.js";
42
42
  export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, type SandboxPageDeliveryConfig, } from "./sandbox-page.js";
43
43
  export type { ViewCspDiagnosis, ViewCspOrigins, ViewHostPhase } from "./view-host-protocol.js";
44
44
  export type { ResolvedViewMount, ViewMount, ViewMountChannel } from "./card-mount.js";
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAwC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjG,OAAO,EAAoC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzG,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKzD,MAAM,WAAW,cACf,SAAQ,IAAI,CACV,oBAAoB,EAClB,kBAAkB,GAClB,UAAU,GACV,aAAa,GACb,YAAY,GACZ,sBAAsB,GACtB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,sBAAsB,GACtB,YAAY,GACZ,gBAAgB,CACnB;IACD,iFAAiF;IACjF,KAAK,EAAE,iBAAiB,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC;IACjF;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CACpC;AAkDD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAsL1D"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAwC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjG,OAAO,EAAoC,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACzF,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9H,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/F,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKzD,MAAM,WAAW,cACf,SAAQ,IAAI,CACV,oBAAoB,EAClB,kBAAkB,GAClB,UAAU,GACV,aAAa,GACb,YAAY,GACZ,sBAAsB,GACtB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,WAAW,GACX,sBAAsB,GACtB,YAAY,GACZ,gBAAgB,CACnB;IACD,iFAAiF;IACjF,KAAK,EAAE,iBAAiB,CAAC;IACzB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;IACtC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,gBAAgB,KAAK,SAAS,CAAC;IACjF;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;CACpC;AAkDD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CAsL1D"}
package/dist/react.js CHANGED
@@ -38,7 +38,7 @@ import { attachViewHost, viewDocumentHtml } from "./view-host.js";
38
38
  import { attachSandboxPageDelivery } from "./sandbox-page.js";
39
39
  import { decideAutoResize, initialAutoResizeState } from "./auto-resize.js";
40
40
  import { defaultViewHostStrings } from "./view-strings.js";
41
- export { attachViewHost, viewDocumentHtml } from "./view-host.js";
41
+ export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS } from "./view-host.js";
42
42
  export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, } from "./sandbox-page.js";
43
43
  export { defaultViewHostStrings } from "./view-strings.js";
44
44
  /** Accessible name for a mounted view when the caller has nothing better. */
@@ -176,8 +176,11 @@ export type ViewHostEffect = {
176
176
  * A `ui/message` the view handed the host (guuey#422): role-user
177
177
  * content blocks the host forwards into ITS conversation loop (the
178
178
  * composer's send gate — a real turn starts). ggui's #440 post-turn
179
- * doorbell rides this. The machine already ANSWERED the view (`{}` —
180
- * accepted); delivery is this effect. Only emitted when
179
+ * doorbell rides this. Under `messageAnswer: "on-accept"` (the default)
180
+ * the machine already ANSWERED the view (`{}` — accepted) and delivery is
181
+ * this effect; under `"on-delivery"` (guuey#1706) this effect is the ONLY
182
+ * output and the host owes the view its answer — `{}` delivered, `{
183
+ * isError: true }` not — keyed by `id`. Only emitted when
181
184
  * {@link ViewHostBehavior.messageSink} is true.
182
185
  */
183
186
  kind: "user-message";
@@ -254,6 +257,16 @@ export interface ViewHostBehavior {
254
257
  modelContextSink: boolean;
255
258
  /** Whether a ui/message sink is wired (see `view-host.ts`, guuey#422). */
256
259
  messageSink: boolean;
260
+ /**
261
+ * WHO answers a `ui/message`, and when (guuey#1706). Default `"on-accept"`:
262
+ * the machine answers `{}` BEFORE delivery and emits `user-message` — the
263
+ * pre-#1706 contract, kept for direct embedders of {@link viewHostReceive}.
264
+ * `"on-delivery"`: the machine emits `user-message` ONLY and the HOST answers
265
+ * from the sink's delivery outcome (`{}` delivered · `{ isError: true }` not),
266
+ * so a view learns whether its message actually became a turn. This
267
+ * package's own `attachViewHost` sets `"on-delivery"`.
268
+ */
269
+ messageAnswer?: "on-accept" | "on-delivery";
257
270
  /**
258
271
  * `ui/open-link` wired (guuey#522). Unwired, the method refuses with
259
272
  * the honest -32601 — the host chose today's no-links posture.
@@ -1 +1 @@
1
- {"version":3,"file":"view-host-protocol.d.ts","sourceRoot":"","sources":["../src/view-host-protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view-host-protocol.d.ts","sourceRoot":"","sources":["../src/view-host-protocol.ts"],"names":[],"mappings":"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"}
@@ -292,10 +292,15 @@ export function viewHostReceive(state, behavior, data) {
292
292
  };
293
293
  }
294
294
  if (req.method === MESSAGE_METHOD_LOCAL && behavior.messageSink) {
295
- // Answer accepted FIRST (spec result: rejected-flag absent = delivered)
296
- // and deliver as an effect — the sink starting a turn must never make
297
- // the view wait on the model.
298
295
  const params = isPlainObject(req.params) ? req.params : {};
296
+ if (behavior.messageAnswer === "on-delivery") {
297
+ // guuey#1706: no pre-answer. The host posts the answer from the sink's
298
+ // delivery outcome — `{}` once the message became a turn, `{ isError: true }`
299
+ // when it could not (spec result: `isError` is the only rejection word).
300
+ return { state, effects: [{ kind: "user-message", id: req.id, params }] };
301
+ }
302
+ // "on-accept" (the default): answer accepted FIRST (spec result: the
303
+ // rejected flag absent = delivered) and deliver as an effect.
299
304
  return {
300
305
  state,
301
306
  effects: [
@@ -67,6 +67,36 @@ export interface ViewCspEvents {
67
67
  addEventListener(type: "securitypolicyviolation", listener: (event: CspViolationLike) => void): void;
68
68
  removeEventListener(type: "securitypolicyviolation", listener: (event: CspViolationLike) => void): void;
69
69
  }
70
+ /**
71
+ * What a `ui/message` sink reports back so the host can answer the view
72
+ * truthfully (guuey#1706): `{ delivered: true }` once the message BECAME a
73
+ * turn — sent through the host's composer gate, now or after waiting behind an
74
+ * in-progress turn — or `{ delivered: false, reason }` when it could not (the
75
+ * chat is unavailable; it was dropped from the queue).
76
+ */
77
+ export type UserMessageDelivery = {
78
+ delivered: true;
79
+ } | {
80
+ delivered: false;
81
+ reason: string;
82
+ };
83
+ /**
84
+ * The longest the host holds a view's `ui/message` answer (guuey#1706).
85
+ *
86
+ * A spec MCP App awaiting `app.sendMessage` times out at the MCP SDK's default
87
+ * request timeout (`DEFAULT_REQUEST_TIMEOUT_MSEC` = 60 s in
88
+ * `@modelcontextprotocol/sdk` 1.30.0), so a message still queued behind a long
89
+ * turn is answered `{}` at this cap rather than timing the view out on a message
90
+ * that WILL deliver. What that trades, exactly: a prior turn LONGER than 50 s
91
+ * AND the session ending while the message is still queued leaves the view
92
+ * holding `{}` for a message that never sends — visible to operators only (the
93
+ * sink's console trace). `{ isError: true }` at the cap would be the other lie
94
+ * (the view shows "failed", then the message sends when the turn ends). The
95
+ * spec's result carries only `isError`, so "queued" has no word of its own.
96
+ * ggui's views — today's only producer — post `ui/message` raw and never await
97
+ * the answer, so the cap has no consumer there.
98
+ */
99
+ export declare const MESSAGE_ANSWER_CAP_MS = 50000;
70
100
  export interface AttachViewHostConfig {
71
101
  /**
72
102
  * Capabilities to advertise in the initialize result. Default: `{}` —
@@ -116,11 +146,20 @@ export interface AttachViewHostConfig {
116
146
  * turn). ggui's #440 post-turn doorbell depends on this — without it,
117
147
  * a successfully-relayed post-turn gesture dead-ends in their
118
148
  * 'cannot relay actions' latch. Wiring it advertises `message`
119
- * (text modality); the machine answers the view BEFORE delivery.
149
+ * (text modality).
150
+ *
151
+ * The answer (guuey#1706): return a Promise of {@link UserMessageDelivery}
152
+ * and the host answers the view from it — `{}` delivered, `{ isError: true }`
153
+ * not, `{}` if still pending at {@link MESSAGE_ANSWER_CAP_MS}. A sink that
154
+ * returns nothing, synchronously OR as a Promise that resolves to nothing
155
+ * (an `async` sink that delivers itself), is answered `{}`: the pre-#1706
156
+ * answer, kept for existing embedders. A sink that throws or rejects, or a
157
+ * resolution the host cannot read, is answered `{ isError: true }`. The view
158
+ * is answered exactly once on every path.
120
159
  */
121
160
  onUserMessage?: (params: {
122
161
  [key: string]: unknown;
123
- }) => void;
162
+ }) => void | Promise<UserMessageDelivery | void>;
124
163
  /**
125
164
  * `ui/open-link` executor (guuey#522) — receives a URL the machine's
126
165
  * scheme wall already passed (absolute http/https only). The kit
@@ -220,12 +259,6 @@ export interface AttachViewHostConfig {
220
259
  /** Message-event source, injectable for tests. Default: `window`. */
221
260
  events?: ViewHostEvents;
222
261
  }
223
- /**
224
- * Attach the Host role to a mounted view frame. Returns a detach function;
225
- * call it before the frame unmounts — it stops listening and posts the
226
- * spec-mannered `ui/resource-teardown` farewell through the CACHED window
227
- * handle (post-removal, `frame.contentWindow` is already null).
228
- */
229
262
  export declare function attachViewHost(frame: ViewFrameLike, config?: AttachViewHostConfig): () => void;
230
263
  /**
231
264
  * The document a {@link McpUiResourcePayload} mounts: `text` verbatim, or
@@ -1 +1 @@
1
- {"version":3,"file":"view-host.d.ts","sourceRoot":"","sources":["../src/view-host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view-host.d.ts","sourceRoot":"","sources":["../src/view-host.ts"],"names":[],"mappings":"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"}
package/dist/view-host.js CHANGED
@@ -1,5 +1,22 @@
1
1
  import { diagnoseCspViolation, initialViewHostState, resourceReadResponse, teardownMessage, toolCallResponse, viewHostElapsed, viewHostReceive, } from "./view-host-protocol.js";
2
2
  import { unavailableToolCallResult, } from "./action.js";
3
+ /**
4
+ * The longest the host holds a view's `ui/message` answer (guuey#1706).
5
+ *
6
+ * A spec MCP App awaiting `app.sendMessage` times out at the MCP SDK's default
7
+ * request timeout (`DEFAULT_REQUEST_TIMEOUT_MSEC` = 60 s in
8
+ * `@modelcontextprotocol/sdk` 1.30.0), so a message still queued behind a long
9
+ * turn is answered `{}` at this cap rather than timing the view out on a message
10
+ * that WILL deliver. What that trades, exactly: a prior turn LONGER than 50 s
11
+ * AND the session ending while the message is still queued leaves the view
12
+ * holding `{}` for a message that never sends — visible to operators only (the
13
+ * sink's console trace). `{ isError: true }` at the cap would be the other lie
14
+ * (the view shows "failed", then the message sends when the turn ends). The
15
+ * spec's result carries only `isError`, so "queued" has no word of its own.
16
+ * ggui's views — today's only producer — post `ui/message` raw and never await
17
+ * the answer, so the cap has no consumer there.
18
+ */
19
+ export const MESSAGE_ANSWER_CAP_MS = 50_000;
3
20
  const DEFAULT_HOST_INFO = { name: "guuey-view-host", version: "1" };
4
21
  const DEFAULT_NEGOTIATION_TIMEOUT_MS = 8000;
5
22
  /** Derived + configured context, per the {@link AttachViewHostConfig.hostContext} contract. */
@@ -38,6 +55,8 @@ function behaviorFor(frame, config) {
38
55
  resourceRelay: readWired,
39
56
  modelContextSink: contextSinkWired,
40
57
  messageSink: messageSinkWired,
58
+ // guuey#1706: this host answers `ui/message` from the sink's delivery outcome.
59
+ messageAnswer: "on-delivery",
41
60
  };
42
61
  }
43
62
  /**
@@ -65,6 +84,16 @@ function narrowReadEntry(entry) {
65
84
  * spec-mannered `ui/resource-teardown` farewell through the CACHED window
66
85
  * handle (post-removal, `frame.contentWindow` is already null).
67
86
  */
87
+ /**
88
+ * The view's answer for a sink's resolved value (guuey#1706). Only an explicit
89
+ * `{ delivered: false }` is a refusal. A resolution to nothing — an `async`
90
+ * sink that delivers itself and returns nothing, legal TypeScript through
91
+ * void-return substitutability — is ACCEPTED, `{}`, exactly as a synchronous
92
+ * `void` is: the pre-#1706 contract, kept for every existing embedder.
93
+ */
94
+ function answerForDelivery(delivery) {
95
+ return typeof delivery === "object" && delivery !== null && delivery.delivered === false ? { isError: true } : {};
96
+ }
68
97
  export function attachViewHost(frame, config = {}) {
69
98
  const cachedWindow = frame.contentWindow;
70
99
  let state = initialViewHostState();
@@ -77,6 +106,48 @@ export function attachViewHost(frame, config = {}) {
77
106
  const post = (message) => {
78
107
  frame.contentWindow?.postMessage(message, "*");
79
108
  };
109
+ // guuey#1706 — the machine emits `user-message` WITHOUT answering
110
+ // (`messageAnswer: "on-delivery"`); the view is answered here, from the
111
+ // sink's delivery outcome, exactly once, and never later than the cap.
112
+ const pendingAnswerCaps = new Set();
113
+ const answerMessage = (id, params) => {
114
+ // ONE exit (guuey-cto on 0.27.0): every path answers through `settle`,
115
+ // exactly once, and clears the cap on the way out. Nothing after the
116
+ // settle can reach the view, and no path can end without one.
117
+ let settled = false;
118
+ let cap = undefined;
119
+ const settle = (result) => {
120
+ if (settled)
121
+ return;
122
+ settled = true;
123
+ if (cap !== undefined) {
124
+ clearTimeout(cap);
125
+ pendingAnswerCaps.delete(cap);
126
+ }
127
+ post({ jsonrpc: "2.0", id, result });
128
+ };
129
+ let outcome;
130
+ try {
131
+ outcome = config.onUserMessage?.(params);
132
+ }
133
+ catch {
134
+ // A throwing sink delivered nothing — the view hears so, never a hang.
135
+ settle({ isError: true });
136
+ return;
137
+ }
138
+ if (!(outcome instanceof Promise)) {
139
+ // A sink that returns nothing: accepted at call time (the pre-#1706 answer).
140
+ settle({});
141
+ return;
142
+ }
143
+ cap = setTimeout(() => settle({}), MESSAGE_ANSWER_CAP_MS); // still queued: see MESSAGE_ANSWER_CAP_MS
144
+ pendingAnswerCaps.add(cap);
145
+ outcome
146
+ .then((delivery) => settle(answerForDelivery(delivery)), () => settle({ isError: true }))
147
+ // A throw while handling the resolution (a `delivered` the host cannot
148
+ // read) is an answer the view still gets.
149
+ .catch(() => settle({ isError: true }));
150
+ };
80
151
  const relay = (id, name, args) => {
81
152
  const { onCallTool, resourceUri } = config;
82
153
  // The machine only emits the effect when the relay is wired (behavior
@@ -111,14 +182,8 @@ export function attachViewHost(frame, config = {}) {
111
182
  relay(effect.id, effect.name, effect.arguments);
112
183
  else if (effect.kind === "relay-resource-read")
113
184
  relayRead(effect.id, effect.uri);
114
- else if (effect.kind === "user-message") {
115
- try {
116
- config.onUserMessage?.(effect.params);
117
- }
118
- catch {
119
- // Observer failure is the embedder's bug; the view is answered.
120
- }
121
- }
185
+ else if (effect.kind === "user-message")
186
+ answerMessage(effect.id, effect.params);
122
187
  else if (effect.kind === "open-link") {
123
188
  // The machine's scheme wall already passed this URL; the executor
124
189
  // SURFACES it to the human (the kit default renders a disclosure
@@ -210,6 +275,9 @@ export function attachViewHost(frame, config = {}) {
210
275
  return () => {
211
276
  if (timer !== undefined)
212
277
  clearTimeout(timer);
278
+ for (const cap of pendingAnswerCaps)
279
+ clearTimeout(cap);
280
+ pendingAnswerCaps.clear();
213
281
  unsubscribe();
214
282
  unsubscribeCsp();
215
283
  cachedWindow?.postMessage(teardownMessage(), "*");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guuey/mcp-apps-host",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "The MCP Apps (SEP-1865) Host role for guuey's chat surfaces — view-mount narrowing across UI channels, ui:// locator rehydration by resources/read, and the sandbox-trust channel contract. Vendor-neutral: any spec-following MCP App mounts through it.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,9 +33,9 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@ggui-ai/protocol": "0.19.0",
36
+ "@ggui-ai/protocol": "0.23.0",
37
37
  "@modelcontextprotocol/ext-apps": "^1.7.5",
38
- "@silverprotocol/core": "0.6.2"
38
+ "@silverprotocol/core": "0.7.2"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": ">=18"
package/src/block-ui.ts CHANGED
@@ -176,9 +176,26 @@ export function uiLocator(uiData: JsonValue | undefined): string | undefined {
176
176
  }
177
177
 
178
178
  /**
179
- * The `ui://` locator a `tool-result` block carries, from EITHER channel it
179
+ * The `ui://` locator a `tool-result` block carries, from ANY channel it
180
180
  * can arrive on — the single seam every locator reader goes through.
181
181
  *
182
+ * `_meta.ui.resourceUri` is read FIRST. Under AgJSON draft.4 (core 0.7.0) a
183
+ * kept-open result's later `tool.done` replaces the payload as a unit, so a
184
+ * final that omits `uiData` / `structuredContent` CLEARS them on the fold,
185
+ * while `_meta` (a descriptor) survives an omitting final. The view's
186
+ * identity lives there by the spec's own rule. Reading the payload channels
187
+ * alone would take a folded card dark. Under 0.6.x both channels were kept,
188
+ * so this order gives the same answer on either version.
189
+ *
190
+ * Producers differ in what they put on `_meta.ui`. A server that only MARKS
191
+ * a result as UI (`_meta.ui: {}`) and carries the locator in
192
+ * `structuredContent` names no locator here, so the payload order decides:
193
+ * the same answer as before. A server that states `_meta.ui.resourceUri`
194
+ * gets that one, even when its payload names another, because the spec makes
195
+ * `_meta.ui` the durable identity. Note that a kept-open result (`more:
196
+ * true`) is what exercises the clearing; a producer that never keeps results
197
+ * open never folds an omitting final.
198
+ *
182
199
  * AgJSON §2.1 routes a tool result's `structuredContent` by its `_meta.ui`
183
200
  * sibling: WITH the sibling it is surface data and the normalizer stamps
184
201
  * `uiData`; WITHOUT it, it is model-channel data and lands in
@@ -186,14 +203,18 @@ export function uiLocator(uiData: JsonValue | undefined): string | undefined {
186
203
  * posture; any plain-locator MCP server) therefore delivers a locator that
187
204
  * is byte-identical in shape but lives one field over — reading `uiData`
188
205
  * alone renders NOTHING for it (dark, not "expired"), and the persistence
189
- * projector minted no placeholder row (the read plane 404s). `uiData` wins
190
- * when both carry one (guuey#209 route-A finding).
206
+ * projector minted no placeholder row (the read plane 404s). Between the
207
+ * payload channels `uiData` wins when both carry one (guuey#209 route-A
208
+ * finding). The pod's `blockLocator` (nocode-runtime live-cards.ts) and the
209
+ * persisted door's `snapshotCarriesLocator` (publicApi ui-resource.ts) apply
210
+ * the same order, so live and persisted guards recognize one locator set.
191
211
  */
192
212
  export function toolResultLocator(block: {
213
+ _meta?: { [key: string]: JsonValue };
193
214
  uiData?: JsonValue;
194
215
  structuredContent?: JsonValue;
195
216
  }): string | undefined {
196
- return uiLocator(block.uiData) ?? uiLocator(block.structuredContent);
217
+ return uiLocator(block._meta?.ui) ?? uiLocator(block.uiData) ?? uiLocator(block.structuredContent);
197
218
  }
198
219
 
199
220
  export function snapshotUiResource(cardSnapshot: JsonValue): McpUiResourcePayload | undefined {
package/src/index.ts CHANGED
@@ -84,7 +84,9 @@ export {
84
84
  export {
85
85
  attachViewHost,
86
86
  viewDocumentHtml,
87
+ MESSAGE_ANSWER_CAP_MS,
87
88
  type AttachViewHostConfig,
89
+ type UserMessageDelivery,
88
90
  type ViewCspEvents,
89
91
  type ViewFrameLike,
90
92
  type ViewHostEvents,
package/src/react.tsx CHANGED
@@ -40,8 +40,8 @@ import type { ViewCspDiagnosis, ViewHostPhase } from "./view-host-protocol.js";
40
40
  import type { ResolvedViewMount } from "./card-mount.js";
41
41
  import { defaultViewHostStrings, type ViewHostStrings } from "./view-strings.js";
42
42
 
43
- export { attachViewHost, viewDocumentHtml } from "./view-host.js";
44
- export type { AttachViewHostConfig, ViewCspEvents, ViewFrameLike, ViewHostEvents } from "./view-host.js";
43
+ export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS } from "./view-host.js";
44
+ export type { AttachViewHostConfig, UserMessageDelivery, ViewCspEvents, ViewFrameLike, ViewHostEvents } from "./view-host.js";
45
45
  export {
46
46
  attachSandboxPageDelivery,
47
47
  isSandboxProxyReady,
@@ -205,8 +205,11 @@ export type ViewHostEffect =
205
205
  * A `ui/message` the view handed the host (guuey#422): role-user
206
206
  * content blocks the host forwards into ITS conversation loop (the
207
207
  * composer's send gate — a real turn starts). ggui's #440 post-turn
208
- * doorbell rides this. The machine already ANSWERED the view (`{}` —
209
- * accepted); delivery is this effect. Only emitted when
208
+ * doorbell rides this. Under `messageAnswer: "on-accept"` (the default)
209
+ * the machine already ANSWERED the view (`{}` — accepted) and delivery is
210
+ * this effect; under `"on-delivery"` (guuey#1706) this effect is the ONLY
211
+ * output and the host owes the view its answer — `{}` delivered, `{
212
+ * isError: true }` not — keyed by `id`. Only emitted when
210
213
  * {@link ViewHostBehavior.messageSink} is true.
211
214
  */
212
215
  kind: "user-message";
@@ -285,6 +288,16 @@ export interface ViewHostBehavior {
285
288
  modelContextSink: boolean;
286
289
  /** Whether a ui/message sink is wired (see `view-host.ts`, guuey#422). */
287
290
  messageSink: boolean;
291
+ /**
292
+ * WHO answers a `ui/message`, and when (guuey#1706). Default `"on-accept"`:
293
+ * the machine answers `{}` BEFORE delivery and emits `user-message` — the
294
+ * pre-#1706 contract, kept for direct embedders of {@link viewHostReceive}.
295
+ * `"on-delivery"`: the machine emits `user-message` ONLY and the HOST answers
296
+ * from the sink's delivery outcome (`{}` delivered · `{ isError: true }` not),
297
+ * so a view learns whether its message actually became a turn. This
298
+ * package's own `attachViewHost` sets `"on-delivery"`.
299
+ */
300
+ messageAnswer?: "on-accept" | "on-delivery";
288
301
  /**
289
302
  * `ui/open-link` wired (guuey#522). Unwired, the method refuses with
290
303
  * the honest -32601 — the host chose today's no-links posture.
@@ -532,10 +545,15 @@ export function viewHostReceive(
532
545
  }
533
546
 
534
547
  if (req.method === MESSAGE_METHOD_LOCAL && behavior.messageSink) {
535
- // Answer accepted FIRST (spec result: rejected-flag absent = delivered)
536
- // and deliver as an effect — the sink starting a turn must never make
537
- // the view wait on the model.
538
548
  const params = isPlainObject(req.params) ? req.params : {};
549
+ if (behavior.messageAnswer === "on-delivery") {
550
+ // guuey#1706: no pre-answer. The host posts the answer from the sink's
551
+ // delivery outcome — `{}` once the message became a turn, `{ isError: true }`
552
+ // when it could not (spec result: `isError` is the only rejection word).
553
+ return { state, effects: [{ kind: "user-message", id: req.id, params }] };
554
+ }
555
+ // "on-accept" (the default): answer accepted FIRST (spec result: the
556
+ // rejected flag absent = delivered) and deliver as an effect.
539
557
  return {
540
558
  state,
541
559
  effects: [
package/src/view-host.ts CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  type ViewHostOutbound,
44
44
  type ViewHostPhase,
45
45
  type ViewHostState,
46
+ type ViewRequestId,
46
47
  } from "./view-host-protocol.js";
47
48
  import type { McpResourceReadResult } from "./reader.js";
48
49
  import {
@@ -89,6 +90,33 @@ export interface ViewCspEvents {
89
90
  removeEventListener(type: "securitypolicyviolation", listener: (event: CspViolationLike) => void): void;
90
91
  }
91
92
 
93
+ /**
94
+ * What a `ui/message` sink reports back so the host can answer the view
95
+ * truthfully (guuey#1706): `{ delivered: true }` once the message BECAME a
96
+ * turn — sent through the host's composer gate, now or after waiting behind an
97
+ * in-progress turn — or `{ delivered: false, reason }` when it could not (the
98
+ * chat is unavailable; it was dropped from the queue).
99
+ */
100
+ export type UserMessageDelivery = { delivered: true } | { delivered: false; reason: string };
101
+
102
+ /**
103
+ * The longest the host holds a view's `ui/message` answer (guuey#1706).
104
+ *
105
+ * A spec MCP App awaiting `app.sendMessage` times out at the MCP SDK's default
106
+ * request timeout (`DEFAULT_REQUEST_TIMEOUT_MSEC` = 60 s in
107
+ * `@modelcontextprotocol/sdk` 1.30.0), so a message still queued behind a long
108
+ * turn is answered `{}` at this cap rather than timing the view out on a message
109
+ * that WILL deliver. What that trades, exactly: a prior turn LONGER than 50 s
110
+ * AND the session ending while the message is still queued leaves the view
111
+ * holding `{}` for a message that never sends — visible to operators only (the
112
+ * sink's console trace). `{ isError: true }` at the cap would be the other lie
113
+ * (the view shows "failed", then the message sends when the turn ends). The
114
+ * spec's result carries only `isError`, so "queued" has no word of its own.
115
+ * ggui's views — today's only producer — post `ui/message` raw and never await
116
+ * the answer, so the cap has no consumer there.
117
+ */
118
+ export const MESSAGE_ANSWER_CAP_MS = 50_000;
119
+
92
120
  export interface AttachViewHostConfig {
93
121
  /**
94
122
  * Capabilities to advertise in the initialize result. Default: `{}` —
@@ -136,9 +164,18 @@ export interface AttachViewHostConfig {
136
164
  * turn). ggui's #440 post-turn doorbell depends on this — without it,
137
165
  * a successfully-relayed post-turn gesture dead-ends in their
138
166
  * 'cannot relay actions' latch. Wiring it advertises `message`
139
- * (text modality); the machine answers the view BEFORE delivery.
167
+ * (text modality).
168
+ *
169
+ * The answer (guuey#1706): return a Promise of {@link UserMessageDelivery}
170
+ * and the host answers the view from it — `{}` delivered, `{ isError: true }`
171
+ * not, `{}` if still pending at {@link MESSAGE_ANSWER_CAP_MS}. A sink that
172
+ * returns nothing, synchronously OR as a Promise that resolves to nothing
173
+ * (an `async` sink that delivers itself), is answered `{}`: the pre-#1706
174
+ * answer, kept for existing embedders. A sink that throws or rejects, or a
175
+ * resolution the host cannot read, is answered `{ isError: true }`. The view
176
+ * is answered exactly once on every path.
140
177
  */
141
- onUserMessage?: (params: { [key: string]: unknown }) => void;
178
+ onUserMessage?: (params: { [key: string]: unknown }) => void | Promise<UserMessageDelivery | void>;
142
179
  /**
143
180
  * `ui/open-link` executor (guuey#522) — receives a URL the machine's
144
181
  * scheme wall already passed (absolute http/https only). The kit
@@ -276,6 +313,8 @@ function behaviorFor(frame: ViewFrameLike, config: AttachViewHostConfig): ViewHo
276
313
  resourceRelay: readWired,
277
314
  modelContextSink: contextSinkWired,
278
315
  messageSink: messageSinkWired,
316
+ // guuey#1706: this host answers `ui/message` from the sink's delivery outcome.
317
+ messageAnswer: "on-delivery",
279
318
  };
280
319
  }
281
320
 
@@ -303,6 +342,17 @@ function narrowReadEntry(entry: McpResourceReadResult | undefined): McpResourceR
303
342
  * spec-mannered `ui/resource-teardown` farewell through the CACHED window
304
343
  * handle (post-removal, `frame.contentWindow` is already null).
305
344
  */
345
+ /**
346
+ * The view's answer for a sink's resolved value (guuey#1706). Only an explicit
347
+ * `{ delivered: false }` is a refusal. A resolution to nothing — an `async`
348
+ * sink that delivers itself and returns nothing, legal TypeScript through
349
+ * void-return substitutability — is ACCEPTED, `{}`, exactly as a synchronous
350
+ * `void` is: the pre-#1706 contract, kept for every existing embedder.
351
+ */
352
+ function answerForDelivery(delivery: UserMessageDelivery | void): { isError?: true } {
353
+ return typeof delivery === "object" && delivery !== null && delivery.delivered === false ? { isError: true } : {};
354
+ }
355
+
306
356
  export function attachViewHost(frame: ViewFrameLike, config: AttachViewHostConfig = {}): () => void {
307
357
  const cachedWindow = frame.contentWindow;
308
358
 
@@ -318,6 +368,50 @@ export function attachViewHost(frame: ViewFrameLike, config: AttachViewHostConfi
318
368
  frame.contentWindow?.postMessage(message, "*");
319
369
  };
320
370
 
371
+ // guuey#1706 — the machine emits `user-message` WITHOUT answering
372
+ // (`messageAnswer: "on-delivery"`); the view is answered here, from the
373
+ // sink's delivery outcome, exactly once, and never later than the cap.
374
+ const pendingAnswerCaps = new Set<ReturnType<typeof setTimeout>>();
375
+ const answerMessage = (id: ViewRequestId, params: { [key: string]: unknown }): void => {
376
+ // ONE exit (guuey-cto on 0.27.0): every path answers through `settle`,
377
+ // exactly once, and clears the cap on the way out. Nothing after the
378
+ // settle can reach the view, and no path can end without one.
379
+ let settled = false;
380
+ let cap: ReturnType<typeof setTimeout> | undefined = undefined;
381
+ const settle = (result: { isError?: true }): void => {
382
+ if (settled) return;
383
+ settled = true;
384
+ if (cap !== undefined) {
385
+ clearTimeout(cap);
386
+ pendingAnswerCaps.delete(cap);
387
+ }
388
+ post({ jsonrpc: "2.0", id, result });
389
+ };
390
+ let outcome: void | Promise<UserMessageDelivery | void>;
391
+ try {
392
+ outcome = config.onUserMessage?.(params);
393
+ } catch {
394
+ // A throwing sink delivered nothing — the view hears so, never a hang.
395
+ settle({ isError: true });
396
+ return;
397
+ }
398
+ if (!(outcome instanceof Promise)) {
399
+ // A sink that returns nothing: accepted at call time (the pre-#1706 answer).
400
+ settle({});
401
+ return;
402
+ }
403
+ cap = setTimeout(() => settle({}), MESSAGE_ANSWER_CAP_MS); // still queued: see MESSAGE_ANSWER_CAP_MS
404
+ pendingAnswerCaps.add(cap);
405
+ outcome
406
+ .then(
407
+ (delivery) => settle(answerForDelivery(delivery)),
408
+ () => settle({ isError: true }),
409
+ )
410
+ // A throw while handling the resolution (a `delivered` the host cannot
411
+ // read) is an answer the view still gets.
412
+ .catch(() => settle({ isError: true }));
413
+ };
414
+
321
415
  const relay = (id: number | string, name: string, args?: McpToolStructuredContent): void => {
322
416
  const { onCallTool, resourceUri } = config;
323
417
  // The machine only emits the effect when the relay is wired (behavior
@@ -352,13 +446,8 @@ export function attachViewHost(frame: ViewFrameLike, config: AttachViewHostConfi
352
446
  if (effect.kind === "respond") post(effect.message);
353
447
  else if (effect.kind === "relay-tool-call") relay(effect.id, effect.name, effect.arguments);
354
448
  else if (effect.kind === "relay-resource-read") relayRead(effect.id, effect.uri);
355
- else if (effect.kind === "user-message") {
356
- try {
357
- config.onUserMessage?.(effect.params);
358
- } catch {
359
- // Observer failure is the embedder's bug; the view is answered.
360
- }
361
- } else if (effect.kind === "open-link") {
449
+ else if (effect.kind === "user-message") answerMessage(effect.id, effect.params);
450
+ else if (effect.kind === "open-link") {
362
451
  // The machine's scheme wall already passed this URL; the executor
363
452
  // SURFACES it to the human (the kit default renders a disclosure
364
453
  // affordance — navigation only ever happens on their own click).
@@ -443,6 +532,8 @@ export function attachViewHost(frame: ViewFrameLike, config: AttachViewHostConfi
443
532
 
444
533
  return () => {
445
534
  if (timer !== undefined) clearTimeout(timer);
535
+ for (const cap of pendingAnswerCaps) clearTimeout(cap);
536
+ pendingAnswerCaps.clear();
446
537
  unsubscribe();
447
538
  unsubscribeCsp();
448
539
  cachedWindow?.postMessage(teardownMessage(), "*");