@sema-agent/server 7.50.0 → 7.51.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 (47) hide show
  1. package/dist/boot/device-lane.d.ts +79 -0
  2. package/dist/boot/device-lane.js +63 -0
  3. package/dist/boot/execution-env.d.ts +48 -2
  4. package/dist/boot/execution-env.js +57 -5
  5. package/dist/boot/resolve-spec.d.ts +3 -0
  6. package/dist/boot/resolve-spec.js +12 -9
  7. package/dist/boot/session-faces.d.ts +5 -0
  8. package/dist/boot/session-faces.js +4 -1
  9. package/dist/boot/shutdown.d.ts +8 -0
  10. package/dist/boot/shutdown.js +3 -1
  11. package/dist/boot/stores.js +9 -0
  12. package/dist/config-center/types.d.ts +10 -3
  13. package/dist/config-invariants.d.ts +2 -2
  14. package/dist/config-invariants.js +18 -0
  15. package/dist/config-types.d.ts +12 -0
  16. package/dist/config.js +46 -9
  17. package/dist/device-enrollment.d.ts +125 -0
  18. package/dist/device-enrollment.js +156 -0
  19. package/dist/device-store.d.ts +385 -0
  20. package/dist/device-store.js +407 -0
  21. package/dist/device-ws-hub.d.ts +182 -0
  22. package/dist/device-ws-hub.js +1012 -0
  23. package/dist/device-ws-protocol.d.ts +429 -0
  24. package/dist/device-ws-protocol.js +464 -0
  25. package/dist/env-facts.d.ts +4 -1
  26. package/dist/env-facts.js +1 -0
  27. package/dist/execution-lane-caps.d.ts +152 -0
  28. package/dist/execution-lane-caps.js +166 -0
  29. package/dist/http/routes/capabilities.js +7 -2
  30. package/dist/http/server.d.ts +14 -0
  31. package/dist/http/server.js +4 -1
  32. package/dist/leader/wire.js +4 -2
  33. package/dist/main.js +10 -3
  34. package/dist/orchestration/hardened-vm-runner.d.ts +7 -0
  35. package/dist/orchestration/hardened-vm-runner.js +11 -1
  36. package/dist/orchestration/hardened-vm-worker-runner.js +2 -2
  37. package/dist/plugins/device-store-sql.d.ts +130 -0
  38. package/dist/plugins/device-store-sql.js +574 -0
  39. package/dist/plugins/remote-env-device.d.ts +271 -0
  40. package/dist/plugins/remote-env-device.js +727 -0
  41. package/dist/plugins/remote-scratchpad.js +1 -1
  42. package/dist/plugins/store-backend.d.ts +9 -0
  43. package/dist/plugins/store-backend.js +3 -0
  44. package/dist/task-cwd.d.ts +25 -0
  45. package/dist/task-cwd.js +3 -0
  46. package/dist/task-settings.js +3 -1
  47. package/package.json +2 -2
@@ -0,0 +1,429 @@
1
+ import type { ExecutionErrorCode, FileErrorCode } from "@sema-agent/core";
2
+ /** 唯一的 upgrade 路径(§5.1:`GET /v1/device/ws`)。 */
3
+ export declare const DEVICE_WS_PATH = "/v1/device/ws";
4
+ /** 本 server 铸/认的协议版本。加帧/加字段 = additive 不升版;改语义 = 升版(§5.6)。 */
5
+ export declare const DEVICE_PROTOCOL_VERSION = 1;
6
+ /** 低于此版本 ⇒ `helloReject: protocol_version_unsupported`(响亮拒,不静默降级,#157)。 */
7
+ export declare const DEVICE_MIN_PROTOCOL_VERSION = 1;
8
+ /** 签名的 domain tag —— 换域即换 tag,防止别处的 Ed25519 签名被拿来当握手用。 */
9
+ export declare const DEVICE_HELLO_DOMAIN_TAG = "sema.device.hello.v1";
10
+ /**
11
+ * 传输面硬帽。
12
+ *
13
+ * 🔴 `maxPreAuthFrameBytes` 与 `handshakeDeadlineMs` 是**未鉴权洪泛**的边界(§5.1 pre-auth 资源边界):
14
+ * 它们必须早于任何 SQL 查询与大对象解析生效 —— 一条 1MiB 的垃圾首帧不该换来一次设备表查询。
15
+ */
16
+ export declare const DEVICE_WS_LIMITS: {
17
+ /** 单帧上限(§5.1:文本帧 JSON,超限 = 协议错误)。 */
18
+ readonly maxFrameBytes: number;
19
+ /** 鉴权**之前**允许的帧尺寸(hello 首帧)。 */
20
+ readonly maxPreAuthFrameBytes: 4096;
21
+ /** upgrade 到 helloAck/helloReject 的墙钟上限。 */
22
+ readonly handshakeDeadlineMs: 5000;
23
+ /** `payload` 下行分片的默认片长(单帧帽之下留出 base64 膨胀与帧头余量)。 */
24
+ readonly defaultPayloadPartBytes: number;
25
+ };
26
+ /** 设备 → 服务端的帧型全集。 */
27
+ export declare const DEVICE_FRAME_TYPES: readonly ["hello", "generationAck", "heartbeat", "result", "chunk", "goodbye"];
28
+ export type DeviceFrameType = (typeof DEVICE_FRAME_TYPES)[number];
29
+ /** 服务端 → 设备的帧型全集。 */
30
+ export declare const DEVICE_SERVER_FRAME_TYPES: readonly ["challenge", "helloAck", "helloReject", "instruction", "payload", "cancel", "resultAck", "chunkAck", "bye", "ping"];
31
+ export type DeviceServerFrameType = (typeof DEVICE_SERVER_FRAME_TYPES)[number];
32
+ /** 握手拒因(§5.1/§5.2)。`auth_failed` 刻意同时承载「不存在的 deviceId」与「验签失败」——反枚举。 */
33
+ export declare const DEVICE_HELLO_REJECT_CODES: readonly ["auth_failed", "revoked", "protocol_version_unsupported", "draining", "already_connected"];
34
+ export type DeviceHelloRejectCode = (typeof DEVICE_HELLO_REJECT_CODES)[number];
35
+ /** 服务端主动断连的理由(§5.2/§8-R4/§8-R7)。 */
36
+ export declare const DEVICE_BYE_REASONS: readonly ["revoked", "draining", "superseded"];
37
+ export type DeviceByeReason = (typeof DEVICE_BYE_REASONS)[number];
38
+ /** 设备优雅下线的理由(§5.2)。 */
39
+ export declare const DEVICE_GOODBYE_REASONS: readonly ["shutdown", "sleep", "user_stop"];
40
+ export type DeviceGoodbyeReason = (typeof DEVICE_GOODBYE_REASONS)[number];
41
+ /** 上行流片的种类。`exit` = exec 流的**唯一**终止片,直接承载 exitCode(core OutputChunk 契约)。 */
42
+ export declare const DEVICE_CHUNK_KINDS: readonly ["stdout", "stderr", "data", "exit"];
43
+ export type DeviceChunkKind = (typeof DEVICE_CHUNK_KINDS)[number];
44
+ /** cancel 的终局回执状态(§5.3)。 */
45
+ export declare const DEVICE_CANCEL_STATES: readonly ["not_started", "killed"];
46
+ export type DeviceCancelState = (typeof DEVICE_CANCEL_STATES)[number];
47
+ /**
48
+ * 指令状态闭集(§5.3 task-R2-1)。
49
+ *
50
+ * 🔴 **刻意没有第五个词**:轮2 删掉的那个「已投递」态不可观测(`ws.send()` 成功 ≠ 设备收到),留着
51
+ * 必逼实现者在 commit 与它之间造一个窗口,让「窗口内到达的结果」被当成 unexpected 丢掉。两条路径:
52
+ * `queued → dispatch_committed → terminal` 与 `queued → terminal_never_started`。
53
+ */
54
+ export declare const DEVICE_INSTRUCTION_STATES: readonly ["queued", "dispatch_committed", "terminal", "terminal_never_started"];
55
+ export type DeviceInstructionState = (typeof DEVICE_INSTRUCTION_STATES)[number];
56
+ /** 指令 kind ↔ core 的 FileSystem+Shell 面 1:1(§5.1;双端穷举 switch)。 */
57
+ export declare const DEVICE_INSTRUCTION_KINDS: readonly ["exec", "execStream", "readTextFile", "readTextLines", "readBinaryFile", "writeFile", "writeFileExclusive", "writeFileGuarded", "appendFile", "fileInfo", "listDir", "canonicalPath", "exists", "readLink", "createDir", "remove", "createTempDir", "createTempFile", "statBatch"];
58
+ export type DeviceInstructionKind = (typeof DEVICE_INSTRUCTION_KINDS)[number];
59
+ /** 认得就收窄,认不出就 null(**不猜**:未知 kind 恒不下发,fail-closed 方向)。 */
60
+ export declare function asInstructionKind(v: string): DeviceInstructionKind | null;
61
+ /**
62
+ * 结果帧的错误码闭集 = core 的 `FileErrorCode ∪ ExecutionErrorCode`(§5.1「error code 全闭集」:
63
+ * 未知值映射为协议错误而非透传 string)。`satisfies` 保证这里没有 core 不认识的词。
64
+ */
65
+ export declare const DEVICE_OUTCOME_ERROR_CODES: readonly ["aborted", "already_exists", "auth_failed", "callback_error", "invalid", "is_directory", "not_directory", "not_found", "not_supported", "outcome_unknown", "permission_denied", "precondition_failed", "shell_unavailable", "spawn_error", "suspended", "target_unavailable", "timeout", "transport_lost", "unknown"];
66
+ export type DeviceOutcomeErrorCode = (typeof DEVICE_OUTCOME_ERROR_CODES)[number];
67
+ /**
68
+ * **编译期**对账的另一半:core 往 `FileErrorCode`/`ExecutionErrorCode` 加一个词而本表没跟 ⇒ `Exclude`
69
+ * 不再是 `never` ⇒ `return v` 编译红。`satisfies` 只挡「本表多写了 core 不认的词」,这条挡「本表漏了
70
+ * core 的词」——两个方向都要,否则提货 core 新版时错误码表会静默变成子集(未覆盖的词落进
71
+ * `parseOutcomeErrorCode` 的 null 臂 = 一个合法结局被当协议错误拒掉)。
72
+ */
73
+ export declare function assertCoreOutcomeErrorCodesAreCovered(v: Exclude<FileErrorCode | ExecutionErrorCode, DeviceOutcomeErrorCode>): never;
74
+ /** 帧解析的拒因闭集(进 `device_frame_unexpected` 审计行的 `detail.reason`)。 */
75
+ export declare const DEVICE_FRAME_REJECT_REASONS: readonly ["frame_too_large", "not_json", "not_object", "unknown_type", "bad_shape"];
76
+ export type DeviceFrameRejectReason = (typeof DEVICE_FRAME_REJECT_REASONS)[number];
77
+ /**
78
+ * 汇聚端在**解析之后**还会拒的理由(世代闸 / pending 表不变量)。与解析拒因合成审计行的闭集词表:
79
+ * 两段合起来才是「一条设备帧可能被拒的全部理由」,审计消费方按这一张表读。
80
+ */
81
+ export declare const DEVICE_FRAME_GATE_REJECT_REASONS: readonly ["pre_hello", "generation_mismatch", "unknown_instruction", "instruction_not_committed", "device_mismatch"];
82
+ export type DeviceFrameGateRejectReason = (typeof DEVICE_FRAME_GATE_REJECT_REASONS)[number];
83
+ /** 审计行 `detail.reason` 的全集。 */
84
+ export declare const DEVICE_FRAME_AUDIT_REASONS: readonly ["frame_too_large", "not_json", "not_object", "unknown_type", "bad_shape", "pre_hello", "generation_mismatch", "unknown_instruction", "instruction_not_committed", "device_mismatch"];
85
+ export type DeviceFrameAuditReason = DeviceFrameRejectReason | DeviceFrameGateRejectReason;
86
+ /** 设备文件面的 wire 投影(A-4 的 adapter 负责映射到 core `FileInfo`;本层只冻结形)。 */
87
+ export interface WireFileInfo {
88
+ path: string;
89
+ kind: "file" | "dir" | "symlink" | "other";
90
+ size: number;
91
+ mtimeMs: number;
92
+ mode?: number;
93
+ }
94
+ /**
95
+ * 守卫写的前置条件在 wire 上的投影(core `WriteExpectation` 的字段子集,逐字同名)。
96
+ *
97
+ * 🔴 刻意**不**直接把 core 的类型放上 wire:wire 型是本仓铸的、cli 消费的独立契约(§3.7),core 加一
98
+ * 个字段不该无声地变成一条设备必须懂的新前置条件 —— 那是静默的语义升版。core 加字段时这里编译不红,
99
+ * 但 adapter 侧的**穷举投影**会红(见 `remote-env-device.ts` 的 `toWireExpectation`)。
100
+ */
101
+ export interface WireWriteExpectation {
102
+ canonicalPath: string;
103
+ fileId?: string;
104
+ exclusive?: boolean;
105
+ noFollow?: boolean;
106
+ }
107
+ /** 指令结局(§5.1 `InstructionOutcome`)。 */
108
+ export type DeviceInstructionOutcome = {
109
+ ok: true;
110
+ kind: "exec";
111
+ exitCode: number;
112
+ truncated?: {
113
+ stdout?: boolean;
114
+ stderr?: boolean;
115
+ };
116
+ } | {
117
+ ok: true;
118
+ kind: "read";
119
+ } | {
120
+ ok: true;
121
+ kind: "stat";
122
+ info?: WireFileInfo;
123
+ entries?: WireFileInfo[];
124
+ existsValue?: boolean;
125
+ canonicalPath?: string;
126
+ linkTarget?: string | null;
127
+ }
128
+ /** 写面的**落盘对象收据**(§4.3.1 / core `WriteReceipt`)。`created` 是 additive 扩键(车A-4):
129
+ * core 的收据形要求它,缺席 = adapter 只能猜「是新建还是覆盖」,而猜出来的收据就是假收据。 */
130
+ | {
131
+ ok: true;
132
+ kind: "write";
133
+ canonicalPath: string;
134
+ inode?: string;
135
+ created?: boolean;
136
+ } | {
137
+ ok: true;
138
+ kind: "void";
139
+ } | {
140
+ ok: true;
141
+ kind: "cancelled";
142
+ state: DeviceCancelState;
143
+ } | {
144
+ ok: false;
145
+ errorCode: DeviceOutcomeErrorCode;
146
+ message: string;
147
+ };
148
+ /** per-kind 精确入参(§5.1 task-R1-8:`InstructionArgs` 按 kind 判别,非 any)。 */
149
+ export interface DeviceInstructionArgsByKind {
150
+ exec: {
151
+ command: string;
152
+ };
153
+ execStream: {
154
+ command: string;
155
+ };
156
+ readTextFile: {
157
+ path: string;
158
+ };
159
+ readTextLines: {
160
+ path: string;
161
+ offset?: number;
162
+ limit?: number;
163
+ };
164
+ readBinaryFile: {
165
+ path: string;
166
+ };
167
+ /** 内容**不在** args 里:走 `payload` 下行子序(§5.1),args 只带长度与摘要供设备校验。 */
168
+ writeFile: {
169
+ path: string;
170
+ contentLen: number;
171
+ sha256: string;
172
+ };
173
+ writeFileExclusive: {
174
+ path: string;
175
+ contentLen: number;
176
+ sha256: string;
177
+ };
178
+ /** `expect` = core `WriteExpectation` 的 wire 投影(车A-4 additive):设备在**同一原子步**里验证它。 */
179
+ writeFileGuarded: {
180
+ path: string;
181
+ contentLen: number;
182
+ sha256: string;
183
+ expect: WireWriteExpectation;
184
+ };
185
+ appendFile: {
186
+ path: string;
187
+ contentLen: number;
188
+ sha256: string;
189
+ };
190
+ fileInfo: {
191
+ path: string;
192
+ };
193
+ listDir: {
194
+ path: string;
195
+ };
196
+ canonicalPath: {
197
+ path: string;
198
+ };
199
+ exists: {
200
+ path: string;
201
+ };
202
+ readLink: {
203
+ path: string;
204
+ };
205
+ createDir: {
206
+ path: string;
207
+ recursive?: boolean;
208
+ };
209
+ remove: {
210
+ path: string;
211
+ recursive?: boolean;
212
+ };
213
+ createTempDir: {
214
+ prefix?: string;
215
+ };
216
+ createTempFile: {
217
+ prefix?: string;
218
+ suffix?: string;
219
+ };
220
+ statBatch: {
221
+ paths: string[];
222
+ };
223
+ }
224
+ export type DeviceChallengeFrame = {
225
+ t: "challenge";
226
+ nonce: string;
227
+ protocolVersion: number;
228
+ };
229
+ export type DeviceHelloAckFrame = {
230
+ t: "helloAck";
231
+ ok: true;
232
+ heartbeatIntervalMs: number;
233
+ maxInflight: number;
234
+ /** ⚠️ canonical 十进制**字符串**(F18:SQL BIGINT 超 2^53,wire 不声明装不下 SQL 类型的字段)。 */
235
+ gen: string;
236
+ pendingInstructions: string[];
237
+ chunkWatermarks: Record<string, number>;
238
+ };
239
+ export type DeviceHelloRejectFrame = {
240
+ t: "helloReject";
241
+ code: DeviceHelloRejectCode;
242
+ minProtocolVersion?: number;
243
+ };
244
+ export type DeviceInstructionFrame = {
245
+ [K in DeviceInstructionKind]: {
246
+ t: "instruction";
247
+ gen: string;
248
+ instructionId: string;
249
+ rootSessionId: string;
250
+ issuedSeq: number;
251
+ kind: K;
252
+ args: DeviceInstructionArgsByKind[K];
253
+ cwd?: string;
254
+ shellEnv?: Record<string, string>;
255
+ timeoutMs: number;
256
+ };
257
+ }[DeviceInstructionKind];
258
+ export type DevicePayloadFrame = {
259
+ t: "payload";
260
+ gen: string;
261
+ instructionId: string;
262
+ part: "begin" | "chunk" | "commit";
263
+ offset?: number;
264
+ dataB64?: string;
265
+ totalLen?: number;
266
+ sha256?: string;
267
+ };
268
+ export type DeviceServerFrame = DeviceChallengeFrame | DeviceHelloAckFrame | DeviceHelloRejectFrame | DeviceInstructionFrame | DevicePayloadFrame | {
269
+ t: "cancel";
270
+ gen: string;
271
+ instructionId: string;
272
+ } | {
273
+ t: "resultAck";
274
+ gen: string;
275
+ instructionId: string;
276
+ } | {
277
+ t: "chunkAck";
278
+ gen: string;
279
+ instructionId: string;
280
+ upToStreamSeq: number;
281
+ } | {
282
+ t: "bye";
283
+ gen: string;
284
+ reason: DeviceByeReason;
285
+ } | {
286
+ t: "ping";
287
+ };
288
+ export type DeviceHelloFrame = {
289
+ t: "hello";
290
+ protocolVersion: number;
291
+ deviceId: string;
292
+ epoch: string;
293
+ signatureB64: string;
294
+ platform: {
295
+ os: string;
296
+ arch: string;
297
+ executorVersion: string;
298
+ };
299
+ workspaceRoot: string;
300
+ pathFlavor: string;
301
+ /**
302
+ * 🔴 刻意是 `string[]` 而不是 `DeviceInstructionKind[]`:协议是 additive 的(§5.6),一个**更新的**
303
+ * executor 会声明本 server 还不认识的 kind。收窄在这里拒 = 老 server 认不下新 executor;所以帧里存
304
+ * 原样,由汇聚端与自己的闭集取交集 —— 不认识的 kind 恒不下发,方向仍是 fail-closed。
305
+ */
306
+ supports: string[];
307
+ resuming?: {
308
+ unackedResults: number;
309
+ };
310
+ };
311
+ export type DeviceFrame = DeviceHelloFrame | {
312
+ t: "generationAck";
313
+ gen: string;
314
+ } | {
315
+ t: "heartbeat";
316
+ gen: string;
317
+ seq: number;
318
+ inflight: string[];
319
+ } | {
320
+ t: "result";
321
+ gen: string;
322
+ instructionId: string;
323
+ deviceSeq: number;
324
+ outcome: DeviceInstructionOutcome;
325
+ } | {
326
+ t: "chunk";
327
+ gen: string;
328
+ instructionId: string;
329
+ streamSeq: number;
330
+ kind: DeviceChunkKind;
331
+ dataB64?: string;
332
+ exitCode?: number;
333
+ } | {
334
+ t: "goodbye";
335
+ gen: string;
336
+ reason: DeviceGoodbyeReason;
337
+ graceMs?: number;
338
+ };
339
+ export type DeviceResultFrame = Extract<DeviceFrame, {
340
+ t: "result";
341
+ }>;
342
+ export type DeviceChunkFrame = Extract<DeviceFrame, {
343
+ t: "chunk";
344
+ }>;
345
+ export type DeviceGoodbyeFrame = Extract<DeviceFrame, {
346
+ t: "goodbye";
347
+ }>;
348
+ export type DeviceHeartbeatFrame = Extract<DeviceFrame, {
349
+ t: "heartbeat";
350
+ }>;
351
+ export type DeviceFrameParse = {
352
+ ok: true;
353
+ frame: DeviceFrame;
354
+ } | {
355
+ ok: false;
356
+ reason: DeviceFrameRejectReason;
357
+ detail: string;
358
+ };
359
+ /**
360
+ * **规范** base64(codex 对抗轮 R1-F6,判真)。
361
+ *
362
+ * 🔴 为什么不能只判「是个字符串」再交给 `Buffer.from(v, "base64")`:Node 的解码是**宽松**的 ——
363
+ * `"!!!!"` 解出一个**空** Buffer 而不抛,`"aG@k="` 悄悄丢掉非法字符照样解出两个字节。于是一条被
364
+ * 传输层损坏(或被恶意构造)的流片会以「合法的空/短数据」进入交付路径,水位照常推进、chunkAck
365
+ * 照常回给设备 —— 设备释放了它手上唯一的正本,调用方拿到的是**静默损坏**的输出。
366
+ * 在协议边界判规范形 + 回编码自比,这条路就没了。跨仓同判据:cli 侧编码器必须产规范形。
367
+ */
368
+ export declare function isCanonicalBase64(v: string): boolean;
369
+ /** `gen` 的**规范**十进制串。前导零/正号/空白/小数/负数/超安全整数一律 null(见文件头 F18 注)。 */
370
+ export declare function parseGeneration(v: string): number | null;
371
+ export declare function formatGeneration(n: number): string;
372
+ /**
373
+ * 设备帧的唯一解析入口。
374
+ *
375
+ * 🔴 顺序是 帽 → JSON → 对象 → 帧型 → 形:每一步都有自己的拒因词,审计行才能回答「它到底哪儿坏了」。
376
+ * 没有任何一步会「尽量修一下再放行」——安全轴上的坏形帧只有响亮拒一条路(#157)。
377
+ */
378
+ export declare function parseDeviceFrameText(text: string, opts?: {
379
+ maxBytes?: number;
380
+ }): DeviceFrameParse;
381
+ /**
382
+ * 26 位 Crockford base32(ULID)→ 16 字节。非法字符/长度 ⇒ `null`(**不猜**:一个被静默补零的 epoch
383
+ * 会让两个不同的 executor 进程签出同一份字节)。
384
+ */
385
+ export declare function decodeUlid16(s: string): Buffer | null;
386
+ /**
387
+ * 握手签名的**唯一**字节编码(§5.1 末,跨仓冻结):
388
+ * `"sema.device.hello.v1" ‖ u32be(len(nonce))‖nonce ‖ u32be(len(deviceId))‖deviceId ‖ epoch16 ‖ u32be(version)`。
389
+ */
390
+ export declare function buildHelloSignaturePayload(input: {
391
+ nonce: string;
392
+ deviceId: string;
393
+ epoch: string;
394
+ protocolVersion: number;
395
+ }): Buffer;
396
+ /**
397
+ * 验签判决。`reason` **只进日志**(wire 上永远是同一个 `auth_failed` —— 反枚举),但它必须存在:
398
+ * 一个「公钥导入失败」和一个「签名对不上」在运维上是两回事,把它们一起折成 `false` 就等于把
399
+ * 「这台设备的公钥列存坏了」这条事实永久删掉(#191 门② SHAPE C:错误要**当数据交出**,不是吞掉)。
400
+ */
401
+ export type HelloSignatureVerdict = {
402
+ ok: true;
403
+ } | {
404
+ ok: false;
405
+ reason: string;
406
+ };
407
+ /**
408
+ * 验签。公钥 wire 形 = **raw 32 字节的 base64**(店里 `devices.pubkey` 存的就是它)。
409
+ *
410
+ * 🔴 **恒不抛**:握手期的输入全部来自未鉴权的对端,一个畸形 base64 把异常抛进 socket 事件回调 =
411
+ * 未鉴权者可以让进程走进 uncaughtException。所以每条失败路径都收敛成 `{ok:false, reason}` —— 方向
412
+ * 是拒(fail-closed),而拒的**原因**原样交给调用方去打日志。
413
+ */
414
+ export declare function verifyHelloSignature(input: {
415
+ pubkeyB64: string;
416
+ signatureB64: string;
417
+ payload: Buffer;
418
+ }): HelloSignatureVerdict;
419
+ /**
420
+ * 幂等键(§5.1:server 铸 ULID,**不可猜**)。设备按它去重(at-most-once);wire 上刻意不承载
421
+ * runId/toolCallId —— 它们在 `ExecutionEnv` 面结构性拿不到(core `ExecutionEnvFactoryContext`
422
+ * 只有 sessionId/taskId),关联在 server 侧 pending 表里完成。
423
+ */
424
+ export declare function mintInstructionId(nowMs?: number): string;
425
+ /** 一次性 challenge nonce(128 位熵,base64url canonical,每 socket 一次)。 */
426
+ export declare function mintChallengeNonce(): string;
427
+ /** executor 进程 epoch 的铸造(测试/自测用;真值由设备侧每次启动自铸)。 */
428
+ export declare function mintDeviceEpoch(nowMs?: number): string;
429
+ //# sourceMappingURL=device-ws-protocol.d.ts.map