@sema-agent/server 7.142.0 → 7.144.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 +608 -0
- package/USAGE.md +63 -36
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +7 -5
- package/dist/approval-ask-audit-store.js +12 -6
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +155 -39
- package/dist/approval-card.js +111 -31
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +8 -8
- package/dist/approval-reconciler.js +3 -3
- package/dist/audit.js +4 -2
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +3 -3
- package/dist/boot/reapers.js +5 -4
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -8
- package/dist/boot/runner-deps.d.ts +14 -6
- package/dist/boot/runner-deps.js +6 -17
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +18 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +3 -5
- package/dist/boot/stage-05-execution-env.d.ts +5 -2
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +6 -2
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +4 -1
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +7 -7
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +31 -19
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +23 -17
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +8 -8
- package/dist/capability-facts.js +4 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +1 -1
- package/dist/config-d-migration.d.ts +2 -0
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +5 -5
- package/dist/config.d.ts +4 -3
- package/dist/config.js +1 -1
- package/dist/delegated-session-owner.d.ts +35 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +28 -52
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/hooks/hook-runner.js +28 -7
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +82 -53
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +143 -31
- package/dist/http/routes/capabilities.js +11 -2
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +1 -1
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +9 -8
- package/dist/http/routes/session-memory-sync.d.ts +34 -6
- package/dist/http/routes/session-memory-sync.js +27 -10
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +27 -49
- package/dist/http/server.d.ts +13 -7
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.js +14 -3
- package/dist/memory-sync.d.ts +20 -4
- package/dist/memory-sync.js +23 -4
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +30 -10
- package/dist/observability/fail-open.js +27 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +6 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +122 -0
- package/dist/peer-inbound-face.js +123 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.js +18 -6
- package/dist/plugins/approval-ask-store-memory.js +8 -6
- package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
- package/dist/plugins/approval-ask-store-sql.js +30 -11
- package/dist/plugins/caching-session-store.d.ts +0 -3
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +45 -38
- package/dist/plugins/checkpoint-store-sql.js +108 -45
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +29 -0
- package/dist/plugins/file-run-store.js +97 -8
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +6 -8
- package/dist/plugins/local-checkpoint-store.js +5 -4
- package/dist/plugins/local-session-store.d.ts +69 -30
- package/dist/plugins/local-session-store.js +133 -57
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +44 -14
- package/dist/plugins/one-time-migrations.js +118 -15
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-env-k8s.d.ts +37 -24
- package/dist/plugins/remote-env-k8s.js +50 -17
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.d.ts +23 -28
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +29 -5
- package/dist/plugins/store-contracts.js +4 -1
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/task-attachment-store.js +37 -52
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -6
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +10 -7
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/security.d.ts +0 -4
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +34 -23
- package/dist/session-error-wire.js +8 -3
- package/dist/session-incarnation.d.ts +3 -3
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +97 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +11 -8
- package/dist/task-settings.js +31 -11
- package/dist/tool-approval.d.ts +55 -35
- package/dist/tool-approval.js +112 -74
- package/dist/trace/core-keyset-guard.d.ts +12 -9
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +7 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +74 -25
- package/dist/trace/model-segment.js +141 -32
- package/dist/trace/project.d.ts +15 -6
- package/dist/trace/project.js +12 -4
- package/dist/trace/redact.d.ts +72 -9
- package/dist/trace/redact.js +1509 -471
- package/dist/trace/self-minted-wire.d.ts +598 -47
- package/dist/trace/self-minted-wire.js +190 -20
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +90 -10
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.143.0.json +1167 -0
- package/wire-changes/server@7.144.0.json +291 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 信箱**收件人生命周期面**([ref])—— core `MailboxStore`
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 逐字):「Put `(scope, handle)` into the deployment's PRE-DELETE state — whatever that is for this backend
|
|
7
|
-
* (a cascade marking the session row, a `deleting` column, a tombstone table). Core does not name the
|
|
8
|
-
* mechanism, only what `append` must then do.」 ⇒ 本文件就是 server 侧那个 "whatever"。
|
|
2
|
+
* 信箱**收件人生命周期面**([ref])—— 「这个收件人正在被部署删掉」。core 7.61.0 起它是 `MailboxStore` 契约里的一对可选成员
|
|
3
|
+
* (`retireRecipient` / `listRetiredRecipients`,一个能力:两个都在或都不在),经 core 的唯一判官 `mailboxRecipientLifecycleOf` 读
|
|
4
|
+
* (缺一半 ⇒ 判官拒)。本仓不另立型:{@link MailboxRecipientLifecycle} 就是那只判官答的形,{@link recipientLifecycleOf} 是本仓唯一的读法 ——
|
|
5
|
+
* 店带那一对 ⇒ 判官的答;不带 ⇒ {@link createDropOnlyRecipientLifecycle}(见文末「缺席形」)。
|
|
9
6
|
*
|
|
10
7
|
* ## 两个动词,各答一个问题
|
|
11
8
|
*
|
|
@@ -50,30 +47,11 @@
|
|
|
50
47
|
* 后端选择点(`boot/stores.ts` 的 `mailbox_store_enabled` 行)逐次落 `recipientTombstones` 读数,
|
|
51
48
|
* 披露在 `docs/DEPLOY-PREREQS.md`。方向上它也不放宽任何东西 —— 两种形都**不收**给已删会话的信。
|
|
52
49
|
*/
|
|
53
|
-
import type
|
|
54
|
-
/**
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/** 退役时刻(epoch ms)——目录据此铸 `diedAt`。 */
|
|
59
|
-
readonly retiredAt: number;
|
|
60
|
-
/** 退役前的显示名(会话的删前 `title`);缺席 = 当时没有可用的名字。 */
|
|
61
|
-
readonly label?: string;
|
|
62
|
-
}
|
|
63
|
-
export interface MailboxRecipientLifecycle {
|
|
64
|
-
/**
|
|
65
|
-
* 把 `(scope, handle)` 置入**预删除态**:此后 `append` 一律以 `MAILBOX_TOMBSTONED_RECIPIENT_CODE` 拒。
|
|
66
|
-
* **只打标**——不删消息、不动租约、不碰盒行的高水位(理由 = core T2 试剂盒的两格副作用判据,见模块头)。
|
|
67
|
-
* 幂等(重复 retire 只更新时刻/名字)。
|
|
68
|
-
*
|
|
69
|
-
* @returns 是否真的落了一张耐久墓碑。`false` 有两种,都是「没落」:① 本后端没有预删除态,箱已被直接 `drop`
|
|
70
|
-
* (拒的方向不变,退化的只有拒绝措辞与「正在删除中」那一行的可见性);② 这只键收件箱的键判官不收(SQL 两孪生:没有箱可打标,
|
|
71
|
-
* 这只键的 `append` 本来就以判官那一句拒,不带墓碑码)。
|
|
72
|
-
*/
|
|
73
|
-
retireRecipient(scope: string, handle: string, at: number, label?: string): Promise<boolean>;
|
|
74
|
-
/** 本 scope 下句柄以 `handlePrefix` 开头的墓碑。无预删除态的后端恒返回空。 */
|
|
75
|
-
listRetiredRecipients(scope: string, handlePrefix: string): Promise<readonly MailboxRecipientTombstone[]>;
|
|
76
|
-
}
|
|
50
|
+
import { mailboxRecipientLifecycleOf, type MailboxStore } from "@sema-agent/core";
|
|
51
|
+
/** 收件人生命周期面 = core 判官 `mailboxRecipientLifecycleOf` 答的那一对(本仓零份第二型;墓碑行型直接用 core 的 `MailboxRecipientTombstone`)。 */
|
|
52
|
+
export type MailboxRecipientLifecycle = NonNullable<ReturnType<typeof mailboxRecipientLifecycleOf>>;
|
|
53
|
+
/** 一只店的收件人生命周期面 —— 本仓唯一的读法:店带那一对 ⇒ core 判官的答(缺一半判官就拒,`config.invalid_argument`);不带 ⇒ drop-only 形。 */
|
|
54
|
+
export declare function recipientLifecycleOf(store: MailboxStore): MailboxRecipientLifecycle;
|
|
77
55
|
/**
|
|
78
56
|
* 看不见收件人生命周期的后端的诚实形:retire = 直接把箱 `drop` 掉(**不**打标,因为没有可打标的地方),
|
|
79
57
|
* 墓碑清单恒空。
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { mailboxRecipientLifecycleOf } from "@sema-agent/core";
|
|
2
|
+
export function recipientLifecycleOf(store) {
|
|
3
|
+
return mailboxRecipientLifecycleOf(store) ?? createDropOnlyRecipientLifecycle(store);
|
|
4
|
+
}
|
|
1
5
|
export function createDropOnlyRecipientLifecycle(store) {
|
|
2
6
|
return {
|
|
3
7
|
retireRecipient: async (scope, handle) => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
|
-
import type { MailboxAckResult, MailboxAppendMessage, MailboxDecision, MailboxDecisionOutcome, MailboxLease, MailboxStore } from "@sema-agent/core";
|
|
3
|
+
import type { MailboxAckResult, MailboxAppendMessage, MailboxDecision, MailboxDecisionOutcome, MailboxLease, MailboxRecipientTombstone, MailboxStore } from "@sema-agent/core";
|
|
4
4
|
import { type IndexSpec } from "./ensure-index.js";
|
|
5
|
-
import type { MailboxRecipientLifecycle, MailboxRecipientTombstone } from "./mailbox-recipient-lifecycle.js";
|
|
6
5
|
import type { SqlDialect } from "./sql-driver.js";
|
|
7
6
|
export declare const MAILBOX_TABLE = "mailbox";
|
|
8
7
|
export declare const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
@@ -12,6 +11,11 @@ export declare const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
|
12
11
|
* —— `mailbox` 行是**盒**事实(next_seq / lease),墓碑是**收件人**事实,两者的生命周期本来就不同
|
|
13
12
|
* (`drop` 同时终结两者 —— 墓碑随箱亡,理由全文见 `mailbox-recipient-lifecycle.ts` 模块头)。 */
|
|
14
13
|
export declare const MAILBOX_TOMBSTONE_TABLE = "mailbox_tombstone";
|
|
14
|
+
/** 盒的**已收窗**(core 7.61.0):`ack` 删掉的带身份记录各留一行 `{ record_id, seq, digests }`,每盒恰留按 `seq` 最新的 `MAILBOX_CONSUMED_ID_WINDOW` 行;
|
|
15
|
+
* `append` 在盒行锁之内连同持有行一起交引擎的 `judgeMailboxAppendById` 判(同记录 ⇒ 答它当时的 `seq`、零写;不同 ⇒ `mailbox.duplicate_record_id`)。
|
|
16
|
+
* 是**盒**事实(与 `next_seq` 同生命周期):`drop` 随盒删、`reap` 留着(它清掉的东西从没被收过)。新表而不是 `mailbox` 加列,理由同墓碑表(存量库零运维动作)。
|
|
17
|
+
* `digests` = 引擎 `mailboxReplayDigests` 的四面摘要 JSON(只读那一只函数,不另算)。 */
|
|
18
|
+
export declare const MAILBOX_CONSUMED_TABLE = "mailbox_consumed";
|
|
15
19
|
/**
|
|
16
20
|
* 身份键(`scope` / `handle` / lease `owner`)的 UTF-8 字节上界 = 键列宽(TiDB `VARBINARY(190)`;PG 列按字符,本判官在两方言上都按字节拒)。
|
|
17
21
|
*/
|
|
@@ -35,10 +39,10 @@ export type MailboxKeyColumn = "scope" | "handle" | "owner";
|
|
|
35
39
|
/** 库里读回的一对键列判官拒不拒(与入口同一只判官;字节先无损读成串,读不成 = 不良构)。`reap` 的候选句柄与一次性迁移 M27 都按它判。 */
|
|
36
40
|
export declare function storedMailboxKeyless(scopeKey: unknown, handle: unknown): boolean;
|
|
37
41
|
/**
|
|
38
|
-
*
|
|
42
|
+
* 收件箱表族:四张表的键都是 `(scope_key, handle)`。次序 = 锁序(盒行 → 消息行 → 已收窗行 → 墓碑行;盒行先删的理由见 `drop`)。
|
|
39
43
|
* 「丢掉一只箱」= 按这个次序逐表按键删 —— `drop` 与一次性迁移 M27 是同一组语句({@link mailboxKeyDeleteSql})。
|
|
40
44
|
*/
|
|
41
|
-
export declare const MAILBOX_KEYED_TABLES: readonly ["mailbox", "mailbox_message", "mailbox_tombstone"];
|
|
45
|
+
export declare const MAILBOX_KEYED_TABLES: readonly ["mailbox", "mailbox_message", "mailbox_consumed", "mailbox_tombstone"];
|
|
42
46
|
export type MailboxKeyedTable = (typeof MAILBOX_KEYED_TABLES)[number];
|
|
43
47
|
/** 按键删收件箱表族的一张表(两方言;参数 = [scope_key, handle])。 */
|
|
44
48
|
export declare function mailboxKeyDeleteSql(dialect: SqlDialect, table: MailboxKeyedTable): string;
|
|
@@ -55,7 +59,7 @@ export declare function ensureTiDBMailboxSchema(pool: MySqlPool): Promise<void>;
|
|
|
55
59
|
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensurePgMailboxSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
56
60
|
export declare const MAILBOX_INDEXES: readonly IndexSpec[];
|
|
57
61
|
export declare function ensurePgMailboxSchema(q: (text: string, params?: unknown[]) => Promise<unknown>): Promise<void>;
|
|
58
|
-
export declare class TiDBMailboxStore implements MailboxStore
|
|
62
|
+
export declare class TiDBMailboxStore implements MailboxStore {
|
|
59
63
|
private readonly pool;
|
|
60
64
|
private readonly logger?;
|
|
61
65
|
/** [ref]([ref] §1.2⑥):跨进程安全**显式**声明 —— core 的 `mailboxCrossProcessMountVerdict` 只对字面 `true` 放行
|
|
@@ -82,15 +86,15 @@ export declare class TiDBMailboxStore implements MailboxStore, MailboxRecipientL
|
|
|
82
86
|
reap(scope: string, now: number, opts?: {
|
|
83
87
|
maxAgeMs?: number;
|
|
84
88
|
}): Promise<number>;
|
|
85
|
-
/** 见
|
|
89
|
+
/** 见 core `MailboxStore.retireRecipient`。**只打标**:不删消息、不动租约、不碰 next_seq。
|
|
86
90
|
* 锁序 = 盒行先(与 append / claim / ack / drop / reap 五面统一 —— ABBA 死锁形不许在这里重开),
|
|
87
91
|
* 于是 retire 与并发 append 在同一把行锁上串行:要么 append 先拿到 seq、要么它读到墓碑被拒,没有中间态。 */
|
|
88
92
|
retireRecipient(scope: string, handle: string, at: number, label?: string): Promise<boolean>;
|
|
89
|
-
/** 见
|
|
93
|
+
/** 见 core `MailboxStore.listRetiredRecipients`。前缀查带 `ESCAPE`(句柄是开集串,
|
|
90
94
|
* `_`/`%` 若被当通配会把**别的**收件人一起列出来 —— 与本仓 `?q=` 面同一条 escapeLike 纪律)。 */
|
|
91
95
|
listRetiredRecipients(scope: string, handlePrefix: string): Promise<readonly MailboxRecipientTombstone[]>;
|
|
92
96
|
}
|
|
93
|
-
export declare class PgMailboxStore implements MailboxStore
|
|
97
|
+
export declare class PgMailboxStore implements MailboxStore {
|
|
94
98
|
private readonly pool;
|
|
95
99
|
private readonly logger?;
|
|
96
100
|
/** [ref] 跨进程安全显式声明(TiDB 孪生同注:事务 + 盒行 FOR UPDATE 锁序是真值前提;终验 = test/mailbox-cross-process-kit.test.ts pg 腿)。 */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { isUtf8 } from "node:buffer";
|
|
2
|
-
import { MAILBOX_INVALID_PEER_META_CODE, MAILBOX_RECORD_ID_MAX_CHARS, MAILBOX_TOMBSTONED_RECIPIENT_CODE, MailboxStoreError, assertMailboxDecisionMove, judgeMailboxAppendById, readMailboxAppend, readMailboxPeerMeta, readStoredMailboxDecision } from "@sema-agent/core";
|
|
2
|
+
import { MAILBOX_CONSUMED_ID_WINDOW, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_RECORD_ID_MAX_CHARS, MAILBOX_REPLAY_FACETS, MAILBOX_TOMBSTONED_RECIPIENT_CODE, MailboxStoreError, assertMailboxDecisionMove, judgeMailboxAppendById, mailboxReplayDigests, readMailboxAppend, readMailboxPeerMeta, readStoredMailboxDecision } from "@sema-agent/core";
|
|
3
3
|
import { pgHasUnstorable, pgSanitizeText, PgUnstorableError } from "./pg-safe-json.js";
|
|
4
4
|
import { isWellFormedUnicode } from "../principal-jwt.js";
|
|
5
5
|
import { ensureIndexes } from "./ensure-index.js";
|
|
6
6
|
import { escapeLike } from "./sql-escape.js";
|
|
7
|
-
import { decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readStringArrayColumn } from "./sql-json-column.js";
|
|
7
|
+
import { SqlJsonColumnError, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readStringArrayColumn } from "./sql-json-column.js";
|
|
8
8
|
export const MAILBOX_TABLE = "mailbox";
|
|
9
9
|
export const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
10
10
|
export const MAILBOX_TOMBSTONE_TABLE = "mailbox_tombstone";
|
|
11
|
+
export const MAILBOX_CONSUMED_TABLE = "mailbox_consumed";
|
|
11
12
|
const TOMBSTONE_LABEL_MAX_CHARS = 120;
|
|
12
13
|
function boundedLabel(label) {
|
|
13
14
|
if (label === undefined || label === "")
|
|
@@ -49,8 +50,54 @@ function appendSnapshot(msg) {
|
|
|
49
50
|
}
|
|
50
51
|
return rec;
|
|
51
52
|
}
|
|
52
|
-
function replayedSeq(held, scope, handle, logger, asStored) {
|
|
53
|
-
return judgeMailboxAppendById(rowsToMessages(held, scope, handle, logger), asStored);
|
|
53
|
+
function replayedSeq(held, consumed, scope, handle, logger, asStored) {
|
|
54
|
+
return judgeMailboxAppendById({ held: rowsToMessages(held, scope, handle, logger), consumed: consumedRecordsOf(consumed, scope, handle, logger) }, asStored);
|
|
55
|
+
}
|
|
56
|
+
function consumedRecordsOf(rows, scope, handle, logger) {
|
|
57
|
+
return rows.map((r) => {
|
|
58
|
+
const seq = Number(r.seq);
|
|
59
|
+
let digests;
|
|
60
|
+
try {
|
|
61
|
+
digests = decodeJsonColumn(bufToStr(r.digests), readConsumedDigestsShape, `${MAILBOX_CONSUMED_TABLE}.digests for seq ${seq}`);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
if (!(e instanceof SqlJsonColumnError))
|
|
65
|
+
throw e;
|
|
66
|
+
logger?.warn("mailbox_message_column_corrupt", {
|
|
67
|
+
scope,
|
|
68
|
+
handle,
|
|
69
|
+
seq,
|
|
70
|
+
column: "consumed_digests",
|
|
71
|
+
note: "appends under this record id are refused for the box (fail-closed); repair or delete this row of the received window",
|
|
72
|
+
});
|
|
73
|
+
throw new Error(`MailboxStore: received-window row seq ${seq} has unreadable digests (refusing — the replay judgment cannot be made)`);
|
|
74
|
+
}
|
|
75
|
+
return { id: String(r.record_id), seq, digests };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const readConsumedDigestsShape = (body) => {
|
|
79
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
80
|
+
return { problem: "the digests are not an object" };
|
|
81
|
+
const facets = body;
|
|
82
|
+
if (Object.keys(facets).length !== MAILBOX_REPLAY_FACETS.length || !MAILBOX_REPLAY_FACETS.every((f) => typeof facets[f] === "string")) {
|
|
83
|
+
return { problem: `the digests are not exactly the ${MAILBOX_REPLAY_FACETS.length} replay facets as strings` };
|
|
84
|
+
}
|
|
85
|
+
return { value: Object.freeze(Object.fromEntries(MAILBOX_REPLAY_FACETS.map((f) => [f, facets[f]]))) };
|
|
86
|
+
};
|
|
87
|
+
async function enterConsumedWindow(run, removed, scope, handle) {
|
|
88
|
+
const entered = removed.filter((m) => m.id !== undefined);
|
|
89
|
+
if (entered.length === 0)
|
|
90
|
+
return;
|
|
91
|
+
for (const m of entered) {
|
|
92
|
+
await run({ mysql: `INSERT INTO ${MAILBOX_CONSUMED_TABLE} (scope_key, handle, seq, record_id, digests) VALUES (?, ?, ?, ?, ?)`, pg: `INSERT INTO ${MAILBOX_CONSUMED_TABLE} (scope_key, handle, seq, record_id, digests) VALUES ($1, $2, $3, $4, $5)` }, [scope, handle, m.seq, m.id, JSON.stringify(mailboxReplayDigests(m))]);
|
|
93
|
+
}
|
|
94
|
+
const edge = await run({
|
|
95
|
+
mysql: `SELECT seq FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = ? AND handle = ? ORDER BY seq DESC LIMIT 1 OFFSET ?`,
|
|
96
|
+
pg: `SELECT seq FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = $1 AND handle = $2 ORDER BY seq DESC LIMIT 1 OFFSET $3`,
|
|
97
|
+
}, [scope, handle, MAILBOX_CONSUMED_ID_WINDOW - 1]);
|
|
98
|
+
if (edge.length === 0)
|
|
99
|
+
return;
|
|
100
|
+
await run({ mysql: `DELETE FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = ? AND handle = ? AND seq < ?`, pg: `DELETE FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = $1 AND handle = $2 AND seq < $3` }, [scope, handle, Number(edge[0].seq)]);
|
|
54
101
|
}
|
|
55
102
|
function asUtf8Stored(v) {
|
|
56
103
|
return Buffer.from(v, "utf8").toString("utf8");
|
|
@@ -71,7 +118,7 @@ export function storedMailboxKeyless(scopeKey, handle) {
|
|
|
71
118
|
const h = storedKeyText(handle);
|
|
72
119
|
return scope === undefined || h === undefined || keyless(scope, h);
|
|
73
120
|
}
|
|
74
|
-
export const MAILBOX_KEYED_TABLES = [MAILBOX_TABLE, MAILBOX_MSG_TABLE, MAILBOX_TOMBSTONE_TABLE];
|
|
121
|
+
export const MAILBOX_KEYED_TABLES = [MAILBOX_TABLE, MAILBOX_MSG_TABLE, MAILBOX_CONSUMED_TABLE, MAILBOX_TOMBSTONE_TABLE];
|
|
75
122
|
export function mailboxKeyDeleteSql(dialect, table) {
|
|
76
123
|
return dialect === "mysql" ? `DELETE FROM ${table} WHERE scope_key = ? AND handle = ?` : `DELETE FROM ${table} WHERE scope_key = $1 AND handle = $2`;
|
|
77
124
|
}
|
|
@@ -210,6 +257,14 @@ export async function ensureTiDBMailboxSchema(pool) {
|
|
|
210
257
|
record_id VARCHAR(${MAILBOX_RECORD_ID_MAX_CHARS}) NULL,
|
|
211
258
|
PRIMARY KEY (scope_key, handle, seq),
|
|
212
259
|
KEY idx_mbm_sent (scope_key, handle, sent_at_ms)
|
|
260
|
+
) COLLATE utf8mb4_bin`);
|
|
261
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS ${MAILBOX_CONSUMED_TABLE} (
|
|
262
|
+
scope_key VARBINARY(190) NOT NULL,
|
|
263
|
+
handle VARBINARY(190) NOT NULL,
|
|
264
|
+
seq BIGINT NOT NULL,
|
|
265
|
+
record_id VARCHAR(${MAILBOX_RECORD_ID_MAX_CHARS}) NOT NULL,
|
|
266
|
+
digests TEXT NOT NULL,
|
|
267
|
+
PRIMARY KEY (scope_key, handle, seq)
|
|
213
268
|
) COLLATE utf8mb4_bin`);
|
|
214
269
|
await pool.query(`CREATE TABLE IF NOT EXISTS ${MAILBOX_TOMBSTONE_TABLE} (
|
|
215
270
|
scope_key VARBINARY(190) NOT NULL,
|
|
@@ -247,6 +302,14 @@ export async function ensurePgMailboxSchema(q) {
|
|
|
247
302
|
decision VARCHAR(16) COLLATE "C", -- core 7.57.0 #972b(TiDB twin 同注:留置决定,NULL = 从未 held;只经 moveDecision 移动,随记录亡)
|
|
248
303
|
record_id VARCHAR(${MAILBOX_RECORD_ID_MAX_CHARS}) COLLATE "C", -- core 7.59.0(TiDB twin 同注:生产者铸的记录身份,NULL = 没说身份;盒行锁之内经引擎判,随记录亡)
|
|
249
304
|
PRIMARY KEY (scope_key, handle, seq)
|
|
305
|
+
)`);
|
|
306
|
+
await q(`CREATE TABLE IF NOT EXISTS ${MAILBOX_CONSUMED_TABLE} (
|
|
307
|
+
scope_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
308
|
+
handle VARCHAR(190) COLLATE "C" NOT NULL,
|
|
309
|
+
seq BIGINT NOT NULL,
|
|
310
|
+
record_id VARCHAR(${MAILBOX_RECORD_ID_MAX_CHARS}) COLLATE "C" NOT NULL,
|
|
311
|
+
digests TEXT COLLATE "C" NOT NULL, -- TiDB twin 同注:已收窗(core 7.61.0),四面摘要 JSON
|
|
312
|
+
PRIMARY KEY (scope_key, handle, seq)
|
|
250
313
|
)`);
|
|
251
314
|
await q(`CREATE TABLE IF NOT EXISTS ${MAILBOX_TOMBSTONE_TABLE} (
|
|
252
315
|
scope_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
@@ -299,7 +362,8 @@ export class TiDBMailboxStore {
|
|
|
299
362
|
throw tombstonedRecipientError(scope, handle);
|
|
300
363
|
if (rec.id !== undefined) {
|
|
301
364
|
const [held] = await c.query(`SELECT seq, from_id, content, sent_at_ms, ${jsonColumnText("mysql", "hop_chain")}, ${jsonColumnText("mysql", "peer_meta")}, decision, record_id FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? AND record_id = ? FOR UPDATE`, [scope, handle, rec.id]);
|
|
302
|
-
const
|
|
365
|
+
const [consumed] = await c.query(`SELECT seq, record_id, digests FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = ? AND handle = ? AND record_id = ? FOR UPDATE`, [scope, handle, rec.id]);
|
|
366
|
+
const replay = replayedSeq(held, consumed, scope, handle, this.logger, { ...rec, content: asUtf8Stored(rec.content), ...(rec.from !== undefined ? { from: asUtf8Stored(rec.from) } : {}) });
|
|
303
367
|
if (replay !== undefined)
|
|
304
368
|
return replay;
|
|
305
369
|
}
|
|
@@ -338,7 +402,9 @@ export class TiDBMailboxStore {
|
|
|
338
402
|
const [rows] = (await c.query(`SELECT lease_owner FROM ${MAILBOX_TABLE} WHERE scope_key = ? AND handle = ? FOR UPDATE`, [scope, handle]));
|
|
339
403
|
if (rows.length === 0 || rows[0].lease_owner == null || bufToStr(rows[0].lease_owner) !== owner)
|
|
340
404
|
return { acked: false };
|
|
405
|
+
const [removed] = await c.query(`SELECT seq, from_id, content, sent_at_ms, ${jsonColumnText("mysql", "hop_chain")}, ${jsonColumnText("mysql", "peer_meta")}, decision, record_id FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? AND seq <= ? AND record_id IS NOT NULL ORDER BY seq FOR UPDATE`, [scope, handle, upToSeq]);
|
|
341
406
|
await c.query(`DELETE FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? AND seq <= ?`, [scope, handle, upToSeq]);
|
|
407
|
+
await enterConsumedWindow(async (sql, params) => (await c.query(sql.mysql, params))[0], rowsToMessages(removed, scope, handle, this.logger), scope, handle);
|
|
342
408
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = NULL, lease_expires_at_ms = NULL, lease_max_seq = NULL
|
|
343
409
|
WHERE scope_key = ? AND handle = ? AND lease_max_seq IS NOT NULL AND lease_max_seq <= ?`, [scope, handle, upToSeq]);
|
|
344
410
|
return { acked: true };
|
|
@@ -475,7 +541,8 @@ export class PgMailboxStore {
|
|
|
475
541
|
throw tombstonedRecipientError(scope, handle);
|
|
476
542
|
if (rec.id !== undefined) {
|
|
477
543
|
const { rows: held } = await c.query(`SELECT seq, from_id, content, sent_at_ms, ${jsonColumnText("pg", "hop_chain")}, ${jsonColumnText("pg", "peer_meta")}, decision, record_id FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 AND record_id = $3 FOR UPDATE`, [scope, handle, rec.id]);
|
|
478
|
-
const
|
|
544
|
+
const { rows: consumed } = await c.query(`SELECT seq, record_id, digests FROM ${MAILBOX_CONSUMED_TABLE} WHERE scope_key = $1 AND handle = $2 AND record_id = $3 FOR UPDATE`, [scope, handle, rec.id]);
|
|
545
|
+
const replay = replayedSeq(held, consumed, scope, handle, this.logger, { ...rec, content });
|
|
479
546
|
if (replay !== undefined)
|
|
480
547
|
return replay;
|
|
481
548
|
}
|
|
@@ -511,7 +578,9 @@ export class PgMailboxStore {
|
|
|
511
578
|
const r = (await c.query(`SELECT lease_owner FROM ${MAILBOX_TABLE} WHERE scope_key = $1 AND handle = $2 FOR UPDATE`, [scope, handle]));
|
|
512
579
|
if (r.rows.length === 0 || r.rows[0].lease_owner == null || bufToStr(r.rows[0].lease_owner) !== owner)
|
|
513
580
|
return { acked: false };
|
|
581
|
+
const { rows: removed } = await c.query(`SELECT seq, from_id, content, sent_at_ms, ${jsonColumnText("pg", "hop_chain")}, ${jsonColumnText("pg", "peer_meta")}, decision, record_id FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 AND seq <= $3 AND record_id IS NOT NULL ORDER BY seq FOR UPDATE`, [scope, handle, upToSeq]);
|
|
514
582
|
await c.query(`DELETE FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 AND seq <= $3`, [scope, handle, upToSeq]);
|
|
583
|
+
await enterConsumedWindow(async (sql, params) => (await c.query(sql.pg, params)).rows, rowsToMessages(removed, scope, handle, this.logger), scope, handle);
|
|
515
584
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = NULL, lease_expires_at_ms = NULL, lease_max_seq = NULL
|
|
516
585
|
WHERE scope_key = $1 AND handle = $2 AND lease_max_seq IS NOT NULL AND lease_max_seq <= $3`, [scope, handle, upToSeq]);
|
|
517
586
|
return { acked: true };
|
|
@@ -3,9 +3,16 @@ import { type IndexSpec } from "./ensure-index.js";
|
|
|
3
3
|
/** Table names (single source). SAME names as the TiDB twin (the two dialects never share one DB). */
|
|
4
4
|
export declare const PG_MEMORY_SYNC_TABLES: {
|
|
5
5
|
readonly syncCursor: "agent_memory_engine_sync_cursor";
|
|
6
|
+
readonly syncPartition: "agent_memory_engine_sync_partition";
|
|
6
7
|
readonly pushQueue: "agent_memory_engine_push_queue";
|
|
7
8
|
readonly history: "agent_memory_engine_history";
|
|
8
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* 一个层分区的化身的**唯一铸点**(两只方言孪生同读)。化身说的是「中心此刻答话的是这个分区的哪一份状态」:分区出生时铸一次、存进
|
|
12
|
+
* `agent_memory_engine_sync_partition`,此后每轮同步答它;库被清空重建 ⇒ 那一行随之没了,下一轮再出生 ⇒ 新值。两条契约句(引擎的):
|
|
13
|
+
* (i) 没有别的分区、别的状态带同一个值 —— 随机 UUID(122 位熵);(ii) 不带凭据 —— 客户端的引擎拒带凭据的化身(钉:十六进制与连字符,凭据判官判否)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function mintSyncPartitionIncarnation(): string;
|
|
9
16
|
/** What the caller hands `enqueue` — the transport-neutral unit (§3 传输中立包:payload = the full
|
|
10
17
|
* entry, opaque to this store; this queue never interprets it). */
|
|
11
18
|
export interface PushQueueItem {
|
|
@@ -45,6 +52,11 @@ export interface CentralSyncCursor {
|
|
|
45
52
|
/** The dialect-neutral contract both twins implement (anti-drift: one shared integration suite,
|
|
46
53
|
* test/memory-sync-store-db-integration.test.ts, runs the SAME scenarios against BOTH engines). */
|
|
47
54
|
export interface MemorySyncStore {
|
|
55
|
+
/**
|
|
56
|
+
* 层分区 `scope` 的化身:有 ⇒ 存着的那一只;没有 ⇒ 分区此刻出生(铸点 {@link mintSyncPartitionIncarnation},插入即定;并发的第一次只有一只落定,
|
|
57
|
+
* 两边读回同一只)。中心同步每轮在任何读写之前读它一次、写完之后再读一次(栅栏两端,`memory-sync.ts`)。
|
|
58
|
+
*/
|
|
59
|
+
partitionIncarnation(scope: string): Promise<string>;
|
|
48
60
|
getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
|
|
49
61
|
/** Whole-row replace (round semantics — baseRevs is swapped as one map, never merged). */
|
|
50
62
|
putCursor(cursor: CentralSyncCursor): Promise<void>;
|
|
@@ -67,6 +79,8 @@ export declare function ensurePgMemorySyncSchema(query: PgQueryFn): Promise<void
|
|
|
67
79
|
* ([ref] 车①② 裁定,沿用;不新铸码)。两个孪生共用本函数(MySQL 腿 import 它 —— 一条折码规则一个写者)。
|
|
68
80
|
*/
|
|
69
81
|
export declare function refusingMemorySyncRow<T>(read: () => T): T;
|
|
82
|
+
/** 读回的分区化身(两孪生同读):插入即定之后必有一行、值是非空串;否则响亮抛(不铸替身值 —— 一个读不出的化身没有安全的答法)。 */
|
|
83
|
+
export declare function partitionIncarnationOf(value: unknown, scope: string): string;
|
|
70
84
|
/** Options for {@link PgMemorySyncStore}. */
|
|
71
85
|
export interface PgMemorySyncStoreOptions {
|
|
72
86
|
/** How long a `claimDue` lease holds a row invisible before a crashed worker's claim self-heals.
|
|
@@ -77,6 +91,7 @@ export declare class PgMemorySyncStore implements MemorySyncStore {
|
|
|
77
91
|
private readonly query;
|
|
78
92
|
private readonly claimLeaseMs;
|
|
79
93
|
constructor(query: PgQueryFn, opts?: PgMemorySyncStoreOptions);
|
|
94
|
+
partitionIncarnation(scope: string): Promise<string>;
|
|
80
95
|
getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
|
|
81
96
|
putCursor(cursor: CentralSyncCursor): Promise<void>;
|
|
82
97
|
enqueue(item: PushQueueItem): Promise<void>;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { ensureIndexes } from "./ensure-index.js";
|
|
2
3
|
import { pgSafeJsonStringify, pgProtocolJsonStringify } from "./pg-safe-json.js";
|
|
3
4
|
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
4
5
|
import { decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, jsonColumnText, readNonNullJsonValueColumn, readOpaqueJsonValueColumn, readStringRecordColumn, } from "./sql-json-column.js";
|
|
5
6
|
export const PG_MEMORY_SYNC_TABLES = {
|
|
6
7
|
syncCursor: "agent_memory_engine_sync_cursor",
|
|
8
|
+
syncPartition: "agent_memory_engine_sync_partition",
|
|
7
9
|
pushQueue: "agent_memory_engine_push_queue",
|
|
8
10
|
history: "agent_memory_engine_history",
|
|
9
11
|
};
|
|
12
|
+
export function mintSyncPartitionIncarnation() {
|
|
13
|
+
return randomUUID();
|
|
14
|
+
}
|
|
10
15
|
export const MEMORY_SYNC_INDEXES = [
|
|
11
16
|
{ table: `${PG_MEMORY_SYNC_TABLES.pushQueue}`, name: `idx_${PG_MEMORY_SYNC_TABLES.pushQueue}_next_attempt_ms`, columns: ["next_attempt_at_ms"] },
|
|
12
17
|
];
|
|
@@ -17,6 +22,11 @@ export async function ensurePgMemorySyncSchema(query) {
|
|
|
17
22
|
base_revs jsonb NOT NULL,
|
|
18
23
|
updated_at_ms bigint NOT NULL,
|
|
19
24
|
PRIMARY KEY (scope, peer)
|
|
25
|
+
)`);
|
|
26
|
+
await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.syncPartition} (
|
|
27
|
+
scope varchar(190) COLLATE "C" NOT NULL,
|
|
28
|
+
incarnation varchar(64) COLLATE "C" NOT NULL,
|
|
29
|
+
PRIMARY KEY (scope)
|
|
20
30
|
)`);
|
|
21
31
|
await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.pushQueue} (
|
|
22
32
|
id char(36) COLLATE "C" PRIMARY KEY,
|
|
@@ -37,6 +47,11 @@ export function refusingMemorySyncRow(read) {
|
|
|
37
47
|
throw foldJsonColumn(e, (m) => `memory-sync store: ${m} — refusing rather than serve a sync row nobody can read`);
|
|
38
48
|
}
|
|
39
49
|
}
|
|
50
|
+
export function partitionIncarnationOf(value, scope) {
|
|
51
|
+
if (typeof value === "string" && value.length > 0)
|
|
52
|
+
return value;
|
|
53
|
+
throw new Error(`memory-sync store: ${PG_MEMORY_SYNC_TABLES.syncPartition}.incarnation for scope ${scope} is ${value === undefined ? "missing after insert" : "not a non-empty string"} — refusing rather than answer a sync under an unknown state`);
|
|
54
|
+
}
|
|
40
55
|
function toMs(v) {
|
|
41
56
|
return typeof v === "number" ? v : Number(v);
|
|
42
57
|
}
|
|
@@ -51,6 +66,12 @@ export class PgMemorySyncStore {
|
|
|
51
66
|
}
|
|
52
67
|
this.claimLeaseMs = lease;
|
|
53
68
|
}
|
|
69
|
+
async partitionIncarnation(scope) {
|
|
70
|
+
assertMemoryScopeWidth(scope);
|
|
71
|
+
await this.query(`INSERT INTO ${PG_MEMORY_SYNC_TABLES.syncPartition} (scope, incarnation) VALUES ($1, $2) ON CONFLICT (scope) DO NOTHING`, [scope, mintSyncPartitionIncarnation()]);
|
|
72
|
+
const res = await this.query(`SELECT incarnation FROM ${PG_MEMORY_SYNC_TABLES.syncPartition} WHERE scope = $1`, [scope]);
|
|
73
|
+
return partitionIncarnationOf(res.rows[0]?.incarnation, scope);
|
|
74
|
+
}
|
|
54
75
|
async getCursor(scope, peer) {
|
|
55
76
|
const res = await this.query(`SELECT ${jsonColumnText("pg", "base_revs")}, updated_at_ms FROM ${PG_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = $1 AND peer = $2`, [scope, peer]);
|
|
56
77
|
const row = res.rows[0];
|
|
@@ -5,6 +5,7 @@ import type { Pool } from "mysql2/promise";
|
|
|
5
5
|
* one database). */
|
|
6
6
|
export declare const TIDB_MEMORY_SYNC_TABLES: {
|
|
7
7
|
readonly syncCursor: "agent_memory_engine_sync_cursor";
|
|
8
|
+
readonly syncPartition: "agent_memory_engine_sync_partition";
|
|
8
9
|
readonly pushQueue: "agent_memory_engine_push_queue";
|
|
9
10
|
readonly history: "agent_memory_engine_history";
|
|
10
11
|
};
|
|
@@ -21,6 +22,7 @@ export declare class TiDBMemorySyncStore implements MemorySyncStore {
|
|
|
21
22
|
private readonly pool;
|
|
22
23
|
private readonly claimLeaseMs;
|
|
23
24
|
constructor(pool: Pool, opts?: TiDBMemorySyncStoreOptions);
|
|
25
|
+
partitionIncarnation(scope: string): Promise<string>;
|
|
24
26
|
getCursor(scope: string, peer: string): Promise<CentralSyncCursor | undefined>;
|
|
25
27
|
putCursor(cursor: CentralSyncCursor): Promise<void>;
|
|
26
28
|
enqueue(item: PushQueueItem): Promise<void>;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { historyRowFrom, refusingMemorySyncRow } from "./memory-sync-store-pg.js";
|
|
1
|
+
import { historyRowFrom, mintSyncPartitionIncarnation, partitionIncarnationOf, refusingMemorySyncRow } from "./memory-sync-store-pg.js";
|
|
2
2
|
import { decodeJsonColumn, jsonColumnText, readOpaqueJsonValueColumn, readStringRecordColumn } from "./sql-json-column.js";
|
|
3
3
|
import { ensureIndexes } from "./ensure-index.js";
|
|
4
4
|
import { MEMORY_SYNC_INDEXES, MEMORY_HISTORY_INDEXES, PG_MEMORY_SYNC_TABLES } from "./memory-sync-store-pg.js";
|
|
5
5
|
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
6
6
|
export const TIDB_MEMORY_SYNC_TABLES = {
|
|
7
7
|
syncCursor: "agent_memory_engine_sync_cursor",
|
|
8
|
+
syncPartition: "agent_memory_engine_sync_partition",
|
|
8
9
|
pushQueue: "agent_memory_engine_push_queue",
|
|
9
10
|
history: "agent_memory_engine_history",
|
|
10
11
|
};
|
|
@@ -17,6 +18,11 @@ export async function ensureTiDBMemorySyncSchema(pool) {
|
|
|
17
18
|
base_revs JSON NOT NULL,
|
|
18
19
|
updated_at_ms BIGINT NOT NULL,
|
|
19
20
|
PRIMARY KEY (scope, peer)
|
|
21
|
+
) COLLATE utf8mb4_bin`);
|
|
22
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.syncPartition} (
|
|
23
|
+
scope VARCHAR(190) NOT NULL,
|
|
24
|
+
incarnation VARCHAR(64) NOT NULL,
|
|
25
|
+
PRIMARY KEY (scope)
|
|
20
26
|
) COLLATE utf8mb4_bin`);
|
|
21
27
|
await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.pushQueue} (
|
|
22
28
|
id CHAR(36) NOT NULL,
|
|
@@ -45,6 +51,12 @@ export class TiDBMemorySyncStore {
|
|
|
45
51
|
}
|
|
46
52
|
this.claimLeaseMs = lease;
|
|
47
53
|
}
|
|
54
|
+
async partitionIncarnation(scope) {
|
|
55
|
+
assertMemoryScopeWidth(scope);
|
|
56
|
+
await this.pool.query(`INSERT INTO ${TIDB_MEMORY_SYNC_TABLES.syncPartition} (scope, incarnation) VALUES (?, ?) ON DUPLICATE KEY UPDATE scope = scope`, [scope, mintSyncPartitionIncarnation()]);
|
|
57
|
+
const [rows] = await this.pool.query(`SELECT incarnation FROM ${TIDB_MEMORY_SYNC_TABLES.syncPartition} WHERE scope = ?`, [scope]);
|
|
58
|
+
return partitionIncarnationOf(rows[0]?.incarnation, scope);
|
|
59
|
+
}
|
|
48
60
|
async getCursor(scope, peer) {
|
|
49
61
|
const [rows] = await this.pool.query(`SELECT ${jsonColumnText("mysql", "base_revs")}, updated_at_ms FROM ${TIDB_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = ? AND peer = ?`, [scope, peer]);
|
|
50
62
|
const row = rows[0];
|
|
@@ -194,9 +194,10 @@
|
|
|
194
194
|
* (拒因点名版本与键);不带 ⇒ 载荷零改动读回,读回方按 5 写回原位,启动日志 `config_lkg_migrated` 一行。规则在 `config-lkg.ts` 顶注 formatVersion 5 一段与
|
|
195
195
|
* `migrateV4Lkg`。没有读数键(它不写库)。
|
|
196
196
|
* · **M23 存量 placed 子代行的属主**(7.133.0)—— 7.132.x 及以前两只 SQL 店建委派子代的 placed 行时属主恒 NULL,会话面的属主谓词对 NULL 放行 ⇒ 谁拿到子代的会话 id
|
|
197
|
-
* (父客户端在子代帧的 `transcriptId` 上收到)谁就读得到整段转录。7.133.0
|
|
197
|
+
* (父客户端在子代帧的 `transcriptId` 上收到)谁就读得到整段转录。7.133.0–7.143.x 建行那一笔按本仓委派继承律写属主,7.144.0 起记引擎交的 `placement.owner`
|
|
198
|
+
* (core 7.62.0 [ref];`src/delegated-session-owner.ts` 头注),
|
|
198
199
|
* 属主为 NULL 的 placed 行只对 fleet 调用方放行(无主放行臂的唯一判据 `security.ts` 的 `ownerlessRowOpen`,常设的读面律,不随本条删);存量行 ⇒ **每次起服的幂等回填**,
|
|
199
|
-
*
|
|
200
|
+
* 判据 = 旧律(`judgeDelegatedOwner`,属主 = 判断时见证到的;引擎当年的读数没有落库,读不回):根会话的行在 ⇒ 它的属主(根无主 ⇒ 子代仍无主,不写);根行不在 ⇒ 父会话的行;都不在(孤行)⇒
|
|
200
201
|
* 哨兵属主 `_orphan:<id>`(保留主体前缀,任何已验身份都不等于它 —— 不留 NULL,宁可没人能读),计数 + 起服一行 warn `placed_session_owners_orphaned` 点名条数。记下的根 / 父是空串 ⇒ 读作没有那一格。
|
|
201
202
|
* **单趟**:按 `session_id` 键集分页扫 `placement_kind IS NOT NULL ∧ owner IS NULL` 的行,每一页一条语句连读根 / 父两行(属主、住客、是不是 placed 行;连接键套
|
|
202
203
|
* `NULLIF(…, '')`);回写 = 认领句(`CLAIM_OWNERLESS_PLACED_SQL`:只 CAS 子行自己 —— 扫描读到的化身 + `owner IS NULL` + `placement_kind IS NOT NULL`;扫描与回写之间根 / 父被删、
|
|
@@ -204,10 +205,9 @@
|
|
|
204
205
|
* 化身为空的行(子代或来源行)⇒ 响亮拒(全仓「读到空化身」同一条律)。读数三格:写成的(`placedSessionOwnersInherited` / `placedSessionOwnersOrphaned`)与
|
|
205
206
|
* **残余** `placedSessionOwnersRemaining` = 律这一读判不出、因为它读到的来源本身还是一只无主的委派子代行的行(根已删、父是同样待回填的行,且排在它之后或在同一页里)。
|
|
206
207
|
* 残余非零 ⇒ 读数行 warn;**不拒启、不重扫**:本文件的每一条在窗内每次起服都跑(「窗」一节),残余行只对 fleet 调用方放行(读面律),由之后起服的本条(一次至少再定下一层)
|
|
207
|
-
*
|
|
208
|
-
* **够不到的一格(如实登记)
|
|
209
|
-
*
|
|
210
|
-
* local 带身份的部署升级前先把停驻待批的审批处理完)。窗 = 发布日 + 30 天(与 M19 / M21 同一条论证:
|
|
208
|
+
* 定下(7.144.0 起停驻挂点只比对、不认领)。根无主的子代(律就让它无主)不是残余,每次起服照样被扫到、照样不写。
|
|
209
|
+
* **够不到的一格(如实登记)**:本条只扫 SQL 两店,不碰 local 的会话边档 ⇒ 经晋升腿进 local 宿主店的升级前子代行(普通行)仍无主;它们再次停驻时停驻挂点只比对
|
|
210
|
+
* 引擎交来的属主(复活腿 = 那一行自己的属主 ⇒ 无主照无主比对;随包文字的补偿:local 带身份的部署升级前先把停驻待批的审批处理完)。窗 = 发布日 + 30 天(与 M19 / M21 同一条论证:
|
|
211
211
|
* 窗关时缺省部署上升级前的子代转录已被联合收割按 `BG_AGENT_RETENTION_MS` 收走;调长了留存的部署、滚动升级里旧副本在窗内新写的行如实登记,不为它们加长窗)。
|
|
212
212
|
* · **M24 workflow journal 的属主 scope 搬家**(7.133.0)—— 7.132.x 及以前 `workflow_journal` 的 scope 是**每一行**上的一列:读面按它判,写面(按
|
|
213
213
|
* `(run_id, ordinal)` 的 upsert)从不判它 ⇒ 别的 scope 的 append 落新序数插进一条 run、落既有序数覆盖属主的结果。7.133.0 起一条 run 的属主 scope 只记在
|
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
* · **local 车道**:没有绑定表、没有 `session_meta`(device 车道只在 SQL 后端)⇒ 只有 ④ 与回填前提:读本地 run 店有 run 的会话,执行席读出 absent 的 ⇒ 经同一只席 `place` 成 writer(表唯一项)
|
|
311
311
|
* (`LocalBackend.runOneTimeMigrations`);local 没有「切换收尾」的形可读 ⇒ 窗内每次起服都判,首跑之后它只碰认领与放置之间被打断的会话(与 `place` 会落下的格同值)。
|
|
312
312
|
* 有 run、而**存档读不出**的会话 —— 会话元数据格缺(升级链上从没被再读过的老会话)而探针读不出,或 fork 创建的格而探针说不出存档在不在 ——:回填跳过它并点名
|
|
313
|
-
* (读数 `executionPlacementSessionsUnreadable` + 一行一条 warn `execution_placement_session_unreadable`,带会话 id 与故障词〔闭集 `corrupt_session` / `
|
|
313
|
+
* (读数 `executionPlacementSessionsUnreadable` + 一行一条 warn `execution_placement_session_unreadable`,带会话 id 与故障词〔闭集 `corrupt_session` / `storage`;
|
|
314
314
|
* `storage` = 读存档文件时抛了别的故障:权限 / 磁盘 I/O / 超大,再带 `errno`〕),**不拒启**、一个字节没写 —— 回填只补一格放置、不动存档,读不出的存档不归它判
|
|
315
315
|
* (这一支只收**探针对这只会话的读故障**;会话元数据边档本身读不出、问账日志压实失败等整库级故障照旧拒启);该会话的请求照旧答存档的故障,存档修好(从留存副本恢复,
|
|
316
316
|
* 或 `storage` 时权限 / 磁盘修好)后的下一次起服照常补放置;存档移走则它不再是本服务的会话(不补放置、不再点名;fork 创建的会话例外 —— 它的格还在,读作 fork 未落定,出路是同键重试那次 fork 或删除该会话)。
|
|
@@ -338,6 +338,25 @@
|
|
|
338
338
|
* 起服迁移的 local 腿(`FileApprovalAskStore.upgradeJournalEnvelope`)一次压实把整本重写成 v2。
|
|
339
339
|
* **回滚**:7.141.x 对改名后的表每一条读写都响亮失败、对 v2 日志拒启 —— 回滚的唯一支持形 = 恢复升级前的备份,或手工把列改回(MIGRATION 写了语句)。
|
|
340
340
|
* 读数:`askCauseColumnsChanged`(0–2 处表形改动)/ `askParkTriggersMoved`(SQL;local 恒 0)/ `askJournalRecordsUpgraded`(local;SQL 恒 0)。
|
|
341
|
+
* · **M35 配置根上 env 模型表的决定(settings-schema SS-M11)**(7.143.0)—— 不在店里(与 M16 同为本地配置根上的起服迁移):本版起本地配置腿上,一份管模型面的
|
|
342
|
+
* `config.d/models.json` 在 env 模型表已声明时,只有配置根上的决定文档 `config.env-models.json` 读作 `replaced` 才被采纳(否则模型面组拒
|
|
343
|
+
* `config.catalog_replaces_env_models`,`env-model-table-gate.ts`);升级前这种目录静默顶替 env 表。本条保住升级前的读法:起服、本地配置第一次读之前
|
|
344
|
+
* (`config-root-migration.ts`,两个入口同调),调契约的 `migrateEnvModelsDecision` —— 先读决定与目录,再独占创建运行记录 `config.migration.SS-M11.json`
|
|
345
|
+
* (建成的进程是本根唯一的铸造者),决定缺席且目录管模型面才铸 `replaced`;运行记录在场之后决定缺席 ≡ `kept`。没有读数键(它不写库)。
|
|
346
|
+
*
|
|
347
|
+
* · **M36 引擎 7.61.0 之前导入的、在每个项目根放行的全局 allow 退役**(7.143.0,引擎 7.61.0)—— 更早的导入门把用户层里从 `/` 写起的路径规则存成全局行:全局行在每个任务的
|
|
348
|
+
* 项目根读 `/x`,用户层在它自己的配置目录读,同一串字落店之后放行的是另一个地方。引擎不迁、不在读侧丢,只给一句判据(INTEGRATION-CORE §5.14):行是 allow ∧ add 的出处是
|
|
349
|
+
* `imported-cc` ∧ scope 是 global ∧ `ruleBasesNeeded` 点名 `root` ⇒ 这只 add 退役;按 add、不按行(每只选中的行一只 observed-remove 墓碑,`removedDots` 恰是选中的 add,
|
|
350
|
+
* 人亲手同意过的 `user` add 留着);deny / ask 不退役。判据、读哪些桶、写在哪里与失败方向的唯一属主是 `imported-layer-root-retirement.ts`(头注逐条)—— 两腿同调一只函数:
|
|
351
|
+
* SQL 在本段握锁的那一条连接上(`SqlDurableRulePartitionProvider` 收这条连接,墓碑经桶的写面 = 店内唯一写法 `writeBucket` 落),排在 M11 之后;local 在
|
|
352
|
+
* `LocalBackend.runOneTimeMigrations` 里经引擎 File 分区的写面落(规则目录不在 ⇒ 没有行、零读,不为它建目录)。读的桶:SQL = 规则表里的每一只;File = 导入记录点名过的属主(引擎的 File 分区后端没有列桶口;缺口见那份头注)。
|
|
353
|
+
* 每次起服都跑(窗内;更早的引擎回滚上来会重新铸出这种 add),第二跑起零选零写。读数两键:退役了的行(一行一条 warn,不带规则正文)与没能读 / 写的桶(一桶一条 warn,
|
|
354
|
+
* 每次起服都报)。删码另有一个前提:不再支持回滚到引擎 7.61.0 之前(登记表 M36 的 removal)。
|
|
355
|
+
*
|
|
356
|
+
* · **M37 存卡信封上委派出处的旧键(只有读侧)**(7.143.0,引擎 7.61.0)—— 引擎把审批请求的发起会话键 `sourceTaskId` 改名 `sourceSessionId`(值不变),本服务的卡随改、零别名;
|
|
357
|
+
* 本版之前铸的卡信封(`approval_ask.card_json` / local 问账)上那一格还是旧名,`.strict()` 的卡 schema 只认新名。存卡的唯一读者入口 `readStoredCardEnvelope`
|
|
358
|
+
* 在 M18 的读侧改写与 schema 之前把它改名(`approval-card.ts` 的 `renameRetiredSourceKey`,交浅拷贝、零写回,响亮计数)。不起服改写:存卡的寿命以分钟计、
|
|
359
|
+
* 升级前结清在途审批是部署前置(MIGRATION 本版第 17 条),读侧接住的是滚动升级窗里旧副本新写的与升级前已结仍被读的那几行。没有读数键。
|
|
341
360
|
*
|
|
342
361
|
* ## 收据账(一次起服一只,每一条迁移同一只)
|
|
343
362
|
* 每一步把它**已提交**的改写 / 删除与点名读数当场记进同一只收据账({@link MigrationReceipts}):计数随每次提交累加、行随每次提交追加、形读数在那一步落定时写。走完 ⇒
|
|
@@ -357,9 +376,10 @@ import type { Pool as PgPool } from "pg";
|
|
|
357
376
|
import { type AllowEntryInertReason, type GateKind } from "@sema-agent/core";
|
|
358
377
|
import { type SqlDialect } from "./sql-driver.js";
|
|
359
378
|
import { StoreBootRefusalError } from "./store-contracts.js";
|
|
360
|
-
import { type UndurableScopeRuleRow, type UnreadableRuleBucket } from "./permission-rule-store-sql.js";
|
|
379
|
+
import { listRuleBucketOwners, type UndurableScopeRuleRow, type UnreadableRuleBucket } from "./permission-rule-store-sql.js";
|
|
361
380
|
import type { SchemaProbeRead } from "./schema-live-columns.js";
|
|
362
381
|
import type { ExecutionLane } from "../execution-lane-caps.js";
|
|
382
|
+
import { type ImportedLayerRootAllowRetired, type ImportedLayerRootBucketSkipped, type ImportedLayerRootRetirementSource } from "./imported-layer-root-retirement.js";
|
|
363
383
|
import { type LocalPlacementBackfillSeat, type UnusableArchiveWord } from "../execution-placement.js";
|
|
364
384
|
/** 迁移读数(启动日志一行写全;测试按它断言,不靠日志文本)。 */
|
|
365
385
|
export interface OneTimeMigrationReport {
|
|
@@ -410,7 +430,7 @@ export interface OneTimeMigrationReport {
|
|
|
410
430
|
* 行数(SQL 独有;local 恒 0)。非零 ⇒ 另一行 warn 点名条数。 */
|
|
411
431
|
readonly placedSessionOwnersOrphaned: number;
|
|
412
432
|
/** M23:这一次起服留着无主的存量 placed 子代行 —— 律这一读判不出,因为它读到的来源本身还是一只无主的委派子代行(根已删、父待回填)。它们只对 fleet 调用方
|
|
413
|
-
* 放行(读面律)
|
|
433
|
+
* 放行(读面律),由之后起服的本条定下。根无主的子代(律就让它无主)不算。行数(SQL 独有;local 恒 0)。非零 ⇒ 读数行 warn(不拒启)。 */
|
|
414
434
|
readonly placedSessionOwnersRemaining: number;
|
|
415
435
|
/** M24:workflow journal 的属主 scope —— `moved` = 本次把行上的 scope 列搬进 `workflow_journal_owner`(回填 + 删跨 scope 行 + 删列);`current` = 早已是新形
|
|
416
436
|
* (新库 / 第二跑:`workflow_journal` 上没有 scope 列);`file-store` = local 车道(core 的 File 店,属主 = 每条 run 的 journal 文件首行)。 */
|
|
@@ -462,6 +482,11 @@ export interface OneTimeMigrationReport {
|
|
|
462
482
|
readonly askParkTriggersMoved: number;
|
|
463
483
|
/** M34 local 腿:问账日志里信封号 1 的记录数(窗内读侧已按意义译读;本次一次压实把整本重写成 v2)。非零 = 真重写了日志。local 独有;SQL 恒 0。 */
|
|
464
484
|
readonly askJournalRecordsUpgraded: number;
|
|
485
|
+
/** M36:本次退役了引擎 7.61.0 之前导入的全局 `/x` allow add 的行(一行一条:桶键 / 工具名 / 退役的 add 数 / 这一行是否还活着;不带规则正文),启动日志逐行 warn。
|
|
486
|
+
* SQL 与 local 都有(同一只函数,见 `imported-layer-root-retirement.ts`)。非空 = 真写了墓碑;第二跑起恒空。 */
|
|
487
|
+
readonly importedLayerRootAllowsRetired: readonly ImportedLayerRootAllowRetired[];
|
|
488
|
+
/** M36:这次没能读 / 没能写的桶(闭集四词:读不出 / 写它的那一步里桶被并发删掉 / 没有写面或写面拒 / 并发争不过),一桶一条 warn;后两种那几行照旧放行,每次起服都点名直到处置。 */
|
|
489
|
+
readonly importedLayerRootBucketsSkipped: readonly ImportedLayerRootBucketSkipped[];
|
|
465
490
|
}
|
|
466
491
|
/** 退役的两只命令桶(引擎 7.51.0 起不是桶;只给 M17 点名认)。 */
|
|
467
492
|
export type RetiredSessionRuleBucket = "commandAllow" | "commandDeny";
|
|
@@ -503,7 +528,7 @@ export interface ParkedApprovalCardRowTooLong extends ParkedApprovalCardRowUnrea
|
|
|
503
528
|
readonly bytes: number;
|
|
504
529
|
}
|
|
505
530
|
/**
|
|
506
|
-
* M32 local 腿被跳过的一只会话:会话 id 是点名坐标;故障词是闭集 {@link UnusableArchiveWord}(`corrupt_session` =
|
|
531
|
+
* M32 local 腿被跳过的一只会话:会话 id 是点名坐标;故障词是闭集 {@link UnusableArchiveWord}(`corrupt_session` = 会话头读不出,或读得出而日志走不成一棵树;
|
|
507
532
|
* `storage` = 读的时候抛了别的故障,I/O 级 —— 权限 / 磁盘 / 超大,此时 `errno` 带那只故障的 `code`)。盘上路径与故障句不出店。
|
|
508
533
|
*/
|
|
509
534
|
export interface ExecutionPlacementSessionUnreadable {
|
|
@@ -601,7 +626,7 @@ export declare function migrationWindowOpen(window: Pick<OneTimeMigrationWindow,
|
|
|
601
626
|
*/
|
|
602
627
|
export declare function migrationWindowEnd(id: string): string;
|
|
603
628
|
/** local 车道上 SQL 独有的几条(M8 / M9 / M11 / M17 / M18 / M21 / M23 / M24 / M26 / M27 / M29)的读数(M6 与 M32 的回填两车道都有、M33 只在 local,local 腿的读数由 `LocalBackend.runOneTimeMigrations` 现算):那几张表 / 列在文件底座上从来不存在 ⇒ 如实答 absent / 0。 */
|
|
604
|
-
export declare const NO_SQL_MIGRATIONS: Omit<OneTimeMigrationReport, "holderDeathCodeRowsBackfilled" | "executionPlacementsBackfilled" | "askExecutionLanesBackfilled" | "executionPlacementSessionsUnreadable" | "localArchiveRowFactsBackfilled" | "localArchivesAnonymized" | "localArchivesKeptStamped" | "localArchivesUnreadable" | "localArchiveSnapshotsRecovered" | "askJournalRecordsUpgraded">;
|
|
629
|
+
export declare const NO_SQL_MIGRATIONS: Omit<OneTimeMigrationReport, "holderDeathCodeRowsBackfilled" | "executionPlacementsBackfilled" | "askExecutionLanesBackfilled" | "executionPlacementSessionsUnreadable" | "localArchiveRowFactsBackfilled" | "localArchivesAnonymized" | "localArchivesKeptStamped" | "localArchivesUnreadable" | "localArchiveSnapshotsRecovered" | "askJournalRecordsUpgraded" | "importedLayerRootAllowsRetired" | "importedLayerRootBucketsSkipped">;
|
|
605
630
|
/** 启动期的日志口(logger 的子集)。两级各有其用,见 {@link logOneTimeMigrations}。 */
|
|
606
631
|
export interface MigrationLogSink {
|
|
607
632
|
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
@@ -634,6 +659,11 @@ export interface MigrationDb {
|
|
|
634
659
|
* 本函数**不问**退役 schema:带着它的库在更早处就被 `ensureSchema()` 自身拒掉了(起服对账一族 `schema-consistency.ts` 的探针口,先于建表);走公开序列
|
|
635
660
|
* `createStoreBackend()` → `ensureSchema()` → `runOneTimeMigrations()` 的嵌入方与本服务的起服序列一样,有这一问、零动作。
|
|
636
661
|
*/
|
|
662
|
+
/**
|
|
663
|
+
* M36 的 SQL 腿交给退役步的那一只源(读哪些桶 = 规则表里的每一只 · 桶的 durable 分区后端 · 店的判官):起服序列(握锁的那一条连接)与真库钉同一只构造,
|
|
664
|
+
* 判官不在两处各写一份。
|
|
665
|
+
*/
|
|
666
|
+
export declare function createSqlImportedLayerRootSource(exec: Parameters<typeof listRuleBucketOwners>[0]): ImportedLayerRootRetirementSource;
|
|
637
667
|
export declare function runSqlOneTimeMigrations(dialect: SqlDialect, pool: MysqlPool | PgPool, input: ExecutionPlacementSwitchInput): Promise<OneTimeMigrationReport>;
|
|
638
668
|
/**
|
|
639
669
|
* M34 —— 问账行成因两列(判据与形见头注 M34 段)。三步,每一步先读形再判,DDL 按后置条件收敛({@link execUntil}),中断 ⇒ 下一次起服从首个未满足的后置条件续:
|
|
@@ -816,13 +846,13 @@ export declare function repairParkedRows(db: MigrationDb, dialect: SqlDialect, r
|
|
|
816
846
|
export declare function backfillParkedApprovalCards(db: MigrationDb, dialect: SqlDialect, receipts: MigrationReceipts): Promise<void>;
|
|
817
847
|
/**
|
|
818
848
|
* M23 —— 存量 placed 子代行的属主回填,**每次起服单趟、幂等**(规则、为什么、够不到的一格见头注 M23 段):键集分页扫完 placed ∧ 属主 NULL 的行,每一行按唯一判据判
|
|
819
|
-
* (`judgeDelegatedOwner
|
|
820
|
-
* · 判出属主 ⇒ {@link CLAIM_OWNERLESS_PLACED_SQL}(只 CAS
|
|
849
|
+
* (`judgeDelegatedOwner`,旧律:来源顺序根、父;属主 = 这一读见证到的):
|
|
850
|
+
* · 判出属主 ⇒ {@link CLAIM_OWNERLESS_PLACED_SQL}(只 CAS 子行自己,绑扫描读到的化身;第二跑零命中);写成的按属主计进
|
|
821
851
|
* `placedSessionOwnersOrphaned`(谁都不匹配:孤行哨兵,或继承自孤行祖先的同一只哨兵)或 `placedSessionOwnersInherited`,每一次提交当场记进收据账;零命中(这一行在扫描之后
|
|
822
852
|
* 被写过 / 删过 / 换了住客)⇒ 不计,下一次起服按那时的律重判;
|
|
823
853
|
* · 判出无主:读到的来源本身是一只 placed 行(还没定下的委派子代行)⇒ 计进 `placedSessionOwnersRemaining`(残余);来源是普通会话(根无主)⇒ 律就让它无主,不计;
|
|
824
854
|
* · 扫到的任何一行化身为空 ⇒ 响亮拒(不论判不判得出属主;来源行同一条律)。
|
|
825
|
-
*
|
|
855
|
+
* 不重扫、不拒启:残余由下一次起服定下,期间只对 fleet 调用方放行(头注 M23 段)。
|
|
826
856
|
*/
|
|
827
857
|
export declare function inheritPlacedSessionOwners(db: MigrationDb, dialect: SqlDialect, receipts: MigrationReceipts): Promise<void>;
|
|
828
858
|
/**
|