paseo-plugin-zcode-provider 0.3.0 → 0.4.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.
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,137 @@
1
+ // ============================================================
2
+ // Dynamic Workflow 灰度:服务端 feature key 的取值域与客户端快照
3
+ // ============================================================
4
+ // 服务端 `/api/v1/client/configs` 下发 `data.configs.dynamicWorkflow.mode`。
5
+
6
+ // 这里只放三端(Host services、Desktop main、UI)共用的取值域、归一化与快照形状;
7
+ // 读取远端、覆盖与下发都在各自的 owner 里,不在 shared 层发请求。
8
+
9
+ export const DYNAMIC_WORKFLOW_MODES = ["disabled", "onDemand", "alwaysOn"] as const;
10
+ export type DynamicWorkflowMode = (typeof DYNAMIC_WORKFLOW_MODES)[number];
11
+
12
+ /**
13
+ * 本地覆盖用的环境变量。语义按构建档位分三层,由 Desktop main 在 fork Host 前**改写或删除**
14
+ * (desktopRuntimeEnv.ts 的 buildHostProcessEnv),Host 只消费不再分辨来源:
15
+ * - 未打包 dev:透传开发者 shell 里的合法取值;
16
+ * - 打包 preview:固定写入 `alwaysOn`,忽略 shell;
17
+ * - 打包 production:删除继承值,永不写入。
18
+ * 没有 main 的 Web/server Host 直接读进程环境(运维/开发者设置)。
19
+ */
20
+ export const ZCODE_DYNAMIC_WORKFLOW_MODE_ENV = "ZCODE_DYNAMIC_WORKFLOW_MODE";
21
+
22
+ /** 服务端缺省、格式非法或请求失败时的取值:fail-closed,与闲时任务灰度一致。 */
23
+ export const DEFAULT_DYNAMIC_WORKFLOW_MODE: DynamicWorkflowMode = "disabled";
24
+
25
+ export function normalizeDynamicWorkflowMode(value: unknown): DynamicWorkflowMode | undefined {
26
+ if (typeof value !== "string") return undefined;
27
+ const trimmed = value.trim();
28
+ return (DYNAMIC_WORKFLOW_MODES as readonly string[]).includes(trimmed)
29
+ ? (trimmed as DynamicWorkflowMode)
30
+ : undefined;
31
+ }
32
+
33
+ /**
34
+ * 「功能是否提供」的折叠:`onDemand` 与 `alwaysOn` 同为开启——目录里的 `/workflow`、技能、
35
+ * 自动化页的工作流 tab 与 Resume 入口在两种模式下都在。两者的差别只在模型工具面的注册时机
36
+ * (launch.md「On demand: activation」),由 CLI 侧按 mode 本身裁决,这里不参与。
37
+ */
38
+ export function isDynamicWorkflowModeEnabled(mode: DynamicWorkflowMode): boolean {
39
+ return mode !== "disabled";
40
+ }
41
+
42
+ /** 快照的来源:观测用,UI 与日志据此区分「服务端关」与「本地覆盖」。只描述「提供」来自哪里。 */
43
+ export type DynamicWorkflowClientConfigSource = "remote" | "override" | "default";
44
+
45
+ /**
46
+ * 灰度快照(launch.md「Gray release」「The user's choice」)。两层:
47
+ * - 提供(offer):服务端 key 或本地覆盖决定默认是哪个模式,记在 `offeredMode`(disabled 也只是默认值);
48
+ * - 生效(effective):用户在设置里的明确选择永远取代提供的模式,记在 `mode` / `enabled`。
49
+ * 旧消费者读 `mode` / `enabled` 即得生效值;设置页读 `offeredMode` 决定「默认」标签。
50
+ */
51
+ export interface DynamicWorkflowClientConfig {
52
+ /** 生效模式:用户有选择时是用户选择,否则等于 offeredMode。 */
53
+ readonly mode: DynamicWorkflowMode;
54
+ /** 等于 isDynamicWorkflowModeEnabled(mode);单独落字段免得每个消费者各写一遍折叠规则。 */
55
+ readonly enabled: boolean;
56
+ readonly source: DynamicWorkflowClientConfigSource;
57
+ /** 服务端(或本地覆盖)提供的模式:用户没选时的生效模式,也是设置页的「默认」选项。 */
58
+ readonly offeredMode: DynamicWorkflowMode;
59
+ /** 实际生效的用户选择;缺席表示跟随提供的模式(未选择或非法值)。 */
60
+ readonly userMode?: DynamicWorkflowMode;
61
+ }
62
+
63
+ export function createDynamicWorkflowClientConfig(
64
+ offeredMode: DynamicWorkflowMode,
65
+ source: DynamicWorkflowClientConfigSource,
66
+ ): DynamicWorkflowClientConfig {
67
+ return {
68
+ mode: offeredMode,
69
+ enabled: isDynamicWorkflowModeEnabled(offeredMode),
70
+ source,
71
+ offeredMode,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * 读快照里服务端提供的默认模式(与用户选择无关);设置行与中枢的「工作流设置」只在读得到时出现。
77
+ * disabled 也是合法的默认值:此时行照样出现,「默认」标签落在「关闭」上。
78
+ *
79
+ * 快照可能来自没有 offeredMode 的旧 Host(新 UI——例如手机远控 bundle——连旧桌面)。旧 Host 不认
80
+ * 用户选择,摆出设置行只会把选择写进一个不生效的设置,所以缺席或读不懂的 offeredMode 返回 undefined:
81
+ * 只有认用户选择的 Host 才会写这个字段。
82
+ */
83
+ export function readDynamicWorkflowOfferedMode(
84
+ config: DynamicWorkflowClientConfig,
85
+ ): DynamicWorkflowMode | undefined {
86
+ return normalizeDynamicWorkflowMode(config.offeredMode);
87
+ }
88
+
89
+ /**
90
+ * 「服务端给默认、用户选择永远生效」规则的唯一实现(launch.md「The user's choice」):
91
+ * - 合法的用户选择取代提供的模式,提供的是 disabled 也一样——功能已全量,服务端只决定默认值;
92
+ * - 缺席或非法值(旧文件、未来版本写入的新值)按跟随提供的模式处理。
93
+ * 只从 `offeredMode` 推导,所以对已应用过选择的快照再次应用是覆盖而不是叠加:Host 闩住的是提供,
94
+ * 每次使用都用最新选择重新应用。
95
+ */
96
+ export function applyDynamicWorkflowUserMode(
97
+ config: DynamicWorkflowClientConfig,
98
+ userMode: unknown,
99
+ ): DynamicWorkflowClientConfig {
100
+ const offer = createDynamicWorkflowClientConfig(config.offeredMode, config.source);
101
+ const choice = normalizeDynamicWorkflowMode(userMode);
102
+ if (!choice) return offer;
103
+ return {
104
+ ...offer,
105
+ mode: choice,
106
+ enabled: isDynamicWorkflowModeEnabled(choice),
107
+ userMode: choice,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * 纯函数:把远端 envelope 的 `configs.dynamicWorkflow` 与本地覆盖环境变量折叠成「提供」,再应用用户选择。
113
+ * 提供的优先级:覆盖 > 远端合法值 > 缺省。远端成功但**未下发**该 key 也视为 disabled——
114
+ * 服务端撤掉 key 等于关闭,不能沿用旧快照(与 desktopContextPromptRollout 同一裁决)。
115
+ */
116
+ export function resolveDynamicWorkflowClientConfig(input: {
117
+ remote: unknown;
118
+ env?: Record<string, string | undefined>;
119
+ userMode?: unknown;
120
+ }): DynamicWorkflowClientConfig {
121
+ return applyDynamicWorkflowUserMode(resolveDynamicWorkflowOffer(input), input.userMode);
122
+ }
123
+
124
+ function resolveDynamicWorkflowOffer(input: {
125
+ remote: unknown;
126
+ env?: Record<string, string | undefined>;
127
+ }): DynamicWorkflowClientConfig {
128
+ const override = normalizeDynamicWorkflowMode(input.env?.[ZCODE_DYNAMIC_WORKFLOW_MODE_ENV]);
129
+ if (override) return createDynamicWorkflowClientConfig(override, "override");
130
+ const remoteMode = normalizeDynamicWorkflowMode(
131
+ typeof input.remote === "object" && input.remote !== null
132
+ ? (input.remote as { mode?: unknown }).mode
133
+ : undefined,
134
+ );
135
+ if (remoteMode) return createDynamicWorkflowClientConfig(remoteMode, "remote");
136
+ return createDynamicWorkflowClientConfig(DEFAULT_DYNAMIC_WORKFLOW_MODE, "default");
137
+ }
@@ -8,6 +8,9 @@ export type ArmsRumEnv = "local" | "prod";
8
8
  // 非构建环境(如 e2e 测试的 mocha)下 define 不存在,用 typeof 检查 + fallback 避免 ReferenceError
9
9
  declare const __ZCODE_ENV__: string;
10
10
  declare const __ZCODE_PRODUCT_FLAVOR__: string;
11
+ declare const __ZCODE_ARMS_RUM_ENDPOINT__: string;
12
+ declare const __ZCODE_TELEMETRY_REPORT_ENDPOINT__: string;
13
+ declare const __ZCODE_AUTO_UPDATE_ENABLED__: boolean;
11
14
 
12
15
  export function normalizeZCodeEnv(value: string | undefined): ZCodeEnv {
13
16
  return value?.trim().toLowerCase() === "production" ? "production" : "test";
@@ -45,17 +48,18 @@ export const ZCODE_BUILD_COMMIT_ID_ENV = "ZCODE_BUILD_COMMIT_ID" as const;
45
48
  export const RUNTIME_ZCODE_DEBUG =
46
49
  typeof process !== "undefined" ? process.env.ZCODE_DEBUG : undefined;
47
50
 
48
- // 恢复原因:写死 false 会让运行时已配置的数仓/ARMS 永远空转。
49
- // 功能保持可用;实际出网由各出口的运行时端点检查决定,未配置不上报。
50
- export const ZCODE_TELEMETRY_ENABLED: boolean = true;
51
-
52
- /** 数仓事件上报端点:由运行时环境变量提供,未配置即停用,构建产物不内嵌。 */
53
- export const ZCODE_TELEMETRY_REPORT_ENDPOINT =
54
- typeof process !== "undefined" ? (process.env.ZCODE_TELEMETRY_REPORT_ENDPOINT ?? "") : "";
55
-
56
- /** ARMS RUM 接入端点:由运行时环境变量提供,未配置即停用,构建产物不内嵌。 */
57
- export const ZCODE_ARMS_RUM_ENDPOINT =
58
- typeof process !== "undefined" ? (process.env.ZCODE_ARMS_RUM_ENDPOINT ?? "") : "";
51
+ // ── 构建期可选能力(见 docs/desktop/build-time-optional-capabilities.md),未注入时一律关闭 ──
52
+ /** ARMS RUM 上报地址;为空时 SDK 以 enable=false 初始化,不产生上报。 */
53
+ export const ZCODE_ARMS_RUM_ENDPOINT: string =
54
+ typeof __ZCODE_ARMS_RUM_ENDPOINT__ !== "undefined" ? __ZCODE_ARMS_RUM_ENDPOINT__ : "";
55
+ /** 事件上报(数仓)地址;为空时不上报。 */
56
+ export const ZCODE_TELEMETRY_REPORT_ENDPOINT: string =
57
+ typeof __ZCODE_TELEMETRY_REPORT_ENDPOINT__ !== "undefined"
58
+ ? __ZCODE_TELEMETRY_REPORT_ENDPOINT__
59
+ : "";
60
+ /** 桌面自动更新是否启用。 */
61
+ export const ZCODE_AUTO_UPDATE_ENABLED: boolean =
62
+ typeof __ZCODE_AUTO_UPDATE_ENABLED__ !== "undefined" ? __ZCODE_AUTO_UPDATE_ENABLED__ : false;
59
63
 
60
64
  /** 将本地运行态与编译期 ZCODE_ENV 映射为 ARMS 控制台识别的上报环境标签 */
61
65
  export function mapZCodeEnvToArmsRumEnv(runtimeEnv: ZCodeRuntimeEnv): ArmsRumEnv {
@@ -0,0 +1,232 @@
1
+ import { z } from "zod";
2
+ import {
3
+ modelExecutionSelectionFallbackReasonSchema,
4
+ type ModelExecutionSelectionFallback,
5
+ } from "./model-execution.js";
6
+ import type { ModelSelection } from "./model-selection.js";
7
+ import { BUILTIN_MODEL_PROVIDER_IDS } from "./model-provider-types.js";
8
+
9
+ /**
10
+ * 卡在 Turn 中过期时加速网关返回的业务错误码(HTTP 400 / provider_code=3402)。
11
+ * 见 docs/highspeed/highspeed-card-spec.md:命中它要保留上下文并退回原模型跑完本轮。
12
+ */
13
+ export const HIGHSPEED_CARD_EXPIRED_PROVIDER_ERROR_CODE = "3402";
14
+
15
+ /**
16
+ * 加速卡的隐藏 Built-in Provider;端点与模型名单由 ZCode Built-in Provider Config 提供
17
+ * (config/provider/zcode-builtin.json),这里只保留跨层引用的 ID。
18
+ * 与 Off-Peak 同规则按账号 Family 拆分:卡是对当前账号 Coding Plan 权益的抽取,
19
+ * 不跨 Family 静默迁移。
20
+ */
21
+ export const HIGHSPEED_PROVIDER_IDS = {
22
+ zai: "account:zai-highspeed-card",
23
+ bigmodel: "account:bigmodel-highspeed-card",
24
+ } as const;
25
+
26
+ export function resolveHighspeedProviderId(
27
+ family: "zai" | "bigmodel",
28
+ ): (typeof HIGHSPEED_PROVIDER_IDS)[typeof family] {
29
+ return HIGHSPEED_PROVIDER_IDS[family];
30
+ }
31
+
32
+ /**
33
+ * 判定一次 Selection 是否指向加速卡 Provider。拆 Family 之后不能再和单个常量比较,
34
+ * 否则 BigModel 账号的卡在队列提升时会被判成「非加速」而静默丢卡。
35
+ */
36
+ export function isHighspeedProviderId(providerId: string | undefined): boolean {
37
+ if (providerId === undefined) return false;
38
+ return Object.values(HIGHSPEED_PROVIDER_IDS).some((id) => id === providerId);
39
+ }
40
+
41
+ /**
42
+ * draw body 的 provider 契约值:服务端 highspeed 白名单只认 Coding Plan 的 builtin 身份
43
+ * (builtin:zai-coding-plan / builtin:bigmodel-coding-plan,2026-09-17 与服务端确认;
44
+ * 设计文档示例里的 "zai" 是缩写示意,非线上取值)。Start Plan、Off-Peak、API Key 与
45
+ * 自定义 Provider 都不在白名单内,必须解析为 null 后降级,禁止映射后发送。
46
+ */
47
+ export const HIGHSPEED_DRAW_PROVIDER_IDS = {
48
+ zai: "builtin:zai-coding-plan",
49
+ bigmodel: "builtin:bigmodel-coding-plan",
50
+ } as const;
51
+
52
+ const HIGHSPEED_DRAW_PROVIDER_ID_BY_PROVIDER_ID = new Map<string, string>([
53
+ [BUILTIN_MODEL_PROVIDER_IDS.zaiIndividualCodingPlan, HIGHSPEED_DRAW_PROVIDER_IDS.zai],
54
+ [BUILTIN_MODEL_PROVIDER_IDS.zaiTeamCodingPlan, HIGHSPEED_DRAW_PROVIDER_IDS.zai],
55
+ [BUILTIN_MODEL_PROVIDER_IDS.bigmodelIndividualCodingPlan, HIGHSPEED_DRAW_PROVIDER_IDS.bigmodel],
56
+ [BUILTIN_MODEL_PROVIDER_IDS.bigmodelTeamCodingPlan, HIGHSPEED_DRAW_PROVIDER_IDS.bigmodel],
57
+ ]);
58
+
59
+ /** 把会话选择的 Provider ID 解析成 draw body 可用的白名单 provider 值;不命中返回 null。 */
60
+ export function resolveHighspeedDrawProviderId(providerId: string): string | null {
61
+ return HIGHSPEED_DRAW_PROVIDER_ID_BY_PROVIDER_ID.get(providerId) ?? null;
62
+ }
63
+
64
+ export const highspeedCardSchema = z.object({
65
+ card_id: z.string().min(1),
66
+ task_id: z.string().min(1),
67
+ provider: z.string().min(1),
68
+ model: z.string().min(1),
69
+ issued_at: z.number().int().nonnegative(),
70
+ expires_at: z.number().int().nonnegative(),
71
+ });
72
+
73
+ /**
74
+ * 写入 message.data 的协议级卡快照。版本字段保证后续扩展仍能宽容读取旧消息。
75
+ */
76
+ export const highspeedMessageMetaSchema = z
77
+ .object({
78
+ schemaVersion: z.literal(1),
79
+ cardId: z.string().min(1),
80
+ taskId: z.string().min(1),
81
+ provider: z.string().min(1),
82
+ model: z.string().min(1),
83
+ issuedAt: z.number().int().nonnegative(),
84
+ expiresAt: z.number().int().nonnegative(),
85
+ /** 发送该轮时从 Coding Plan 健康检查采样的普通 Pro/Max decode TPS。 */
86
+ regularTps: z.number().positive().optional(),
87
+ /** 完成态统计;这些字段由同一次 CLI 持久化更新原子写入。 */
88
+ outputTokens: z.number().int().nonnegative().optional(),
89
+ durationMs: z.number().nonnegative().optional(),
90
+ /** 卡到期后从 Highspeed healthy usage 采样的卡级平均 completion TPS。 */
91
+ highspeedTps: z.number().positive().optional(),
92
+ savedDurationMs: z.number().nonnegative().optional(),
93
+ /** CLI 从运行时事件聚合的真实耗时拆分;缺省时客户端回退到 20/80 估算。 */
94
+ modelDurationMs: z.number().nonnegative().optional(),
95
+ toolDurationMs: z.number().nonnegative().optional(),
96
+ otherDurationMs: z.number().nonnegative().optional(),
97
+ /** Turn 内加速请求失败并回落普通模型的时刻;只用于投影降级提示,不改变分享统计。 */
98
+ fallbackAt: z.number().int().nonnegative().optional(),
99
+ /** 回落原因:卡过期或其他加速请求失败;Renderer 据此选择提示文案,缺省按卡过期处理旧数据。 */
100
+ fallbackReason: modelExecutionSelectionFallbackReasonSchema.optional(),
101
+ })
102
+ .strict();
103
+
104
+ export const highspeedDrawRequestSchema = z.object({
105
+ task_id: z.string().min(1),
106
+ provider: z.string().min(1),
107
+ model: z.string().min(1),
108
+ });
109
+
110
+ export const highspeedDrawResponseSchema = z.object({
111
+ code: z.number().int(),
112
+ msg: z.string(),
113
+ data: z.object({
114
+ card: highspeedCardSchema.nullable(),
115
+ next_draw_at: z.number().int().nonnegative(),
116
+ }),
117
+ });
118
+
119
+ export const highspeedHealthyResponseSchema = z.object({
120
+ code: z.number().int(),
121
+ msg: z.string(),
122
+ data: z.object({
123
+ card_id: z.string().min(1),
124
+ prompt_tokens: z.number().int().nonnegative(),
125
+ completion_tokens: z.number().int().nonnegative(),
126
+ duration_seconds: z.number().int().nonnegative(),
127
+ }),
128
+ });
129
+
130
+ export type HighspeedCardApi = z.infer<typeof highspeedCardSchema>;
131
+ export type HighspeedMessageMeta = z.infer<typeof highspeedMessageMetaSchema>;
132
+ export type HighspeedDrawRequest = z.infer<typeof highspeedDrawRequestSchema>;
133
+ export type HighspeedDrawResponse = z.infer<typeof highspeedDrawResponseSchema>;
134
+ export type HighspeedHealthyResponse = z.infer<typeof highspeedHealthyResponseSchema>;
135
+
136
+ export interface HighspeedCardUsage {
137
+ cardId: string;
138
+ promptTokens: number;
139
+ completionTokens: number;
140
+ durationSeconds: number;
141
+ }
142
+
143
+ export interface HighspeedCardSnapshot {
144
+ cardId: string;
145
+ taskId: string;
146
+ provider: string;
147
+ model: string;
148
+ issuedAt: number;
149
+ expiresAt: number;
150
+ regularTps?: number;
151
+ }
152
+
153
+ export type HighspeedPrepareFallbackReason =
154
+ | "automation"
155
+ | "cooldown"
156
+ | "draw-miss"
157
+ | "draw-timeout"
158
+ | "expired"
159
+ | "foreign-card"
160
+ | "no-coding-plan"
161
+ | "unavailable";
162
+
163
+ export interface HighspeedPrepareTurnParams {
164
+ taskId: string;
165
+ /**
166
+ * 会话模型选择的 Provider ID。draw body 的 provider 契约是白名单里的 Coding Plan
167
+ * builtin 身份(builtin:zai-coding-plan / builtin:bigmodel-coding-plan),由服务层
168
+ * 发送前解析;调用方禁止直接传白名单值或把完整 providerId 透传上链路
169
+ * (透传会被服务端白名单拒绝并返回 3001 参数错误)。
170
+ */
171
+ providerId: string;
172
+ /**
173
+ * 会话当前选择的模型。除 provider 白名单外,模型还必须属于所选 Coding Plan Provider 的
174
+ * 模型目录(Built-in Provider Config 投影)才会发起新 draw;陈旧的 (coding-plan provider,
175
+ * 已下线模型) 组合按 unavailable 降级(spec §3 规则 14)。
176
+ */
177
+ model: string;
178
+ /**
179
+ * 会话当前生效的思考档位。加速只换端点不换模型,卡的 modelSelection 必须带上与会话
180
+ * 一致的 reasoningLevel;否则 CLI Registry 会因 execution 选择缺档位判定
181
+ * reasoning-level-missing,抛出 "Reasoning level is required" 让整轮发送失败。
182
+ * 模型无思考档位时省略(不写 options)。
183
+ */
184
+ reasoningLevel?: string;
185
+ /** 仅表示本轮由 Automation 派发;不得用 Task 上粘性的 cronAutomationId 代替。 */
186
+ automationId?: string;
187
+ waitBudgetMs?: number;
188
+ }
189
+
190
+ /**
191
+ * 单轮加速执行材料。静态事实(端点、API 形态、模型能力)由 Built-in Provider Config
192
+ * 提供,这里只携带「本轮选哪个模型」和「本次执行的动态鉴权」,禁止再下发 provider 定义。
193
+ */
194
+ export interface HighspeedTurnExecution {
195
+ modelSelection: ModelSelection;
196
+ requestAuth: { apiKey: string; headers: Record<string, string> };
197
+ /**
198
+ * 加速请求任何失败时退回原模型跑完本轮;卡过期(3402)与其他失败按规则给出不同原因。
199
+ * 退回目标 `target` 是抽卡时的提交选择(会话原模型 + 档位),CLI 优先用它,缺省才退回会话常驻选择。
200
+ */
201
+ selectionFallback: ModelExecutionSelectionFallback;
202
+ }
203
+
204
+ export type HighspeedPrepareTurnResult =
205
+ | {
206
+ kind: "accelerated";
207
+ card: HighspeedCardSnapshot;
208
+ nextDrawAt: number;
209
+ /** Mock 只保留加速业务语义,省略该字段即可继续使用 session 普通推理路由。 */
210
+ execution?: HighspeedTurnExecution;
211
+ }
212
+ | {
213
+ kind: "fallback";
214
+ reason: HighspeedPrepareFallbackReason;
215
+ card?: HighspeedCardSnapshot;
216
+ nextDrawAt: number | null;
217
+ };
218
+
219
+ export interface HighspeedServiceSnapshot {
220
+ card: HighspeedCardSnapshot | null;
221
+ nextDrawAt: number | null;
222
+ drawing: boolean;
223
+ }
224
+
225
+ export type HighspeedMockScenario =
226
+ | "hit-fast"
227
+ | "miss"
228
+ | "hit-after-timeout"
229
+ | "hit-invalid-model"
230
+ | "hit-queue-edit"
231
+ | "existing-foreign-task-card"
232
+ | "expired-card";
@@ -0,0 +1,148 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * App-Provided Tools(MCP Apps 规范「App-Provided Tools」):页面在 `ui/initialize` 声明 `tools` 能力后,
5
+ * 宿主 `tools/list` 发现页面工具、登记到 agent 暴露给模型;模型调用经"实例信箱"(live-only 投影增量
6
+ * `pluginUi.appToolCall`,按 scopeId + generation 投递)到达渲染端,页面 `tools/call` 执行后回传。
7
+ * 这里只有限额、描述符归一化与模型侧命名;纯 domain,零 IO。
8
+ */
9
+
10
+ /** 每个页面实例最多登记的工具数(规范推荐值)。 */
11
+ export const MCP_APPS_APP_TOOLS_MAX_PER_INSTANCE = 50;
12
+ export const MCP_APPS_APP_TOOL_NAME_MAX_CHARS = 128;
13
+ export const MCP_APPS_APP_TOOL_TITLE_MAX_CHARS = 256;
14
+ export const MCP_APPS_APP_TOOL_DESCRIPTION_MAX_CHARS = 4096;
15
+ /** inputSchema / outputSchema 序列化后的上限(UTF-8 字节)。 */
16
+ export const MCP_APPS_APP_TOOL_SCHEMA_MAX_BYTES = 64 * 1024;
17
+ /** 渲染端认领待执行调用的期限;超时视为页面未运行(信箱投递不重发)。 */
18
+ export const MCP_APPS_APP_TOOL_CLAIM_TIMEOUT_MS = 5_000;
19
+ /** 认领后页面执行 tools/call 的期限(规范推荐值)。 */
20
+ export const MCP_APPS_APP_TOOL_CALL_TIMEOUT_MS = 30_000;
21
+ /** 单次结果序列化上限(规范推荐值)。 */
22
+ export const MCP_APPS_APP_TOOL_RESULT_MAX_BYTES = 10 * 1024 * 1024;
23
+ /** 模型调用参数进信箱前的上限;超限直接以错误结束,不下发。 */
24
+ export const MCP_APPS_APP_TOOL_ARGUMENTS_MAX_BYTES = 1024 * 1024;
25
+ /** list_changed 触发的重新登记最小间隔。 */
26
+ export const MCP_APPS_APP_TOOLS_REFRESH_INTERVAL_MS = 1_000;
27
+ export const MCP_APPS_APP_TOOL_ERROR_MESSAGE_MAX_CHARS = 4096;
28
+ export const MCP_APPS_APP_TOOL_CALL_ID_MAX_CHARS = 128;
29
+ /** 模型侧名字前缀:与 server 工具的 `mcp__` 天然不冲突。 */
30
+ export const MCP_APPS_APP_TOOL_MODEL_NAME_PREFIX = "app__";
31
+
32
+ const utf8Bytes = (value: string) => new TextEncoder().encode(value).length;
33
+
34
+ /** 与 agent 的 MCP 工具名同一清洗规则:非 `[A-Za-z0-9_-]` 替换为 `_` 并合并连续下划线。 */
35
+ export function sanitizeMcpAppsModelNamePart(name: string): string {
36
+ const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
37
+ return sanitized.length > 0 ? sanitized : "unknown";
38
+ }
39
+
40
+ /** 模型侧名字 `app__<server>__<tool>`。 */
41
+ export function buildMcpAppsAppToolModelName(serverName: string, toolName: string): string {
42
+ return `${MCP_APPS_APP_TOOL_MODEL_NAME_PREFIX}${sanitizeMcpAppsModelNamePart(serverName)}__${sanitizeMcpAppsModelNamePart(toolName)}`;
43
+ }
44
+
45
+ const objectSchemaSchema = z
46
+ .record(z.string(), z.unknown())
47
+ .refine((value) => value.type === "object", { message: "schema type must be object" })
48
+ .refine((value) => utf8Bytes(JSON.stringify(value)) <= MCP_APPS_APP_TOOL_SCHEMA_MAX_BYTES, {
49
+ message: "schema is too large",
50
+ });
51
+
52
+ export const mcpAppsAppToolAnnotationsSchema = z.strictObject({
53
+ title: z.string().max(MCP_APPS_APP_TOOL_TITLE_MAX_CHARS).optional(),
54
+ readOnlyHint: z.boolean().optional(),
55
+ destructiveHint: z.boolean().optional(),
56
+ idempotentHint: z.boolean().optional(),
57
+ openWorldHint: z.boolean().optional(),
58
+ });
59
+ export type McpAppsAppToolAnnotations = z.infer<typeof mcpAppsAppToolAnnotationsSchema>;
60
+
61
+ export const mcpAppsAppToolDescriptorSchema = z.strictObject({
62
+ name: z.string().min(1).max(MCP_APPS_APP_TOOL_NAME_MAX_CHARS),
63
+ title: z.string().max(MCP_APPS_APP_TOOL_TITLE_MAX_CHARS).optional(),
64
+ description: z.string().max(MCP_APPS_APP_TOOL_DESCRIPTION_MAX_CHARS).optional(),
65
+ inputSchema: objectSchemaSchema,
66
+ outputSchema: objectSchemaSchema.optional(),
67
+ annotations: mcpAppsAppToolAnnotationsSchema.optional(),
68
+ });
69
+ export type McpAppsAppToolDescriptor = z.infer<typeof mcpAppsAppToolDescriptorSchema>;
70
+
71
+ const ANNOTATION_BOOLEAN_KEYS = [
72
+ "readOnlyHint",
73
+ "destructiveHint",
74
+ "idempotentHint",
75
+ "openWorldHint",
76
+ ] as const;
77
+
78
+ /**
79
+ * 渲染端用:把页面 `tools/list` 的结果收窄成可登记的描述符。不合规的项跳过(不让整次登记失败),
80
+ * 超过 50 个只取前 50 个;返回被跳过的名字与原因供日志。
81
+ */
82
+ export function normalizeMcpAppsAppTools(tools: readonly unknown[]): {
83
+ tools: McpAppsAppToolDescriptor[];
84
+ skipped: Array<{ name: string | null; reason: string }>;
85
+ } {
86
+ const accepted: McpAppsAppToolDescriptor[] = [];
87
+ const skipped: Array<{ name: string | null; reason: string }> = [];
88
+ const seen = new Set<string>();
89
+ for (const raw of tools) {
90
+ const record = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : null;
91
+ const name = typeof record?.name === "string" ? record.name : null;
92
+ if (accepted.length >= MCP_APPS_APP_TOOLS_MAX_PER_INSTANCE) {
93
+ skipped.push({ name, reason: "limit" });
94
+ continue;
95
+ }
96
+ if (!record || name === null) {
97
+ skipped.push({ name, reason: "invalid" });
98
+ continue;
99
+ }
100
+ if (seen.has(name)) {
101
+ skipped.push({ name, reason: "duplicate" });
102
+ continue;
103
+ }
104
+ const rawAnnotations =
105
+ record.annotations && typeof record.annotations === "object"
106
+ ? (record.annotations as Record<string, unknown>)
107
+ : null;
108
+ const annotations: Record<string, unknown> = {};
109
+ if (typeof rawAnnotations?.title === "string") annotations.title = rawAnnotations.title;
110
+ for (const key of ANNOTATION_BOOLEAN_KEYS) {
111
+ if (typeof rawAnnotations?.[key] === "boolean") annotations[key] = rawAnnotations[key];
112
+ }
113
+ const candidate = {
114
+ name,
115
+ ...(typeof record.title === "string" ? { title: record.title } : {}),
116
+ ...(typeof record.description === "string" ? { description: record.description } : {}),
117
+ inputSchema: record.inputSchema,
118
+ ...(record.outputSchema !== undefined ? { outputSchema: record.outputSchema } : {}),
119
+ ...(Object.keys(annotations).length > 0 ? { annotations } : {}),
120
+ };
121
+ const parsed = mcpAppsAppToolDescriptorSchema.safeParse(candidate);
122
+ if (!parsed.success) {
123
+ skipped.push({ name, reason: parsed.error.issues[0]?.message ?? "invalid" });
124
+ continue;
125
+ }
126
+ seen.add(name);
127
+ accepted.push(parsed.data);
128
+ }
129
+ return { tools: accepted, skipped };
130
+ }
131
+
132
+ /** 页面 `tools/call` 的结果(MCP CallToolResult 形状,字段集宽松:content 项原样保留)。 */
133
+ export const mcpAppsAppToolCallResultSchema = z.object({
134
+ content: z.array(z.record(z.string(), z.unknown())),
135
+ structuredContent: z.unknown().optional(),
136
+ isError: z.boolean().optional(),
137
+ _meta: z.record(z.string(), z.unknown()).optional(),
138
+ });
139
+ export type McpAppsAppToolCallResult = z.infer<typeof mcpAppsAppToolCallResultSchema>;
140
+
141
+ /** 信箱里的一次待执行调用:按 (scopeId, generation) 投递给唯一实例。 */
142
+ export interface McpAppsAppToolCallRequest {
143
+ activity?: boolean;
144
+ cancelled?: boolean;
145
+ callId: string;
146
+ toolName: string;
147
+ arguments: Record<string, unknown>;
148
+ }