@sema-agent/server 7.117.0 → 7.119.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/MIGRATION.md +182 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +58 -28
- package/deploy/sema-up/README.md +2 -0
- package/deploy/sema-up/chart/templates/minio.yaml +2 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +1 -0
- package/deploy/sema-up/chart/templates/server.yaml +1 -0
- package/deploy/sema-up/chart/values.yaml +7 -1
- package/deploy/sema-up/cluster-up.sh +20 -8
- package/deploy/sema-up/kube-up.sh +29 -16
- package/deploy/sema-up/lib/buckets.sh +21 -0
- package/deploy/sema-up/lib/health.sh +33 -0
- package/deploy/sema-up/sema-up.sh +14 -9
- package/deploy/sema-up/smoke.sh +16 -6
- package/deploy/sema-up/stack/compose.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -1
- package/dist/approval-ask-audit-store.js +2 -7
- package/dist/approval-card.d.ts +40 -10
- package/dist/approval-card.js +11 -1
- package/dist/approval.d.ts +55 -17
- package/dist/approval.js +74 -33
- package/dist/audit.d.ts +12 -1
- package/dist/audit.js +62 -2
- package/dist/auto-mode-face.d.ts +2 -3
- package/dist/bake-runner/main.js +1 -6
- package/dist/bake-runner/protocol.d.ts +18 -0
- package/dist/bake-runner/protocol.js +22 -2
- package/dist/bake-runner/runner.d.ts +12 -8
- package/dist/bake-runner/runner.js +5 -9
- package/dist/boot/config-center.js +1 -1
- package/dist/boot/coordinators.js +4 -1
- package/dist/boot/device-lane.d.ts +3 -3
- package/dist/boot/device-lane.js +1 -2
- package/dist/boot/leader.d.ts +4 -4
- package/dist/boot/leader.js +1 -2
- package/dist/boot/parked-revive-gate.d.ts +4 -6
- package/dist/boot/parked-revive-gate.js +2 -6
- package/dist/boot/resolve-spec.js +9 -11
- package/dist/boot/runner-deps.js +2 -2
- package/dist/boot/stage-07-capability-layer.js +7 -3
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.js +12 -7
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +24 -6
- package/dist/capabilities/code-review-council.js +8 -11
- package/dist/capabilities/hands-lane.d.ts +25 -0
- package/dist/capabilities/hands-lane.js +1 -1
- package/dist/capabilities/mcp-probe.d.ts +2 -2
- package/dist/capabilities/scenarios.d.ts +34 -7
- package/dist/capabilities/scenarios.js +33 -6
- package/dist/config-catalog.js +3 -3
- package/dist/config.d.ts +14 -18
- package/dist/config.js +15 -26
- package/dist/deployment-governance.d.ts +5 -7
- package/dist/deployment-governance.js +4 -5
- package/dist/host-decision.d.ts +114 -31
- package/dist/host-decision.js +39 -2
- package/dist/http/admission.js +12 -6
- package/dist/http/approval-sweeps.js +1 -1
- package/dist/http/mcp-dial-deadline.d.ts +35 -0
- package/dist/http/mcp-dial-deadline.js +21 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +11 -5
- package/dist/http/memory-compliance-failure-reply.js +17 -8
- package/dist/http/memory-store-refusal-reply.d.ts +14 -11
- package/dist/http/memory-store-refusal-reply.js +16 -5
- package/dist/http/resume-legs.js +8 -3
- package/dist/http/routes/approvals-assistant.d.ts +25 -3
- package/dist/http/routes/approvals-assistant.js +27 -26
- package/dist/http/routes/capabilities.js +6 -3
- package/dist/http/routes/mcp-reconnect.d.ts +17 -0
- package/dist/http/routes/mcp-reconnect.js +6 -1
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/runs.js +47 -33
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +23 -22
- package/dist/http/routes/workflows.js +2 -0
- package/dist/http/wire-types.d.ts +17 -13
- package/dist/images/manifest.d.ts +7 -1
- package/dist/images/manifest.js +18 -16
- package/dist/memory-store-refusal.d.ts +38 -18
- package/dist/memory-store-refusal.js +25 -7
- package/dist/model-unreachable-notice.d.ts +1 -1
- package/dist/namespaced-rule-name.d.ts +46 -41
- package/dist/namespaced-rule-name.js +36 -22
- package/dist/observability/corrupt-read-seat.d.ts +4 -0
- package/dist/observability/corrupt-read-seat.js +1 -0
- package/dist/observability/fail-open.d.ts +19 -3
- package/dist/observability/fail-open.js +19 -3
- package/dist/operator-ask.d.ts +6 -2
- package/dist/orchestration/workflow-agent-session-index.d.ts +82 -59
- package/dist/orchestration/workflow-agent-session-index.js +80 -14
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -0
- package/dist/orchestration/workflow-completion-inbox.js +39 -17
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.js +7 -2
- package/dist/outbound-dispatcher.d.ts +29 -20
- package/dist/outbound-dispatcher.js +105 -16
- package/dist/parked-decide.d.ts +43 -48
- package/dist/parked-decide.js +54 -16
- package/dist/permission-rule-vocab.d.ts +4 -3
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +17 -20
- package/dist/plugins/approval-ask-store-sql.js +0 -3
- package/dist/plugins/approval-exemption-store.d.ts +4 -0
- package/dist/plugins/approval-exemption-store.js +1 -0
- package/dist/plugins/blob-backend.d.ts +13 -0
- package/dist/plugins/blob-backend.js +26 -0
- package/dist/plugins/local-session-store.d.ts +78 -47
- package/dist/plugins/local-session-store.js +152 -122
- package/dist/plugins/one-time-migrations.d.ts +11 -0
- package/dist/plugins/one-time-migrations.js +128 -4
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/snapshot-bucket-presence.d.ts +36 -0
- package/dist/plugins/snapshot-bucket-presence.js +40 -0
- package/dist/plugins/store-backend.d.ts +17 -6
- package/dist/plugins/store-backend.js +46 -16
- package/dist/plugins/store-open-wait.d.ts +33 -0
- package/dist/plugins/store-open-wait.js +73 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/web-search.d.ts +20 -1
- package/dist/plugins/web-search.js +39 -10
- package/dist/plugins/workflow-run-store-sql.d.ts +7 -6
- package/dist/plugins/workflow-run-store-sql.js +5 -7
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +35 -46
- package/dist/rule-entry-list.js +18 -26
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.js +5 -3
- package/dist/runs.d.ts +1 -1
- package/dist/security.d.ts +5 -3
- package/dist/security.js +4 -0
- package/dist/session-policy-wire.d.ts +13 -12
- package/dist/session-policy-wire.js +11 -11
- package/dist/spec-fields.d.ts +13 -11
- package/dist/spec-fields.js +4 -7
- package/dist/standing-answer-grant.d.ts +14 -2
- package/dist/standing-answer-grant.js +8 -1
- package/dist/store-posture.d.ts +14 -6
- package/dist/store-posture.js +7 -4
- package/dist/task-settings.d.ts +27 -55
- package/dist/task-settings.js +13 -21
- package/dist/tool-approval.d.ts +133 -138
- package/dist/tool-approval.js +175 -66
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/redact.d.ts +6 -0
- package/dist/trace/redact.js +9 -9
- package/dist/trace/self-minted-wire.d.ts +16 -2
- package/dist/trace/self-minted-wire.js +7 -0
- package/dist/user-input-ledger.d.ts +30 -14
- package/dist/user-input-ledger.js +25 -14
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +74 -3
- package/scripts/drain-check.mysql.sql +43 -17
- package/scripts/drain-check.pg.sql +46 -20
|
@@ -24,7 +24,11 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
24
24
|
};
|
|
25
25
|
readonly "server.workflow-park-index.sync-failed": {
|
|
26
26
|
readonly cls: "P-DEBT";
|
|
27
|
-
readonly note: "S-185 车CM(codex r1-F2 [high],验真后登记):workflow 出身 park 的 join 索引(`workflow_agent_session` / local 账本)在**一次成功的 run 写之后**同步失败 ⇒ 该次同步被吞,重试要等这条 run 的**下一次**写。而 park 恰好常常是这条 run 的**最后**一次写(park = run 终局 `failed`)⇒ 那条 park 可能永久 join
|
|
27
|
+
readonly note: "S-185 车CM(codex r1-F2 [high],验真后登记):workflow 出身 park 的 join 索引(`workflow_agent_session` / local 账本)在**一次成功的 run 写之后**同步失败 ⇒ 该次同步被吞,重试要等这条 run 的**下一次**写。而 park 恰好常常是这条 run 的**最后**一次写(park = run 终局 `failed`)⇒ 那条 park 可能永久 join 不到。**多数情形**是诚实 miss:这张卡没有别的指针 ⇒ `/decide` 结构性退回本批之前的行为(409 `conflict.resume_context_unavailable`),不误配、不伪造、不放行,丢的是「人能不能把这条批准送出去」这个能力。**7.118.0 起不止于此**:路由把指针候选当持有者的全集读,并有分叉门(两条同代次持有者 ⇒ 409 `decide.workflow_park_forked`);同一张卡上一枚**竞争**指针没写上(本臂的写失败,或写记录与写指针之间进程崩溃 —— 后者不经本臂、不计数)⇒ 分叉被判成唯一持有者 ⇒ 决定投给幸存的那一支(它确实持有这张卡,但另一支同样站在上面,引擎把这种形交给消费方拒)。归 P 类的理由:缺指针决定**车道选择**(缺行 ⇒ 选中 legacy 腿),且能让分叉门失效;归 DEBT 的理由是真正的解要一条**按子代 checkpoint 键的耐久决议回执 + 重启期回填**(与 codex r1-F3 同一件欠账,另车),分叉门失效那一形另有设计票。计数非零 = 有 park 可能决不掉、或这张卡的分叉门可能失效,运维要按 detail 里的 runId 去查那条 run 的续跑链。";
|
|
28
|
+
};
|
|
29
|
+
readonly "server.workflow-park-index.parks-unreadable": {
|
|
30
|
+
readonly cls: "F";
|
|
31
|
+
readonly note: "S-1173(`workflow-notify-journal.ts` 的 `syncAgentSessionIndex`,与上一条 `sync-failed` 同一个写点、另一臂):workflow run 记录的 `parks[]` **读不出**(在场却不是列表 / 某条目身份位或持有者两位形坏;读者 = `workflow-agent-session-index.ts` 的 `readRunParks`,整档成败制)⇒ 这一份指针投影派生不出来 ⇒ 写者**不动**这条 run 已有的指针(上一份已知良好的投影留着)。修前这一臂静默 `return`。**`parks` 缺席不进这一臂**:那是引擎契约里的合法形(更早引擎写的记录 / 准入没走完的续跑记录),读者答 `[]`(没有条目),写者照常收敛成空指针集,不计。**与 `sync-failed` 分家的理由(后果不同句,不并成一个 tag)**:`sync-failed` 是索引**写**挂了、丢的是一份已知良好的投影(park 常常是 run 的最后一次写 ⇒ 永久 join 不到,归 P-DEBT);本臂没有丢任何一份已知良好的投影 —— 读不出的是**记录本身**,引擎对条目形坏的记录整条拒续跑(`park_binding_broken`),读口对已有指针的 run 据记录响亮拒(`unproven/unreadable`),所以无需「重试这次写」,要的是查那条记录为什么读不出(投影把它写坏 / 手改)。放行的最坏后果:这条 run 的指针不被刷新(从没写过则没有指针 ⇒ 这张卡没有别的指针时 `/decide` 落 legacy 腿、409 响亮拒),多数情形方向是 fail-closed,不误配、不放行任何东西;例外与上一条同形 —— 首写就读不出的这一支没有指针,同一张卡上另一支的指针在时分叉门判不出、决定投给那一支(引擎从不写坏形,要这一支在首写之前就被写坏,比上一条的写失败更罕见);derived cache 没更新、真源读口自带响亮拒 ⇒ F 类。detail = `runId=`;计数非零 = 有记录的 `parks[]` 读不出,按 runId 去查记录。";
|
|
28
32
|
};
|
|
29
33
|
readonly "server.trace.gate-outcome-defective": {
|
|
30
34
|
readonly cls: "F";
|
|
@@ -296,7 +300,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
296
300
|
};
|
|
297
301
|
readonly "server.approval-card.closed-word-out-of-set": {
|
|
298
302
|
readonly cls: "F";
|
|
299
|
-
readonly note: "S-249(`approval-card.ts` 的边界窄读族;今天三个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` 判别是哪一座):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。";
|
|
303
|
+
readonly note: "S-249(`approval-card.ts` 的边界窄读族;今天三个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛)、S-1170 起的 `classifierNoRuling`(core 7.43.0 #1353 的成因词,活卡帧读 `AskRequest.classifierNoRuling`,闭集筛 = core `isAutoModeFallbackCause`)与 `differs`(core 7.44.0 #1374,载体是 `runs.ts` 的 409 `steering.duplicate_input_id` 体读的引擎拒错 `detail.differs`,闭集筛 = core `STEER_REPLAY_FACETS` 的非空子集;集外成员 / 空 / 非数组 ⇒ 不铸 `detail`,码仍是契约);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` / `classifierNoRuling=` / `differs=` 判别是哪一座):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。";
|
|
300
304
|
};
|
|
301
305
|
readonly "server.pg-pool.idle-client-error": {
|
|
302
306
|
readonly cls: "F";
|
|
@@ -334,6 +338,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
334
338
|
readonly cls: "F";
|
|
335
339
|
readonly note: "S-977(`orchestration/workflow-agent-session-index.ts`,local 车道 workflow 出身 park 的 join 索引账本的 boot 重放;登记名 `workflow-agent-session-index`):账本**中间**一行读不出形(`42` / 数组 / 缺 `runId`·`rows` / 任一行成员缺身份位 / 半截 JSON;撕尾不走本 tag)⇒ **整行**跳过,那条 run 保留上一份已知良好的快照(不做「丢坏成员、留好成员」—— 那会铸出比真相少几条 park 的行集)。最坏后果 = 该 run 的 park 在索引里缺行 ⇒ `/decide` 找不到它、退回 legacy 腿(409 `conflict.resume_context_unavailable`):诚实 miss,不是错配 —— 赎回键由记录上的 `parks[]` 逐字比对,不读本索引。索引不开门 ⇒ F。修前:形不对的整行响亮(`workflow_agent_session_ledger_line_rejected`,同事件名保留)但**无 tag**,带换行的半截 JSON 静默 `continue`。";
|
|
336
340
|
};
|
|
341
|
+
readonly "server.workflow-completion-inbox.corrupt-record-skipped": {
|
|
342
|
+
readonly cls: "F";
|
|
343
|
+
readonly note: "S-978(`orchestration/workflow-completion-inbox.ts`,local 车道 File 完成信箱的 boot 重放;登记名 `workflow-completion-inbox`):账本**中间**一行读不出形(`42` / `null` / 数组 / 缺 `op` 或 `op` 词表外 / `enqueue` 缺 `entry.sessionId`·`entry.runId` / `ack` 缺 `sessionId`·`runId` / `drain` 缺 `sessionId` / 半截 JSON;撕尾不走本 tag)⇒ 跳过它继续。最坏后果按行种:丢 `enqueue` ⇒ 那条完成推送不进信箱(`WorkflowStatus` 轮询地板照旧兜住);丢 `ack` ⇒ 一条已送达的完成在重启后再投一次(壳按 runId 去重,至少一次的既有语义);丢 `drain`(会话清除墓碑)⇒ 被删会话里尚未送达的条目复活 —— 出帧前仍过 owner 二道闸(`emitPendingWorkflowCompletions`:条目盖的 owner 与本次调用方不符 ⇒ ack 丢弃、不出帧),同属主重领同一 sessionId 只会多收一条按 runId 去重的推送。信箱不开门、不改任何 run 的执行或授权 ⇒ F。修前:解析失败 `continue`(静默),而解析成功的 `null` 行在读 `rec.op` 时抛 TypeError ⇒ `new FileWorkflowCompletionInbox` 抛(构造点无 catch ⇒ 这一面起不来),其余非对象 / 缺键形静默忽略。boot 压实随后从内存活集重写账本 ⇒ 坏行的字节压实后不留盘,披露是它唯一的记录。";
|
|
344
|
+
};
|
|
337
345
|
readonly "server.user-input-ledger.append-failed": {
|
|
338
346
|
readonly cls: "F";
|
|
339
347
|
readonly note: "S-928(`src/user-input-ledger.ts`):200 承诺**之后**的一次账本写失败 —— detail 的 `op=` 点名是哪一种:`settle`(腿在本进程收场)/ `withdraw`(引擎没收下这次 steer)/ `converge`(收敛器判孤儿)/ `announce`(通告已投出,记「告知过」)。放行的最坏后果逐个说清:settle / withdraw 漏写 ⇒ 这次受理在账上仍是 open —— 本进程下一次收敛扫描补结清(本进程写的、已不在活腿上 ⇒ leg_over);进程在那之前就崩了 ⇒ 下一次 boot 的收敛读 run 店判(S-969 L8 起两车道同一只判词:承载它的 run 正常翻面 / 已 park ⇒ leg_over 补结清、不告知;落款死亡判决或写者已死仍 running ⇒ crashed)—— 仍会多说一句的只剩两形:withdraw 漏写后那条腿崩了(一次没变成 200 的受理被告知一次);settle 漏写后本进程续跑了同一条 run、续跑腿崩了(run 行说的是同一 taskId 上**最新**那条腿)。方向都是多说,不漏;converge 漏写 ⇒ 留在 open,下一次扫描再判(延迟,不丢);announce 漏写 ⇒ 下一条 run 再说一遍(至多一次退化成至少一次)。都是**告知面**的偏差,不开任何门、不改任何 run 的执行 ⇒ F。计数非零 = 这台机器的账本写不动了(local:数据根盘满 / 只读;SQL:库不可达),steer 此刻多半也在回 503。";
|
|
@@ -348,7 +356,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
348
356
|
};
|
|
349
357
|
readonly "server.session-repo.corrupt-record-skipped": {
|
|
350
358
|
readonly cls: "F";
|
|
351
|
-
readonly note: "S-990(`plugins/store
|
|
359
|
+
readonly note: "S-990(`plugins/local-session-store.ts` `createLocalSessionRepo` 的 `FileSessionRepo` 座,local 车道的会话存档;`LocalSessionStore` 与会话视图口 `audit.ts` 共用,S-879):一份会话存档里有记录读不出(整行毁 / 半截 JSON / 形不对,core 记成 `kind:\"record\"` 逐条披露)或存档文件 / 目录整体读不出(`kind:\"file\"`)⇒ core 的成文处置是跳过 + 披露,会话照常打开、列表少一行。最坏后果 = 模型读到的历史少了一截 / 列表静默少一份会话 —— 都是**可用性**损失,不开任何门;发起这一轮的人另有 wire 面披露(打开自己存档时的 `engine_notice` 码 `session.records_skipped`,与本 tag 无关、不动)⇒ F。**计数单位 = 一条被跳过的记录 = 一次命中**(`replay` 级是同一批记录的汇总,不计,S-616 同教训);**每命中一行探针**(`SEMA_FAILOPEN_PROBE`)带 `path` / `sessionId` / `line` / `form` / `reason` 逐条明细 —— 运维要看「哪份存档的哪一行坏了」读探针,不读本条 warn:本条是每进程每 tag 一次的告警(`detail` 是首条明细,不是全部)。修前:每命中一行 warn,一份 113 行坏记录的存档做一次列表 + 一次查看打出 342 行,存档没修好每读再发。";
|
|
352
360
|
};
|
|
353
361
|
readonly "server.workflow-journal.corrupt-record-skipped": {
|
|
354
362
|
readonly cls: "F";
|
|
@@ -374,10 +382,18 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
374
382
|
readonly cls: "F";
|
|
375
383
|
readonly note: "7.116.0 修复轮(`approval-crash-convergence.ts` 的 `convergeCrashedAsksAtBoot`;local 车道启动收敛):前世留下的一条 `STREAM_PENDING` 问行,它的卡信封(`card_json`)读不出 ⇒ 读不出 wire `approvalId`、无从向审计账要成因词 ⇒ 成因**回落**成 `crashed_before_park`。行照样收 VOID(决断口照样 410 —— 可决性 fail-closed,不受影响);最坏后果 = 一张其实是被有序关停截断的卡,在 410 体的 `deniedReason` 上说成「进程崩了」(`crashConverged` 读面若有它的审计组,那里的成因仍按审计账判,两处可能不同词)。计数非零 = 问账里有读不出卡信封的行(位腐 / 手改 / 版本错位),去看探针 detail 点名的 askId。读数另在启动日志 `approval_ask_audit_crash_converged` 的 `causeUnreadable` 格单列。";
|
|
376
384
|
};
|
|
385
|
+
readonly "server.outbound.hop-member-unreadable": {
|
|
386
|
+
readonly cls: "F";
|
|
387
|
+
readonly note: "出网代理那一跳建连失败的 wire 投影(`outbound-dispatcher.ts` `projectHopFailure`):按 core 连接失败分类器的读法复制错误图时,某个成员的访问器抛了(或对象不可 toString)。读法与分类器同一把,所以抛了当缺席;最坏后果 = 副本上少一个成员,分类器把这一次失败读成 unclassified(终局句只剩 `via outbound proxy` 一句、不带码),不影响重试阶梯的方向。只在怪形错误对象上触发,计数即可。";
|
|
388
|
+
};
|
|
377
389
|
readonly "server.outbound.connect-observer-threw": {
|
|
378
390
|
readonly cls: "F";
|
|
379
391
|
readonly note: "S-925(`outbound-dispatcher.ts` 的 undici 诊断通道订阅者:`undici:request:create` / `:headers` / `:error`):一只订阅回调抛了。通道在发布方的调用栈里同步回调订阅者,抛出去在 Node 上是未捕获异常(整进程倒),所以每只回调整体 try、落本 tag。最坏后果 = 这一条请求的建连失败没被记下:少一行 `outbound_connect_failed`、诊断段 `outbound.lastConnectFailure` 不更新、那次模型调用不铸 `server.model_unreachable` 用户通告 —— 请求本身、它的错误、run 的终态一个字节不受影响(观察只读)。计数恒零是证据;非零 = undici 通道载荷的形变了,去看 detail 的 site。";
|
|
380
392
|
};
|
|
393
|
+
readonly "server.snapshot-bucket.presence-unproven": {
|
|
394
|
+
readonly cls: "F";
|
|
395
|
+
readonly note: "S-1195(7.119.0;`plugins/snapshot-bucket-presence.ts` 的 `ensureSnapshotBucket`,boot 期核一次快照桶 `SESSION_SNAPSHOT_BUCKET`;detail = `bucket=… endpoint=<buildDisplaySafeUrl 的显示形> <HTTP 状态 | request failed: 错类>`):presigned 列举对 `MINIO_ENDPOINT` **连不上 / 403 / 5xx / 没有 NoSuchBucket 的 404**,或探针到窗尾一次都没答过(detail 写 `probe still pending when the window ended`)⇒ **证明不了桶缺**,不拒启、照常起服(只有 404 + `NoSuchBucket` 才是「缺」,那一形窗内重试、到窗拒启,不走本 tag)。三问:①错误落到哪 —— 本 tag 的计数 / 探针行 / 记录器的一次性 warn,detail 带端点显示形与状态或错类;②谁受害 —— 桶其实不存在、或凭据不能列举的部署:之后每一次快照 offload 写与 SendUserFile 私有 presigned 请求会答 404 / 403,孤儿 sweep 每分钟一条失败,运维读到的是一串失败请求而不是「桶没建」(与 7.118.x 的既有形状同形,不是新损失);③缺席倒向哪边 —— 倒向**放行起服**:没有证据既不拒启、也不伪造「桶在场」或「桶缺」,不是门 / 权限 / 凭证。最坏后果 = 缺桶的部署晚一步才暴露;不放行的代价是对象存储一次抖动 / 一次 403 就让整只服务起不来。计数非零 = 这台部署启动时没能确认快照桶,按 detail 的端点与状态去核对网络 / 凭据 / 桶。";
|
|
396
|
+
};
|
|
381
397
|
};
|
|
382
398
|
/** 词表键推导的闭集类型——未登记的 tag 传不进 {@link recordFailOpen}(编译期拒)。 */
|
|
383
399
|
export type FailOpenTag = keyof typeof FAIL_OPEN_TAGS;
|
|
@@ -18,7 +18,11 @@ export const FAIL_OPEN_TAGS = {
|
|
|
18
18
|
},
|
|
19
19
|
"server.workflow-park-index.sync-failed": {
|
|
20
20
|
cls: "P-DEBT",
|
|
21
|
-
note: "S-185 车CM(codex r1-F2 [high],验真后登记):workflow 出身 park 的 join 索引(`workflow_agent_session` / local 账本)在**一次成功的 run 写之后**同步失败 ⇒ 该次同步被吞,重试要等这条 run 的**下一次**写。而 park 恰好常常是这条 run 的**最后**一次写(park = run 终局 `failed`)⇒ 那条 park 可能永久 join
|
|
21
|
+
note: "S-185 车CM(codex r1-F2 [high],验真后登记):workflow 出身 park 的 join 索引(`workflow_agent_session` / local 账本)在**一次成功的 run 写之后**同步失败 ⇒ 该次同步被吞,重试要等这条 run 的**下一次**写。而 park 恰好常常是这条 run 的**最后**一次写(park = run 终局 `failed`)⇒ 那条 park 可能永久 join 不到。**多数情形**是诚实 miss:这张卡没有别的指针 ⇒ `/decide` 结构性退回本批之前的行为(409 `conflict.resume_context_unavailable`),不误配、不伪造、不放行,丢的是「人能不能把这条批准送出去」这个能力。**7.118.0 起不止于此**:路由把指针候选当持有者的全集读,并有分叉门(两条同代次持有者 ⇒ 409 `decide.workflow_park_forked`);同一张卡上一枚**竞争**指针没写上(本臂的写失败,或写记录与写指针之间进程崩溃 —— 后者不经本臂、不计数)⇒ 分叉被判成唯一持有者 ⇒ 决定投给幸存的那一支(它确实持有这张卡,但另一支同样站在上面,引擎把这种形交给消费方拒)。归 P 类的理由:缺指针决定**车道选择**(缺行 ⇒ 选中 legacy 腿),且能让分叉门失效;归 DEBT 的理由是真正的解要一条**按子代 checkpoint 键的耐久决议回执 + 重启期回填**(与 codex r1-F3 同一件欠账,另车),分叉门失效那一形另有设计票。计数非零 = 有 park 可能决不掉、或这张卡的分叉门可能失效,运维要按 detail 里的 runId 去查那条 run 的续跑链。",
|
|
22
|
+
},
|
|
23
|
+
"server.workflow-park-index.parks-unreadable": {
|
|
24
|
+
cls: "F",
|
|
25
|
+
note: "S-1173(`workflow-notify-journal.ts` 的 `syncAgentSessionIndex`,与上一条 `sync-failed` 同一个写点、另一臂):workflow run 记录的 `parks[]` **读不出**(在场却不是列表 / 某条目身份位或持有者两位形坏;读者 = `workflow-agent-session-index.ts` 的 `readRunParks`,整档成败制)⇒ 这一份指针投影派生不出来 ⇒ 写者**不动**这条 run 已有的指针(上一份已知良好的投影留着)。修前这一臂静默 `return`。**`parks` 缺席不进这一臂**:那是引擎契约里的合法形(更早引擎写的记录 / 准入没走完的续跑记录),读者答 `[]`(没有条目),写者照常收敛成空指针集,不计。**与 `sync-failed` 分家的理由(后果不同句,不并成一个 tag)**:`sync-failed` 是索引**写**挂了、丢的是一份已知良好的投影(park 常常是 run 的最后一次写 ⇒ 永久 join 不到,归 P-DEBT);本臂没有丢任何一份已知良好的投影 —— 读不出的是**记录本身**,引擎对条目形坏的记录整条拒续跑(`park_binding_broken`),读口对已有指针的 run 据记录响亮拒(`unproven/unreadable`),所以无需「重试这次写」,要的是查那条记录为什么读不出(投影把它写坏 / 手改)。放行的最坏后果:这条 run 的指针不被刷新(从没写过则没有指针 ⇒ 这张卡没有别的指针时 `/decide` 落 legacy 腿、409 响亮拒),多数情形方向是 fail-closed,不误配、不放行任何东西;例外与上一条同形 —— 首写就读不出的这一支没有指针,同一张卡上另一支的指针在时分叉门判不出、决定投给那一支(引擎从不写坏形,要这一支在首写之前就被写坏,比上一条的写失败更罕见);derived cache 没更新、真源读口自带响亮拒 ⇒ F 类。detail = `runId=`;计数非零 = 有记录的 `parks[]` 读不出,按 runId 去查记录。",
|
|
22
26
|
},
|
|
23
27
|
"server.trace.gate-outcome-defective": {
|
|
24
28
|
cls: "F",
|
|
@@ -269,7 +273,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
269
273
|
"server.redact.ppk-eat-to-end": { cls: "F", note: "S-107 第三十二轮重扫:PuTTY .ppk 容器扫描器(redact.ts redactPpkContainers)在「无合格终止 / 收口后同容器范围再见私钥字段 / 收口后到下一版本头之间夹非空白」三形下从头**吃到输入末尾**(fail-closed 取证丢弃)——终态 errorMessage 座上=整条错误文案变成 «redacted:private-key»,与 redactions_applied_total{private-key} 的诚实替换同形不可分,故要有自己的计数/探针;影子扫不计;计数=多少次整段吃尾。" },
|
|
270
274
|
"server.approval-card.closed-word-out-of-set": {
|
|
271
275
|
cls: "F",
|
|
272
|
-
note: "S-249(`approval-card.ts` 的边界窄读族;今天三个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` 判别是哪一座):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。",
|
|
276
|
+
note: "S-249(`approval-card.ts` 的边界窄读族;今天三个成员:`readRuleOffersAbsence`、S-934 ② 起的 `readAskClassPair`(core 7.37.0 #1214 的类对 `askClass` / `classes`,活卡帧 / 呈卡帧的卡 / 运维队列行同一只,判法 = core 屏 `screenAskClassMembers`(S-955 ⑥ / core 7.38.0 #1249,本仓零自写对判),两键同在同缺,半对 / 集外 / 首员不符 / 重复员 = 屏拒 = 读不出;detail 前缀 `askClass=` / `classes=`),与 S-721 起的 `readClosedWordSeat` —— 一座一只 core 成员筛,座是参数:`mandate`(core 7.32.0 #1092,活卡帧 / 活挂起行 / 运维队列行同一只,闭集筛 = core `isPersistedRuleMandate`)与 S-755 起的 `origin`(运维队列行读 park 行孪生座,闭集筛 = core `isAskOrigin`,与 inbox 行 core 投影处同一只筛)、S-1170 起的 `classifierNoRuling`(core 7.43.0 #1353 的成因词,活卡帧读 `AskRequest.classifierNoRuling`,闭集筛 = core `isAutoModeFallbackCause`)与 `differs`(core 7.44.0 #1374,载体是 `runs.ts` 的 409 `steering.duplicate_input_id` 体读的引擎拒错 `detail.differs`,闭集筛 = core `STEER_REPLAY_FACETS` 的非空子集;集外成员 / 空 / 非数组 ⇒ 不铸 `detail`,码仍是契约);detail 前缀 `ruleOffersAbsence=` / `mandate=` / `origin=` / `classifierNoRuling=` / `differs=` 判别是哪一座):活卡帧 / `card_json` / (S-700 起)运维队列行(`parkRowFacts` 读寄存行上的 park 孪生座;队列是热读面 ⇒ 同一行每读一次计一次)的一个**闭集词**座上,键在场而词不在本仓认得的那张表里(世代差,或第三方 producer)⇒ 该座**按缺席**处置。放行的最坏后果=卡上少一行「为什么这次没有规则候选」的解释(壳退回不显示该行),它**不改变任何裁决**——门在引擎里早已判完,本座是展示/观测面;反方向(把一个消费端词表外的值原样上卡)更坏:下游按闭集写的分支会落进 default 臂或渲染出生词。本臂存在的全部理由是**判别**:修前「上游发了个生词」与「上游根本没发这个键」在遥测上同形,而前者意味着本仓的词表该跟着 core 加词了(型面另有两向编译围栏,但那只在**重新编译**时说话,跑在线上的那一版不会自己红)。detail 带座名与那个词(经 redactSecrets 截 40);数 / 布尔等原始值原样,对象 / 数组 / 函数 / null 只报形名(`<object>` / `<array>` / `<function>` / `<null>`,S-700 codex r1:格式化从不调用值自己的转换方法 —— 持久层里一格 `{\"toString\":null}` 曾让诊断本身抛出、打掉整只队列)。",
|
|
273
277
|
},
|
|
274
278
|
"server.pg-pool.idle-client-error": {
|
|
275
279
|
cls: "F",
|
|
@@ -307,6 +311,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
307
311
|
cls: "F",
|
|
308
312
|
note: "S-977(`orchestration/workflow-agent-session-index.ts`,local 车道 workflow 出身 park 的 join 索引账本的 boot 重放;登记名 `workflow-agent-session-index`):账本**中间**一行读不出形(`42` / 数组 / 缺 `runId`·`rows` / 任一行成员缺身份位 / 半截 JSON;撕尾不走本 tag)⇒ **整行**跳过,那条 run 保留上一份已知良好的快照(不做「丢坏成员、留好成员」—— 那会铸出比真相少几条 park 的行集)。最坏后果 = 该 run 的 park 在索引里缺行 ⇒ `/decide` 找不到它、退回 legacy 腿(409 `conflict.resume_context_unavailable`):诚实 miss,不是错配 —— 赎回键由记录上的 `parks[]` 逐字比对,不读本索引。索引不开门 ⇒ F。修前:形不对的整行响亮(`workflow_agent_session_ledger_line_rejected`,同事件名保留)但**无 tag**,带换行的半截 JSON 静默 `continue`。",
|
|
309
313
|
},
|
|
314
|
+
"server.workflow-completion-inbox.corrupt-record-skipped": {
|
|
315
|
+
cls: "F",
|
|
316
|
+
note: "S-978(`orchestration/workflow-completion-inbox.ts`,local 车道 File 完成信箱的 boot 重放;登记名 `workflow-completion-inbox`):账本**中间**一行读不出形(`42` / `null` / 数组 / 缺 `op` 或 `op` 词表外 / `enqueue` 缺 `entry.sessionId`·`entry.runId` / `ack` 缺 `sessionId`·`runId` / `drain` 缺 `sessionId` / 半截 JSON;撕尾不走本 tag)⇒ 跳过它继续。最坏后果按行种:丢 `enqueue` ⇒ 那条完成推送不进信箱(`WorkflowStatus` 轮询地板照旧兜住);丢 `ack` ⇒ 一条已送达的完成在重启后再投一次(壳按 runId 去重,至少一次的既有语义);丢 `drain`(会话清除墓碑)⇒ 被删会话里尚未送达的条目复活 —— 出帧前仍过 owner 二道闸(`emitPendingWorkflowCompletions`:条目盖的 owner 与本次调用方不符 ⇒ ack 丢弃、不出帧),同属主重领同一 sessionId 只会多收一条按 runId 去重的推送。信箱不开门、不改任何 run 的执行或授权 ⇒ F。修前:解析失败 `continue`(静默),而解析成功的 `null` 行在读 `rec.op` 时抛 TypeError ⇒ `new FileWorkflowCompletionInbox` 抛(构造点无 catch ⇒ 这一面起不来),其余非对象 / 缺键形静默忽略。boot 压实随后从内存活集重写账本 ⇒ 坏行的字节压实后不留盘,披露是它唯一的记录。",
|
|
317
|
+
},
|
|
310
318
|
"server.user-input-ledger.append-failed": {
|
|
311
319
|
cls: "F",
|
|
312
320
|
note: "S-928(`src/user-input-ledger.ts`):200 承诺**之后**的一次账本写失败 —— detail 的 `op=` 点名是哪一种:`settle`(腿在本进程收场)/ `withdraw`(引擎没收下这次 steer)/ `converge`(收敛器判孤儿)/ `announce`(通告已投出,记「告知过」)。放行的最坏后果逐个说清:settle / withdraw 漏写 ⇒ 这次受理在账上仍是 open —— 本进程下一次收敛扫描补结清(本进程写的、已不在活腿上 ⇒ leg_over);进程在那之前就崩了 ⇒ 下一次 boot 的收敛读 run 店判(S-969 L8 起两车道同一只判词:承载它的 run 正常翻面 / 已 park ⇒ leg_over 补结清、不告知;落款死亡判决或写者已死仍 running ⇒ crashed)—— 仍会多说一句的只剩两形:withdraw 漏写后那条腿崩了(一次没变成 200 的受理被告知一次);settle 漏写后本进程续跑了同一条 run、续跑腿崩了(run 行说的是同一 taskId 上**最新**那条腿)。方向都是多说,不漏;converge 漏写 ⇒ 留在 open,下一次扫描再判(延迟,不丢);announce 漏写 ⇒ 下一条 run 再说一遍(至多一次退化成至少一次)。都是**告知面**的偏差,不开任何门、不改任何 run 的执行 ⇒ F。计数非零 = 这台机器的账本写不动了(local:数据根盘满 / 只读;SQL:库不可达),steer 此刻多半也在回 503。",
|
|
@@ -321,7 +329,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
321
329
|
},
|
|
322
330
|
"server.session-repo.corrupt-record-skipped": {
|
|
323
331
|
cls: "F",
|
|
324
|
-
note: "S-990(`plugins/store
|
|
332
|
+
note: "S-990(`plugins/local-session-store.ts` `createLocalSessionRepo` 的 `FileSessionRepo` 座,local 车道的会话存档;`LocalSessionStore` 与会话视图口 `audit.ts` 共用,S-879):一份会话存档里有记录读不出(整行毁 / 半截 JSON / 形不对,core 记成 `kind:\"record\"` 逐条披露)或存档文件 / 目录整体读不出(`kind:\"file\"`)⇒ core 的成文处置是跳过 + 披露,会话照常打开、列表少一行。最坏后果 = 模型读到的历史少了一截 / 列表静默少一份会话 —— 都是**可用性**损失,不开任何门;发起这一轮的人另有 wire 面披露(打开自己存档时的 `engine_notice` 码 `session.records_skipped`,与本 tag 无关、不动)⇒ F。**计数单位 = 一条被跳过的记录 = 一次命中**(`replay` 级是同一批记录的汇总,不计,S-616 同教训);**每命中一行探针**(`SEMA_FAILOPEN_PROBE`)带 `path` / `sessionId` / `line` / `form` / `reason` 逐条明细 —— 运维要看「哪份存档的哪一行坏了」读探针,不读本条 warn:本条是每进程每 tag 一次的告警(`detail` 是首条明细,不是全部)。修前:每命中一行 warn,一份 113 行坏记录的存档做一次列表 + 一次查看打出 342 行,存档没修好每读再发。",
|
|
325
333
|
},
|
|
326
334
|
"server.workflow-journal.corrupt-record-skipped": {
|
|
327
335
|
cls: "F",
|
|
@@ -347,10 +355,18 @@ export const FAIL_OPEN_TAGS = {
|
|
|
347
355
|
cls: "F",
|
|
348
356
|
note: "7.116.0 修复轮(`approval-crash-convergence.ts` 的 `convergeCrashedAsksAtBoot`;local 车道启动收敛):前世留下的一条 `STREAM_PENDING` 问行,它的卡信封(`card_json`)读不出 ⇒ 读不出 wire `approvalId`、无从向审计账要成因词 ⇒ 成因**回落**成 `crashed_before_park`。行照样收 VOID(决断口照样 410 —— 可决性 fail-closed,不受影响);最坏后果 = 一张其实是被有序关停截断的卡,在 410 体的 `deniedReason` 上说成「进程崩了」(`crashConverged` 读面若有它的审计组,那里的成因仍按审计账判,两处可能不同词)。计数非零 = 问账里有读不出卡信封的行(位腐 / 手改 / 版本错位),去看探针 detail 点名的 askId。读数另在启动日志 `approval_ask_audit_crash_converged` 的 `causeUnreadable` 格单列。",
|
|
349
357
|
},
|
|
358
|
+
"server.outbound.hop-member-unreadable": {
|
|
359
|
+
cls: "F",
|
|
360
|
+
note: "出网代理那一跳建连失败的 wire 投影(`outbound-dispatcher.ts` `projectHopFailure`):按 core 连接失败分类器的读法复制错误图时,某个成员的访问器抛了(或对象不可 toString)。读法与分类器同一把,所以抛了当缺席;最坏后果 = 副本上少一个成员,分类器把这一次失败读成 unclassified(终局句只剩 `via outbound proxy` 一句、不带码),不影响重试阶梯的方向。只在怪形错误对象上触发,计数即可。",
|
|
361
|
+
},
|
|
350
362
|
"server.outbound.connect-observer-threw": {
|
|
351
363
|
cls: "F",
|
|
352
364
|
note: "S-925(`outbound-dispatcher.ts` 的 undici 诊断通道订阅者:`undici:request:create` / `:headers` / `:error`):一只订阅回调抛了。通道在发布方的调用栈里同步回调订阅者,抛出去在 Node 上是未捕获异常(整进程倒),所以每只回调整体 try、落本 tag。最坏后果 = 这一条请求的建连失败没被记下:少一行 `outbound_connect_failed`、诊断段 `outbound.lastConnectFailure` 不更新、那次模型调用不铸 `server.model_unreachable` 用户通告 —— 请求本身、它的错误、run 的终态一个字节不受影响(观察只读)。计数恒零是证据;非零 = undici 通道载荷的形变了,去看 detail 的 site。",
|
|
353
365
|
},
|
|
366
|
+
"server.snapshot-bucket.presence-unproven": {
|
|
367
|
+
cls: "F",
|
|
368
|
+
note: "S-1195(7.119.0;`plugins/snapshot-bucket-presence.ts` 的 `ensureSnapshotBucket`,boot 期核一次快照桶 `SESSION_SNAPSHOT_BUCKET`;detail = `bucket=… endpoint=<buildDisplaySafeUrl 的显示形> <HTTP 状态 | request failed: 错类>`):presigned 列举对 `MINIO_ENDPOINT` **连不上 / 403 / 5xx / 没有 NoSuchBucket 的 404**,或探针到窗尾一次都没答过(detail 写 `probe still pending when the window ended`)⇒ **证明不了桶缺**,不拒启、照常起服(只有 404 + `NoSuchBucket` 才是「缺」,那一形窗内重试、到窗拒启,不走本 tag)。三问:①错误落到哪 —— 本 tag 的计数 / 探针行 / 记录器的一次性 warn,detail 带端点显示形与状态或错类;②谁受害 —— 桶其实不存在、或凭据不能列举的部署:之后每一次快照 offload 写与 SendUserFile 私有 presigned 请求会答 404 / 403,孤儿 sweep 每分钟一条失败,运维读到的是一串失败请求而不是「桶没建」(与 7.118.x 的既有形状同形,不是新损失);③缺席倒向哪边 —— 倒向**放行起服**:没有证据既不拒启、也不伪造「桶在场」或「桶缺」,不是门 / 权限 / 凭证。最坏后果 = 缺桶的部署晚一步才暴露;不放行的代价是对象存储一次抖动 / 一次 403 就让整只服务起不来。计数非零 = 这台部署启动时没能确认快照桶,按 detail 的端点与状态去核对网络 / 凭据 / 桶。",
|
|
369
|
+
},
|
|
354
370
|
};
|
|
355
371
|
export function failOpenTagForDroppedFrame(frameType) {
|
|
356
372
|
switch (frameType) {
|
package/dist/operator-ask.d.ts
CHANGED
|
@@ -54,12 +54,16 @@ export declare function operatorAsk(rule: string, message: string): OperatorAskR
|
|
|
54
54
|
* 任何 scope)退不掉,只有**这一次**的应答能答它。
|
|
55
55
|
*
|
|
56
56
|
* **为什么现在需要**:修前这两类 ask 挡住会话授权靠的是策略层的**次序**(`createDurableAskPolicy` 先判 neverAuto / 预算耗尽,再查
|
|
57
|
-
* 豁免探针)。候裁 16 把会话授权的兑现位收到**审批席**(协调器 `standingGrantSettles`,判据 = `
|
|
57
|
+
* 豁免探针)。候裁 16 把会话授权的兑现位收到**审批席**(协调器 `standingGrantSettles`,判据 = `standingAnswerHome`),而审批席
|
|
58
58
|
* 读不到 `matchedAskRule`(core 刻意不上卡)—— 不盖出身,一条 `remember:"session"` 就会在审批席上退役「恒问人」。盖了之后
|
|
59
59
|
* 席的同一只判据按 `mandated` 挡住它,耐久 `/decide` 腿的可记性(park 行 `mandated` 孪生位)同判 ⇒ 回执 `rememberApplied:false`。
|
|
60
60
|
* live 腿修前这里本就是个洞(进程内 `allow_session` 授权可退役 neverAuto 卡),随类收掉。
|
|
61
61
|
*
|
|
62
|
-
* ⚠️ **只**这两类:普通 `APPROVAL_REQUIRE` 仍只经 {@link operatorAsk}(
|
|
62
|
+
* ⚠️ **只**这两类:普通 `APPROVAL_REQUIRE` 仍只经 {@link operatorAsk}(不盖出身)。它的问带 `matchedAskRule` ⇒ 引擎类 `ask_rule`:bypass 放不掉、
|
|
63
|
+
* auto 分类器答不了(引擎眼里的免疫只靠这一位 —— core 7.44.0 的类表里 `operator_forced` 分类器**可答**,所以不拿出身位替它)。它是 server 自己的问,
|
|
64
|
+
* 它的站着的答案(「本会话放行」、耐久豁免行)由名单门在出问之前读 server 的会话豁免店(`approval.ts`),引擎的类表只裁引擎铸的问
|
|
65
|
+
* (`tool-approval.ts` 的 `standingAnswerHome` 按问的属主分读)。不盖出身还有第二个理由:出身位让引擎盖 `mandated`,卡面就成了「强制卡」——
|
|
66
|
+
* 名单卡要带「本会话放行」,而强制卡上没有。「恒问人」两类盖出身 ⇒ core 读 `operator_forced`、盖 `mandated`,站着的答案在哪一层都退不掉它们。
|
|
63
67
|
*/
|
|
64
68
|
export declare function operatorAlwaysHumanAsk(rule: string, message: string): OperatorAskResult;
|
|
65
69
|
/**
|
|
@@ -1,83 +1,104 @@
|
|
|
1
1
|
import { type WorkflowRun } from "@sema-agent/core";
|
|
2
|
-
/**
|
|
2
|
+
/** 一条索引行 = 一枚**指针**:记录 `runId` 的 `parks[]` 点名过子会话 `sessionId`。主键 = (子会话, run)—— 一条续跑链 / 一次分叉上
|
|
3
|
+
* 同一个子会话可以被好几条记录点名,判决要看到它们全部(见 {@link routeWorkflowPark})。 */
|
|
3
4
|
export interface WorkflowAgentSessionRow {
|
|
4
|
-
/**
|
|
5
|
+
/** 那只 workflow 子代自己的会话 id(= 条目的 `sessionId` = 那条 checkpoint 行的 `sessionId`;
|
|
5
6
|
* **不是** `sourceTaskId` —— 后者在 core 契约上是 ECHO-ONLY、明令不可作门输入)。 */
|
|
6
7
|
sessionId: string;
|
|
7
|
-
/**
|
|
8
|
+
/** 点名它的 workflow run(`w*`)。 */
|
|
8
9
|
runId: string;
|
|
9
|
-
/**
|
|
10
|
+
/** 该条目的稳定顺位身份(条目的 `callKey`)—— 行自描述用,判决以记录上的条目为准。 */
|
|
10
11
|
agentKey: string;
|
|
11
12
|
/** run 的租户键(`WorkflowRun.scope`)。 */
|
|
12
13
|
scope: string;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
|
-
* 索引面的两个动词。**写者只有一个**(见文件头)
|
|
16
|
+
* 索引面的两个动词。**写者只有一个**(见文件头),读者是判别式。
|
|
16
17
|
*
|
|
17
|
-
* `syncRun` 语义 = 「这条 run
|
|
18
|
-
*
|
|
18
|
+
* `syncRun` 语义 = 「这条 run 当前的指针集**就是** rows」:多出来的旧行删,rows 里的行 upsert。幂等、可重放、与 run 的一次成功写同拍。
|
|
19
|
+
* `listByAgentSession` = 点名这个子会话的**全部**指针(各 run 一行;顺序不承重)。
|
|
19
20
|
*/
|
|
20
21
|
export interface WorkflowAgentSessionIndex {
|
|
21
22
|
syncRun(runId: string, rows: readonly WorkflowAgentSessionRow[]): Promise<void>;
|
|
22
|
-
|
|
23
|
+
listByAgentSession(sessionId: string): Promise<WorkflowAgentSessionRow[]>;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
|
-
* 🔴 **park
|
|
26
|
+
* 🔴 **park 身份的单一真源**(core 7.17.0 [ref];持有者两成员 core 7.44.0 #1319)—— 引擎写在记录上的 `parks[]` 的元素形。
|
|
26
27
|
*
|
|
27
|
-
* 型从 core 的那一位取、不在本仓复述:core 往元素上加一位 ⇒ `trace/core-keyset-guard.ts` 的 ⑬b
|
|
28
|
-
* 与下面那只读者的构造式**同时**红,加键的人二选一,没有第三条静默的路。
|
|
28
|
+
* 型从 core 的那一位取、不在本仓复述:core 往元素上加一位 ⇒ `trace/core-keyset-guard.ts` 的 ⑬b 成员门与下面那只读者的构造式**同时**红。
|
|
29
29
|
*/
|
|
30
30
|
export type WorkflowRunParkKey = NonNullable<WorkflowRun["parks"]>[number];
|
|
31
31
|
/**
|
|
32
|
-
* 盘上 run blob → `parks[]` 的**唯一读者**(宪法 [ref] §2:进程外字节禁裸 `as`
|
|
33
|
-
* {@link
|
|
32
|
+
* 盘上 run blob → `parks[]` 的**唯一读者**(宪法 [ref] §2:进程外字节禁裸 `as` 铸形)。路由({@link routeWorkflowPark})与写者
|
|
33
|
+
* ({@link buildParkSessionRows})都经它读,「缺席」与「读不出」的边界只在这里划一次。
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
* ·
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* ·
|
|
43
|
-
*
|
|
44
|
-
* 而这只读者的消费面正是 park 的赎回路由。
|
|
45
|
-
*
|
|
46
|
-
* ⚠️ 「读不出来」与「读得出来、只是空的」是**两个状态**:后者 = 这条 run 不负责任何 park(诚实 miss),
|
|
47
|
-
* 前者 = 证不了,两者的处置相反 ⇒ 缺席永远不折成 `[]`。
|
|
35
|
+
* 判据:
|
|
36
|
+
* · 这一位**缺席** ⇒ `[]`:没有条目,持有不了任何东西。缺席是引擎契约里的合法形 —— core 7.17.0 之前写下的记录没有它,core 7.44.0
|
|
37
|
+
* 准入没走完的续跑记录(`resumeAdmissionIncomplete`)也没有它;引擎续跑这样的记录时自己从 journal 派生,走到停驻时写下带持有者位的新条目;
|
|
38
|
+
* · 这一位**在场却不是数组** ⇒ 读不出(`undefined`):记录说了话、说的不是一份列表;
|
|
39
|
+
* · 每条四个身份位必须是非空串;`originUnconfirmed` 只认字面 `true`(core 从不写 `false`);
|
|
40
|
+
* · **持有者两成员同进同出**(core 7.44.0 逐字:both or neither):`parkedBy` 非空串、`generation` 非负安全整数;只有其一、或任一形坏
|
|
41
|
+
* ⇒ 坏条目。两者皆无 = 旧引擎写的条目(证不了持有者),照读;
|
|
42
|
+
* · **任一条目形坏 ⇒ 整只读不出来**(整档成败制):core 对同一形整条拒 resume(`park_binding_broken`);「丢掉坏的、留下好的」会静默
|
|
43
|
+
* 铸出一份**比真相少几条 park** 的集合,而这只读者的消费面正是 park 的路由与赎回。
|
|
48
44
|
*/
|
|
49
45
|
export declare function readRunParks(run: unknown): WorkflowRunParkKey[] | undefined;
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
|
|
46
|
+
/**
|
|
47
|
+
* 🔴 **「这条记录的 run 持有这条停驻」的唯一读法**(core 7.44.0 #1319 逐字):`entry.parkedBy === record.id`。
|
|
48
|
+
* 继承来、本跑还没走到的条目带上一跑的 id;旧引擎写的条目两成员皆无 —— 两者都不是本记录持有的。
|
|
49
|
+
*/
|
|
50
|
+
export declare function heldParksOf(recordId: string, parks: readonly WorkflowRunParkKey[]): WorkflowRunParkKey[];
|
|
51
|
+
/**
|
|
52
|
+
* 派生的**唯一属主**:一只 `WorkflowRun` 的指针行 = 它 `parks[]` 里点名的每一个子会话(一个子会话一行)。`parks` 缺席 ⇒ 零条
|
|
53
|
+
* ({@link readRunParks}:没有条目)⇒ 写者按「这条 run 的指针集就是这一份」清掉它已有的指针。
|
|
54
|
+
* `parks[]` 读不出 ⇒ `undefined`(**派生不出来**,不是「零条」):写者据此不动这条 run 已有的指针(文件头「单写者」段)。
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildParkSessionRows(runId: string, run: {
|
|
57
|
+
scope: string;
|
|
58
|
+
parks?: unknown;
|
|
59
|
+
}): WorkflowAgentSessionRow[] | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* 一次路由的判决(闭集):
|
|
62
|
+
* · `held` —— 持有这个子会话的记录里最高代次**恰一条**:`park` = 它持有的那一条条目(赎回键在条目上);
|
|
63
|
+
* · `forked` —— 最高代次上不止一条记录(同一条 run 先后被续跑两次,两支都走到了这里):`runIds` 升序;
|
|
64
|
+
* · `unproven` —— 没有任何记录持有它,而这件事证不了:`unreadable` = 某一条候选记录的 `parks[]` 读不出(它可能就是持有者);
|
|
65
|
+
* `older_engine` = 点名它的条目没有持有者位(写于不带这两位的旧引擎);`runId` = 去续跑的那一条(`unreadable` 时是读不出的那条里 id 最小的);
|
|
66
|
+
* `readableHolderRunId` 只在 `unreadable` 上可能在场 —— **其余可读候选里**持有它的最高代次**恰一条**记录(规则 ②,与 `held` 同一只判);没有 / 并列 ⇒ 缺席。
|
|
67
|
+
* 它不改判决(任一读不出 ⇒ 整卡拒不变),只让拒体多给一条线索:读不出的那条引擎往往恰拒续跑,而健康的持有者就在旁边(工作可从它续上;
|
|
68
|
+
* 读不出的那条的指针仍在候选里,续跑它之后这张卡照旧整卡拒,直到那条记录被修好);
|
|
69
|
+
* · `none` —— 没有任何记录持有它,且候选全部读得出、条目都带持有者位 ⇒ 诚实 miss。
|
|
70
|
+
*/
|
|
71
|
+
export type WorkflowParkRoute = {
|
|
72
|
+
readonly kind: "held";
|
|
73
|
+
readonly runId: string;
|
|
74
|
+
readonly run: WorkflowRun;
|
|
75
|
+
readonly park: WorkflowRunParkKey;
|
|
76
|
+
} | {
|
|
77
|
+
readonly kind: "forked";
|
|
78
|
+
readonly runIds: readonly string[];
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: "unproven";
|
|
81
|
+
readonly cause: "unreadable" | "older_engine";
|
|
82
|
+
readonly runId: string;
|
|
83
|
+
readonly run: WorkflowRun;
|
|
84
|
+
readonly readableHolderRunId?: string;
|
|
85
|
+
} | {
|
|
86
|
+
readonly kind: "none";
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* 🔴 **路由与赎回的唯一判决**(纯函数;输入 = 点名这个子会话的每一条记录,读不到的记录 —— 已被收割 —— 由调用方先剔掉)。
|
|
60
90
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `persist("update")` 打在 append **之前**。⇒ 那条判据会在 append 失败(以及 append 前的那一拍)上把
|
|
69
|
-
* 真正持有**新 token** 的那一跑整只排除,而上一跑的记录里只有**旧** token ⇒ 这条审批再也送不出去。
|
|
91
|
+
* 一条规则,按序:
|
|
92
|
+
* ① 任一候选的 `parks[]` 读不出 ⇒ `unproven/unreadable`(它可能正是最高代次的持有者;宁可不投,绝不投错 —— 引擎对条目形坏的记录整条拒续跑);
|
|
93
|
+
* 其余可读候选按下一条判出的持有者(恰一条)带进 `readableHolderRunId`(只给出路,不改判决);
|
|
94
|
+
* ② 其余候选里,持有这个子会话({@link heldParksOf})的条目取最高 `generation`:恰一条记录 ⇒ `held`;多于一条 ⇒ `forked`;
|
|
95
|
+
* ③ 没有持有者:有条目两成员皆无(旧引擎写的)⇒ `unproven/older_engine`,`runId` = 这类记录里最晚建的那条(续跑它,新引擎走到这张停驻时
|
|
96
|
+
* 写下自己的代次 0 行);否则 `none`。
|
|
97
|
+
* 判决不看写序、不看 `agents[]`、不看 `originRunId`(core:那一位说的是「材料在哪条 run 的 journal 里」,不是「谁站在上面」)。
|
|
70
98
|
*
|
|
71
|
-
*
|
|
72
|
-
|
|
73
|
-
export declare function
|
|
74
|
-
scope: string;
|
|
75
|
-
agents?: ReadonlyArray<{
|
|
76
|
-
status?: unknown;
|
|
77
|
-
sessionId?: unknown;
|
|
78
|
-
callKey?: unknown;
|
|
79
|
-
}>;
|
|
80
|
-
}): WorkflowAgentSessionRow[];
|
|
99
|
+
* 🔴 **窗与删码**:规则 ③ 的 `older_engine` 读臂是一次性迁移 M9 的读侧半场(升级那一刻还停驻着的存量记录)。窗口与删码清单见 `plugins/one-time-migrations.ts` 登记表 M9 一行,随 M9 一起删;这里不写第二份窗。
|
|
100
|
+
*/
|
|
101
|
+
export declare function routeWorkflowPark(sessionId: string, candidates: readonly WorkflowRun[]): WorkflowParkRoute;
|
|
81
102
|
/** 两处(装饰器的变更检测、local 后端的重放)共用的行集等值判据 —— 顺序无关的集合比较,单一属主。 */
|
|
82
103
|
export declare function sameAgentSessionRows(a: readonly WorkflowAgentSessionRow[], b: readonly WorkflowAgentSessionRow[]): boolean;
|
|
83
104
|
/** 账本一行的**内域形**:一次 `syncRun` 的整份行集快照(`{runId, rows}`)。 */
|
|
@@ -88,7 +109,7 @@ export interface WorkflowAgentSessionLedgerLine {
|
|
|
88
109
|
/**
|
|
89
110
|
* 盘上账本行 → 内域形的**唯一读者**(宪法 [ref] §2:进程外字节禁裸 `as` 铸形)。
|
|
90
111
|
*
|
|
91
|
-
* 姿态与 SQL 孪生逐字同源:那边 `
|
|
112
|
+
* 姿态与 SQL 孪生逐字同源:那边 `listByAgentSession` 逐字段收窄(`String(r.session_id)` …),这边逐字段
|
|
92
113
|
* 判形 —— 一张索引面、两个后端、**一种**入域姿态。
|
|
93
114
|
*
|
|
94
115
|
* **整行成败制**(与既有的撕裂行臂同一条规则):一行 = 一次 syncRun 的**整份**快照,任一位读不出来就说明
|
|
@@ -109,18 +130,20 @@ export declare function readAgentSessionLedgerLine(v: unknown): WorkflowAgentSes
|
|
|
109
130
|
* 追加 / 重放走本仓 JSONL 店的**共用口**(S-977;此前 ctor 里自写 `truncateTornTail` + 手写 `split("\n") + JSON.parse` 重放):
|
|
110
131
|
* 追加 = core `AppendLog`(惰性打开、撕尾在**打开与每次追加前**修 —— 修前只在 boot 修,运行期半写后的下一次成功写仍会被粘进残尾),
|
|
111
132
|
* 重放 = `replayJsonlSkipping`(登记座 `workflow-agent-session-index`:中间坏行 —— 含半截 JSON 与形不对的整行 —— 跳过 + 逐条披露 + F tag;
|
|
112
|
-
* 撕尾不计、不披露)。盘上字节 / 0o600 账本 / 0o700
|
|
133
|
+
* 撕尾不计、不披露)。盘上字节 / 0o600 账本 / 0o700 目录与修前逐字同,升级零迁移:一行仍是「这条 run 的指针集」,旧账本行就是旧记录的指针
|
|
134
|
+
* (判决从记录读,指针本身没有新旧两种读法)。
|
|
113
135
|
*/
|
|
114
136
|
export declare class LocalWorkflowAgentSessionIndex implements WorkflowAgentSessionIndex {
|
|
115
137
|
private readonly fsyncEnabled;
|
|
116
138
|
private readonly ledgerPath;
|
|
117
139
|
private readonly byRun;
|
|
140
|
+
/** 子会话 → (run → 指针)。一个子会话可以被好几条记录点名(续跑链 / 分叉),全部留着 —— 判决看全集。 */
|
|
118
141
|
private readonly bySession;
|
|
119
142
|
private log;
|
|
120
143
|
constructor(root?: string, fsyncEnabled?: boolean);
|
|
121
|
-
/** 内存两张表的收敛(重放与写路径共用一份逻辑)
|
|
144
|
+
/** 内存两张表的收敛(重放与写路径共用一份逻辑):先摘这条 run 的旧指针,再挂新的。 */
|
|
122
145
|
private apply;
|
|
123
146
|
syncRun(runId: string, rows: readonly WorkflowAgentSessionRow[]): Promise<void>;
|
|
124
|
-
|
|
147
|
+
listByAgentSession(sessionId: string): Promise<WorkflowAgentSessionRow[]>;
|
|
125
148
|
}
|
|
126
149
|
//# sourceMappingURL=workflow-agent-session-index.d.ts.map
|
|
@@ -6,27 +6,87 @@ export function readRunParks(run) {
|
|
|
6
6
|
if (typeof run !== "object" || run === null)
|
|
7
7
|
return undefined;
|
|
8
8
|
const v = run.parks;
|
|
9
|
+
if (v === undefined)
|
|
10
|
+
return [];
|
|
9
11
|
if (!Array.isArray(v))
|
|
10
12
|
return undefined;
|
|
11
13
|
const out = [];
|
|
12
14
|
for (const raw of v) {
|
|
13
15
|
if (typeof raw !== "object" || raw === null)
|
|
14
16
|
return undefined;
|
|
15
|
-
const { callKey, sessionId, token, originRunId } = raw;
|
|
17
|
+
const { callKey, sessionId, token, originRunId, originUnconfirmed, parkedBy, generation } = raw;
|
|
16
18
|
if (!nonEmptyString(callKey) || !nonEmptyString(sessionId) || !nonEmptyString(token) || !nonEmptyString(originRunId))
|
|
17
19
|
return undefined;
|
|
18
|
-
|
|
20
|
+
if (originUnconfirmed !== undefined && originUnconfirmed !== true)
|
|
21
|
+
return undefined;
|
|
22
|
+
const holder = parkedBy === undefined && generation === undefined
|
|
23
|
+
? {}
|
|
24
|
+
: nonEmptyString(parkedBy) && typeof generation === "number" && Number.isSafeInteger(generation) && generation >= 0
|
|
25
|
+
? { parkedBy, generation }
|
|
26
|
+
: undefined;
|
|
27
|
+
if (holder === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
out.push({ callKey, sessionId, token, originRunId, ...(originUnconfirmed === true ? { originUnconfirmed } : {}), ...holder });
|
|
19
30
|
}
|
|
20
31
|
return out;
|
|
21
32
|
}
|
|
22
|
-
export function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
export function heldParksOf(recordId, parks) {
|
|
34
|
+
return parks.filter((p) => p.parkedBy === recordId);
|
|
35
|
+
}
|
|
36
|
+
export function buildParkSessionRows(runId, run) {
|
|
37
|
+
const parks = readRunParks(run);
|
|
38
|
+
if (parks === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
const bySession = new Map();
|
|
41
|
+
for (const p of parks)
|
|
42
|
+
if (!bySession.has(p.sessionId))
|
|
43
|
+
bySession.set(p.sessionId, { sessionId: p.sessionId, runId, agentKey: p.callKey, scope: run.scope });
|
|
44
|
+
return [...bySession.values()];
|
|
45
|
+
}
|
|
46
|
+
export function routeWorkflowPark(sessionId, candidates) {
|
|
47
|
+
const ordered = [...candidates].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
48
|
+
const readable = [];
|
|
49
|
+
let firstUnreadable;
|
|
50
|
+
for (const run of ordered) {
|
|
51
|
+
const parks = readRunParks(run);
|
|
52
|
+
if (parks === undefined)
|
|
53
|
+
firstUnreadable ??= run;
|
|
54
|
+
else
|
|
55
|
+
readable.push({ run, parks });
|
|
56
|
+
}
|
|
57
|
+
const top = highestHolders(sessionId, readable);
|
|
58
|
+
if (firstUnreadable !== undefined) {
|
|
59
|
+
const holder = top.length === 1 ? top[0].run.id : undefined;
|
|
60
|
+
return { kind: "unproven", cause: "unreadable", runId: firstUnreadable.id, run: firstUnreadable, ...(holder !== undefined ? { readableHolderRunId: holder } : {}) };
|
|
61
|
+
}
|
|
62
|
+
if (top.length === 1)
|
|
63
|
+
return { kind: "held", runId: top[0].run.id, run: top[0].run, park: top[0].park };
|
|
64
|
+
if (top.length > 1)
|
|
65
|
+
return { kind: "forked", runIds: top.map((t) => t.run.id) };
|
|
66
|
+
const older = readable.filter(({ parks }) => parks.some((p) => p.sessionId === sessionId && p.parkedBy === undefined));
|
|
67
|
+
if (older.length > 0) {
|
|
68
|
+
const latest = older.reduce((a, b) => (b.run.createdAt > a.run.createdAt || (b.run.createdAt === a.run.createdAt && b.run.id > a.run.id) ? b : a));
|
|
69
|
+
return { kind: "unproven", cause: "older_engine", runId: latest.run.id, run: latest.run };
|
|
70
|
+
}
|
|
71
|
+
return { kind: "none" };
|
|
72
|
+
}
|
|
73
|
+
function highestHolders(sessionId, readable) {
|
|
74
|
+
let top = [];
|
|
75
|
+
let topGeneration = -1;
|
|
76
|
+
for (const { run, parks } of readable) {
|
|
77
|
+
for (const park of heldParksOf(run.id, parks)) {
|
|
78
|
+
if (park.sessionId !== sessionId || park.generation === undefined)
|
|
79
|
+
continue;
|
|
80
|
+
if (park.generation > topGeneration) {
|
|
81
|
+
topGeneration = park.generation;
|
|
82
|
+
top = [{ run, park }];
|
|
83
|
+
}
|
|
84
|
+
else if (park.generation === topGeneration && !top.some((t) => t.run.id === run.id)) {
|
|
85
|
+
top.push({ run, park });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
28
88
|
}
|
|
29
|
-
return
|
|
89
|
+
return top;
|
|
30
90
|
}
|
|
31
91
|
export function sameAgentSessionRows(a, b) {
|
|
32
92
|
if (a.length !== b.length)
|
|
@@ -97,15 +157,21 @@ export class LocalWorkflowAgentSessionIndex {
|
|
|
97
157
|
}
|
|
98
158
|
apply(runId, rows) {
|
|
99
159
|
for (const old of this.byRun.get(runId) ?? []) {
|
|
100
|
-
|
|
160
|
+
const runs = this.bySession.get(old.sessionId);
|
|
161
|
+
runs?.delete(runId);
|
|
162
|
+
if (runs?.size === 0)
|
|
101
163
|
this.bySession.delete(old.sessionId);
|
|
102
164
|
}
|
|
103
165
|
if (rows.length === 0)
|
|
104
166
|
this.byRun.delete(runId);
|
|
105
167
|
else
|
|
106
168
|
this.byRun.set(runId, [...rows]);
|
|
107
|
-
for (const r of rows)
|
|
108
|
-
this.bySession.
|
|
169
|
+
for (const r of rows) {
|
|
170
|
+
let runs = this.bySession.get(r.sessionId);
|
|
171
|
+
if (runs === undefined)
|
|
172
|
+
this.bySession.set(r.sessionId, (runs = new Map()));
|
|
173
|
+
runs.set(runId, r);
|
|
174
|
+
}
|
|
109
175
|
}
|
|
110
176
|
async syncRun(runId, rows) {
|
|
111
177
|
if (this.ledgerPath !== undefined) {
|
|
@@ -114,8 +180,8 @@ export class LocalWorkflowAgentSessionIndex {
|
|
|
114
180
|
}
|
|
115
181
|
this.apply(runId, rows);
|
|
116
182
|
}
|
|
117
|
-
async
|
|
118
|
-
return this.bySession.get(sessionId) ??
|
|
183
|
+
async listByAgentSession(sessionId) {
|
|
184
|
+
return [...(this.bySession.get(sessionId)?.values() ?? [])];
|
|
119
185
|
}
|
|
120
186
|
}
|
|
121
187
|
//# sourceMappingURL=workflow-agent-session-index.js.map
|
|
@@ -177,7 +177,12 @@ export declare class FileWorkflowCompletionInbox extends InMemoryWorkflowComplet
|
|
|
177
177
|
private fd;
|
|
178
178
|
private appendsSinceCompact;
|
|
179
179
|
constructor(root: string, onWarn?: (msg: string, meta: Record<string, unknown>) => void, fsyncEnabled?: boolean);
|
|
180
|
+
/** 重放 = 本仓 JSONL 重放属主(登记座 `workflow-completion-inbox`,fail-open + F tag):每行过 {@link INBOX_RECORD_SHAPE},
|
|
181
|
+
* 读不出形(`null` / 非对象 / 缺 `op` / 缺 id …)⇒ 属主跳过 + 逐条披露,**不抛**;撕尾(崩在一次 append 中途)是崩溃的正常落盘形,
|
|
182
|
+
* 属主不计。好行按序折叠 —— {@link foldRecord} 与运行期各口同一套语义(重放是严格折叠)。 */
|
|
180
183
|
private replay;
|
|
184
|
+
/** 把一条已判过形的记录折进内存。穷尽 switch:加一种记录 ⇒ 编译红,由人处置(形判据与折叠同批改)。 */
|
|
185
|
+
private foldRecord;
|
|
181
186
|
private commit;
|
|
182
187
|
/** Compact iff we've appended enough since the last one. MUST be called AFTER the in-memory mutation the
|
|
183
188
|
* triggering commit records — compact() rewrites from `bySession`, so a compact fired BEFORE the caller
|