@sema-agent/server 7.3.0 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/USAGE.md +63 -0
- package/dist/approval-card.d.ts +15 -3
- package/dist/approval-card.js +41 -7
- package/dist/approval-reconciler.d.ts +120 -16
- package/dist/approval-reconciler.js +146 -19
- package/dist/boot/coordinators.js +13 -3
- package/dist/boot/deferred-sandbox-path-env.d.ts +99 -0
- package/dist/boot/deferred-sandbox-path-env.js +279 -0
- package/dist/boot/execution-env.js +11 -1
- package/dist/boot/org-memory.d.ts +6 -0
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +2 -0
- package/dist/boot/reapers.js +11 -4
- package/dist/boot/resolve-spec.d.ts +3 -2
- package/dist/boot/resolve-spec.js +175 -63
- package/dist/boot/runner-deps.d.ts +23 -1
- package/dist/boot/runner-deps.js +8 -11
- package/dist/boot/workflow-orchestration.d.ts +8 -3
- package/dist/boot/workflow-orchestration.js +23 -1
- package/dist/capabilities/center-prompts.js +4 -1
- package/dist/config-center/apply-effective.js +33 -10
- package/dist/config-types.d.ts +32 -9
- package/dist/config.d.ts +6 -1
- package/dist/config.js +65 -12
- package/dist/elicitation.js +5 -1
- package/dist/env-facts.d.ts +3 -1
- package/dist/env-facts.js +3 -1
- package/dist/fleet/fleet-bus.d.ts +6 -1
- package/dist/fleet/fleet-bus.js +25 -3
- package/dist/governance-ask-marks.d.ts +31 -0
- package/dist/governance-ask-marks.js +122 -0
- package/dist/hooks/hook-runner.d.ts +28 -0
- package/dist/hooks/hook-runner.js +180 -24
- package/dist/http/routes/diagnostics.d.ts +84 -0
- package/dist/http/routes/diagnostics.js +145 -0
- package/dist/http/routes/memory-policy.d.ts +2 -1
- package/dist/http/routes/memory-policy.js +77 -13
- package/dist/http/routes/runs.js +1 -1
- package/dist/http/routes/tasks.js +87 -29
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.js +29 -12
- package/dist/http/wire-types.d.ts +7 -2
- package/dist/main.js +51 -8
- package/dist/observability/fail-open.d.ts +109 -0
- package/dist/observability/fail-open.js +227 -0
- package/dist/observability/prompt-manifest.d.ts +17 -0
- package/dist/observability/prompt-manifest.js +8 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +57 -1
- package/dist/orchestration/workflow-notify-journal.js +137 -32
- package/dist/parked-decide.js +9 -4
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -2
- package/dist/plugins/approval-ask-store-memory.js +3 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +27 -5
- package/dist/plugins/approval-ask-store-sql.js +9 -2
- package/dist/plugins/background-shell-support.d.ts +1 -1
- package/dist/plugins/background-shell-support.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +62 -6
- package/dist/plugins/checkpoint-store-sql.js +71 -11
- package/dist/plugins/local-checkpoint-store.d.ts +20 -1
- package/dist/plugins/local-checkpoint-store.js +19 -0
- package/dist/plugins/mailbox-store-sql.d.ts +4 -10
- package/dist/plugins/mailbox-store-sql.js +57 -4
- package/dist/question.d.ts +18 -14
- package/dist/question.js +83 -34
- package/dist/runs.d.ts +8 -0
- package/dist/runs.js +15 -2
- package/dist/runtime-governance.d.ts +18 -0
- package/dist/runtime-governance.js +90 -3
- package/dist/task-settings.d.ts +16 -21
- package/dist/task-settings.js +22 -19
- package/dist/tool-approval.d.ts +33 -6
- package/dist/tool-approval.js +95 -30
- package/dist/trace/core-keyset-guard.d.ts +17 -3
- package/dist/trace/project.d.ts +36 -1
- package/dist/trace/project.js +55 -2
- package/package.json +3 -3
- package/dist/boot/lexical-path-env.d.ts +0 -14
- package/dist/boot/lexical-path-env.js +0 -116
package/USAGE.md
CHANGED
|
@@ -118,6 +118,8 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
118
118
|
# 是 core 退化检测(安全网)的预防。长链推理/council 易跑飞的部署建议设。
|
|
119
119
|
MODEL_FREQUENCY_PENALTY=0.5 MODEL_PRESENCE_PENALTY=0.3
|
|
120
120
|
MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalty 同名键会覆盖它)
|
|
121
|
+
# ⚠️ core ≥5.15 起 max_completion_tokens 是保留键(config.extra_body_output_cap
|
|
122
|
+
# 响亮拒)——输出上限请用任务面的 limits.maxOutputTokens,别从这里塞
|
|
121
123
|
```
|
|
122
124
|
- **brain 拥有的键永远赢**:`temperature`/`max_tokens` 等放进 `extraBody` 会被 strip + core 警告(`phase:"config"`),不会静默改;auth/content-type/version 头硬锁不可顶替(1.60 安全修复)。
|
|
123
125
|
- **必须静态**:`extraBody` 在 boot 时按固定 env 建一次(稳定键序),**不可逐任务变**,否则破前缀缓存(design/9/31)。未设 → 请求字节级不变。
|
|
@@ -148,6 +150,26 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
148
150
|
- 经 `RoleSpec.systemPrompt` 挂在**角色**上,非开发角色保持中立、全局默认 `DEFAULT_SYSTEM_PROMPT` 不变;任务自带 `systemPrompt`(或客户端注入)时仍优先。
|
|
149
151
|
- **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读对抗 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。
|
|
150
152
|
- **记忆(design/138 文件记忆引擎,2026-07-08 起唯一记忆面)**:core 注入式文件引擎——任务开始时 materialize 记忆目录(`MEMORY_ENGINE_DIR`,默认 `~/.ai-agent`),模型用**普通文件技能**读写记忆(CC `# Memory` 指令 + 派生索引;无 remember/recall 工具),任务边界 harvest 门(secret/cap 扫描)提交。单用户默认开,`MEMORY_ENGINE=off` 显式关;多租户恒关(文件基座无租户隔离,fail-closed)。旧 SQL 记忆面(`MEMORY_BACKEND`/`EMBEDDING_*`/`MEMORY_READ_LIMIT`/去重/向量检索、`GET/DELETE /v1/memory` 与 session memory 写 verb)已退役,数据不迁移——升级后对库跑一次 `scripts/drop-memory-tables.sql`。`body.memoryWrite:false` 仍是每请求只读开关(harvest 不提交)。
|
|
153
|
+
- **org 记忆准入(design/170 件A,7.0.0 起 BREAKING)**:`org:*` 记忆 scope 分**两个来源**——部署自证
|
|
154
|
+
(env `MEMORY_SCOPE` 的 org 形 + **单用户部署**的 `projects[].defaultScopes` org 键)直通;**多租户**
|
|
155
|
+
部署里由调用方 `projectId` 选中的登记簿 org 键算 **request 来源**,必须拿到授权目录的逐 principal
|
|
156
|
+
授予才准入,拿不到一律 fail-closed 拒(纯读泄露面:projectId 只过形状门不过授权)。目录源**三态单选,
|
|
157
|
+
授权面不双源合并**:config-center 远程腿(per-principal `orgMemory` 段)> `MEMORY_ORG_DIRECTORY_JSON`
|
|
158
|
+
静态表 > 缺席(request 来源的 org scope 恒拒)。同一个目录也是 `GET /v1/memory/export` /
|
|
159
|
+
`POST /v1/memory/sync/:scope` 的 `org:` 属主门真源(写面另需条目 `write:true`);拒绝形 = 终局
|
|
160
|
+
`memory.admission_denied`(403)/ 瞬时 `memory.admission_required`(503,带 `retryAfterSec`)。
|
|
161
|
+
|
|
162
|
+
| env | 缺省 | 说明 |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| `MEMORY_ORG_ADMISSION_MODE` | `enforce` | `enforce`=判决即结果;`audit`=**运维诊断位**——准入面判决照算、拒绝降为日志+metric,零行为变化(不整拒、不窄化写面);`/v1/memory/export`、`/v1/memory/sync/:scope` 的 `org:` 属主门在 audit 下**逐字保持收编前的 operator-only**(未验证的目录不得开数据面)。两模式都要求目录 client 在场 |
|
|
165
|
+
| `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有 config-center 时被忽略(center 胜 + warn) |
|
|
166
|
+
| `MEMORY_ORG_GRANT_TTL_MS` | `60000`(`[1000, 3600000]`) | 授予/负结果的缓存 TTL = 「有界 LKG」:**新任务/新 resume 腿**的准入判决滞后 ≤ 此值;**在跑任务不受吊销影响**(判决点在 prepare 期) |
|
|
167
|
+
| `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
|
|
168
|
+
|
|
169
|
+
⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
|
|
170
|
+
并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
|
|
171
|
+
⚠️ **回滚脚枪**:回滚到「无 config-center」模板前先清掉残留的 `MEMORY_ORG_DIRECTORY_JSON` —— 否则
|
|
172
|
+
它作为 operator 自证通道**复活旧授权**(详见 `docs/DEPLOY-PREREQS.md`)。
|
|
151
173
|
|
|
152
174
|
**可选 — 模型级联 cascade(core 1.45,opt-in)**
|
|
153
175
|
```bash
|
|
@@ -231,6 +253,19 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启 flee
|
|
|
231
253
|
- **仅 `/v1/tasks`(同步)+ `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多次尝试非单流,400)。与 `verify` **互斥**(同时给 → 400)。
|
|
232
254
|
- 成本上界 = 任务的 `maxCostUsd`(防冷重跑税)。**三条 core 警示**:① 每档**冷重跑**重付输入成本(便宜档常过才划算);② 门收到**未脱敏**输出(自定义门转发外部 verifier 要脱敏);③ **写工具会跑 N 次**——**只用于只读/幂等任务**(每次升级整任务重跑)。开放式任务(找全 bug/文笔)没有可判定 oracle、级联会空转,那种用 `team`(广度对抗)而非级联(深度阶梯)。
|
|
233
255
|
|
|
256
|
+
**可选 — AskUserQuestion 的裁量窗(问活人的三个帽子)**
|
|
257
|
+
```bash
|
|
258
|
+
# 每条 run 腿同时能挂几个问题(并发帽,1..64)
|
|
259
|
+
QUESTION_MAX_CONCURRENT_PER_RUN=2
|
|
260
|
+
# 每条 run 腿一共能问人几次(总量帽=防刷屏,1..10000)
|
|
261
|
+
QUESTION_MAX_TOTAL_PER_RUN=20
|
|
262
|
+
# 一个没人答的问题挂多久后释放(毫秒,1000..3600000;默认 5 分钟)
|
|
263
|
+
QUESTION_TTL_MS=300000
|
|
264
|
+
```
|
|
265
|
+
- 三个值都有出厂缺省(即上面写的值),**不设=行为不变**;越界值**启动期响亮拒**(不静默夹取)。
|
|
266
|
+
- 窗用尽 / 到期 **不是替人作答**:服务只报「此刻无人可答」,落点由引擎选——**durable 部署**(`DURABLE_APPROVAL=true` 且这条腿没有活的流)把问题 **park** 成待办,运维随后经审批面带答案补答;**无 durable 的部署**则让模型被告知"没人在"后按自己的判断继续(run 永不挂死)。
|
|
267
|
+
- 想让人有更长时间答就调大 `QUESTION_TTL_MS`;想让 agent 少打扰人就调小 `QUESTION_MAX_TOTAL_PER_RUN`。
|
|
268
|
+
|
|
234
269
|
**布尔旋钮的取值与极性(运维必读)**
|
|
235
270
|
|
|
236
271
|
布尔 env **只认 `true` / `false` 两个字面量**。写成 `1` / `yes` / `TRUE` ⇒ 该旋钮退回自己的缺省值,并在启动时
|
|
@@ -393,6 +428,29 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
393
428
|
|
|
394
429
|
> **operator 鉴权(审批队列)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator——列任意 owner 待办 + 决议(批/否)。**单租户部署不设=旧行为**(握 service token 即 operator,向后兼容);设了之后,非名单 principal 列待办只看自己的、且**不能决议**(403,防"请求方批自己的高危操作"绕过 F4 闸)。⚠️ **多租户形拒启**(#157-①):`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法——否则空名单会让任一已验证租户读到其他租户的待批队列(读面 true-for-all)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
|
|
395
430
|
>
|
|
431
|
+
> **🔧 升级到 7.5.0(引擎 core 5.17.0)前:把待决审批排空。** 5.17.0 起,park 铸行按**后端能承载的
|
|
432
|
+
> 宽度**落——审批人看到的 args / 预览、盘上躺着的行、resume 真正执行的那份参数,以及运维在 `/decide`
|
|
433
|
+
> 上要回显的那个不透明 `boundInputHash`,都从同一份投影铸出。**本服务的两条 checkpoint 后端
|
|
434
|
+
> (SQL 双生 / 本地文件)都是 JSON 序列化**,已按 5.17.0 的新轴显式声明 `fidelity: "json"`,所以对
|
|
435
|
+
> **JSON 值域**(模型产出的 args 恒在此域内)这次折叠规则变更是**逐字节零变化**:同一份 args 在 7.4.0
|
|
436
|
+
> 和 7.5.0 上算出的 `boundInputHash` 相同,已经发出去的哈希不需要重新取。
|
|
437
|
+
> 需要动作的只有一种行:**升级前就已经 pending 的那些**——它们带的是旧版本铸的哈希与(可能已降级的)
|
|
438
|
+
> 参数,新引擎不会追认改写。**滚版前把它们批/否掉**(`GET /v1/approvals` 列出来,逐个 `/decide`),
|
|
439
|
+
> 或明确接受那批老行仍按旧语义结算。新铸的行不受影响。
|
|
440
|
+
> **同一次升级还要重建 mailbox 两表。** `mailbox_messages` 新增 `hop_chain` 列(引擎的 peer 消息守卫),
|
|
441
|
+
> 而建表语句是 `CREATE TABLE IF NOT EXISTS` —— 对已存在的旧表**一字不改**,升级后每一次 teammate 消息
|
|
442
|
+
> 投递都会报 unknown column 并失败。按本服务的 schema 契约(删库重建、不做增量迁移),滚版时
|
|
443
|
+
> **重建 `mailboxes` / `mailbox_messages` 两表**(或整库),基线见 `docs/schema/baseline-*.sql`。
|
|
444
|
+
> 该表是带 TTL 的短命投递队列而非账本,重建只丢排队中的 teammate 消息;介意就先让在飞的 peer 会话收敛。
|
|
445
|
+
>
|
|
446
|
+
> 顺带一提,新引擎会在铸点**直接拒绝 park**(点名后端、退回同步门)的只有两类值,而且都只可能由
|
|
447
|
+
> 部署侧的 hook / policy 改写进 args —— 模型自己给的参数永远是 JSON,碰不到任何一条:
|
|
448
|
+
> ① **拿不住的值**(函数、symbol、活句柄这些 `structuredClone` 复制不了的),以及 **`SharedArrayBuffer`**
|
|
449
|
+
> ——后者的理由不是「编不成 JSON」而是「克隆之后仍与原持有者共享同一块内存」,一行存下去别人还能改它,
|
|
450
|
+
> 所以在捕获性检查那一步就被拒;② **编不成 JSON 的值**(`BigInt`、循环引用)。
|
|
451
|
+
> 至于 `Date` / `Map` / 正则这类**能编码但会被 JSON 投影压扁**的值,不拒绝 —— 它们按投影后的形态入行,
|
|
452
|
+
> 若投影改变了值,引擎会拿投影后的那份**重新过一遍部署策略**再决定 park。
|
|
453
|
+
>
|
|
396
454
|
> **parked 后台子代的待办分两个 scope 桶**(durable 审批面,core 1.389 起):父任务显式转发审批范围的常规 ask 落在该范围的 scope 下(可预算);无转发时无人值守拦下的敏感操作 ask 落在按 principal 派生的隔离 scope 下(带缺省 deadline、永不自动放行)。operator 全量列表天然两桶全见;**按 `?owner` 过滤时注意两桶可能不同名**,展示面要两个都查。
|
|
397
455
|
|
|
398
456
|
---
|
|
@@ -433,6 +491,11 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
433
491
|
`limit.` / `usage.`(部署级治理窗)/ `capability.`(本部署没接这个面)/ `feature.`(开关没开)/
|
|
434
492
|
`internal.` / `state.`;未知码按前缀兜底永远安全。完整码表见 `docs/ASSISTANT-WIRE-CONTRACT.md` §附录 A。
|
|
435
493
|
|
|
494
|
+
**进程警告面(日志采集方注意)**:引擎(core ≥5.16)在持久 agent 行的 org 判决写回被丢弃等场合走
|
|
495
|
+
Node `process.emitWarning`,警告文案前缀固定 `sema durable-agents:`——采集规则按该前缀匹配,别当噪音过滤掉。
|
|
496
|
+
server 自身的兜底留痕走 stderr 契约行 `[sema] fail-open: <tag>`(逐 tag 一次)+ `fail_open_total` 指标,
|
|
497
|
+
tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。
|
|
498
|
+
|
|
436
499
|
### 9.1 limits.approachNotice(6.1.0 起)
|
|
437
500
|
|
|
438
501
|
引擎默认在上下文用量到 80%/95% 时各注入一次接近提示(SSE 上是 `steering_injected` 帧、
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -32,7 +32,11 @@ export declare const MAX_AGENT_NAME = 200;
|
|
|
32
32
|
* `risk` 的三态形(设计稿 §14.1,core [2794] 回帖后定):`AskRequest.riskAxes?.{irreversible,egress}`
|
|
33
33
|
* 是 **additive optional**,**缺席 = 引擎未判,不是「安全」**。所以两轴在这里是 `optional()`:
|
|
34
34
|
* `true` / `false` / **缺席(未标注)** 三态各自可分,投影层**禁把缺席折算成 false** —— 那等于替引擎
|
|
35
|
-
* 打包票。`requiresRealApproval`
|
|
35
|
+
* 打包票。`requiresRealApproval` 是**粗粒度**安全类标记,两侧的在场契约**不同、别混**:core 侧是
|
|
36
|
+
* `AskRequest.requiresRealApproval?: boolean`(**可选、只在为真时带**,缺席 = 这不是一次安全类 ask,是
|
|
37
|
+
* 正常的否定形而不是坏形);本卡面这一格是**必填 boolean**,由车2 的入参归一化(`=== true`)而来。
|
|
38
|
+
* 它与两轴是两件事而不是新旧替代——原注写的「core 今天唯一在场的标记」是 `riskAxes` 上树之前的现势话,
|
|
39
|
+
* 自 core **5.14.0**(其 CHANGELOG 的 `AskRequest.riskAxes` additive 条)起两者并存。
|
|
36
40
|
*/
|
|
37
41
|
export declare const ApprovalCardSchema: z.ZodObject<{
|
|
38
42
|
toolName: z.ZodString;
|
|
@@ -45,6 +49,7 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
45
49
|
egress: z.ZodOptional<z.ZodBoolean>;
|
|
46
50
|
requiresRealApproval: z.ZodBoolean;
|
|
47
51
|
}, z.core.$strict>;
|
|
52
|
+
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
48
53
|
fromSubagent: z.ZodOptional<z.ZodLiteral<true>>;
|
|
49
54
|
sourceTaskId: z.ZodOptional<z.ZodString>;
|
|
50
55
|
sourceAgentName: z.ZodOptional<z.ZodString>;
|
|
@@ -75,6 +80,7 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
75
80
|
egress: z.ZodOptional<z.ZodBoolean>;
|
|
76
81
|
requiresRealApproval: z.ZodBoolean;
|
|
77
82
|
}, z.core.$strict>;
|
|
83
|
+
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
78
84
|
fromSubagent: z.ZodOptional<z.ZodLiteral<true>>;
|
|
79
85
|
sourceTaskId: z.ZodOptional<z.ZodString>;
|
|
80
86
|
sourceAgentName: z.ZodOptional<z.ZodString>;
|
|
@@ -107,6 +113,9 @@ export interface ApprovalCardSource {
|
|
|
107
113
|
args?: unknown;
|
|
108
114
|
argsOmitted?: boolean;
|
|
109
115
|
toolCallId?: string;
|
|
116
|
+
/** [2942]/[2943]:治理来源标 —— 与 wire 帧**同一份素材**(`ToolApprovalFrame` 结构上满足本接口),
|
|
117
|
+
* 于是 live 帧 / `card_json` / 重放帧三面同源,不是三处各判一遍。 */
|
|
118
|
+
governanceForced?: true;
|
|
110
119
|
fromSubagent?: true;
|
|
111
120
|
sourceTaskId?: string;
|
|
112
121
|
/** 已 redactSecrets。 */
|
|
@@ -121,8 +130,11 @@ export interface ApprovalCardSource {
|
|
|
121
130
|
* design/172 §3.1 的**中性投影**(设计稿 §6.2)—— 写侧的唯一铸造点。
|
|
122
131
|
*
|
|
123
132
|
* `risk` 三态(§14.1):两轴 `optional`,`true`/`false`/**缺席(未标注)** 各自可分。`req` 是 core 交来的
|
|
124
|
-
* `AskRequest`(可能带、也可能不带 `riskAxes`),按 `unknown` 窄读;`requiresRealApproval`
|
|
125
|
-
*
|
|
133
|
+
* `AskRequest`(可能带、也可能不带 `riskAxes`),按 `unknown` 窄读;`requiresRealApproval` 走**独立入参**
|
|
134
|
+
* (车2 已归一化的 boolean),不从 `req` 里读。core 侧它是可选、只在为真时带,缺席 = 这不是一次安全类
|
|
135
|
+
* ask(core 的铸造点语义,不是我们的折算);卡面这一格恒在,`false` 就是那个否定形的如实投影。
|
|
136
|
+
* (原注写的「core 今天唯一在场的粗粒度标记」自 core 5.14.0 的 `riskAxes` 起过期,见 `ApprovalCardSchema`
|
|
137
|
+
* 头注。)
|
|
126
138
|
*/
|
|
127
139
|
export declare function buildApprovalCard(source: ApprovalCardSource, req: unknown, requiresRealApproval: boolean): ApprovalCard;
|
|
128
140
|
/** 落库信封的纯构造(写侧;读侧 = `ApprovalCardEnvelopeSchema.safeParse`,**同一个 schema**)。 */
|
package/dist/approval-card.js
CHANGED
|
@@ -35,7 +35,11 @@ const MAX_MESSAGE = 8192;
|
|
|
35
35
|
* `risk` 的三态形(设计稿 §14.1,core [2794] 回帖后定):`AskRequest.riskAxes?.{irreversible,egress}`
|
|
36
36
|
* 是 **additive optional**,**缺席 = 引擎未判,不是「安全」**。所以两轴在这里是 `optional()`:
|
|
37
37
|
* `true` / `false` / **缺席(未标注)** 三态各自可分,投影层**禁把缺席折算成 false** —— 那等于替引擎
|
|
38
|
-
* 打包票。`requiresRealApproval`
|
|
38
|
+
* 打包票。`requiresRealApproval` 是**粗粒度**安全类标记,两侧的在场契约**不同、别混**:core 侧是
|
|
39
|
+
* `AskRequest.requiresRealApproval?: boolean`(**可选、只在为真时带**,缺席 = 这不是一次安全类 ask,是
|
|
40
|
+
* 正常的否定形而不是坏形);本卡面这一格是**必填 boolean**,由车2 的入参归一化(`=== true`)而来。
|
|
41
|
+
* 它与两轴是两件事而不是新旧替代——原注写的「core 今天唯一在场的标记」是 `riskAxes` 上树之前的现势话,
|
|
42
|
+
* 自 core **5.14.0**(其 CHANGELOG 的 `AskRequest.riskAxes` additive 条)起两者并存。
|
|
39
43
|
*/
|
|
40
44
|
export const ApprovalCardSchema = z
|
|
41
45
|
.object({
|
|
@@ -55,6 +59,25 @@ export const ApprovalCardSchema = z
|
|
|
55
59
|
requiresRealApproval: z.boolean(),
|
|
56
60
|
})
|
|
57
61
|
.strict(),
|
|
62
|
+
/**
|
|
63
|
+
* [2942]/[2943] **ADDITIVE**:`true` ⇔ 这只 ask 的门来自运维治理层(语义、判定缝与「缺席 ≠ false」
|
|
64
|
+
* 的硬条款逐字见 `tool-approval.ts` 的 `ToolApprovalFrame.governanceForced` 与
|
|
65
|
+
* `governance-ask-marks.ts` 顶注)。放在**卡的顶层**而不是 `risk` 里:`risk` 讲的是引擎对这次操作的
|
|
66
|
+
* 风险判定(不可逆/出网/安全类),本键讲的是**门是谁下的**,两件事。
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ **回滚窗的行为(codex 交叉复审 round1 [high],验真后按「真实但内生」收下)**。形版本闩
|
|
69
|
+
* (`schemaVersion`)**不动**:additive optional 键在 `.strict()` 下对**旧行**无碍(缺席合法),代价
|
|
70
|
+
* 全在**回滚方向** —— 一个降级回旧二进制的副本读到带本键的新行,`safeParse` 会失败,两条读面各自:
|
|
71
|
+
* · 重放腿(`buildReplayFrame`)⇒ 该行**跳过** + 一次 warn(§5.3),不炸流;
|
|
72
|
+
* · `askBroadcast` 幂等命中该行 ⇒ 走「持久行不可用」臂 ⇒ `"unavailable"` = **park 路由**
|
|
73
|
+
* (tool-approval.ts 的 `persisted-row-unusable`)。park 是本协议的 fail-safe 出口:人仍可经
|
|
74
|
+
* durable gate 补批 —— 不是「批准凭空消失」,更不是把它折算成 deny。
|
|
75
|
+
* 这个代价是**任何** additive 键在一个 `.strict()` schema 下的内生代价,不是本键独有:bump 到
|
|
76
|
+
* `schemaVersion: 2` 只会更糟(旧读面 `z.literal(1)` 直接全量拒收**所有**新行,连缺席本键的行都收不了)。
|
|
77
|
+
* ⇒ 处置 = 不改形版本;**发车条款**写进 CHANGELOG:整队滚前(不留长期混版窗),回滚窗内新卡降级为
|
|
78
|
+
* park(fail-safe),回滚窗结束即自愈。
|
|
79
|
+
*/
|
|
80
|
+
governanceForced: z.literal(true).optional(),
|
|
58
81
|
/** 委派出处(子代 ask 才在场;判别键 = `fromSubagent`,core RB-39②)。 */
|
|
59
82
|
fromSubagent: z.literal(true).optional(),
|
|
60
83
|
sourceTaskId: z.string().max(MAX_IDENT).optional(),
|
|
@@ -86,10 +109,16 @@ export const APPROVAL_CARD_SCHEMA_VERSION = 1;
|
|
|
86
109
|
/**
|
|
87
110
|
* core 的风险轴(`AskRequest.riskAxes`,[2829]/§14.1)的**边界窄读**。
|
|
88
111
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
112
|
+
* ✅ **已点亮**:`riskAxes` 现在既在类型面也在真码面(树上 core 5.16.0,`core/tool-policy.d.ts` 的
|
|
113
|
+
* `readonly riskAxes?: {…}`;package.json 的 floor 已抬到 `^5.16.0`)。窄读当初写下时的现势前提是
|
|
114
|
+
* 「树上 core d.ts(5.13.0)还没有这个键、字段形只由 core 认领」——该键自 core **5.14.0** 起就在
|
|
115
|
+
* (其 CHANGELOG 的 `AskRequest.riskAxes` additive 条),那句现势话早已过期,**别再据它判断「core 还没
|
|
116
|
+
* 供值 ⇒ 卡面 risk 恒缺席」**。判在不在场以**装树的 d.ts 为准**,不要读注里的版本号
|
|
117
|
+
* (同族销账见 `tool-approval.ts` 的 `readBoundInputHash` 头注,#164)。
|
|
118
|
+
*
|
|
119
|
+
* 🔴 保留 `safeParse` 而不改成直读 `req.riskAxes` 的理由不变(它从来不只是等字段):窄读让**编译与行为
|
|
120
|
+
* 解耦**,缺席=未标注;裸 `as` 转型会在两个方向上都出错——既绕过宪法 [2704] 的「边界必 schema」,也会在
|
|
121
|
+
* core 发出一个形状不同的键(或加轴)时静默把垃圾投上卡面。
|
|
93
122
|
*
|
|
94
123
|
* 未知键被 zod 默认 strip(此处**故意不 `.strict()`**:core additive 加轴时不该让整只 ask 的卡面塌掉);
|
|
95
124
|
* 形不合(如 `irreversible: "yes"`)⇒ 整个 safeParse 失败 ⇒ 按**缺席**处置 = 「未标注」,
|
|
@@ -115,8 +144,11 @@ function clip(s, max) {
|
|
|
115
144
|
* design/172 §3.1 的**中性投影**(设计稿 §6.2)—— 写侧的唯一铸造点。
|
|
116
145
|
*
|
|
117
146
|
* `risk` 三态(§14.1):两轴 `optional`,`true`/`false`/**缺席(未标注)** 各自可分。`req` 是 core 交来的
|
|
118
|
-
* `AskRequest`(可能带、也可能不带 `riskAxes`),按 `unknown` 窄读;`requiresRealApproval`
|
|
119
|
-
*
|
|
147
|
+
* `AskRequest`(可能带、也可能不带 `riskAxes`),按 `unknown` 窄读;`requiresRealApproval` 走**独立入参**
|
|
148
|
+
* (车2 已归一化的 boolean),不从 `req` 里读。core 侧它是可选、只在为真时带,缺席 = 这不是一次安全类
|
|
149
|
+
* ask(core 的铸造点语义,不是我们的折算);卡面这一格恒在,`false` 就是那个否定形的如实投影。
|
|
150
|
+
* (原注写的「core 今天唯一在场的粗粒度标记」自 core 5.14.0 的 `riskAxes` 起过期,见 `ApprovalCardSchema`
|
|
151
|
+
* 头注。)
|
|
120
152
|
*/
|
|
121
153
|
export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
122
154
|
const axes = RiskAxesEnvelopeSchema.safeParse(req);
|
|
@@ -134,6 +166,8 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
134
166
|
...(riskAxes?.egress !== undefined ? { egress: riskAxes.egress } : {}),
|
|
135
167
|
requiresRealApproval,
|
|
136
168
|
},
|
|
169
|
+
// [2942]/[2943]:只在为真时投影(`=== true` 严判:非布尔真值不得把一张普通卡染成治理卡)。
|
|
170
|
+
...(source.governanceForced === true ? { governanceForced: true } : {}),
|
|
137
171
|
...(source.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
138
172
|
...(sourceTaskId !== undefined ? { sourceTaskId } : {}),
|
|
139
173
|
...(sourceAgentName !== undefined ? { sourceAgentName } : {}),
|
|
@@ -5,12 +5,26 @@
|
|
|
5
5
|
* `PARKED | DENIED | VOID`,并在崩溃后补位那些没人打 expire 的孤儿 `STREAM_PENDING` 行。
|
|
6
6
|
*
|
|
7
7
|
* ── 判据表 v2(设计稿 §9 尾的五臂汇总,逐字落地;每臂注读口)────────────────────────────────────────
|
|
8
|
-
* ①
|
|
9
|
-
*
|
|
10
|
-
* ∧ `cp.boundInputHash === ask.boundInputHash
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* ① **身份三元组 ∧ hash 双等** ⇒ `bindBatch`(判别式返回;`ok:false` ⇒ 降级续判)
|
|
9
|
+
* 身份 = `sourceTaskId` 相等 ∧ `toolCallId` 相等 ∧ **因果下界**(不是等式)`cp.createdAtMs ≥
|
|
10
|
+
* ask.createdAtMs`;再 ∧ `cp.boundInputHash === ask.boundInputHash`。三维里只有前两维是等式,把时间
|
|
11
|
+
* 那一维读成等式会让合法 park 几乎命不中。逐字实现在 {@link classifyGateMatch};判据的**唯一
|
|
12
|
+
* 属主**是那个函数的头注,这里只列纲要,细则(祖先层 fold 否决、多候选取舍)不在此复述。
|
|
13
|
+
* 🔴 两处易错,写在这里免得下一个人照旧口径改码:
|
|
14
|
+
* · 承重的第一维是 **`sourceTaskId`**(#168 件1,黑板 [2897]③①)——`sessionId` **不进身份等式**
|
|
15
|
+
* (委派子代的 ask 落行记的是投递上下文的根会话,park 却发生在子代自己的 sessionId 上,只按
|
|
16
|
+
* session 等值会张冠李戴),它在这一层只是读口 `findCheckpointCandidatesForAsk` 的入参。
|
|
17
|
+
* ⚠️ 但别据此把它当无用键:{@link isAncestorFoldMint} 的祖先层否决判的正是
|
|
18
|
+
* `sourceTaskId !== sessionId` —— 那是内存里的承重用法,只是不属于身份等式;
|
|
19
|
+
* · hash **任一侧缺席一律不 bind**(硬相等不放宽,理由同下)。缺席的**归因**分两级,别写成一句:
|
|
20
|
+
* 身份先判 —— 同身份候选一条都没有且候选集非空 ⇒ `identity_miss`(有对家但不是这一只,或读不出);
|
|
21
|
+
* 只有在身份这一层没被判掉时,hash 缺席才落 `single_mint` 三形
|
|
22
|
+
* (`ask_only` / `checkpoint_only` / `neither`)。`single_mint` 是可观测分类、不是放宽的命中;
|
|
23
|
+
* 把「只有一侧铸过」这格结构事实混进 `identity_miss` 的噪声底,运维就读不出两者的区别。
|
|
24
|
+
* 两道等式都不许放宽的原因不变(§9 C2:同 session 内 `toolCallId` 会被网关重用,身份不严会把旧 ask
|
|
25
|
+
* PARK 到别人的 resume 坐标上,而 `PARKED` 是不可回滚的终态)。读口 = `findCheckpointCandidatesForAsk`
|
|
26
|
+
* (§9 C4 窄谓词精确查,无分页假阴性);`unparseable` 候选**视同不匹配**(单行读不出不许打断整段
|
|
27
|
+
* 扫描,§8 C-6)。
|
|
14
28
|
* ② run 终局分臂(读口 `runStore.getRun`):`status ∈ {completed, failed, blocked}`(§8 A-1 词表修正 ——
|
|
15
29
|
* `cancelled` 不是 run 状态,取消 = `failed` + `errorCode`)——
|
|
16
30
|
* - `failed ∧ errorCode === "cancelled"`,或批行已 `ABORTED` ⇒ `VOID`(取消不是路由失败,§9 C3);
|
|
@@ -82,18 +96,82 @@ export interface ReconcileInput {
|
|
|
82
96
|
allowBind?: boolean;
|
|
83
97
|
}
|
|
84
98
|
/**
|
|
85
|
-
* 判据 1
|
|
99
|
+
* 判据 1 的一次**分类**结果(#168 件1;纯数据)。
|
|
86
100
|
*
|
|
87
|
-
*
|
|
101
|
+
* 为什么不只回「命中 / 不命中」:黑板 [2897]/[2898] 两帖把「两侧摘要不等」拆成了**四种成因**,处置各不
|
|
102
|
+
* 相同 —— 只有一种是真的「两个值不一样」,其余三种根本只有一侧(或零侧)铸过摘要。压成同一个
|
|
103
|
+
* `undefined` 会让运维看着一条「不匹配」去查一件从没发生过的事。
|
|
104
|
+
*/
|
|
105
|
+
export type GateMatchOutcome =
|
|
106
|
+
/** 三元组身份 ∧ 摘要双等 —— 判据 1 命中。 */
|
|
107
|
+
{
|
|
108
|
+
kind: "match";
|
|
109
|
+
candidate: CheckpointAskCandidate;
|
|
110
|
+
}
|
|
111
|
+
/** 祖先冻结 approver 层 fold 中途的那一次铸造:其后还可能有 rewrite,两侧**本不该等**([2912]③)。 */
|
|
112
|
+
| {
|
|
113
|
+
kind: "ancestor_fold_mint";
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 单铸路径 —— **不是** mismatch([2897]③②):
|
|
117
|
+
* - `ask_only`:纯 sync 腿(同轮结算,永无 checkpoint 行)/ 再审批链(2 次 ask 铸造、0 次 checkpoint);
|
|
118
|
+
* - `checkpoint_only`:durable-first 干净 args(ask 侧 0 次铸造,checkpoint 铸出);
|
|
119
|
+
* - `neither`:字符串模式 `onAsk` + durableApproval(两侧都没铸)。
|
|
120
|
+
*/
|
|
121
|
+
| {
|
|
122
|
+
kind: "single_mint";
|
|
123
|
+
side: "ask_only" | "checkpoint_only" | "neither";
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 三元组身份对得上、两侧摘要都在场却**不等** —— 唯一的真 mismatch。成因是**部署自伤**而非攻击
|
|
127
|
+
* ([2897]②:hook/policy 把自己仍持引用的活对象在两铸点之间改了,或每读返回新值的有状态 getter)。
|
|
128
|
+
* 处置 = 留痕不拒:计数 + 一条 warn,行照 ②③④⑤ 走(判据 1 不命中的既有 fail-safe 路径不变)。
|
|
129
|
+
*/
|
|
130
|
+
| {
|
|
131
|
+
kind: "hash_mismatch";
|
|
132
|
+
candidates: number;
|
|
133
|
+
}
|
|
134
|
+
/** 三元组身份就对不上(不是摘要的事):别的 sourceTaskId / 别的 callId / 早于本 ask 的 park。 */
|
|
135
|
+
| {
|
|
136
|
+
kind: "identity_miss";
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* 这只 ask 是不是**祖先冻结 approver 层**在委派 fold 中途铸的那一份(#168 件1④,黑板 [2912]③)。
|
|
140
|
+
*
|
|
141
|
+
* 判别位是**产品自己发的**,不是外部约定:core 的 `withDelegationProvenance` 只包**子代自己那条缝**,
|
|
142
|
+
* 祖先层那次走未包装的原函数 ⇒ `AskRequest.delegation` 在不在,就是「这一份是哪一层发的」。落到行上,
|
|
143
|
+
* `delegation.parentToolCallId` 是必填字段,铸行时逐字透传进 `parent_tool_call_id` 列 ⇒ **列非 NULL
|
|
144
|
+
* ⇔ delegation 在场**(不需要新列,也不需要回 `req` 里再读一次)。
|
|
145
|
+
*
|
|
146
|
+
* 第二维 `sourceTaskId !== sessionId` 把「根腿」摘出去:根腿的 `sourceTaskId` 恒等于会话锚(core 给
|
|
147
|
+
* `AskRequest.sourceTaskId` 填的就是该腿 sessionId),它根本不在任何 fold 里,`parent_tool_call_id`
|
|
148
|
+
* 为 NULL 是它的常态而不是信号。
|
|
149
|
+
*
|
|
150
|
+
* 命中 ⇒ 退出硬相等(判据 1 结构上不命中,行落 ②③④⑤)。实测(test AI 围栏)checkpoint 存的摘要**逐字
|
|
151
|
+
* 等于子代自己那条缝**、只不等于祖先层那一次 —— 所以收窄到这一层,子代自己的缝照常参与。
|
|
152
|
+
*/
|
|
153
|
+
export declare function isAncestorFoldMint(ask: AskRow): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* 判据 1 的**硬谓词**(纯函数,§9 C2 + §8 D-2;#168 件1 起返回分类而不是布尔)。
|
|
156
|
+
*
|
|
157
|
+
* 逐条:
|
|
158
|
+
* - 祖先层 fold 中途铸点 ⇒ 直接退出(见 {@link isAncestorFoldMint});
|
|
88
159
|
* - `unparseable` 候选直接出局(读不出 ⇒ 不确定 ⇒ 不命中);
|
|
89
|
-
* - `
|
|
90
|
-
*
|
|
91
|
-
*
|
|
160
|
+
* - **身份三元组**:`sourceTaskId` **相等** ∧ `toolCallId` **相等** ∧ 时间维的**因果下界**
|
|
161
|
+
* `candidate.createdAtMs >= ask.createdAtMs`(⚠️ 第三维**不是等式** —— 把它读成等式会让合法的 park
|
|
162
|
+
* 几乎命不中,行随后落 ②③⑤ 被误判)—— 摘要**不是**身份([2897]③①:
|
|
163
|
+
* 同 args 的两次调用摘要天然相同,只靠摘要硬相等会把第二次投递并进第一次的票);任一维在候选侧
|
|
164
|
+
* 缺席(读不出的 blob 没有 `sourceTaskId`)即身份不成立;
|
|
165
|
+
* - **hash 双等**:两侧都必须在场且相等,任一侧缺席一律不 bind;禁「能取到时才比」的可选谓词。
|
|
166
|
+
* 归因分两级(顺序即代码顺序):身份这一层先判 —— 同身份候选为空**且**候选集非空 ⇒ `identity_miss`;
|
|
167
|
+
* 走到 hash 这一层才把缺席记成**单铸** `single_mint`(见 {@link GateMatchOutcome})。
|
|
92
168
|
*
|
|
93
169
|
* 多候选时的取舍:优先 `status === "pending"`(活着的那张 gate),否则取最早的一条(读口按
|
|
94
170
|
* `created_at ASC` 返回)。两者都满足全部硬谓词,选谁都不会错配;取 pending 只是让 `PARKED` 行落到
|
|
95
171
|
* 一个还能被 resume 的坐标上,对壳更有用。
|
|
96
172
|
*/
|
|
173
|
+
export declare function classifyGateMatch(ask: AskRow, candidates: readonly CheckpointAskCandidate[]): GateMatchOutcome;
|
|
174
|
+
/** {@link classifyGateMatch} 的布尔面(判据 1 命中即返回那条候选)。分类信息由调用方按需另取。 */
|
|
97
175
|
export declare function selectGateCandidate(ask: AskRow, candidates: readonly CheckpointAskCandidate[]): CheckpointAskCandidate | undefined;
|
|
98
176
|
/** 判据表 v2 的判定(纯函数;顺序 = 设计稿 §9 尾的五臂汇总,注见文件头)。 */
|
|
99
177
|
export declare function decideReconcileAction(input: ReconcileInput): ReconcileAction;
|
|
@@ -105,13 +183,39 @@ export interface ReconcileStats {
|
|
|
105
183
|
* 🔴 判据 1 **结构上无法命中**的行数(`bound_input_hash` 列为 NULL ⇒ 双等硬谓词永远不成立)。
|
|
106
184
|
*
|
|
107
185
|
* 存在理由(codex 交叉复审 C1,2026-08-06 验真):设计稿 §9 C2 的前提是「`AskRequest.boundInputHash`
|
|
108
|
-
* core
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
186
|
+
* core 侧已在场」——对**当时**树上的 core 5.13.0 不成立(`AskRequest` 没有这个字段,铸行侧无值可存),
|
|
187
|
+
* 于是判据 1 恒不命中、PARKING 行只会落 ②③④⑤。这个计数把那片盲区变成**可观测量**而不是静默行为
|
|
188
|
+
* (下面每 tick 一条 warn),并被写成协议开关翻真的前置判据之一。
|
|
189
|
+
*
|
|
190
|
+
* ✅ **该前置判据已销账**(#164 翻真验证车,2026-08-07 亲验 core 5.15.0):字段在
|
|
191
|
+
* `core/tool-policy.d.ts:83`,真码 `core/tool-policy.js:610` 每一只 ask 都填 ——
|
|
192
|
+
* 判据 1 现在会真命中(钉:test/stream-approval-on-e2e.test.ts 件4 末条,同构两行只差 hash 一位,
|
|
193
|
+
* 有 hash 的 bind 成 PARKED、无 hash 的进本计数)。
|
|
194
|
+
*
|
|
195
|
+
* 🔴 **本字段与它的 warn 都不删**:语义从「结构性盲区」变成「边缘异常」—— 翻真之后一条非零读数说明
|
|
196
|
+
* 有 ask 的铸行侧没拿到 hash(旧版 core 的存量行 / 回滚形 / 未来某条不经 `resolveAsk` 的新路径),
|
|
197
|
+
* 那正是运维需要当场看见的东西。恒零才是健康态,不是这条计数没用了。
|
|
113
198
|
*/
|
|
114
199
|
unmatchableNoHash: number;
|
|
200
|
+
/**
|
|
201
|
+
* 🔴 判据 1 的**真** mismatch 行数(#168 件1③,黑板 [2897]②):三元组身份对得上、两侧摘要都在场却不等。
|
|
202
|
+
*
|
|
203
|
+
* 成因裁定 = **部署自伤,不是攻击**:core 已证在真正产生两次铸造的弧上摘要恒等,两条分歧路径都要求
|
|
204
|
+
* 部署自己交出活对象(hook/policy 返回己持引用后在两铸点之间突变;或每读返回新值的有状态 getter)。
|
|
205
|
+
* 所以处置是**留痕不拒**:计一笔 + 一条 warn,行照 ②③④⑤ 的既有 fail-safe 走 —— 判据 1 不命中本来
|
|
206
|
+
* 就不会产生终态 denial(约束②),这里不新增任何拒绝语义。
|
|
207
|
+
*/
|
|
208
|
+
hashMismatch: number;
|
|
209
|
+
/**
|
|
210
|
+
* 单铸路径行数(#168 件1②)——**不计入** {@link hashMismatch}。三形分列:
|
|
211
|
+
* `askOnly` 纯 sync / 再审批链;`checkpointOnly` durable-first 干净 args;`neither` 两侧都没铸。
|
|
212
|
+
* 它们都是**结构上只有一侧(或零侧)有摘要**,把它们读成「比过了、不匹配」是把没发生的事记成异常。
|
|
213
|
+
*/
|
|
214
|
+
singleMintAskOnly: number;
|
|
215
|
+
singleMintCheckpointOnly: number;
|
|
216
|
+
singleMintNeither: number;
|
|
217
|
+
/** 祖先冻结 approver 层 fold 中途铸点(#168 件1④):退出硬相等,不是 mismatch 也不是单铸。 */
|
|
218
|
+
ancestorFoldMint: number;
|
|
115
219
|
parked: number;
|
|
116
220
|
denied: number;
|
|
117
221
|
voided: number;
|