paseo-plugin-zcode-provider 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +9 -9
  3. package/package.json +4 -4
  4. package/server/account.ts +10 -0
  5. package/server/build-info.ts +1 -1
  6. package/server/discovery/manifest.ts +2 -2
  7. package/server/mapping.ts +31 -0
  8. package/server/provider.ts +5 -18
  9. package/server/runtime/pins.ts +3 -3
  10. package/server/session.ts +7 -10
  11. package/server/unsupported-capabilities.ts +4 -1
  12. package/server/vendor/zcode/NOTICE.md +4 -1
  13. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +1 -0
  14. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +2 -0
  15. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +1 -0
  16. package/server/vendor/zcode/packages/rpc/src/protocol.ts +3 -2
  17. package/server/vendor/zcode/packages/shared/src/app-runtime-preferences.ts +18 -0
  18. package/server/vendor/zcode/packages/shared/src/attachment-source.ts +5 -0
  19. package/server/vendor/zcode/packages/shared/src/bots.ts +385 -2
  20. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +3 -1
  21. package/server/vendor/zcode/packages/shared/src/channel-mention.ts +131 -0
  22. package/server/vendor/zcode/packages/shared/src/conversationSelection.ts +37 -0
  23. package/server/vendor/zcode/packages/shared/src/dynamic-workflow-feature.ts +137 -0
  24. package/server/vendor/zcode/packages/shared/src/env.ts +15 -11
  25. package/server/vendor/zcode/packages/shared/src/highspeed.ts +232 -0
  26. package/server/vendor/zcode/packages/shared/src/mcp-apps/appTools.ts +148 -0
  27. package/server/vendor/zcode/packages/shared/src/mcp-apps/contract.ts +262 -0
  28. package/server/vendor/zcode/packages/shared/src/mcp-apps/instance.ts +12 -0
  29. package/server/vendor/zcode/packages/shared/src/mcp-apps/resourceMime.ts +73 -0
  30. package/server/vendor/zcode/packages/shared/src/mcp-apps/sampling.ts +117 -0
  31. package/server/vendor/zcode/packages/shared/src/mcp-apps/schemas.ts +90 -0
  32. package/server/vendor/zcode/packages/shared/src/model-execution.ts +58 -1
  33. package/server/vendor/zcode/packages/shared/src/oauth.ts +3 -0
  34. package/server/vendor/zcode/packages/shared/src/off-peak-execution.ts +73 -0
  35. package/server/vendor/zcode/packages/shared/src/protocol.ts +78 -4
  36. package/server/vendor/zcode/packages/shared/src/remoteTarget.ts +65 -0
  37. package/server/vendor/zcode/packages/shared/src/request-security-edition/verification-policy.ts +46 -0
  38. package/server/vendor/zcode/packages/shared/src/server-remote.ts +93 -0
  39. package/server/vendor/zcode/packages/shared/src/source-server/compatibility.ts +115 -0
  40. package/server/vendor/zcode/packages/shared/src/source-server/contracts.ts +426 -0
  41. package/server/vendor/zcode/packages/shared/src/source-server/errorCodes.ts +74 -0
  42. package/server/vendor/zcode/packages/shared/src/sqlite-migrations/contract.ts +83 -0
  43. package/server/vendor/zcode/packages/shared/src/subagent-profile.ts +62 -0
  44. package/server/vendor/zcode/packages/shared/src/subagent-runtime-config.ts +51 -0
  45. package/server/vendor/zcode/packages/shared/src/subagents-types.ts +164 -0
  46. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +14 -0
  47. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +136 -3
  48. package/server/vendor/zcode/packages/shared/src/version.ts +13 -0
  49. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +24 -1
  50. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +697 -36
  51. package/server/vendor/zcode/packages/shared/src/zcode-protocol/trace.ts +11 -0
  52. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +14 -2
  53. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +28 -9
  54. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +3 -0
  55. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +73 -53
  56. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-session-config.ts +15 -0
  57. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +47 -1
  58. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +58 -2
  59. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +31 -0
  60. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +28 -13
  61. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +37 -3
  62. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +36 -0
  63. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +21 -0
  64. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +1 -1
  65. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +32 -4
  66. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +31 -6
  67. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +4 -1
  68. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +4 -1
  69. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +62 -4
  70. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-command-rejections.ts +30 -0
  71. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +40 -3
  72. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +2 -2
  73. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +6 -5
  74. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +103 -26
  75. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +13 -1
  76. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +31 -2
  77. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +188 -73
  78. package/server/vendor/zcode/provenance.json +189 -80
@@ -0,0 +1,262 @@
1
+ /**
2
+ * MCP Apps 协议契约:agent、host、renderer 与 main 共用的常量、工具 UI 元数据类型、沙箱句柄与登记载荷。
3
+ * 页面 ↔ 宿主的线协议(`ui/*` 方法、握手、版本协商、JSON-RPC)由官方 `@modelcontextprotocol/ext-apps` 定义,
4
+ * 这里只保留 ZCode 自己的扩展键与限额。只放类型与常量;归一化与 schema 在同目录其他文件,经 index.ts 导出。
5
+ */
6
+
7
+ /** MCP 扩展标识(规范「Extension Identifier」);与官方 SDK `EXTENSION_ID` 一致。 */
8
+ export const MCP_APPS_EXTENSION_ID = "io.modelcontextprotocol/ui";
9
+ /** UI 资源 mimeType(规范 MUST);与官方 SDK `RESOURCE_MIME_TYPE` 一致。 */
10
+ export const MCP_APPS_RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
11
+ /** 弃用的扁平键 `_meta["ui/resourceUri"]`;规范要求宿主 MUST 同时兼容,与官方 SDK `RESOURCE_URI_META_KEY` 一致。 */
12
+ export const MCP_APPS_LEGACY_RESOURCE_URI_META_KEY = "ui/resourceUri";
13
+
14
+ /**
15
+ * MCP client `initialize` 里的 ui 扩展能力声明(规范「Client (Host) Capabilities」)。
16
+ * 用 type 而不是 interface:MCP SDK 的 `ClientCapabilities.extensions` 要求值带字符串索引签名。
17
+ */
18
+ export type McpAppsClientCapability = { mimeTypes: string[] };
19
+ /** agent 对每个 MCP server 宣告的能力片段;放进 `Client` 构造参数的 `capabilities.extensions`。 */
20
+ export function buildMcpAppsClientCapabilities(): {
21
+ extensions: { [extensionId: string]: McpAppsClientCapability };
22
+ } {
23
+ return { extensions: { [MCP_APPS_EXTENSION_ID]: { mimeTypes: [MCP_APPS_RESOURCE_MIME_TYPE] } } };
24
+ }
25
+
26
+ /** 宿主 hostInfo.name;hostInfo.version 只供页面 feature detect,与应用版本无关。 */
27
+ export const MCP_APPS_HOST_NAME = "zcode";
28
+ export const MCP_APPS_HOST_VERSION = "1";
29
+ /** `ui/initialize` 握手超时(端口到达起算)与卸载前 `ui/resource-teardown` 的等待上限。 */
30
+ export const MCP_APPS_HANDSHAKE_TIMEOUT_MS = 15_000;
31
+ export const MCP_APPS_TEARDOWN_TIMEOUT_MS = 500;
32
+
33
+ export type McpAppsDisplayMode = "inline" | "fullscreen" | "pip";
34
+ export type McpAppsToolVisibility = "model" | "app";
35
+ export const MCP_APPS_DEFAULT_TOOL_VISIBILITY: readonly McpAppsToolVisibility[] = ["model", "app"];
36
+
37
+ /** MCP Apps 资源级 CSP(`McpUiResourceCsp`):四类域。资源 `_meta` 为主,工具级 `_meta.ui.csp` 只在资源没有声明时兜底。 */
38
+ export interface McpToolUiCsp {
39
+ connectDomains?: string[];
40
+ resourceDomains?: string[];
41
+ frameDomains?: string[];
42
+ baseUriDomains?: string[];
43
+ }
44
+
45
+ /**
46
+ * 资源级 `_meta["zcode/csp"]`:宿主缺省按规范块(只有 'self' 'unsafe-inline'),不放行 `'unsafe-eval'` /
47
+ * `'wasm-unsafe-eval'`;需要 eval / WebAssembly 的资源在这里显式声明。宿主经
48
+ * `hostCapabilities.experimental["zcode/csp"].flags` 宣告支持的键。
49
+ */
50
+ export const MCP_APPS_CSP_RELAXATION_META_KEY = "zcode/csp";
51
+ export const MCP_APPS_CSP_RELAXATION_FLAGS = ["unsafeEval", "wasmUnsafeEval"] as const;
52
+ export type McpAppCspRelaxationFlag = (typeof MCP_APPS_CSP_RELAXATION_FLAGS)[number];
53
+ export type McpAppCspRelaxations = Partial<Record<McpAppCspRelaxationFlag, boolean>>;
54
+ export function buildMcpAppsCspExperimentalCapability(): {
55
+ [MCP_APPS_CSP_RELAXATION_META_KEY]: { flags: McpAppCspRelaxationFlag[] };
56
+ } {
57
+ return { [MCP_APPS_CSP_RELAXATION_META_KEY]: { flags: [...MCP_APPS_CSP_RELAXATION_FLAGS] } };
58
+ }
59
+
60
+ /**
61
+ * 资源可声明的浏览器权限(规范草案 `UIResourceMeta.permissions` 的四键),顺序即归一化顺序。
62
+ * 宿主:未声明一律拒绝;camera / microphone / geolocation 首次使用时原生弹框确认,clipboardWrite 按声明放行。
63
+ */
64
+ export const MCP_APPS_RESOURCE_PERMISSIONS = [
65
+ "camera",
66
+ "microphone",
67
+ "geolocation",
68
+ "clipboardWrite",
69
+ ] as const;
70
+ export type McpAppsResourcePermission = (typeof MCP_APPS_RESOURCE_PERMISSIONS)[number];
71
+ /** 规范权限键 → 插件 iframe `allow` 的 Permissions Policy 特性名。 */
72
+ export const MCP_APPS_IFRAME_ALLOW_TOKENS: Record<McpAppsResourcePermission, string> = {
73
+ camera: "camera",
74
+ microphone: "microphone",
75
+ geolocation: "geolocation",
76
+ clipboardWrite: "clipboard-write",
77
+ };
78
+ /** 与官方 SDK `buildAllowAttribute` 同形:`"camera; clipboard-write"`;没有声明返回空串。 */
79
+ export function buildMcpAppsIframeAllow(
80
+ permissions: readonly McpAppsResourcePermission[] | undefined,
81
+ ): string {
82
+ if (!permissions?.length) return "";
83
+ return MCP_APPS_RESOURCE_PERMISSIONS.filter((key) => permissions.includes(key))
84
+ .map((key) => MCP_APPS_IFRAME_ALLOW_TOKENS[key])
85
+ .join("; ");
86
+ }
87
+
88
+ /**
89
+ * `ui://` HTML 资源自己的 `_meta`(`resources/read` 内容项优先)。`heightHint` / `minFrameHeight` 是初始高度与
90
+ * 最小高度(px);`showInline` 为 true 时卡片强制常驻、不进折叠区。
91
+ */
92
+ export interface McpAppResourceMeta {
93
+ csp?: McpToolUiCsp;
94
+ cspRelaxations?: McpAppCspRelaxations;
95
+ /** `_meta.ui.permissions` 声明的浏览器权限(规范四键;顺序固定、去重)。 */
96
+ permissions?: McpAppsResourcePermission[];
97
+ prefersBorder?: boolean;
98
+ heightHint?: number;
99
+ minFrameHeight?: number;
100
+ showInline?: boolean;
101
+ }
102
+ /** 随沙箱句柄回到 renderer 的资源级呈现信息;CSP 与放宽只在 main 生效,不回传。 */
103
+ export type PluginSandboxResourceMeta = Omit<McpAppResourceMeta, "csp" | "cspRelaxations">;
104
+ /** 工具 `_meta` 归一化后的 UI 描述;`resourceUri` 非法时整个描述不存在。 */
105
+ export interface McpToolUiDescriptor {
106
+ resourceUri: string;
107
+ visibility?: McpAppsToolVisibility[];
108
+ preferredDisplayMode?: McpAppsDisplayMode;
109
+ csp?: McpToolUiCsp;
110
+ prefersBorder?: boolean;
111
+ /** 工具结果归属的面板 id(清单 `ui.surfaces[].id`)。 */
112
+ surface?: string;
113
+ /** 工具级 `openai/widgetShowCodexWidgetInline`,卡片强制常驻。 */
114
+ showInline?: boolean;
115
+ }
116
+
117
+ /** `_meta.ui.surface` / 清单 surface id 的长度与字符集(与 sandboxId 同源:可进 CSP host-source)。 */
118
+ export const MCP_APPS_SURFACE_ID_MAX_CHARS = 128;
119
+ export const MCP_APPS_SURFACE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
120
+
121
+ export const MCP_APPS_UI_RESOURCE_SCHEME = "ui://";
122
+ export const MCP_APPS_RESOURCE_URI_MAX_CHARS = 2048;
123
+ /**
124
+ * `display.ui.structuredContent` / `widgetMeta` / `content` 的持久化上限(JSON 文本的 UTF-8 字节)。
125
+ * 超限不截断字符串(半截 JSON 没有意义):字段整个省略,`truncated: true` 且 `truncatedBytes` 记被丢弃的字节数;
126
+ * 页面侧 `toolOutput` 为 null、`toolResponseMetadata["zcode/truncated"] = { bytes }`,可用 tools/call 重取。
127
+ */
128
+ export const MCP_APPS_STRUCTURED_CONTENT_MAX_BYTES = 64 * 1024;
129
+ export const MCP_APPS_WIDGET_META_MAX_BYTES = 16 * 1024;
130
+ export const MCP_APPS_CONTENT_MAX_BYTES = 32 * 1024;
131
+ export const MCP_APPS_TRUNCATED_META_KEY = "zcode/truncated";
132
+ /** host 侧接受的 `ui://` HTML 上限与 mimeType 白名单(规范只认带 profile 的;无 profile 的 text/html 是有意的宽松超集)。 */
133
+ // 官方 pdf-server 示例的单文件页面(内联 pdf.js)就有 4.3 MB,4 MiB 会把它拒掉;放到 16 MiB。
134
+ export const MCP_APPS_HTML_MAX_BYTES = 16 * 1024 * 1024;
135
+ export const MCP_APPS_HTML_MIME_TYPES = [MCP_APPS_RESOURCE_MIME_TYPE, "text/html"] as const;
136
+ /**
137
+ * 页面经 ui/update-model-context 与 ui/message 送来的 image 块:白名单三种位图,单张解码后 ≤ 4 MiB;
138
+ * 不在白名单或超限整个请求 -32602 `unsupported_content`。
139
+ */
140
+ export const MCP_APPS_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp"] as const;
141
+ export const MCP_APPS_IMAGE_MAX_BYTES = 4 * 1024 * 1024;
142
+ export {
143
+ MCP_APPS_UI_READ_RESOURCE_MAX_BYTES,
144
+ MCP_APPS_UI_READ_RESOURCE_MIME_ALLOWLIST,
145
+ isMcpAppsUiReadResourceMimeAllowed,
146
+ } from "./resourceMime.js";
147
+
148
+ /**
149
+ * `hostCapabilities.experimental` 里 ZCode 扩展的键。官方 SDK 的 zod 会剥掉标准位之外的未知能力键,
150
+ * 扩展一律放 experimental(规范预留位),页面按键 feature detect。
151
+ */
152
+ export const MCP_APPS_WIDGET_STATE_EXPERIMENTAL_KEY = "zcode/widgetState";
153
+ export const MCP_APPS_RESOURCE_SUBSCRIBE_EXPERIMENTAL_KEY = "zcode/resourceSubscribe";
154
+ export const MCP_APPS_SURFACE_EXPERIMENTAL_KEY = "zcode/surface";
155
+ /**
156
+ * ZCode 扩展方法 `ui/set-widget-state { widgetState }`(MCP Apps 无 widgetState):页面把界面状态交给宿主
157
+ * renderer 内存保存(不持久化,会话内重挂载可恢复);初值经 hostContext 的 `zcode/widgetState` 键随握手下发。
158
+ */
159
+ export const MCP_APPS_SET_WIDGET_STATE_METHOD = "ui/set-widget-state";
160
+ export const MCP_APPS_WIDGET_STATE_CONTEXT_KEY = "zcode/widgetState";
161
+
162
+ /** 宿主 renderer ↔ relay shell 之间唯一一条 MessageChannel 的名字;上面跑原始 JSON-RPC(官方 AppBridge / App)。 */
163
+ export const PLUGIN_SANDBOX_PORT_NAME = "plugin-sandbox-rpc";
164
+
165
+ /** MessagePort 的最小结构类型;宿主 renderer 的 Transport 与 relay shell 都只依赖这几个成员。 */
166
+ export interface PluginSandboxTransportPort {
167
+ postMessage(message: unknown): void;
168
+ onmessage: ((event: { data: unknown }) => void) | null;
169
+ start?(): void;
170
+ close(): void;
171
+ }
172
+
173
+ /** main → 宿主 renderer 的端口事件(经 preload `window.postMessage` transfer)。 */
174
+ export interface PluginSandboxPortsEvent {
175
+ sandboxId: string;
176
+ initId: number;
177
+ port: PluginSandboxTransportPort;
178
+ }
179
+ /**
180
+ * renderer 可用的沙箱平台端口,以 `IPlatformService.pluginSandbox` 可选字段挂载;web 端为 undefined,UI 据此回退。
181
+ * 登记沙箱不在这里:它由 host 经 parentPort 完成,renderer 只拿句柄。
182
+ */
183
+ export interface PluginSandboxPlatformPort {
184
+ /** preload 已安装限定的沙箱原子移动适配。 */
185
+ supportsRetainedMove?: boolean;
186
+ /** 仅宿主操作可用;Main 校验 owner/代次后复制当前页面内容。 */
187
+ copyImage?(input: PluginSandboxCaptureRequest): Promise<void>;
188
+ /** 只为指定代际的 MCP guest 保持可逆后台执行;不改变窗口或其他页面的节流。 */
189
+ setExecutionActive?(sandboxId: string, initId: number, active: boolean): Promise<void>;
190
+ /** 宿主窗口 webContents id;prepareSandbox 用它作为 owner,main 在 will-attach 时校验。 */
191
+ getOwnerWebContentsId(): Promise<number>;
192
+ /**
193
+ * 释放沙箱。带 initId 时只释放捕获的登记:新页面使用新 Agent 凭证和 sandboxId,
194
+ * 两条 IPC 路径不同(host parentPort vs ipcMain),旧 dispose 到得晚也不能误杀新登记。
195
+ */
196
+ disposeSandbox(sandboxId: string, initId?: number): Promise<void>;
197
+ consumeUserGesture(sandboxId: string): Promise<boolean>;
198
+ onPorts(handler: (event: PluginSandboxPortsEvent) => void): () => void;
199
+ }
200
+
201
+ export interface PluginSandboxCaptureRequest {
202
+ sandboxId: string;
203
+ initId: number;
204
+ captureRect: { x: number; y: number; width: number; height: number };
205
+ viewportSize: { width: number; height: number };
206
+ }
207
+
208
+ /**
209
+ * 沙箱作用域。工具卡片按 toolCallId,面板(清单 `ui.surfaces[]`)按 surfaceId;
210
+ * 两者都映射到一个字符串 scopeId 进注册表幂等键与 widgetState 路由,避免 main / host 认识"面板"概念。
211
+ */
212
+ export type PluginUiScopeRef =
213
+ | { kind: "toolCall"; toolCallId: string }
214
+ | { kind: "surface"; surfaceId: string };
215
+ export function buildPluginSandboxScopeId(scope: PluginUiScopeRef): string {
216
+ return scope.kind === "toolCall" ? `tool:${scope.toolCallId}` : `surface:${scope.surfaceId}`;
217
+ }
218
+
219
+ /** host → main 登记沙箱内容的消息载荷(模式同 LocalMediaPreviewPathAuthorize*)。 */
220
+ export interface PluginSandboxRegisterRequestPayload {
221
+ instance: import("./instance.js").McpAppInstance;
222
+ requestId: string;
223
+ /** 工作区隔离;旧调用允许缺省,新宿主登记须贯穿实际 workspace 范围。 */
224
+ workspacePath?: string;
225
+ workspaceIdentity?: string;
226
+ ownerWebContentsId: number;
227
+ sessionId: string;
228
+ /** Gen UI has Host provenance and never impersonates an MCP plugin. */
229
+ contentKind?: "gen-ui";
230
+ pluginId?: string;
231
+ /** 资源所属的 MCP server 运行时名;沙箱 partition 按它派生(同一 server 的页面共享一份持久存储)。 */
232
+ serverName?: string;
233
+ /** `buildPluginSandboxScopeId` 的结果:`tool:<toolCallId>` 或 `surface:<surfaceId>`。 */
234
+ scopeId: string;
235
+ html: string;
236
+ /** 有效 CSP:资源级优先,工具级兜底(host plugin-ui-bridge 已合并)。 */
237
+ csp?: McpToolUiCsp;
238
+ /** 资源级 `_meta["zcode/csp"]` 声明的放宽项。 */
239
+ cspRelaxations?: McpAppCspRelaxations;
240
+ /** 资源声明的浏览器权限;main 据此放宽 Permissions-Policy 与 iframe allow,并把权限请求交给闸门。 */
241
+ permissions?: McpAppsResourcePermission[];
242
+ prefersBorder?: boolean;
243
+ /** 资源级尺寸提示,原样进句柄回给 renderer。 */
244
+ heightHint?: number;
245
+ minFrameHeight?: number;
246
+ /** 资源级强制常驻标记,随句柄 resourceMeta 回 renderer。 */
247
+ showInline?: boolean;
248
+ }
249
+ /** host 内部使用的登记输入:去掉传输层 requestId。 */
250
+ export type PluginSandboxRegisterInput = Omit<PluginSandboxRegisterRequestPayload, "requestId">;
251
+ export interface PluginSandboxHandle {
252
+ instance: import("./instance.js").McpAppInstance;
253
+ sandboxId: string;
254
+ initId: number;
255
+ shellUrl: string;
256
+ partition: string;
257
+ /** 资源级呈现信息(prefersBorder / heightHint / minFrameHeight / showInline);资源没声明时省略。 */
258
+ resourceMeta?: PluginSandboxResourceMeta;
259
+ }
260
+ export type PluginSandboxRegisterResultPayload =
261
+ | ({ requestId: string; ok: true } & PluginSandboxHandle)
262
+ | { requestId: string; ok: false; error: string };
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+
3
+ /** Agent 签发的内存凭证;Main 的 initId 只用于端口握手。 */
4
+ export const mcpAppInstanceSchema = z
5
+ .object({
6
+ runtimeId: z.string().min(1),
7
+ generation: z.number().int().positive(),
8
+ appIdentity: z.string().regex(/^[a-f0-9]{64}$/),
9
+ token: z.string().min(1),
10
+ })
11
+ .strict();
12
+ export type McpAppInstance = z.infer<typeof mcpAppInstanceSchema>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * 页面发起的 `resources/read`(`mcp/uiReadResource`)单次结果总大小上限,以及 mimeType 白名单。
3
+ * 白名单项以 `/` 结尾表示前缀匹配(如 `text/`),否则精确匹配(忽略参数与大小写)。
4
+ */
5
+ export const MCP_APPS_UI_READ_RESOURCE_MAX_BYTES = 8 * 1024 * 1024;
6
+ // 原列表遗漏 PDF 等常见资源,导致插件已握手却读不到文件;按明确 MIME 扩展,保留未知类型拒绝规则。
7
+ export const MCP_APPS_UI_READ_RESOURCE_MIME_ALLOWLIST: readonly string[] = [
8
+ "text/",
9
+ "application/json",
10
+ "application/xml",
11
+ "application/yaml",
12
+ "application/x-yaml",
13
+ // 文档与办公文件。
14
+ "application/pdf",
15
+ "application/rtf",
16
+ "application/msword",
17
+ "application/vnd.ms-excel",
18
+ "application/vnd.ms-powerpoint",
19
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
20
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
21
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
22
+ "application/vnd.oasis.opendocument.text",
23
+ "application/vnd.oasis.opendocument.spreadsheet",
24
+ "application/vnd.oasis.opendocument.presentation",
25
+ // 图片。
26
+ "image/png",
27
+ "image/jpeg",
28
+ "image/webp",
29
+ "image/svg+xml",
30
+ "image/gif",
31
+ "image/avif",
32
+ "image/bmp",
33
+ "image/tiff",
34
+ "image/x-icon",
35
+ "image/vnd.microsoft.icon",
36
+ "image/heic",
37
+ "image/heif",
38
+ // 音视频。
39
+ "audio/mpeg",
40
+ "audio/mp4",
41
+ "audio/ogg",
42
+ "audio/wav",
43
+ "audio/x-wav",
44
+ "audio/webm",
45
+ "audio/aac",
46
+ "audio/flac",
47
+ "video/mp4",
48
+ "video/webm",
49
+ "video/ogg",
50
+ "video/quicktime",
51
+ "video/mpeg",
52
+ // 归档、字体与模型只传输内容,由插件决定如何使用。
53
+ "application/zip",
54
+ "application/gzip",
55
+ "application/x-gzip",
56
+ "application/x-tar",
57
+ "application/x-7z-compressed",
58
+ "application/vnd.rar",
59
+ "font/woff",
60
+ "font/woff2",
61
+ "font/ttf",
62
+ "font/otf",
63
+ "model/gltf+json",
64
+ "model/gltf-binary",
65
+ "application/octet-stream",
66
+ ];
67
+ export function isMcpAppsUiReadResourceMimeAllowed(mimeType: string): boolean {
68
+ const essence = mimeType.split(";")[0]!.trim().toLowerCase();
69
+ if (!essence) return false;
70
+ return MCP_APPS_UI_READ_RESOURCE_MIME_ALLOWLIST.some((entry) =>
71
+ entry.endsWith("/") ? essence.startsWith(entry) : essence === entry,
72
+ );
73
+ }
@@ -0,0 +1,117 @@
1
+ import { z } from "zod";
2
+ import { MCP_APPS_IMAGE_MAX_BYTES, MCP_APPS_IMAGE_MIME_TYPES } from "./contract.js";
3
+
4
+ export const MCP_APPS_SAMPLING_TIMEOUT_MS = 60_000;
5
+ export const MCP_APPS_SAMPLING_TRANSPORT_TIMEOUT_MS = 65_000;
6
+ export const MCP_APPS_SAMPLING_MAX_BYTES = 8 * 1024 * 1024;
7
+ export const MCP_APPS_SAMPLING_MAX_BLOCKS = 64;
8
+ export const MCP_APPS_SAMPLING_MAX_PER_SESSION = 4;
9
+ export const MCP_APPS_SAMPLING_MAX_PER_AGENT = 8;
10
+ export const MCP_APPS_SAMPLING_MAX_OPERATIONS = 4096;
11
+ const utf8Bytes = (text: string) => new TextEncoder().encode(text).byteLength;
12
+ const annotations = z
13
+ .object({
14
+ audience: z.array(z.enum(["user", "assistant"])).optional(),
15
+ priority: z.number().min(0).max(1).optional(),
16
+ lastModified: z.string().optional(),
17
+ })
18
+ .strict()
19
+ .optional();
20
+ const samplingBlockSchema = z.discriminatedUnion("type", [
21
+ z
22
+ .object({
23
+ type: z.literal("text"),
24
+ text: z.string().max(MCP_APPS_SAMPLING_MAX_BYTES),
25
+ annotations,
26
+ _meta: z.record(z.string(), z.unknown()).optional(),
27
+ })
28
+ .strict(),
29
+ z
30
+ .object({
31
+ type: z.literal("image"),
32
+ mimeType: z.enum(MCP_APPS_IMAGE_MIME_TYPES),
33
+ data: z
34
+ .string()
35
+ .min(1)
36
+ .max(Math.ceil(MCP_APPS_IMAGE_MAX_BYTES / 3) * 4)
37
+ // 重复四字符分组会在 MiB 级图片上耗尽 V8 正则栈;平坦字符检查保持线性。
38
+ .refine((data) => data.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(data))
39
+ .refine((data) => decodedBytes(data) <= MCP_APPS_IMAGE_MAX_BYTES),
40
+ annotations,
41
+ _meta: z.record(z.string(), z.unknown()).optional(),
42
+ })
43
+ .strict(),
44
+ ]);
45
+ const priority = z.number().min(0).max(1).optional();
46
+ /** 只开放宿主确实实现的官方子集;不静默剥除 tools/audio/模型配置等字段。 */
47
+ export const mcpAppsSamplingParamsSchema = z
48
+ .object({
49
+ messages: z
50
+ .array(
51
+ z
52
+ .object({
53
+ role: z.enum(["user", "assistant"]),
54
+ content: z.union([
55
+ samplingBlockSchema,
56
+ z.array(samplingBlockSchema).min(1).max(MCP_APPS_SAMPLING_MAX_BLOCKS),
57
+ ]),
58
+ })
59
+ .strict(),
60
+ )
61
+ .min(1)
62
+ .max(MCP_APPS_SAMPLING_MAX_BLOCKS),
63
+ systemPrompt: z.string().max(MCP_APPS_SAMPLING_MAX_BYTES).optional(),
64
+ maxTokens: z.number().int().positive().safe(),
65
+ includeContext: z.literal("none").optional(),
66
+ modelPreferences: z
67
+ .object({
68
+ hints: z
69
+ .array(z.object({ name: z.string().max(256).optional() }).strict())
70
+ .max(16)
71
+ .optional(),
72
+ costPriority: priority,
73
+ speedPriority: priority,
74
+ intelligencePriority: priority,
75
+ })
76
+ .strict()
77
+ .optional(),
78
+ metadata: z.record(z.string(), z.unknown()).optional(),
79
+ _meta: z.record(z.string(), z.unknown()).optional(),
80
+ })
81
+ .strict()
82
+ .superRefine((params, ctx) => {
83
+ const blocks = params.messages.flatMap((message) =>
84
+ Array.isArray(message.content) ? message.content : [message.content],
85
+ );
86
+ let bytes = utf8Bytes(params.systemPrompt ?? "");
87
+ for (const block of blocks)
88
+ bytes += block.type === "image" ? decodedBytes(block.data) : utf8Bytes(block.text);
89
+ // metadata 不转发给 provider,但仍计入边界预算,避免任意扩展字段绕过载荷限制。
90
+ const metadataBytes = utf8Bytes(
91
+ JSON.stringify({
92
+ metadata: params.metadata,
93
+ _meta: params._meta,
94
+ blockMetadata: blocks.map((b) => ({ _meta: b._meta, annotations: b.annotations })),
95
+ }),
96
+ );
97
+ if (
98
+ blocks.length > MCP_APPS_SAMPLING_MAX_BLOCKS ||
99
+ bytes + metadataBytes > MCP_APPS_SAMPLING_MAX_BYTES
100
+ )
101
+ ctx.addIssue({ code: "custom", message: "Sampling content exceeds host limits" });
102
+ });
103
+ function decodedBytes(data: string): number {
104
+ return (data.length * 3) / 4 - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
105
+ }
106
+ export const mcpAppsSamplingResultSchema = z
107
+ .object({
108
+ role: z.literal("assistant"),
109
+ model: z.string().min(1),
110
+ content: z
111
+ .object({ type: z.literal("text"), text: z.string().max(MCP_APPS_SAMPLING_MAX_BYTES) })
112
+ .strict(),
113
+ stopReason: z.string().min(1),
114
+ })
115
+ .strict();
116
+ export type McpAppsSamplingParams = z.infer<typeof mcpAppsSamplingParamsSchema>;
117
+ export type McpAppsSamplingResult = z.infer<typeof mcpAppsSamplingResultSchema>;
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import {
3
+ MCP_APPS_RESOURCE_URI_MAX_CHARS,
4
+ MCP_APPS_CONTENT_MAX_BYTES,
5
+ MCP_APPS_STRUCTURED_CONTENT_MAX_BYTES,
6
+ MCP_APPS_SURFACE_ID_MAX_CHARS,
7
+ MCP_APPS_SURFACE_ID_PATTERN,
8
+ MCP_APPS_WIDGET_META_MAX_BYTES,
9
+ } from "./contract.js";
10
+
11
+ /**
12
+ * 以下 schema 是 mcp-apps-protocol 的运行时校验面:只覆盖 ZCode 自己的投影 / 元数据形状。
13
+ * 页面 ↔ 宿主的 `ui/*` 参数由官方 SDK(`McpUi*Schema`)校验,这里不重复。类型见 contract.ts。
14
+ */
15
+
16
+ export const mcpAppsDisplayModeSchema = z.enum(["inline", "fullscreen", "pip"]);
17
+ export const mcpAppsToolVisibilitySchema = z.enum(["model", "app"]);
18
+ /** 面板 id。 */
19
+ export const mcpAppsSurfaceIdSchema = z
20
+ .string()
21
+ .min(1)
22
+ .max(MCP_APPS_SURFACE_ID_MAX_CHARS)
23
+ .regex(MCP_APPS_SURFACE_ID_PATTERN);
24
+
25
+ export const mcpToolUiCspSchema = z
26
+ .object({
27
+ connectDomains: z.array(z.string().min(1).max(512)).optional(),
28
+ resourceDomains: z.array(z.string().min(1).max(512)).optional(),
29
+ frameDomains: z.array(z.string().min(1).max(512)).optional(),
30
+ baseUriDomains: z.array(z.string().min(1).max(512)).optional(),
31
+ })
32
+ .strict();
33
+
34
+ /** 资源级 `_meta["zcode/csp"]` 归一化后的形状(只有为 true 的键)。 */
35
+ export const mcpAppCspRelaxationsSchema = z
36
+ .object({ unsafeEval: z.boolean().optional(), wasmUnsafeEval: z.boolean().optional() })
37
+ .strict();
38
+
39
+ export const mcpToolUiDescriptorSchema = z
40
+ .object({
41
+ resourceUri: z.string().min(1).max(MCP_APPS_RESOURCE_URI_MAX_CHARS),
42
+ visibility: z.array(mcpAppsToolVisibilitySchema).optional(),
43
+ preferredDisplayMode: mcpAppsDisplayModeSchema.optional(),
44
+ csp: mcpToolUiCspSchema.optional(),
45
+ prefersBorder: z.boolean().optional(),
46
+ surface: mcpAppsSurfaceIdSchema.optional(),
47
+ showInline: z.boolean().optional(),
48
+ })
49
+ .strict();
50
+
51
+ /**
52
+ * `display.ui`:agent 投影到 ToolCallRow.display 的插件 UI 元数据。
53
+ * contracts 与 zcode-protocol-v4 两处 strict schema 都从这里复用,避免三份手写不同步。
54
+ */
55
+ export const mcpToolDisplayUiSchema = z
56
+ .object({
57
+ pluginId: z.string().min(1).max(256),
58
+ resourceUri: z.string().min(1).max(MCP_APPS_RESOURCE_URI_MAX_CHARS),
59
+ preferredDisplayMode: mcpAppsDisplayModeSchema.optional(),
60
+ prefersBorder: z.boolean().optional(),
61
+ csp: mcpToolUiCspSchema.optional(),
62
+ // 上限按 UTF-8 字节由 agent 侧保证;这里的 max 按字符数只是兜底(字符数 ≤ 字节数)。
63
+ structuredContent: z.string().max(MCP_APPS_STRUCTURED_CONTENT_MAX_BYTES).optional(),
64
+ widgetMeta: z.string().max(MCP_APPS_WIDGET_META_MAX_BYTES).optional(),
65
+ /** 原始 `content` 数组的 JSON 文本(≤ 32 KiB),页面 tool-result 用它而不是拼接的纯文本。 */
66
+ content: z.string().max(MCP_APPS_CONTENT_MAX_BYTES).optional(),
67
+ truncated: z.boolean().optional(),
68
+ /** 被省略字段的 UTF-8 字节数之和。 */
69
+ truncatedBytes: z.number().int().nonnegative().optional(),
70
+ /** MCP 结果 `isError: true`(调用本身完成、工具报错);页面 tool-result 带 isError,面板不用它覆盖上一份成功快照。 */
71
+ isError: z.literal(true).optional(),
72
+ /** 工具级强制常驻(`openai/widgetShowCodexWidgetInline`),卡片不进折叠区。 */
73
+ showInline: z.literal(true).optional(),
74
+ // 所属面板;contracts 侧 mcpToolDisplayUiPayloadSchema 同步。
75
+ surface: mcpAppsSurfaceIdSchema.optional(),
76
+ })
77
+ .strict();
78
+ export type McpToolDisplayUi = z.infer<typeof mcpToolDisplayUiSchema>;
79
+
80
+ /** 当前执行的成功 UI 工具结果;独立于 conversation 订阅,不用于历史回放。 */
81
+ export const pluginUiToolResultNotificationSchema = z
82
+ .object({
83
+ sessionId: z.string().min(1),
84
+ toolCallId: z.string().min(1),
85
+ serverName: z.string().min(1),
86
+ ui: mcpToolDisplayUiSchema.refine((ui) => !ui.isError),
87
+ input: z.record(z.string(), z.unknown()).optional(),
88
+ })
89
+ .strict();
90
+ export type PluginUiToolResultNotification = z.infer<typeof pluginUiToolResultNotificationSchema>;
@@ -1,13 +1,37 @@
1
1
  import { z } from "zod";
2
+ import { modelSelectionSchema } from "./model-selection.js";
3
+
4
+ /**
5
+ * 执行 Selection 退回 Session Selection 的原因。由发起方在 selectionFallback 规则里声明,
6
+ * CLI 只透传命中规则的原因,不自行解释 provider 错误码。
7
+ */
8
+ export const modelExecutionSelectionFallbackReasonSchema = z.enum([
9
+ "highspeed_card_expired",
10
+ "highspeed_request_failed",
11
+ "highspeed_selection_unavailable",
12
+ ]);
2
13
 
3
14
  /** 文本与附件发送共用执行约束,凭据只属于单次执行,不进入 Session 配置。 */
4
15
  export const modelExecutionSchema = z
5
16
  .object({
6
17
  memoryExtraction: z.literal("skip").optional(),
7
18
  selectionScope: z.literal("execution"),
19
+ offPeak: z
20
+ .object({
21
+ executionId: z.string().min(1),
22
+ ticketId: z.string().min(1),
23
+ providerErrorCode: z.string().min(1),
24
+ })
25
+ .strict()
26
+ .optional(),
8
27
  requestAuth: z
9
28
  .object({
10
29
  apiKey: z.string().min(1).optional(),
30
+ apiKeyId: z.string().trim().min(1).optional(),
31
+ accountScope: z
32
+ .string()
33
+ .regex(/^[a-f0-9]{64}$/)
34
+ .optional(),
11
35
  headers: z.record(z.string().min(1), z.string().min(1)).optional(),
12
36
  })
13
37
  .strict()
@@ -15,9 +39,42 @@ export const modelExecutionSchema = z
15
39
  subagents: z
16
40
  .object({
17
41
  foregroundModel: z.literal("submission"),
18
- background: z.literal("deny"),
42
+ background: z.enum(["deny", "allow"]),
43
+ })
44
+ .strict()
45
+ .optional(),
46
+ /**
47
+ * 本次执行的 Selection 失败时,同一 Turn 内退回原模型继续跑完,而不是让整轮失败。
48
+ * 触发条件由发起方声明:`providerId` 限定只匹配本轮 execution Selection 指向的 provider;
49
+ * `rules` 按声明顺序匹配,首个命中的规则决定退回原因,`providerErrorCode` 缺省表示该 provider
50
+ * 的任何失败(用户取消与上下文超窗除外)。CLI 不按 provider id 前缀、模型名或错误文案硬编码。
51
+ * 退回目标先取 `target`(发起方抽卡时的提交选择,即会话原模型),再取本轮接纳前复制的 runtime 会话常驻 Selection:
52
+ * 接纳用户消息时绑定普通提交选择,但常驻选择只在 runtime 内存里,冷恢复竞态下可能未绑定,
53
+ * 所以发起方必须声明 `target`(docs/highspeed/highspeed-card-spec.md §2.2)。
54
+ */
55
+ selectionFallback: z
56
+ .object({
57
+ providerId: z.string().min(1),
58
+ rules: z
59
+ .array(
60
+ z
61
+ .object({
62
+ reason: modelExecutionSelectionFallbackReasonSchema,
63
+ providerErrorCode: z.string().min(1).optional(),
64
+ })
65
+ .strict(),
66
+ )
67
+ .min(1),
68
+ target: modelSelectionSchema.optional(),
19
69
  })
20
70
  .strict()
21
71
  .optional(),
22
72
  })
23
73
  .strict();
74
+
75
+ export type ModelExecution = z.infer<typeof modelExecutionSchema>;
76
+ export type ModelExecutionSelectionFallback = NonNullable<ModelExecution["selectionFallback"]>;
77
+ export type ModelExecutionSelectionFallbackRule = ModelExecutionSelectionFallback["rules"][number];
78
+ export type ModelExecutionSelectionFallbackReason = z.infer<
79
+ typeof modelExecutionSelectionFallbackReasonSchema
80
+ >;
@@ -149,6 +149,9 @@ export type OAuthCachedSessionRestoreResult =
149
149
  | { status: "signed-out" }
150
150
  | { status: "reauthentication-required"; reason: "jwt-expired" };
151
151
 
152
+ /** OAuth owner 已完成会话凭据变更;不携带用户资料或 token。 */
153
+ export type OAuthSessionState = "signed-in" | "signed-out";
154
+
152
155
  /** Host 在检测到 ZCode JWT 失效后通知 Renderer 展示确认并重启。 */
153
156
  export const ZCODE_JWT_INVALID_BROADCAST_CHANNEL = "auth:zcode-jwt-invalid";
154
157