@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.
- package/dist/block-ui.d.ts +26 -3
- package/dist/block-ui.d.ts.map +1 -1
- package/dist/block-ui.js +24 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +1 -1
- package/dist/view-host-protocol.d.ts +15 -2
- package/dist/view-host-protocol.d.ts.map +1 -1
- package/dist/view-host-protocol.js +8 -3
- package/dist/view-host.d.ts +41 -8
- package/dist/view-host.d.ts.map +1 -1
- package/dist/view-host.js +76 -8
- package/package.json +3 -3
- package/src/block-ui.ts +25 -4
- package/src/index.ts +2 -0
- package/src/react.tsx +2 -2
- package/src/view-host-protocol.ts +23 -5
- package/src/view-host.ts +100 -9
package/dist/block-ui.d.ts
CHANGED
|
@@ -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
|
|
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).
|
|
119
|
-
* when both carry one (guuey#209 route-A
|
|
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;
|
package/dist/block-ui.d.ts.map
CHANGED
|
@@ -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
|
|
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
|
|
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).
|
|
155
|
-
* when both carry one (guuey#209 route-A
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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";
|
package/dist/react.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|
|
180
|
-
*
|
|
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAC3G,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAKD,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AAErE;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AAEtD;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AASrD,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,aAAa,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACpC,MAAM,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACpC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,WAAW,GAAG,cAAc,CAAC;AAEzE,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,aAAa,CAAC;IACrB,0EAA0E;IAC1E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,IAAI,aAAa,CAEpD;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GAC9C;IACE;;;;;;OAMG;IACH,IAAI,EAAE,iBAAiB,CAAC;IACxB,EAAE,EAAE,aAAa,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,wBAAwB,CAAC;CACtC,GACD;IACE;;;;;;;OAOG;IACH,IAAI,EAAE,qBAAqB,CAAC;IAC5B,EAAE,EAAE,aAAa,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GACD;IACE;;;;;;;OAOG;IACH,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CACpC,GACD;IACE
|
|
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: [
|
package/dist/view-host.d.ts
CHANGED
|
@@ -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)
|
|
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
|
package/dist/view-host.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-host.d.ts","sourceRoot":"","sources":["../src/view-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAQL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAGnB,KAAK,aAAa,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
36
|
+
"@ggui-ai/protocol": "0.23.0",
|
|
37
37
|
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
|
38
|
-
"@silverprotocol/core": "0.
|
|
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
|
|
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).
|
|
190
|
-
* when both carry one (guuey#209 route-A
|
|
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
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.
|
|
209
|
-
*
|
|
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)
|
|
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
|
-
|
|
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(), "*");
|