@sema-agent/server 7.138.0 → 7.140.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 +457 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +91 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +26 -26
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/adoption.js +1 -1
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +28 -34
- package/dist/boot/execution-lane.d.ts +9 -1
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-boundary.d.ts +14 -51
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +15 -11
- package/dist/boot/memory-consolidation.js +25 -14
- package/dist/boot/resolve-spec.d.ts +4 -6
- package/dist/boot/resolve-spec.js +10 -13
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +2 -6
- package/dist/boot/stage-06-runners.js +2 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -6
- package/dist/boot/stage-08-reapers.d.ts +2 -6
- package/dist/boot/stage-09-leader.d.ts +2 -6
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -6
- package/dist/boot/stage-10-http-server.js +7 -6
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +27 -40
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +37 -11
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/mcp-probe.js +1 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +15 -7
- package/dist/capability-facts.js +8 -3
- package/dist/config-catalog.js +9 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +33 -37
- package/dist/config-provider.js +51 -30
- package/dist/config-types.d.ts +21 -7
- package/dist/config.d.ts +33 -0
- package/dist/config.js +46 -16
- package/dist/delegated-session-owner.d.ts +4 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/deployment-shape.d.ts +10 -9
- package/dist/deployment-shape.js +4 -4
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +115 -105
- package/dist/execution-lane-caps.js +76 -82
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/durable-run-leg.js +9 -5
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +63 -36
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-ctx.d.ts +14 -8
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +8 -1
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +5 -0
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +143 -22
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +4 -5
- package/dist/http/routes/tasks.js +41 -33
- package/dist/http/server.d.ts +40 -44
- package/dist/http/server.js +13 -2
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +8 -12
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +29 -5
- package/dist/memory-scope.js +16 -4
- package/dist/memory-sync-client.d.ts +70 -18
- package/dist/memory-sync-client.js +215 -117
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.js +0 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +34 -9
- package/dist/parked-decide.js +12 -19
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +13 -7
- package/dist/peer-seat.js +37 -3
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +44 -26
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +21 -23
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +68 -122
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +9 -8
- package/dist/plugins/file-history-store-sql.js +9 -9
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/leader-run-store-sql.js +4 -4
- package/dist/plugins/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -8
- package/dist/plugins/one-time-migrations.js +408 -38
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
- package/dist/plugins/permission-rule-store-sql.js +29 -32
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +31 -20
- package/dist/plugins/pg-session-storage.js +120 -52
- package/dist/plugins/remote-env-adb.d.ts +18 -19
- package/dist/plugins/remote-env-adb.js +91 -66
- package/dist/plugins/remote-env-device.d.ts +23 -31
- package/dist/plugins/remote-env-device.js +19 -17
- package/dist/plugins/remote-env-e2b.d.ts +27 -21
- package/dist/plugins/remote-env-e2b.js +91 -45
- package/dist/plugins/remote-env-file-error.d.ts +3 -1
- package/dist/plugins/remote-env-file-error.js +9 -3
- package/dist/plugins/remote-env-host.d.ts +31 -34
- package/dist/plugins/remote-env-host.js +91 -72
- package/dist/plugins/remote-env-k8s.d.ts +13 -29
- package/dist/plugins/remote-env-k8s.js +63 -55
- package/dist/plugins/remote-env-local-docker.d.ts +24 -12
- package/dist/plugins/remote-env-local-docker.js +49 -61
- package/dist/plugins/remote-env-ssh.d.ts +11 -19
- package/dist/plugins/remote-env-ssh.js +74 -57
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +10 -7
- package/dist/plugins/retention-store-sql.js +13 -7
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +20 -15
- package/dist/plugins/schema-consistency.d.ts +21 -11
- package/dist/plugins/schema-consistency.js +15 -9
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- package/dist/plugins/session-fork.d.ts +54 -0
- package/dist/plugins/session-fork.js +38 -1
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +5 -7
- package/dist/plugins/store-backend.js +23 -11
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +13 -7
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +127 -59
- package/dist/plugins/tool-result-store-sql.d.ts +5 -4
- package/dist/plugins/tool-result-store-sql.js +21 -13
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +34 -35
- package/dist/runs.d.ts +10 -5
- package/dist/runs.js +52 -66
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +9 -5
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +20 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +22 -9
- package/dist/task-mcp.js +17 -9
- package/dist/terminal.d.ts +27 -0
- package/dist/terminal.js +6 -0
- package/dist/tool-approval.d.ts +6 -0
- package/dist/tool-approval.js +8 -1
- package/dist/trace/project.d.ts +1 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +80 -28
- package/dist/trace/self-minted-wire.d.ts +169 -2
- package/dist/trace/self-minted-wire.js +54 -1
- package/dist/url-refusal.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/candidate-gate.d.ts +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/dist/web-admin-client/skills-mcp.js +3 -2
- package/dist/web-admin-client/types.d.ts +6 -0
- package/package.json +3 -3
- package/wire-changes/server@7.139.0.json +348 -0
- package/wire-changes/server@7.140.0.json +184 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
|
@@ -44,6 +44,7 @@ import { type AskState, type BatchState } from "../approval-ask-machine.js";
|
|
|
44
44
|
import type { HostSettledBy } from "../host-decision.js";
|
|
45
45
|
import { type AskRowCause, type SettleCause } from "../approval-deny-reasons.js";
|
|
46
46
|
import { type IndexSpec } from "./ensure-index.js";
|
|
47
|
+
import { type ExecutionLane } from "../execution-lane-caps.js";
|
|
47
48
|
export type AskDecision = "approve" | "deny";
|
|
48
49
|
export interface AskRow {
|
|
49
50
|
askId: string;
|
|
@@ -52,6 +53,11 @@ export interface AskRow {
|
|
|
52
53
|
sessionId: string;
|
|
53
54
|
/** 租户 scope——哨兵归一在消费层(车2+),店里存原值(设计定稿 §3)。 */
|
|
54
55
|
owner: string | null;
|
|
56
|
+
/**
|
|
57
|
+
* 造这只问的那条执行的车道(审批出处带车道:问行唯一铸点 `ensureAsk` 与 `taskId` / `sessionId` 同处写;既有行命中一个字节不改)。
|
|
58
|
+
* 读根授权只读问自己记下的车道。存量行随执行放置切换的一次性迁移回填(SQL `approval_ask.execution_lane`、local 问账日志压实)。
|
|
59
|
+
*/
|
|
60
|
+
executionLane: ExecutionLane;
|
|
55
61
|
batchId: string;
|
|
56
62
|
toolCallId: string;
|
|
57
63
|
/** 车3 §3.2′ 换轴:数值 `leg` → `legKey`(= `sha256(resume token)` 的 hex,首腿空串)。语义与派生
|
|
@@ -174,6 +180,8 @@ export interface NewAskRow {
|
|
|
174
180
|
sourceTaskId: string;
|
|
175
181
|
sessionId: string;
|
|
176
182
|
owner?: string | null;
|
|
183
|
+
/** 出处的执行车道(必填;语义见 {@link AskRow.executionLane})。 */
|
|
184
|
+
executionLane: ExecutionLane;
|
|
177
185
|
batchId: string;
|
|
178
186
|
toolCallId: string;
|
|
179
187
|
legKey: string;
|
|
@@ -562,6 +570,14 @@ export interface ApprovalAskStore {
|
|
|
562
570
|
getBatch(batchId: string): Promise<BatchRow | null>;
|
|
563
571
|
}
|
|
564
572
|
export declare const APPROVAL_ASK_TABLE = "approval_ask";
|
|
573
|
+
/**
|
|
574
|
+
* `approval_ask.execution_lane` 的列定义(两方言;唯一属主)—— 建表 DDL 内联它,存量库的加列只由执行放置切换的一次性迁移做(`one-time-migrations.ts` M32 步 ②),
|
|
575
|
+
* 那一步也读这里的定义。`DEFAULT ''` 只为存量库一句加列;回填(同迁移步 ④b)之后没有 '' 行,读口对 '' 与词表外的值一律响亮拒({@link executionLaneColumnOf})。
|
|
576
|
+
*/
|
|
577
|
+
export declare const APPROVAL_ASK_EXECUTION_LANE_COLUMN: {
|
|
578
|
+
readonly mysql: "execution_lane VARCHAR(32) NOT NULL DEFAULT ''";
|
|
579
|
+
readonly pg: "execution_lane VARCHAR(32) COLLATE \"C\" NOT NULL DEFAULT ''";
|
|
580
|
+
};
|
|
565
581
|
export declare const APPROVAL_BATCH_TABLE = "approval_batch";
|
|
566
582
|
/**
|
|
567
583
|
* TiDB/MySQL-protocol 侧的两条建表语句,**抽成导出数组**(车3 刀 3a):`tidb-pool.ts` 的
|
|
@@ -571,7 +587,8 @@ export declare const APPROVAL_BATCH_TABLE = "approval_batch";
|
|
|
571
587
|
* 下面的 `ensureTiDBApprovalAskSchema` 保留为遍历这个数组的薄壳(集成测试直接调它建表,
|
|
572
588
|
* 不必为了建两张表去拉起整套中央 schema)。
|
|
573
589
|
*
|
|
574
|
-
* SCHEMA POLICY 同 `tidb-pool.ts` 头注:纯 CREATE,禁 ALTER 增量 seam
|
|
590
|
+
* SCHEMA POLICY 同 `tidb-pool.ts` 头注:纯 CREATE,禁 ALTER 增量 seam,加列直接改这里(新建库随它到货;带数据的存量库由列集对账拒启并打印 `ALTER`,部署者自己执行;
|
|
591
|
+
* 改列类型 / 可空性列集对账看不见,另走一次性迁移;只有空库 / 开发机可以整库重建〔销毁全部数据〕)
|
|
575
592
|
* (`docs/schema/baseline-mysql.sql` 是由这些文本机器录制出的**产物**,永不手改)。
|
|
576
593
|
*
|
|
577
594
|
* ── 列宽的**测量依据**([ref] A10;U2 逐列取证在档)──────────────────────────────────────────────────
|
|
@@ -619,18 +636,18 @@ export declare function ensurePgApprovalAskSchema(q: PgQueryFn): Promise<void>;
|
|
|
619
636
|
* [ref] 升级前置断言 —— **拒启**,不是 warn(形照 [ref] 的 `assertPermissionRuleApprovalSchema` /
|
|
620
637
|
* [ref] 的 `assertToolResultProvenanceSchema` 两条先例)。
|
|
621
638
|
*
|
|
622
|
-
* 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY
|
|
639
|
+
* 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:代码是唯一真源,存量库缺列由部署者按拒启句打印的 ALTER 自己执行)。于是一台**没跟上**就升上来
|
|
623
640
|
* 的部署,`CREATE TABLE IF NOT EXISTS` 对存量 `approval_ask` 是空操作 —— 本车新加的
|
|
624
641
|
* `rule_command` / `rule_scope_root` 两列不在,而 `ensureAsk` 的 INSERT 与 `askCols` 的每一条读
|
|
625
642
|
* **无条件**引用它们。后果不是「少一个新功能」,是**整条流内审批协议**在那台机器上是坏的:
|
|
626
643
|
* · 每一只 ask 的 `ensureAsk` 撞 unknown column ⇒ 被 `noteStoreError` 吞掉 ⇒ 整只 ask 退回 park 路由
|
|
627
644
|
* (卡不落库、`GET /v1/approvals` 的重放面恒空、迟到决议腿恒 404);
|
|
628
645
|
* · 而 `/v1/capabilities` 的 `streamApproval` 仍然报 active ⇒「says yes ⟺ route works」整句为假。
|
|
629
|
-
*
|
|
646
|
+
* 没有这道断言,运维得不到**任何**「这次升级必须先给表加列」的信号 —— 正是 [ref]「禁静默降级」要挡的形。
|
|
630
647
|
*
|
|
631
648
|
* 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**(判据属主 `sql-errors.ts` 的
|
|
632
|
-
* {@link isMissingColumnError});其余错误**原样 rethrow**,一个字都不加工 ——
|
|
633
|
-
*
|
|
649
|
+
* {@link isMissingColumnError});其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
|
|
650
|
+
* 比它要挡的缺陷更贵([ref] 那两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),没有销毁性的一条。
|
|
634
651
|
*
|
|
635
652
|
* 🔴 位置与两条先例同款:**不在** `ensureSchema` 里(那条通道的契约是「只发 CREATE」,有运行时门看着),
|
|
636
653
|
* 放在 DDL 之后、任何路由装配之前;且**只在流内审批协议真会上场时**跑(判据 = 与协调器同一个符号
|
|
@@ -2,8 +2,10 @@ import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
|
|
|
2
2
|
import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
|
|
3
3
|
import { APPROVAL_ENDING_CAUSES, causeOfAskRow } from "../approval-deny-reasons.js";
|
|
4
4
|
import { isDupKeyError, isMissingColumnError } from "./sql-errors.js";
|
|
5
|
+
import { describeProbedTableDrift } from "./schema-drift-sentence.js";
|
|
5
6
|
import { decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, readTriStateJsonValueColumn } from "./sql-json-column.js";
|
|
6
7
|
import { ensureIndexes } from "./ensure-index.js";
|
|
8
|
+
import { isExecutionLane } from "../execution-lane-caps.js";
|
|
7
9
|
export function assertIntegralEpochMs(field, value) {
|
|
8
10
|
if (!Number.isSafeInteger(value)) {
|
|
9
11
|
throw new TypeError(`approval-ask store: ${field} must be an integer epoch-ms (BIGINT column), got ${String(value)}`);
|
|
@@ -99,6 +101,17 @@ export function projectDecideReading(row, owner) {
|
|
|
99
101
|
return askRowCarriesOwner(row, owner) ? row : null;
|
|
100
102
|
}
|
|
101
103
|
export const APPROVAL_ASK_TABLE = "approval_ask";
|
|
104
|
+
export const APPROVAL_ASK_EXECUTION_LANE_COLUMN = {
|
|
105
|
+
mysql: "execution_lane VARCHAR(32) NOT NULL DEFAULT ''",
|
|
106
|
+
pg: `execution_lane VARCHAR(32) COLLATE "C" NOT NULL DEFAULT ''`,
|
|
107
|
+
};
|
|
108
|
+
function executionLaneColumnOf(raw, askId) {
|
|
109
|
+
const lane = typeof raw === "string" ? raw : "";
|
|
110
|
+
if (!isExecutionLane(lane)) {
|
|
111
|
+
throw new Error(`approval-ask-store: ask ${askId} carries execution_lane ${JSON.stringify(lane)}, which is not an execution lane — the execution placement switch migration backfills every row; refusing rather than guess the lane this ask was raised on`);
|
|
112
|
+
}
|
|
113
|
+
return lane;
|
|
114
|
+
}
|
|
102
115
|
export const APPROVAL_BATCH_TABLE = "approval_batch";
|
|
103
116
|
export const TIDB_APPROVAL_ASK_STATEMENTS = [
|
|
104
117
|
`CREATE TABLE IF NOT EXISTS ${APPROVAL_BATCH_TABLE} (
|
|
@@ -118,6 +131,9 @@ export const TIDB_APPROVAL_ASK_STATEMENTS = [
|
|
|
118
131
|
source_task_id VARCHAR(255) NOT NULL,
|
|
119
132
|
session_id VARCHAR(64) NOT NULL,
|
|
120
133
|
owner VARCHAR(190) NULL,
|
|
134
|
+
-- execution_lane:造这只问的那条执行的车道(审批出处带车道;ensureAsk 铸行时写,既有行命中不改)。DEFAULT '' 只为存量库一句加列
|
|
135
|
+
-- (一次性迁移加列并按根会话放置回填,迁移后没有 '' 行)。
|
|
136
|
+
${APPROVAL_ASK_EXECUTION_LANE_COLUMN.mysql},
|
|
121
137
|
batch_id VARCHAR(64) NOT NULL,
|
|
122
138
|
tool_call_id VARCHAR(255) NOT NULL,
|
|
123
139
|
-- leg_key: 车3 §3.2′ 身份轴——sha256(resume checkpoint token) 的 hex(64 字符),首腿存空串。
|
|
@@ -172,7 +188,7 @@ export const TIDB_APPROVAL_ASK_STATEMENTS = [
|
|
|
172
188
|
UNIQUE KEY uq_approval_ask_idem (task_id, idempotency_key),
|
|
173
189
|
KEY idx_ask_task_state (task_id, state),
|
|
174
190
|
-- idx_ask_session_state: 车3 §5.1 的 sync 腿开流重放读口(listPendingBySession)的支撑索引。
|
|
175
|
-
-- 表尚未进生产
|
|
191
|
+
-- 表尚未进生产 ⇒ 此刻加索引零迁移成本,晚一车加就要动生产表。
|
|
176
192
|
KEY idx_ask_session_state (session_id, state),
|
|
177
193
|
KEY idx_ask_batch (batch_id),
|
|
178
194
|
KEY idx_ask_state (state)
|
|
@@ -206,6 +222,8 @@ export async function ensurePgApprovalAskSchema(q) {
|
|
|
206
222
|
source_task_id VARCHAR(255) COLLATE "C" NOT NULL,
|
|
207
223
|
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
208
224
|
owner VARCHAR(190) COLLATE "C",
|
|
225
|
+
-- execution_lane —— MySQL twin 逐字同形(语义见那侧注)。
|
|
226
|
+
${APPROVAL_ASK_EXECUTION_LANE_COLUMN.pg},
|
|
209
227
|
batch_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
210
228
|
tool_call_id VARCHAR(255) COLLATE "C" NOT NULL,
|
|
211
229
|
-- leg_key: 车3 §3.2′ 身份轴——sha256(resume checkpoint token) 的 hex(64 字符),首腿存空串
|
|
@@ -258,18 +276,16 @@ export async function assertApprovalAskRuleMaterialSchema(query, dialect) {
|
|
|
258
276
|
`persisted on the row too, so a replica that does not hold the waiting leg replays the approved bytes; every ask-row ` +
|
|
259
277
|
`write and read names these columns unconditionally, so on this table the whole in-stream approval protocol degrades silently (no card ` +
|
|
260
278
|
`is persisted, the replay list stays empty, late decisions 404) while /v1/capabilities still reports it active. ` +
|
|
261
|
-
`
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
`in-flight card falls back to the durable gate (\`POST /v1/approvals/:sessionId/decide\`). ` +
|
|
267
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}`);
|
|
279
|
+
`The table holds IN-FLIGHT approval asks (cards awaiting an answer) plus settled bookkeeping; the durable park face ` +
|
|
280
|
+
`(checkpoint rows) and the persisted rules themselves are untouched, so an in-flight card falls back to the durable gate ` +
|
|
281
|
+
`(\`POST /v1/approvals/:sessionId/decide\`) until the columns exist.\n` +
|
|
282
|
+
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
283
|
+
(await describeProbedTableDrift(query, dialect, [APPROVAL_ASK_TABLE])));
|
|
268
284
|
}
|
|
269
285
|
}
|
|
270
286
|
function askCols(dialect) {
|
|
271
287
|
return [
|
|
272
|
-
"ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state",
|
|
288
|
+
"ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state",
|
|
273
289
|
"provisional, rev, decision",
|
|
274
290
|
jsonColumnText(dialect, "decision_actor"),
|
|
275
291
|
"decision_note, settled_by",
|
|
@@ -307,6 +323,7 @@ function mapAskRowBody(r, askId) {
|
|
|
307
323
|
sourceTaskId: String(r.source_task_id),
|
|
308
324
|
sessionId: String(r.session_id),
|
|
309
325
|
owner: strOrNull(r.owner),
|
|
326
|
+
executionLane: executionLaneColumnOf(r.execution_lane, askId),
|
|
310
327
|
batchId: String(r.batch_id),
|
|
311
328
|
toolCallId: String(r.tool_call_id),
|
|
312
329
|
legKey: String(r.leg_key),
|
|
@@ -369,7 +386,7 @@ function patchColumns(patch, json) {
|
|
|
369
386
|
return out;
|
|
370
387
|
}
|
|
371
388
|
function ph(dialect, n) {
|
|
372
|
-
return dialect === "
|
|
389
|
+
return dialect === "mysql" ? "?" : `$${n}`;
|
|
373
390
|
}
|
|
374
391
|
function pendingClaimWhere(dialect, firstParam) {
|
|
375
392
|
return `ask_id = ${ph(dialect, firstParam)} AND batch_id = ${ph(dialect, firstParam + 1)} AND state = 'STREAM_PENDING'`;
|
|
@@ -377,15 +394,15 @@ function pendingClaimWhere(dialect, firstParam) {
|
|
|
377
394
|
function decideOwnerWhere(dialect, owner, param) {
|
|
378
395
|
if (owner === null)
|
|
379
396
|
return { sql: "owner IS NULL", params: [] };
|
|
380
|
-
return dialect === "
|
|
397
|
+
return dialect === "mysql" ? { sql: "CAST(owner AS BINARY) = CAST(? AS BINARY)", params: [owner] } : { sql: `owner = ${ph(dialect, param)}`, params: [owner] };
|
|
381
398
|
}
|
|
382
399
|
export class SqlApprovalAskStore {
|
|
383
400
|
db;
|
|
384
401
|
constructor(db) {
|
|
385
402
|
this.db = db;
|
|
386
403
|
}
|
|
387
|
-
q(
|
|
388
|
-
return this.db.dialect === "
|
|
404
|
+
q(mysql, pg) {
|
|
405
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
389
406
|
}
|
|
390
407
|
json(value) {
|
|
391
408
|
return dialectJsonEncoder(this.db.dialect)(value);
|
|
@@ -394,7 +411,7 @@ export class SqlApprovalAskStore {
|
|
|
394
411
|
return isDupKeyError(this.db.dialect, e);
|
|
395
412
|
}
|
|
396
413
|
async getAskOn(conn, askId) {
|
|
397
|
-
const { rows } = await conn.query(this.q(`SELECT ${askCols("
|
|
414
|
+
const { rows } = await conn.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE ask_id = $1`), [askId]);
|
|
398
415
|
return rows[0] ? mapAskRow(rows[0]) : null;
|
|
399
416
|
}
|
|
400
417
|
async getBatchOn(conn, batchId) {
|
|
@@ -410,14 +427,15 @@ export class SqlApprovalAskStore {
|
|
|
410
427
|
try {
|
|
411
428
|
await conn.begin();
|
|
412
429
|
await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_BATCH_TABLE} (batch_id, task_id, state, rev, created_at_ms, updated_at_ms) VALUES (?, ?, 'OPEN', 0, ?, ?)`, `INSERT INTO ${APPROVAL_BATCH_TABLE} (batch_id, task_id, state, rev, created_at_ms, updated_at_ms) VALUES ($1, $2, 'OPEN', 0, $3, $4) ON CONFLICT (batch_id) DO NOTHING`), [row.batchId, row.taskId, now, now]);
|
|
413
|
-
const askInsert = await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
|
|
414
|
-
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?, ?, ?, ?, ?, ?)", `INSERT INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
|
|
415
|
-
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, $
|
|
430
|
+
const askInsert = await conn.query(this.q(`INSERT IGNORE INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
|
|
431
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?, ?, ?, ?, ?, ?)", `INSERT INTO ${APPROVAL_ASK_TABLE} (ask_id, task_id, source_task_id, session_id, owner, execution_lane, batch_id, tool_call_id, leg_key, parent_tool_call_id, bound_input_hash, state, provisional, rev, decision, decision_actor, decision_note, settled_by, decided_at_ms, denied_reason, gate_token, gate_bound_call_id, gate_bound_input_hash, idempotency_key, card_json, rule_command, rule_scope_root, schema_version, expires_at_ms, created_at_ms, updated_at_ms) ` +
|
|
432
|
+
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 'STREAM_PENDING', 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, $12, $13, $14, $15, $16, $17, $18) ON CONFLICT (ask_id) DO NOTHING"), [
|
|
416
433
|
row.askId,
|
|
417
434
|
row.taskId,
|
|
418
435
|
row.sourceTaskId,
|
|
419
436
|
row.sessionId,
|
|
420
437
|
row.owner ?? null,
|
|
438
|
+
row.executionLane,
|
|
421
439
|
row.batchId,
|
|
422
440
|
row.toolCallId,
|
|
423
441
|
row.legKey,
|
|
@@ -686,40 +704,40 @@ export class SqlApprovalAskStore {
|
|
|
686
704
|
}
|
|
687
705
|
}
|
|
688
706
|
async listByState(state, limit) {
|
|
689
|
-
const { rows } = await this.db.query(this.q(`SELECT ${askCols("
|
|
707
|
+
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE state = ? ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE state = $1 ORDER BY updated_at_ms ASC, created_at_ms ASC LIMIT $2`), [state, limit]);
|
|
690
708
|
return rows.map(mapAskRow);
|
|
691
709
|
}
|
|
692
710
|
async listPendingByTask(taskId, signal) {
|
|
693
711
|
if (signal?.aborted === true)
|
|
694
712
|
throw abortedReadError("listPendingByTask");
|
|
695
|
-
const { rows } = await withReadSignal(this.db.query(this.q(`SELECT ${askCols("
|
|
713
|
+
const { rows } = await withReadSignal(this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND state = 'STREAM_PENDING' ORDER BY created_at_ms ASC`), [taskId]), signal, "listPendingByTask");
|
|
696
714
|
return rows.map(mapAskRow);
|
|
697
715
|
}
|
|
698
716
|
async listPendingBySession(sessionId, owner, signal) {
|
|
699
717
|
if (signal?.aborted === true)
|
|
700
718
|
throw abortedReadError("listPendingBySession");
|
|
701
719
|
const { rows } = await withReadSignal(this.db.query(owner === null
|
|
702
|
-
? this.q(`SELECT ${askCols("
|
|
703
|
-
: this.q(`SELECT ${askCols("
|
|
720
|
+
? this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner IS NULL ORDER BY created_at_ms ASC`)
|
|
721
|
+
: this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'STREAM_PENDING' AND owner = ? ORDER BY created_at_ms ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'STREAM_PENDING' AND owner = $2 ORDER BY created_at_ms ASC`), owner === null ? [sessionId] : [sessionId, owner]), signal, "listPendingBySession");
|
|
704
722
|
return rows.map(mapAskRow);
|
|
705
723
|
}
|
|
706
724
|
async listByCall(call) {
|
|
707
|
-
const { rows } = await this.db.query(this.q(`SELECT ${askCols("
|
|
725
|
+
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND leg_key = ? AND tool_call_id = ? AND source_task_id = ? ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND leg_key = $2 AND tool_call_id = $3 AND source_task_id = $4 ORDER BY created_at_ms ASC, ask_id ASC`), [call.taskId, call.legKey, call.toolCallId, call.sourceTaskId]);
|
|
708
726
|
return rows.map(mapAskRow).filter((r) => askRowOnCall(r, call));
|
|
709
727
|
}
|
|
710
728
|
async listParkingBySession(sessionId) {
|
|
711
|
-
const { rows } = await this.db.query(this.q(`SELECT ${askCols("
|
|
729
|
+
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = ? AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE session_id = $1 AND state = 'PARKING' ORDER BY created_at_ms ASC, ask_id ASC`), [sessionId]);
|
|
712
730
|
return rows.map(mapAskRow).filter((r) => r.sessionId === sessionId);
|
|
713
731
|
}
|
|
714
732
|
async getAsk(askId) {
|
|
715
|
-
const { rows } = await this.db.query(this.q(`SELECT ${askCols("
|
|
733
|
+
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`), [
|
|
716
734
|
askId,
|
|
717
735
|
]);
|
|
718
736
|
return rows[0] ? mapAskRow(rows[0]) : null;
|
|
719
737
|
}
|
|
720
738
|
async getByIdempotencyKey(taskId, key) {
|
|
721
739
|
assertIdempotencyKeyShape(key);
|
|
722
|
-
const { rows } = await this.db.query(this.q(`SELECT ${askCols("
|
|
740
|
+
const { rows } = await this.db.query(this.q(`SELECT ${askCols("mysql")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ? AND idempotency_key = ?`, `SELECT ${askCols("pg")} FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1 AND idempotency_key = $2`), [taskId, key]);
|
|
723
741
|
return rows[0] ? mapAskRow(rows[0]) : null;
|
|
724
742
|
}
|
|
725
743
|
async deferReconcile(askId, expectedState, expectedRev, nowMs) {
|
|
@@ -754,7 +772,7 @@ export class SqlApprovalAskStore {
|
|
|
754
772
|
const ANCHOR_COLUMNS = ["task_id", "session_id", "source_task_id"];
|
|
755
773
|
let keepSql = "";
|
|
756
774
|
let keepParams = [];
|
|
757
|
-
if (keep.length > 0 && dialect === "
|
|
775
|
+
if (keep.length > 0 && dialect === "mysql") {
|
|
758
776
|
const list = keep.map(() => "?").join(", ");
|
|
759
777
|
keepSql = ANCHOR_COLUMNS.map((c) => ` AND ${c} NOT IN (${list})`).join("");
|
|
760
778
|
keepParams = ANCHOR_COLUMNS.flatMap(() => keep);
|
|
@@ -10,15 +10,15 @@ export class SqlApprovalNonceStore {
|
|
|
10
10
|
constructor(db) {
|
|
11
11
|
this.db = db;
|
|
12
12
|
}
|
|
13
|
-
q(
|
|
14
|
-
return this.db.dialect === "
|
|
13
|
+
q(mysql, pg) {
|
|
14
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
15
15
|
}
|
|
16
16
|
async nowMs() {
|
|
17
17
|
return sqlDbNowMs(this.db, APPROVAL_NONCE_TABLE);
|
|
18
18
|
}
|
|
19
19
|
async consume(sessionId, nonce, expiresAtMs) {
|
|
20
20
|
try {
|
|
21
|
-
await this.db.query(this.q(`INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES (?,?,${SQL_DB_NOW_MS.
|
|
21
|
+
await this.db.query(this.q(`INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES (?,?,${SQL_DB_NOW_MS.mysql},?)`, `INSERT INTO ${APPROVAL_NONCE_TABLE} (session_id, nonce, consumed_at_ms, expires_at_ms) VALUES ($1,$2,${SQL_DB_NOW_MS.pg},$3)`), [sessionId, nonce, expiresAtMs]);
|
|
22
22
|
return true;
|
|
23
23
|
}
|
|
24
24
|
catch (err) {
|
|
@@ -28,7 +28,7 @@ export class SqlApprovalNonceStore {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
async reapExpired() {
|
|
31
|
-
const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.
|
|
31
|
+
const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.mysql}`, `DELETE FROM ${APPROVAL_NONCE_TABLE} WHERE expires_at_ms <= ${SQL_DB_NOW_MS.pg}`), []);
|
|
32
32
|
return res.affected;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
|
-
import { type SqlEngineFacts, type SqlEngineReading, type SqlWallSource, type SqlWallsReading } from "./sql-driver.js";
|
|
3
|
+
import { type SqlEngineFacts, type SqlEngineReading, type SqlWallSource, type SqlWallsReading, type SqlDialect } from "./sql-driver.js";
|
|
4
4
|
import { type Logger } from "../observability/logger.js";
|
|
5
5
|
/**
|
|
6
6
|
* 文本协议的 blob 膨胀律 —— mysql2 `query()` 把 Buffer 参数写成 `X'<hex>'`,一字节两个字符,所以承载一个 blob 的语句至少是它的 2 倍。
|
|
@@ -115,7 +115,7 @@ export declare class SqlBlobBackend implements BlobBackend {
|
|
|
115
115
|
private readonly db;
|
|
116
116
|
/** Where a stale connection's replacement is logged ({@link connectJudged}); the boot assembly passes the service logger. */
|
|
117
117
|
private readonly log;
|
|
118
|
-
constructor(dialect:
|
|
118
|
+
constructor(dialect: SqlDialect, pool: MysqlPool | PgPool, maxBytes?: number, table?: string, log?: Pick<Logger, "warn">);
|
|
119
119
|
/** The created_at-refreshing content-addressed upsert — VERBATIM the tidb/pg stores' `putBlobRow`. Refreshes
|
|
120
120
|
* created_at even on a dedup hit so a re-used/just-written blob is "touched=now" and GC-grace-protected.
|
|
121
121
|
* (a): an over-cap blob is REJECTED (typed {@link BlobTooLargeError}) BEFORE the driver ever sees the bytes —
|
|
@@ -140,7 +140,7 @@ export declare class SqlBlobBackend implements BlobBackend {
|
|
|
140
140
|
/** SHARED index op(workspace 浏览面 #3,[ref]②)—— per-blob 字节数,读 SQL INDEX 的 `byte_len`
|
|
141
141
|
* 列(sqlHasBlobs 同款单 IN 查询;MinIO 后端的 bytes-NULL 索引行同样带 byte_len,两后端同源)。
|
|
142
142
|
* tree/file 面的 size 投影与「超限先拒后拉」都靠它——绝不为拿大小去 getBlob 整块字节。 */
|
|
143
|
-
export declare function sqlBlobSizes(dialect:
|
|
143
|
+
export declare function sqlBlobSizes(dialect: SqlDialect, pool: MysqlPool | PgPool, hashes: string[]): Promise<Map<string, number>>;
|
|
144
144
|
export interface MinioBlobBackendConfig {
|
|
145
145
|
endpoint: string;
|
|
146
146
|
bucket: string;
|
|
@@ -198,7 +198,7 @@ export declare class MinioBlobBackend implements BlobBackend {
|
|
|
198
198
|
/** Blob-domain index table ([ref] 域隔离,SqlBlobBackend.table 同款;默认 = E19 纪元的 snapshot_blob)。 */
|
|
199
199
|
private readonly indexTable;
|
|
200
200
|
constructor(cfg: MinioBlobBackendConfig, index?: {
|
|
201
|
-
dialect:
|
|
201
|
+
dialect: SqlDialect;
|
|
202
202
|
pool: MysqlPool | PgPool;
|
|
203
203
|
table?: string;
|
|
204
204
|
});
|
|
@@ -76,7 +76,7 @@ export class SqlBlobBackend {
|
|
|
76
76
|
this.pool = pool;
|
|
77
77
|
this.explicitCapBytes = maxBytes;
|
|
78
78
|
this.table = table;
|
|
79
|
-
this.db = dialect === "
|
|
79
|
+
this.db = dialect === "mysql" ? mysqlDriver(pool) : pgDriver(pool);
|
|
80
80
|
this.log = log ?? createLogger();
|
|
81
81
|
}
|
|
82
82
|
async putBlob(hash, bytes) {
|
|
@@ -90,7 +90,7 @@ export class SqlBlobBackend {
|
|
|
90
90
|
if (judged.refusal !== undefined)
|
|
91
91
|
throw new BlobTooLargeError(bytes.byteLength, judged.refusal.cap);
|
|
92
92
|
try {
|
|
93
|
-
await judged.conn.query(this.dialect === "
|
|
93
|
+
await judged.conn.query(this.dialect === "mysql"
|
|
94
94
|
? `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`
|
|
95
95
|
: `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES ($1,$2,$3,$4) ON CONFLICT (blob_hash) DO UPDATE SET created_at = EXCLUDED.created_at`, [hash, bytes.byteLength, Buffer.from(bytes), new Date()]);
|
|
96
96
|
}
|
|
@@ -99,7 +99,7 @@ export class SqlBlobBackend {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
async getBlob(hash) {
|
|
102
|
-
if (this.dialect === "
|
|
102
|
+
if (this.dialect === "mysql") {
|
|
103
103
|
const [rows] = await this.pool.query(`SELECT bytes FROM ${this.table} WHERE blob_hash = ?`, [hash]);
|
|
104
104
|
return rows[0]?.bytes != null ? new Uint8Array(rows[0].bytes) : undefined;
|
|
105
105
|
}
|
|
@@ -118,7 +118,7 @@ async function sqlHasBlobs(dialect, pool, hashes, table = "snapshot_blob") {
|
|
|
118
118
|
if (hashes.length === 0)
|
|
119
119
|
return new Set();
|
|
120
120
|
const distinct = [...new Set(hashes)];
|
|
121
|
-
if (dialect === "
|
|
121
|
+
if (dialect === "mysql") {
|
|
122
122
|
const ph = distinct.map(() => "?").join(",");
|
|
123
123
|
const [rows] = await pool.query(`SELECT blob_hash FROM ${table} WHERE blob_hash IN (${ph})`, distinct);
|
|
124
124
|
return new Set(rows.map((r) => String(r.blob_hash)));
|
|
@@ -135,7 +135,7 @@ export async function sqlBlobSizes(dialect, pool, hashes) {
|
|
|
135
135
|
const out = new Map();
|
|
136
136
|
for (let i = 0; i < distinct.length; i += CHUNK) {
|
|
137
137
|
const part = distinct.slice(i, i + CHUNK);
|
|
138
|
-
if (dialect === "
|
|
138
|
+
if (dialect === "mysql") {
|
|
139
139
|
const ph = part.map(() => "?").join(",");
|
|
140
140
|
const [rows] = await pool.query(`SELECT blob_hash, byte_len FROM snapshot_blob WHERE blob_hash IN (${ph})`, part);
|
|
141
141
|
for (const r of rows)
|
|
@@ -155,7 +155,7 @@ async function sqlDeleteIndexRowsPastGrace(dialect, pool, hashes, table = "snaps
|
|
|
155
155
|
return [];
|
|
156
156
|
const distinct = [...new Set(hashes)];
|
|
157
157
|
const cutoff = new Date(Date.now() - BLOB_GC_GRACE_MS);
|
|
158
|
-
if (dialect === "
|
|
158
|
+
if (dialect === "mysql") {
|
|
159
159
|
const ph = distinct.map(() => "?").join(",");
|
|
160
160
|
const grace = await pool
|
|
161
161
|
.query(`SELECT blob_hash FROM ${table} WHERE created_at < ? AND blob_hash IN (${ph})`, [cutoff, ...distinct])
|
|
@@ -216,7 +216,7 @@ export class MinioBlobBackend {
|
|
|
216
216
|
throw new Error(`MinioBlobBackend.putBlob ${hash}: HTTP ${res.status} ${detail.slice(0, 200)}`);
|
|
217
217
|
}
|
|
218
218
|
if (this.indexPool && this.indexDialect) {
|
|
219
|
-
if (this.indexDialect === "
|
|
219
|
+
if (this.indexDialect === "mysql") {
|
|
220
220
|
await this.indexPool.query(`INSERT INTO ${this.indexTable} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,NULL,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`, [hash, bytes.byteLength, new Date()]);
|
|
221
221
|
}
|
|
222
222
|
else {
|
|
@@ -24,8 +24,8 @@ export class SqlBreakerState {
|
|
|
24
24
|
this.db = db;
|
|
25
25
|
this.onWriteFail = onWriteFail;
|
|
26
26
|
}
|
|
27
|
-
q(
|
|
28
|
-
return this.db.dialect === "
|
|
27
|
+
q(mysql, pg) {
|
|
28
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
29
29
|
}
|
|
30
30
|
get(key) {
|
|
31
31
|
return this.cache.get(key);
|
|
@@ -54,7 +54,7 @@ export declare class CachingSessionStore implements SessionStore {
|
|
|
54
54
|
private readonly onStaleHit?;
|
|
55
55
|
/** core `SessionStore.headOf` —— 按 id 读一行(属主 / 住客 / 叶子)的唯一读口,**纯转发、不经暖缓存**(答的是行,绝不从暖缓存里那只会话的元数据答 ——
|
|
56
56
|
* 缓存的会话活得比「另一只店删掉又重建了这一行」长)。内层店必有(本类的暖命中按它核新鲜)。🔴 必须转发:生产默认把真店包进本类,授权口 / 属主门 /
|
|
57
|
-
*
|
|
57
|
+
* k8s 工作区空间 / SSE 探针 / resume-at 读的都是本类。 */
|
|
58
58
|
readonly headOf: SessionHeadReader["headOf"];
|
|
59
59
|
readonly register?: (sessionId: string, owner: string | null) => Promise<void>;
|
|
60
60
|
/** [ref] 委派继承律的认领口,纯转发(写的是行上的属主那一格 —— 只 CAS 子行自己,不碰暖缓存里的会话;停驻挂点在默认部署上读的就是本类 —— 与 `placementOf` 同转发同缺:内店记录委派树 ⇒ 两只都在)。 */
|
|
@@ -72,12 +72,16 @@ export declare class CachingSessionStore implements SessionStore {
|
|
|
72
72
|
q?: string;
|
|
73
73
|
}) => Promise<SessionListItem[]>;
|
|
74
74
|
readonly fork?: (sourceId: string, owner: string | null) => Promise<string | null>;
|
|
75
|
+
/** fork 续写的店契约(原样转发;与 fork 同理绕过暖缓存 —— 读写的是持久行,新 id 本来就不在缓存里)。 */
|
|
76
|
+
readonly forkTo?: OwnerAwareSessionStore["forkTo"];
|
|
77
|
+
/** 执行席(原样转发:放置是会话**行**上的一格,缓存只缓存会话日志的暖句柄,不缓存行格 —— 读与写都直达内层店)。 */
|
|
78
|
+
readonly executionPlacement?: OwnerAwareSessionStore["executionPlacement"];
|
|
75
79
|
readonly deleteSession?: (sessionId: string, owner: string | null) => Promise<boolean>;
|
|
76
80
|
/** The session row's list projection for one id + its writable bits (`title` / `pinned`) — DURABLE, pure pass-through
|
|
77
81
|
* (they read / write `session_meta` / the local sidecars, never the warm cache). 🔴 Must forward (the retention / placements
|
|
78
82
|
* lesson): the production default wraps the real store in this class, and the route's fact `sessionPatch` reads THIS object. */
|
|
79
83
|
readonly sessionSummaryOf?: (sessionId: string) => Promise<SessionListItem | undefined>;
|
|
80
|
-
readonly
|
|
84
|
+
readonly writeSessionRowCells?: OwnerAwareSessionStore["writeSessionRowCells"];
|
|
81
85
|
/** The auto-titler's NULL-only title write (SQL twins + local sidecar carry it; pure pass-through). */
|
|
82
86
|
readonly setTitleIfNull?: (sessionId: string, title: string) => Promise<boolean>;
|
|
83
87
|
readonly probeTitle?: (sessionId: string) => Promise<"none" | "untitled" | "titled">;
|
|
@@ -12,9 +12,11 @@ export class CachingSessionStore {
|
|
|
12
12
|
claimOwnerless;
|
|
13
13
|
listSessions;
|
|
14
14
|
fork;
|
|
15
|
+
forkTo;
|
|
16
|
+
executionPlacement;
|
|
15
17
|
deleteSession;
|
|
16
18
|
sessionSummaryOf;
|
|
17
|
-
|
|
19
|
+
writeSessionRowCells;
|
|
18
20
|
setTitleIfNull;
|
|
19
21
|
probeTitle;
|
|
20
22
|
readInstructionSource;
|
|
@@ -57,8 +59,8 @@ export class CachingSessionStore {
|
|
|
57
59
|
this.listSessions = ownerAware.listSessions.bind(inner);
|
|
58
60
|
if (ownerAware.sessionSummaryOf)
|
|
59
61
|
this.sessionSummaryOf = ownerAware.sessionSummaryOf.bind(inner);
|
|
60
|
-
if (ownerAware.
|
|
61
|
-
this.
|
|
62
|
+
if (ownerAware.writeSessionRowCells)
|
|
63
|
+
this.writeSessionRowCells = ownerAware.writeSessionRowCells.bind(inner);
|
|
62
64
|
if (ownerAware.setTitleIfNull)
|
|
63
65
|
this.setTitleIfNull = ownerAware.setTitleIfNull.bind(inner);
|
|
64
66
|
if (ownerAware.probeTitle)
|
|
@@ -73,6 +75,10 @@ export class CachingSessionStore {
|
|
|
73
75
|
this.recordSessionWorkspace = ownerAware.recordSessionWorkspace.bind(inner);
|
|
74
76
|
if (ownerAware.fork)
|
|
75
77
|
this.fork = ownerAware.fork.bind(inner);
|
|
78
|
+
if (ownerAware.forkTo)
|
|
79
|
+
this.forkTo = ownerAware.forkTo.bind(inner);
|
|
80
|
+
if (ownerAware.executionPlacement)
|
|
81
|
+
this.executionPlacement = ownerAware.executionPlacement;
|
|
76
82
|
if (ownerAware.exportEntries)
|
|
77
83
|
this.exportEntries = ownerAware.exportEntries.bind(inner);
|
|
78
84
|
if (ownerAware.listEntryIds)
|
|
@@ -33,7 +33,7 @@ export function buildCheckpointIndexCells(cp) {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
export const APPROVAL_CARD_COLUMN_MAX_BYTES = {
|
|
36
|
-
|
|
36
|
+
mysql: 65_535,
|
|
37
37
|
pg: Number.POSITIVE_INFINITY,
|
|
38
38
|
};
|
|
39
39
|
export function judgeCheckpointIndexCells(cp, dialect) {
|
|
@@ -83,7 +83,7 @@ function renderFacetCase(compared) {
|
|
|
83
83
|
return `CASE ${CHECKPOINT_FACET_CASES.map((c) => `WHEN ${compared(c.column)} IN (${c.words.map(literal).join(", ")}) THEN ${literal(c.facet)}`).join(" ")} END`;
|
|
84
84
|
}
|
|
85
85
|
export const CHECKPOINT_FACET_SQL = Object.freeze({
|
|
86
|
-
|
|
86
|
+
mysql: renderFacetCase((column) => `CAST(${column} AS BINARY)`),
|
|
87
87
|
pg: renderFacetCase((column) => column),
|
|
88
88
|
});
|
|
89
89
|
export const CHECKPOINT_SWEEP_ORDER_SQL = "CASE WHEN approval_card IS NULL THEN 1 ELSE 0 END ASC, deadline ASC, token ASC";
|
|
@@ -240,8 +240,8 @@ export declare function pendingRowPrecedes(a: {
|
|
|
240
240
|
*
|
|
241
241
|
* 🔴 为什么是一张表而不是每列一个 `assert*ColumnSchema` 函数([ref] 归一):[ref]① 那次是一列一函数,
|
|
242
242
|
* 本批 [ref] 又要加两列 —— 照抄就是第二、第三份三十行的同形代码,而它们**唯一的差别只有列名与那句话**。
|
|
243
|
-
* 探测法(恒空 `WHERE 1=0` 读 + 方言缺列码)、失败方向(
|
|
244
|
-
* (
|
|
243
|
+
* 探测法(恒空 `WHERE 1=0` 读 + 方言缺列码)、失败方向(只在**可证**缺列时才拒)、恢复动作
|
|
244
|
+
* (部署者按拒因打印的 ALTER 在原表上加列;表里是在飞的停驻,不动它)三者对整张表是同一条,所以规则只留一条,列变成数据:下一次加列
|
|
245
245
|
* 只加一行,不再多一条规则。
|
|
246
246
|
*/
|
|
247
247
|
export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
|
|
@@ -254,14 +254,15 @@ export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
|
|
|
254
254
|
* 依据是本仓 assert*Schema 探针族的真先例与失败方向本身)。[ref] 起判据对 {@link CHECKPOINT_ADDED_COLUMNS}
|
|
255
255
|
* **整张列册**跑(见那张表的头注:归一的理由)。
|
|
256
256
|
*
|
|
257
|
-
* 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY
|
|
257
|
+
* 病:本仓不发 `ALTER TABLE`(SCHEMA POLICY:代码是唯一真源,存量库缺列由部署者按拒启句打印的 ALTER 自己执行)。一台**没跟上**就升上来的
|
|
258
258
|
* 部署,`CREATE TABLE IF NOT EXISTS` 对存量 `checkpoint` 是空操作 —— 后加的列不在,而写口与读口
|
|
259
259
|
* **无条件**引用它们。没有这道断言,服务照常过启动与健康检查,直到运行期才响,且首撞点在用户的
|
|
260
260
|
* 审批 / resume 路径上。
|
|
261
261
|
*
|
|
262
262
|
* 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**({@link isMissingColumnError},
|
|
263
|
-
* 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 ——
|
|
264
|
-
*
|
|
263
|
+
* 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
|
|
264
|
+
* 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link describeProbedTableDrift}),
|
|
265
|
+
* 没有销毁性的一条:这张表里是在飞的停驻,出路是原表上加列。
|
|
265
266
|
*
|
|
266
267
|
* 位置:与三条先例同款,不在 `ensureSchema` 里(那条通道的契约是「只发 CREATE」),放在 boot 的
|
|
267
268
|
* store 装配段、路由装配之前;**无条件**跑(与 [ref] tool_result 探针同档)——checkpoint 表在两方言的
|