@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
|
@@ -87,6 +87,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
87
87
|
readonly cls: "P-DEBT";
|
|
88
88
|
readonly note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。";
|
|
89
89
|
};
|
|
90
|
+
readonly "server.run-store.file-active-claims-unreadable": {
|
|
91
|
+
readonly cls: "P-DEBT";
|
|
92
|
+
readonly note: "`FileRunStore.hydrate()` 的第二处 `readdirSync(runs/active/)`(claim 平面 = 单活索引)失败,且 `RUN_STORE_STRICT_HYDRATE` 关(缺省)⇒ 服务照常起动、单活索引为**空**。放行的后果两条,都不报错:① 已有 run 行仍可读(`GET /v1/runs/:id` 照常答),但此后对这些会话的审批决定在请求里同步跑完、却找不到要更新的 run 行 —— run 行停在决前的 `suspended`;② 读单活索引的各处读到「没人占用」—— `getActiveTaskId` 恒答空,例如会话同步推送前的「会话有在飞 run」检查(与 `server.run-store.file-hydrate-unreadable` 同一句错话:「证不出有占用 ⇒ 没有占用」,只是换了一个盘面);新提交不读这张索引,claim 文件完好时仍按盘上的 claim 裁定,不受影响。同一条债的另一个盘面:缺省不拒启是刻意保留的行为,所以留作 P-DEBT —— error 级启动日志(`file_run_store_active_claims_unreadable`,带 `runs/active/` 绝对路径)早已在,本计数补的是遥测轴(此前 `runs/` 臂有计数、这一臂没有)。`RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 fail-closed 启动门,本计数因此只在缺省 off 档的真放行时产生(拒启那一形不记)—— 读遥测时「计数为零」也可能是部署开了拒启门、根本起不来。";
|
|
93
|
+
};
|
|
90
94
|
readonly "server.fork-routing.placed-probe-host-error": {
|
|
91
95
|
readonly cls: "F";
|
|
92
96
|
readonly note: "#292 placement 半场:hostPlacedProbe 对 host 店的 requireExisting 探针失败且**不是 not_found**(host 读故障)时折叠为 absent —— 路由回旧规则(claim-create 会在 transient 建新会话)。放行的是「host 暂不可达时 transient 腿保持独立可用」;最坏后果=故障窗内对一个真 placed id 铸出 transient 影子(host 恢复后双身,requireExisting 冷探针会重新指回 host 真身)。not_found 是常态路径不计数;只有真故障臂进本计数。";
|
|
@@ -549,6 +553,14 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
549
553
|
readonly cls: "P-DEBT";
|
|
550
554
|
readonly note: "S-163(`config-dir-trust.ts` `judgeWin32`,本地配置源 `config.d` 读之前的目录信任判官在 win32 上的那一半):Node 在 win32 上 `uid` 恒 0、模式位是只读属性的投影 ⇒ 「属主 ∈ {本服务用户, root, CONFIG_LOCAL_OWNER_UID}」与「不得组 / 他人可写」两判不可做;结构判(常规文件 / 16 MiB 上界 / 链接成环)照做。每一次读照常进行(起服一次 + 每拍一次)记一次。放行的最坏后果 = 一台多用户 Windows 机器上,别的本机用户若能写这台服务的 `config.d`(缺省的用户目录 ACL 不允许),改一份 `mcp.json` 就能让本服务以它的身份起命令 —— 与 7.132.x 逐字同。照 POSIX 拒启会让每个 Windows 本机引擎(含 cli 起的)升级即起不来,而让 cli 替用户设豁免键是中间层替部署者设安全旋钮。收口 = 有了 ACL 判据或一次裁定(改 R 或改拒启)那一版,本 tag 删或改类。detail = `config.d=<物理路径>`。";
|
|
551
555
|
};
|
|
556
|
+
readonly "server.remote-write.staging-dir-left": {
|
|
557
|
+
readonly cls: "F";
|
|
558
|
+
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。";
|
|
559
|
+
};
|
|
560
|
+
readonly "server.remote-write.stage-seal-fallback": {
|
|
561
|
+
readonly cls: "F";
|
|
562
|
+
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。";
|
|
563
|
+
};
|
|
552
564
|
};
|
|
553
565
|
/** 词表键推导的闭集类型——未登记的 tag 传不进 {@link recordFailOpen}(编译期拒)。 */
|
|
554
566
|
export type FailOpenTag = keyof typeof FAIL_OPEN_TAGS;
|
|
@@ -68,6 +68,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
68
68
|
cls: "P-DEBT",
|
|
69
69
|
note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。",
|
|
70
70
|
},
|
|
71
|
+
"server.run-store.file-active-claims-unreadable": {
|
|
72
|
+
cls: "P-DEBT",
|
|
73
|
+
note: "`FileRunStore.hydrate()` 的第二处 `readdirSync(runs/active/)`(claim 平面 = 单活索引)失败,且 `RUN_STORE_STRICT_HYDRATE` 关(缺省)⇒ 服务照常起动、单活索引为**空**。放行的后果两条,都不报错:① 已有 run 行仍可读(`GET /v1/runs/:id` 照常答),但此后对这些会话的审批决定在请求里同步跑完、却找不到要更新的 run 行 —— run 行停在决前的 `suspended`;② 读单活索引的各处读到「没人占用」—— `getActiveTaskId` 恒答空,例如会话同步推送前的「会话有在飞 run」检查(与 `server.run-store.file-hydrate-unreadable` 同一句错话:「证不出有占用 ⇒ 没有占用」,只是换了一个盘面);新提交不读这张索引,claim 文件完好时仍按盘上的 claim 裁定,不受影响。同一条债的另一个盘面:缺省不拒启是刻意保留的行为,所以留作 P-DEBT —— error 级启动日志(`file_run_store_active_claims_unreadable`,带 `runs/active/` 绝对路径)早已在,本计数补的是遥测轴(此前 `runs/` 臂有计数、这一臂没有)。`RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 fail-closed 启动门,本计数因此只在缺省 off 档的真放行时产生(拒启那一形不记)—— 读遥测时「计数为零」也可能是部署开了拒启门、根本起不来。",
|
|
74
|
+
},
|
|
71
75
|
"server.fork-routing.placed-probe-host-error": {
|
|
72
76
|
cls: "F",
|
|
73
77
|
note: "#292 placement 半场:hostPlacedProbe 对 host 店的 requireExisting 探针失败且**不是 not_found**(host 读故障)时折叠为 absent —— 路由回旧规则(claim-create 会在 transient 建新会话)。放行的是「host 暂不可达时 transient 腿保持独立可用」;最坏后果=故障窗内对一个真 placed id 铸出 transient 影子(host 恢复后双身,requireExisting 冷探针会重新指回 host 真身)。not_found 是常态路径不计数;只有真故障臂进本计数。",
|
|
@@ -509,6 +513,14 @@ export const FAIL_OPEN_TAGS = {
|
|
|
509
513
|
cls: "P-DEBT",
|
|
510
514
|
note: "S-163(`config-dir-trust.ts` `judgeWin32`,本地配置源 `config.d` 读之前的目录信任判官在 win32 上的那一半):Node 在 win32 上 `uid` 恒 0、模式位是只读属性的投影 ⇒ 「属主 ∈ {本服务用户, root, CONFIG_LOCAL_OWNER_UID}」与「不得组 / 他人可写」两判不可做;结构判(常规文件 / 16 MiB 上界 / 链接成环)照做。每一次读照常进行(起服一次 + 每拍一次)记一次。放行的最坏后果 = 一台多用户 Windows 机器上,别的本机用户若能写这台服务的 `config.d`(缺省的用户目录 ACL 不允许),改一份 `mcp.json` 就能让本服务以它的身份起命令 —— 与 7.132.x 逐字同。照 POSIX 拒启会让每个 Windows 本机引擎(含 cli 起的)升级即起不来,而让 cli 替用户设豁免键是中间层替部署者设安全旋钮。收口 = 有了 ACL 判据或一次裁定(改 R 或改拒启)那一版,本 tag 删或改类。detail = `config.d=<物理路径>`。",
|
|
511
515
|
},
|
|
516
|
+
"server.remote-write.staging-dir-left": {
|
|
517
|
+
cls: "F",
|
|
518
|
+
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。",
|
|
519
|
+
},
|
|
520
|
+
"server.remote-write.stage-seal-fallback": {
|
|
521
|
+
cls: "F",
|
|
522
|
+
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。",
|
|
523
|
+
},
|
|
512
524
|
};
|
|
513
525
|
export function failOpenTagForDroppedFrame(frameType) {
|
|
514
526
|
switch (frameType) {
|
|
@@ -593,7 +593,6 @@ const STATS_KEY_TREATMENT = {
|
|
|
593
593
|
humanReview: "human-review",
|
|
594
594
|
};
|
|
595
595
|
const STATS_MECHANISMS_TREATMENT = {
|
|
596
|
-
finalVerifyInjected: "verbatim",
|
|
597
596
|
finalVerifyInjections: "verbatim",
|
|
598
597
|
attachmentsInjected: "verbatim",
|
|
599
598
|
approachNoticesSent: "verbatim",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
2
|
+
import { AppendLog, REDACTION_MARKS, ensureDir } from "@sema-agent/core";
|
|
3
3
|
import { buildFleetWorkflowRow } from "../fleet/fleet-bus.js";
|
|
4
4
|
import { isTerminalWorkflowRowStatus, seedTerminalWorkflowRow } from "../fleet/fleet-terminal-window.js";
|
|
5
5
|
import { buildParkSessionRows } from "./workflow-agent-session-index.js";
|
|
@@ -26,11 +26,11 @@ export function recoverySummary(status, error) {
|
|
|
26
26
|
return "workflow completed";
|
|
27
27
|
const base = error ? `workflow failed: ${error}` : "workflow failed";
|
|
28
28
|
const redacted = base
|
|
29
|
-
.replace(/https?:\/\/[^\s"'`]+/gi,
|
|
29
|
+
.replace(/https?:\/\/[^\s"'`]+/gi, REDACTION_MARKS.url)
|
|
30
30
|
.replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[redacted-token]")
|
|
31
31
|
.replace(/\b(?:api[_-]?key|secret|token|password|authorization)\b\s*[:=]\s*"?[^\s"',}]+/gi, "[redacted-secret]")
|
|
32
|
-
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g,
|
|
33
|
-
.replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g,
|
|
32
|
+
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g, REDACTION_MARKS.path)
|
|
33
|
+
.replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g, REDACTION_MARKS.path);
|
|
34
34
|
return redacted.length > SUMMARY_MAX ? `${redacted.slice(0, SUMMARY_MAX)}…[+${redacted.length - SUMMARY_MAX} chars]` : redacted;
|
|
35
35
|
}
|
|
36
36
|
export class WorkflowNotifyGate {
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* core 侧契约(RunnerDeps.memoryScopeAdmission,core types.d.ts):resolver 返回 {ok:false} ⇒ core 铸终局码
|
|
13
13
|
* memory.admission_denied;resolver throw 一个带 `retryAfterMs`(ms)属性的 Error ⇒ core 铸瞬时码
|
|
14
14
|
* memory.admission_required 并透传 retryAfterMs;{ok:true}.scopes 只能 ⊆ requested(多给 = resolver fault
|
|
15
|
-
* 会被 core 整拒)
|
|
16
|
-
*
|
|
15
|
+
* 会被 core 整拒)。「谁在问」只有引擎一处读法:入参按调用方在不在场分两形,无调用方那一形只装部署来源的 scope
|
|
16
|
+
* (请求来源的 scope 没有调用方 ⇒ 引擎在自己的门上先拒,不问本解析器)—— 本模块按这一判别式分支,不另判一次缺席;
|
|
17
|
+
* 引擎型不可达的那一组合(无调用方 ∧ 请求来源 scope)若仍到了这里 ⇒ 抛,不放行。
|
|
17
18
|
*/
|
|
18
19
|
import type { MemoryScopeAdmission } from "@sema-agent/core";
|
|
19
20
|
/**
|
|
@@ -72,7 +73,7 @@ export interface MemoryScopeAdmissionOptions {
|
|
|
72
73
|
/** "audit": 判决照算但从不真拒——会拒的结果(终局 ok:false 或瞬时 throw)一律换成全额放行 + 观测记录
|
|
73
74
|
* would-deny;真通过的判决(含 write 收窄为 null)照常返回。"enforce": 判决即结果。 */
|
|
74
75
|
mode: "audit" | "enforce";
|
|
75
|
-
/** 观测 hook。outcome ∈ "ok" | "denied" | "
|
|
76
|
+
/** 观测 hook。outcome ∈ "ok" | "denied" | "directory_absent" | "directory_stale" |
|
|
76
77
|
* "directory_malformed"。audit 模式下的 would-deny 也走这里,`details.audited === true`。hook 本身绝不
|
|
77
78
|
* 允许打断准入判决——一律吞掉它可能抛出的异常。 */
|
|
78
79
|
onOutcome?: (outcome: string, details?: Record<string, unknown>) => void;
|
|
@@ -164,8 +164,9 @@ export function createMemoryScopeAdmission(directory, opts) {
|
|
|
164
164
|
catch {
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
|
-
return async (
|
|
168
|
-
const
|
|
167
|
+
return async (input) => {
|
|
168
|
+
const { requested, requestedWriteScope } = input;
|
|
169
|
+
const attributed = (details) => input.principal === undefined ? details : { principal: input.principal, ...details };
|
|
169
170
|
const requestOriginRequested = requested.filter((r) => r.origin === "request");
|
|
170
171
|
const writeIsRequestOrigin = requestedWriteScope?.origin === "request";
|
|
171
172
|
const hasRequestOrigin = requestOriginRequested.length > 0 || writeIsRequestOrigin;
|
|
@@ -175,10 +176,9 @@ export function createMemoryScopeAdmission(directory, opts) {
|
|
|
175
176
|
async function decide() {
|
|
176
177
|
if (!hasRequestOrigin)
|
|
177
178
|
return { kind: "admit", verdict: admitFull() };
|
|
178
|
-
if (principal === undefined)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const lookup = await directory.lookup(principal);
|
|
179
|
+
if (input.principal === undefined)
|
|
180
|
+
throw new Error("org memory admission was asked with no caller yet holds a request-origin scope — the engine's caller-less arm carries deployment-origin scopes only, so this input is an engine contract break and is refused, never admitted");
|
|
181
|
+
const lookup = await directory.lookup(input.principal);
|
|
182
182
|
if (lookup.kind === "unavailable") {
|
|
183
183
|
const outcome = lookup.reason === "stale_generation" ? "directory_stale" : lookup.reason === "malformed" ? "directory_malformed" : "directory_absent";
|
|
184
184
|
return { kind: "unavailable", retryAfterMs: lookup.retryAfterMs, outcome, lookupReason: lookup.reason, details: { reason: lookup.reason } };
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -15,8 +15,15 @@ import type { ParkedReviveSurfaceFactory } from "./parked-revive-surface.js";
|
|
|
15
15
|
import type { RebuiltInheritedGate } from "./boot/parked-revive-gate.js";
|
|
16
16
|
import { type WorkflowRunParkKey, type WorkflowAgentSessionIndex } from "./orchestration/workflow-agent-session-index.js";
|
|
17
17
|
import type { WorkflowParkDecisionStore, WorkflowParkOutcome } from "./orchestration/workflow-park-decision.js";
|
|
18
|
-
import {
|
|
18
|
+
import type { SessionFace } from "./session-incarnation.js";
|
|
19
|
+
import { type ExecutionCommitDeps, type RootedExecutionContext } from "./http/execution-context.js";
|
|
19
20
|
import type { GateIdentity } from "./checkpoint-identity.js";
|
|
21
|
+
/**
|
|
22
|
+
* 本腿的**装配前提**(`createHttpServer` 起服拒的拒句;规则见 {@link ParkedDecideDeps} 的 `execution`):接了本腿(`parkedReviveSurface` 在场)的部署,
|
|
23
|
+
* 会话店必须带执行席 —— 认领之后对根会话住客的唯一判官是执行席上的写口,无席 ⇒ 认领之后没有任何判官(安全轴上不留静默放开的臂,所以拒启,
|
|
24
|
+
* 不留「无席时照旧两读」的臂)。
|
|
25
|
+
*/
|
|
26
|
+
export declare const PARKED_REVIVE_EXECUTION_SEAT_REQUIRED = "the parked-agent revive leg (parkedReviveSurface) needs a session store with an execution placement seat (sessionStorage.executionPlacement) \u2014 after the claim, the root session's occupant is judged only by the write on that seat; every built-in session store carries one (the MySQL-protocol and PostgreSQL stores and the local store, i.e. the session store a StoreBackend hands out; the caching and fork-routing wrappers forward it), and an in-memory test or embedding passes InMemoryExecutionPlacementSeat; refusing to start rather than revive parked agents with nobody judging the root session's occupant after the claim";
|
|
20
27
|
export interface ParkedAgentMatch {
|
|
21
28
|
handle: string;
|
|
22
29
|
row: BackgroundAgentRecord;
|
|
@@ -151,14 +158,6 @@ export interface ParkedDecideDeps {
|
|
|
151
158
|
* checkpointStore 同实例,[ref] 设计稿 §4),以及与它同出一张列表的继承座 `mountsCallerTool`(一层行才有;属主与全部判据
|
|
152
159
|
* 在 `parked-revive-surface.ts`)。修前是 boot 期的一只裸 Agent 工具、从不交座 ⇒ 7.32.0 起停在继承工具上的 allow 恒 422。 */
|
|
153
160
|
reviveSurface: ParkedReviveSurfaceFactory;
|
|
154
|
-
/**
|
|
155
|
-
* [ref]([ref] §4)—— 会话店按 id 读一行的唯一读口(`sessionStorage.headOf`:属主 + 化身 + 叶子一次读;活住客经 `sessionFaceOfHead`
|
|
156
|
-
* 投影,空化身响亮拒)。**必填**:本腿对根会话读两次 ——
|
|
157
|
-
* claim 之前一次、claim 成功之后一次 —— 会话不在或两次不是同一位住客 ⇒ 回滚 claim、答既有的 `409 decide.not_parked`;赎回面只在
|
|
158
|
-
* 属主相等且两次是同一位住客时交继承座。读抛 ⇒ claim 前原样上抛(没有东西要回滚),claim 后先回滚再上抛。
|
|
159
|
-
* ([ref] 那只可选的 `sessionOwnerOf` 退役:可选 ⇒ 缺席时静默不复核,正是 [ref] 那个窗的形。)
|
|
160
|
-
*/
|
|
161
|
-
headOf: HeadRead;
|
|
162
161
|
/** revive args 的 `subagent_type` 成员性判定集([ref] Q1 修正:非 roster 成员必须省略,否则
|
|
163
162
|
* unknown subagent_type 拒;字面比对 general-purpose 在 roster 撞名时会错省)。缺省空集=恒省略。 */
|
|
164
163
|
knownAgentTypes?: ReadonlySet<string>;
|
|
@@ -187,7 +186,33 @@ export interface ParkedDecideDeps {
|
|
|
187
186
|
owner: string | null;
|
|
188
187
|
taskId: string;
|
|
189
188
|
sessionId: string;
|
|
189
|
+
executionLane: import("./execution-lane-caps.js").ExecutionLane;
|
|
190
190
|
}) => NonNullable<ToolExecuteContext["onAsk"]>;
|
|
191
|
+
/**
|
|
192
|
+
* 边界 9 的执行上下文(设计 §3.2;与提交腿 / 续跑家族同一只解析器、同一只写口)—— 本腿对根会话的住客**一读一判**:
|
|
193
|
+
* · `resolve` —— **认领之前**按行的根取上下文:解析器那一次行读(执行席:住客 + 格)就是本腿对根会话的**唯一一读**;根行不在 ⇒ `gone`
|
|
194
|
+
* (本腿答既有的 `decide.not_parked`);其余拒(mirror / 车道不再服务 / 主体的车道裁定)按续跑家族的折叠律答好交回(`answer`)—— 不认领、不驱动。
|
|
195
|
+
* 成功形的 `occupant` **必有**:接本腿的部署必须带执行席(无席的会话店交不出这一读;`createHttpServer` 在装配时拒无席形);
|
|
196
|
+
* · `commitDeps` —— 认领之后 `commitExecutionContext` 读的部署件:唯一写口按那一读的住客定址写 / 再读(化身 ∧ 属主),这就是认领之后对根住客的
|
|
197
|
+
* **唯一一判**(拒 ⇒ 先回滚认领)。写口放行的那一位就是赎回面的根住客见证;写口之后本腿不再读根。
|
|
198
|
+
* 复活子代的审批席带这只上下文的车道(`approverSeatFor` 的 `executionLane`)。
|
|
199
|
+
*/
|
|
200
|
+
execution: {
|
|
201
|
+
resolve: (root: string) => Promise<{
|
|
202
|
+
readonly ok: true;
|
|
203
|
+
readonly ctx: RootedExecutionContext & {
|
|
204
|
+
readonly occupant: SessionFace;
|
|
205
|
+
};
|
|
206
|
+
} | {
|
|
207
|
+
readonly ok: false;
|
|
208
|
+
readonly gone: true;
|
|
209
|
+
} | {
|
|
210
|
+
readonly ok: false;
|
|
211
|
+
readonly gone: false;
|
|
212
|
+
readonly answer: ParkedDecideResult;
|
|
213
|
+
}>;
|
|
214
|
+
commitDeps: ExecutionCommitDeps;
|
|
215
|
+
};
|
|
191
216
|
warn?: (event: string, fields: Record<string, unknown>) => void;
|
|
192
217
|
}
|
|
193
218
|
export interface ParkedDecideRequest {
|
package/dist/parked-decide.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { compareOccupants } from "@sema-agent/core";
|
|
2
1
|
import { readClosedWordSeat } from "./approval-card.js";
|
|
3
2
|
import { routeWorkflowPark } from "./orchestration/workflow-agent-session-index.js";
|
|
4
3
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
4
|
import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
|
|
6
5
|
import { backgroundScopesForCheckpointScope, decodeCheckpointScope, ownerOfCheckpointScope } from "./security.js";
|
|
7
6
|
import { ASK_USER_QUESTION_TOOL_NAME, isEngineQuestionRow } from "./approval-content-kind.js";
|
|
8
|
-
import { sessionFaceOfHead } from "./session-incarnation.js";
|
|
9
7
|
import { refusalBody, resumeRefusalFacts } from "./http/refusal-body.js";
|
|
10
8
|
import { backgroundAgentNameOf } from "./background-agent-name.js";
|
|
11
9
|
import { buildSettlementEvidence } from "./gate-settlement.js";
|
|
10
|
+
import { commitExecutionContext } from "./http/execution-context.js";
|
|
11
|
+
import { EXECUTION_PLACEMENT_SEAT_CARRIERS } from "./execution-placement.js";
|
|
12
|
+
export const PARKED_REVIVE_EXECUTION_SEAT_REQUIRED = `the parked-agent revive leg (parkedReviveSurface) needs a session store with an execution placement seat (sessionStorage.executionPlacement) — after the claim, the root session's occupant is judged only by the write on that seat; ${EXECUTION_PLACEMENT_SEAT_CARRIERS}; refusing to start rather than revive parked agents with nobody judging the root session's occupant after the claim`;
|
|
12
13
|
export async function findParkOrigin(sources, cp) {
|
|
13
14
|
if (sources.agentStore !== undefined) {
|
|
14
15
|
let match;
|
|
@@ -270,9 +271,10 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
270
271
|
if (revivability.kind === "unrevivable")
|
|
271
272
|
return req.decision === "deny" ? voidUnrevivableCard(deps, req, match) : revivability.approveRefusal;
|
|
272
273
|
const root = revivability.root;
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
275
|
-
return sessionGone(match.handle);
|
|
274
|
+
const resolved = await deps.execution.resolve(root);
|
|
275
|
+
if (!resolved.ok)
|
|
276
|
+
return resolved.gone ? sessionGone(match.handle) : resolved.answer;
|
|
277
|
+
const execution = resolved.ctx;
|
|
276
278
|
const inheritedGate = deps.rebuildInheritedGate ? await deps.rebuildInheritedGate(row, { principal: decodeCheckpointScope(req.scope) }) : undefined;
|
|
277
279
|
const stores = { agentStore: deps.agentStore, checkpointStore: deps.checkpointStore };
|
|
278
280
|
const claim = await deps.registry.claimParkedAgent(stores, match.handle, { scope: row.scope, owner: row.owner });
|
|
@@ -312,21 +314,12 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
312
314
|
await rollback("claimed_token_moved");
|
|
313
315
|
return gateMoved(match.handle);
|
|
314
316
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
catch (e) {
|
|
320
|
-
await rollback("session_face_unreadable");
|
|
321
|
-
throw e;
|
|
322
|
-
}
|
|
323
|
-
if (compareOccupants(faceBefore, faceAfter) !== "same") {
|
|
324
|
-
await rollback("session_occupant_changed");
|
|
325
|
-
return sessionGone(match.handle);
|
|
326
|
-
}
|
|
317
|
+
const placementRefusal = await commitExecutionContext(deps.execution.commitDeps, { kind: "parked", taskId: match.handle, rollback, goneAnswer: sessionGone(match.handle) }, execution);
|
|
318
|
+
if (placementRefusal)
|
|
319
|
+
return placementRefusal;
|
|
327
320
|
let surface;
|
|
328
321
|
try {
|
|
329
|
-
surface = deps.reviveSurface(row, { principal: decodeCheckpointScope(req.scope),
|
|
322
|
+
surface = deps.reviveSurface(row, { principal: decodeCheckpointScope(req.scope), rootOccupant: execution.occupant });
|
|
330
323
|
}
|
|
331
324
|
catch (e) {
|
|
332
325
|
await rollback("revive_surface_threw");
|
|
@@ -350,7 +343,7 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
350
343
|
toolCallId: `drv-${ticket.claimId}`,
|
|
351
344
|
...(surface.mountsCallerTool !== undefined ? { mountsCallerTool: surface.mountsCallerTool } : {}),
|
|
352
345
|
...(deps.approverSeatFor !== undefined && seatRoot !== undefined
|
|
353
|
-
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot }) }
|
|
346
|
+
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot, executionLane: execution.lane }) }
|
|
354
347
|
: {}),
|
|
355
348
|
reviveClaim: {
|
|
356
349
|
row: { ...ticket.row, parkClaimId: ticket.claimId },
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* 给那个会话的 run 组 `spec.tools` 的同一只工厂、同一个会话分区(任务清单店 `taskListStoreFor(root)` 与领队那一张是同一张;
|
|
12
12
|
* 多租户的 WebFetch 过滤同样生效 ⇒ 子代同样拿不到)。座 = `(t) => tools.includes(t)`(身份判:同名副本不算)。
|
|
13
13
|
* · **禁**用 boot 期单例填座:那只 bundle 没有会话,它的任务清单是一张私有表 —— 填了座,每个会话 / 租户的复活队友都会共用它。
|
|
14
|
-
* ·
|
|
15
|
-
*
|
|
16
|
-
* ①
|
|
17
|
-
*
|
|
18
|
-
* (
|
|
19
|
-
*
|
|
14
|
+
* · **根会话属于这张卡的租户**才填座(codex r1 [high]):任务清单店只按会话 id 分区,而会话 id 由调用方自选、删后可被别人(或同一个人)重领 ——
|
|
15
|
+
* 根会话删了(或删了重建)之后赎回一张旧卡,按 id 重解出来的是**下一位住客**的清单。两件事,两处判,各判一次:
|
|
16
|
+
* ① **住客判**(根会话现在还是这次决定判过的那一位:化身 ∧ 属主)只在赎回腿认领之后的唯一写口(执行放置,按解析器那一读的住客定址)——
|
|
17
|
+
* 不是那一位 ⇒ 赎回腿整条拒(`409 decide.not_parked`),走不到这里;只比属主比不出「同一个人删了重建」,所以住客判比化身;
|
|
18
|
+
* ② **租户判**在这里:写口放行的那一位(`rootOccupant`)的属主与卡的权威 principal 逐字相等(匿名会话 `null` ≡ 无 principal)。
|
|
19
|
+
* 不成立 ⇒ 不交座(fail-closed:子代不继承,继承工具上的 allow 回 422、审批待决,deny 照结)。根删除时把子孙的 parked 卡一并作废 = [ref] 件 B
|
|
20
20
|
* (`boot/session-faces.ts` 的删除协调器)。
|
|
21
21
|
* · 只对**一层**行填座(`rootSessionId` 在场且 = `parentSessionId`:派它的 run 就是根会话的 run,列表正是那个 run 的挂载)。更深的行
|
|
22
22
|
* (孙代:派它的是一个子代,它的挂载是继承裁剪后的子集,行上读不出来)不填 —— 无座 = core 的 fail-closed 臂;
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
* 座只判 bundle 实例的成员性,继承面是 `[...web, ...scaffolds]`,那两项差异不改变谁在面内。
|
|
26
26
|
*
|
|
27
27
|
* **属主来源([ref] / [ref])与交座是两个独立判据**:赎回工具的停驻挂点(复活的子代再要审批时停驻,`plugins/session-store.ts`)按委派继承律定宿主行属主,
|
|
28
|
-
* 律要的是**委派树根的见证**:根 id(行上本来就带 `rootSessionId`)+
|
|
29
|
-
*
|
|
30
|
-
* `{ sessionId: root, delegationRoot }` 重解,不交座的行(
|
|
28
|
+
* 律要的是**委派树根的见证**:根 id(行上本来就带 `rootSessionId`)+ 判断那一刻的根行住客 = 写口放行的那一位(赎回腿解析器那一读;不是那一位时
|
|
29
|
+
* 写口整条拒,走不到这里)—— 见证恒带住客。所以**每一行**都交出它({@link ScenarioContext} 的 `delegationRoot`):交座的一层行以
|
|
30
|
+
* `{ sessionId: root, delegationRoot }` 重解,不交座的行(更深的行、根属主不是卡的租户的一层行)以 `{ delegationRoot }` 重解 —— 只喂停驻挂点,不挂任何会话级设施、
|
|
31
31
|
* 不带 principal,交座判据(上面三条)一个字不动。行上没有根 ⇒ boot 期那只裸 Agent(没有根可交 ⇒ 律给哨兵)。挂点那一刻根行已不在 ⇒ 律给哨兵;在而不是见证的那一位
|
|
32
32
|
* (批准之后删了、同 id 被重新登记)⇒ 停驻否决 —— 两件都在挂点那一侧判,与这里无关。
|
|
33
33
|
*/
|
|
@@ -41,15 +41,12 @@ export interface ParkedReviveSurface {
|
|
|
41
41
|
readonly mountsCallerTool?: (tool: ToolSpec) => boolean;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* 按行解析赎回面的入参身份:`principal` = 权威 principal(取 checkpoint 的 scope 列,不取行上有损的 `row.scope`);`
|
|
45
|
-
*
|
|
44
|
+
* 按行解析赎回面的入参身份:`principal` = 权威 principal(取 checkpoint 的 scope 列,不取行上有损的 `row.scope`);`rootOccupant` = 写口放行的那一位
|
|
45
|
+
* 根会话住客(赎回腿认领之前解析器那一次行读;属主 `null` = 匿名会话)—— 型上恒在:接赎回腿的部署必须带执行席。
|
|
46
46
|
*/
|
|
47
47
|
export interface ParkedReviveSurfaceIdentity {
|
|
48
48
|
readonly principal: string | undefined;
|
|
49
|
-
readonly
|
|
50
|
-
readonly before: SessionFace;
|
|
51
|
-
readonly after: SessionFace;
|
|
52
|
-
};
|
|
49
|
+
readonly rootOccupant: SessionFace;
|
|
53
50
|
}
|
|
54
51
|
/** 按行解析赎回面。 */
|
|
55
52
|
export type ParkedReviveSurfaceFactory = (row: BackgroundAgentRecord, identity: ParkedReviveSurfaceIdentity) => ParkedReviveSurface;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_SUBAGENT_TOOL_NAME
|
|
1
|
+
import { DEFAULT_SUBAGENT_TOOL_NAME } from "@sema-agent/core";
|
|
2
2
|
const agentOf = (tools) => tools.find((t) => t.name === DEFAULT_SUBAGENT_TOOL_NAME);
|
|
3
3
|
export function createParkedReviveSurface(defaultScenario) {
|
|
4
4
|
const bootTool = agentOf(defaultScenario({}).tools);
|
|
@@ -9,7 +9,7 @@ export function createParkedReviveSurface(defaultScenario) {
|
|
|
9
9
|
if (root === undefined)
|
|
10
10
|
return { reviveTool: bootTool };
|
|
11
11
|
const seated = holdsSeat(row.parentSessionId === root, identity);
|
|
12
|
-
const delegationRoot = { sessionId: root, incarnation: identity.
|
|
12
|
+
const delegationRoot = { sessionId: root, incarnation: identity.rootOccupant.incarnation };
|
|
13
13
|
const tools = (seated ? defaultScenario({}, identity.principal, { sessionId: root, delegationRoot }) : defaultScenario({}, undefined, { delegationRoot })).tools;
|
|
14
14
|
const reviveTool = agentOf(tools);
|
|
15
15
|
if (reviveTool === undefined)
|
|
@@ -18,9 +18,6 @@ export function createParkedReviveSurface(defaultScenario) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function holdsSeat(oneLevel, identity) {
|
|
21
|
-
|
|
22
|
-
if (!oneLevel || face === undefined || compareOccupants(face.before, face.after) !== "same")
|
|
23
|
-
return false;
|
|
24
|
-
return (face.before.owner ?? undefined) === identity.principal && (face.after.owner ?? undefined) === identity.principal;
|
|
21
|
+
return oneLevel && (identity.rootOccupant.owner ?? undefined) === identity.principal;
|
|
25
22
|
}
|
|
26
23
|
//# sourceMappingURL=parked-revive-surface.js.map
|
package/dist/peer-seat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { A2aServerSpec, McpServerSpec, PeerAnswer, PeerQuestion, PeerSeatTarget } from "@sema-agent/core";
|
|
1
|
+
import type { A2aServerSpec, McpServerSpec, PeerAnswer, PeerQuestion, PeerSeat, PeerSeatTarget } from "@sema-agent/core";
|
|
2
2
|
import { type Logger } from "./observability/logger.js";
|
|
3
3
|
import { type SafeText } from "./trace/exit-seat.js";
|
|
4
4
|
type StdioArm = Extract<McpServerSpec["transport"], {
|
|
@@ -95,16 +95,22 @@ export interface PeerLease {
|
|
|
95
95
|
/** Stop holding. Entries no other holder holds are dropped now. A second call does nothing. */
|
|
96
96
|
release(): void;
|
|
97
97
|
}
|
|
98
|
-
/**
|
|
99
|
-
export interface PeerDeskSeat {
|
|
100
|
-
/** The pieces for one send, or a throw (refused: logged as one `peer_seat_refused` row naming protocol, peer and reason only). */
|
|
101
|
-
answer(question: PeerQuestion): Promise<PeerAnswer>;
|
|
102
|
-
}
|
|
103
|
-
/** Live entries and the host leases holding them, summed across entries — the registry's whole observable state. */
|
|
98
|
+
/** Live entries and their holders — host leases and engine holds — summed across entries: the registry's whole observable state. */
|
|
104
99
|
export interface PeerDeskSize {
|
|
105
100
|
readonly entries: number;
|
|
106
101
|
readonly holders: number;
|
|
107
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* The engine's seat as the desk is it (`RunnerDeps.peerSeat`, the probe's and the panels' `peerSeat`): `answer` the pieces for one send, or a
|
|
105
|
+
* throw (refused: logged as one `peer_seat_refused` row naming protocol, peer and reason only) — it reads no signal (the desk does no IO);
|
|
106
|
+
* `hold` / `release` one engine holder of a reference this desk minted. Every hold the desk cannot count throws — a reference this desk did not
|
|
107
|
+
* mint (any kind but `handle`), a handle whose entry is gone — and so does a release with no counted hold: the engine reads a hold that returns
|
|
108
|
+
* as held, so a quiet return would be a hold nobody keeps (security axis, fail-closed). The engine reports each throw once as `peer-seat-hold`;
|
|
109
|
+
* a refused hold holds nothing and gets no release, a refused release changes nothing.
|
|
110
|
+
*/
|
|
111
|
+
export interface PeerDeskSeat extends PeerSeat {
|
|
112
|
+
answer(question: PeerQuestion, signal?: AbortSignal): Promise<PeerAnswer>;
|
|
113
|
+
}
|
|
108
114
|
export interface PeerDesk {
|
|
109
115
|
readonly seat: PeerDeskSeat;
|
|
110
116
|
/**
|
package/dist/peer-seat.js
CHANGED
|
@@ -104,7 +104,7 @@ export function createPeerDesk(opts = {}) {
|
|
|
104
104
|
return entry;
|
|
105
105
|
};
|
|
106
106
|
const dropIfUnheld = (id, entry) => {
|
|
107
|
-
if (entry.hosts.size === 0)
|
|
107
|
+
if (entry.hosts.size === 0 && entry.engineHolds === 0)
|
|
108
108
|
table.delete(id);
|
|
109
109
|
};
|
|
110
110
|
const liveState = (lease) => {
|
|
@@ -151,7 +151,41 @@ export function createPeerDesk(opts = {}) {
|
|
|
151
151
|
return { kind: "carry", env: entry.pieces };
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
|
+
const handleIdOf = (ref) => {
|
|
155
|
+
switch (ref.kind) {
|
|
156
|
+
case "handle":
|
|
157
|
+
return ref.id;
|
|
158
|
+
case "env":
|
|
159
|
+
case "vault":
|
|
160
|
+
case "assertion":
|
|
161
|
+
return undefined;
|
|
162
|
+
default: {
|
|
163
|
+
const unknownKind = ref;
|
|
164
|
+
void unknownKind;
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
154
169
|
const seat = Object.freeze({
|
|
170
|
+
hold: (ref) => {
|
|
171
|
+
const id = handleIdOf(ref);
|
|
172
|
+
if (id === undefined)
|
|
173
|
+
throw new Error("peer desk: hold of a reference this desk did not mint — not held");
|
|
174
|
+
const entry = table.get(id);
|
|
175
|
+
if (entry === undefined)
|
|
176
|
+
throw new Error("peer desk: hold of a handle whose entry is gone — not held (a hold never revives an entry)");
|
|
177
|
+
entry.engineHolds += 1;
|
|
178
|
+
},
|
|
179
|
+
release: (ref) => {
|
|
180
|
+
const id = handleIdOf(ref);
|
|
181
|
+
if (id === undefined)
|
|
182
|
+
throw new Error("peer desk: release of a reference this desk did not mint — nothing released");
|
|
183
|
+
const entry = table.get(id);
|
|
184
|
+
if (entry === undefined || entry.engineHolds === 0)
|
|
185
|
+
throw new Error("peer desk: release of a handle with no counted engine hold — nothing released");
|
|
186
|
+
entry.engineHolds -= 1;
|
|
187
|
+
dropIfUnheld(id, entry);
|
|
188
|
+
},
|
|
155
189
|
answer: async (question) => {
|
|
156
190
|
const target = question.peer;
|
|
157
191
|
const ref = target.credentials;
|
|
@@ -193,7 +227,7 @@ export function createPeerDesk(opts = {}) {
|
|
|
193
227
|
const id = mintId(canonical);
|
|
194
228
|
const known = table.get(id);
|
|
195
229
|
if (known === undefined) {
|
|
196
|
-
table.set(id, { binding, lane, pieces, hosts: new Set([holder.id]) });
|
|
230
|
+
table.set(id, { binding, lane, pieces, hosts: new Set([holder.id]), engineHolds: 0 });
|
|
197
231
|
}
|
|
198
232
|
else if (canonicalOf(known.binding, known.lane, known.pieces) !== canonical) {
|
|
199
233
|
throw new PeerDeskError("peer_binding_conflict", "peer desk: two different declarations were minted one id; the registered one stands");
|
|
@@ -212,7 +246,7 @@ export function createPeerDesk(opts = {}) {
|
|
|
212
246
|
size: () => {
|
|
213
247
|
let holders = 0;
|
|
214
248
|
for (const entry of table.values())
|
|
215
|
-
holders += entry.hosts.size;
|
|
249
|
+
holders += entry.hosts.size + entry.engineHolds;
|
|
216
250
|
return { entries: table.size, holders };
|
|
217
251
|
},
|
|
218
252
|
});
|
|
@@ -86,8 +86,8 @@ export class SqlAdoptionLogStore {
|
|
|
86
86
|
constructor(db) {
|
|
87
87
|
this.db = db;
|
|
88
88
|
}
|
|
89
|
-
q(
|
|
90
|
-
return this.db.dialect === "
|
|
89
|
+
q(mysql, pg) {
|
|
90
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
91
91
|
}
|
|
92
92
|
get reader() {
|
|
93
93
|
return this.db;
|
|
@@ -191,7 +191,7 @@ export class SqlAdoptionLogStore {
|
|
|
191
191
|
try {
|
|
192
192
|
lockName = qualifyAdoptionLockName(await this.resolveLockKeyspace(conn), name);
|
|
193
193
|
for (let i = 0; i < attempts && !held; i += 1) {
|
|
194
|
-
const res = await conn.query(this.q("SELECT GET_LOCK(?, 0) AS got", "SELECT pg_try_advisory_lock($1, $2) AS got"), this.db.dialect === "
|
|
194
|
+
const res = await conn.query(this.q("SELECT GET_LOCK(?, 0) AS got", "SELECT pg_try_advisory_lock($1, $2) AS got"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
|
|
195
195
|
const got = res.rows[0]?.got;
|
|
196
196
|
held = got === 1 || got === true || got === "1";
|
|
197
197
|
if (!held)
|
|
@@ -204,7 +204,7 @@ export class SqlAdoptionLogStore {
|
|
|
204
204
|
finally {
|
|
205
205
|
if (held) {
|
|
206
206
|
try {
|
|
207
|
-
await conn.query(this.q("SELECT RELEASE_LOCK(?) AS released", "SELECT pg_advisory_unlock($1, $2) AS released"), this.db.dialect === "
|
|
207
|
+
await conn.query(this.q("SELECT RELEASE_LOCK(?) AS released", "SELECT pg_advisory_unlock($1, $2) AS released"), this.db.dialect === "mysql" ? [lockName] : [PG_ADOPTION_LOCK_CLASS, pgLockObjectKey(lockName)]);
|
|
208
208
|
}
|
|
209
209
|
catch (releaseErr) {
|
|
210
210
|
console.warn("[adoption-log] releasing the adoption arc lock failed (it auto-frees when the connection closes):", releaseErr);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ExecutionLane } from "../execution-lane-caps.js";
|
|
1
2
|
import type { AskState } from "../approval-ask-machine.js";
|
|
2
3
|
import type { SettleCause } from "../approval-deny-reasons.js";
|
|
3
4
|
import type { AskCallCoordinate, AskRow, AskTerminalClaimIntent, AskTerminalClaimOpts, AskTerminalClaimOutcome, AskTransitionPatch, ApprovalAskStore, BatchRow, BindGateInput, BindResult, DecideAskInput, DecideResult, EnsureAskResult, ExpireResult, NewAskRow } from "./approval-ask-store-sql.js";
|
|
@@ -209,6 +210,12 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
209
210
|
};
|
|
210
211
|
/** 释放追加 fd(`LocalBackend.close` → 优雅重启重开)。不包 try/catch 的理由同
|
|
211
212
|
* {@link FileApprovalNonceStore.dispose}:core 的 `AppendLog.close()` 自己就是全函数。 */
|
|
213
|
+
/**
|
|
214
|
+
* 执行放置切换 ④b 的 local 腿(一次性迁移登记表 M32;`LocalBackend.runOneTimeMigrations` 在任何读者之前调):升级前写下的问(日志 `ensureAsk` 记录行 /
|
|
215
|
+
* 压实快照行没有 `executionLane`)记成本部署车道表的唯一项,然后**一次压实**把整本账重写成带车道的快照(压实 = 既有整本原子重写)。
|
|
216
|
+
* 没有缺车道的行 ⇒ 零写。车道表不是一元而还有缺车道的行 ⇒ 抛与 SQL 同一只拒(`backfill_ambiguous`,由调用方铸),一个字节没写。
|
|
217
|
+
*/
|
|
218
|
+
backfillExecutionLanes(lanes: readonly ExecutionLane[], refuse: (pending: number) => never): number;
|
|
212
219
|
dispose(): void;
|
|
213
220
|
}
|
|
214
221
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { closeSync, copyFileSync, openSync, readSync, statSync, existsSync } from "node:fs";
|
|
2
|
+
import { isExecutionLane } from "../execution-lane-caps.js";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { AppendLog, atomicWriteFile, ensureDir } from "@sema-agent/core";
|
|
4
5
|
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
@@ -295,6 +296,16 @@ export class FileApprovalAskStore {
|
|
|
295
296
|
replayStats() {
|
|
296
297
|
return { rejected: this.replayRejectedCount, corruptLines: this.corruptLineCount, records: this.records };
|
|
297
298
|
}
|
|
299
|
+
backfillExecutionLanes(lanes, refuse) {
|
|
300
|
+
const pending = this.core.exportRows().asks.filter((r) => !isExecutionLane(String(r.executionLane ?? ""))).length;
|
|
301
|
+
if (pending === 0)
|
|
302
|
+
return 0;
|
|
303
|
+
if (lanes.length !== 1)
|
|
304
|
+
refuse(pending);
|
|
305
|
+
const filled = this.core.fillMissingExecutionLanes(lanes[0]);
|
|
306
|
+
this.compact();
|
|
307
|
+
return filled;
|
|
308
|
+
}
|
|
298
309
|
dispose() {
|
|
299
310
|
this.log?.close();
|
|
300
311
|
this.log = undefined;
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
* - resolveProvisional 故意不经 canAskTransition(同 SQL twin 头注:这是版本化补偿的例外通道)。
|
|
30
30
|
*/
|
|
31
31
|
import { type AskState, type BatchState } from "../approval-ask-machine.js";
|
|
32
|
+
import { type ExecutionLane } from "../execution-lane-caps.js";
|
|
32
33
|
import type { AskCallCoordinate, AskDecision, AskRow, AskTerminalClaimIntent, AskTerminalClaimOpts, AskTerminalClaimOutcome, AskTransitionPatch, ApprovalAskStore, BatchRow, BindGateInput, BindResult, DecideAskInput, EnsureAskResult, DecideResult, ExpireResult, NewAskRow } from "./approval-ask-store-sql.js";
|
|
33
34
|
/**
|
|
34
35
|
* [ref] 的 `updatedInput` **入店编码**——两 twin 共用的那一只(本函数是唯一真源)。
|
|
@@ -72,6 +73,11 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
|
|
|
72
73
|
* 两种可变性)。
|
|
73
74
|
*/
|
|
74
75
|
exportRows(): ApprovalAskRowsSnapshot;
|
|
76
|
+
/**
|
|
77
|
+
* 执行放置切换 ④b 的 local 腿(File 形的内核口;`FileApprovalAskStore.backfillExecutionLanes` 调):出处车道还没记下的行(升级前写下的日志 / 快照行
|
|
78
|
+
* 装回后这一格缺席)记成 `lane`(一元车道表的唯一项 —— 调用方判过)。返回改了的行数;只碰缺这一格的行。
|
|
79
|
+
*/
|
|
80
|
+
fillMissingExecutionLanes(lane: ExecutionLane): number;
|
|
75
81
|
/** [ref]:一行的**同步**点读(拷贝;纯读,零判定)—— File 形在写口的同步前缀里用它(见类顶注)。 */
|
|
76
82
|
peekAsk(askId: string): AskRow | null;
|
|
77
83
|
/** [ref]:把快照里的行**原样**装回两张 Map(同键覆盖)。零谓词、零状态机 —— 它不是一条转移,是重放
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
|
|
2
|
+
import { isExecutionLane } from "../execution-lane-caps.js";
|
|
2
3
|
import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
|
|
3
4
|
import { askRowCarriesOwner, askRowOnCall, assertDecideSettledBy, assertIdempotencyKeyShape, assertIntegralEpochMs, assertReapBounds, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth, projectDecideReading } from "./approval-ask-store-sql.js";
|
|
4
5
|
function applyPatch(row, patch) {
|
|
@@ -41,6 +42,16 @@ export class InMemoryApprovalAskStore {
|
|
|
41
42
|
exportRows() {
|
|
42
43
|
return { asks: [...this.asks.values()].map((r) => ({ ...r })), batches: [...this.batches.values()].map((b) => ({ ...b })) };
|
|
43
44
|
}
|
|
45
|
+
fillMissingExecutionLanes(lane) {
|
|
46
|
+
let filled = 0;
|
|
47
|
+
for (const [askId, row] of this.asks) {
|
|
48
|
+
if (isExecutionLane(String(row.executionLane ?? "")))
|
|
49
|
+
continue;
|
|
50
|
+
this.asks.set(askId, { ...row, executionLane: lane });
|
|
51
|
+
filled += 1;
|
|
52
|
+
}
|
|
53
|
+
return filled;
|
|
54
|
+
}
|
|
44
55
|
peekAsk(askId) {
|
|
45
56
|
const row = this.asks.get(askId);
|
|
46
57
|
return row ? { ...row } : null;
|
|
@@ -74,6 +85,7 @@ export class InMemoryApprovalAskStore {
|
|
|
74
85
|
sourceTaskId: row.sourceTaskId,
|
|
75
86
|
sessionId: row.sessionId,
|
|
76
87
|
owner: row.owner ?? null,
|
|
88
|
+
executionLane: row.executionLane,
|
|
77
89
|
batchId: row.batchId,
|
|
78
90
|
toolCallId: row.toolCallId,
|
|
79
91
|
legKey: row.legKey,
|