@sema-agent/server 7.151.0 → 7.152.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 +266 -0
- package/README.md +15 -3
- package/README.zh-CN.md +10 -2
- package/USAGE.md +93 -64
- package/deploy/sema-up/README.md +5 -4
- package/deploy/sema-up/cluster-up.sh +6 -6
- package/deploy/sema-up/kube-up.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +36 -8
- package/deploy/sema-up/machines.example.yaml +3 -1
- package/deploy/sema-up/sema-up.sh +22 -16
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-ask-machine.d.ts +4 -10
- package/dist/approval-ask-retention.d.ts +1 -1
- package/dist/approval-card.d.ts +10 -12
- package/dist/approval-gate-currency.d.ts +8 -8
- package/dist/approval-gate-currency.js +3 -4
- package/dist/approval-hmac.d.ts +17 -1
- package/dist/approval-hmac.js +7 -1
- package/dist/approval-queue-kind.d.ts +1 -1
- package/dist/approval.d.ts +2 -2
- package/dist/approval.js +2 -2
- package/dist/bind-host.d.ts +1 -3
- package/dist/bind-host.js +2 -3
- package/dist/boot/coordinators.d.ts +1 -1
- package/dist/boot/coordinators.js +9 -10
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/execution-env.d.ts +1 -0
- package/dist/boot/execution-env.js +31 -5
- package/dist/boot/leader.d.ts +31 -30
- package/dist/boot/leader.js +38 -28
- package/dist/boot/leg-path-adjudication.d.ts +40 -0
- package/dist/boot/leg-path-adjudication.js +18 -0
- package/dist/boot/parked-revive-gate.js +3 -15
- package/dist/boot/reapers.d.ts +1 -1
- package/dist/boot/reapers.js +1 -1
- package/dist/boot/resolve-spec.js +12 -26
- package/dist/boot/runner-deps.d.ts +39 -35
- package/dist/boot/runner-deps.js +92 -89
- package/dist/boot/shutdown.d.ts +6 -0
- package/dist/boot/shutdown.js +10 -4
- package/dist/boot/stage-02-web-admin-pull.js +4 -5
- package/dist/boot/stage-05-execution-env.js +2 -1
- package/dist/boot/stage-06-runners.d.ts +2 -0
- package/dist/boot/stage-06-runners.js +10 -4
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +8 -0
- package/dist/boot/stage-10-http-server.js +11 -15
- package/dist/boot/stores.js +7 -4
- package/dist/boot/web-admin.js +2 -7
- package/dist/capabilities/approvals-push.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +28 -13
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capability-facts.d.ts +22 -20
- package/dist/capability-facts.js +18 -34
- package/dist/card-rule-offers.d.ts +32 -0
- package/dist/card-rule-offers.js +130 -0
- package/dist/config-catalog.js +12 -11
- package/dist/config-dir-trust.js +4 -2
- package/dist/config-invariants.d.ts +18 -7
- package/dist/config-invariants.js +51 -8
- package/dist/config-lkg.d.ts +5 -19
- package/dist/config-lkg.js +8 -62
- package/dist/config-provider.d.ts +1 -1
- package/dist/config-root-migration.d.ts +2 -2
- package/dist/config-types.d.ts +28 -38
- package/dist/config.d.ts +7 -5
- package/dist/config.js +55 -16
- package/dist/control-socket.d.ts +51 -0
- package/dist/control-socket.js +115 -0
- package/dist/deployment-governance.d.ts +2 -1
- package/dist/device-ws-hub.d.ts +7 -0
- package/dist/device-ws-hub.js +7 -1
- package/dist/elicitation.js +2 -2
- package/dist/entry-problems.d.ts +40 -3
- package/dist/entry-problems.js +3 -1
- package/dist/execution-env-refusal.d.ts +8 -2
- package/dist/execution-env-refusal.js +3 -0
- package/dist/fleet/engine-signal.d.ts +12 -29
- package/dist/fleet/engine-signal.js +1 -7
- package/dist/fleet-client.js +2 -5
- package/dist/gate-settlement.d.ts +1 -8
- package/dist/gate-settlement.js +0 -4
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +2 -3
- package/dist/hitl-delivery.js +0 -2
- package/dist/hooks/hook-llm.js +4 -2
- package/dist/hooks/hook-runner.d.ts +3 -2
- package/dist/hooks/hook-runner.js +16 -1
- package/dist/host-decision.d.ts +11 -11
- package/dist/http/active-run-conflict.d.ts +35 -2
- package/dist/http/active-run-conflict.js +48 -19
- package/dist/http/approval-sweeps.d.ts +1 -1
- package/dist/http/approval-sweeps.js +5 -2
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +6 -2
- package/dist/http/dispatch.js +25 -5
- package/dist/http/durable-run-leg.js +0 -1
- package/dist/http/health-reader.d.ts +16 -9
- package/dist/http/health-reader.js +2 -1
- package/dist/http/health-self-report.js +1 -1
- package/dist/http/owner-access.d.ts +2 -3
- package/dist/http/owner-access.js +3 -4
- package/dist/http/principal-gate.d.ts +49 -21
- package/dist/http/principal-gate.js +61 -11
- package/dist/http/request-channel.d.ts +23 -0
- package/dist/http/request-channel.js +9 -0
- package/dist/http/request-guards.js +17 -2
- package/dist/http/resume-ctx-read.js +20 -3
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +111 -41
- package/dist/http/route-ctx.d.ts +73 -7
- package/dist/http/route-ctx.js +17 -0
- package/dist/http/route-table.d.ts +4 -0
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/admin-config-refresh.js +3 -3
- package/dist/http/routes/admin-drain.d.ts +3 -0
- package/dist/http/routes/admin-drain.js +7 -7
- package/dist/http/routes/admin-memory-optout.js +3 -3
- package/dist/http/routes/adoption.js +3 -5
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +95 -48
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +30 -5
- package/dist/http/routes/config-catalog.js +2 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +19 -7
- package/dist/http/routes/images.js +10 -10
- package/dist/http/routes/memory-bundle.js +3 -3
- package/dist/http/routes/memory-compliance.js +3 -3
- package/dist/http/routes/memory-consolidation.js +2 -2
- package/dist/http/routes/memory-origin.js +3 -3
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/observability.js +3 -3
- package/dist/http/routes/retention-ops.js +3 -3
- package/dist/http/routes/rules.js +5 -5
- package/dist/http/routes/run-permission-mode.d.ts +2 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +4 -9
- package/dist/http/routes/runs.js +73 -33
- package/dist/http/routes/session-sync.d.ts +18 -6
- package/dist/http/routes/session-sync.js +38 -36
- package/dist/http/routes/sessions.js +20 -9
- package/dist/http/routes/tasks.js +28 -37
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/server.d.ts +9 -3
- package/dist/http/server.js +15 -3
- package/dist/http/submit-ctx.d.ts +1 -1
- package/dist/http/submit-ctx.js +2 -0
- package/dist/human-facing-args.d.ts +33 -0
- package/dist/human-facing-args.js +33 -0
- package/dist/index.d.ts +1 -1
- package/dist/k8s-api-credentials.d.ts +29 -0
- package/dist/k8s-api-credentials.js +25 -0
- package/dist/lane-home-probe.d.ts +1 -1
- package/dist/lane-home-probe.js +2 -1
- package/dist/leader/diffup.d.ts +19 -0
- package/dist/leader/diffup.js +7 -0
- package/dist/leader/leader.d.ts +114 -10
- package/dist/leader/leader.js +69 -5
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +2 -1
- package/dist/leader/run-entry.js +1 -1
- package/dist/leader/wire.d.ts +42 -132
- package/dist/leader/wire.js +81 -67
- package/dist/memory-scope.d.ts +20 -9
- package/dist/memory-scope.js +15 -3
- package/dist/model-entry-headers.d.ts +9 -10
- package/dist/model-entry-headers.js +0 -4
- package/dist/model-entry-refusal.d.ts +7 -4
- package/dist/model-entry-refusal.js +11 -5
- package/dist/model-plane-face.d.ts +1 -1
- package/dist/model-plane-face.js +3 -1
- package/dist/model-route-endpoint.d.ts +13 -7
- package/dist/model-route-endpoint.js +14 -11
- package/dist/model-select.d.ts +22 -0
- package/dist/model-select.js +9 -0
- package/dist/model-unreachable-notice.d.ts +11 -0
- package/dist/model-unreachable-notice.js +4 -4
- package/dist/object-store-buckets.d.ts +640 -6
- package/dist/object-store-buckets.js +166 -0
- package/dist/observability/fail-open.d.ts +20 -16
- package/dist/observability/fail-open.js +20 -16
- package/dist/observability/logger.js +3 -1
- package/dist/operator-roster.d.ts +68 -0
- package/dist/operator-roster.js +32 -0
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/outbound-dispatcher.d.ts +71 -8
- package/dist/outbound-dispatcher.js +48 -7
- package/dist/owner-faces.d.ts +1 -1
- package/dist/parked-decide.d.ts +2 -2
- package/dist/permission-mode-admission.d.ts +59 -26
- package/dist/permission-mode-admission.js +37 -10
- package/dist/plugins/approval-ask-store-file.d.ts +4 -0
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
- package/dist/plugins/approval-ask-store-memory.js +7 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
- package/dist/plugins/approval-ask-store-sql.js +12 -1
- package/dist/plugins/blob-backend.d.ts +83 -7
- package/dist/plugins/blob-backend.js +57 -6
- package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
- package/dist/plugins/checkpoint-index-columns.js +3 -6
- package/dist/plugins/checkpoint-store-face.d.ts +119 -61
- package/dist/plugins/checkpoint-store-face.js +134 -55
- package/dist/plugins/landed-attendants.d.ts +2 -2
- package/dist/plugins/local-checkpoint-store.d.ts +13 -3
- package/dist/plugins/local-checkpoint-store.js +38 -49
- package/dist/plugins/one-time-migrations.d.ts +51 -21
- package/dist/plugins/one-time-migrations.js +67 -45
- package/dist/plugins/pg-pool.js +11 -9
- package/dist/plugins/remote-env-host.d.ts +29 -10
- package/dist/plugins/remote-env-host.js +99 -19
- package/dist/plugins/remote-env-k8s.d.ts +68 -2
- package/dist/plugins/remote-env-k8s.js +107 -24
- package/dist/plugins/replica-registration.d.ts +1 -1
- package/dist/plugins/replica-registration.js +1 -1
- package/dist/plugins/s3-presign.d.ts +23 -0
- package/dist/plugins/s3-presign.js +37 -3
- package/dist/plugins/scheduler-support.d.ts +2 -2
- package/dist/plugins/scheduler-support.js +0 -3
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-file-ledger.d.ts +2 -2
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/send-user-file.d.ts +3 -4
- package/dist/plugins/send-user-file.js +56 -52
- package/dist/plugins/session-blob-claim-token.d.ts +15 -0
- package/dist/plugins/session-blob-claim-token.js +20 -0
- package/dist/plugins/session-blob-namespace.d.ts +46 -0
- package/dist/plugins/session-blob-namespace.js +116 -0
- package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
- package/dist/plugins/snapshot-bucket-presence.js +8 -2
- package/dist/plugins/sql-json-column.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +44 -2
- package/dist/plugins/store-backend.js +75 -53
- package/dist/plugins/task-attachment-store.d.ts +2 -1
- package/dist/plugins/task-attachment-store.js +5 -1
- package/dist/plugins/tidb-pool.js +19 -24
- package/dist/plugins/workspace-custody.d.ts +4 -3
- package/dist/question.d.ts +2 -1
- package/dist/question.js +5 -13
- package/dist/request-form.d.ts +6 -1
- package/dist/request-form.js +4 -1
- package/dist/rule-issue-reading.d.ts +1 -1
- package/dist/rules-consent.d.ts +1 -1
- package/dist/run-local.js +8 -9
- package/dist/runs.d.ts +17 -8
- package/dist/runs.js +15 -2
- package/dist/runtime-governance.d.ts +2 -2
- package/dist/sema-config-dir.d.ts +2 -1
- package/dist/session-sync.d.ts +27 -11
- package/dist/session-sync.js +15 -7
- package/dist/stores-sql/blob-index-sql.d.ts +30 -9
- package/dist/stores-sql/blob-index-sql.js +40 -21
- package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
- package/dist/stores-sql/checkpoint-store-sql.js +70 -146
- package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
- package/dist/stores-sql/file-history-store-sql.js +100 -28
- package/dist/stores-sql/retention-store-sql.js +3 -9
- package/dist/task-settings.d.ts +6 -5
- package/dist/task-settings.js +3 -3
- package/dist/tool-approval.d.ts +342 -518
- package/dist/tool-approval.js +498 -1207
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/ledger-events.d.ts +3 -3
- package/dist/trace/project.d.ts +2 -3
- package/dist/trace/redact.js +3 -2
- package/dist/trace/self-minted-wire.d.ts +287 -157
- package/dist/trace/self-minted-wire.js +93 -48
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/read-warnings.js +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/dist/workspace-space.d.ts +10 -4
- package/dist/workspace-space.js +4 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
- package/package.json +6 -2
- package/scripts/embed-minimal.mjs +3 -3
- package/wire-changes/server@7.152.0.json +900 -0
- package/dist/config-d-migration.d.ts +0 -37
- package/dist/config-d-migration.js +0 -60
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* 先 `SELECT` 后 `UPDATE`(设计定稿 §4 原话)。
|
|
29
29
|
* - JSON 列(`card_json`/`decision_actor`)走 `dialectJsonEncoder`(TiDB verbatim `JSON.stringify`;
|
|
30
30
|
* PG 走 `pgSafeJsonStringify` 深度 sanitize——这两列是「已帽已洗后的投影」内容面 blob,不是像
|
|
31
|
-
* checkpoint
|
|
31
|
+
* checkpoint 列里待决动作的 `args` 那样跨信任边界做 hash 绑定的执行面字节,故用有损而非无损信封)。
|
|
32
32
|
*
|
|
33
33
|
* ── 施工偏离记录(设计定稿字面矛盾,未擅自改设计,如实记在这里 + 车1 最终汇报)──────────────────────
|
|
34
34
|
* - deriveAskId/deriveBatchId 的分隔符:设计定稿 §5 代码片段字面写 `.join(' ')`(单空格),但同一段
|
|
@@ -497,11 +497,16 @@ export declare function askRowOnCall(row: AskRow, call: AskCallCoordinate): bool
|
|
|
497
497
|
* 🔴 [ref] —— **决议作用域的属主维**:这一行此刻带着的属主,是不是这次决议的门判过的那一位(逐字相等;`null` 只等于 `null`)。
|
|
498
498
|
*
|
|
499
499
|
* 律(一条):一次决议请求的作用域 = (ask, 批, **门判过的那位属主**)。属主的唯一来源是行上的 `owner` 列(铸行写定,只有 `/v1/adoption` 改写它);
|
|
500
|
-
* 门(面表 `owner-faces.ts` 的 ask
|
|
500
|
+
* 门(面表 `owner-faces.ts` 的 ask 两行:决断口 `ask.decide` 属主 ∨ 无主 ∨ 运维、按问读口 `ask.answer` 属主 ∨ 无主)照旧在门里判授权,本谓词**不复判授权**,只绑「门判的那一份属主事实」——
|
|
501
501
|
* 门读行与 CAS 之间属主被改写,门的判断就不再是关于这一行的判断。于是这次请求拿到的**每一份**行读数(CAS 本身、CAS 输者的读数、
|
|
502
502
|
* 提交歧义的确认读、幂等回放的命中)都过这一只:出域 = 缺席,门按缺席答(与行不在同体同码)。
|
|
503
503
|
* InMemory twin 的判定、SQL twin 输者读数的投影、两扇门的确认读读同一只;SQL twin 的 UPDATE 谓词是它的方言镜像({@link decideOwnerWhere})。
|
|
504
504
|
*/
|
|
505
|
+
/** {@link ApprovalAskStore.listAsksAtGate} 的行判据(三 twin 的出口对齐与 InMemory 内核同调这一只)。 */
|
|
506
|
+
export declare function askAtGate(r: AskRow, sourceSessionId: string, binding: {
|
|
507
|
+
readonly boundCallId: string;
|
|
508
|
+
readonly boundInputHash: string;
|
|
509
|
+
}): boolean;
|
|
505
510
|
export declare function askRowCarriesOwner(row: AskRow | null, owner: string | null): row is AskRow;
|
|
506
511
|
/** [ref]:决议输者读数的投影(三 twin 共用一份):行出了这次决议的属主域 ⇒ 读作缺席,不交出别人的行。 */
|
|
507
512
|
export declare function projectDecideReading(row: AskRow | null, owner: string | null): AskRow | null;
|
|
@@ -617,6 +622,21 @@ export interface ApprovalAskStore {
|
|
|
617
622
|
* 行数随在途停驻,不随账本总量)。三 twin 同答:SQL twin 读后按发起会话逐字再滤一遍(MySQL 协议 VARCHAR 等值的 PAD SPACE,与 `listByCall` 同一条出口对齐)。
|
|
618
623
|
*/
|
|
619
624
|
listParkingBySourceSession(sourceSessionId: string): Promise<AskRow[]>;
|
|
625
|
+
/**
|
|
626
|
+
* 「这道停驻门属于哪只问」:发起会话(列 `source_task_id`)= 给定会话,且 ① `state = 'PARKED'` ∧ 门上的绑定对(`gate_bound_call_id` / `gate_bound_input_hash`,收敛器
|
|
627
|
+
* 按停驻行的「同调用 ∧ 同展示入参摘要」绑上的那一对)逐字等于给定的一对,或 ② `state = 'PARKING'`(收敛器还没绑上)∧ 这只问自己的调用 id 与展示入参摘要
|
|
628
|
+
* (`tool_call_id` / `bound_input_hash`,收敛器绑定判的就是这两格)逐字等于给定的一对 —— 同一只问在绑定前后都是同一行、同一份 `card_json`,两个读者跨过绑定那一拍同答。按绑定对而不是令牌:运维队列行与续跑腿两个读者手里都有这一对
|
|
629
|
+
* (队列行不带令牌),同一个问题一个键、一只读口。
|
|
630
|
+
* 停驻行陈述的会话 = 铸它的那条腿的会话 = 同一次调用的问的发起会话(委派 / 后台子代 = 子代自己的会话;行上的 `sessionId` 记的是投递上下文,不是这一格)。
|
|
631
|
+
* 两个消费者(同一只协调器读口):续跑腿的规则记录读者(`http/resume-legs.ts`)与运维队列行的候选投影(`routes/approvals-assistant.ts`)—— 有问行的停驻卡读问行、
|
|
632
|
+
* 没有读停驻行;恰好一行才是这张卡的记录,零行 = 直停驻卡,多行 = 读不出记录(两个读者同答 fail-closed)。
|
|
633
|
+
* 不带租户门(内部读口,不上 wire;调用方已复核过停驻行的属主,记录读者再比一次行属主)。索引走既有 `idx_ask_source_session (source_task_id)`;零 DDL。
|
|
634
|
+
* 三 twin 同答:SQL twin 读后按发起会话与令牌逐字再滤一遍(MySQL 协议 VARCHAR 等值的 PAD SPACE,与 `listByCall` 同一条出口对齐)。
|
|
635
|
+
*/
|
|
636
|
+
listAsksAtGate(sourceSessionId: string, binding: {
|
|
637
|
+
readonly boundCallId: string;
|
|
638
|
+
readonly boundInputHash: string;
|
|
639
|
+
}): Promise<AskRow[]>;
|
|
620
640
|
getAsk(askId: string): Promise<AskRow | null>;
|
|
621
641
|
/** 回决幂等回放的读口。🔴 `taskId` **必填**且是键的第一维——UNIQUE 已改 `(task_id, idempotency_key)`
|
|
622
642
|
* (车4 §12-E),裸按 key 全局查在多租户库上既可能回**别人 task** 的行(存在性外泄),也可能在同 key
|
|
@@ -832,6 +852,10 @@ export declare class SqlApprovalAskStore implements ApprovalAskStore {
|
|
|
832
852
|
listPendingBySession(sessionId: string, owner: string | null, signal?: AbortSignal): Promise<AskRow[]>;
|
|
833
853
|
listByCall(call: AskCallCoordinate): Promise<AskRow[]>;
|
|
834
854
|
listParkingBySourceSession(sourceSessionId: string): Promise<AskRow[]>;
|
|
855
|
+
listAsksAtGate(sourceSessionId: string, binding: {
|
|
856
|
+
readonly boundCallId: string;
|
|
857
|
+
readonly boundInputHash: string;
|
|
858
|
+
}): Promise<AskRow[]>;
|
|
835
859
|
getAsk(askId: string): Promise<AskRow | null>;
|
|
836
860
|
getByIdempotencyKey(taskId: string, key: string): Promise<AskRow | null>;
|
|
837
861
|
deferReconcile(askId: string, expectedState: AskState, expectedRev: number, nowMs: number): Promise<boolean>;
|
|
@@ -121,6 +121,13 @@ export function projectAskTerminalClaimTruth(row) {
|
|
|
121
121
|
export function askRowOnCall(row, call) {
|
|
122
122
|
return row.taskId === call.taskId && row.legKey === call.legKey && row.toolCallId === call.toolCallId && row.sourceSessionId === call.sourceSessionId;
|
|
123
123
|
}
|
|
124
|
+
export function askAtGate(r, sourceSessionId, binding) {
|
|
125
|
+
if (r.sourceSessionId !== sourceSessionId)
|
|
126
|
+
return false;
|
|
127
|
+
if (r.state === "PARKED")
|
|
128
|
+
return r.gateBoundCallId === binding.boundCallId && r.gateBoundInputHash === binding.boundInputHash;
|
|
129
|
+
return r.state === "PARKING" && r.toolCallId === binding.boundCallId && r.boundInputHash === binding.boundInputHash;
|
|
130
|
+
}
|
|
124
131
|
export function askRowCarriesOwner(row, owner) {
|
|
125
132
|
return row !== null && row.owner === owner;
|
|
126
133
|
}
|
|
@@ -817,6 +824,10 @@ export class SqlApprovalAskStore {
|
|
|
817
824
|
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE source_task_id = ? AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE source_task_id = $1 AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`), [sourceSessionId]);
|
|
818
825
|
return rows.map(mapAskRow).filter((r) => r.sourceSessionId === sourceSessionId);
|
|
819
826
|
}
|
|
827
|
+
async listAsksAtGate(sourceSessionId, binding) {
|
|
828
|
+
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE source_task_id = ? AND ((state = 'PARKED' AND gate_bound_call_id = ? AND gate_bound_input_hash = ?) OR (state = 'PARKING' AND tool_call_id = ? AND bound_input_hash = ?)) ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE source_task_id = $1 AND ((state = 'PARKED' AND gate_bound_call_id = $2 AND gate_bound_input_hash = $3) OR (state = 'PARKING' AND tool_call_id = $2 AND bound_input_hash = $3)) ORDER BY created_at_ms ASC, ask_id ASC`), [sourceSessionId, binding.boundCallId, binding.boundInputHash, binding.boundCallId, binding.boundInputHash]);
|
|
829
|
+
return rows.map(mapAskRow).filter((r) => askAtGate(r, sourceSessionId, binding));
|
|
830
|
+
}
|
|
820
831
|
async getAsk(askId) {
|
|
821
832
|
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [
|
|
822
833
|
askId,
|
|
@@ -904,7 +915,7 @@ sealStoreClass(SqlApprovalAskStore, "sql-approval-ask-store", {
|
|
|
904
915
|
ensureAsk: "exit", transitionAsk: "exit", decideAsk: "exit", claimTerminal: "exit", expireAsk: "exit", bindBatch: "exit", abortBatch: "exit",
|
|
905
916
|
listByState: "exit", listPendingByTask: "exit", listPendingBySession: "exit", listByCall: "exit", listParkingBySourceSession: "exit", getAsk: "exit",
|
|
906
917
|
getByIdempotencyKey: "exit", deferReconcile: "exit", resolveProvisional: "exit", deleteBySession: "exit", reapSettledAsks: "exit", reapEmptyBatches: "exit",
|
|
907
|
-
getBatch: "exit", recordParkedDecision: "exit",
|
|
918
|
+
getBatch: "exit", recordParkedDecision: "exit", listAsksAtGate: "exit",
|
|
908
919
|
});
|
|
909
920
|
export class TiDBApprovalAskStore extends SqlApprovalAskStore {
|
|
910
921
|
constructor(pool) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ObjectStoreOp } from "../object-store-buckets.js";
|
|
2
|
+
import type { SessionBlobNamespace } from "./session-blob-namespace.js";
|
|
1
3
|
/**
|
|
2
4
|
* The byte port. Keys are opaque to it (today every index owner keys a blob by its sha256 hash).
|
|
3
5
|
* - `put`: store `bytes` under `key`. Idempotent (a repeat for the same key overwrites with the identical bytes); a rejection is a
|
|
@@ -5,7 +7,10 @@
|
|
|
5
7
|
* - `get`: the bytes under `key`, or `undefined` when nothing is there. A TRANSIENT fault THROWS (so a restore / import fails LOUD,
|
|
6
8
|
* not as a silent "missing blob"). The index owner checks the bytes against the hash ({@link readAtAddress}).
|
|
7
9
|
* - `delete`: remove the bytes under each key. Idempotent — a key with nothing under it is a success; a key that could not be removed
|
|
8
|
-
* makes the call reject (after every key was tried).
|
|
10
|
+
* makes the call reject (after every key was tried). A port whose deletes wait on a precondition (the session blob ports: the namespace's
|
|
11
|
+
* ownership, `session-blob-namespace.ts`) checks it at the start of EVERY call, before any key — so `delete([])` is that precondition alone:
|
|
12
|
+
* it rejects exactly as a real delete would be refused, and removes nothing. An index owner whose own writes come before its byte deletes
|
|
13
|
+
* asks it that way first (`SqlBlobIndex.sweep`).
|
|
9
14
|
*/
|
|
10
15
|
export interface BlobBackend {
|
|
11
16
|
put(key: string, bytes: Uint8Array): Promise<void>;
|
|
@@ -19,20 +24,47 @@ export interface BlobBackend {
|
|
|
19
24
|
* where both index owners judge it (the file-history blob index, the attachment store).
|
|
20
25
|
*/
|
|
21
26
|
export declare function readAtAddress(port: Pick<BlobBackend, "get">, hash: string): Promise<Uint8Array | undefined>;
|
|
22
|
-
|
|
27
|
+
/** A name a content-addressed domain could have written: a hex sha256, one key segment. A listed name of any other shape (a nested prefix's object, a
|
|
28
|
+
* hand-placed file) is not the domain's — its sweep leaves it alone. */
|
|
29
|
+
export declare function isContentAddress(name: string): boolean;
|
|
30
|
+
/** The object-store action table rows one blob backend signs, by verb (`object-store-buckets.ts`). A verb with no row is a request this backend never
|
|
31
|
+
* makes: calling it throws before anything is signed (the retired epoch's index only deletes; the boot check only lists). */
|
|
32
|
+
export interface BlobBackendOps {
|
|
33
|
+
readonly put?: ObjectStoreOp;
|
|
34
|
+
readonly get?: ObjectStoreOp;
|
|
35
|
+
readonly head?: ObjectStoreOp;
|
|
36
|
+
readonly delete?: ObjectStoreOp;
|
|
37
|
+
readonly list?: ObjectStoreOp;
|
|
38
|
+
}
|
|
39
|
+
interface MinioBlobBackendBaseConfig {
|
|
23
40
|
endpoint: string;
|
|
24
41
|
bucket: string;
|
|
25
42
|
accessKey: string;
|
|
26
43
|
secretKey: string;
|
|
27
44
|
region?: string;
|
|
28
|
-
/** Key prefix for blob objects
|
|
29
|
-
keyPrefix
|
|
30
|
-
/** Presigned-URL TTL (seconds).
|
|
31
|
-
*
|
|
45
|
+
/** Key prefix for blob objects. Object key = keyPrefix + key. The production domains get theirs from {@link createSessionBlobBackend}. */
|
|
46
|
+
keyPrefix: string;
|
|
47
|
+
/** Presigned-URL TTL (seconds; `SESSION_SNAPSHOT_TTL_SEC`). Unset ⇒ the row's default in the object-store action table (3600 s — it must
|
|
48
|
+
* comfortably exceed a ~64 MiB single-PUT/GET on a slow link: a SigV4 X-Amz-Expires bounds the WHOLE transfer from X-Amz-Date; a too-short TTL
|
|
49
|
+
* fails mid-stream). A row whose TTL is fixed (the boot probe's) refuses a configured one. */
|
|
32
50
|
presignTtlSec?: number;
|
|
33
51
|
/** 测试注入口(listObjects 分页钉用);生产缺省 global fetch。 */
|
|
34
52
|
fetchImpl?: typeof fetch;
|
|
35
53
|
}
|
|
54
|
+
/** A backend's rows and, for one that signs DELETE, the namespace's ownership every DELETE waits for — required by construction: every delete of a
|
|
55
|
+
* content-addressed domain is judged by one database's references, so a port that can delete is built from that database's proven namespace
|
|
56
|
+
* (`session-blob-namespace.ts`; `createSessionBlobBackend` passes it). A port with no DELETE row (the boot probe) has no owner. */
|
|
57
|
+
export type MinioBlobBackendConfig = MinioBlobBackendBaseConfig & ({
|
|
58
|
+
readonly ops: BlobBackendOps & {
|
|
59
|
+
readonly delete?: undefined;
|
|
60
|
+
};
|
|
61
|
+
readonly owner?: undefined;
|
|
62
|
+
} | {
|
|
63
|
+
readonly ops: BlobBackendOps & {
|
|
64
|
+
readonly delete: ObjectStoreOp;
|
|
65
|
+
};
|
|
66
|
+
readonly owner: Pick<SessionBlobNamespace, "assertOwned">;
|
|
67
|
+
});
|
|
36
68
|
/** 鲁棒性批2 C2/C3/C6(2026-08-03):S3 兼容端点 IO 的统一 abort 上界(fleet-client C1 定常量形
|
|
37
69
|
* 先例)。半开端点此前各挂 undici 默认 ~300s headersTimeout——blob GET 卡住 restore/session-import
|
|
38
70
|
* 同步路径,SendUserFile 工具调用永不返回。120s 对慢链路 ~64MiB 单 PUT/GET 仍宽裕(presignTtlSec
|
|
@@ -56,11 +88,14 @@ export type BucketProbe = {
|
|
|
56
88
|
export declare class MinioBlobBackend implements BlobBackend {
|
|
57
89
|
private readonly cfg;
|
|
58
90
|
private readonly keyPrefix;
|
|
91
|
+
/** The configured TTL, or nothing (the seat then takes the row's own). */
|
|
59
92
|
private readonly ttl;
|
|
60
93
|
/** 六个站点(put / get / delete / listObjects / headObject / probeBucket)的唯一出口:端点回重定向 ⇒ 拒,预签名 URL 与对象体不被带去 `Location`。 */
|
|
61
94
|
private readonly egressFetch;
|
|
62
95
|
constructor(cfg: MinioBlobBackendConfig);
|
|
63
96
|
private objectKey;
|
|
97
|
+
/** The table row this backend signs `verb` under; none ⇒ throw (a request this backend's domain never makes). */
|
|
98
|
+
private opFor;
|
|
64
99
|
private presign;
|
|
65
100
|
/** PUT the bytes under the key. A non-2xx PUT throws (an upload failure must surface — a silent drop would later read_failed on
|
|
66
101
|
* restore). NOTE no post-PUT read-back verify on the live path (perf): a corrupted PUT is caught at the next read's content-address
|
|
@@ -75,7 +110,8 @@ export declare class MinioBlobBackend implements BlobBackend {
|
|
|
75
110
|
/** DELETE every key's object, at most {@link MINIO_OP_CONCURRENCY} in flight (no FD / throttle storm). Each key is independent: a
|
|
76
111
|
* 2xx or a 404 (already gone) is a success; any other answer or a network failure is a failure. Every key is tried; then, if any
|
|
77
112
|
* failed, the call REJECTS naming how many (🔴 复审 F4 的同一判据:对象存储完全不可写时绝不报「清理成功」—— 失败只在这里说出来,
|
|
78
|
-
* 调用方据此记账,不吞)。
|
|
113
|
+
* 调用方据此记账,不吞)。The namespace's ownership is read first on every call, an empty `keys` included (that call is the precondition
|
|
114
|
+
* alone: the port contract, {@link BlobBackend}). */
|
|
79
115
|
delete(keys: readonly string[]): Promise<void>;
|
|
80
116
|
/** D-1 孤儿对象 GC(clay 拍 2026-07-28):列举本后端 keyPrefix 下的全部对象(ListObjectsV2 分页,
|
|
81
117
|
* 零依赖 XML 取 <Key>/<LastModified>/<NextContinuationToken>)。**server 端 sweep 专用**——presign 的
|
|
@@ -95,4 +131,44 @@ export declare class MinioBlobBackend implements BlobBackend {
|
|
|
95
131
|
* 都**证明不了**缺桶 ⇒ `unknown`,调用方不拒启、只报一次。总函数:不抛。`timeoutMs`:boot 期调用方给的单次上界(黑洞端点不许把起服拖到 120s)。 */
|
|
96
132
|
probeBucket(timeoutMs?: number): Promise<BucketProbe>;
|
|
97
133
|
}
|
|
134
|
+
/** The blob domains of the session snapshot bucket (closed set) → the table rows each signs. File history reads, writes and sweeps; attachments also list
|
|
135
|
+
* and HEAD (their orphan sweep); the retired snapshot epoch only deletes (its sweep). */
|
|
136
|
+
export declare const SESSION_BLOB_DOMAIN_OPS: {
|
|
137
|
+
readonly "file-history": {
|
|
138
|
+
readonly put: "file-history/blob:PUT";
|
|
139
|
+
readonly get: "file-history/blob:GET";
|
|
140
|
+
readonly delete: "file-history/blob:DELETE";
|
|
141
|
+
};
|
|
142
|
+
readonly attachments: {
|
|
143
|
+
readonly put: "attachments/blob:PUT";
|
|
144
|
+
readonly get: "attachments/blob:GET";
|
|
145
|
+
readonly head: "attachments/blob:HEAD";
|
|
146
|
+
readonly delete: "attachments/blob:DELETE";
|
|
147
|
+
readonly list: "attachments/prefix:LIST";
|
|
148
|
+
};
|
|
149
|
+
readonly "legacy-blobs": {
|
|
150
|
+
readonly delete: "legacy-blobs/blob:DELETE";
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
export type SessionBlobDomain = keyof typeof SESSION_BLOB_DOMAIN_OPS;
|
|
154
|
+
/** The session snapshot bucket's store config (`config.snapshotBlobStore`: `SESSION_SNAPSHOT_BUCKET`, `SESSION_SNAPSHOT_PREFIX`, `SESSION_SNAPSHOT_TTL_SEC`). */
|
|
155
|
+
export interface SessionBlobStoreConfig {
|
|
156
|
+
endpoint: string;
|
|
157
|
+
bucket: string;
|
|
158
|
+
accessKey: string;
|
|
159
|
+
secretKey: string;
|
|
160
|
+
region?: string;
|
|
161
|
+
keyPrefix?: string;
|
|
162
|
+
presignTtlSec?: number;
|
|
163
|
+
}
|
|
164
|
+
/** The key prefix of a session blob domain, or the key of the namespace's claim — the ONE place each composes `SESSION_SNAPSHOT_PREFIX` with its
|
|
165
|
+
* segment (the table's key forms `{sessionPrefix}file-history/…`, `{sessionPrefix}attachments/…`, `{legacyBlobRoot}…`, `{sessionPrefix}blob-namespace-claim`).
|
|
166
|
+
* An empty prefix reads as unset (the env loader's reading of the knob, kept for a hand-built config too): file history, attachments and the claim then
|
|
167
|
+
* sit at the bucket root, the retired epoch at its own root `blobs/` (where its objects were written). */
|
|
168
|
+
export declare function sessionBlobKeyPrefixOf(store: Pick<SessionBlobStoreConfig, "keyPrefix">, part: SessionBlobDomain | "claim"): string;
|
|
169
|
+
/** The byte port of one session blob domain over the session snapshot bucket: its key prefix ({@link sessionBlobKeyPrefixOf}), its table rows
|
|
170
|
+
* ({@link SESSION_BLOB_DOMAIN_OPS}) and the namespace's ownership its deletes wait for — the one construction every production domain uses, and it
|
|
171
|
+
* takes the namespace (one database's), not a bare bucket config. */
|
|
172
|
+
export declare function createSessionBlobBackend(namespace: SessionBlobNamespace, domain: SessionBlobDomain): MinioBlobBackend;
|
|
173
|
+
export {};
|
|
98
174
|
//# sourceMappingURL=blob-backend.d.ts.map
|
|
@@ -2,12 +2,16 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { presignS3Url, presignS3ListUrl } from "./s3-presign.js";
|
|
3
3
|
import { isNoSuchBucketAnswer } from "./s3-answer.js";
|
|
4
4
|
import { createEgressFetch } from "../egress-redirect.js";
|
|
5
|
+
import { ATTACHMENT_BLOB_SEGMENT, FILE_HISTORY_BLOB_SEGMENT, LEGACY_BLOB_ROOT_DEFAULT, SESSION_BLOB_CLAIM_KEY } from "../object-store-buckets.js";
|
|
5
6
|
export async function readAtAddress(port, hash) {
|
|
6
7
|
const bytes = await port.get(hash);
|
|
7
8
|
if (bytes === undefined)
|
|
8
9
|
return undefined;
|
|
9
10
|
return createHash("sha256").update(bytes).digest("hex") === hash ? bytes : undefined;
|
|
10
11
|
}
|
|
12
|
+
export function isContentAddress(name) {
|
|
13
|
+
return /^[0-9a-f]{64}$/.test(name);
|
|
14
|
+
}
|
|
11
15
|
const MINIO_OP_CONCURRENCY = 16;
|
|
12
16
|
async function mapBounded(items, limit, fn) {
|
|
13
17
|
let i = 0;
|
|
@@ -27,23 +31,30 @@ export class MinioBlobBackend {
|
|
|
27
31
|
egressFetch;
|
|
28
32
|
constructor(cfg) {
|
|
29
33
|
this.cfg = cfg;
|
|
30
|
-
this.keyPrefix = cfg.keyPrefix
|
|
31
|
-
this.ttl = cfg.presignTtlSec
|
|
34
|
+
this.keyPrefix = cfg.keyPrefix;
|
|
35
|
+
this.ttl = cfg.presignTtlSec !== undefined ? { expiresSec: cfg.presignTtlSec } : {};
|
|
32
36
|
this.egressFetch = createEgressFetch("declare the final object-store address as MINIO_ENDPOINT / S3_ENDPOINT", cfg.fetchImpl);
|
|
33
37
|
}
|
|
34
38
|
objectKey(key) {
|
|
35
39
|
return `${this.keyPrefix}${key}`;
|
|
36
40
|
}
|
|
41
|
+
opFor(verb) {
|
|
42
|
+
const op = this.cfg.ops[verb];
|
|
43
|
+
if (op === undefined)
|
|
44
|
+
throw new Error(`MinioBlobBackend: this blob domain (prefix ${JSON.stringify(this.keyPrefix)}) signs no ${verb.toUpperCase()} — no object-store action row for it`);
|
|
45
|
+
return op;
|
|
46
|
+
}
|
|
37
47
|
presign(key, method) {
|
|
38
48
|
return presignS3Url({
|
|
49
|
+
op: this.opFor(method === "GET" ? "get" : method === "PUT" ? "put" : method === "HEAD" ? "head" : "delete"),
|
|
39
50
|
endpoint: this.cfg.endpoint,
|
|
40
51
|
bucket: this.cfg.bucket,
|
|
41
52
|
key: this.objectKey(key),
|
|
42
|
-
method
|
|
53
|
+
method,
|
|
43
54
|
accessKey: this.cfg.accessKey,
|
|
44
55
|
secretKey: this.cfg.secretKey,
|
|
45
56
|
...(this.cfg.region ? { region: this.cfg.region } : {}),
|
|
46
|
-
|
|
57
|
+
...this.ttl,
|
|
47
58
|
});
|
|
48
59
|
}
|
|
49
60
|
async put(key, bytes) {
|
|
@@ -72,7 +83,13 @@ export class MinioBlobBackend {
|
|
|
72
83
|
return new Uint8Array(await res.arrayBuffer());
|
|
73
84
|
}
|
|
74
85
|
async delete(keys) {
|
|
86
|
+
const owner = this.cfg.owner;
|
|
87
|
+
if (owner === undefined)
|
|
88
|
+
throw new Error(`MinioBlobBackend: this blob domain (prefix ${JSON.stringify(this.keyPrefix)}) has no namespace ownership to delete under — a port that signs DELETE is built from a proven namespace`);
|
|
89
|
+
await owner.assertOwned();
|
|
75
90
|
const distinct = [...new Set(keys)];
|
|
91
|
+
if (distinct.length === 0)
|
|
92
|
+
return;
|
|
76
93
|
let failed = 0;
|
|
77
94
|
let firstFailure;
|
|
78
95
|
await mapBounded(distinct, MINIO_OP_CONCURRENCY, async (key) => {
|
|
@@ -100,6 +117,7 @@ export class MinioBlobBackend {
|
|
|
100
117
|
if (++pages > MAX_PAGES)
|
|
101
118
|
throw new Error(`listObjects: exceeded ${MAX_PAGES} pages (max-keys 1000/page) — the backend is looping its continuation-token or the prefix is unexpectedly large; refusing to report a partial orphan set`);
|
|
102
119
|
const url = presignS3ListUrl({
|
|
120
|
+
op: this.opFor("list"),
|
|
103
121
|
endpoint: this.cfg.endpoint,
|
|
104
122
|
bucket: this.cfg.bucket,
|
|
105
123
|
prefix: this.keyPrefix,
|
|
@@ -108,7 +126,7 @@ export class MinioBlobBackend {
|
|
|
108
126
|
accessKey: this.cfg.accessKey,
|
|
109
127
|
secretKey: this.cfg.secretKey,
|
|
110
128
|
...(this.cfg.region ? { region: this.cfg.region } : {}),
|
|
111
|
-
|
|
129
|
+
...this.ttl,
|
|
112
130
|
});
|
|
113
131
|
const res = await f(url, { method: "GET", signal: AbortSignal.timeout(OBJECT_STORE_IO_TIMEOUT_MS) });
|
|
114
132
|
if (!res.ok)
|
|
@@ -141,6 +159,7 @@ export class MinioBlobBackend {
|
|
|
141
159
|
async probeBucket(timeoutMs = OBJECT_STORE_IO_TIMEOUT_MS) {
|
|
142
160
|
const f = this.egressFetch;
|
|
143
161
|
const url = presignS3ListUrl({
|
|
162
|
+
op: this.opFor("list"),
|
|
144
163
|
endpoint: this.cfg.endpoint,
|
|
145
164
|
bucket: this.cfg.bucket,
|
|
146
165
|
prefix: this.keyPrefix,
|
|
@@ -148,7 +167,7 @@ export class MinioBlobBackend {
|
|
|
148
167
|
accessKey: this.cfg.accessKey,
|
|
149
168
|
secretKey: this.cfg.secretKey,
|
|
150
169
|
...(this.cfg.region ? { region: this.cfg.region } : {}),
|
|
151
|
-
|
|
170
|
+
...this.ttl,
|
|
152
171
|
});
|
|
153
172
|
let res;
|
|
154
173
|
try {
|
|
@@ -165,4 +184,36 @@ export class MinioBlobBackend {
|
|
|
165
184
|
return { state: "unknown", detail: `HTTP ${res.status}` };
|
|
166
185
|
}
|
|
167
186
|
}
|
|
187
|
+
export const SESSION_BLOB_DOMAIN_OPS = {
|
|
188
|
+
"file-history": { put: "file-history/blob:PUT", get: "file-history/blob:GET", delete: "file-history/blob:DELETE" },
|
|
189
|
+
attachments: { put: "attachments/blob:PUT", get: "attachments/blob:GET", head: "attachments/blob:HEAD", delete: "attachments/blob:DELETE", list: "attachments/prefix:LIST" },
|
|
190
|
+
"legacy-blobs": { delete: "legacy-blobs/blob:DELETE" },
|
|
191
|
+
};
|
|
192
|
+
export function sessionBlobKeyPrefixOf(store, part) {
|
|
193
|
+
const prefix = store.keyPrefix === "" ? undefined : store.keyPrefix;
|
|
194
|
+
switch (part) {
|
|
195
|
+
case "file-history":
|
|
196
|
+
return `${prefix ?? ""}${FILE_HISTORY_BLOB_SEGMENT}`;
|
|
197
|
+
case "attachments":
|
|
198
|
+
return `${prefix ?? ""}${ATTACHMENT_BLOB_SEGMENT}`;
|
|
199
|
+
case "claim":
|
|
200
|
+
return `${prefix ?? ""}${SESSION_BLOB_CLAIM_KEY}`;
|
|
201
|
+
case "legacy-blobs":
|
|
202
|
+
return prefix ?? LEGACY_BLOB_ROOT_DEFAULT;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function createSessionBlobBackend(namespace, domain) {
|
|
206
|
+
const store = namespace.store;
|
|
207
|
+
return new MinioBlobBackend({
|
|
208
|
+
endpoint: store.endpoint,
|
|
209
|
+
bucket: store.bucket,
|
|
210
|
+
accessKey: store.accessKey,
|
|
211
|
+
secretKey: store.secretKey,
|
|
212
|
+
...(store.region ? { region: store.region } : {}),
|
|
213
|
+
keyPrefix: sessionBlobKeyPrefixOf(store, domain),
|
|
214
|
+
ops: SESSION_BLOB_DOMAIN_OPS[domain],
|
|
215
|
+
owner: namespace,
|
|
216
|
+
...(store.presignTtlSec ? { presignTtlSec: store.presignTtlSec } : {}),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
168
219
|
//# sourceMappingURL=blob-backend.js.map
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## 写:一只派生 = core `checkpointIndexFacts(cp)`(core 7.55.0 [ref] P1 阶段二)
|
|
6
6
|
* 行上「core 铸下、此后不动」的十三个事实(令牌、scope、会话、格式版本、门型、卡、待决工具的名 / 调用 id / 入参摘要、deadline、铸行时刻、风险描述符、
|
|
7
|
-
* 双向控制字符在场位),每一个都由 core
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
7
|
+
* 双向控制字符在场位),每一个都由 core 那一只函数答;本模块只把**有读者找行 / 判到期**的那几个落到列名下({@link buildCheckpointIndexCells})。
|
|
8
|
+
* 键表对 core 的事实键闭合({@link CHECKPOINT_INDEX_COLUMNS} `satisfies Record<keyof CheckpointIndexFacts, string | null>`):core 加 / 删一个事实 ⇒ 编译红,
|
|
9
|
+
* 由人决定它落不落列。**不落列**(`null`)的三个 —— 工具名、风险描述符、bidi 位 —— 只是运维队列的展示材料:队列行在读时从续跑执行的同一份字节、同一次整行解码投影
|
|
10
|
+
* (`checkpoint-store-face.ts` 的 `buildQueueActionFacts`,同调这同一只 core 派生),落一份列就是同一事实的第二份存储(7.151.x 及以前落过,一次性迁移 M54 删)。
|
|
11
|
+
* 不在这里的:CAS 面推动的状态(`status` / `rev` / `reopen_reason` / 执行记录)、部署铸的值(`terminal_at_ms` 兜底)—— 它们不是行的不变事实,core 也不答。
|
|
12
12
|
*
|
|
13
13
|
* ## 写之前:一只判官 = {@link judgeCheckpointIndexCells}
|
|
14
14
|
* 一份文档能不能落进这些列,在写之前由**同一只**判官答 —— 写口 `put` 与 M29 的存量回填都问它,界不抄第二份。三个答案:`cells`(放得下,列格在手);`unreadable`(门或待决动作
|
|
@@ -29,9 +29,13 @@
|
|
|
29
29
|
* (`boot/stores.ts`,先于任何路由与收割器装配),跑不完就拒启 —— 所以每只副本的收割器只在本次起服的 M29 完成之后才运行。混跑期间旧副本新写的行
|
|
30
30
|
* 不带卡列(它们不认这一列),由下一次起服的 M29 补上;升级序见 MIGRATION 7.135.0 节(同一只库不混跑两版)。
|
|
31
31
|
*/
|
|
32
|
-
import { PAUSE_REGISTRY, type Checkpoint, type CheckpointIndexFacts, type GateKind
|
|
32
|
+
import { PAUSE_REGISTRY, type Checkpoint, type CheckpointIndexFacts, type GateKind } from "@sema-agent/core";
|
|
33
33
|
import type { SqlDialect } from "./sql-driver.js";
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 每个索引事实落在哪一列;`null` = 不落列(键表对 core 的事实键闭合:core 加 / 删一个事实 ⇒ 编译红,由人定落不落)。
|
|
36
|
+
* 落列的判据只有一条:**有读者按它找行或判到期**(决断口的门型绑定、对账与决断回放的调用 id / 绑定摘要、到期 facet 的卡与门型、会话 / scope / 时刻 / 截止)。
|
|
37
|
+
* 只给人看的事实不落列 —— 队列在读时从同一次整行解码投影,一个事实一份存储。
|
|
38
|
+
*/
|
|
35
39
|
export declare const CHECKPOINT_INDEX_COLUMNS: {
|
|
36
40
|
readonly token: "token";
|
|
37
41
|
readonly scope: "scope";
|
|
@@ -39,21 +43,21 @@ export declare const CHECKPOINT_INDEX_COLUMNS: {
|
|
|
39
43
|
readonly version: "version";
|
|
40
44
|
readonly gateKind: "gate_kind";
|
|
41
45
|
readonly approvalCard: "approval_card";
|
|
42
|
-
readonly toolName:
|
|
46
|
+
readonly toolName: null;
|
|
43
47
|
readonly toolCallId: "tool_call_id";
|
|
44
48
|
readonly boundInputHash: "bound_input_hash";
|
|
45
49
|
readonly deadline: "deadline";
|
|
46
50
|
readonly createdAt: "created_at_ms";
|
|
47
|
-
readonly riskDescriptor:
|
|
48
|
-
readonly hasBidiControls:
|
|
51
|
+
readonly riskDescriptor: null;
|
|
52
|
+
readonly hasBidiControls: null;
|
|
49
53
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*/
|
|
54
|
+
/** 落列的那几个事实(键表上列名不是 `null` 的)。 */
|
|
55
|
+
type StoredIndexFact = {
|
|
56
|
+
[K in keyof CheckpointIndexFacts]: (typeof CHECKPOINT_INDEX_COLUMNS)[K] extends string ? K : never;
|
|
57
|
+
}[keyof CheckpointIndexFacts];
|
|
58
|
+
/** 一行的索引列格:落列的 core 事实落在列名下,值逐字是事实本身。 */
|
|
55
59
|
export type CheckpointIndexCells = {
|
|
56
|
-
readonly [K in
|
|
60
|
+
readonly [K in StoredIndexFact as (typeof CHECKPOINT_INDEX_COLUMNS)[K] & string]: CheckpointIndexFacts[K];
|
|
57
61
|
};
|
|
58
62
|
/** 一行的索引列格(纯数据;写口与 M29 回填同调这一只 —— 回填不另读门子树自算)。 */
|
|
59
63
|
export declare function buildCheckpointIndexCells(cp: Checkpoint): CheckpointIndexCells;
|
|
@@ -7,13 +7,13 @@ export const CHECKPOINT_INDEX_COLUMNS = {
|
|
|
7
7
|
version: "version",
|
|
8
8
|
gateKind: "gate_kind",
|
|
9
9
|
approvalCard: "approval_card",
|
|
10
|
-
toolName:
|
|
10
|
+
toolName: null,
|
|
11
11
|
toolCallId: "tool_call_id",
|
|
12
12
|
boundInputHash: "bound_input_hash",
|
|
13
13
|
deadline: "deadline",
|
|
14
14
|
createdAt: "created_at_ms",
|
|
15
|
-
riskDescriptor:
|
|
16
|
-
hasBidiControls:
|
|
15
|
+
riskDescriptor: null,
|
|
16
|
+
hasBidiControls: null,
|
|
17
17
|
};
|
|
18
18
|
export function buildCheckpointIndexCells(cp) {
|
|
19
19
|
const f = checkpointIndexFacts(cp);
|
|
@@ -24,13 +24,10 @@ export function buildCheckpointIndexCells(cp) {
|
|
|
24
24
|
version: f.version,
|
|
25
25
|
gate_kind: f.gateKind,
|
|
26
26
|
approval_card: f.approvalCard,
|
|
27
|
-
tool_name: f.toolName,
|
|
28
27
|
tool_call_id: f.toolCallId,
|
|
29
28
|
bound_input_hash: f.boundInputHash,
|
|
30
29
|
deadline: f.deadline,
|
|
31
30
|
created_at_ms: f.createdAt,
|
|
32
|
-
risk_descriptor: f.riskDescriptor,
|
|
33
|
-
has_bidi_controls: f.hasBidiControls === true ? 1 : null,
|
|
34
31
|
};
|
|
35
32
|
}
|
|
36
33
|
export const APPROVAL_CARD_COLUMN_MAX_BYTES = {
|