paseo-plugin-zcode-provider 0.0.0-stage → 0.2.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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +29 -0
  3. package/README.md +112 -2
  4. package/client/account.tsx +827 -0
  5. package/client/diagnostics.tsx +379 -0
  6. package/client/runtime.tsx +296 -0
  7. package/client/settings.tsx +84 -0
  8. package/client/value.tsx +20 -0
  9. package/icon.svg +1 -0
  10. package/index.client.tsx +22 -0
  11. package/index.server.ts +39 -0
  12. package/package.json +77 -4
  13. package/paseo-plugin.json +6 -0
  14. package/server/account.ts +781 -0
  15. package/server/attachments.ts +150 -0
  16. package/server/build-info.ts +2 -0
  17. package/server/conversation.ts +319 -0
  18. package/server/diagnostics.ts +252 -0
  19. package/server/discovery/discover.ts +225 -0
  20. package/server/discovery/manifest.ts +36 -0
  21. package/server/discovery/types.ts +30 -0
  22. package/server/errors.ts +36 -0
  23. package/server/host/bridge.ts +490 -0
  24. package/server/host/schemas.ts +119 -0
  25. package/server/logger.ts +19 -0
  26. package/server/mapping.ts +270 -0
  27. package/server/models.ts +68 -0
  28. package/server/persistence.ts +146 -0
  29. package/server/presentation.ts +470 -0
  30. package/server/provider-status.ts +81 -0
  31. package/server/provider.ts +838 -0
  32. package/server/runtime/managed.ts +501 -0
  33. package/server/runtime/pins.ts +72 -0
  34. package/server/runtime-setup.ts +77 -0
  35. package/server/session-types.ts +49 -0
  36. package/server/session.ts +866 -0
  37. package/server/status.ts +89 -0
  38. package/server/timeline.ts +18 -0
  39. package/server/usage.ts +362 -0
  40. package/server/vendor/zcode/LICENSE +202 -0
  41. package/server/vendor/zcode/NOTICE.md +70 -0
  42. package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
  43. package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
  44. package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
  45. package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
  46. package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
  47. package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
  48. package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
  49. package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
  50. package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
  51. package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
  52. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
  53. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
  54. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
  55. package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
  56. package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
  57. package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
  58. package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
  59. package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
  60. package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
  61. package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
  62. package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
  63. package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
  64. package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
  65. package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
  66. package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
  67. package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
  68. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
  69. package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
  70. package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
  71. package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
  72. package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
  73. package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
  74. package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
  75. package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
  76. package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
  77. package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
  78. package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
  79. package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
  80. package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
  81. package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
  82. package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
  83. package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
  84. package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
  85. package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
  86. package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
  87. package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
  88. package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
  89. package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
  90. package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
  91. package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
  92. package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
  93. package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
  94. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
  95. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
  96. package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
  97. package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
  98. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
  99. package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
  100. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
  101. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
  102. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
  103. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
  104. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
  105. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
  106. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
  107. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
  108. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
  109. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
  110. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
  111. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
  112. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
  113. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
  114. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
  115. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
  116. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
  117. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
  118. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
  119. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
  120. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
  121. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
  122. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
  123. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
  124. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
  125. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
  126. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
  127. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
  128. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
  129. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
  130. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
  131. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
  132. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
  133. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
  134. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
  135. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
  136. package/server/vendor/zcode/provenance.json +405 -0
  137. package/shared/account.ts +201 -0
  138. package/shared/diagnostics.ts +52 -0
  139. package/shared/runtime-setup.ts +73 -0
  140. package/vendor/acp-registry/LICENSE +176 -0
  141. package/vendor/paseo/LICENSE +211 -0
@@ -0,0 +1,240 @@
1
+ // V4 physical wire encoder:按调用方真实 JSON/RPC envelope 计量,logical frame
2
+ // 超过物理上限时只在 UTF-8 bytes 层分片,不改变 seq/apply 语义。
3
+ import { PROTOCOL_V4_LIMITS, V4_WIRE_PROTOCOL_VERSION } from "./core.js";
4
+ import { crc32WireBytes, encodeWireBytesBase64 } from "./wire-binary.js";
5
+ import type {
6
+ TopicFrameDeliveryKind,
7
+ TopicWireChecksum,
8
+ TopicWireFrame,
9
+ TopicWireFrameCandidate,
10
+ } from "./wire.js";
11
+
12
+ const CHANNEL_EVENT_RESPONSE_TYPE = 204;
13
+ const SOCKET_PROTOCOL_HEADER_BYTES = 13;
14
+
15
+ function vqlByteLength(value: number): number {
16
+ let bytes = 1;
17
+ for (let remaining = value >>> 7; remaining > 0; remaining >>>= 7) bytes += 1;
18
+ return bytes;
19
+ }
20
+
21
+ /**
22
+ * 按当前生产三层真实承载计量 topic notification。mobile relay
23
+ * 会把 Channel binary 再 base64,不能只测 CLI 的 logical JSON。
24
+ */
25
+ export function measureTopicNotificationEnvelopeBytes(wire: TopicWireFrameCandidate): {
26
+ cliNdjsonBytes: number;
27
+ channelSocketBytes: number;
28
+ mobileRelayBytes: number;
29
+ maxBytes: number;
30
+ } {
31
+ const cliNdjsonBytes =
32
+ utf8JsonByteLength({
33
+ method: "v4/conversation/frame",
34
+ params: wire,
35
+ }) + 1;
36
+
37
+ const wireJsonBytes = utf8JsonByteLength(wire);
38
+ // @zcode/rpc serialization:Array tag+length+[EventFire,id],再加 Object tag+length+JSON。
39
+ // ChannelClient 的 id 无 31-bit wrap;超过 signed int 后 serialize 会走 Object JSON
40
+ // fallback。因此按 Number.MAX_SAFE_INTEGER 的 16-byte JSON 形态计算最坏 header。
41
+ const maxEventIdJsonBytes = String(Number.MAX_SAFE_INTEGER).length;
42
+ const maxEventIdSerializedBytes = 1 + vqlByteLength(maxEventIdJsonBytes) + maxEventIdJsonBytes;
43
+ const channelHeaderBytes =
44
+ 1 +
45
+ vqlByteLength(2) +
46
+ 1 +
47
+ vqlByteLength(CHANNEL_EVENT_RESPONSE_TYPE) +
48
+ maxEventIdSerializedBytes;
49
+ const channelPayloadBytes = channelHeaderBytes + 1 + vqlByteLength(wireJsonBytes) + wireJsonBytes;
50
+ // SocketProtocol/PersistentProtocol 在 Channel payload 外再加固定 13-byte frame header。
51
+ const channelSocketBytes = channelPayloadBytes + SOCKET_PROTOCOL_HEADER_BYTES;
52
+ // topic encoder 继续用 legacy 单片 outer 作为保守 logical budget;production 已不发送该形态,
53
+ // Channel payload 会再经 acknowledged raw adapter 独立分片并按最终 JSON 精确计量。
54
+ // 两层预算分别约束 topic logical frame 与 relay physical frame,不能互相替代。
55
+ const dataBase64Bytes = 4 * Math.ceil(channelPayloadBytes / 3);
56
+ const transportId = "x".repeat(PROTOCOL_V4_LIMITS.transportEnvelopeIdMaxChars);
57
+ const mobileRelayFixedBytes = utf8JsonByteLength({
58
+ type: "data",
59
+ payload: {
60
+ zcode_type: "rpc-frame",
61
+ bridgeSessionId: transportId,
62
+ bridgeGeneration: Number.MAX_SAFE_INTEGER,
63
+ recoveryId: transportId,
64
+ seq: Number.MAX_SAFE_INTEGER,
65
+ dataBase64: "",
66
+ },
67
+ client_ts: Number.MAX_SAFE_INTEGER,
68
+ server_ts: Number.MAX_SAFE_INTEGER,
69
+ });
70
+ const mobileRelayBytes = mobileRelayFixedBytes + dataBase64Bytes;
71
+ return {
72
+ cliNdjsonBytes,
73
+ channelSocketBytes,
74
+ mobileRelayBytes,
75
+ maxBytes: Math.max(cliNdjsonBytes, channelSocketBytes, mobileRelayBytes),
76
+ };
77
+ }
78
+
79
+ export function utf8JsonByteLength(value: unknown): number {
80
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
81
+ }
82
+
83
+ function encodeJson(value: unknown): Uint8Array {
84
+ return new TextEncoder().encode(JSON.stringify(value));
85
+ }
86
+
87
+ export class TopicWireFrameEncodingError extends Error {
88
+ constructor(readonly reasonCode: string) {
89
+ super(reasonCode);
90
+ this.name = "TopicWireFrameEncodingError";
91
+ }
92
+ }
93
+
94
+ function hardBound(value: number | undefined, maximum: number, name: string): number {
95
+ const resolved = value ?? maximum;
96
+ if (!Number.isFinite(resolved) || resolved <= 0) {
97
+ throw new TopicWireFrameEncodingError(`proto.invalidLimit.${name}`);
98
+ }
99
+ return Math.min(Math.floor(resolved), maximum);
100
+ }
101
+
102
+ export interface EncodeTopicWireFramesOptions<F> {
103
+ deliveryKind: TopicFrameDeliveryKind;
104
+ topic: string;
105
+ subscriptionId: string;
106
+ logicalFrameId: string;
107
+ logicalFrameOrdinal: number;
108
+ maxPhysicalFrameBytes?: number;
109
+ maxAssemblyBytes?: number;
110
+ measurePhysicalFrameBytes(wire: TopicWireFrame<F>): number;
111
+ }
112
+
113
+ function makeFragment<F>(params: {
114
+ options: EncodeTopicWireFramesOptions<F>;
115
+ fragmentIndex: number;
116
+ fragmentCount: number;
117
+ logicalBytes: number;
118
+ checksum: TopicWireChecksum;
119
+ dataBase64: string;
120
+ }): TopicWireFrame<F> {
121
+ return {
122
+ wireVersion: V4_WIRE_PROTOCOL_VERSION,
123
+ kind: "fragment",
124
+ deliveryKind: params.options.deliveryKind,
125
+ logicalFrameId: params.options.logicalFrameId,
126
+ logicalFrameOrdinal: params.options.logicalFrameOrdinal,
127
+ topic: params.options.topic,
128
+ subscriptionId: params.options.subscriptionId,
129
+ fragmentIndex: params.fragmentIndex,
130
+ fragmentCount: params.fragmentCount,
131
+ logicalBytes: params.logicalBytes,
132
+ checksum: params.checksum,
133
+ dataBase64: params.dataBase64,
134
+ };
135
+ }
136
+
137
+ function findFragmentByteBudget<F>(params: {
138
+ options: EncodeTopicWireFramesOptions<F>;
139
+ logicalBytes: number;
140
+ checksum: TopicWireChecksum;
141
+ maxPhysicalFrameBytes: number;
142
+ }): number {
143
+ let low = 1;
144
+ let high = Math.min(params.logicalBytes, params.maxPhysicalFrameBytes);
145
+ let best = 0;
146
+ // 使用最坏索引位数测量,实际 fragment 的 envelope 只会更小。
147
+ const worstCount = params.logicalBytes;
148
+ while (low <= high) {
149
+ const candidate = Math.floor((low + high) / 2);
150
+ const dataBase64 = "A".repeat(4 * Math.ceil(candidate / 3));
151
+ const wire = makeFragment({
152
+ options: params.options,
153
+ fragmentIndex: Math.max(0, worstCount - 1),
154
+ fragmentCount: worstCount,
155
+ logicalBytes: params.logicalBytes,
156
+ checksum: params.checksum,
157
+ dataBase64,
158
+ });
159
+ if (params.options.measurePhysicalFrameBytes(wire) <= params.maxPhysicalFrameBytes) {
160
+ best = candidate;
161
+ low = candidate + 1;
162
+ } else {
163
+ high = candidate - 1;
164
+ }
165
+ }
166
+ return best;
167
+ }
168
+
169
+ export function encodeTopicWireFrames<F>(
170
+ frame: F,
171
+ options: EncodeTopicWireFramesOptions<F>,
172
+ ): TopicWireFrame<F>[] {
173
+ const maxPhysicalFrameBytes = hardBound(
174
+ options.maxPhysicalFrameBytes,
175
+ PROTOCOL_V4_LIMITS.maxFrameBytes,
176
+ "maxPhysicalFrameBytes",
177
+ );
178
+ const maxAssemblyBytes = hardBound(
179
+ options.maxAssemblyBytes,
180
+ PROTOCOL_V4_LIMITS.logicalFrameAssemblyMaxBytes,
181
+ "maxAssemblyBytes",
182
+ );
183
+ const logical = encodeJson(frame);
184
+ if (logical.byteLength > maxAssemblyBytes) {
185
+ throw new TopicWireFrameEncodingError("proto.frameAssemblyTooLarge");
186
+ }
187
+
188
+ const complete: TopicWireFrame<F> = {
189
+ wireVersion: V4_WIRE_PROTOCOL_VERSION,
190
+ kind: "complete",
191
+ deliveryKind: options.deliveryKind,
192
+ logicalFrameId: options.logicalFrameId,
193
+ logicalFrameOrdinal: options.logicalFrameOrdinal,
194
+ topic: options.topic,
195
+ subscriptionId: options.subscriptionId,
196
+ frame,
197
+ };
198
+ if (options.measurePhysicalFrameBytes(complete) <= maxPhysicalFrameBytes) {
199
+ return [complete];
200
+ }
201
+
202
+ const checksum: TopicWireChecksum = {
203
+ algorithm: "crc32",
204
+ value: crc32WireBytes(logical),
205
+ };
206
+ const chunkBytes = findFragmentByteBudget({
207
+ options,
208
+ logicalBytes: logical.byteLength,
209
+ checksum,
210
+ maxPhysicalFrameBytes,
211
+ });
212
+ if (chunkBytes < 1) {
213
+ throw new TopicWireFrameEncodingError("proto.frameEnvelopeTooLarge");
214
+ }
215
+ const fragmentCount = Math.ceil(logical.byteLength / chunkBytes);
216
+ if (fragmentCount > PROTOCOL_V4_LIMITS.logicalFrameAssemblyMaxFragments) {
217
+ throw new TopicWireFrameEncodingError("proto.frameFragmentCountExceeded");
218
+ }
219
+
220
+ const frames: TopicWireFrame<F>[] = [];
221
+ for (let fragmentIndex = 0; fragmentIndex < fragmentCount; fragmentIndex += 1) {
222
+ const start = fragmentIndex * chunkBytes;
223
+ const end = Math.min(start + chunkBytes, logical.byteLength);
224
+ const wire = makeFragment({
225
+ options,
226
+ fragmentIndex,
227
+ fragmentCount,
228
+ logicalBytes: logical.byteLength,
229
+ checksum,
230
+ dataBase64: encodeWireBytesBase64(logical.subarray(start, end)),
231
+ });
232
+ if (options.measurePhysicalFrameBytes(wire) > maxPhysicalFrameBytes) {
233
+ // physical 上限包含调用方真实 envelope;任何估算漂移都必须
234
+ // fail closed,不能把超限 frame 交给下游再静默截断。
235
+ throw new TopicWireFrameEncodingError("proto.frameEnvelopeTooLarge");
236
+ }
237
+ frames.push(wire);
238
+ }
239
+ return frames;
240
+ }
@@ -0,0 +1,39 @@
1
+ // V4 physical assembly fault 的分类:**内容确定性失败** vs 瞬态失败。
2
+ //
3
+ // 恢复阶梯默认把 fault 当瞬态处理:先 same-sub resync(服务端可能回 resume),再断档则升级
4
+ // forceSnapshot,仍不成则 fail closed。这套升级对丢片、超时、校验和不符是对的——重投一次很可能
5
+ // 就好了。对 **schema 拒收** 却是错的:字节已经过了 length/checksum/UTF-8/JSON 四道关,被拒说明
6
+ // 本端读不懂对端发来的**内容**,而 resume 档只会把同一批 delta 再投一遍,必然同样被拒。
7
+ //
8
+ // 工具卡载荷出现不兼容字段时,重投相同内容无法恢复;应尝试快照后明确报告内容不兼容。
9
+ //
10
+ // 本模块只回答「这个 reason code 是不是确定性内容失败」,不决定怎么处置——处置在两个 store 里
11
+ // (conversationProjectionStore / sessionsIndexStore)。
12
+
13
+ /**
14
+ * 帧内容通过了 checksum/JSON,却没过 zod schema 的 reason code。
15
+ *
16
+ * 常量化而不是各处写字面量:判定这一侧与产出这一侧(wire-assembler、wire-reassembly)必须永远
17
+ * 是同一个词——两处分叉的症状正是本模块要修的那类静默失配。
18
+ */
19
+ export const WIRE_FAULT_INVALID_PAYLOAD = "proto.frameAssemblyInvalidPayload";
20
+
21
+ /**
22
+ * 这个 fault 是不是「同一份内容再投一次必然再被拒」的确定性失败。
23
+ *
24
+ * 目前只有 schema 拒收一个。刻意**不**把 `proto.frameAssemblyInvalidJson` 也算进来:JSON 不合法
25
+ * 也可能来自组装路径本身(分片拼接、编码边界),重投确实可能不同;把瞬态误判成确定性会让本该
26
+ * 自愈的 gap 停在原地,那是比多一次无用重试更坏的失败。按证据逐个加,不按猜测扩大。
27
+ */
28
+ export function isDeterministicContentFault(reasonCode: string | undefined): boolean {
29
+ return reasonCode === WIRE_FAULT_INVALID_PAYLOAD;
30
+ }
31
+
32
+ /**
33
+ * 内容确定性失败走到底的客户端终态 code(`fault.subscription.*` 是 04-sync 的词表)。
34
+ *
35
+ * 与 `fault.subscription.recoveryFailed` 分开是有用的区分,不是命名洁癖:后者是「链路没恢复
36
+ * 过来」,重连有意义;本 code 是「本端读不懂对端发来的内容」,重连必然得到同样结果。遥测按
37
+ * code 聚合,两者混在一起会把一次版本失配读成一片网络抖动。
38
+ */
39
+ export const SUBSCRIPTION_CONTENT_REJECTED = "fault.subscription.contentRejected";
@@ -0,0 +1,159 @@
1
+ // V4 physical wire schema:logical topic frame 保持原子 seq 语义,超大帧只在
2
+ // UTF-8 byte 层分片。codec/reassembly 纯函数见 wire-codec.ts。
3
+ import { z } from "zod";
4
+ import { PROTOCOL_V4_LIMITS, V4_WIRE_PROTOCOL_VERSION } from "./core.js";
5
+ import { topicWireBase64Schema } from "./wire-binary.js";
6
+
7
+ export const topicWireChecksumSchema = z
8
+ .object({
9
+ algorithm: z.literal("crc32"),
10
+ value: z.string().regex(/^[0-9a-f]{8}$/u),
11
+ })
12
+ .strict();
13
+ export type TopicWireChecksum = z.infer<typeof topicWireChecksumSchema>;
14
+
15
+ /** publisher reservation 对物理帧用途的权威标记;consumer 禁止按 RPC 时序猜测。 */
16
+ export const topicFrameDeliveryKindSchema = z.enum(["initial", "online", "recovery"]);
17
+ export type TopicFrameDeliveryKind = z.infer<typeof topicFrameDeliveryKindSchema>;
18
+
19
+ export type TopicWireFrame<F> =
20
+ | {
21
+ wireVersion: typeof V4_WIRE_PROTOCOL_VERSION;
22
+ kind: "complete";
23
+ deliveryKind: TopicFrameDeliveryKind;
24
+ logicalFrameId: string;
25
+ logicalFrameOrdinal: number;
26
+ topic: string;
27
+ subscriptionId: string;
28
+ frame: F;
29
+ }
30
+ | {
31
+ wireVersion: typeof V4_WIRE_PROTOCOL_VERSION;
32
+ kind: "fragment";
33
+ deliveryKind: TopicFrameDeliveryKind;
34
+ logicalFrameId: string;
35
+ logicalFrameOrdinal: number;
36
+ topic: string;
37
+ subscriptionId: string;
38
+ fragmentIndex: number;
39
+ fragmentCount: number;
40
+ logicalBytes: number;
41
+ checksum: TopicWireChecksum;
42
+ dataBase64: string;
43
+ };
44
+
45
+ /**
46
+ * service 边界只验证可安全路由/计量的 outer 形状;range/base64/checksum/logical
47
+ * payload 的完整校验必须在 ownership 过滤后的 assembler 中产生 typed fault。
48
+ */
49
+ export const topicWireFrameCandidateSchema = z.discriminatedUnion("kind", [
50
+ z
51
+ .object({
52
+ wireVersion: z.literal(V4_WIRE_PROTOCOL_VERSION),
53
+ kind: z.literal("complete"),
54
+ // ownership 路由只读 topic/subId;坏 deliveryKind 必须进入 owned assembler
55
+ // 产生 typed fault,不能在 service 边界 warn/drop 后让 store 永久等待。
56
+ deliveryKind: z.unknown().optional(),
57
+ logicalFrameId: z.string().min(1),
58
+ logicalFrameOrdinal: z.number(),
59
+ topic: z.string().min(1),
60
+ subscriptionId: z.string().min(1),
61
+ // inner payload 故意不在 service route boundary 校验;缺失/类型/extra 由
62
+ // ownership 后 assembler 统一转 typed fault,避免早期 warn/drop 永久 loading。
63
+ frame: z.unknown().optional(),
64
+ })
65
+ .passthrough(),
66
+ z
67
+ .object({
68
+ wireVersion: z.literal(V4_WIRE_PROTOCOL_VERSION),
69
+ kind: z.literal("fragment"),
70
+ deliveryKind: z.unknown().optional(),
71
+ logicalFrameId: z.string().min(1),
72
+ logicalFrameOrdinal: z.number(),
73
+ topic: z.string().min(1),
74
+ subscriptionId: z.string().min(1),
75
+ fragmentIndex: z.unknown().optional(),
76
+ fragmentCount: z.unknown().optional(),
77
+ logicalBytes: z.unknown().optional(),
78
+ checksum: z.unknown().optional(),
79
+ dataBase64: z.unknown().optional(),
80
+ })
81
+ .passthrough(),
82
+ ]);
83
+ export type TopicWireFrameCandidate = z.infer<typeof topicWireFrameCandidateSchema>;
84
+
85
+ export function createTopicWireFrameSchema<F extends z.ZodTypeAny>(frameSchema: F) {
86
+ return z
87
+ .discriminatedUnion("kind", [
88
+ z
89
+ .object({
90
+ wireVersion: z.literal(V4_WIRE_PROTOCOL_VERSION),
91
+ kind: z.literal("complete"),
92
+ deliveryKind: topicFrameDeliveryKindSchema,
93
+ logicalFrameId: z.string().min(1),
94
+ logicalFrameOrdinal: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
95
+ topic: z.string().min(1),
96
+ subscriptionId: z.string().min(1),
97
+ frame: frameSchema,
98
+ })
99
+ .strict(),
100
+ z
101
+ .object({
102
+ wireVersion: z.literal(V4_WIRE_PROTOCOL_VERSION),
103
+ kind: z.literal("fragment"),
104
+ deliveryKind: topicFrameDeliveryKindSchema,
105
+ logicalFrameId: z.string().min(1),
106
+ logicalFrameOrdinal: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
107
+ topic: z.string().min(1),
108
+ subscriptionId: z.string().min(1),
109
+ fragmentIndex: z.number().int().nonnegative(),
110
+ fragmentCount: z
111
+ .number()
112
+ .int()
113
+ .positive()
114
+ .max(PROTOCOL_V4_LIMITS.logicalFrameAssemblyMaxFragments),
115
+ logicalBytes: z.number().int().positive(),
116
+ checksum: topicWireChecksumSchema,
117
+ dataBase64: topicWireBase64Schema,
118
+ })
119
+ .strict(),
120
+ ])
121
+ .superRefine((wire, context) => {
122
+ const value = wire as unknown as TopicWireFrame<z.output<F>>;
123
+ if (value.kind === "fragment") {
124
+ if (value.fragmentIndex >= value.fragmentCount) {
125
+ context.addIssue({
126
+ code: "custom",
127
+ message: "fragmentIndex must be smaller than fragmentCount",
128
+ path: ["fragmentIndex"],
129
+ });
130
+ }
131
+ if (value.fragmentCount > value.logicalBytes) {
132
+ context.addIssue({
133
+ code: "custom",
134
+ message: "fragmentCount cannot exceed logicalBytes",
135
+ path: ["fragmentCount"],
136
+ });
137
+ }
138
+ return;
139
+ }
140
+ const frame = value.frame as {
141
+ topic?: unknown;
142
+ subscriptionId?: unknown;
143
+ };
144
+ if (frame.topic !== value.topic) {
145
+ context.addIssue({
146
+ code: "custom",
147
+ message: "complete wire topic must match logical frame topic",
148
+ path: ["topic"],
149
+ });
150
+ }
151
+ if (frame.subscriptionId !== value.subscriptionId) {
152
+ context.addIssue({
153
+ code: "custom",
154
+ message: "complete wire subscriptionId must match logical frame subscriptionId",
155
+ path: ["subscriptionId"],
156
+ });
157
+ }
158
+ });
159
+ }