@sema-agent/server 7.43.0 → 7.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
|
@@ -1,58 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* #292 C18(core 5.48.0 design/323「不造新店」提货批,黑板 [4743] 点名义务)——**subagent 转录持久化**
|
|
3
|
-
* 的 placement 语义,SQL 孪生共用的一份判据。
|
|
4
|
-
*
|
|
5
|
-
* ## 这是什么
|
|
6
|
-
*
|
|
7
|
-
* core 5.48.0 给 `SessionStore` 开了一组**全 optional** 的 placement 面:一只**声明** `placements`
|
|
8
|
-
* (「本店懂 placement 语义,且落在 subagent 分区里的会话是这个耐久度」)+ `acquire(id,{placement})`
|
|
9
|
-
* 的创建形 + `listPlaced` / `placementOf` 两只读面。声明即承诺**六条义务**(core `SessionStore.placements`
|
|
10
|
-
* 顶注逐字):
|
|
11
|
-
*
|
|
12
|
-
* 1. placed 会话享受所声明的耐久度(重启后 `acquire(id,{requireExisting:true})` 仍到得了);
|
|
13
|
-
* 2. placed 会话**不上宿主枚举面**(壳的 /resume 列表不许把子代转录当成可续聊会话);
|
|
14
|
-
* 3. **未** placed 的会话,acquire 语义逐字节不变;
|
|
15
|
-
* 4. `release(placedId)` 是**真删除**(durable 史一并),联合 reap 的「行与会话同亡」靠它;
|
|
16
|
-
* 5. placement **first-write 不可变**:re-open(`requireExisting`,带不带 placement 参数)返回同一
|
|
17
|
-
* 会话且**永不重写**元组;
|
|
18
|
-
* 6. placed id **不可被 claim**:后来的 claim 形 acquire(无 `requireExisting`、无创建 placement)
|
|
19
|
-
* 撞上已存在的 placed id ⇒ 响亮拒 `SessionError("placement_refused")`,绝不附着。
|
|
20
|
-
*
|
|
21
|
-
* ## 为什么判据住在这里而不是各写一份
|
|
22
|
-
*
|
|
23
|
-
* 本仓成文教训(`memory-origin-law.ts` 顶注同族、`key-resolver.ts` 顶注「链只有一条」):两支方言各
|
|
24
|
-
* 抄一份准入判据,迟早在某一支上少一个分支——而少掉的那一支**没有任何报错面**,表现只是「这台机器上
|
|
25
|
-
* 一个陌生人可以把自己接到别人子代的转录上」。所以准入判据(两个方向)、列名、行→元组的映射都在
|
|
26
|
-
* 这一处;两支后端只负责在自己的 I/O 点上调用它。
|
|
27
|
-
*
|
|
28
|
-
* ## 判据的两个方向(缺一不可,core `TtlSessionStore.assertPlacementAdmission` 的镜像)
|
|
29
|
-
*
|
|
30
|
-
* ① **claim 关门**:已 placed 的行拒绝 claim 形 acquire;
|
|
31
|
-
* ② **镜像方向(pre-claim squat 竞态)**:子会话 id 在 spawn 帧上**先于**子代 prepare 公布,于是一次
|
|
32
|
-
* 并发的 claim 形 acquire 可能抢先建出**普通**会话,而后带 placement 的可信调用方拿到的就是一只
|
|
33
|
-
* 宿主可枚举、release 不真删的普通会话 ——「声明了 full tier 却在静默说谎」。⇒ 在**声明了分区**的
|
|
34
|
-
* 店上,带 placement 的 acquire 必须回读到**已持久化**且同一元组身份的行,回读缺失/异组=响亮拒。
|
|
35
|
-
*/
|
|
36
1
|
import { SessionError } from "@sema-agent/core";
|
|
37
|
-
/**
|
|
38
|
-
* 两支 SQL 孪生的 placement **声明**(core `SessionStore.placements`)。
|
|
39
|
-
*
|
|
40
|
-
* 🔴 声明制,不是鸭子类型:core 的铸造门判「row store ∧ session 店声明」,不声明的店**不收** placed
|
|
41
|
-
* 语义(合法降级:placement 参数被忽略,会话就是普通会话)。所以这一格只有在上面六条义务**全部**在
|
|
42
|
-
* 本店实装之后才允许存在——它是一句关于本店行为的承诺,不是一个功能开关。
|
|
43
|
-
* `durable` 的依据:两支孪生的会话史落在 `session_meta`+`session_event` 两张真表上,进程重启后
|
|
44
|
-
* `acquire(id,{requireExisting:true})` 从事件日志重放(义务 1 的机器证在
|
|
45
|
-
* `test/session-placement-db-integration.test.ts` 的跨进程复活格)。
|
|
46
|
-
*/
|
|
47
2
|
export const SQL_SESSION_PLACEMENTS = { subagent: { durability: "durable" } };
|
|
48
|
-
/**
|
|
49
|
-
* `session_meta` 上承载 placement 元组的六列,**两方言同名**(单点定义:列名手抄两份必漂,而漂掉的
|
|
50
|
-
* 那一支只会表现为「这台机器上 placed 行读不出来」——静默,不报错)。
|
|
51
|
-
* `placed_at` 是 GC 的年龄锚(core `PlacedSessionRow.placedAt`);其余五列是元组本身。
|
|
52
|
-
*/
|
|
53
3
|
export const PLACEMENT_SELECT_COLUMNS = "placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at";
|
|
54
|
-
/** DATETIME/TIMESTAMPTZ 单元格 → epoch ms。两支池都返回 `Date`(mysql2 `timezone:"Z"` / node-pg 原生),
|
|
55
|
-
* 字符串臂是防御性回落(驱动配置若变,宁可解析一次也不要静默把 placedAt 读成 NaN)。 */
|
|
56
4
|
function toEpochMs(v) {
|
|
57
5
|
if (v == null)
|
|
58
6
|
return undefined;
|
|
@@ -62,14 +10,6 @@ function toEpochMs(v) {
|
|
|
62
10
|
return Number.isFinite(ms) ? ms : undefined;
|
|
63
11
|
}
|
|
64
12
|
const asStringOrUndefined = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
|
|
65
|
-
/**
|
|
66
|
-
* 行 → 持久化的 placement 元组(`undefined` = 这是一只普通会话)。
|
|
67
|
-
*
|
|
68
|
-
* 🔴 判别位是 `placement_kind`:它在场即 placed。`placed_at` 缺失(带外 SQL 写进半只元组)时回落
|
|
69
|
-
* `0` 而**不是**把整只元组读成缺席——把「元组半缺」读成「没有 placement」等于让义务 2/4/6 在那一行上
|
|
70
|
-
* 集体失效(宿主枚举会重新列出它、release 退回租约语义);`listPlaced` 那一侧另有 `tupleIncomplete`
|
|
71
|
-
* 的诚实变体负责让 GC 对它 fail-closed。
|
|
72
|
-
*/
|
|
73
13
|
export function placementFromRow(row) {
|
|
74
14
|
if (row === undefined)
|
|
75
15
|
return undefined;
|
|
@@ -85,12 +25,6 @@ export function placementFromRow(row) {
|
|
|
85
25
|
placedAt: toEpochMs(row.placed_at) ?? 0,
|
|
86
26
|
};
|
|
87
27
|
}
|
|
88
|
-
/**
|
|
89
|
-
* 行 → 一条 {@link PlacedSessionRow}(`listPlaced` 的行形)。
|
|
90
|
-
*
|
|
91
|
-
* core 契约:join 键是**完整**的 `(scope, handle)`;缺任何一半 ⇒ 返回 `tupleIncomplete` 的诚实变体,
|
|
92
|
-
* 消费方对它 fail-closed(永不按年龄收割)。一个「无行」与「另一个租户有行」分不清的 join 不许删东西。
|
|
93
|
-
*/
|
|
94
28
|
export function placedRowFromRow(sessionId, row, lastActivityMs) {
|
|
95
29
|
const placedAt = toEpochMs(row.placed_at) ?? lastActivityMs ?? 0;
|
|
96
30
|
const scope = asStringOrUndefined(row.placement_scope);
|
|
@@ -99,7 +33,6 @@ export function placedRowFromRow(sessionId, row, lastActivityMs) {
|
|
|
99
33
|
? { sessionId, placedAt, scope, handle }
|
|
100
34
|
: { sessionId, placedAt, tupleIncomplete: true };
|
|
101
35
|
}
|
|
102
|
-
/** 创建时一次落行的六个绑定值(顺序 = {@link PLACEMENT_SELECT_COLUMNS});无 placement ⇒ 六个 NULL。 */
|
|
103
36
|
export function placementInsertValues(placement, placedAt) {
|
|
104
37
|
if (placement === undefined)
|
|
105
38
|
return [null, null, null, null, null, null];
|
|
@@ -112,23 +45,9 @@ export function placementInsertValues(placement, placedAt) {
|
|
|
112
45
|
placedAt,
|
|
113
46
|
];
|
|
114
47
|
}
|
|
115
|
-
/**
|
|
116
|
-
* 准入判据 —— **两个方向**(顶注 ①②;core `TtlSessionStore.assertPlacementAdmission` 的 SQL 镜像,
|
|
117
|
-
* 逐条同义,文案自铸)。
|
|
118
|
-
*
|
|
119
|
-
* @param persisted 该 id 在库里**已持久化**的元组(缺席 = 普通会话 / 无此行)。
|
|
120
|
-
*
|
|
121
|
-
* · 方向①(claim 关门):`persisted` 在场 + 非 `requireExisting` + 调用方没带创建 placement ⇒ 拒。
|
|
122
|
-
* `requireExisting`(读/复活形,义务 5)与「创建调用及其幂等重取」(带 placement 的可信内部链)
|
|
123
|
-
* 是仅有的两个合法形。placement 不可变意味着带 placement 的参数**永远重写不了**任何东西——持久化
|
|
124
|
-
* 的那一份恒赢,所以放行它不开任何后门。
|
|
125
|
-
* · 方向②(镜像/squat):带 placement 的创建形回读到**缺席**或**异组**元组 ⇒ 拒。缺席 = 并发的
|
|
126
|
-
* claim 形抢先建出了普通会话(或带外写把元组抹了);异组 = 同 id 上坐着另一条委派链的转录。
|
|
127
|
-
* 两者都不许让「声明了 durable 分区」这句话在静默中变成谎。
|
|
128
|
-
*/
|
|
129
48
|
export function assertPlacementAdmission(sessionId, persisted, opts) {
|
|
130
49
|
if (opts?.requireExisting === true)
|
|
131
|
-
return;
|
|
50
|
+
return;
|
|
132
51
|
if (persisted !== undefined && opts?.placement === undefined) {
|
|
133
52
|
throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${persisted.kind} placement partition — a claim-form acquire cannot attach to it (read it with requireExisting, or address the agent through its a* handle)`);
|
|
134
53
|
}
|
|
@@ -142,12 +61,5 @@ export function assertPlacementAdmission(sessionId, persisted, opts) {
|
|
|
142
61
|
throw new SessionError("placement_refused", `session ${sessionId}'s persisted placement does not match the declared one (persisted ${persisted.kind}/${persisted.scope ?? "-"}/${persisted.handle ?? "-"} vs declared ${want.kind}/${want.scope ?? "-"}/${want.handle ?? "-"}) — refusing a cross-tuple attach`);
|
|
143
62
|
}
|
|
144
63
|
}
|
|
145
|
-
/**
|
|
146
|
-
* 一次 `listPlaced` 最多带回的行数。
|
|
147
|
-
*
|
|
148
|
-
* core 契约不设上界,但两支孪生面对的是一张可以很大的真表:无界 SELECT 会让一次 reap tick 把整个
|
|
149
|
-
* placed 分区拉进内存。行按**年龄升序**(最老先出)⇒ 截断的语义是「本 tick 先收最老的一批,下一 tick
|
|
150
|
-
* 接着收」,而不是「有些行永远看不见」。判据消费方(core 的分区收割腿)本就是每 tick 重新枚举的。
|
|
151
|
-
*/
|
|
152
64
|
export const LIST_PLACED_BATCH = 1000;
|
|
153
65
|
//# sourceMappingURL=session-placement.js.map
|
|
@@ -1,82 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable `SessionPolicyStore` (E6) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场). ONE
|
|
3
|
-
* implementation, TWO dialects; the historical `TiDBSessionPolicyStore` / `PgSessionPolicyStore` class
|
|
4
|
-
* names survive as thin ctor subclasses so every consumer (store-backend.ts, the session-sync
|
|
5
|
-
* fanout/push/pull suites, the E6 equivalence suite) is untouched.
|
|
6
|
-
*
|
|
7
|
-
* Operator-tightened per-session tool-permission rules (CAS-versioned, tighten-only) — the cross-replica
|
|
8
|
-
* twin of core's InMemorySessionPolicyStore (`@sema-agent/core` session-policy-store.js), backing core's
|
|
9
|
-
* `RunnerDeps.sessionPolicyStore`. core READS via getRules at prepare-time and folds the rules into the
|
|
10
|
-
* ToolPolicy FIRST (subtract-only); the service's operator endpoint WRITES via putRules.
|
|
11
|
-
*
|
|
12
|
-
* BYTE-MATCH discipline (the env-gated equivalence test vs InMemorySessionPolicyStore is the contract):
|
|
13
|
-
* - Key = `sha256(JSON.stringify([sessionId, principal ?? null]))` — the SHA of core's exact composite key. The hash
|
|
14
|
-
* (a) gives a FIXED 64-char PK regardless of how long / JSON-escapable the principal is (a VARCHAR(190) principal of
|
|
15
|
-
* control chars JSON-escapes past any fixed width — review finding), and (b) sidesteps PK collation pitfalls
|
|
16
|
-
* (trailing-space PAD). `principal` undefined/null collapse to one row, "" stays distinct (core's `?? null`).
|
|
17
|
-
* - putRules order is LOAD-BEARING: (1) CAS expectedRev (stale → `conflict`), (2) normalize+stripRev the incoming
|
|
18
|
-
* rules, (3) tighten gate via the REUSED core `loosenReasons` (non-operator loosen → `loosen_forbidden`), (4) rev
|
|
19
|
-
* bump (+1; first = 1). A missing row reads as rev 0 (so expectedRev:0 = create-once). The conflict + loosen_forbidden
|
|
20
|
-
* MESSAGES match core verbatim. read-check-write runs in ONE txn (SELECT…FOR UPDATE the prior so the loosen diff +
|
|
21
|
-
* CAS are atomic; the InMemory gets this free from single-threaded JS). The UPDATE additionally guards `AND rev=?`
|
|
22
|
-
* (defense beyond the row lock); a concurrent FIRST-write (no row → FOR UPDATE takes no lock) loses the PK race and
|
|
23
|
-
* is RETRIED (re-read takes the UPDATE path) so a no-expectedRev create matches core's serialized rev-2 outcome
|
|
24
|
-
* rather than a spurious conflict.
|
|
25
|
-
* - allowDirs is stored NORMALIZED (`..`-free) so a later read-back `prior` feeds loosenReasons in the same canonical
|
|
26
|
-
* shape as `clean` (the /work/../etc tighten-bypass blocker). The return is structuredClone'd (core's isolation).
|
|
27
|
-
* `operator` is honored verbatim — core never adjudicates it; the HTTP route FREEZES it from a verified principal
|
|
28
|
-
* (mirrors steer.trusted). `deleteBySession` is a service extension (E21 purge), not core's interface.
|
|
29
|
-
*
|
|
30
|
-
* ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
|
|
31
|
-
* - `?` placeholders vs `$n`
|
|
32
|
-
* - dup-key errno `1062`/`ER_DUP_ENTRY` vs SQLSTATE `23505`
|
|
33
|
-
* - PG-only genuinely-divergent ALGORITHM: `pgHasUnstorable` rejects rules containing NUL / lone-surrogate
|
|
34
|
-
* bytes (fail-loud, never rewritten) before the write; TiDB has no equivalent guard. Kept as an explicit
|
|
35
|
-
* `if (this.db.dialect === "pg")` branch in putRules — see the inline 🔴 comment there for the codex
|
|
36
|
-
* R2-H1→R3 rationale (rules are the authorization semantic body: reject-only, never silently rewritten).
|
|
37
|
-
* - transaction plumbing: TiDB's native `beginTransaction/commit/rollback` vs PG's statement-issued
|
|
38
|
-
* `BEGIN/COMMIT/ROLLBACK` — normalized by `SqlDriver`'s shared `connect()`/`begin()`/`commit()`/`rollback()`
|
|
39
|
-
* (see sql-driver.ts); this store's original `beginTransaction()` call shape maps onto the driver's plain
|
|
40
|
-
* `begin()` (NOT `beginPessimistic()` — the original TiDB store never opted into `BEGIN PESSIMISTIC`).
|
|
41
|
-
*/
|
|
42
1
|
import { createHash } from "node:crypto";
|
|
43
2
|
import { loosenReasons, SessionPolicyError, normalizeRules, stripRev } from "@sema-agent/core";
|
|
44
3
|
import { pgHasUnstorable } from "./pg-safe-json.js";
|
|
45
4
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
46
5
|
import { isDupKeyError } from "./sql-errors.js";
|
|
47
|
-
/** `session_policy.policy_key` 的**唯一铸造点**(core 复合键的 sha256)。
|
|
48
|
-
*
|
|
49
|
-
* 🔴 导出而不是让收编腿再抄一份(clay 宪法「schema 单一属主禁复制」):design/183 form b 的身份重绑要
|
|
50
|
-
* 逐行重算这把键(身份在 hash 的**原像**里,等值 UPDATE 够不着)。两处各写一遍 sha256 表达式的下场是
|
|
51
|
-
* 某天有人在这里改了 key 公式而收编腿还在按旧式算 —— 迁完的行从此谁也读不到,且**静默**。 */
|
|
52
6
|
export const policyKeyFor = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
|
|
53
7
|
const policyKey = policyKeyFor;
|
|
54
|
-
const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v);
|
|
55
|
-
/** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
|
|
8
|
+
const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v);
|
|
56
9
|
export class SqlSessionPolicyStore {
|
|
57
10
|
db;
|
|
58
11
|
constructor(db) {
|
|
59
12
|
this.db = db;
|
|
60
13
|
}
|
|
61
|
-
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
62
14
|
q(tidb, pg) {
|
|
63
15
|
return this.db.dialect === "tidb" ? tidb : pg;
|
|
64
16
|
}
|
|
65
|
-
/** 判据属主 = `sql-errors.ts`(A-032 P1-①;本站点旧形已是并集形,收编后语义不变)。 */
|
|
66
17
|
isDupKey(e) {
|
|
67
18
|
return isDupKeyError(this.db.dialect, e);
|
|
68
19
|
}
|
|
69
|
-
/** Read the stored rules for (session, principal-owner); null when none (parity with InMemory). */
|
|
70
20
|
async getRules(sessionId, principal) {
|
|
71
21
|
const { rows } = await this.db.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ?", "SELECT rules, rev FROM session_policy WHERE policy_key = $1"), [policyKey(sessionId, principal)]);
|
|
72
22
|
if (!rows[0])
|
|
73
23
|
return null;
|
|
74
24
|
return { ...parseRules(rows[0].rules), rev: Number(rows[0].rev) };
|
|
75
25
|
}
|
|
76
|
-
/** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
|
|
77
|
-
* per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
|
|
78
|
-
* replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
|
|
79
|
-
* NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev. */
|
|
80
26
|
async listBySession(sessionId) {
|
|
81
27
|
const { rows } = await this.db.query(this.q("SELECT principal, rules, rev FROM session_policy WHERE session_id = ?", "SELECT principal, rules, rev FROM session_policy WHERE session_id = $1"), [sessionId]);
|
|
82
28
|
return rows.map((r) => ({
|
|
@@ -84,7 +30,6 @@ export class SqlSessionPolicyStore {
|
|
|
84
30
|
rules: { ...parseRules(r.rules), rev: Number(r.rev) },
|
|
85
31
|
}));
|
|
86
32
|
}
|
|
87
|
-
/** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
|
|
88
33
|
async putRules(sessionId, principal, rules, opts) {
|
|
89
34
|
const key = policyKey(sessionId, principal);
|
|
90
35
|
for (let attempt = 0;; attempt++) {
|
|
@@ -93,24 +38,16 @@ export class SqlSessionPolicyStore {
|
|
|
93
38
|
const { rows } = await conn.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ? FOR UPDATE", "SELECT rules, rev FROM session_policy WHERE policy_key = $1 FOR UPDATE"), [key]);
|
|
94
39
|
const prior = rows[0] ? { rules: parseRules(rows[0].rules), rev: Number(rows[0].rev) } : undefined;
|
|
95
40
|
const priorRev = prior?.rev ?? 0;
|
|
96
|
-
// (1) CAS — checked BEFORE the tighten gate; a missing row is rev 0 (expectedRev:0 = create-once).
|
|
97
41
|
if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
|
|
98
42
|
throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${priorRev})`);
|
|
99
43
|
}
|
|
100
|
-
// (2) normalize+stripRev the INCOMING rules (allowDirs `..`-free before store AND before the loosen diff).
|
|
101
44
|
const clean = normalizeRules(stripRev(rules));
|
|
102
|
-
// (3) tighten gate — ONLY when a prior exists AND not an operator write (strict !== true). REUSE core loosenReasons.
|
|
103
45
|
if (prior && opts?.operator !== true) {
|
|
104
46
|
const loosens = loosenReasons(prior.rules, clean);
|
|
105
47
|
if (loosens.length > 0) {
|
|
106
48
|
throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
|
|
107
49
|
}
|
|
108
50
|
}
|
|
109
|
-
// 🔴 PG-only genuinely-divergent ALGORITHM (not a syntax difference): codex R2-H1→R3(NUL 批终形):
|
|
110
|
-
// rules=授权语义本体,**拒绝式**——含不可存字节的规则是垃圾输入(NUL 路径本不可用),fail-loud
|
|
111
|
-
// 拒收;绝不改写(有损=写读不一致)、绝不信封(R3:滚动部署/回滚窗口老 replica 读信封=字段全缺=
|
|
112
|
-
// fail-open 放行,授权面不赌版本偏斜)。TiDB's VARBINARY/utf8mb4 storage never chokes on these bytes
|
|
113
|
-
// the way PG's protocol-byte encoding does, so it carries no equivalent guard.
|
|
114
51
|
if (this.db.dialect === "pg" && pgHasUnstorable(clean)) {
|
|
115
52
|
throw new SessionPolicyError("loosen_forbidden", "rules contain unstorable bytes (NUL / lone surrogate) — refused; strip them at the source (authorization rules are never rewritten by the store)");
|
|
116
53
|
}
|
|
@@ -118,38 +55,26 @@ export class SqlSessionPolicyStore {
|
|
|
118
55
|
const json = JSON.stringify(clean);
|
|
119
56
|
const now = new Date();
|
|
120
57
|
if (prior) {
|
|
121
|
-
// `principal` is enumeration-only (not a key, not CAS): write it on UPDATE too so a pre-migration
|
|
122
|
-
// row (principal NULL) SELF-HEALS to its real principal on the next write — never changes the rev/CAS path.
|
|
123
58
|
const upd = await conn.query(this.q("UPDATE session_policy SET principal = ?, rules = ?, rev = ?, updated_at = ? WHERE policy_key = ? AND rev = ?", "UPDATE session_policy SET principal = $1, rules = $2::jsonb, rev = $3, updated_at = $4 WHERE policy_key = $5 AND rev = $6"), [principal ?? null, json, nextRev, now, key, priorRev]);
|
|
124
|
-
// Defense beyond the FOR UPDATE row lock: if rev moved under us, the CAS lost (treat as conflict).
|
|
125
59
|
if (upd.affected !== 1)
|
|
126
60
|
throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts?.expectedRev ?? priorRev}, have ${priorRev})`);
|
|
127
61
|
}
|
|
128
62
|
else {
|
|
129
|
-
// No prior → FOR UPDATE took no row lock; a concurrent first-writer can win the PK. Let the dup-key
|
|
130
|
-
// PROPAGATE (do NOT convert to conflict here) so the outer loop RETRIES — the re-read then finds the row
|
|
131
|
-
// and takes the UPDATE path (matching core's serialized rev-2 outcome / or an honest CAS conflict if
|
|
132
|
-
// expectedRev was supplied), instead of a spurious conflict. `principal ?? null` = the enumeration column.
|
|
133
63
|
await conn.query(this.q("INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES (?,?,?,?,?,?,?)", "INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES ($1,$2,$3,$4::jsonb,$5,$6,$7)"), [key, sessionId, principal ?? null, json, nextRev, now, now]);
|
|
134
64
|
}
|
|
135
|
-
return structuredClone({ ...clean, rev: nextRev });
|
|
65
|
+
return structuredClone({ ...clean, rev: nextRev });
|
|
136
66
|
});
|
|
137
67
|
}
|
|
138
68
|
catch (e) {
|
|
139
69
|
if (this.isDupKey(e) && attempt < 4)
|
|
140
|
-
continue;
|
|
70
|
+
continue;
|
|
141
71
|
throw e;
|
|
142
72
|
}
|
|
143
73
|
}
|
|
144
74
|
}
|
|
145
|
-
/** E21 purge — drop ALL policy rows for a session (every principal). Scoped by session_id (the route owner-gates).
|
|
146
|
-
* Return shape aligned to core 1.423's optional interface seam (`Promise<void>`) — the affectedRows count was
|
|
147
|
-
* incidental and unconsumed; the integration test asserts the post-state (listBySession empty) instead. */
|
|
148
75
|
async deleteBySession(sessionId) {
|
|
149
76
|
await this.db.query(this.q("DELETE FROM session_policy WHERE session_id = ?", "DELETE FROM session_policy WHERE session_id = $1"), [sessionId]);
|
|
150
77
|
}
|
|
151
|
-
/** `begin()` (NOT `beginPessimistic()`) — matches both twins' original call shape: TiDB's native
|
|
152
|
-
* `beginTransaction()` (optimistic), PG's plain `BEGIN`. Normalized by SqlDriver's shared tx plumbing. */
|
|
153
78
|
async tx(fn) {
|
|
154
79
|
const conn = await this.db.connect();
|
|
155
80
|
try {
|
|
@@ -163,7 +88,6 @@ export class SqlSessionPolicyStore {
|
|
|
163
88
|
await conn.rollback();
|
|
164
89
|
}
|
|
165
90
|
catch {
|
|
166
|
-
/* ignore */
|
|
167
91
|
}
|
|
168
92
|
throw err;
|
|
169
93
|
}
|
|
@@ -172,13 +96,11 @@ export class SqlSessionPolicyStore {
|
|
|
172
96
|
}
|
|
173
97
|
}
|
|
174
98
|
}
|
|
175
|
-
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
176
99
|
export class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
|
|
177
100
|
constructor(pool) {
|
|
178
101
|
super(mysqlDriver(pool));
|
|
179
102
|
}
|
|
180
103
|
}
|
|
181
|
-
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
182
104
|
export class PgSessionPolicyStore extends SqlSessionPolicyStore {
|
|
183
105
|
constructor(pool) {
|
|
184
106
|
super(pgDriver(pool));
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { TtlSessionStore } from "@sema-agent/core";
|
|
2
2
|
import { CachingSessionStore } from "./caching-session-store.js";
|
|
3
|
-
/**
|
|
4
|
-
* Session-store plugin seam.
|
|
5
|
-
*
|
|
6
|
-
* - `memory` (default): core's in-memory `TtlSessionStore` (lost on restart, 7-day idle TTL).
|
|
7
|
-
* - `tidb`: the durable "Session event-log center" (L1) — append-only events + wake/resume +
|
|
8
|
-
* cross-instance CAS (F2), on whichever SQL engine `DB_BACKEND` selects (TiDB or PG, via the store backend).
|
|
9
|
-
* Optionally wrapped in a warm cache (reuse a hot session across turns; requires session affinity) —
|
|
10
|
-
* set `SESSION_CACHE_TTL_SEC=0` to disable.
|
|
11
|
-
*/
|
|
12
3
|
export function createSessionStore(config, backend, metrics) {
|
|
13
4
|
if (config.sessionBackend === "tidb") {
|
|
14
5
|
if (!backend)
|
|
@@ -20,71 +11,23 @@ export function createSessionStore(config, backend, metrics) {
|
|
|
20
11
|
}
|
|
21
12
|
return new TtlSessionStore({ defaultTtlDays: 7 });
|
|
22
13
|
}
|
|
23
|
-
/**
|
|
24
|
-
* core 1.382(design/153,[1561] 提货单①②)`SubagentToolOptions.background.ensureChildSessionDurable`
|
|
25
|
-
* 的实现体——从 main.ts 装配点抽出为可独立测试的纯函数(main.ts 里的其余装配闭包历来无单测覆盖,
|
|
26
|
-
* 这条因为承载真实的 park/veto 裁决,拆出来直接钉)。
|
|
27
|
-
*
|
|
28
|
-
* 只在 `sessionId` 已经真正路由到 HOST(durable)会话店、且**真有内容**时 resolve;其余(未路由到 host/
|
|
29
|
-
* 两店皆无/host 店只有一行空壳)一律 reject——veto,core 侧子代按 pre-153 settle failed,零回归,不是
|
|
30
|
-
* 危险失败模式(见函数体内注释)。
|
|
31
|
-
*
|
|
32
|
-
* 传入必须是 HOST 店本身(main.ts 里 `createSessionStore(...)` 的返回值),不能是 subRunner 用的
|
|
33
|
-
* `ForkRoutingSessionStore` 包装店——那层包装对 `acquire(id,{requireExisting:true})` 是 transient-优先
|
|
34
|
-
* (F-7 案:让"真会话在 transient"赢过"host 店里的同 id 空壳"),会把"只在 transient 店有真内容"误判成
|
|
35
|
-
* "host 店已经有了"。这里要问的是纯粹"host 店本身是否已经有这份真内容",必须绕开那层路由。
|
|
36
|
-
*
|
|
37
|
-
* 🔴 [1575] F3(cli 复查抓获,真红确认)调用方前提——**本函数假设 `hostStore.release()` 是"归还租约"
|
|
38
|
-
* 语义(不销毁内容)**,这对 core `TiDBSessionStore` 成立(`release()` 逐字注释「Durable history is
|
|
39
|
-
* retained; just drop any in-flight cache entry」),但对无 repo 构造的 `TtlSessionStore`(`createSessionStore`
|
|
40
|
-
* 在 `SESSION_BACKEND !== "tidb"` 时返回的那种)**不成立**——那个店形的 `release()` 是整删会话
|
|
41
|
-
* (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
|
|
42
|
-
* 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
|
|
43
|
-
* 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
|
|
44
|
-
* 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
|
|
45
|
-
* `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
|
|
46
|
-
*/
|
|
47
14
|
export async function ensureSessionDurable(hostStore, sessionId) {
|
|
48
15
|
const acquired = await hostStore.acquire(sessionId, { requireExisting: true });
|
|
49
16
|
try {
|
|
50
|
-
// security.ts 的 authorizer 对任意调用方自报的 body.sessionId(无主时)会经 `register()` 在 host 店
|
|
51
|
-
// 插一行 session_meta(leaf_id NULL,零内容)——那是 claim-create 的副作用,不是真正落过内容的会话。
|
|
52
|
-
// 裸 acquire 命中不足以证明"这份 durable 记录里有真内容可供跨进程恢复";真实会话到 park 这一步
|
|
53
|
-
// (需要模型已经产出过至少一次带工具调用的回复)恒已有 ≥1 条树内条目。校验失败的唯一后果是多一次
|
|
54
|
-
// reject=veto=pre-153 行为,不会放大权限或掩盖错误——误判方向永远偏保守。
|
|
55
17
|
const leafId = await acquired.session.getLeafId();
|
|
56
18
|
if (leafId === null)
|
|
57
19
|
throw new Error(`session "${sessionId}" exists in the durable store but is empty (no leaf entry) — likely a claim-create shell row, not real durable content`);
|
|
58
20
|
}
|
|
59
21
|
finally {
|
|
60
|
-
// 🔴 #292 C18(core 5.48.0 [4743] 点名义务①,**preflight/探活法**):这是一次「只为验存在」的
|
|
61
|
-
// acquire ⇒ 收尾必须是 `forget`(丢暖引用),**绝不能**是 `release`。core 5.48 起
|
|
62
|
-
// `release(placedId)` 被重定义为**真删除**,而本函数正是 park 前的探活腿:一条跨重启 parked 的
|
|
63
|
-
// 子代转录在这里被 release 一次就等于**删掉转录 + 烧掉那次审批**(core 发车前重扫的 P1 同款撞点,
|
|
64
|
-
// 四域复现)。缺 `forget` 面 ⇒ 什么都不做(core B-17 判例:绝不代之以 release);两支 SQL 孪生与
|
|
65
|
-
// 包装店都实装了 forget,所以生产路径上这一步是真的丢引用,不是 no-op。
|
|
66
22
|
await hostStore.forget?.(acquired.sessionId);
|
|
67
23
|
}
|
|
68
24
|
}
|
|
69
|
-
/**
|
|
70
|
-
* [1593] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
|
|
71
|
-
* 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
|
|
72
|
-
* (14ms~2s)+ settle failed,赎回窗口不存在):
|
|
73
|
-
* 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
|
|
74
|
-
* 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
|
|
75
|
-
* 三步见 ForkRoutingSessionStore.promoteToHost)。
|
|
76
|
-
* 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
|
|
77
|
-
* durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
|
|
78
|
-
* 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
|
|
79
|
-
* (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
|
|
80
|
-
*/
|
|
81
25
|
export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId) {
|
|
82
26
|
try {
|
|
83
27
|
await ensureSessionDurable(hostStore, sessionId);
|
|
84
28
|
return;
|
|
85
29
|
}
|
|
86
30
|
catch {
|
|
87
|
-
// 不在 host 店(或空壳行)→ 走晋升;晋升自身的失败在下方如实上浮
|
|
88
31
|
}
|
|
89
32
|
await routing.promoteToHost(sessionId);
|
|
90
33
|
await ensureSessionDurable(hostStore, sessionId);
|