@sema-agent/server 7.135.0 → 7.137.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 +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
package/deploy/sema-up/README.md
CHANGED
|
@@ -166,10 +166,24 @@ server 的 `<数据根>/sealed-key/` 存着停驻任务续跑上下文里请求
|
|
|
166
166
|
有效访问模式是 `ReadWriteOnce` 时 Deployment 用 `Recreate`(RWO 卷同时只能被一个节点挂,滚动更新的新 Pod 落在别的节点就永远等不到卷;代价是升级窗口内短暂没有副本)。
|
|
167
167
|
- **没有 RWX 的集群**:`--set server.replicas=1`,或单节点多副本盖 RWO(上一条),或关持久 `--set sealedKey.persistence.enabled=false` —— 关掉时图表**显式**给每只 worker 设 `DURABLE_APPROVAL=false`
|
|
168
168
|
(耐久审批整条关,不产生续不了的停驻行;`helm install` 打印的安装说明里也有这一句),且同时配 `server.durableApproval=true` 渲染即拒。
|
|
169
|
-
- **首装是空卷**:多个 pod
|
|
169
|
+
- **首装是空卷**:多个 pod 并发首启可能各建一把钥匙、各持自己那一把(钥匙创建没有互斥,只有两个副本在同一个目录上同时首启、都没读到可用的钥匙文件时才发生,不是每次都分叉);装好后不论有没有分叉都执行 `kubectl -n <ns> rollout restart deploy/sema-server`(启用 `serverV4pro` 时再加 `deploy/sema-server-v4pro`)一次,之后每个 pod 读到全部钥匙。
|
|
170
170
|
- PVC 的 `accessModes` 建好后不可改:副本数跨过 1 的边界时 `helm upgrade` 会撞「spec is immutable」;卷里是私钥,换卷前先结掉停驻行或备份钥匙文件。
|
|
171
171
|
PVC 带 `helm.sh/resource-policy: keep`:`helm uninstall` 与把 `enabled` 改成 `false` 的 upgrade 都不删它(确认不要了再 `kubectl -n <ns> delete pvc sema-server-sealed-key`)。
|
|
172
172
|
|
|
173
|
+
## 升级时的副本更新(7.137.0:停驻任务的请求体改写)
|
|
174
|
+
|
|
175
|
+
7.137.0 起服时把 7.136.x 及以前停驻的任务存下的请求体原地改写(请求键 `promptProfile` 换成布尔 `promptLean`;窗内每次起服都扫,细节见随包 `MIGRATION.md` 7.137.0 节第 4 条)。
|
|
176
|
+
改写过的体 7.136.x 读不懂,所以升级序是**先停光 7.136.x 副本,再起第一个 7.137.0 副本** —— 滚动更新做不到这一点。
|
|
177
|
+
|
|
178
|
+
**升级前先缩光副本,按安装路径分三种**(滚动更新做不到「先停光 7.136.x、再起第一个本版副本」;下面三段各管一种安装路径,照你的那一种做):
|
|
179
|
+
|
|
180
|
+
- **Helm 形安装**(`kube-up.sh` 的 `--kube=existing` / `--kube=k3s`;`--kube=machines` 下设了 `STACK_ENGINE=helm`):图表只在钥匙卷是 `ReadWriteOnce` 时给 Deployment 写 `Recreate`;缺省的多副本(`server.replicas: 3`、`ReadWriteMany`)不写更新策略,`helm upgrade` 走 Kubernetes 缺省的滚动更新 —— 第一只本版 pod 起服改写存体时 7.136.x 副本还在跑。所以升级前先 `kubectl -n <命名空间> scale deploy/sema-server --replicas=0`(启用 `serverV4pro` 时同样缩 `deploy/sema-server-v4pro`),等旧 pod 退光,再 `helm upgrade`(重跑 `sema-up` / `kube-up.sh` 在这些安装形上走的就是这条),升完核副本数回到 `server.replicas`。
|
|
181
|
+
- **`--kube=machines` 缺省形**(不设 `STACK_ENGINE` = `render`;`cluster-up.sh` 渲染清单后 `kubectl apply`,不是 Helm):清单里 `sema-server` 的副本数按机器数取 2(少于 3 台)或 3(3 台及以上),没有 `strategy` 键 = Kubernetes 缺省的滚动更新,也没有 `serverV4pro`。所以升级前先 `kubectl -n sema scale deploy/sema-server --replicas=0`(第一台机器上写 `k3s kubectl`),等旧 pod 退光,再 `kubectl apply`(重跑 `sema-up` 做的就是这一步;清单里的 `replicas` 把副本数写回 2 或 3),升完核副本数。
|
|
182
|
+
- **compose 两档**(`--profile=full-single` / `local-plus`):`server` 服务只有一只容器、没有副本数键 —— 没有第二只旧副本在滚动更新里陪跑,零动作。
|
|
183
|
+
|
|
184
|
+
已经滚动升过的:等最后一只 7.136.x 副本退出之后,`kubectl -n <命名空间> rollout restart deploy/sema-server`(启用 `serverV4pro` 时再加 `deploy/sema-server-v4pro`)重起一轮本版副本 —— 起服即改写窗内没改写到的任务(重启用的配置里须 `DURABLE_APPROVAL=true` —— 多租户部署缺省关,要显式写,且多租户还需 `OPERATOR_PRINCIPALS` 非空,否则起服拒(拒句点名出路);起服日志里 `checkpoint_ctx_prompt_profile_rewritten`(M30)与 `checkpoint_ctx_credentials_sealed`(M28)两行都出现、且 M30 那一行是 `"window":"open"`,才说明守卫过了、改写跑过 —— M28 的窗先关,窗关后它那一行 `"window":"closed"` 属正常)。
|
|
185
|
+
Helm 形里,未启用 `serverV4pro`、钥匙卷是 `ReadWriteOnce` 的形(`server.replicas=1`、或随包 `values-full-single.yaml` 这类显式盖 `ReadWriteOnce` 的形)图表对 `deploy/sema-server` 本来用 `Recreate`,旧 pod 先停,零动作;启用 `serverV4pro` 时两只 Deployment 各自 `Recreate`、互不等待,仍按上面 Helm 形一条先把两只都缩到 0。
|
|
186
|
+
|
|
173
187
|
## 凭据纪律
|
|
174
188
|
|
|
175
189
|
全部凭据生成一次、落 `~/.sema-up/<project>/.env`(0600),重跑**绝不轮换**;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
多个 pod 共用这只卷,存储类必须支持 ReadWriteMany(PVC 一直 Pending 就是它不支持:出路 server.replicas=1,或 sealedKey.persistence.enabled=false 并接受耐久审批随之关)。
|
|
9
9
|
{{- end }}
|
|
10
10
|
{{- if gt (int (include "sema.sealedKey.podCount" .)) 1 }}
|
|
11
|
-
|
|
11
|
+
首装是空卷:多个副本并发首启可能各建一把钥匙、各持自己那一把(只有两个副本在同一个目录上同时首启、都没读到可用的钥匙文件时才发生,不是每次都分叉)—— 装好后不论有没有分叉都执行一次 kubectl -n {{ .Release.Namespace }} rollout restart deploy/sema-server{{ if .Values.serverV4pro.enabled }} deploy/sema-server-v4pro{{ end }},此后每个副本读到目录里的全部钥匙。
|
|
12
12
|
{{- end }}
|
|
13
13
|
{{- else }}
|
|
14
14
|
停驻封签钥匙:sealedKey.persistence.enabled=false —— 不建卷,图表已给每只 worker 显式设上 DURABLE_APPROVAL=false(耐久审批整条关,不产生续不了的停驻行;代价见 values.yaml 的 sealedKey 块;要重新打开,把它改回 true)。关持久 + 提交体没声明 settings.approvalFrameFamilies 的客户端 ⇒ 受门工具当场拒(没有耐久店,无处停驻;7.134.0 起审批卡只发给声明认得这一族的客户端)。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ── 沙箱 lane RBAC(k8s+kata;pod 五动词 + 缓存卷四动词 + 出网自检的两只只读:本 ns 的 NetworkPolicy、集群的节点表)────────────────────────────────
|
|
1
|
+
# ── 沙箱 lane RBAC(k8s+kata;pod 五动词 + 缓存卷四动词 + 沙箱秘密座 create + 出网自检的两只只读:本 ns 的 NetworkPolicy、集群的节点表)────────────────────────────────
|
|
2
2
|
apiVersion: v1
|
|
3
3
|
kind: ServiceAccount
|
|
4
4
|
metadata:
|
|
@@ -33,6 +33,12 @@ rules:
|
|
|
33
33
|
- apiGroups: [""]
|
|
34
34
|
resources: [persistentvolumeclaims]
|
|
35
35
|
verbs: [get, create, delete, deletecollection]
|
|
36
|
+
# 沙箱秘密座:K8S_SANDBOX_ENV 点名的值只进每只 pod 自己的 Secret(pod spec 只带 secretKeyRef;Secret 属于 pod,随 pod 由集群回收)
|
|
37
|
+
# 动词 = 适配器为 env Secret 真发的调用:create;不读回(无 get)、不删(随属主 pod 由集群 GC)。名单非空而缺它 ⇒ server 起服拒(起服 SelfSubjectAccessReview 问的正是它)
|
|
38
|
+
# 成员 git 凭据平面(本版恒关)打开时,租约 Secret 还要 patch —— 那时再加
|
|
39
|
+
- apiGroups: [""]
|
|
40
|
+
resources: [secrets]
|
|
41
|
+
verbs: [create]
|
|
36
42
|
# 起服的出网自检读本 ns 的 NetworkPolicy(只读)
|
|
37
43
|
- apiGroups: [networking.k8s.io]
|
|
38
44
|
resources: [networkpolicies]
|
|
@@ -86,7 +86,7 @@ server:
|
|
|
86
86
|
# 这只卷里是私钥,不要为了过这个错直接删 PVC:先把停驻行结掉(随包排空检查脚本数一数)或备份钥匙文件,再换卷。
|
|
87
87
|
# PVC 带 helm.sh/resource-policy: keep:`helm uninstall` 与把 enabled 改成 false 的 upgrade 都不删它(钥匙留着;确认不要了再 `kubectl -n <ns> delete pvc sema-server-sealed-key`);
|
|
88
88
|
# 同名 release 重装会接管这只留下的 PVC。
|
|
89
|
-
# ⚠
|
|
89
|
+
# ⚠ 空卷上多副本并发首启:钥匙创建没有互斥,两个副本在同一个目录上同时首启、都没读到可用的钥匙文件时,各副本可能各建一把钥匙、只持有自己那一把,直到下次重启(不是每次都分叉)—— 首装后不论有没有分叉都执行 `kubectl -n <ns> rollout restart deploy/sema-server`
|
|
90
90
|
# 一次(启用 serverV4pro 时再加 deploy/sema-server-v4pro;NOTES 也说这句),重启之后每个副本读到目录里的全部钥匙,从此一致。
|
|
91
91
|
sealedKey:
|
|
92
92
|
persistence:
|
|
@@ -92,7 +92,7 @@ kind: Service
|
|
|
92
92
|
metadata: { name: sema-minio, namespace: sema }
|
|
93
93
|
spec: { selector: { app: sema-minio }, ports: [{ name: s3, port: 9000 }, { name: console, port: 9001 }] }
|
|
94
94
|
---
|
|
95
|
-
# ── 沙箱 lane RBAC(k8s+kata
|
|
95
|
+
# ── 沙箱 lane RBAC(k8s+kata;pod 五动词 + 缓存卷四动词 + 沙箱秘密座 create + 出网自检的两只只读:本 ns 的 NetworkPolicy、集群的节点表)────────────────────────────────
|
|
96
96
|
apiVersion: v1
|
|
97
97
|
kind: ServiceAccount
|
|
98
98
|
metadata: { name: sema-runner, namespace: sema-sandbox }
|
|
@@ -122,6 +122,12 @@ rules:
|
|
|
122
122
|
- apiGroups: [""]
|
|
123
123
|
resources: [persistentvolumeclaims]
|
|
124
124
|
verbs: [get, create, delete, deletecollection]
|
|
125
|
+
# 沙箱秘密座:K8S_SANDBOX_ENV 点名的值只进每只 pod 自己的 Secret(pod spec 只带 secretKeyRef;Secret 属于 pod,随 pod 由集群回收)
|
|
126
|
+
# 动词 = 适配器为 env Secret 真发的调用:create;不读回(无 get)、不删(随属主 pod 由集群 GC)。名单非空而缺它 ⇒ server 起服拒(起服 SelfSubjectAccessReview 问的正是它)
|
|
127
|
+
# 成员 git 凭据平面(本版恒关)打开时,租约 Secret 还要 patch —— 那时再加
|
|
128
|
+
- apiGroups: [""]
|
|
129
|
+
resources: [secrets]
|
|
130
|
+
verbs: [create]
|
|
125
131
|
# 起服的出网自检读本 ns 的 NetworkPolicy(只读)
|
|
126
132
|
- apiGroups: [networking.k8s.io]
|
|
127
133
|
resources: [networkpolicies]
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* 没有方法、没有捕获的行为。
|
|
22
22
|
*/
|
|
23
23
|
import { z } from "zod";
|
|
24
|
-
import { ALLOW_RULE_MEMBERS, READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type ResumeStartupCause, type RuleOffer, type RuleOffersAbsence as CoreRuleOffersAbsence, type SteerReplayFacet } from "@sema-agent/core";
|
|
24
|
+
import { ALLOW_RULE_MEMBERS, READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type CheckpointSummary as CoreCheckpointSummary, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type ResumeStartupCause, type RuleOffer, type RuleOffersAbsence as CoreRuleOffersAbsence, type SteerReplayFacet, type ToolApprovalCard } from "@sema-agent/core";
|
|
25
25
|
import type { AskRow } from "./plugins/approval-ask-store-sql.js";
|
|
26
26
|
import type { SettleCause, SettleOutcome } from "./approval-deny-reasons.js";
|
|
27
27
|
import type { ToolApprovalFrame } from "./tool-approval.js";
|
|
@@ -440,7 +440,7 @@ export interface AskClassPair {
|
|
|
440
440
|
* [ref] ②(core 7.37.0 [ref])第六 / 七位 `askClass` / `classes`:这一问的类对(经 {@link readAskClassPair};两位同在同缺,与活卡帧 / 呈卡帧的卡
|
|
441
441
|
* 同名同义同值)—— durable 审批席按类答的依据。缺席 = 老行(core 7.37.0 之前 park 的,续跑不补)/ 读不出(计 [ref] tag)。
|
|
442
442
|
* [ref](core 7.51.0 [ref])第八位 `boundGrantAbsence`:这张卡为什么没有授予绑定摘要(经 {@link readClosedWordSeat};与活卡帧 / inbox 行同名同义同值)——
|
|
443
|
-
* `not_bindable` = 这一问不绑任何授予(「本会话放行」可按工具记)。缺席 = 技能卡(
|
|
443
|
+
* `not_bindable` = 这一问不绑任何授予(「本会话放行」可按工具记)。缺席 = 技能卡(带摘要;摘要与清单、成因词三位走另一只投影 {@link skillCardFacts},见其顶注)/ 7.51.0 之前停驻的行(core inbox 投
|
|
444
444
|
* `unreadable`,队列行如实缺席)/ 集外词(计 [ref] tag)。
|
|
445
445
|
*/
|
|
446
446
|
export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence" | "mandated" | "mandate" | "origin" | "askClass" | "classes" | "boundGrantAbsence">;
|
|
@@ -471,6 +471,32 @@ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "rule
|
|
|
471
471
|
* 不许拿本函数的产物去推强制 —— 两者共用的是 schema,不是折叠。
|
|
472
472
|
*/
|
|
473
473
|
export declare function parkRowFacts(stored: unknown): ParkRowFacts;
|
|
474
|
+
/**
|
|
475
|
+
* 🔴 **技能卡三位的唯一投影**:授予绑定摘要 `boundGrantDigest`、它哈希的清单 `boundGrants`、分类器没给判词的成因 `classifierNoRuling` ——
|
|
476
|
+
* 助手收件箱行(`GET /v1/assistant/inbox`)、运维队列行(`GET /v1/approvals` 的 `pending[]`)、`/v1/approvals/stream` 的 `pending` 帧三张停驻读面
|
|
477
|
+
* **经这一只函数**投它们。同一张停驻的技能卡于是三面同一份字节:运维从队列决这张卡时,看得到批准它还授予了什么。
|
|
478
|
+
*
|
|
479
|
+
* **输入恒是 core 的检查点摘要**({@link CoreCheckpointSummary},`summarizeCheckpoint` 的产物)—— 三位的校验点单点在 core:摘要是串才回显;清单**只在哈希等于摘要时**才发布
|
|
480
|
+
* (「never a list beside a digest that binds another」—— 一份与摘要对不上的清单会让批准人看着一份清单、批的却是另一份绑定);成因词只在闭集成员时回显。
|
|
481
|
+
* 本仓**不重算哈希**(core 的 `boundInputHashOf` 不在公开导出面上,清单校验读口 `readBoundBinding` 同样不导出),所以**不许**拿寄存行 `pendingAction` 上的原值直接投 ——
|
|
482
|
+
* 原值上的清单是未经哈希校验的(这正是三位此前不上队列行的原因,见 `trace/core-keyset-guard.ts` ⑧b 记账的旧注)。两只店各自拿到「这一行的摘要」的办法不同
|
|
483
|
+
* (LOCAL 孪生本来就握着 core 对同一行投的摘要;SQL 孪生对带三位之一的行读一次该行再调 core 的 `summarizeCheckpoint`,见 `plugins/checkpoint-store-sql.ts`),
|
|
484
|
+
* 但投影只有这一只:OMIT 契约 —— 缺席即缺席,恒不铸 `undefined` 值的键。
|
|
485
|
+
*
|
|
486
|
+
* `boundGrants` 是**部署文本**(技能名、清单条目):本函数不脱敏、不转义,与收件箱行逐字同口径 —— 渲染按上下文转义是客户端的事(契约同句)。
|
|
487
|
+
* 三位都不进 `card_json`(同步路铸点,另一条纪律)。
|
|
488
|
+
*
|
|
489
|
+
* 🚧 清单校验读口 `readBoundBinding` 若成为 core 的公开导出,SQL 孪生那一次整行读与 {@link parkRowCarriesSkillCardFacts} 一并退役:`parkRowFacts` 直接读寄存行原值经 core 的读口投三位。
|
|
490
|
+
*/
|
|
491
|
+
export type SkillCardFacts = Pick<CoreCheckpointSummary, "boundGrantDigest" | "boundGrants" | "classifierNoRuling">;
|
|
492
|
+
export declare function skillCardFacts(summary: SkillCardFacts): SkillCardFacts;
|
|
493
|
+
/**
|
|
494
|
+
* 这一行的寄存行(`pendingAction`,读自持久层 ⇒ `unknown`)**有没有可能**让 core 的摘要投出三位里的任何一位 —— 只回答「值不值得为它取一次摘要」,**不投值**:
|
|
495
|
+
* `tool_approval` 臂上摘要字段是串(core 只在这时回显摘要与清单),或成因词键在场(是不是闭集成员由 core 的摘要判)。判错的方向只有一个 —— 多取一次摘要(摘要读数为空),
|
|
496
|
+
* 绝不漏取:摘要字段不是串 ⇒ core 的摘要既无摘要也无清单;成因词键不在 ⇒ 无成因词 —— 两者都不满足的行,摘要里这三位恒空。普通卡(绝大多数行)因此零额外读。
|
|
497
|
+
* 只读键在不在 / 是不是串,不读值的内容,所以不需要 {@link readClosedWordSeat} 那样的计数。
|
|
498
|
+
*/
|
|
499
|
+
export declare function parkRowCarriesSkillCardFacts(stored: unknown): boolean;
|
|
474
500
|
/** [ref](core 7.4.0 [ref])—— 回落卡的计数与窗。`limit` 是**闭二词**(core d.ts 逐字),两个计数是
|
|
475
501
|
* 非负整数,`autoDenyAfterMs` 是 core 契约里的 `0..2147483647` 非负整数(`0` = 本卡不武装窗:部署把
|
|
476
502
|
* 旋钮关了,或 TOTAL 档的卡按 [ref]① 恒 0 等人)。四成员**全必填**——core 的 `DenialLimitFallback` 上
|
|
@@ -524,6 +550,18 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
524
550
|
}, z.core.$strict>;
|
|
525
551
|
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
526
552
|
rememberRefused: z.ZodOptional<z.ZodLiteral<true>>;
|
|
553
|
+
approvalCard: z.ZodOptional<z.ZodEnum<{
|
|
554
|
+
question: "question";
|
|
555
|
+
plan: "plan";
|
|
556
|
+
skill: "skill";
|
|
557
|
+
shell: "shell";
|
|
558
|
+
workflow: "workflow";
|
|
559
|
+
"file-edit": "file-edit";
|
|
560
|
+
"file-write": "file-write";
|
|
561
|
+
"notebook-edit": "notebook-edit";
|
|
562
|
+
fetch: "fetch";
|
|
563
|
+
generic: "generic";
|
|
564
|
+
}>>;
|
|
527
565
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
528
566
|
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
529
567
|
member: z.ZodEnum<{
|
|
@@ -664,6 +702,18 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
664
702
|
}, z.core.$strict>;
|
|
665
703
|
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
666
704
|
rememberRefused: z.ZodOptional<z.ZodLiteral<true>>;
|
|
705
|
+
approvalCard: z.ZodOptional<z.ZodEnum<{
|
|
706
|
+
question: "question";
|
|
707
|
+
plan: "plan";
|
|
708
|
+
skill: "skill";
|
|
709
|
+
shell: "shell";
|
|
710
|
+
workflow: "workflow";
|
|
711
|
+
"file-edit": "file-edit";
|
|
712
|
+
"file-write": "file-write";
|
|
713
|
+
"notebook-edit": "notebook-edit";
|
|
714
|
+
fetch: "fetch";
|
|
715
|
+
generic: "generic";
|
|
716
|
+
}>>;
|
|
667
717
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
668
718
|
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
669
719
|
member: z.ZodEnum<{
|
|
@@ -806,6 +856,9 @@ export interface ApprovalCardSource {
|
|
|
806
856
|
governanceForced?: true;
|
|
807
857
|
/** 7.116.0 S4:「本会话放行」不会被记住(帧上同名键,见 `ApprovalCardSchema.rememberRefused`)。 */
|
|
808
858
|
rememberRefused?: true;
|
|
859
|
+
/** 7.136.0:引擎铸的卡词 —— 与 wire 帧**同一份素材**(发帧点经 `readApprovalCard` 读定的那一格)⇒ 活卡帧 / `approval_request` 的卡 / `card_json` /
|
|
860
|
+
* 重放帧四面同源(见 `ApprovalCardSchema.approvalCard`)。 */
|
|
861
|
+
approvalCard?: ToolApprovalCard;
|
|
809
862
|
/** E-14:bidi 在场位 —— 与 wire 帧**同一份素材**(`ToolApprovalFrame` 结构上满足本接口)⇒ live 帧 /
|
|
810
863
|
* `approval_request` / `card_json` / 重放帧四面同源,不是四处各扫一遍。 */
|
|
811
864
|
inputHasBidi?: true;
|
|
@@ -880,7 +933,7 @@ export declare function buildApprovalRequestFrame(input: {
|
|
|
880
933
|
/**
|
|
881
934
|
* 🔴 **呈卡帧的活面事实座**(呈卡腿上旧族退役之后,开卡帧的全部事实都由本族承载)。
|
|
882
935
|
*
|
|
883
|
-
* 这十个座是活卡帧 `tool_approval` 上有、而卡(`ApprovalCardSchema`,`card_json` 的耐久信封)
|
|
936
|
+
* 这十个座是活卡帧 `tool_approval` 上有、而卡(`ApprovalCardSchema`,`card_json` 的耐久信封)上**没有**的那几位(`approvalCard` 7.136.0 起也在卡上,见本注末段):
|
|
884
937
|
* 卡外的事实此前只骑活卡帧,呈卡腿的消费方靠「两帧按 approvalId 合读」拿到它们。呈卡腿自此只发本族帧 ⇒ 它们挪到**活铸**的
|
|
885
938
|
* 呈卡帧顶层,同名同型同值 —— 值取自同一只帧对象(活卡帧的那一次铸定,`tool-approval.ts` 的 `askBroadcast`),本仓不另算一遍。
|
|
886
939
|
*
|
|
@@ -889,6 +942,9 @@ export declare function buildApprovalRequestFrame(input: {
|
|
|
889
942
|
* (`readRootCandidate` 有不进卡的硬理由:耐久面兑现不了它,见活卡帧该座顶注)。
|
|
890
943
|
*
|
|
891
944
|
* `requiresRealApproval` 不在表里:卡上 `risk.requiresRealApproval` 是同一个事实(铸卡第三参就是它),一个事实一个读法。
|
|
945
|
+
* ⚠️ **`approvalCard` 是表里唯一一位 7.136.0 起同时在卡上的座**(`ApprovalCardSchema.approvalCard`:断连重放的卡要渲回同一张专卡,卡词必须进卡;
|
|
946
|
+
* 同一次读定、同值)。按上一句的规矩它该离表(活铸帧顶层这一份退役 —— wire BREAKING,下游 sdk 的孪生表同改),本版按 additive 交付、未退役:
|
|
947
|
+
* 一个事实两处读,登记为待收的债,收法 = 离表 + 消费方改读 `card.approvalCard`,不是在这里加第三处。
|
|
892
948
|
*/
|
|
893
949
|
export declare const APPROVAL_REQUEST_LIVE_FACT_KEYS: readonly ["mandated", "mandate", "origin", "boundGrantDigest", "boundGrants", "classifierNoRuling", "boundGrantAbsence", "approvalCard", "readRootCandidate", "probeReason"];
|
|
894
950
|
/** 呈卡帧活面事实座的类型面 = 活卡帧同名座的 `Pick`(同型由构造保证,不手抄)。 */
|
|
@@ -1015,7 +1071,7 @@ export declare function buildRevokeFrame(batchId: string, askIds: readonly strin
|
|
|
1015
1071
|
* 过窗未收敛的行(`expiresInMs === 0`)**仍然重放**,不在这里代打 `expireAsk` CAS(那会制造第五个
|
|
1016
1072
|
* 竞争者;收敛是车2 的窗到期竞争者与车5 恢复扫描的职责)。
|
|
1017
1073
|
*/
|
|
1018
|
-
export declare function buildReplayFrame(row: AskRow, nowMs: number): ApprovalRequestFrame | undefined;
|
|
1074
|
+
export declare function buildReplayFrame(row: Pick<AskRow, "askId" | "taskId" | "expiresAtMs" | "cardJson">, nowMs: number): ApprovalRequestFrame | undefined;
|
|
1019
1075
|
/**
|
|
1020
1076
|
* [ref] —— 持久行上那张卡**要不要人**:卡面 `risk.requiresRealApproval`(铸卡时取自引擎在 `AskRequest` 上盖的同一位,
|
|
1021
1077
|
* {@link buildApprovalCard} 第三参)。读者 = 耐久 ask 回决口在裁决之前交给引擎判官 `policyCannotAnswer` 的那一问(`host-decision.ts` 的 `heldAskOf`)。
|
package/dist/approval-card.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, BOUND_GRANT_ABSENCES, DENIAL_LIMIT_BOUNDS, MAX_RULE_TEXT_CHARS, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, READ_ROOT_CANDIDATE_DIR_MAX, RESUME_STARTUP_CAUSES, RULE_OFFERS_ABSENCES, RULE_OFFER_KINDS, STEER_REPLAY_FACETS, isAskClass, readRuleOffers, isAskOrigin, isAutoModeFallbackCause, isMintableProbeReason, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
2
|
+
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, TOOL_APPROVAL_CARDS, BOUND_GRANT_ABSENCES, DENIAL_LIMIT_BOUNDS, MAX_RULE_TEXT_CHARS, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, READ_ROOT_CANDIDATE_DIR_MAX, RESUME_STARTUP_CAUSES, RULE_OFFERS_ABSENCES, RULE_OFFER_KINDS, STEER_REPLAY_FACETS, isAskClass, readRuleOffers, isAskOrigin, isAutoModeFallbackCause, isMintableProbeReason, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
|
+
import { readApprovalCard } from "./approval-content-kind.js";
|
|
5
6
|
import { capText } from "./caller-text-echo.js";
|
|
6
7
|
import { retireParkArmWords, retireStoredCardWords } from "./retired-card-words.js";
|
|
7
8
|
export const MAX_AGENT_NAME = 200;
|
|
@@ -345,6 +346,19 @@ export function parkRowFacts(stored) {
|
|
|
345
346
|
...(boundGrantAbsence !== undefined ? { boundGrantAbsence } : {}),
|
|
346
347
|
};
|
|
347
348
|
}
|
|
349
|
+
export function skillCardFacts(summary) {
|
|
350
|
+
return {
|
|
351
|
+
...(summary.boundGrantDigest !== undefined ? { boundGrantDigest: summary.boundGrantDigest } : {}),
|
|
352
|
+
...(summary.boundGrants !== undefined ? { boundGrants: summary.boundGrants } : {}),
|
|
353
|
+
...(summary.classifierNoRuling !== undefined ? { classifierNoRuling: summary.classifierNoRuling } : {}),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
export function parkRowCarriesSkillCardFacts(stored) {
|
|
357
|
+
if (typeof stored !== "object" || stored === null || !("kind" in stored) || stored.kind !== "tool_approval")
|
|
358
|
+
return false;
|
|
359
|
+
const digest = "boundGrantDigest" in stored ? stored.boundGrantDigest : undefined;
|
|
360
|
+
return typeof digest === "string" || ("classifierNoRuling" in stored && stored.classifierNoRuling !== undefined);
|
|
361
|
+
}
|
|
348
362
|
export const DenialLimitFallbackSchema = z
|
|
349
363
|
.object({
|
|
350
364
|
consecutive: z.number().int().nonnegative(),
|
|
@@ -377,6 +391,7 @@ export const ApprovalCardSchema = z
|
|
|
377
391
|
.strict(),
|
|
378
392
|
governanceForced: z.literal(true).optional(),
|
|
379
393
|
rememberRefused: z.literal(true).optional(),
|
|
394
|
+
approvalCard: z.enum(TOOL_APPROVAL_CARDS).optional(),
|
|
380
395
|
inputHasBidi: z.literal(true).optional(),
|
|
381
396
|
allowRuleShadowed: z
|
|
382
397
|
.array(z
|
|
@@ -444,6 +459,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
444
459
|
const sourceTaskId = capText(source.sourceTaskId, MAX_IDENT);
|
|
445
460
|
const sourceAgentName = capText(source.sourceAgentName, MAX_AGENT_NAME);
|
|
446
461
|
const allowRuleShadowed = readAllowRuleShadowed(req);
|
|
462
|
+
const approvalCard = readApprovalCard(source.approvalCard);
|
|
447
463
|
return {
|
|
448
464
|
toolName: capText(source.toolName, MAX_IDENT) ?? "",
|
|
449
465
|
message: capText(source.message, MAX_MESSAGE) ?? "",
|
|
@@ -456,6 +472,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
456
472
|
},
|
|
457
473
|
...(source.governanceForced === true ? { governanceForced: true } : {}),
|
|
458
474
|
...(source.rememberRefused === true ? { rememberRefused: true } : {}),
|
|
475
|
+
...(approvalCard !== undefined ? { approvalCard } : {}),
|
|
459
476
|
...(source.inputHasBidi === true ? { inputHasBidi: true } : {}),
|
|
460
477
|
...(allowRuleShadowed !== undefined ? { allowRuleShadowed } : {}),
|
|
461
478
|
...(probeCause !== undefined ? { probeCause } : {}),
|
|
@@ -19,6 +19,12 @@
|
|
|
19
19
|
* 不是卡的渲染读法,不在本模块的规则里。
|
|
20
20
|
*/
|
|
21
21
|
export { ASK_USER_QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
22
|
+
/**
|
|
23
|
+
* core 7.56.0 [ref]:停驻行是不是**引擎自己的问题工具**的(content-ask)—— 读**行的记录**:引擎铸的挂载点 `pendingAction.engineMounted: true` 与保留名同在。
|
|
24
|
+
* 调用方自己挂的同名工具不带挂载点(引擎只给引擎挂载的工具铸这一位),本版之前停驻的行也没有 ⇒ 都不是。读者:两只决定铸点(顶层 `/decide` 与停驻子代赎回腿)——
|
|
25
|
+
* 引擎问题工具的决定**不转** `updatedInput`(解析器忽略它,而认领前的编辑复判会在问题专用的 resume-edit 策略下把它拒成 `resume.constraint_rejected`)。
|
|
26
|
+
*/
|
|
27
|
+
export declare function isEngineQuestionRow(pendingAction: unknown): boolean;
|
|
22
28
|
import { PAUSE_REGISTRY, type GateKind, type ToolApprovalCard } from "@sema-agent/core";
|
|
23
29
|
/**
|
|
24
30
|
* 一格卡词的**闭集窄读**(三个载体共用这一只:活 ask、LOCAL 店的停驻门、SQL 店读出的门子树)。成员 ⇒ 那个词;缺席 ⇒ `undefined`(不计);
|
|
@@ -48,6 +54,7 @@ export declare function parkedFacetOf(gate: ParkedGateFace | undefined): "A" | "
|
|
|
48
54
|
* 审批门型 = core `PAUSE_REGISTRY` 里寄存臂是 `tool_approval` 的登记门 —— 与 core `parkedApprovalCardOf` 判「是不是审批门」同一条判据(只在这些门上读卡)。
|
|
49
55
|
* **派生,不手写**(core 7.55.0 [ref] P1 阶段二):此前本仓手写一份二员词表([ref] 时 core 没有导出词表),一次性迁移 M21 又自派生一份;
|
|
50
56
|
* core 加一行审批味的门,这里跟着变,零处要改。读者:决断口与拒扫腿按门型绑定取行(`gateKinds`)、`/decide` 的门型守卫、M21 / M29 的候选扫描。
|
|
57
|
+
* 包根同出(`index.ts`):嵌入方 / sdk 读这一只,不手抄。
|
|
51
58
|
* 到期分法**不**读这张表 —— SQL 里是由 core 两张表生成的 facet 谓词(`plugins/checkpoint-index-columns.ts`),JS 里是 {@link denySweepOwns}。
|
|
52
59
|
*/
|
|
53
60
|
export type ApprovalGateKind = {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export { ASK_USER_QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
2
|
+
import { ASK_USER_QUESTION_TOOL_NAME as QUESTION_TOOL_NAME } from "@sema-agent/core";
|
|
3
|
+
export function isEngineQuestionRow(pendingAction) {
|
|
4
|
+
if (pendingAction === null || typeof pendingAction !== "object")
|
|
5
|
+
return false;
|
|
6
|
+
const pa = pendingAction;
|
|
7
|
+
return pa.kind === "tool_approval" && pa.engineMounted === true && pa.toolName === QUESTION_TOOL_NAME;
|
|
8
|
+
}
|
|
2
9
|
import { PAUSE_REGISTRY, TOOL_APPROVAL_CARDS, facetOf, isGateKind, parkedApprovalCardOf } from "@sema-agent/core";
|
|
3
10
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
4
11
|
import { redactSecrets } from "./trace/redact.js";
|
|
@@ -33,7 +40,7 @@ export function parkedApprovalCard(gate) {
|
|
|
33
40
|
export function parkedFacetOf(gate) {
|
|
34
41
|
return gate !== undefined && isGateKind(gate.kind) ? facetOf({ kind: gate.kind, approvalCard: gate.approvalCard }) : undefined;
|
|
35
42
|
}
|
|
36
|
-
export const APPROVAL_GATE_KINDS = Object.keys(PAUSE_REGISTRY).filter((k) => PAUSE_REGISTRY[k].pending === "tool_approval");
|
|
43
|
+
export const APPROVAL_GATE_KINDS = Object.freeze(Object.keys(PAUSE_REGISTRY).filter((k) => PAUSE_REGISTRY[k].pending === "tool_approval"));
|
|
37
44
|
export function isApprovalGateKind(k) {
|
|
38
45
|
return k !== undefined && APPROVAL_GATE_KINDS.includes(k);
|
|
39
46
|
}
|
|
@@ -8,10 +8,26 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ## 判官
|
|
10
10
|
* 问题:**这把 gate token 是不是该会话当前待决的审批门?**(应答口的答法:`resumeCheckpoint` 的 stale 臂 —— 回显的 token ≠ 该会话当前待决的审批门 ⇒ `409 approval_stale`。)
|
|
11
|
+
* · {@link sightsCurrentGate} —— 「决断方回显的**门身份**(token / `checkpointId`)是不是当前门的同一面」的**唯一**比较(`approval_stale` 律,两道决断门一条,见下一节);
|
|
12
|
+
* 本仓每一处拿**决断方 / 读口给的那把**门身份比「是不是当前门」都经它(`/decide` 的 token 回显、计划审阅的身份回显、下面的 {@link readGateCurrency});
|
|
13
|
+
* 服务端对**自己选出的那张卡**的复核不是回显比较,不经它(`drivePlanReview` 的卡令牌复核答认领输家 `409 conflict.not_resumable`、唤醒腿的赢家比较);
|
|
14
|
+
* 射程:本判官问的是「**哪扇门**」;`/decide` 的 [ref] 第二源问的是「**哪只调用**」(回显的 `boundCallId` vs 载入行的 `toolCallId`)—— 两问不同,所以是两条比较,
|
|
15
|
+
* 不是「不能并」:它们共用同一个 409 答复,缺值规则各按各的问题定(说不出门身份 ⇒ 判「不是」;行上没有 `toolCallId` ⇒ 那一问不判,交引擎的绑定等值校验 fail-closed 兜)。
|
|
16
|
+
* 过期决断的 409 一族(门身份 / 调用绑定两问怎么排成一族、老行那一形答哪个码)是 7.138.0 的设计票(DEBTS-server,主会话开票);
|
|
11
17
|
* · {@link currentApprovalGateToken} —— 该会话当前待决的审批门(审批门型优先,没有再退到任一待决行);应答口读它,读口读它,别处不再内联第二份;
|
|
12
18
|
* · {@link readGateCurrency} —— 一把 token 对着它的读数:`current`(带那一行)/ `absent`(店里没有这一行)/ `settled`(行在,但不再是当前待决门:已结算、已过期、被取代);
|
|
13
19
|
* · {@link createParkedGateRead} —— 协调器读口的门读席:把读数译成读口要的四种读法({@link ParkedGateReading}),`state` 闭集不加字。
|
|
14
20
|
*
|
|
21
|
+
* ## `approval_stale` 律(两道决断门一条;[ref] 把计划审阅收进来)
|
|
22
|
+
* 一句话:**决断体带了「决断方看到的那道门」,而它不是该会话此刻在这道门上待决的那道门 ⇒ `409 approval_stale`,不落决断,拒体带当前门的回显坐标;不带 ⇒ 照旧受理。**
|
|
23
|
+
* 门的身份有两个面,**同一次铸**在同一行上(core 的每个停驻铸点把 `token` 与 `checkpointId` 一起铸;重开翻的是同一行、两面都不变)⇒ 在带身份的行上两面一一对应:
|
|
24
|
+
* · 能力面 `checkpointToken` —— 秘密,永不外发;`/decide` 的老回显键(直连门上拒收);
|
|
25
|
+
* · 身份面 `checkpointId` —— 非秘密(park 结果的 `terminal.checkpointId`、运维队列 / 收件行上的那一个);计划审阅能回显的只有它。
|
|
26
|
+
* 读法只有 `checkpoint-identity.ts` 的信任边界那一只:core 5.42.0 之前铸的老行没有它、没过边界的值不算它 ⇒ 这样的门**说不出**自己的身份面。
|
|
27
|
+
* 判官 {@link sightsCurrentGate} 只做一件事:回显的那一面 = 当前门的同一面。当前门没有那一面(老行 / 不可信值 / 调用方没读行)⇒ 不是(fail-closed:拒,不放)。
|
|
28
|
+
* 「当前门」由每道门按**自己的门型**选(`/decide` = {@link currentApprovalGateToken};计划审阅 = 它的选卡读),判官门型无关。
|
|
29
|
+
* 不把能力面先解成身份面再比:两面在同一行上同生同灭,比能力面就是比这一行;先解一次只会在快路上多一次店读,并在老行上(两边都读不出身份面)把两道不同的门判成同一道。
|
|
30
|
+
*
|
|
15
31
|
* ## 一个读数只取自一个时刻([ref])
|
|
16
32
|
* 门身份就是 token:引擎重开一张门(`reopen`:续跑腿在被批准的动作开跑之前失败)翻的是**同一行**(resolved → pending,token 不变)。所以「这张卡此刻在哪」可以在两次店读之间
|
|
17
33
|
* 来回变(待决 → 被决 → 被重开);判官的分类只用**同一时刻成立**的读数:
|
|
@@ -33,8 +49,10 @@
|
|
|
33
49
|
* 表明说 `decided` ⇒ 往下读;明说 `void` ⇒ `void`(没有人决过它,答案无处兑现 —— 已过期〔取消 / 收割〕、仍待决却不是当前门〔被取代,应答口答 stale〕、
|
|
34
50
|
* 结算种类是引擎窗到期 / 宿主 SLA 扫 / 无人可答的失败族);**表外 ⇒ `unknown`**(读不懂的词撑不起「有人决过」,也撑不起「没有人决过它」);
|
|
35
51
|
* ② 结算赢家的门记录 `resolvedOutcome.gateOutcome` 先过 core 唯一的门记录筛 `screenGateOutcome`(**档案读法**:读回的耐久行,闭集 = 今日成员 ∪ 已退役词;
|
|
36
|
-
* 坏形记缺陷、绝不抛;本仓 `trace/project.ts` 投影同一种记录也只经这一只筛),有缺陷 ⇒ `unknown
|
|
37
|
-
*
|
|
52
|
+
* 坏形记缺陷、绝不抛;本仓 `trace/project.ts` 投影同一种记录也只经这一只筛),有缺陷 ⇒ `unknown`,过了筛才读它的成员。core 7.56.0 [ref] 起筛自己判
|
|
53
|
+
* I10(`settlement.when` 是有限数)与 I11(调用方把赢家的 `decision` 一并交给筛:它是赢家拿得出的词,方向与记录的结算种类 / 处置同向)—— 本判官在同一次筛调用里
|
|
54
|
+
* 交 `winnerDecision`,时刻与方向的手判随之退役;
|
|
55
|
+
* ③ 分类之前记录要**读得全**:有结算记录、赢家绑的是这一行的调用且判词是 allow / deny(与
|
|
38
56
|
* `findDecidedApprovalsForBinding` 同一只 {@link approvalDecisionOfWinner})—— 缺一个都是 `unknown`(**不编一个首决**;读口答 404,应答口对已结算的门答 409 `approval_stale`,两口不同判;
|
|
39
57
|
* `void` 也要读得全:绑在别的调用上的结算说的不是这张卡);读全了再按**结算种类**表分类:人 / 策略的四个种类 ⇒ `decided`(理由取自赢家的 `reason`),其余十个 ⇒ `void`
|
|
40
58
|
* (按种类不按当事方:跨版本稳定的是种类词,变过义的是当事方词 —— 此前版本的策略拒记 `policy_refused` + `who.party:"none"`,此后记 `"policy"`,按当事方查会把那一代合法历史记录读成 `void`);
|
|
@@ -42,14 +60,38 @@
|
|
|
42
60
|
* `unknown` 的这一类(`decision_unreadable`)带上读不出的那几处(缺陷句,非空):读口答 404 `tool_approval.not_pending`,并记一行 error(首句 + 计数)。
|
|
43
61
|
* 行没了 ⇒ `unknown`(`row_absent`;应答口 PARKED 臂的 404 `not_found.approval` 同判)。
|
|
44
62
|
*
|
|
45
|
-
* ## 盲区(core
|
|
46
|
-
* ·
|
|
47
|
-
*
|
|
48
|
-
* 只有坏行 / 手改行才走得到这两处。候引擎的筛把时刻、方向与种类 × 当事方配对一并判了,两个读者同时退掉各自的手判(配对今天没有手判可退 —— 它无人判)。
|
|
63
|
+
* ## 盲区(core 的筛仍不判的一处,如实写下)
|
|
64
|
+
* · 种类 × 当事方的配对(结算种类与 `settlement.who` 的当事方是否相配)—— 筛不判(core KL-1265-b),本判官也不核:读的是种类。只有坏行 / 手改行才走得到。
|
|
65
|
+
* (时刻与方向两处自 core 7.56.0 [ref] 起由筛判:I10 / I11。trace 投影读同一种记录、经同一只筛,零改即得 I10 —— 坏时刻在两个读者那里是同一个答法。)
|
|
49
66
|
*/
|
|
50
67
|
import { type Checkpoint, type CheckpointToken, type SettlementKind } from "@sema-agent/core";
|
|
51
68
|
import { type PendingRowBinding } from "./plugins/checkpoint-store-sql.js";
|
|
52
69
|
import { type ParkedGateRead } from "./tool-approval.js";
|
|
70
|
+
/**
|
|
71
|
+
* 决断方回显的「我看到的那道门」—— 门的两个身份面之一(两面的来历与读法见模块头注「`approval_stale` 律」)。
|
|
72
|
+
* `face` 就是回显它的那个体键名:`/decide` 的 `checkpointToken`,计划审阅的 `checkpointId`。
|
|
73
|
+
*/
|
|
74
|
+
export type GateSighting = {
|
|
75
|
+
readonly face: "checkpointToken" | "checkpointId";
|
|
76
|
+
readonly value: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* 当前门的身份面。能力面恒在;身份面在场 ⇔ 行上的 `checkpointId` 过了信任边界 —— 老行 / 没过边界 / 调用方没读行时缺席,
|
|
80
|
+
* 缺席读作「说不出它的身份面」(判官据此对身份面回显答「不是」),**不**读作「没有门」。
|
|
81
|
+
*/
|
|
82
|
+
export interface CurrentGateFaces {
|
|
83
|
+
readonly checkpointToken: string;
|
|
84
|
+
readonly checkpointId?: string;
|
|
85
|
+
}
|
|
86
|
+
/** 一道已载入的当前门 → 它的两个身份面(身份面只经 {@link trustedCheckpointId};它含 token 的形在这里就被当成缺席,永不上比较、永不上 wire)。 */
|
|
87
|
+
export declare function currentGateFacesOf(token: string, row: {
|
|
88
|
+
readonly checkpointId?: unknown;
|
|
89
|
+
}): CurrentGateFaces;
|
|
90
|
+
/**
|
|
91
|
+
* `approval_stale` 律的**唯一**判官:回显的那一面是不是当前门的同一面(律全文见模块头注)。没有当前门(`null` / `undefined`)⇒ 不是;
|
|
92
|
+
* 当前门说不出回显的那一面 ⇒ 不是(fail-closed)。调用点:`/decide` 的 token 回显、计划审阅的身份回显、本模块的 {@link readGateCurrency}。
|
|
93
|
+
*/
|
|
94
|
+
export declare function sightsCurrentGate(sighting: GateSighting, current: CurrentGateFaces | null | undefined): boolean;
|
|
53
95
|
/** 判官要的两个店口(三后端同形;窄接口让测试与装配都不必拿整只店)。 */
|
|
54
96
|
export interface GateCurrencyStore {
|
|
55
97
|
get(token: CheckpointToken): Promise<Checkpoint | null>;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { REOPEN_REASONS, screenGateOutcome } from "@sema-agent/core";
|
|
1
|
+
import { REOPEN_REASONS, checkpointRevOf, screenGateOutcome } from "@sema-agent/core";
|
|
2
2
|
import { approvalDecisionOfWinner } from "./plugins/checkpoint-store-sql.js";
|
|
3
3
|
import {} from "./tool-approval.js";
|
|
4
4
|
import { APPROVAL_GATE_KINDS } from "./approval-content-kind.js";
|
|
5
|
+
import { trustedCheckpointId } from "./checkpoint-identity.js";
|
|
6
|
+
export function currentGateFacesOf(token, row) {
|
|
7
|
+
const checkpointId = trustedCheckpointId(row.checkpointId, token);
|
|
8
|
+
return checkpointId === undefined ? { checkpointToken: token } : { checkpointToken: token, checkpointId };
|
|
9
|
+
}
|
|
10
|
+
export function sightsCurrentGate(sighting, current) {
|
|
11
|
+
if (current === null || current === undefined)
|
|
12
|
+
return false;
|
|
13
|
+
const face = current[sighting.face];
|
|
14
|
+
return face !== undefined && face === sighting.value;
|
|
15
|
+
}
|
|
5
16
|
export async function currentApprovalGateToken(cs, sessionId) {
|
|
6
17
|
return (await cs.findPendingTokenBySession(sessionId, undefined, { gateKinds: APPROVAL_GATE_KINDS })) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
7
18
|
}
|
|
8
|
-
function revisionOf(cp) {
|
|
9
|
-
return cp.rev ?? 0;
|
|
10
|
-
}
|
|
11
19
|
const GATE_READ_ROUNDS = 3;
|
|
12
20
|
export async function readGateCurrency(cs, gateToken) {
|
|
13
21
|
const token = gateToken;
|
|
@@ -17,10 +25,11 @@ export async function readGateCurrency(cs, gateToken) {
|
|
|
17
25
|
return { kind: "absent" };
|
|
18
26
|
if (row.status !== "pending")
|
|
19
27
|
return { kind: "settled", checkpoint: row };
|
|
20
|
-
const
|
|
28
|
+
const currentToken = await currentApprovalGateToken(cs, row.sessionId);
|
|
29
|
+
const isCurrent = sightsCurrentGate({ face: "checkpointToken", value: gateToken }, currentToken ? { checkpointToken: currentToken } : null);
|
|
21
30
|
const again = await cs.get(token);
|
|
22
31
|
if (again !== null && again.status === "pending") {
|
|
23
|
-
if (
|
|
32
|
+
if (checkpointRevOf(again) === checkpointRevOf(row))
|
|
24
33
|
return isCurrent ? { kind: "current", checkpoint: again } : { kind: "settled", checkpoint: again };
|
|
25
34
|
if (round === GATE_READ_ROUNDS)
|
|
26
35
|
throw new Error(`the checkpoint row moved between reads in ${GATE_READ_ROUNDS} rounds in a row (decided and reopened each time): there is no single-instant reading of its gate`);
|
|
@@ -68,14 +77,12 @@ function decisionReading(checkpoint, gateBoundCallId) {
|
|
|
68
77
|
function readWinner(checkpoint, gateBoundCallId) {
|
|
69
78
|
const winner = checkpoint.resolvedOutcome;
|
|
70
79
|
const recorded = winner?.gateOutcome;
|
|
71
|
-
const [defect, ...more] = screenGateOutcome(recorded, { reading: "archive" });
|
|
80
|
+
const [defect, ...more] = screenGateOutcome(recorded, { reading: "archive", ...(winner !== undefined ? { winnerDecision: winner.decision } : {}) });
|
|
72
81
|
if (defect !== undefined)
|
|
73
82
|
return unreadable([defect, ...more]);
|
|
74
83
|
const settlement = recorded?.settlement;
|
|
75
84
|
if (settlement === undefined)
|
|
76
85
|
return unreadable(["the gate record carries no settlement: no decider and no time"]);
|
|
77
|
-
if (!Number.isFinite(settlement.when))
|
|
78
|
-
return unreadable(["settlement.when is not a finite epoch-ms time"]);
|
|
79
86
|
const boundCallId = gateBoundCallId ?? (checkpoint.pendingAction.kind === "tool_approval" ? checkpoint.pendingAction.toolCallId : "");
|
|
80
87
|
const direction = approvalDecisionOfWinner(winner, boundCallId);
|
|
81
88
|
if (direction === null)
|
package/dist/approval-hmac.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ import type { ApprovalHmacKey } from "./auth-keys.js";
|
|
|
20
20
|
* `reason`,以及 [ref] 起的 `permissionModeAfter`)。⇒ 「同一张卡上把 approve 的档偷偷调宽」这件事,
|
|
21
21
|
* 结构上只有 HMAC 这一把拦得住;往 `cnf.bnd` 里加位既不解决它,还会把一份合法 JWT 绑死在一个它签发时
|
|
22
22
|
* 根本不知道的值上。**本次([ref] F1)只动 HMAC 这一把,`approvalBnd` 一个字节不动。**
|
|
23
|
+
* [ref] 同一判断:`plan_review` 的门身份 `checkpointId` 进 HMAC 的扩展对象,不进 `cnf.bnd` —— 计划审阅的 `cnf.bnd` 绑的是会话(它的第二位是一次性 nonce),
|
|
24
|
+
* 签发 JWT 的一方不知道此刻是哪道计划门;知道的是作决断的那一方,而 HMAC 正是它签的。
|
|
23
25
|
*
|
|
24
26
|
* ── 🔴 字节构造规则(权威定义;跨仓签名器照此实现,docs/ASSISTANT-WIRE-CONTRACT.md §6.2 同源)──────────
|
|
25
27
|
* 被 HMAC 的是下面这个字符串的 **UTF-8 字节**:
|
|
@@ -81,7 +83,7 @@ export declare function questionAnswerFromProjection(p: QuestionAnswerProjection
|
|
|
81
83
|
* 下面那两条编译期门当场红。**不许有第三种在场判**:恒 `!== undefined`(不是真值判、也不是 `?? null`
|
|
82
84
|
* 折叠)—— `null` 与缺席在扩展位上是两个不同的被签值(「签名方明说这次不动」≠「签名方不知道有这个键」)。
|
|
83
85
|
*/
|
|
84
|
-
export declare const APPROVAL_ENVELOPE_EXT_KEYS: readonly ["answer", "permissionModeAfter"];
|
|
86
|
+
export declare const APPROVAL_ENVELOPE_EXT_KEYS: readonly ["answer", "checkpointId", "permissionModeAfter"];
|
|
85
87
|
export type ApprovalEnvelopeExtKey = (typeof APPROVAL_ENVELOPE_EXT_KEYS)[number];
|
|
86
88
|
/** The canonical envelope both {@link approvalHmacMessage} (the signer's/verifier's shared byte-rule) and
|
|
87
89
|
* {@link verifyApprovalHmac} take — ONE named type so the two signatures cannot silently drift into two
|
|
@@ -105,6 +107,16 @@ export interface ApprovalEnvelope {
|
|
|
105
107
|
* 值是 {@link projectQuestionAnswer} 的产物,不是请求体上那个对象(理由见该函数)。
|
|
106
108
|
*/
|
|
107
109
|
answer?: QuestionAnswerProjection;
|
|
110
|
+
/**
|
|
111
|
+
* 🔴 **[ref](7.137.0)—— `plan_review` 决断方看到的那道门(`checkpointId`)进签名载荷。**
|
|
112
|
+
*
|
|
113
|
+
* 计划审阅的证明只绑会话(`cnf.bnd` = 会话 + 一次性 nonce + 内容哈希),不绑门 ⇒ 决断体上的门身份是这次决断**落在哪道门**的唯一陈述
|
|
114
|
+
* (不是当前门 ⇒ `409 approval_stale`)。不签它,能改写线上字节的一方就能把它摘掉(陈旧保护整个不在)或换掉;拒收它则把这项保护在直连门部署上整个关掉。
|
|
115
|
+
* 判据扩成一句:**凡决定这次决断落在哪道门、续跑能做什么 / 落什么账的决裁字段都在签名信封里**。
|
|
116
|
+
* 与 `permissionModeAfter` 同形:在场才进扩展对象,缺席 ⇒ 字节与不认识它的签名器逐字相同(既有签名器零改动);带了不签 ⇒ `401 approval_mac_invalid`。
|
|
117
|
+
* `/decide` 不送它(那道门的门身份是 `boundCallId` / `boundInputHash`,已在前五位)。
|
|
118
|
+
*/
|
|
119
|
+
checkpointId?: string;
|
|
108
120
|
/**
|
|
109
121
|
* 🔴 **[ref] 合并复审 F1 [high]**(7.86.0 立,[ref] 把它从裸尾位挪进扩展对象)—— `plan_review` 的
|
|
110
122
|
* `permissionModeAfter` 选的是**批准之后这条 run 有没有手**(`acceptEdits` = 工作目录内的写不再逐次
|
package/dist/approval-hmac.js
CHANGED
|
@@ -5,7 +5,7 @@ export function projectQuestionAnswer(a) {
|
|
|
5
5
|
export function questionAnswerFromProjection(p) {
|
|
6
6
|
return { answers: p.map(([header, selected, note]) => ({ header, selected: [...selected], ...(note === null ? {} : { note }) })) };
|
|
7
7
|
}
|
|
8
|
-
export const APPROVAL_ENVELOPE_EXT_KEYS = ["answer", "permissionModeAfter"];
|
|
8
|
+
export const APPROVAL_ENVELOPE_EXT_KEYS = ["answer", "checkpointId", "permissionModeAfter"];
|
|
9
9
|
const APPROVAL_ENVELOPE_HEAD_KEYS = ["sessionId", "boundCallId", "boundInputHash", "decision", "reason"];
|
|
10
10
|
export function approvalHmacMessage(env) {
|
|
11
11
|
const head = [env.sessionId, env.boundCallId ?? null, env.boundInputHash ?? null, env.decision, env.reason ?? null];
|
package/dist/approval.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createApprovalPolicy,
|
|
1
|
+
import { createApprovalPolicy, isCoveringRuleName, ruleNameCovers } from "@sema-agent/core";
|
|
2
2
|
import { operatorAlwaysHumanAsk, operatorAsk, requestAlwaysHumanAsk } from "./operator-ask.js";
|
|
3
3
|
import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
|
|
4
4
|
import { isExplicitPosture } from "./posture-source.js";
|
|
@@ -116,10 +116,10 @@ export function createDeploymentListExemption(store, sessionId, logger, leg) {
|
|
|
116
116
|
}
|
|
117
117
|
function createNameSetMatcher(names) {
|
|
118
118
|
const exact = new Set(names);
|
|
119
|
-
const covering = names.filter((name) =>
|
|
119
|
+
const covering = names.filter((name) => isCoveringRuleName(name, NAME_TABLE_BEHAVIOR));
|
|
120
120
|
if (covering.length === 0)
|
|
121
121
|
return (toolName) => exact.has(toolName);
|
|
122
|
-
return (toolName) => exact.has(toolName) || covering.some((rule) =>
|
|
122
|
+
return (toolName) => exact.has(toolName) || covering.some((rule) => ruleNameCovers(rule, toolName));
|
|
123
123
|
}
|
|
124
124
|
export const AUTO_APPROVE_BUDGET_APPROVER = "auto_approve_budget";
|
|
125
125
|
function budgetSeatAnswer(req, requireHit, signal) {
|
package/dist/auth-bridge.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AuthBridgeErrorCode, type VerifiedIdentity } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
2
|
+
import { type TokenScopeRefusal } from "@sema-agent/settings-schema/api/scopes";
|
|
2
3
|
import type { Logger } from "./observability/logger.js";
|
|
3
4
|
import type { Metrics } from "./observability/metrics.js";
|
|
4
|
-
export type { VerifiedIdentity } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
5
|
/**
|
|
6
6
|
* [ref] ② —— 鉴权桥 issuer 的**唯一**取值(装配 `boot/stage-09-leader.ts` 与 caps `centerWiring.issuer` 同读,不各写一份回退):
|
|
7
7
|
* 显式 `AUTH_BRIDGE_ISSUER`(`config.authBridgeIssuer`)恒赢 ⇒ `source:"explicit"`;缺席则回退中心基址 `webAdmin.baseUrl`
|
|
@@ -34,13 +34,41 @@ export interface WebAdminJwtVerifierOptions {
|
|
|
34
34
|
fetchImpl?: typeof fetch;
|
|
35
35
|
nowMs?: () => number;
|
|
36
36
|
}
|
|
37
|
+
/** 验证腿的拒因(闭集)= 契约 R9 的五个码 ∪ `readTokenScope` 的拒词;两边都是契约的闭集,加词 ⇒ 这里随之变宽(穷举分支的读者编译红)。 */
|
|
38
|
+
type AuthBridgeRefusal = AuthBridgeErrorCode | TokenScopeRefusal;
|
|
39
|
+
/** 本服务受理的桥身份:契约 `VerifiedIdentity`(验证器「令牌带了什么」的报告形)之上,scope 经 `readTokenScope` 读定后**必带**。 */
|
|
40
|
+
type BridgeIdentity = Omit<VerifiedIdentity, "scope"> & {
|
|
41
|
+
scope: string;
|
|
42
|
+
};
|
|
37
43
|
export type VerifyOutcome = {
|
|
38
44
|
ok: true;
|
|
39
|
-
identity:
|
|
45
|
+
identity: BridgeIdentity;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
error: AuthBridgeRefusal;
|
|
49
|
+
};
|
|
50
|
+
/** 拒因记账的去处(验证器选项与 HTTP 装配各自持有同一对 logger / metrics)。 */
|
|
51
|
+
type BridgeSinks = {
|
|
52
|
+
readonly logger?: Logger | undefined;
|
|
53
|
+
readonly metrics?: Pick<Metrics, "inc"> | undefined;
|
|
54
|
+
};
|
|
55
|
+
/** 注入边界收的结局:与 {@link VerifyOutcome} 同形,只是成功身份的 `scope` 是 `unknown` —— 这一位正是边界要判的形,它不信型面
|
|
56
|
+
* (席是嵌入方可换的:`""` 过得了 `scope: string`,JS 侧连缺省 / 非串都交得进来)。`VerifyOutcome` 可赋给它,调用方零改动。 */
|
|
57
|
+
type ReportedOutcome = {
|
|
58
|
+
ok: true;
|
|
59
|
+
identity: Omit<BridgeIdentity, "scope"> & {
|
|
60
|
+
scope?: unknown;
|
|
61
|
+
};
|
|
40
62
|
} | {
|
|
41
63
|
ok: false;
|
|
42
|
-
error:
|
|
64
|
+
error: AuthBridgeRefusal;
|
|
43
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* 注入边界:任何验证器(内建 {@link createWebAdminJwtVerifier},或嵌入方注入的 `ServiceDeps.webAdminJwtVerifier`)交来的结局,在这里定「装不装身份」。
|
|
68
|
+
* 成功身份的 scope 经**同一只** `readTokenScope` 再读一次(入参型见 {@link ReportedOutcome})。
|
|
69
|
+
* 读不出 ⇒ 同一只记账口、不交身份(调用方按验证失败处置:不设身份 ⇒ 门答 generic 401)。验证失败的结局验证器自己已记过账,这里只答「不装」。
|
|
70
|
+
*/
|
|
71
|
+
export declare function admitBridgeIdentity(outcome: ReportedOutcome, sinks: BridgeSinks): BridgeIdentity | undefined;
|
|
44
72
|
/** The live object `createWebAdminJwtVerifier` produces — a single `verify` entry point over the process-level JWKS cache. */
|
|
45
73
|
export interface WebAdminJwtVerifier {
|
|
46
74
|
verify: (bearer: string) => Promise<VerifyOutcome>;
|
|
@@ -50,8 +78,9 @@ export declare const DEFAULT_JWKS_TTL_SEC: number;
|
|
|
50
78
|
/** 一次 JWKS 拉取的时限(毫秒):kid 未知、超离线宽限、后台刷新三条路用同一只拉取,同一个时限。 */
|
|
51
79
|
export declare const JWKS_FETCH_TIMEOUT_MS = 10000;
|
|
52
80
|
/**
|
|
53
|
-
* registry-JWT verifier(单例,进程级 JWKS 缓存)。`verify(bearer)` 全程不 throw
|
|
54
|
-
*
|
|
81
|
+
* registry-JWT verifier(单例,进程级 JWKS 缓存)。`verify(bearer)` 全程不 throw——一切异常折为拒因闭集
|
|
82
|
+
* {@link AuthBridgeRefusal}(HTTP 面由调用方恒答 generic 401,码只进日志/metrics,R9)。
|
|
55
83
|
*/
|
|
56
84
|
export declare function createWebAdminJwtVerifier(opts: WebAdminJwtVerifierOptions): WebAdminJwtVerifier;
|
|
85
|
+
export {};
|
|
57
86
|
//# sourceMappingURL=auth-bridge.d.ts.map
|